diff --git a/Fixtures/Miscellaneous/Plugins/CommandPluginTestStub/Package.swift b/Fixtures/Miscellaneous/Plugins/CommandPluginTestStub/Package.swift index fe69a01f0b5..dcbf3a5e017 100644 --- a/Fixtures/Miscellaneous/Plugins/CommandPluginTestStub/Package.swift +++ b/Fixtures/Miscellaneous/Plugins/CommandPluginTestStub/Package.swift @@ -19,8 +19,21 @@ let package = Package( description: "Build a target for testing" )) ), + .plugin( + name: "plugin-dependencies-stub", + capability: .command(intent: .custom( + verb: "build-plugin-dependency", + description: "Build a plugin dependency for testing" + )), + dependencies: [ + .target(name: "plugintool") + ] + ), .executableTarget( name: "placeholder" ), + .executableTarget( + name: "plugintool" + ), ] ) diff --git a/Fixtures/Miscellaneous/Plugins/CommandPluginTestStub/Plugins/plugin-dependencies-stub/main.swift b/Fixtures/Miscellaneous/Plugins/CommandPluginTestStub/Plugins/plugin-dependencies-stub/main.swift new file mode 100644 index 00000000000..7034dbc528b --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/CommandPluginTestStub/Plugins/plugin-dependencies-stub/main.swift @@ -0,0 +1,13 @@ +import PackagePlugin + +@main +struct test: CommandPlugin { + // This plugin exists to test that the executable it requires is built correctly when cross-compiling + func performCommand(context: PluginContext, arguments: [String]) async throws { + print("Hello from dependencies-stub") + let _ = try packageManager.build( + .product("placeholder"), + parameters: .init(configuration: .debug, logging: .concise) + ) + } +} \ No newline at end of file diff --git a/Fixtures/Miscellaneous/Plugins/CommandPluginTestStub/Sources/main.swift b/Fixtures/Miscellaneous/Plugins/CommandPluginTestStub/Sources/placeholder/main.swift similarity index 100% rename from Fixtures/Miscellaneous/Plugins/CommandPluginTestStub/Sources/main.swift rename to Fixtures/Miscellaneous/Plugins/CommandPluginTestStub/Sources/placeholder/main.swift diff --git a/Fixtures/Miscellaneous/Plugins/CommandPluginTestStub/Sources/plugintool/main.swift b/Fixtures/Miscellaneous/Plugins/CommandPluginTestStub/Sources/plugintool/main.swift new file mode 100644 index 00000000000..3d2f1a1f757 --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/CommandPluginTestStub/Sources/plugintool/main.swift @@ -0,0 +1 @@ +print("Hello from plugintool") diff --git a/Sources/Commands/PackageTools/PluginCommand.swift b/Sources/Commands/PackageTools/PluginCommand.swift index 839b9f17b52..1968dae8183 100644 --- a/Sources/Commands/PackageTools/PluginCommand.swift +++ b/Sources/Commands/PackageTools/PluginCommand.swift @@ -319,7 +319,11 @@ struct PluginCommand: SwiftCommand { // Build or bring up-to-date any executable host-side tools on which this plugin depends. Add them and any binary dependencies to the tool-names-to-path map. let buildSystem = try swiftTool.createBuildSystem( explicitBuildSystem: .native, - cacheBuildManifest: false + cacheBuildManifest: false, + // Force all dependencies to be built for the host, to work around the fact that BuildOperation.plan + // knows to compile build tool plugin dependencies for the host but does not do the same for command + // plugins. + productsBuildParameters: buildParameters ) let accessibleTools = try plugin.processAccessibleTools( packageGraph: packageGraph, diff --git a/Tests/BuildTests/PluginsBuildPlanTests.swift b/Tests/BuildTests/PluginsBuildPlanTests.swift index a98070e0817..5ef99e3fc59 100644 --- a/Tests/BuildTests/PluginsBuildPlanTests.swift +++ b/Tests/BuildTests/PluginsBuildPlanTests.swift @@ -12,7 +12,9 @@ import Basics import SPMTestSupport +@testable import SPMBuildCore import XCTest +import PackageModel final class PluginsBuildPlanTests: XCTestCase { func testBuildToolsDatabasePath() throws { @@ -22,4 +24,41 @@ final class PluginsBuildPlanTests: XCTestCase { XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/plugins/tools/build.db")))) } } + + func testCommandPluginDependenciesWhenCrossCompiling() throws { + // Command Plugin dependencies must be built for the host. + // This test is only supported on macOS because that is the only + // platform on which we can currently be sure of having a viable + // cross-compilation environment (arm64->x86_64 or vice versa). + // On Linux it is typically only possible to build for the host + // environment unless cross-compilation SDKs are being used. + #if !os(macOS) + try XCTSkipIf(true, "test is only supported on macOS") + #endif + + let hostToolchain = try UserToolchain(swiftSDK: .hostSwiftSDK()) + let hostTriple = try! hostToolchain.targetTriple.withoutVersion().tripleString + + let x86Triple = "x86_64-apple-macosx" + let armTriple = "arm64-apple-macosx" + let targetTriple = hostToolchain.targetTriple.arch == .aarch64 ? x86Triple : armTriple + + // By default, plugin dependencies are built for the host platform + try fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in + let (stdout, stderr) = try executeSwiftPackage(fixturePath, extraArgs: ["-v", "build-plugin-dependency"]) + XCTAssertMatch(stdout, .contains("Hello from dependencies-stub")) + XCTAssertMatch(stderr, .contains("Build of product 'plugintool' complete!")) + XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool")))) + XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/placeholder")))) + } + + // When cross compiling the final product, plugin dependencies should still be built for the host + try fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in + let (stdout, stderr) = try executeSwiftPackage(fixturePath, extraArgs: ["--triple", targetTriple, "-v", "build-plugin-dependency"]) + XCTAssertMatch(stdout, .contains("Hello from dependencies-stub")) + XCTAssertMatch(stderr, .contains("Build of product 'plugintool' complete!")) + XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool")))) + XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(targetTriple)/debug/placeholder")))) + } + } }