Skip to content

Commit 45f6c40

Browse files
committed
Support vending products that are backed by binaryTargets
This adds support for vending an executable product that consists solely of a binary target that is backed by an artifact bundle. This allows vending binary executables as their own separate package, independently of the plugins that are using them. rdar://101096803
1 parent db9a253 commit 45f6c40

21 files changed

Lines changed: 262 additions & 82 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
/*.xcodeproj
5+
xcuserdata/
6+
DerivedData/
7+
.swiftpm/config/registries.json
8+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
9+
.netrc
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"schemaVersion": "1.0",
3+
"artifacts": {
4+
"mytool": {
5+
"type": "executable",
6+
"version": "1.2.3",
7+
"variants": [
8+
{
9+
"path": "mytool-macos/mytool",
10+
"supportedTriples": ["x86_64-apple-macosx", "arm64-apple-macosx"]
11+
},
12+
{
13+
"path": "mytool-linux/mytool",
14+
"supportedTriples": ["x86_64-unknown-linux-gnu"]
15+
}
16+
]
17+
}
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/bin/bash
2+
3+
print_usage() {
4+
echo "usage: ${0##*/} [--verbose] <in> <out>"
5+
}
6+
7+
# Parse arguments until we find '--' or an argument that isn't an option.
8+
until [ $# -eq 0 ]
9+
do
10+
case "$1" in
11+
--verbose) verbose=1; shift;;
12+
--) shift; break;;
13+
-*) echo "unknown option: ${1}"; print_usage; exit 1; shift;;
14+
*) break;;
15+
esac
16+
done
17+
18+
# Print usage and leave if we don't have exactly two arguments.
19+
if [ $# -ne 2 ]; then
20+
print_usage
21+
exit 1
22+
fi
23+
24+
# For our sample tool we just copy from one to the other.
25+
if [ $verbose != 0 ]; then
26+
echo "[${0##*/}-linux] '$1' '$2'"
27+
fi
28+
29+
cp "$1" "$2"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/bin/bash
2+
3+
print_usage() {
4+
echo "usage: ${0##*/} [--verbose] <in> <out>"
5+
}
6+
7+
# Parse arguments until we find '--' or an argument that isn't an option.
8+
until [ $# -eq 0 ]
9+
do
10+
case "$1" in
11+
--verbose) verbose=1; shift;;
12+
--) shift; break;;
13+
-*) echo "unknown option: ${1}"; print_usage; exit 1; shift;;
14+
*) break;;
15+
esac
16+
done
17+
18+
# Print usage and leave if we don't have exactly two arguments.
19+
if [ $# -ne 2 ]; then
20+
print_usage
21+
exit 1
22+
fi
23+
24+
# For our sample tool we just copy from one to the other.
25+
if [ $verbose != 0 ]; then
26+
echo "[${0##*/}-macosx] '$1' '$2'"
27+
fi
28+
29+
cp "$1" "$2"
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// swift-tools-version: 5.6
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "MyBinaryProduct",
6+
products: [
7+
.executable(
8+
name: "MyVendedSourceGenBuildTool",
9+
targets: ["MyVendedSourceGenBuildTool"]
10+
),
11+
],
12+
targets: [
13+
.binaryTarget(
14+
name: "MyVendedSourceGenBuildTool",
15+
path: "Binaries/MyVendedSourceGenBuildTool.artifactbundle"
16+
),
17+
]
18+
)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// swift-tools-version: 5.6
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "MyBinaryToolPlugin",
6+
dependencies: [
7+
.package(path: "Dependency"),
8+
],
9+
targets: [
10+
// A local tool that uses a build tool plugin.
11+
.executableTarget(
12+
name: "MyLocalTool",
13+
plugins: [
14+
"MySourceGenBuildToolPlugin",
15+
]
16+
),
17+
// The plugin that generates build tool commands to invoke MySourceGenBuildTool.
18+
.plugin(
19+
name: "MySourceGenBuildToolPlugin",
20+
capability: .buildTool(),
21+
dependencies: [
22+
.product(name: "MyVendedSourceGenBuildTool", package: "Dependency"),
23+
]
24+
),
25+
]
26+
)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import PackagePlugin
2+
3+
@main
4+
struct MyPlugin: BuildToolPlugin {
5+
6+
func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] {
7+
print("Hello from the Build Tool Plugin!")
8+
guard let target = target as? SourceModuleTarget else { return [] }
9+
let inputFiles = target.sourceFiles.filter({ $0.path.extension == "dat" })
10+
return try inputFiles.map {
11+
let inputFile = $0
12+
let inputPath = inputFile.path
13+
let outputName = inputPath.stem + ".swift"
14+
let outputPath = context.pluginWorkDirectory.appending(outputName)
15+
return .buildCommand(
16+
displayName:
17+
"Generating \(outputName) from \(inputPath.lastComponent)",
18+
executable:
19+
try context.tool(named: "mytool").path,
20+
arguments: [
21+
"--verbose",
22+
"\(inputPath)",
23+
"\(outputPath)"
24+
],
25+
inputFiles: [
26+
inputPath,
27+
],
28+
outputFiles: [
29+
outputPath
30+
]
31+
)
32+
}
33+
}
34+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let foo = "I am Foo!"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
print("Generated string Foo: '\(foo)'")

Sources/Build/BuildPlan.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,7 +1469,7 @@ public final class ProductBuildDescription {
14691469
// we will instead have generated a source file containing the redirect.
14701470
// Support for linking tests against executables is conditional on the tools
14711471
// version of the package that defines the executable product.
1472-
let executableTarget = try product.executableTarget()
1472+
let executableTarget = try product.executableTarget
14731473
if executableTarget.underlyingTarget is SwiftTarget, toolsVersion >= .v5_5,
14741474
buildParameters.canRenameEntrypointFunctionName {
14751475
if let flags = buildParameters.linkerFlagsForRenamingMainFunction(of: executableTarget) {
@@ -1989,8 +1989,8 @@ public class BuildPlan {
19891989

19901990
var productMap: [ResolvedProduct: ProductBuildDescription] = [:]
19911991
// Create product description for each product we have in the package graph except
1992-
// for automatic libraries and plugins, because they don't produce any output.
1993-
for product in graph.allProducts where product.type != .library(.automatic) && product.type != .plugin {
1992+
// for automatic libraries, plugins or products which consist solely of binary targets, because they don't produce any output.
1993+
for product in graph.allProducts where product.type != .library(.automatic) && product.type != .plugin && !product.targets.filter({ !($0.underlyingTarget is BinaryTarget) }).isEmpty {
19941994
guard let package = graph.package(for: product) else {
19951995
throw InternalError("unknown package for \(product)")
19961996
}

0 commit comments

Comments
 (0)