Skip to content

Commit

Permalink
Merge pull request Carthage#1906 from klundberg/toolchain-cache-fix
Browse files Browse the repository at this point in the history
Implemented toolchain-aware swift version check
  • Loading branch information
mdiep committed Apr 28, 2017
2 parents 2e733ff + 6bbd272 commit 31c345d
Show file tree
Hide file tree
Showing 8 changed files with 39 additions and 29 deletions.
24 changes: 12 additions & 12 deletions Source/CarthageKit/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -514,12 +514,12 @@ public final class Project {
/// Updates the dependencies of the project to the latest version. The
/// changes will be reflected in Cartfile.resolved, and also in the working
/// directory checkouts if the given parameter is true.
public func updateDependencies(shouldCheckout: Bool = true, dependenciesToUpdate: [String]? = nil) -> SignalProducer<(), CarthageError> {
public func updateDependencies(shouldCheckout: Bool = true, buildOptions: BuildOptions, dependenciesToUpdate: [String]? = nil) -> SignalProducer<(), CarthageError> {
return updatedResolvedCartfile(dependenciesToUpdate)
.attemptMap { resolvedCartfile -> Result<(), CarthageError> in
return self.writeResolvedCartfile(resolvedCartfile)
}
.then(shouldCheckout ? checkoutResolvedDependencies(dependenciesToUpdate) : .empty)
.then(shouldCheckout ? checkoutResolvedDependencies(dependenciesToUpdate, buildOptions: buildOptions) : .empty)
}

/// Unzips the file at the given URL and copies the frameworks, DSYM and
Expand All @@ -528,13 +528,13 @@ public final class Project {
/// for the given frameworks.
///
/// Sends the temporary URL of the unzipped directory
private func unarchiveAndCopyBinaryFrameworks(zipFile: URL, projectName: String, commitish: String) -> SignalProducer<URL, CarthageError> {
private func unarchiveAndCopyBinaryFrameworks(zipFile: URL, projectName: String, commitish: String, toolchain: String?) -> SignalProducer<URL, CarthageError> {
return SignalProducer<URL, CarthageError>(value: zipFile)
.flatMap(.concat, transform: unarchive(archive:))
.flatMap(.concat) { directoryURL in
return frameworksInDirectory(directoryURL)
.flatMap(.merge) { url in
return checkFrameworkCompatibility(url)
return checkFrameworkCompatibility(url, usingToolchain: toolchain)
.mapError { error in CarthageError.internalError(description: error.description) }
}
.flatMap(.merge, transform: self.copyFrameworkToBuildFolder)
Expand Down Expand Up @@ -569,7 +569,7 @@ public final class Project {
/// Installs binaries and debug symbols for the given project, if available.
///
/// Sends a boolean indicating whether binaries were installed.
private func installBinariesForProject(_ project: ProjectIdentifier, atRevision revision: String) -> SignalProducer<Bool, CarthageError> {
private func installBinariesForProject(_ project: ProjectIdentifier, atRevision revision: String, toolchain: String?) -> SignalProducer<Bool, CarthageError> {
return SignalProducer.attempt {
return .success(self.useBinaries)
}
Expand All @@ -590,7 +590,7 @@ public final class Project {
}
return self.downloadMatchingBinariesForProject(project, atRevision: revision, fromRepository: repository, client: Client(repository: repository, isAuthenticated: false))
}
.flatMap(.concat) { self.unarchiveAndCopyBinaryFrameworks(zipFile: $0, projectName: project.name, commitish: revision) }
.flatMap(.concat) { self.unarchiveAndCopyBinaryFrameworks(zipFile: $0, projectName: project.name, commitish: revision, toolchain: toolchain) }
.on(completed: {
_ = try? FileManager.default.trashItem(at: checkoutDirectoryURL, resultingItemURL: nil)
})
Expand Down Expand Up @@ -790,7 +790,7 @@ public final class Project {

/// Checks out the dependencies listed in the project's Cartfile.resolved,
/// optionally they are limited by the given list of dependency names.
public func checkoutResolvedDependencies(_ dependenciesToCheckout: [String]? = nil) -> SignalProducer<(), CarthageError> {
public func checkoutResolvedDependencies(_ dependenciesToCheckout: [String]? = nil, buildOptions: BuildOptions?) -> SignalProducer<(), CarthageError> {
/// Determine whether the repository currently holds any submodules (if
/// it even is a repository).
let submodulesSignal = submodulesInRepository(self.directoryURL)
Expand Down Expand Up @@ -824,7 +824,7 @@ public final class Project {
return checkoutOrCloneDependency
}

return self.installBinariesForProject(project, atRevision: dependency.version.commitish)
return self.installBinariesForProject(project, atRevision: dependency.version.commitish, toolchain: buildOptions?.toolchain)
.flatMap(.merge) { installed -> SignalProducer<(), CarthageError> in
if installed {
return .empty
Expand All @@ -834,7 +834,7 @@ public final class Project {
}

case let .binary(url):
return self.installBinariesForBinaryProject(url: url, pinnedVersion: dependency.version, projectName: project.name)
return self.installBinariesForBinaryProject(url: url, pinnedVersion: dependency.version, projectName: project.name, toolchain: buildOptions?.toolchain)
}


Expand All @@ -843,7 +843,7 @@ public final class Project {
.then(SignalProducer<(), CarthageError>.empty)
}

private func installBinariesForBinaryProject(url: URL, pinnedVersion: PinnedVersion, projectName: String) -> SignalProducer<(), CarthageError> {
private func installBinariesForBinaryProject(url: URL, pinnedVersion: PinnedVersion, projectName: String, toolchain: String?) -> SignalProducer<(), CarthageError> {

return SignalProducer<SemanticVersion, ScannableError>(result: SemanticVersion.from(pinnedVersion))
.mapError { CarthageError(scannableError: $0) }
Expand All @@ -858,7 +858,7 @@ public final class Project {
.flatMap(.concat) { (semanticVersion, frameworkURL) in
return self.downloadBinary(project: ProjectIdentifier.binary(url), version: semanticVersion, url: frameworkURL)
}
.flatMap(.concat) { self.unarchiveAndCopyBinaryFrameworks(zipFile: $0, projectName: projectName, commitish: pinnedVersion.commitish) }
.flatMap(.concat) { self.unarchiveAndCopyBinaryFrameworks(zipFile: $0, projectName: projectName, commitish: pinnedVersion.commitish, toolchain: toolchain) }
.flatMap(.concat) { self.removeItem(at: $0) }
}

Expand Down Expand Up @@ -969,7 +969,7 @@ public final class Project {
return SignalProducer.combineLatest(
SignalProducer(value: dependency),
self.dependencyProjects(for: dependency),
versionFileMatches(dependency, platforms: options.platforms, rootDirectoryURL: self.directoryURL)
versionFileMatches(dependency, platforms: options.platforms, rootDirectoryURL: self.directoryURL, toolchain: options.toolchain)
)
}
.reduce([]) { (includedDependencies, nextGroup) -> [Dependency<PinnedVersion>] in
Expand Down
4 changes: 2 additions & 2 deletions Source/CarthageKit/VersionFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ public func createVersionFileForCommitish(_ commitish: String, dependencyName: S
/// Returns an optional bool which is nil if no version file exists,
/// otherwise true if the version file matches and the build can be
/// skipped or false if there is a mismatch of some kind.
public func versionFileMatches(_ dependency: Dependency<PinnedVersion>, platforms: Set<Platform>, rootDirectoryURL: URL) -> SignalProducer<Bool?, CarthageError> {
public func versionFileMatches(_ dependency: Dependency<PinnedVersion>, platforms: Set<Platform>, rootDirectoryURL: URL, toolchain: String?) -> SignalProducer<Bool?, CarthageError> {
let rootBinariesURL = rootDirectoryURL.appendingPathComponent(CarthageBinariesFolderPath, isDirectory: true).resolvingSymlinksInPath()
let versionFileURL = rootBinariesURL.appendingPathComponent(".\(dependency.project.name).\(VersionFile.pathExtension)")
guard let versionFile = VersionFile(url: versionFileURL) else {
Expand All @@ -290,7 +290,7 @@ public func versionFileMatches(_ dependency: Dependency<PinnedVersion>, platform

let platformsToCheck = platforms.isEmpty ? Set<Platform>(Platform.supportedPlatforms) : platforms

return swiftVersion
return swiftVersion(usingToolchain: toolchain)
.mapError { error in CarthageError.internalError(description: error.description) }
.flatMap(.concat) { localSwiftVersion in
return SignalProducer<Platform, CarthageError>(platformsToCheck)
Expand Down
24 changes: 17 additions & 7 deletions Source/CarthageKit/Xcode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ import XCDBLD
public let CarthageBinariesFolderPath = "Carthage/Build"

/// Emits the currect Swift version
internal let swiftVersion: SignalProducer<String, SwiftVersionError> = { return determineSwiftVersion().replayLazily(upTo: 1) }()
internal func swiftVersion(usingToolchain toolchain: String? = nil) -> SignalProducer<String, SwiftVersionError> {
return determineSwiftVersion(usingToolchain: toolchain).replayLazily(upTo: 1)
}

/// Attempts to determine the local version of swift
private func determineSwiftVersion() -> SignalProducer<String, SwiftVersionError> {
let taskDescription = Task("/usr/bin/env", arguments: ["xcrun", "swift", "--version"])
private func determineSwiftVersion(usingToolchain toolchain: String?) -> SignalProducer<String, SwiftVersionError> {
let taskDescription = Task("/usr/bin/env", arguments: compilerVersionArguments(usingToolchain: toolchain))

return taskDescription.launch(standardInput: nil)
.ignoreTaskData()
Expand All @@ -32,6 +34,14 @@ private func determineSwiftVersion() -> SignalProducer<String, SwiftVersionError
.attemptMap { Result($0, failWith: SwiftVersionError.unknownLocalSwiftVersion) }
}

private func compilerVersionArguments(usingToolchain toolchain: String?) -> [String] {
if let toolchain = toolchain {
return ["xcrun", "--toolchain", toolchain, "swift", "--version"]
} else {
return ["xcrun", "swift", "--version"]
}
}

/// Parses output of `swift --version` for the version string.
private func parseSwiftVersionCommand(output: String?) -> String? {
guard
Expand Down Expand Up @@ -65,8 +75,8 @@ internal func isSwiftFramework(_ frameworkURL: URL) -> SignalProducer<Bool, Swif
}

/// Emits the framework URL if it matches the local Swift version and errors if not.
internal func checkSwiftFrameworkCompatibility(_ frameworkURL: URL) -> SignalProducer<URL, SwiftVersionError> {
return SignalProducer.combineLatest(swiftVersion, frameworkSwiftVersion(frameworkURL))
internal func checkSwiftFrameworkCompatibility(_ frameworkURL: URL, usingToolchain toolchain: String?) -> SignalProducer<URL, SwiftVersionError> {
return SignalProducer.combineLatest(swiftVersion(usingToolchain: toolchain), frameworkSwiftVersion(frameworkURL))
.attemptMap() { localSwiftVersion, frameworkSwiftVersion in
return localSwiftVersion == frameworkSwiftVersion
? .success(frameworkURL)
Expand All @@ -75,11 +85,11 @@ internal func checkSwiftFrameworkCompatibility(_ frameworkURL: URL) -> SignalPro
}

/// Emits the framework URL if it is compatible with the build environment and errors if not.
internal func checkFrameworkCompatibility(_ frameworkURL: URL) -> SignalProducer<URL, SwiftVersionError> {
internal func checkFrameworkCompatibility(_ frameworkURL: URL, usingToolchain toolchain: String?) -> SignalProducer<URL, SwiftVersionError> {
return isSwiftFramework(frameworkURL)
.flatMap(.merge) { isSwift in
return isSwift
? checkSwiftFrameworkCompatibility(frameworkURL)
? checkSwiftFrameworkCompatibility(frameworkURL, usingToolchain: toolchain)
: SignalProducer(value: frameworkURL)
}
}
Expand Down
2 changes: 1 addition & 1 deletion Source/CarthageKitTests/ProjectSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class ProjectSpec: QuickSpec {
let frameworkURL = platformURL.appendingPathComponent("\(frameworkName)_\(platformName).framework", isDirectory: false)
let swiftHeaderURL = frameworkURL.swiftHeaderURL()!

let swiftVersionResult = swiftVersion.first()!
let swiftVersionResult = swiftVersion().first()!
expect(swiftVersionResult.error).to(beNil())

var header = try! String(contentsOf: swiftHeaderURL)
Expand Down
6 changes: 3 additions & 3 deletions Source/CarthageKitTests/XcodeSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class XcodeSpec: QuickSpec {
}

describe("determineSwiftInformation:") {
let currentSwiftVersion = swiftVersion.single()?.value
let currentSwiftVersion = swiftVersion().single()?.value
let testSwiftFramework = "Quick.framework"
let currentDirectory = URL(fileURLWithPath: FileManager.default.currentDirectoryPath, isDirectory: true)
let testSwiftFrameworkURL = currentDirectory.appendingPathComponent(testSwiftFramework)
Expand Down Expand Up @@ -64,14 +64,14 @@ class XcodeSpec: QuickSpec {
}

it("should determine when a Swift framework is compatible") {
let result = checkSwiftFrameworkCompatibility(testSwiftFrameworkURL).single()
let result = checkSwiftFrameworkCompatibility(testSwiftFrameworkURL, usingToolchain: nil).single()

expect(result?.value) == testSwiftFrameworkURL
}

it("should determine when a Swift framework is incompatible") {
let frameworkURL = Bundle(for: type(of: self)).url(forResource: "FakeOldSwift.framework", withExtension: nil)!
let result = checkSwiftFrameworkCompatibility(frameworkURL).single()
let result = checkSwiftFrameworkCompatibility(frameworkURL, usingToolchain: nil).single()

expect(result?.value).to(beNil())
expect(result?.error) == .incompatibleFrameworkSwiftVersions(local: currentSwiftVersion ?? "", framework: "0.0.0")
Expand Down
4 changes: 2 additions & 2 deletions Source/carthage/Bootstrap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public struct BootstrapCommand: CommandProtocol {
if !FileManager.default.fileExists(atPath: project.resolvedCartfileURL.path) {
let formatting = options.checkoutOptions.colorOptions.formatting
carthage.println(formatting.bullets + "No Cartfile.resolved found, updating dependencies")
return project.updateDependencies(shouldCheckout: options.checkoutAfterUpdate)
return project.updateDependencies(shouldCheckout: options.checkoutAfterUpdate, buildOptions: options.buildOptions)
}

let checkDependencies: SignalProducer<(), CarthageError>
Expand All @@ -46,7 +46,7 @@ public struct BootstrapCommand: CommandProtocol {

let checkoutDependencies: SignalProducer<(), CarthageError>
if options.checkoutAfterUpdate {
checkoutDependencies = project.checkoutResolvedDependencies(options.dependenciesToUpdate)
checkoutDependencies = project.checkoutResolvedDependencies(options.dependenciesToUpdate, buildOptions: options.buildOptions)
} else {
checkoutDependencies = .empty
}
Expand Down
2 changes: 1 addition & 1 deletion Source/carthage/Checkout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,6 @@ public struct CheckoutCommand: CommandProtocol {
/// Checks out dependencies with the given options.
public func checkoutWithOptions(_ options: Options) -> SignalProducer<(), CarthageError> {
return options.loadProject()
.flatMap(.merge) { $0.checkoutResolvedDependencies(options.dependenciesToCheckout) }
.flatMap(.merge) { $0.checkoutResolvedDependencies(options.dependenciesToCheckout, buildOptions: nil) }
}
}
2 changes: 1 addition & 1 deletion Source/carthage/Update.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public struct UpdateCommand: CommandProtocol {
}

let updateDependencies = project.updateDependencies(
shouldCheckout: options.checkoutAfterUpdate,
shouldCheckout: options.checkoutAfterUpdate, buildOptions: options.buildOptions,
dependenciesToUpdate: options.dependenciesToUpdate
)

Expand Down

0 comments on commit 31c345d

Please sign in to comment.