Skip to content

Commit f009b72

Browse files
committed
Use the new LLBuildManifestWriter support to pass through shell script build phase environment and working directory for build tool commands.
rdar://74663489
1 parent 8abc017 commit f009b72

File tree

8 files changed

+113
-3
lines changed

8 files changed

+113
-3
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// swift-tools-version: 999.0
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "ContrivedTestPlugin",
6+
targets: [
7+
// A local tool that uses a build tool plugin.
8+
.executableTarget(
9+
name: "MyLocalTool",
10+
dependencies: [
11+
"MySourceGenBuildToolPlugin",
12+
]
13+
),
14+
// The plugin that generates build tool commands to invoke MySourceGenBuildTool.
15+
.plugin(
16+
name: "MySourceGenBuildToolPlugin",
17+
capability: .buildTool(),
18+
dependencies: [
19+
"MySourceGenBuildTool",
20+
]
21+
),
22+
// The command line tool that generates source files.
23+
.executableTarget(
24+
name: "MySourceGenBuildTool"
25+
),
26+
]
27+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
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: '\(PREFIX_foo)'")
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Foundation
2+
3+
// Sample source generator tool that emits a Swift variable declaration of a string containing the hex representation of the contents of a file as a quoted string. The variable name is the base name of the input file, with the value from an environment value prepended. The input file is the first argument and the output file is the second.
4+
if ProcessInfo.processInfo.arguments.count != 3 {
5+
print("usage: MySourceGenBuildTool <input> <output>")
6+
exit(1)
7+
}
8+
let inputFile = ProcessInfo.processInfo.arguments[1]
9+
let outputFile = ProcessInfo.processInfo.arguments[2]
10+
11+
let variablePrefix = ProcessInfo.processInfo.environment["VARIABLE_NAME_PREFIX"] ?? ""
12+
let variableName = URL(fileURLWithPath: inputFile).deletingPathExtension().lastPathComponent
13+
14+
let inputData = FileManager.default.contents(atPath: inputFile) ?? Data()
15+
let dataAsHex = inputData.map { String(format: "%02hhx", $0) }.joined()
16+
let outputString = "public var \(variablePrefix)\(variableName) = \(dataAsHex.quotedForSourceCode)\n"
17+
let outputData = outputString.data(using: .utf8)
18+
FileManager.default.createFile(atPath: outputFile, contents: outputData)
19+
20+
extension String {
21+
22+
public var quotedForSourceCode: String {
23+
return "\"" + self
24+
.replacingOccurrences(of: "\\", with: "\\\\")
25+
.replacingOccurrences(of: "\"", with: "\\\"")
26+
+ "\""
27+
}
28+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import PackagePlugin
2+
3+
print("Hello from the Build Tool Plugin!")
4+
5+
for inputPath in targetBuildContext.otherFiles {
6+
guard inputPath.suffix == ".dat" else { continue }
7+
let outputName = inputPath.basename + ".swift"
8+
let outputPath = targetBuildContext.outputDir.appending(outputName)
9+
commandConstructor.createCommand(
10+
displayName:
11+
"Generating \(outputName) from \(inputPath.filename)",
12+
executable:
13+
try targetBuildContext.lookupTool(named: "MySourceGenBuildTool"),
14+
arguments: [
15+
"\(inputPath)",
16+
"\(outputName)"
17+
],
18+
environment: [
19+
"VARIABLE_NAME_PREFIX": "PREFIX_"
20+
],
21+
workingDirectory:
22+
targetBuildContext.outputDir,
23+
inputPaths: [
24+
inputPath,
25+
],
26+
outputPaths: [
27+
outputPath
28+
]
29+
)
30+
commandConstructor.addGeneratedOutputFile(path: outputPath)
31+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public var PREFIX_foo = "4920616d20466f6f210a"

Sources/Build/ManifestBuilder.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -584,16 +584,17 @@ extension LLBuildManifestBuilder {
584584

585585
// Add any build tool commands created by plugins for the target (prebuild and postbuild commands are handled outside the build).
586586
for command in target.pluginInvocationResults.reduce([], { $0 + $1.commands }) {
587-
if case .buildToolCommand(let displayName, let executable, let arguments, _, _, let inputPaths, let outputPaths) = command {
587+
if case .buildToolCommand(let displayName, let executable, let arguments, let environment, let workingDirectory, let inputPaths, let outputPaths) = command {
588588
// Create a shell command to invoke the executable. We include the path of the executable as a dependency.
589-
// FIXME: We will need to extend the addShellCmd() function to also take working directory and environment.
590589
let execPath = AbsolutePath(executable, relativeTo: buildParameters.buildPath)
591590
manifest.addShellCmd(
592591
name: displayName,
593592
description: displayName,
594593
inputs: [.file(execPath)] + inputPaths.map{ .file($0) },
595594
outputs: outputPaths.map{ .file($0) },
596-
args: [execPath.pathString] + arguments)
595+
args: [execPath.pathString] + arguments,
596+
environ: environment,
597+
workingDir: workingDirectory?.pathString)
597598
}
598599
}
599600

Tests/FunctionalTests/PluginTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,24 @@ class PluginTests: XCTestCase {
7070
}
7171
}
7272
}
73+
74+
func testContrivedTestCases() throws {
75+
// Check if the host compiler supports the '-entry-point-function-name' flag. It's not needed for this test but is needed to build any executable from a package that uses tools version 999.0.
76+
try XCTSkipUnless(doesHostSwiftCompilerSupportRenamingMainSymbol(), "skipping because host compiler doesn't support '-entry-point-function-name'")
77+
78+
fixture(name: "Miscellaneous/Plugins") { path in
79+
do {
80+
let (stdout, _) = try executeSwiftBuild(path.appending(component: "ContrivedTestPlugin"), configuration: .Debug, extraArgs: ["--product", "MyLocalTool"], env: ["SWIFTPM_ENABLE_PLUGINS": "1"])
81+
XCTAssert(stdout.contains("Linking MySourceGenBuildTool"), "stdout:\n\(stdout)")
82+
XCTAssert(stdout.contains("Generating foo.swift from foo.dat"), "stdout:\n\(stdout)")
83+
XCTAssert(stdout.contains("Linking MyLocalTool"), "stdout:\n\(stdout)")
84+
XCTAssert(stdout.contains("Build complete!"), "stdout:\n\(stdout)")
85+
}
86+
catch {
87+
print(error)
88+
throw error
89+
}
90+
}
91+
}
92+
7393
}

0 commit comments

Comments
 (0)