Fix issue with MacCatalyst archiving not being included in build cache when caching `XCFramework` (#5108)
* Wip * Applied code review feedback * Linted the code * Refactor using Graph instances in CacheController * Refactor MacCatalyst branch through bundleArtifactBuilder's build function * Fixed deploymentTarget branch --------- Co-authored-by: Daniele Formichelli <df@bendingspoons.com>
This commit is contained in:
parent
1be1aba5d3
commit
4fdcf9d2b7
|
@ -16,6 +16,7 @@ public protocol CacheArtifactBuilding {
|
|||
/// - deviceName: The specific device that will be used when compiling the given target.
|
||||
/// - into: The directory into which the output artifacts will be copied.
|
||||
func build(
|
||||
graph: Graph,
|
||||
scheme: Scheme,
|
||||
projectTarget: XcodeBuildTarget,
|
||||
configuration: String,
|
||||
|
|
|
@ -26,6 +26,7 @@ public final class CacheBundleBuilder: CacheArtifactBuilding {
|
|||
}
|
||||
|
||||
public func build(
|
||||
graph _: Graph,
|
||||
scheme: Scheme,
|
||||
projectTarget: XcodeBuildTarget,
|
||||
configuration: String,
|
||||
|
|
|
@ -46,6 +46,7 @@ public final class CacheFrameworkBuilder: CacheArtifactBuilding {
|
|||
public var cacheOutputType: CacheOutputType = .framework
|
||||
|
||||
public func build(
|
||||
graph _: Graph,
|
||||
scheme: Scheme,
|
||||
projectTarget: XcodeBuildTarget,
|
||||
configuration: String,
|
||||
|
|
|
@ -38,6 +38,7 @@ public final class CacheXCFrameworkBuilder: CacheArtifactBuilding {
|
|||
public var cacheOutputType: CacheOutputType
|
||||
|
||||
public func build(
|
||||
graph: Graph,
|
||||
scheme: Scheme,
|
||||
projectTarget: XcodeBuildTarget,
|
||||
configuration: String,
|
||||
|
@ -45,7 +46,9 @@ public final class CacheXCFrameworkBuilder: CacheArtifactBuilding {
|
|||
deviceName _: String?,
|
||||
into outputDirectory: AbsolutePath
|
||||
) async throws {
|
||||
guard let buildTargets = scheme.buildAction?.targets else { return }
|
||||
let platform = self.platform(scheme: scheme)
|
||||
let macCatalystSupportedTargets = getMacCatalystTargets(graph: graph, scheme: scheme)
|
||||
|
||||
// Create temporary directories
|
||||
return try await FileHandler.shared.inTemporaryDirectory { temporaryDirectory in
|
||||
|
@ -63,6 +66,18 @@ public final class CacheXCFrameworkBuilder: CacheArtifactBuilding {
|
|||
)
|
||||
}
|
||||
|
||||
// Build for the macCatalyst
|
||||
var macCatalystArchivePath: AbsolutePath?
|
||||
if platform == .iOS, !macCatalystSupportedTargets.isEmpty {
|
||||
macCatalystArchivePath = temporaryDirectory.appending(component: "macCatalyst.xcarchive")
|
||||
try await self.macCatalystBuild(
|
||||
projectTarget: projectTarget,
|
||||
scheme: scheme.name,
|
||||
configuration: configuration,
|
||||
archivePath: macCatalystArchivePath!
|
||||
)
|
||||
}
|
||||
|
||||
// Build for the device - if required
|
||||
var deviceArchivePath: AbsolutePath?
|
||||
if self.cacheOutputType.shouldBuildForDevice {
|
||||
|
@ -77,7 +92,10 @@ public final class CacheXCFrameworkBuilder: CacheArtifactBuilding {
|
|||
}
|
||||
|
||||
try await self.createXCFramework(
|
||||
buildTargets: buildTargets,
|
||||
macCatalystSupportedTargets: macCatalystSupportedTargets,
|
||||
simulatorArchivePath: simulatorArchivePath,
|
||||
macCatalystArchivePath: macCatalystArchivePath,
|
||||
deviceArchivePath: deviceArchivePath,
|
||||
outputDirectory: outputDirectory
|
||||
)
|
||||
|
@ -87,7 +105,10 @@ public final class CacheXCFrameworkBuilder: CacheArtifactBuilding {
|
|||
// MARK: - Fileprivate
|
||||
|
||||
fileprivate func createXCFramework(
|
||||
buildTargets: [TargetReference],
|
||||
macCatalystSupportedTargets: [Target],
|
||||
simulatorArchivePath: AbsolutePath?,
|
||||
macCatalystArchivePath: AbsolutePath?,
|
||||
deviceArchivePath: AbsolutePath?,
|
||||
outputDirectory: AbsolutePath
|
||||
) async throws {
|
||||
|
@ -106,7 +127,7 @@ public final class CacheXCFrameworkBuilder: CacheArtifactBuilding {
|
|||
.glob("*.framework")
|
||||
.map(\.basenameWithoutExt)
|
||||
|
||||
// Build the xcframework
|
||||
logger.notice("Caching to all cacheable targets as xcframeworks", metadata: .section)
|
||||
for productName in productNames {
|
||||
var frameworkpaths = [AbsolutePath]()
|
||||
if let simulatorArchivePath = simulatorArchivePath {
|
||||
|
@ -115,12 +136,29 @@ public final class CacheXCFrameworkBuilder: CacheArtifactBuilding {
|
|||
productName: productName
|
||||
))
|
||||
}
|
||||
|
||||
if !macCatalystSupportedTargets.isEmpty {
|
||||
zip(buildTargets, macCatalystSupportedTargets)
|
||||
.filter { $0.0.name == $0.1.name }
|
||||
.filter { $0.0.name == productName }
|
||||
.forEach { _ in
|
||||
if let macCatalystArchivePath = macCatalystArchivePath {
|
||||
frameworkpaths.append(self.frameworkPath(
|
||||
fromArchivePath: macCatalystArchivePath,
|
||||
productName: productName
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let deviceArchivePath = deviceArchivePath {
|
||||
frameworkpaths.append(self.frameworkPath(
|
||||
fromArchivePath: deviceArchivePath,
|
||||
productName: productName
|
||||
))
|
||||
}
|
||||
|
||||
logger.notice("Caching \(productName).xcframework")
|
||||
let xcframeworkPath = outputDirectory.appending(component: "\(productName).xcframework")
|
||||
try await self.xcodeBuildController.createXCFramework(
|
||||
frameworks: frameworkpaths,
|
||||
|
@ -135,6 +173,16 @@ public final class CacheXCFrameworkBuilder: CacheArtifactBuilding {
|
|||
}
|
||||
}
|
||||
|
||||
fileprivate func getMacCatalystTargets(graph: Graph, scheme: Scheme) -> [Target] {
|
||||
guard let buildTargets = scheme.buildAction?.targets else { return [] }
|
||||
let buildTargetsSet = Set(buildTargets.map(\.name))
|
||||
return graph.targets
|
||||
.flatMap(\.value)
|
||||
.filter { buildTargetsSet.contains($0.key) }
|
||||
.map(\.value)
|
||||
.filter(\.supportsCatalyst)
|
||||
}
|
||||
|
||||
fileprivate func deviceBuild(
|
||||
projectTarget: XcodeBuildTarget,
|
||||
scheme: String,
|
||||
|
@ -155,6 +203,25 @@ public final class CacheXCFrameworkBuilder: CacheArtifactBuilding {
|
|||
).printFormattedOutput()
|
||||
}
|
||||
|
||||
fileprivate func macCatalystBuild(
|
||||
projectTarget: XcodeBuildTarget,
|
||||
scheme: String,
|
||||
configuration: String,
|
||||
archivePath: AbsolutePath
|
||||
) async throws {
|
||||
try await xcodeBuildController.archive(
|
||||
projectTarget,
|
||||
scheme: scheme,
|
||||
clean: false,
|
||||
archivePath: archivePath,
|
||||
arguments: [
|
||||
.xcarg("SKIP_INSTALL", "NO"),
|
||||
.destination("platform=macOS,variant=Mac Catalyst"),
|
||||
.configuration(configuration),
|
||||
]
|
||||
).printFormattedOutput()
|
||||
}
|
||||
|
||||
fileprivate func simulatorBuild(
|
||||
projectTarget: XcodeBuildTarget,
|
||||
scheme: String,
|
||||
|
@ -205,3 +272,14 @@ extension CacheOutputType {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Target {
|
||||
fileprivate var supportsCatalyst: Bool {
|
||||
switch self.deploymentTarget {
|
||||
case let .iOS(_, devices, _) where devices.contains(.mac):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ public final class MockCacheArtifactBuilder: CacheArtifactBuilding {
|
|||
[(scheme: Scheme, projectTarget: XcodeBuildTarget, outputDirectory: AbsolutePath)]()
|
||||
public var stubbedBuildSchemeProjectError: Error?
|
||||
public func build(
|
||||
graph _: Graph,
|
||||
scheme: Scheme,
|
||||
projectTarget: XcodeBuildTarget,
|
||||
configuration _: String,
|
||||
|
|
|
@ -148,10 +148,12 @@ final class CacheController: CacheControlling {
|
|||
.filter { !($0.buildAction?.targets ?? []).isEmpty }
|
||||
|
||||
try await FileHandler.shared.inTemporaryDirectory { outputDirectory in
|
||||
|
||||
for scheme in binariesSchemes {
|
||||
let outputDirectory = outputDirectory.appending(component: scheme.name)
|
||||
try FileHandler.shared.createFolder(outputDirectory)
|
||||
try await self.artifactBuilder.build(
|
||||
graph: graph,
|
||||
scheme: scheme,
|
||||
projectTarget: XcodeBuildTarget(with: projectPath),
|
||||
configuration: cacheProfile.configuration,
|
||||
|
@ -165,6 +167,7 @@ final class CacheController: CacheControlling {
|
|||
let outputDirectory = outputDirectory.appending(component: scheme.name)
|
||||
try FileHandler.shared.createFolder(outputDirectory)
|
||||
try await self.bundleArtifactBuilder.build(
|
||||
graph: graph,
|
||||
scheme: scheme,
|
||||
projectTarget: XcodeBuildTarget(with: projectPath),
|
||||
configuration: cacheProfile.configuration,
|
||||
|
|
|
@ -32,9 +32,11 @@ final class CacheFrameworkBuilderIntegrationTests: TuistTestCase {
|
|||
let frameworksPath = try temporaryFixture("Frameworks")
|
||||
let projectPath = frameworksPath.appending(component: "Frameworks.xcodeproj")
|
||||
let scheme = Scheme.test(name: "iOS")
|
||||
let graph = Graph.test()
|
||||
|
||||
// When
|
||||
try await subject.build(
|
||||
graph: graph,
|
||||
scheme: scheme,
|
||||
projectTarget: XcodeBuildTarget(with: projectPath),
|
||||
configuration: "Debug",
|
||||
|
@ -58,9 +60,11 @@ final class CacheFrameworkBuilderIntegrationTests: TuistTestCase {
|
|||
let frameworksPath = try temporaryFixture("Frameworks")
|
||||
let projectPath = frameworksPath.appending(component: "Frameworks.xcodeproj")
|
||||
let scheme = Scheme.test(name: "macOS")
|
||||
let graph = Graph.test()
|
||||
|
||||
// When
|
||||
try await subject.build(
|
||||
graph: graph,
|
||||
scheme: scheme,
|
||||
projectTarget: XcodeBuildTarget(with: projectPath),
|
||||
configuration: "Debug",
|
||||
|
|
|
@ -35,9 +35,11 @@ final class CacheXCFrameworkBuilderIntegrationTests: TuistTestCase {
|
|||
let frameworksPath = try temporaryFixture("Frameworks")
|
||||
let projectPath = frameworksPath.appending(component: "Frameworks.xcodeproj")
|
||||
let scheme = Scheme.test(name: "iOS")
|
||||
let graph = Graph.test()
|
||||
|
||||
// When
|
||||
try await subject.build(
|
||||
graph: graph,
|
||||
scheme: scheme,
|
||||
projectTarget: XcodeBuildTarget(with: projectPath),
|
||||
configuration: "Debug",
|
||||
|
@ -62,11 +64,13 @@ final class CacheXCFrameworkBuilderIntegrationTests: TuistTestCase {
|
|||
let frameworksPath = try temporaryFixture("Frameworks")
|
||||
let projectPath = frameworksPath.appending(component: "Frameworks.xcodeproj")
|
||||
let scheme = Scheme.test(name: "iOS")
|
||||
let graph = Graph.test()
|
||||
|
||||
subject.cacheOutputType = .xcframework(.device)
|
||||
|
||||
// When
|
||||
try await subject.build(
|
||||
graph: graph,
|
||||
scheme: scheme,
|
||||
projectTarget: XcodeBuildTarget(with: projectPath),
|
||||
configuration: "Debug",
|
||||
|
@ -92,11 +96,13 @@ final class CacheXCFrameworkBuilderIntegrationTests: TuistTestCase {
|
|||
let frameworksPath = try temporaryFixture("Frameworks")
|
||||
let projectPath = frameworksPath.appending(component: "Frameworks.xcodeproj")
|
||||
let scheme = Scheme.test(name: "iOS")
|
||||
let graph = Graph.test()
|
||||
|
||||
subject.cacheOutputType = .xcframework(.simulator)
|
||||
|
||||
// When
|
||||
try await subject.build(
|
||||
graph: graph,
|
||||
scheme: scheme,
|
||||
projectTarget: XcodeBuildTarget(with: projectPath),
|
||||
configuration: "Debug",
|
||||
|
@ -115,15 +121,60 @@ final class CacheXCFrameworkBuilderIntegrationTests: TuistTestCase {
|
|||
try FileHandler.shared.delete(xcframeworkPath)
|
||||
}
|
||||
|
||||
func test_build_when_macCatalyst_framework() async throws {
|
||||
// Given
|
||||
let temporaryPath = try temporaryPath()
|
||||
let frameworksPath = try temporaryFixture("Frameworks")
|
||||
let projectPath = frameworksPath.appending(component: "Frameworks.xcodeproj")
|
||||
let scheme = Scheme.test(
|
||||
name: "iOS",
|
||||
buildAction: .test(targets: [TargetReference(projectPath: "/Project", name: "iOS")])
|
||||
)
|
||||
let graph = Graph.test(
|
||||
targets: [
|
||||
try AbsolutePath(validating: "/test"): [
|
||||
"iOS": Target.test(
|
||||
name: "iOS",
|
||||
deploymentTarget: .iOS("14.0", [.iphone, .ipad, .mac], supportsMacDesignedForIOS: true)
|
||||
),
|
||||
],
|
||||
]
|
||||
)
|
||||
|
||||
// When
|
||||
try await subject.build(
|
||||
graph: graph,
|
||||
scheme: scheme,
|
||||
projectTarget: XcodeBuildTarget(with: projectPath),
|
||||
configuration: "Debug",
|
||||
osVersion: nil,
|
||||
deviceName: nil,
|
||||
into: temporaryPath
|
||||
)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(FileHandler.shared.glob(temporaryPath, glob: "*.xcframework").count, 1)
|
||||
let xcframeworkPath = try XCTUnwrap(FileHandler.shared.glob(temporaryPath, glob: "*.xcframework").first)
|
||||
let infoPlist = try infoPlist(xcframeworkPath: xcframeworkPath)
|
||||
XCTAssertTrue(infoPlist.availableLibraries.contains(where: { $0.identifier == "ios-arm64_x86_64-maccatalyst" }))
|
||||
XCTAssertNotNil(infoPlist.availableLibraries.first(where: { $0.supportedArchitectures.contains("arm64") }))
|
||||
XCTAssertNotNil(infoPlist.availableLibraries.first(where: { $0.supportedArchitectures.contains("x86_64") }))
|
||||
XCTAssertTrue(infoPlist.availableLibraries.allSatisfy { $0.supportedPlatform == "ios" })
|
||||
XCTAssertTrue(infoPlist.availableLibraries.contains(where: { $0.supportedPlatformVariant == "maccatalyst" }))
|
||||
try FileHandler.shared.delete(xcframeworkPath)
|
||||
}
|
||||
|
||||
func test_build_when_macOS_framework() async throws {
|
||||
// Given
|
||||
let temporaryPath = try temporaryPath()
|
||||
let frameworksPath = try temporaryFixture("Frameworks")
|
||||
let projectPath = frameworksPath.appending(component: "Frameworks.xcodeproj")
|
||||
let scheme = Scheme.test(name: "macOS")
|
||||
let graph = Graph.test()
|
||||
|
||||
// When
|
||||
try await subject.build(
|
||||
graph: graph,
|
||||
scheme: scheme,
|
||||
projectTarget: XcodeBuildTarget(with: projectPath),
|
||||
configuration: "Debug",
|
||||
|
@ -149,9 +200,11 @@ final class CacheXCFrameworkBuilderIntegrationTests: TuistTestCase {
|
|||
let frameworksPath = try temporaryFixture("Frameworks")
|
||||
let projectPath = frameworksPath.appending(component: "Frameworks.xcodeproj")
|
||||
let scheme = Scheme.test(name: "tvOS")
|
||||
let graph = Graph.test()
|
||||
|
||||
// When
|
||||
try await subject.build(
|
||||
graph: graph,
|
||||
scheme: scheme,
|
||||
projectTarget: XcodeBuildTarget(with: projectPath),
|
||||
configuration: "Debug",
|
||||
|
@ -176,9 +229,11 @@ final class CacheXCFrameworkBuilderIntegrationTests: TuistTestCase {
|
|||
let frameworksPath = try temporaryFixture("Frameworks")
|
||||
let projectPath = frameworksPath.appending(component: "Frameworks.xcodeproj")
|
||||
let scheme = Scheme.test(name: "watchOS")
|
||||
let graph = Graph.test()
|
||||
|
||||
// When
|
||||
try await subject.build(
|
||||
graph: graph,
|
||||
scheme: scheme,
|
||||
projectTarget: XcodeBuildTarget(with: projectPath),
|
||||
configuration: "Debug",
|
||||
|
@ -204,9 +259,11 @@ final class CacheXCFrameworkBuilderIntegrationTests: TuistTestCase {
|
|||
let frameworksPath = try temporaryFixture("Frameworks")
|
||||
let projectPath = frameworksPath.appending(component: "Frameworks.xcodeproj")
|
||||
let scheme = Scheme.test(name: "Documentation-iOS")
|
||||
let graph = Graph.test()
|
||||
|
||||
// When
|
||||
try await subject.build(
|
||||
graph: graph,
|
||||
scheme: scheme,
|
||||
projectTarget: XcodeBuildTarget(with: projectPath),
|
||||
configuration: "Debug",
|
||||
|
|
Loading…
Reference in New Issue