diff --git a/Sources/SWBCore/SpecImplementations/Tools/ProcessXCFrameworkLibrary.swift b/Sources/SWBCore/SpecImplementations/Tools/ProcessXCFrameworkLibrary.swift index 3b0ae0c1e..7f18dda69 100644 --- a/Sources/SWBCore/SpecImplementations/Tools/ProcessXCFrameworkLibrary.swift +++ b/Sources/SWBCore/SpecImplementations/Tools/ProcessXCFrameworkLibrary.swift @@ -29,7 +29,7 @@ public final class ProcessXCFrameworkLibrarySpec: CommandLineToolSpec, SpecImple fatalError("unexpected direct invocation") } - public func constructTasks(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate, platform: String, environment: String?, outputDirectory copyLibraryToPath: Path, libraryPath: Path, expectedSignatures: [String]?) async { + public func constructTasks(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate, platform: String, environment: String?, libraryIdentifier: String, outputDirectory copyLibraryToPath: Path, libraryPath: Path, expectedSignatures: [String]?) async { let xcframeworkPath = cbc.input.absolutePath let inputs: [any PlannedNode] = [delegate.createDirectoryTreeNode(xcframeworkPath)] + cbc.commandOrderingInputs @@ -43,6 +43,8 @@ public final class ProcessXCFrameworkLibrarySpec: CommandLineToolSpec, SpecImple commandLine.append(contentsOf: ["--environment", env]) } + commandLine.append(contentsOf: ["--library-identifier", libraryIdentifier]) + // Use the calculated paths above to inform the process step what to copy instead of needing the action to perform the same work. commandLine.append(contentsOf: ["--target-path", copyLibraryToPath.str]) diff --git a/Sources/SWBTaskConstruction/TaskProducers/WorkspaceTaskProducers/XCFrameworkTaskProducer.swift b/Sources/SWBTaskConstruction/TaskProducers/WorkspaceTaskProducers/XCFrameworkTaskProducer.swift index 26c9719b2..e0b8e39fe 100644 --- a/Sources/SWBTaskConstruction/TaskProducers/WorkspaceTaskProducers/XCFrameworkTaskProducer.swift +++ b/Sources/SWBTaskConstruction/TaskProducers/WorkspaceTaskProducers/XCFrameworkTaskProducer.swift @@ -136,7 +136,7 @@ final class XCFrameworkTaskProducer: StandardTaskProducer, TaskProducer { } access(path: config.path) - await context.processXCFrameworkLibrarySpec.constructTasks(CommandBuildContext(producer: context, scope: scope, inputs: [FileToBuild(context: context, absolutePath: config.path)], outputs: config.outputs, commandOrderingInputs: outputDirectoryIsBuildDirectory ? [delegate.createBuildDirectoryNode(absolutePath: config.outputDirectory)] : []), delegate, platform: config.platform, environment: config.environment, outputDirectory: config.outputDirectory, libraryPath: config.libraryPath, expectedSignatures: expectedSignatures) + await context.processXCFrameworkLibrarySpec.constructTasks(CommandBuildContext(producer: context, scope: scope, inputs: [FileToBuild(context: context, absolutePath: config.path)], outputs: config.outputs, commandOrderingInputs: outputDirectoryIsBuildDirectory ? [delegate.createBuildDirectoryNode(absolutePath: config.outputDirectory)] : []), delegate, platform: config.platform, environment: config.environment, libraryIdentifier: config.libraryIdentifier, outputDirectory: config.outputDirectory, libraryPath: config.libraryPath, expectedSignatures: expectedSignatures) if scope.evaluate(BuiltinMacros.ENABLE_SIGNATURE_AGGREGATION) { let output = config.outputDirectory.join("\(config.path.basename)-\(config.platform)\(config.environment.map { "-\($0)"} ?? "").signature") diff --git a/Sources/SWBTaskConstruction/XCFrameworkContext.swift b/Sources/SWBTaskConstruction/XCFrameworkContext.swift index 465aab0fd..4c953f87e 100644 --- a/Sources/SWBTaskConstruction/XCFrameworkContext.swift +++ b/Sources/SWBTaskConstruction/XCFrameworkContext.swift @@ -26,6 +26,7 @@ final class XCFrameworkContext: Sendable { package let path: Path package let platform: String package let environment: String? + package let libraryIdentifier: String package let outputDirectory: Path package let libraryPath: Path package let outputs: [Path] @@ -63,7 +64,7 @@ final class XCFrameworkContext: Sendable { if let (library, outputDirectory) = block(xcframework) { let outputs = try xcframework.copy(library: library, from: path, to: outputDirectory, fs: workspaceContext.fs, dryRun: true) - state.copyConfigurations[Key(path: path, guid: target.guid)] = XCFrameworkCopyConfiguration(path: path, platform: library.supportedPlatform, environment: library.platformVariant, outputDirectory: outputDirectory, libraryPath: library.libraryPath, outputs: outputs, expectedSignature: expectedSignature) + state.copyConfigurations[Key(path: path, guid: target.guid)] = XCFrameworkCopyConfiguration(path: path, platform: library.supportedPlatform, environment: library.platformVariant, libraryIdentifier: library.libraryIdentifier, outputDirectory: outputDirectory, libraryPath: library.libraryPath, outputs: outputs, expectedSignature: expectedSignature) } } } diff --git a/Sources/SWBTaskExecution/TaskActions/ProcessXCFrameworkTaskAction.swift b/Sources/SWBTaskExecution/TaskActions/ProcessXCFrameworkTaskAction.swift index aacfd3fbd..f7e560b6c 100644 --- a/Sources/SWBTaskExecution/TaskActions/ProcessXCFrameworkTaskAction.swift +++ b/Sources/SWBTaskExecution/TaskActions/ProcessXCFrameworkTaskAction.swift @@ -29,6 +29,7 @@ public final class ProcessXCFrameworkTaskAction: TaskAction { var xcframeworkPath: Path? var platform: String? var environment: String? + var libraryIdentifier: String? var targetPath: Path? var expectedSignatures: [String] = [] var skipSignatureValidation: Bool = false @@ -56,6 +57,13 @@ public final class ProcessXCFrameworkTaskAction: TaskAction { } environment = value + case "--library-identifier": + guard let value = generator.next() else { + outputDelegate.emitError("`--library-identifier` requires a parameter") + return .failed + } + libraryIdentifier = value + case "--target-path": guard let value = generator.next() else { outputDelegate.emitError("`--target-path` requires a parameter") @@ -101,10 +109,17 @@ public final class ProcessXCFrameworkTaskAction: TaskAction { let platformDisplayName = BuildVersion.Platform(platform: plat, environment: environment)?.displayName(infoLookup: executionDelegate.infoLookup) ?? ("\(plat)" + (environment.flatMap { "-\($0)" } ?? "")) - // Find a library in the XCFramework which is compatible with the current platform. - // Note that we don't validate supported architectures here because this task copies the xcframework's contents for potential use by multiple targets which may have different architecture settings. - guard let library = xcframework.findLibrary(platform: plat, platformVariant: environment ?? "") else { - outputDelegate.emitError("While building for \(platformDisplayName), no library for this platform was found in '\(xcframeworkName)'.") + // Task construction selected the library using the target's architectures and passes its identifier. + // Resolve by identifier so we copy that exact slice: multiple per-arch slices (e.g. linux-aarch64 and + // linux-x86_64) share a platform and library file name, so platform alone cannot disambiguate them here. + guard let libraryIdentifier else { + outputDelegate.emitError("--library-identifier is a required argument") + return .failed + } + let matches = xcframework.libraries.filter { $0.libraryIdentifier == libraryIdentifier && $0.supportedPlatform == plat } + guard let library = matches.only else { + let problem = matches.isEmpty ? "no library was" : "\(matches.count) libraries were" + outputDelegate.emitError("While building for \(platformDisplayName), \(problem) found with identifier '\(libraryIdentifier)' in '\(xcframeworkName)'.") return .failed } diff --git a/Tests/SWBBuildSystemTests/MergeableLibrariesBuildOperationTests.swift b/Tests/SWBBuildSystemTests/MergeableLibrariesBuildOperationTests.swift index 13508bfbc..8040aa783 100644 --- a/Tests/SWBBuildSystemTests/MergeableLibrariesBuildOperationTests.swift +++ b/Tests/SWBBuildSystemTests/MergeableLibrariesBuildOperationTests.swift @@ -1139,10 +1139,10 @@ fileprivate struct MergeableLibrariesBuildOperationTests: CoreBasedTests { results.consumeTasksMatchingRuleTypes(taskTypesToExclude) results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(fwkBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Sources/\(fwkBaseName).xcframework", "--platform", "ios", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Sources/\(fwkBaseName).xcframework", "--platform", "ios", "--library-identifier", "ios-arm64", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(libBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Sources/\(libBaseName).xcframework", "--platform", "ios", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Sources/\(libBaseName).xcframework", "--platform", "ios", "--library-identifier", "ios-arm64", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } // Check the tasks in the target. @@ -1252,10 +1252,10 @@ fileprivate struct MergeableLibrariesBuildOperationTests: CoreBasedTests { results.consumeTasksMatchingRuleTypes(taskTypesToExclude) results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(fwkBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Sources/\(fwkBaseName).xcframework", "--platform", "ios", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Sources/\(fwkBaseName).xcframework", "--platform", "ios", "--library-identifier", "ios-arm64", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(libBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Sources/\(libBaseName).xcframework", "--platform", "ios", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Sources/\(libBaseName).xcframework", "--platform", "ios", "--library-identifier", "ios-arm64", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } // Check the tasks in the target. @@ -1530,10 +1530,10 @@ fileprivate struct MergeableLibrariesBuildOperationTests: CoreBasedTests { results.consumeTasksMatchingRuleTypes(taskTypesToExclude) results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(fwkBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Sources/\(fwkBaseName).xcframework", "--platform", "ios", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Sources/\(fwkBaseName).xcframework", "--platform", "ios", "--library-identifier", "ios-arm64", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(libBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Sources/\(libBaseName).xcframework", "--platform", "ios", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Sources/\(libBaseName).xcframework", "--platform", "ios", "--library-identifier", "ios-arm64", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } // Check MergedFwkTarget diff --git a/Tests/SWBTaskConstructionTests/MergeableLibraryTests.swift b/Tests/SWBTaskConstructionTests/MergeableLibraryTests.swift index 46a1b9ae3..4679ae464 100644 --- a/Tests/SWBTaskConstructionTests/MergeableLibraryTests.swift +++ b/Tests/SWBTaskConstructionTests/MergeableLibraryTests.swift @@ -1407,10 +1407,10 @@ fileprivate struct MergeableLibraryTests: CoreBasedTests { results.consumeTasksMatchingRuleTypes(tasksToIgnore) results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(fwkBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(fwkBaseName).xcframework", "--platform", "ios", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(fwkBaseName).xcframework", "--platform", "ios", "--library-identifier", "\(iosLibraryIdentifier)", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(libBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(libBaseName).xcframework", "--platform", "ios", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(libBaseName).xcframework", "--platform", "ios", "--library-identifier", "\(iosLibraryIdentifier)", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTarget("App") { target in @@ -1505,10 +1505,10 @@ fileprivate struct MergeableLibraryTests: CoreBasedTests { results.consumeTasksMatchingRuleTypes(tasksToIgnore) results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(fwkBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(fwkBaseName).xcframework", "--platform", "ios", "--environment", "simulator", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(fwkBaseName).xcframework", "--platform", "ios", "--environment", "simulator", "--library-identifier", "\(iossimLibraryIdentifier)", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(libBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(libBaseName).xcframework", "--platform", "ios", "--environment", "simulator", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(libBaseName).xcframework", "--platform", "ios", "--environment", "simulator", "--library-identifier", "\(iossimLibraryIdentifier)", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTarget("App") { target in @@ -1586,10 +1586,10 @@ fileprivate struct MergeableLibraryTests: CoreBasedTests { results.consumeTasksMatchingRuleTypes(tasksToIgnore) results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(fwkBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(fwkBaseName).xcframework", "--platform", "ios", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(fwkBaseName).xcframework", "--platform", "ios", "--library-identifier", "\(iosLibraryIdentifier)", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(libBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(libBaseName).xcframework", "--platform", "ios", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(libBaseName).xcframework", "--platform", "ios", "--library-identifier", "\(iosLibraryIdentifier)", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTarget("App") { target in @@ -1659,10 +1659,10 @@ fileprivate struct MergeableLibraryTests: CoreBasedTests { results.consumeTasksMatchingRuleTypes(tasksToIgnore) results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(fwkBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(fwkBaseName).xcframework", "--platform", "ios", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(fwkBaseName).xcframework", "--platform", "ios", "--library-identifier", "\(iosLibraryIdentifier)", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(libBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(libBaseName).xcframework", "--platform", "ios", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(libBaseName).xcframework", "--platform", "ios", "--library-identifier", "\(iosLibraryIdentifier)", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTarget("App") { target in @@ -1739,10 +1739,10 @@ fileprivate struct MergeableLibraryTests: CoreBasedTests { results.consumeTasksMatchingRuleTypes() results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(fwkBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(fwkBaseName).xcframework", "--platform", "ios", "--environment", "simulator", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(fwkBaseName).xcframework", "--platform", "ios", "--environment", "simulator", "--library-identifier", "\(iossimLibraryIdentifier)", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(libBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(libBaseName).xcframework", "--platform", "ios", "--environment", "simulator", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(libBaseName).xcframework", "--platform", "ios", "--environment", "simulator", "--library-identifier", "\(iossimLibraryIdentifier)", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTarget("App") { target in @@ -1895,10 +1895,10 @@ fileprivate struct MergeableLibraryTests: CoreBasedTests { results.consumeTasksMatchingRuleTypes() results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(fwkBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(fwkBaseName).xcframework", "--platform", "macos", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(fwkBaseName).xcframework", "--platform", "macos", "--library-identifier", "\(macosLibraryIdentifier)", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(libBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(libBaseName).xcframework", "--platform", "macos", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(libBaseName).xcframework", "--platform", "macos", "--library-identifier", "\(macosLibraryIdentifier)", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTarget("App") { target in @@ -1993,10 +1993,10 @@ fileprivate struct MergeableLibraryTests: CoreBasedTests { results.consumeTasksMatchingRuleTypes() results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(fwkBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(fwkBaseName).xcframework", "--platform", "macos", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(fwkBaseName).xcframework", "--platform", "macos", "--library-identifier", "\(macosLibraryIdentifier)", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTask(.matchRuleType("ProcessXCFramework"), .matchRuleItemBasename("\(libBaseName).xcframework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(libBaseName).xcframework", "--platform", "macos", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/\(libBaseName).xcframework", "--platform", "macos", "--library-identifier", "\(macosLibraryIdentifier)", "--target-path", "\(BUILT_PRODUCTS_DIR)"]) } results.checkTarget("App") { target in diff --git a/Tests/SWBTaskConstructionTests/XCFrameworkTaskConstructionTests.swift b/Tests/SWBTaskConstructionTests/XCFrameworkTaskConstructionTests.swift index b298ca1bd..f6dc4924c 100644 --- a/Tests/SWBTaskConstructionTests/XCFrameworkTaskConstructionTests.swift +++ b/Tests/SWBTaskConstructionTests/XCFrameworkTaskConstructionTests.swift @@ -78,7 +78,7 @@ fileprivate struct XCFrameworkTaskConstructionTests: CoreBasedTests { try await tester.checkBuild(BuildParameters(action: .build, configuration: "Debug"), runDestination: .macOS, fs: fs) { results in var processSupportXCFrameworkTask: (any PlannedTask)? results.checkTask(.matchRuleType("ProcessXCFramework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--target-path", "\(SRCROOT)/build/Debug"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--library-identifier", "arm64-apple-macos\(core.loadSDK(.macOS).defaultDeploymentTarget)", "--target-path", "\(SRCROOT)/build/Debug"]) task.checkInputs([ .path("\(SRCROOT)/Support.xcframework"), .path("\(SRCROOT)/build/Debug") @@ -189,7 +189,7 @@ fileprivate struct XCFrameworkTaskConstructionTests: CoreBasedTests { var processSupportXCFrameworkTask: (any PlannedTask)? results.checkTask(.matchRule(["ProcessXCFramework", "\(SRCROOT)/Support.xcframework", "\(SRCROOT)/build/Debug/Support.framework", "macos"])) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--target-path", "\(SRCROOT)/build/Debug"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--library-identifier", "arm64-apple-macos\(core.loadSDK(.macOS).defaultDeploymentTarget)", "--target-path", "\(SRCROOT)/build/Debug"]) task.checkInputs([ .path("\(SRCROOT)/Support.xcframework"), .path("\(SRCROOT)/build/Debug") @@ -224,7 +224,7 @@ fileprivate struct XCFrameworkTaskConstructionTests: CoreBasedTests { var processSupportXCFrameworkTask: (any PlannedTask)? results.checkTask(.matchRule(["ProcessXCFramework", "\(SRCROOT)/Support.xcframework", "\(SRCROOT)/build/Debug-driverkit/Support.framework", "driverkit"])) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "driverkit", "--target-path", "\(SRCROOT)/build/Debug-driverkit"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "driverkit", "--library-identifier", "arm64-apple-driverkit\(core.loadSDK(.driverKit).defaultDeploymentTarget)", "--target-path", "\(SRCROOT)/build/Debug-driverkit"]) task.checkInputs([ .path("\(SRCROOT)/Support.xcframework"), .path("\(SRCROOT)/build/Debug-driverkit") @@ -318,7 +318,7 @@ fileprivate struct XCFrameworkTaskConstructionTests: CoreBasedTests { try await tester.checkBuild(BuildParameters(action: .install, configuration: "Release"), runDestination: .macOS, fs: fs) { results in var processSupportXCFrameworkTask: (any PlannedTask)? results.checkTask(.matchRuleType("ProcessXCFramework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--target-path", "\(SRCROOT)/build/Release"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--library-identifier", "arm64-apple-macos\(core.loadSDK(.macOS).defaultDeploymentTarget)", "--target-path", "\(SRCROOT)/build/Release"]) task.checkInputs([ .path("\(SRCROOT)/Support.xcframework"), .path("\(SRCROOT)/build/Release") @@ -413,7 +413,7 @@ fileprivate struct XCFrameworkTaskConstructionTests: CoreBasedTests { try await tester.checkBuild(BuildParameters(action: .build, configuration: "Debug"), runDestination: .macOS, fs: fs) { results in var processSupportXCFrameworkTask: (any PlannedTask)? results.checkTask(.matchRuleType("ProcessXCFramework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--target-path", "\(SRCROOT)/build/Debug"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--library-identifier", "arm64-apple-macos\(core.loadSDK(.macOS).defaultDeploymentTarget)", "--target-path", "\(SRCROOT)/build/Debug"]) task.checkInputs([ .path("\(SRCROOT)/Support.xcframework"), .path("\(SRCROOT)/build/Debug") @@ -553,7 +553,7 @@ fileprivate struct XCFrameworkTaskConstructionTests: CoreBasedTests { await tester.checkBuild(BuildParameters(action: .build, configuration: "Debug"), runDestination: .macOS, fs: fs) { results in results.checkTask(.matchRule(["ProcessXCFramework", "\(SRCROOT)/Support.xcframework", "\(SRCROOT)/build/Debug/Support.framework", "macos"])) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--target-path", "\(SRCROOT)/build/Debug"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--library-identifier", "arm64-apple-macos\(core.loadSDK(.macOS).defaultDeploymentTarget)", "--target-path", "\(SRCROOT)/build/Debug"]) task.checkInputs([ .path("\(SRCROOT)/Support.xcframework"), .path("\(SRCROOT)/build/Debug") @@ -625,7 +625,7 @@ fileprivate struct XCFrameworkTaskConstructionTests: CoreBasedTests { try await tester.checkBuild(BuildParameters(action: .build, configuration: "Debug"), runDestination: .macOS, fs: fs) { results in var processSupportXCFrameworkTask: (any PlannedTask)? results.checkTask(.matchRuleType("ProcessXCFramework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--target-path", "\(SRCROOT)/build/Debug"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--library-identifier", "arm64-apple-macos\(core.loadSDK(.macOS).defaultDeploymentTarget)", "--target-path", "\(SRCROOT)/build/Debug"]) task.checkInputs([ .path("\(SRCROOT)/Support.xcframework"), .path("\(SRCROOT)/build/Debug") @@ -726,7 +726,7 @@ fileprivate struct XCFrameworkTaskConstructionTests: CoreBasedTests { try await tester.checkBuild(BuildParameters(action: .build, configuration: "Debug"), runDestination: .macOS, fs: fs) { results in var processSupportXCFrameworkTask: (any PlannedTask)? results.checkTask(.matchRuleType("ProcessXCFramework")) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--target-path", "\(SRCROOT)/build/Debug"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--library-identifier", "arm64-apple-macos\(core.loadSDK(.macOS).defaultDeploymentTarget)", "--target-path", "\(SRCROOT)/build/Debug"]) task.checkInputs([ .path("\(SRCROOT)/Support.xcframework"), .path("\(SRCROOT)/build/Debug") @@ -963,7 +963,7 @@ fileprivate struct XCFrameworkTaskConstructionTests: CoreBasedTests { try await tester.checkBuild(BuildParameters(action: .build, configuration: "Debug"), runDestination: .macOS, fs: fs) { results in var processExtrasXCFrameworkTask: (any PlannedTask)? results.checkTask(.matchRule(["ProcessXCFramework", "\(SRCROOT)/Extras.xcframework", "\(SRCROOT)/build/Debug-iphoneos/Extras.framework", "ios"])) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Extras.xcframework", "--platform", "ios", "--target-path", "\(SRCROOT)/build/Debug-iphoneos"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Extras.xcframework", "--platform", "ios", "--library-identifier", "arm64-apple-iphoneos\(core.loadSDK(.iOS).defaultDeploymentTarget)", "--target-path", "\(SRCROOT)/build/Debug-iphoneos"]) task.checkInputs([ .path("\(SRCROOT)/Extras.xcframework"), .path("\(SRCROOT)/build/Debug-iphoneos") @@ -978,7 +978,7 @@ fileprivate struct XCFrameworkTaskConstructionTests: CoreBasedTests { var processSupportXCFrameworkTask: (any PlannedTask)? results.checkTask(.matchRule(["ProcessXCFramework", "\(SRCROOT)/Support.xcframework", "\(SRCROOT)/build/Debug-iphoneos/Support.framework", "ios"])) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "ios", "--target-path", "\(SRCROOT)/build/Debug-iphoneos"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "ios", "--library-identifier", "arm64-apple-iphoneos\(core.loadSDK(.iOS).defaultDeploymentTarget)", "--target-path", "\(SRCROOT)/build/Debug-iphoneos"]) task.checkInputs([ .path("\(SRCROOT)/Support.xcframework"), .path("\(SRCROOT)/build/Debug-iphoneos") @@ -1114,7 +1114,7 @@ fileprivate struct XCFrameworkTaskConstructionTests: CoreBasedTests { try await tester.checkBuild(BuildParameters(action: .build, configuration: "Debug"), runDestination: .macOS, fs: fs) { results in var processSupportXCFrameworkTask: (any PlannedTask)? results.checkTask(.matchRule(["ProcessXCFramework", "\(SRCROOT)/Support.xcframework", "\(SRCROOT)/build/Debug/Support.framework", "macos"])) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--target-path", "\(SRCROOT)/build/Debug"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--library-identifier", "arm64-apple-macos\(core.loadSDK(.macOS).defaultDeploymentTarget)", "--target-path", "\(SRCROOT)/build/Debug"]) task.checkInputs([ .path("\(SRCROOT)/Support.xcframework"), .path("\(SRCROOT)/build/Debug") @@ -1129,7 +1129,7 @@ fileprivate struct XCFrameworkTaskConstructionTests: CoreBasedTests { var processExtrasXCFrameworkTask: (any PlannedTask)? results.checkTask(.matchRule(["ProcessXCFramework", "\(SRCROOT)/Extras.xcframework", "\(SRCROOT)/build/Debug/Extras.framework", "macos"])) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Extras.xcframework", "--platform", "macos", "--target-path", "\(SRCROOT)/build/Debug"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Extras.xcframework", "--platform", "macos", "--library-identifier", "arm64-apple-macos\(core.loadSDK(.macOS).defaultDeploymentTarget)", "--target-path", "\(SRCROOT)/build/Debug"]) task.checkInputs([ .path("\(SRCROOT)/Extras.xcframework"), .path("\(SRCROOT)/build/Debug") @@ -1178,7 +1178,7 @@ fileprivate struct XCFrameworkTaskConstructionTests: CoreBasedTests { try results.checkTarget("App2") { target in var processSupportXCFrameworkTask: (any PlannedTask)? results.checkTask(.matchRule(["ProcessXCFramework", "\(SRCROOT)/Support.xcframework", "\(SRCROOT)/build/Debug/Support.framework", "macos"])) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--target-path", "\(SRCROOT)/build/Debug"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Support.xcframework", "--platform", "macos", "--library-identifier", "arm64-apple-macos\(core.loadSDK(.macOS).defaultDeploymentTarget)", "--target-path", "\(SRCROOT)/build/Debug"]) task.checkInputs([ .path("\(SRCROOT)/Support.xcframework"), .path("\(SRCROOT)/build/Debug") @@ -1193,7 +1193,7 @@ fileprivate struct XCFrameworkTaskConstructionTests: CoreBasedTests { var processExtrasXCFrameworkTask: (any PlannedTask)? results.checkTask(.matchRule(["ProcessXCFramework", "\(SRCROOT)/Extras.xcframework", "\(SRCROOT)/build/Debug/Extras.framework", "macos"])) { task in - task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Extras.xcframework", "--platform", "macos", "--target-path", "\(SRCROOT)/build/Debug"]) + task.checkCommandLine(["builtin-process-xcframework", "--xcframework", "\(SRCROOT)/Extras.xcframework", "--platform", "macos", "--library-identifier", "arm64-apple-macos\(core.loadSDK(.macOS).defaultDeploymentTarget)", "--target-path", "\(SRCROOT)/build/Debug"]) task.checkInputs([ .path("\(SRCROOT)/Extras.xcframework"), .path("\(SRCROOT)/build/Debug") diff --git a/Tests/SWBTaskExecutionTests/ProcessXCFrameworkLibraryTaskActionTests.swift b/Tests/SWBTaskExecutionTests/ProcessXCFrameworkLibraryTaskActionTests.swift index 9e7570aaa..8c561fd9f 100644 --- a/Tests/SWBTaskExecutionTests/ProcessXCFrameworkLibraryTaskActionTests.swift +++ b/Tests/SWBTaskExecutionTests/ProcessXCFrameworkLibraryTaskActionTests.swift @@ -47,7 +47,7 @@ fileprivate struct ProcessXCFrameworkLibraryTaskActionTests: CoreBasedTests { try fs.createDirectory(Path("\(DSTROOT)"), recursive: true) - let task = Task(forTarget: nil, ruleInfo: [], commandLine: ["", "--xcframework", supportXCFrameworkPath.str, "--platform", "macos", "--target-path", DSTROOT], workingDirectory: Path(DSTROOT), action: ProcessXCFrameworkTaskAction()) + let task = Task(forTarget: nil, ruleInfo: [], commandLine: ["", "--xcframework", supportXCFrameworkPath.str, "--platform", "macos", "--library-identifier", "x86_64-apple-macos10.15", "--target-path", DSTROOT], workingDirectory: Path(DSTROOT), action: ProcessXCFrameworkTaskAction()) guard let result = await task.action?.performTaskAction(task, dynamicExecutionDelegate: MockDynamicTaskExecutionDelegate(), executionDelegate: executionDelegate, clientDelegate: MockTaskExecutionClientDelegate(), outputDelegate: outputDelegate) else { Issue.record("No result was returned.") return @@ -92,7 +92,7 @@ fileprivate struct ProcessXCFrameworkLibraryTaskActionTests: CoreBasedTests { try fs.createDirectory(Path("\(DSTROOT)"), recursive: true) - let task = Task(forTarget: nil, ruleInfo: [], commandLine: ["", "--xcframework", supportXCFrameworkPath.str, "--platform", "macos", "--target-path", DSTROOT], workingDirectory: Path(DSTROOT), action: ProcessXCFrameworkTaskAction()) + let task = Task(forTarget: nil, ruleInfo: [], commandLine: ["", "--xcframework", supportXCFrameworkPath.str, "--platform", "macos", "--library-identifier", "x86_64-apple-macos10.15", "--target-path", DSTROOT], workingDirectory: Path(DSTROOT), action: ProcessXCFrameworkTaskAction()) guard let result = await task.action?.performTaskAction(task, dynamicExecutionDelegate: MockDynamicTaskExecutionDelegate(), executionDelegate: executionDelegate, clientDelegate: MockTaskExecutionClientDelegate(), outputDelegate: outputDelegate) else { Issue.record("No result was returned.") return @@ -115,6 +115,48 @@ fileprivate struct ProcessXCFrameworkLibraryTaskActionTests: CoreBasedTests { } } + @Test + func copyForLinuxLibraryByIdentifier() async throws { + let fs = localFS + let executionDelegate = MockExecutionDelegate(fs: fs) + let outputDelegate = MockTaskOutputDelegate() + + try await withTemporaryDirectory { tmpDir in + let SRCROOT = tmpDir.join("src").str + let DSTROOT = tmpDir.join("dst").str + + try fs.createDirectory(Path(SRCROOT), recursive: true) + + // Two per-arch slices sharing a platform and library file name, with the aarch64 slice listed first. + let supportXCFramework = try XCFramework(version: Version(1, 0), libraries: [ + XCFramework.Library(libraryIdentifier: "linux-aarch64", supportedPlatform: "linux", supportedArchitectures: ["aarch64"], platformVariant: nil, libraryPath: Path("libsample.so"), binaryPath: Path("libsample.so"), headersPath: nil, debugSymbolsPath: nil), + XCFramework.Library(libraryIdentifier: "linux-x86_64", supportedPlatform: "linux", supportedArchitectures: ["x86_64"], platformVariant: nil, libraryPath: Path("libsample.so"), binaryPath: Path("libsample.so"), headersPath: nil, debugSymbolsPath: nil), + ]) + let supportXCFrameworkPath = Path(SRCROOT).join("libsample.xcframework") + try fs.createDirectory(supportXCFrameworkPath, recursive: true) + // Write the structure by hand; `writeXCFramework` compiles a real library and only supports Apple platforms. + try fs.write(supportXCFrameworkPath.join("Info.plist"), contents: ByteString(try supportXCFramework.serialize())) + for slice in supportXCFramework.libraries { + let sliceRoot = supportXCFrameworkPath.join(slice.libraryIdentifier) + try fs.createDirectory(sliceRoot, recursive: true) + try fs.write(sliceRoot.join(slice.libraryPath), contents: ByteString(encodingAsUTF8: slice.libraryIdentifier)) + } + + try fs.createDirectory(Path("\(DSTROOT)"), recursive: true) + + let task = Task(forTarget: nil, ruleInfo: [], commandLine: ["", "--xcframework", supportXCFrameworkPath.str, "--platform", "linux", "--library-identifier", "linux-x86_64", "--target-path", DSTROOT], workingDirectory: Path(DSTROOT), action: ProcessXCFrameworkTaskAction()) + guard let result = await task.action?.performTaskAction(task, dynamicExecutionDelegate: MockDynamicTaskExecutionDelegate(), executionDelegate: executionDelegate, clientDelegate: MockTaskExecutionClientDelegate(), outputDelegate: outputDelegate) else { + Issue.record("No result was returned.") + return + } + + #expect(result == .succeeded) + #expect(outputDelegate.messages == []) + // The x86_64 slice is copied, not the first-listed aarch64 slice. + #expect(try fs.read(Path(DSTROOT).join("libsample.so")) == ByteString(encodingAsUTF8: "linux-x86_64")) + } + } + @Test func errorMessageForMissingFramework() async throws { let fs = localFS @@ -141,7 +183,7 @@ fileprivate struct ProcessXCFrameworkLibraryTaskActionTests: CoreBasedTests { let expectedLibraryPath = supportXCFrameworkPath.join("x86_64-apple-macos10.15/Support.framework") try fs.removeDirectory(expectedLibraryPath) - let task = Task(forTarget: nil, ruleInfo: [], commandLine: ["", "--xcframework", supportXCFrameworkPath.str, "--platform", "macos", "--target-path", DSTROOT], workingDirectory: Path(DSTROOT), action: ProcessXCFrameworkTaskAction()) + let task = Task(forTarget: nil, ruleInfo: [], commandLine: ["", "--xcframework", supportXCFrameworkPath.str, "--platform", "macos", "--library-identifier", "x86_64-apple-macos10.15", "--target-path", DSTROOT], workingDirectory: Path(DSTROOT), action: ProcessXCFrameworkTaskAction()) guard let result = await task.action?.performTaskAction(task, dynamicExecutionDelegate: MockDynamicTaskExecutionDelegate(), executionDelegate: executionDelegate, clientDelegate: MockTaskExecutionClientDelegate(), outputDelegate: outputDelegate) else { Issue.record("No result was returned.") return