Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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"]
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash

print_usage() {
echo "usage: ${0##*/} [--verbose] <in> <out>"
}

# 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"
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash

print_usage() {
echo "usage: ${0##*/} [--verbose] <in> <out>"
}

# 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"
Original file line number Diff line number Diff line change
@@ -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"
),
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import PackagePlugin

@main
struct MyPlugin: BuildToolPlugin {

func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] {
print("Hello from MyPlugin!")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
print("It's Me MyPluginExe\n")
10 changes: 8 additions & 2 deletions Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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!
}
}
Expand Down
28 changes: 28 additions & 0 deletions Tests/SwiftBuildSupportTests/PIFBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,32 @@ struct PIFBuilderTests {
#expect(releaseConfig.settings.platformSpecificSettings[.windows]?[.SWIFT_ACTIVE_COMPILATION_CONDITIONS] == ["$(inherited)"])
}
}

@Test func pluginWithBinaryTargetDependency() async throws {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Great to see more PIF tests in OSS SPM ☺️

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @pmattos. We should expand the PIF test coverage.

Although this is a great test, it's a large test (ie: end-to-end). Can we investigate writing tests lower in the test pyramid, possibly testing productRepresentingDependencyOfBuildPlugin in isolation?

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 }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: do we expect warnings to occur? If no, should we also ensure no warnings exist? if the answer is "we don't care about warning", is there benefits adding a comment indicating so?

#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")
}
}
}