In any project with several Carthage dependencies, calling /usr/local/bin/carthage copy-frameworks starts becoming a very large part of the compilation time. AFAIK most of that time is due to stripping architectures.
However, I just realized that if we made that optional, we could make it only do that when archiving for submission, and in that way make iteration times much lower.
Maybe we should just not do it for debug builds?
Some people might want to send debug builds to TestFlight. But I think that could be a simpler way, too, if we don't want to add a parameter, everyone would get the optimization for free
Or better yet, look at ONLY_ACTIVE_ARCH. If YES, don't try to strip it (it means we're debugging to just one architecture, either simulator or device), and that would also not be valid for production. I think that'd be the most accurate flag to use.
After taking a shot at implementing this, as far as I can tell the (non-optional) code signing step vastly outweighs the stripping step in terms of time expense:
Measured with Carthage 0.20.1-11-gf9f5575 when building for a device (ONLY_ACTIVE_ARCH=YES).
Perhaps I've missed something鈥攖he logic was as follows:
private func copyFramework(_ source: URL, target: URL, validArchitectures: [String]) -> SignalProducer<(), CarthageError> {
return SignalProducer.combineLatest(copyProduct(source, target), codeSigningIdentity())
.flatMap(.merge) { (url, codesigningIdentity) -> SignalProducer<(), CarthageError> in
let strip: SignalProducer<(), CarthageError>
// If only one architecture is active, we're building for speed and
// thus can get away with not stripping architectures nor headers.
if onlyActiveArchitecture() {
strip = SignalProducer<(), CarthageError>.empty
} else {
strip = stripFramework(url, keepingArchitectures: validArchitectures)
}
let sign = signFramework(url, codesigningIdentity: codesigningIdentity)
if buildActionIsArchiveOrInstall() {
return strip
.concat(sign)
.then(copyBCSymbolMapsForFramework(url, fromDirectory: source.deletingLastPathComponent()))
.then(SignalProducer<(), CarthageError>.empty)
} else {
return strip.concat(sign)
}
}
}
...
private func onlyActiveArchitecture() -> Bool {
return getEnvironmentVariable("ONLY_ACTIVE_ARCH")
.map { $0 == "YES" }.value ?? false
}
This makes sense, as the Simulator does not require embedded frameworks to be code signed, and the copy-frameworks command is much faster for Simulator targets than it is for device targets.
It seems as if caching signed frameworks by their SHA would perhaps be a better solution to this issue than skipping the stripping step.
Most helpful comment
Or better yet, look at
ONLY_ACTIVE_ARCH. IfYES, don't try to strip it (it means we're debugging to just one architecture, either simulator or device), and that would also not be valid for production. I think that'd be the most accurate flag to use.