mirror of
https://github.com/XcodesOrg/XcodesApp.git
synced 2026-03-25 08:55:46 +00:00
add a unxip experiment for faster unxipping
This commit is contained in:
parent
c0974edc98
commit
ce001c8e68
13 changed files with 139 additions and 9 deletions
12
README.md
12
README.md
|
|
@ -18,6 +18,13 @@ _If you're looking for a command-line version of Xcodes.app, try [`xcodes`](http
|
|||
- View release notes, OS compatibility, included SDKs and compilers from [Xcode Releases](https://xcodereleases.com).
|
||||
- Dark/Light Mode supported
|
||||
|
||||
## Experiments
|
||||
|
||||
- Thanks to the wonderful work of [https://github.com/saagarjha/unxip](https://github.com/saagarjha/unxip), turn on the experiment to increase your unxipping time by up to 70%! More can be found on his repo, but bugs, high memory may occur if used.
|
||||
|
||||

|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
Xcodes.app runs on macOS Big Sur 11.0 or later.
|
||||
|
|
@ -80,7 +87,10 @@ scripts/package_release.sh
|
|||
# Notarize the app
|
||||
# Do this from the Product directory so the app is zipped without being nested inside Product
|
||||
# Create a app specific password on appleid.apple.com if you haven't already
|
||||
# % xcrun altool --store-password-in-keychain-item "AC_PASSWORD" -u "<appleiduseremail>" -p <app_specific_secret>
|
||||
# xcrun notarytool store-credentials "AC_PASSWORD" \
|
||||
# --apple-id "test@example.com" \
|
||||
# --team-id "teamid" \
|
||||
# --password "app specific password"
|
||||
|
||||
pushd Product
|
||||
../scripts/notarize.sh "test@example.com" "@keychain:AC_PASSWORD" <MyOrg> Xcodes.zip
|
||||
|
|
|
|||
|
|
@ -104,6 +104,9 @@
|
|||
E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87DD6EA25D053FA00D86808 /* Progress+.swift */; };
|
||||
E89342FA25EDCC17007CF557 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89342F925EDCC17007CF557 /* NotificationManager.swift */; };
|
||||
E8977EA325C11E1500835F80 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8977EA225C11E1500835F80 /* PreferencesView.swift */; };
|
||||
E8CBDB8727ADD92000B22292 /* unxip in Resources */ = {isa = PBXBuildFile; fileRef = E8CBDB8627ADD92000B22292 /* unxip */; };
|
||||
E8CBDB8927ADE32300B22292 /* unxip in Copy aria2c */ = {isa = PBXBuildFile; fileRef = E8CBDB8627ADD92000B22292 /* unxip */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
E8CBDB8B27AE02FF00B22292 /* ExperiementsPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8CBDB8A27AE02FF00B22292 /* ExperiementsPreferencePane.swift */; };
|
||||
E8DA461125FAF7FB002E85EF /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8DA461025FAF7FB002E85EF /* NotificationsView.swift */; };
|
||||
E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBC3FF259AC17F00E2A3D8 /* InstallationStepRowView.swift */; };
|
||||
E8E98A9625D863D700EC89A0 /* InstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */; };
|
||||
|
|
@ -153,6 +156,7 @@
|
|||
dstPath = "";
|
||||
dstSubfolderSpec = 6;
|
||||
files = (
|
||||
E8CBDB8927ADE32300B22292 /* unxip in Copy aria2c */,
|
||||
CAA8589325A2B77E00ACF8C0 /* aria2c in Copy aria2c */,
|
||||
);
|
||||
name = "Copy aria2c";
|
||||
|
|
@ -272,6 +276,8 @@
|
|||
E87DD6EA25D053FA00D86808 /* Progress+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Progress+.swift"; sourceTree = "<group>"; };
|
||||
E89342F925EDCC17007CF557 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
||||
E8977EA225C11E1500835F80 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; };
|
||||
E8CBDB8627ADD92000B22292 /* unxip */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = unxip; sourceTree = "<group>"; };
|
||||
E8CBDB8A27AE02FF00B22292 /* ExperiementsPreferencePane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExperiementsPreferencePane.swift; sourceTree = "<group>"; };
|
||||
E8DA461025FAF7FB002E85EF /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = "<group>"; };
|
||||
E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationStepDetailView.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
|
@ -484,6 +490,7 @@
|
|||
CA9FF83E2594FBC000E47BAF /* Licenses.rtf */,
|
||||
CAD2E7AE2449575000113D76 /* Xcodes.entitlements */,
|
||||
CA8FB64D256E17B100469DA5 /* XcodesTest.entitlements */,
|
||||
E8CBDB8627ADD92000B22292 /* unxip */,
|
||||
);
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -561,6 +568,7 @@
|
|||
CAFE4ABB25B7D54B0064FE51 /* UpdatesPreferencePane.swift */,
|
||||
E8977EA225C11E1500835F80 /* PreferencesView.swift */,
|
||||
E8DA461025FAF7FB002E85EF /* NotificationsView.swift */,
|
||||
E8CBDB8A27AE02FF00B22292 /* ExperiementsPreferencePane.swift */,
|
||||
);
|
||||
path = Preferences;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -707,6 +715,7 @@
|
|||
files = (
|
||||
CAD2E7A92449575000113D76 /* Preview Assets.xcassets in Resources */,
|
||||
CA9FF83F2594FBC000E47BAF /* Licenses.rtf in Resources */,
|
||||
E8CBDB8727ADD92000B22292 /* unxip in Resources */,
|
||||
CAA858DB25A3E11F00ACF8C0 /* aria2-release-1.35.0.tar.gz in Resources */,
|
||||
CAD2E7A62449575000113D76 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
|
|
@ -785,6 +794,7 @@
|
|||
CABFA9BD2592EEEA00380FEE /* Environment.swift in Sources */,
|
||||
CABFA9C32592EEEA00380FEE /* Downloads.swift in Sources */,
|
||||
CAC281DA259F985100B8AB0B /* InstallationStep.swift in Sources */,
|
||||
E8CBDB8B27AE02FF00B22292 /* ExperiementsPreferencePane.swift in Sources */,
|
||||
E8E98A9625D863D700EC89A0 /* InstallationStepDetailView.swift in Sources */,
|
||||
CA378F992466567600A58CE0 /* AppState.swift in Sources */,
|
||||
CAD2E7A42449574E00113D76 /* XcodeListView.swift in Sources */,
|
||||
|
|
|
|||
7
Xcodes/AcknowledgementsGenerator/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
Xcodes/AcknowledgementsGenerator/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -238,8 +238,8 @@ extension AppState {
|
|||
|
||||
func unarchiveAndMoveXIP(availableXcode: AvailableXcode, at source: URL, to destination: URL) -> AnyPublisher<URL, Swift.Error> {
|
||||
self.setInstallationStep(of: availableXcode.version, to: .unarchiving)
|
||||
|
||||
return Current.shell.unxip(source)
|
||||
|
||||
return unxipOrUnxipExperiment(source)
|
||||
.catch { error -> AnyPublisher<ProcessOutput, Swift.Error> in
|
||||
if let executionError = error as? ProcessExecutionError {
|
||||
if executionError.standardError.contains("damaged and can’t be expanded") {
|
||||
|
|
@ -278,6 +278,16 @@ extension AppState {
|
|||
})
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func unxipOrUnxipExperiment(_ source: URL) -> AnyPublisher<ProcessOutput, Error> {
|
||||
if unxipExperiment {
|
||||
// All hard work done by https://github.com/saagarjha/unxip
|
||||
// Compiled to binary with `swiftc -parse-as-library -O unxip.swift`
|
||||
return Current.shell.unxipExperiment(source)
|
||||
} else {
|
||||
return Current.shell.unxip(source)
|
||||
}
|
||||
}
|
||||
|
||||
public func verifySecurityAssessment(of xcode: InstalledXcode) -> AnyPublisher<Void, Error> {
|
||||
return Current.shell.spctlAssess(xcode.path.url)
|
||||
|
|
|
|||
|
|
@ -62,6 +62,12 @@ class AppState: ObservableObject {
|
|||
Current.defaults.set(localPath, forKey: "localPath")
|
||||
}
|
||||
}
|
||||
|
||||
@Published var unxipExperiment = false {
|
||||
didSet {
|
||||
Current.defaults.set(unxipExperiment, forKey: "unxipExperiment")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Publisher Cancellables
|
||||
|
||||
|
|
@ -96,6 +102,7 @@ class AppState: ObservableObject {
|
|||
|
||||
func setupDefaults() {
|
||||
localPath = Current.defaults.string(forKey: "localPath") ?? Path.defaultXcodesApplicationSupport.string
|
||||
unxipExperiment = Current.defaults.bool(forKey: "unxipExperiment") ?? false
|
||||
}
|
||||
|
||||
// MARK: Timer
|
||||
|
|
|
|||
|
|
@ -111,6 +111,12 @@ public struct Shell {
|
|||
|
||||
return (progress, publisher)
|
||||
}
|
||||
|
||||
public var unxipExperiment: (URL) -> AnyPublisher<ProcessOutput, Error> = { url in
|
||||
let unxipPath = Path(url: Bundle.main.url(forAuxiliaryExecutable: "unxip")!)!
|
||||
return Process.run(unxipPath.url, workingDirectory: url.deletingLastPathComponent(), ["\(url.path)"])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct Files {
|
||||
|
|
@ -238,6 +244,11 @@ public struct Defaults {
|
|||
public func get(forKey key: String) -> Any? {
|
||||
get(key)
|
||||
}
|
||||
|
||||
public var bool: (String) -> Bool? = { UserDefaults.standard.bool(forKey: $0) }
|
||||
public func bool(forKey key: String) -> Bool? {
|
||||
bool(key)
|
||||
}
|
||||
}
|
||||
|
||||
private let helperClient = HelperClient()
|
||||
|
|
|
|||
|
|
@ -14,9 +14,6 @@ struct AboutView: View {
|
|||
|
||||
Text("Version \(Bundle.main.shortVersion!) (\(Bundle.main.version!))")
|
||||
|
||||
Color.clear
|
||||
.frame(width: 300, height: 16)
|
||||
|
||||
HStack(spacing: 32) {
|
||||
Button(action: {
|
||||
openURL(URL(string: "https://github.com/RobotsAndPencils/XcodesApp/")!)
|
||||
|
|
@ -30,6 +27,24 @@ struct AboutView: View {
|
|||
}
|
||||
.buttonStyle(LinkButtonStyle())
|
||||
}
|
||||
Color.clear
|
||||
.frame(width: 300, height: 0)
|
||||
Label("Unxip Experiment", systemImage: "testtube.2")
|
||||
HStack(spacing: 32) {
|
||||
Button(action: {
|
||||
openURL(URL(string: "https://github.com/saagarjha/unxip/")!)
|
||||
}) {
|
||||
Label("Github Repo", systemImage: "link")
|
||||
}
|
||||
.buttonStyle(LinkButtonStyle())
|
||||
|
||||
Button(action: {
|
||||
openURL(URL(string: "https://github.com/saagarjha/unxip/blob/main/LICENSE")!)
|
||||
}) {
|
||||
Label("License", systemImage: "link")
|
||||
}
|
||||
.buttonStyle(LinkButtonStyle())
|
||||
}
|
||||
|
||||
Text(Bundle.main.humanReadableCopyright!)
|
||||
.font(.footnote)
|
||||
|
|
|
|||
55
Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift
Normal file
55
Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import AppleAPI
|
||||
import SwiftUI
|
||||
import Path
|
||||
|
||||
struct ExperimentsPreferencePane: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
GroupBox(label: Text("Faster Unxip")) {
|
||||
VStack(alignment: .leading) {
|
||||
Toggle(
|
||||
"When unxipping, use experiment",
|
||||
isOn: $appState.unxipExperiment
|
||||
)
|
||||
AttributedText(unxipFootnote)
|
||||
}
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
.groupBoxStyle(PreferencesGroupBoxStyle())
|
||||
|
||||
Divider()
|
||||
}
|
||||
.frame(width: 500)
|
||||
}
|
||||
|
||||
private var unxipFootnote: NSAttributedString {
|
||||
let string = """
|
||||
Thanks to @_saagarjha, this experiment can increase unxipping
|
||||
speed by up to 70% for some systems.
|
||||
|
||||
More information on how this is accomplished can be seen
|
||||
on the unxip repo - https://github.com/saagarjha/unxip
|
||||
"""
|
||||
let attributedString = NSMutableAttributedString(
|
||||
string: string,
|
||||
attributes: [
|
||||
.font: NSFont.preferredFont(forTextStyle: .footnote, options: [:]),
|
||||
.foregroundColor: NSColor.labelColor
|
||||
]
|
||||
)
|
||||
attributedString.addAttribute(.link, value: URL(string: "https://twitter.com/_saagarjha")!, range: NSRange(string.range(of: "@_saagarjha")!, in: string))
|
||||
attributedString.addAttribute(.link, value: URL(string: "https://github.com/saagarjha/unxip")!, range: NSRange(string.range(of: "https://github.com/saagarjha/unxip")!, in: string))
|
||||
return attributedString
|
||||
}
|
||||
}
|
||||
|
||||
struct ExperimentsPreferencePane_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
ExperimentsPreferencePane()
|
||||
.environmentObject(AppState())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ import SwiftUI
|
|||
|
||||
struct PreferencesView: View {
|
||||
private enum Tabs: Hashable {
|
||||
case general, updates, advanced
|
||||
case general, updates, advanced, experiment
|
||||
}
|
||||
@EnvironmentObject var appState: AppState
|
||||
|
||||
|
|
@ -25,6 +25,11 @@ struct PreferencesView: View {
|
|||
Label("Advanced", systemImage: "gearshape.2")
|
||||
}
|
||||
.tag(Tabs.advanced)
|
||||
ExperimentsPreferencePane()
|
||||
.tabItem {
|
||||
Label("Experiments", systemImage: "testtube.2")
|
||||
}
|
||||
.tag(Tabs.experiment)
|
||||
}
|
||||
.padding(20)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2020 Robots and Pencils.</string>
|
||||
<string>Copyright © 2022 Robots and Pencils.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSSupportsAutomaticTermination</key>
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
<key>SMPrivilegedExecutables</key>
|
||||
<dict>
|
||||
<key>com.robotsandpencils.XcodesApp.Helper</key>
|
||||
<string>identifier "com.robotsandpencils.XcodesApp.Helper" and info [CFBundleShortVersionString] >= "1.0.0" and anchor apple generic and certificate leaf[subject.OU] = "$(CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT)"</string>
|
||||
<string>identifier "com.robotsandpencils.XcodesApp.Helper" and info [CFBundleShortVersionString] >= "1.0.0" and anchor apple generic and certificate leaf[subject.OU] = "$(CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT)"</string>
|
||||
</dict>
|
||||
<key>SUFeedURL</key>
|
||||
<string>https://robotsandpencils.github.io/XcodesApp/appcast.xml</string>
|
||||
|
|
|
|||
BIN
Xcodes/Resources/unxip
Executable file
BIN
Xcodes/Resources/unxip
Executable file
Binary file not shown.
BIN
experiment_dark.jpg
Normal file
BIN
experiment_dark.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 75 KiB |
BIN
experiment_light.jpg
Normal file
BIN
experiment_light.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
Loading…
Reference in a new issue