Skip to content

Commit 239898f

Browse files
committed
Moved XIP expansion to a temporal directory
Instead of expanding XIPs on its current directory, a temporal directory is used. The main advantage of this approach is that, because the temporal directory is created on the same volume where Xcode will be installed, the installation is vastly improved if the XIP is on a different volume. - https://nshipster.com/temporary-files/ - The implementation of the `temporalDirectory` function and variable are based on the similar `trashItem`, but without the `@discardableResult` annotation (I cannot think of any case where this URL could to be ignored). This closes #178.
1 parent ab1c5e4 commit 239898f

File tree

4 files changed

+14
-6
lines changed

4 files changed

+14
-6
lines changed

Sources/XcodesKit/Environment.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public struct Environment {
2323
public var Current = Environment()
2424

2525
public struct Shell {
26-
public var unxip: (URL) -> Promise<ProcessOutput> = { Process.run(Path.root.usr.bin.xip, workingDirectory: $0.deletingLastPathComponent(), "--expand", "\($0.path)") }
26+
public var unxip: (URL, URL) -> Promise<ProcessOutput> = { Process.run(Path.root.usr.bin.xip, workingDirectory: $1, "--expand", "\($0.path)") }
2727
public var spctlAssess: (URL) -> Promise<ProcessOutput> = { Process.run(Path.root.usr.sbin.spctl, "--assess", "--verbose", "--type", "execute", "\($0.path)") }
2828
public var codesignVerify: (URL) -> Promise<ProcessOutput> = { Process.run(Path.root.usr.bin.codesign, "-vv", "-d", "\($0.path)") }
2929
public var devToolsSecurityEnable: (String?) -> Promise<ProcessOutput> = { Process.sudo(password: $0, Path.root.usr.sbin.DevToolsSecurity, "-enable") }
@@ -238,6 +238,12 @@ public struct Files {
238238
try createDirectory(url, createIntermediates, attributes)
239239
}
240240

241+
public var temporalDirectory: (URL) throws -> URL = { try FileManager.default.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: $0, create: true) }
242+
243+
public func temporalDirectory(for URL: URL) throws -> URL {
244+
return try temporalDirectory(URL)
245+
}
246+
241247
public var installedXcodes = XcodesKit.installedXcodes
242248
}
243249
private func installedXcodes(directory: Path) -> [InstalledXcode] {

Sources/XcodesKit/XcodeInstaller.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -718,9 +718,10 @@ public final class XcodeInstaller {
718718
}
719719

720720
func unarchiveAndMoveXIP(at source: URL, to destination: URL) -> Promise<URL> {
721+
let xcodeExpansionDirectory = (try? Current.files.temporalDirectory(for: destination)) ?? source.deletingLastPathComponent()
721722
return firstly { () -> Promise<ProcessOutput> in
722723
Current.logging.log(InstallationStep.unarchiving.description)
723-
return Current.shell.unxip(source)
724+
return Current.shell.unxip(source, xcodeExpansionDirectory)
724725
.recover { (error) throws -> Promise<ProcessOutput> in
725726
if case Process.PMKError.execution(_, _, let standardError) = error,
726727
standardError?.contains("damaged and can’t be expanded") == true {
@@ -732,8 +733,8 @@ public final class XcodeInstaller {
732733
.map { output -> URL in
733734
Current.logging.log(InstallationStep.moving(destination: destination.path).description)
734735

735-
let xcodeURL = source.deletingLastPathComponent().appendingPathComponent("Xcode.app")
736-
let xcodeBetaURL = source.deletingLastPathComponent().appendingPathComponent("Xcode-beta.app")
736+
let xcodeURL = xcodeExpansionDirectory.appendingPathComponent("Xcode.app")
737+
let xcodeBetaURL = xcodeExpansionDirectory.appendingPathComponent("Xcode-beta.app")
737738
if Current.files.fileExists(atPath: xcodeURL.path) {
738739
try Current.files.moveItem(at: xcodeURL, to: destination)
739740
}

Tests/XcodesKitTests/Environment+Mock.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ extension Shell {
1616
static var processOutputMock: ProcessOutput = (0, "", "")
1717

1818
static var mock = Shell(
19-
unxip: { _ in return Promise.value(Shell.processOutputMock) },
19+
unxip: { _, _ in return Promise.value(Shell.processOutputMock) },
2020
spctlAssess: { _ in return Promise.value(Shell.processOutputMock) },
2121
codesignVerify: { _ in return Promise.value(Shell.processOutputMock) },
2222
devToolsSecurityEnable: { _ in return Promise.value(Shell.processOutputMock) },
@@ -60,6 +60,7 @@ extension Files {
6060
trashItem: { _ in return URL(fileURLWithPath: "\(NSHomeDirectory())/.Trash") },
6161
createFile: { _, _, _ in return true },
6262
createDirectory: { _, _, _ in },
63+
temporalDirectory: { _ in return URL(fileURLWithPath: NSTemporaryDirectory()) },
6364
installedXcodes: { _ in [] }
6465
)
6566
}

Tests/XcodesKitTests/XcodesKitTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,7 @@ final class XcodesKitTests: XCTestCase {
707707
XcodesKit.Current.logging.log(prompt)
708708
return "asdf"
709709
}
710-
Current.shell.unxip = { _ in
710+
Current.shell.unxip = { _, _ in
711711
unxipCallCount += 1
712712
if unxipCallCount == 1 {
713713
return Promise(error: Process.PMKError.execution(process: Process(), standardOutput: nil, standardError: "The file \"Xcode-0.0.0.xip\" is damaged and can’t be expanded."))

0 commit comments

Comments
 (0)