diff --git a/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Dependency/MyBinaryTargetExeArtifactBundle.artifactbundle/info.json b/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Dependency/MyBinaryTargetExeArtifactBundle.artifactbundle/info.json new file mode 100644 index 00000000000..322cdaa8f4a --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Dependency/MyBinaryTargetExeArtifactBundle.artifactbundle/info.json @@ -0,0 +1,19 @@ +{ + "schemaVersion": "1.0", + "artifacts": { + "mytool": { + "type": "executable", + "version": "1.2.3", + "variants": [ + { + "path": "mytool-macos/mytool", + "supportedTriples": ["x86_64-apple-macosx", "arm64-apple-macosx"] + }, + { + "path": "mytool-linux/mytool", + "supportedTriples": ["x86_64-unknown-linux-gnu"] + } + ] + } + } +} diff --git a/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Dependency/MyBinaryTargetExeArtifactBundle.artifactbundle/mytool-linux/mytool b/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Dependency/MyBinaryTargetExeArtifactBundle.artifactbundle/mytool-linux/mytool new file mode 100755 index 00000000000..bc45801290f --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Dependency/MyBinaryTargetExeArtifactBundle.artifactbundle/mytool-linux/mytool @@ -0,0 +1,29 @@ +#!/bin/bash + +print_usage() { + echo "usage: ${0##*/} [--verbose] " +} + +# Parse arguments until we find '--' or an argument that isn't an option. +until [ $# -eq 0 ] +do + case "$1" in + --verbose) verbose=1; shift;; + --) shift; break;; + -*) echo "unknown option: ${1}"; print_usage; exit 1; shift;; + *) break;; + esac +done + +# Print usage and leave if we don't have exactly two arguments. +if [ $# -ne 2 ]; then + print_usage + exit 1 +fi + +# For our sample tool we just copy from one to the other. +if [ $verbose != 0 ]; then + echo "[${0##*/}-linux] '$1' '$2'" +fi + +cp "$1" "$2" diff --git a/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Dependency/MyBinaryTargetExeArtifactBundle.artifactbundle/mytool-macos/mytool b/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Dependency/MyBinaryTargetExeArtifactBundle.artifactbundle/mytool-macos/mytool new file mode 100755 index 00000000000..38d637c5f8e --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Dependency/MyBinaryTargetExeArtifactBundle.artifactbundle/mytool-macos/mytool @@ -0,0 +1,29 @@ +#!/bin/bash + +print_usage() { + echo "usage: ${0##*/} [--verbose] " +} + +# Parse arguments until we find '--' or an argument that isn't an option. +until [ $# -eq 0 ] +do + case "$1" in + --verbose) verbose=1; shift;; + --) shift; break;; + -*) echo "unknown option: ${1}"; print_usage; exit 1; shift;; + *) break;; + esac +done + +# Print usage and leave if we don't have exactly two arguments. +if [ $# -ne 2 ]; then + print_usage + exit 1 +fi + +# For our sample tool we just copy from one to the other. +if [ $verbose != 0 ]; then + echo "[${0##*/}-macosx] '$1' '$2'" +fi + +cp "$1" "$2" diff --git a/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Package.swift b/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Package.swift new file mode 100644 index 00000000000..d240b1f4ef0 --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Package.swift @@ -0,0 +1,38 @@ +// swift-tools-version: 5.6 +import PackageDescription + +let package = Package( + name: "MyBinaryTargetExePlugin", + + products: [ + .executable( + name: "MyPluginExe", + targets: ["MyPluginExe"] + ), + .plugin( + name: "MyPlugin", + targets: ["MyPlugin"] + ), + .executable( + name: "MyBinaryTargetExe", + targets: ["MyBinaryTargetExeArtifactBundle"] + ), + ], + targets: [ + .executableTarget( + name: "MyPluginExe", + dependencies: [], + exclude: [], + ), + + .plugin( + name: "MyPlugin", + capability: .buildTool(), + dependencies: ["MyPluginExe", "MyBinaryTargetExeArtifactBundle"] + ), + .binaryTarget( + name: "MyBinaryTargetExeArtifactBundle", + path: "Dependency/MyBinaryTargetExeArtifactBundle.artifactbundle" + ), + ] +) diff --git a/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Plugins/MyPlugin/plugin.swift b/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Plugins/MyPlugin/plugin.swift new file mode 100644 index 00000000000..8b446c5634d --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Plugins/MyPlugin/plugin.swift @@ -0,0 +1,9 @@ +import PackagePlugin + +@main +struct MyPlugin: BuildToolPlugin { + + func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] { + print("Hello from MyPlugin!") + } +} diff --git a/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Sources/MyPluginExe/main.swift b/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Sources/MyPluginExe/main.swift new file mode 100644 index 00000000000..d64011ba57e --- /dev/null +++ b/Fixtures/Miscellaneous/Plugins/BinaryTargetExePlugin/Sources/MyPluginExe/main.swift @@ -0,0 +1 @@ +print("It's Me MyPluginExe\n") \ No newline at end of file diff --git a/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift b/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift index b409518837d..dc8c91102d6 100644 --- a/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift +++ b/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift @@ -513,12 +513,18 @@ extension PackageGraph.ResolvedModule { func productRepresentingDependencyOfBuildPlugin(in mainModuleProducts: [ResolvedProduct]) -> ResolvedProduct? { mainModuleProducts.only { (mainModuleProduct: ResolvedProduct) -> Bool in + // Handle binary-only executable products that don't have a main module, i.e. binaryTarget + guard let mainModule = mainModuleProduct.mainModule else { + return mainModuleProduct.type == .executable && + mainModuleProduct.modules.only?.type == .binary && + mainModuleProduct.modules.only?.name == self.name + } // NOTE: We can't use the 'id' here as we need to explicitly ignore the build triple because our build // triple will be '.tools' while the target we want to depend on will have a build triple of '.destination'. // See for more details: // https://github.com/swiftlang/swift-package-manager/commit/b22168ec41061ddfa3438f314a08ac7a776bef7a. - return mainModuleProduct.mainModule!.packageIdentity == self.packageIdentity && - mainModuleProduct.mainModule!.name == self.name + return mainModule.packageIdentity == self.packageIdentity && + mainModule.name == self.name // Intentionally ignore the build triple! } } diff --git a/Tests/SwiftBuildSupportTests/PIFBuilderTests.swift b/Tests/SwiftBuildSupportTests/PIFBuilderTests.swift index 0d1fa537072..14f425f0bed 100644 --- a/Tests/SwiftBuildSupportTests/PIFBuilderTests.swift +++ b/Tests/SwiftBuildSupportTests/PIFBuilderTests.swift @@ -135,4 +135,32 @@ struct PIFBuilderTests { #expect(releaseConfig.settings.platformSpecificSettings[.windows]?[.SWIFT_ACTIVE_COMPILATION_CONDITIONS] == ["$(inherited)"]) } } + + @Test func pluginWithBinaryTargetDependency() async throws { + try await withGeneratedPIF(fromFixture: "Miscellaneous/Plugins/BinaryTargetExePlugin") { pif, observabilitySystem in + // Verify that PIF generation succeeds for a package with a plugin that depends on a binary target + #expect(pif.workspace.projects.count > 0) + + let project = try pif.workspace.project(named: "MyBinaryTargetExePlugin") + + // Verify the plugin target exists + let pluginTarget = try project.target(named: "MyPlugin") + #expect(pluginTarget.common.name == "MyPlugin") + + // Verify the executable target that uses the plugin exists + let executableTarget = try project.target(named: "MyPluginExe") + #expect(executableTarget.common.name == "MyPluginExe") + + // Verify no errors were emitted during PIF generation + let errors = observabilitySystem.diagnostics.filter { $0.severity == .error } + #expect(errors.isEmpty, "Expected no errors during PIF generation, but got: \(errors)") + + // Verify that the plugin target has a dependency (binary targets are handled differently in PIF) + // The key test is that PIF generation succeeds without errors when a plugin depends on a binary target + let binaryArtifactMessages = observabilitySystem.diagnostics.filter { + $0.message.contains("found binary artifact") + } + #expect(binaryArtifactMessages.count > 0, "Expected to find binary artifact processing messages") + } + } }