Skip to content

Commit 39a8e7f

Browse files
committed
Adjust to SE-0303: Change PackagePlugin API to match proposal.
1 parent caab591 commit 39a8e7f

File tree

11 files changed

+170
-52
lines changed

11 files changed

+170
-52
lines changed

Fixtures/Miscellaneous/Plugins/ContrivedTestPlugin/Plugins/MySourceGenBuildToolPlugin/plugin.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ print("Hello from the Build Tool Plugin!")
55
for inputFile in targetBuildContext.inputFiles.filter({ $0.path.extension == "dat" }) {
66
let inputPath = inputFile.path
77
let outputName = inputPath.stem + ".swift"
8-
let outputPath = targetBuildContext.outputDirectory.appending(outputName)
9-
commandConstructor.createBuildCommand(
8+
let outputPath = targetBuildContext.pluginWorkDirectory.appending(outputName)
9+
commandConstructor.addBuildCommand(
1010
displayName:
1111
"Generating \(outputName) from \(inputPath.lastComponent)",
1212
executable:

Fixtures/Miscellaneous/Plugins/MyBinaryToolPlugin/Plugins/MySourceGenBuildToolPlugin/plugin.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ print("Hello from the Build Tool Plugin!")
55
for inputFile in targetBuildContext.inputFiles.filter({ $0.path.extension == "dat" }) {
66
let inputPath = inputFile.path
77
let outputName = inputPath.stem + ".swift"
8-
let outputPath = targetBuildContext.outputDirectory.appending(outputName)
9-
commandConstructor.createBuildCommand(
8+
let outputPath = targetBuildContext.pluginWorkDirectory.appending(outputName)
9+
commandConstructor.addBuildCommand(
1010
displayName:
1111
"Generating \(outputName) from \(inputPath.lastComponent)",
1212
executable:

Fixtures/Miscellaneous/Plugins/MySourceGenPlugin/Plugins/MySourceGenBuildToolPlugin/plugin.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ print("Hello from the Build Tool Plugin!")
55
for inputPath in targetBuildContext.inputFiles.map{ $0.path } {
66
guard inputPath.extension == "dat" else { continue }
77
let outputName = inputPath.stem + ".swift"
8-
let outputPath = targetBuildContext.outputDirectory.appending(outputName)
9-
commandConstructor.createBuildCommand(
8+
let outputPath = targetBuildContext.pluginWorkDirectory.appending(outputName)
9+
commandConstructor.addBuildCommand(
1010
displayName:
1111
"Generating \(outputName) from \(inputPath.lastComponent)",
1212
executable:

Fixtures/Miscellaneous/Plugins/MySourceGenPlugin/Plugins/MySourceGenPrebuildPlugin/plugin.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ import PackagePlugin
33
print("Hello from the Prebuild Plugin!")
44

55
let outputPaths: [Path] = targetBuildContext.inputFiles.filter{ $0.path.extension == "dat" }.map { file in
6-
targetBuildContext.outputDirectory.appending(file.path.stem + ".swift")
6+
targetBuildContext.pluginWorkDirectory.appending(file.path.stem + ".swift")
77
}
88

99
if !outputPaths.isEmpty {
10-
commandConstructor.createPrebuildCommand(
10+
commandConstructor.addPrebuildCommand(
1111
displayName:
1212
"Running prebuild command for target \(targetBuildContext.targetName)",
1313
executable:
1414
Path("/usr/bin/touch"),
1515
arguments:
1616
outputPaths.map{ $0.string },
1717
outputFilesDirectory:
18-
targetBuildContext.outputDirectory
18+
targetBuildContext.pluginWorkDirectory
1919
)
2020
}

Fixtures/Miscellaneous/Plugins/SandboxTesterPlugin/Plugins/MySourceGenBuildToolPlugin/plugin.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import PackagePlugin
22
import Foundation
33

44
// Check that we can write to the output directory.
5-
let allowedOutputPath = targetBuildContext.outputDirectory.appending("Foo")
5+
let allowedOutputPath = targetBuildContext.pluginWorkDirectory.appending("Foo")
66
if mkdir(allowedOutputPath.string, 0o777) != 0 {
77
throw StringError("unexpectedly could not write to '\(allowedOutputPath)': \(String(utf8String: strerror(errno)))")
88
}

Sources/PackagePlugin/ImplementationDetails.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ struct BuildCommand: Encodable {
6161
let displayName: String?
6262
let executable: Path
6363
let arguments: [String]
64-
let environment: [String: String]?
64+
let environment: [String: String]
6565
let workingDirectory: Path?
6666
let inputFiles: [Path]
6767
let outputFiles: [Path]
@@ -71,7 +71,7 @@ struct PrebuildCommand: Encodable {
7171
let displayName: String?
7272
let executable: Path
7373
let arguments: [String]
74-
let environment: [String: String]?
74+
let environment: [String: String]
7575
let workingDirectory: Path?
7676
let outputFilesDirectory: Path
7777
}

Sources/PackagePlugin/PublicAPI/CommandConstructor.swift

Lines changed: 119 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,82 @@
88
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
99
*/
1010

11-
/// Constructs commands to run during the build, including full command lines.
12-
/// All paths should be based on the ones passed to the plugin in the target
13-
/// build context.
11+
/// Constructs commands to run during the build, including command lines,
12+
/// environment variables, initial working directory, etc. All paths should
13+
/// be based on the ones passed to the plugin in the target build context.
1414
public final class CommandConstructor {
15+
1516
/// Prevents the CommandConstructor from being instantiated by the script.
1617
internal init() {}
1718

1819
/// Creates a command to run during the build. The executable should be a
19-
/// path returned by `TargetBuildContext.tool(named:)`, the inputs should
20-
/// be the files that are used by the command, and the outputs should be
21-
/// the files that are produced by the command.
22-
public func createBuildCommand(
20+
/// tool returned by `TargetBuildContext.tool(named:)`, and any paths in
21+
/// the arguments list as well as in the input and output lists should be
22+
/// based on the paths provided in the target build context structure.
23+
///
24+
/// The build command will run whenever its outputs are missing or if its
25+
/// inputs have changed since the command was last run. In other words,
26+
/// it is incorporated into the build command graph.
27+
///
28+
/// This is the preferred kind of command to create when the outputs that
29+
/// will be generated are known ahead of time.
30+
///
31+
/// - parameters:
32+
/// - displayName: An optional string to show in build logs and other
33+
/// status areas.
34+
/// - executable: The executable to be invoked; should be a tool looked
35+
/// up using `tool(named:)`, which may reference either a tool provided
36+
/// by a binary target or build from source.
37+
/// - arguments: Arguments to be passed to the tool. Any paths should be
38+
/// based on the paths provided in the target build context.
39+
/// - environment: Any custom environment assignments for the subprocess.
40+
/// - inputFiles: Input files to the build command. Any changes to the
41+
/// input files cause the command to be rerun.
42+
/// - outputFiles: Output files that should be processed further according
43+
/// to the rules defined by the build system.
44+
public func addBuildCommand(
2345
displayName: String?,
2446
executable: Path,
2547
arguments: [String],
26-
environment: [String: String]? = nil,
48+
environment: [String: String] = [:],
2749
inputFiles: [Path] = [],
2850
outputFiles: [Path] = []
2951
) {
3052
output.buildCommands.append(BuildCommand(displayName: displayName, executable: executable, arguments: arguments, environment: environment, workingDirectory: nil, inputFiles: inputFiles, outputFiles: outputFiles))
3153
}
3254

55+
/// Creates a command to run during the build. The executable should be a
56+
/// tool returned by `TargetBuildContext.tool(named:)`, and any paths in
57+
/// the arguments list as well as in the input and output lists should be
58+
/// based on the paths provided in the target build context structure.
59+
///
60+
/// The build command will run whenever its outputs are missing or if its
61+
/// inputs have changed since the command was last run. In other words,
62+
/// it is incorporated into the build command graph.
63+
///
64+
/// This is the preferred kind of command to create when the outputs that
65+
/// will be generated are known ahead of time.
66+
///
67+
/// - parameters:
68+
/// - displayName: An optional string to show in build logs and other
69+
/// status areas.
70+
/// - executable: The executable to be invoked; should be a tool looked
71+
/// up using `tool(named:)`, which may reference either a tool provided
72+
/// by a binary target or build from source.
73+
/// - arguments: Arguments to be passed to the tool. Any paths should be
74+
/// based on the paths provided in the target build context.
75+
/// - environment: Any custom environment assignments for the subprocess.
76+
/// - workingDirectory: Optional initial working directory of the command.
77+
/// - inputFiles: Input files to the build command. Any changes to the
78+
/// input files cause the command to be rerun.
79+
/// - outputFiles: Output files that should be processed further according
80+
/// to the rules defined by the build system.
3381
@available(*, unavailable, message: "specifying the initial working directory for a command is not yet supported")
34-
public func createBuildCommand(
82+
public func addBuildCommand(
3583
displayName: String?,
3684
executable: Path,
3785
arguments: [String],
38-
environment: [String: String]? = nil,
86+
environment: [String: String] = [:],
3987
workingDirectory: Path? = nil,
4088
inputFiles: [Path] = [],
4189
outputFiles: [Path] = []
@@ -44,25 +92,80 @@ public final class CommandConstructor {
4492
}
4593

4694
/// Creates a command to run before the build. The executable should be a
47-
/// path returned by `TargetBuildContext.tool(named:)`, the output direc-
48-
/// tory should be a directory in which the command will create the output
49-
/// files that should be subject to further processing.
50-
public func createPrebuildCommand(
95+
/// tool returned by `TargetBuildContext.tool(named:)`, and any paths in
96+
/// the arguments list and the output files directory should be based on
97+
/// the paths provided in the target build context structure.
98+
///
99+
/// The build command will run before the build starts, and is allowed to
100+
/// create an arbitrary set of output files based on the contents of the
101+
/// inputs.
102+
///
103+
/// Because prebuild commands are run on every build, they are can have
104+
/// significant performance impact and should only be used when there is
105+
/// no way to know the names of the outputs before the command is run.
106+
///
107+
/// The `outputFilesDirectory` parameter is the path of a directory into
108+
/// which the command will write its output files. Any files that are in
109+
/// that directory after the prebuild command finishes will be interpreted
110+
/// according to same build rules as for sources.
111+
///
112+
/// - parameters:
113+
/// - displayName: An optional string to show in build logs and other
114+
/// status areas.
115+
/// - executable: The executable to be invoked; should be a tool looked
116+
/// up using `tool(named:)`, which may reference either a tool provided
117+
/// by a binary target or build from source.
118+
/// - arguments: Arguments to be passed to the tool. Any paths should be
119+
/// based on the paths provided in the target build context.
120+
/// - environment: Any custom environment assignments for the subprocess.
121+
/// - outputFilesDirectory: A directory into which the command can write
122+
/// output files that should be processed further.
123+
public func addPrebuildCommand(
51124
displayName: String?,
52125
executable: Path,
53126
arguments: [String],
54-
environment: [String: String]? = nil,
127+
environment: [String: String] = [:],
55128
outputFilesDirectory: Path
56129
) {
57130
output.prebuildCommands.append(PrebuildCommand(displayName: displayName, executable: executable, arguments: arguments, environment: environment, workingDirectory: nil, outputFilesDirectory: outputFilesDirectory))
58131
}
59132

133+
/// Creates a command to run before the build. The executable should be a
134+
/// tool returned by `TargetBuildContext.tool(named:)`, and any paths in
135+
/// the arguments list and the output files directory should be based on
136+
/// the paths provided in the target build context structure.
137+
///
138+
/// The build command will run before the build starts, and is allowed to
139+
/// create an arbitrary set of output files based on the contents of the
140+
/// inputs.
141+
///
142+
/// Because prebuild commands are run on every build, they are can have
143+
/// significant performance impact and should only be used when there is
144+
/// no way to know the names of the outputs before the command is run.
145+
///
146+
/// The `outputFilesDirectory` parameter is the path of a directory into
147+
/// which the command will write its output files. Any files that are in
148+
/// that directory after the prebuild command finishes will be interpreted
149+
/// according to same build rules as for sources.
150+
///
151+
/// - parameters:
152+
/// - displayName: An optional string to show in build logs and other
153+
/// status areas.
154+
/// - executable: The executable to be invoked; should be a tool looked
155+
/// up using `tool(named:)`, which may reference either a tool provided
156+
/// by a binary target or build from source.
157+
/// - arguments: Arguments to be passed to the tool. Any paths should be
158+
/// based on the paths provided in the target build context.
159+
/// - environment: Any custom environment assignments for the subprocess.
160+
/// - workingDirectory: Optional initial working directory of the command.
161+
/// - outputFilesDirectory: A directory into which the command can write
162+
/// output files that should be processed further.
60163
@available(*, unavailable, message: "specifying the initial working directory for a command is not yet supported")
61164
public func createPrebuildCommand(
62165
displayName: String?,
63166
executable: Path,
64167
arguments: [String],
65-
environment: [String: String]? = nil,
168+
environment: [String: String] = [:],
66169
workingDirectory: Path? = nil,
67170
outputFilesDirectory: Path
68171
) {

Sources/PackagePlugin/PublicAPI/FileList.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ extension FileList: Sequence {
3535

3636
/// Provides information about a single file in a FileList.
3737
public struct FileInfo: Decodable {
38+
/// The path of the file.
3839
public let path: Path
40+
/// File type, as determined by SwiftPM.
3941
public let type: FileType
4042
}
4143

Sources/PackagePlugin/PublicAPI/Globals.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public let commandConstructor = CommandConstructor()
3333

3434
/// The diagnostics emitter lets the plugin emit errors, warnings, and remarks
3535
/// for issues discovered by the plugin. Note that diagnostics from the plugin
36-
/// itself are relatively rare, and relate such things as missing tools or to
36+
/// itself are relatively rare, and relate to such things as missing tools or
3737
/// problems constructing the build command. Diagnostics from the build tools
3838
/// themselves are processed in the same way as any other output from a build
3939
/// tool.

Sources/PackagePlugin/PublicAPI/TargetBuildContext.swift

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@
1313
/// be configured to write their outputs. This information should be used as
1414
/// part of generating the commands to be run during the build.
1515
public final class TargetBuildContext: Decodable {
16-
/// The name of the target being built, as aspecified in the manifest.
16+
17+
/// The name of the target being built, as specified in the manifest.
1718
public let targetName: String
1819

1920
/// The module name of the target. This is currently derived from the name,
2021
/// but could be customizable in the package manifest in a future SwiftPM
2122
/// version.
2223
public let moduleName: String
2324

24-
/// The path of the target directory.
25+
/// The path of the target source directory.
2526
public let targetDirectory: Path
2627

2728
/// That path of the package that contains the target.
@@ -41,9 +42,10 @@ public final class TargetBuildContext: Decodable {
4142
/// generating lists of search path arguments, etc.
4243
public let dependencies: [DependencyTargetInfo]
4344

44-
/// Provides information about a target in the dependency closure of the
45-
/// target to which the plugin is being applied.
45+
/// Provides information about a target that appears in the dependency
46+
/// closure of the target to which the plugin is being applied.
4647
public struct DependencyTargetInfo: Decodable {
48+
4749
/// The name of the target.
4850
public let targetName: String
4951

@@ -52,30 +54,35 @@ public final class TargetBuildContext: Decodable {
5254
/// SwiftPM version.
5355
public let moduleName: String
5456

55-
/// The path of the target source directory.
57+
/// Path of the target source directory.
5658
public let targetDirectory: Path
59+
60+
/// Path of the public headers directory, if any (Clang targets only).
61+
public let publicHeadersDirectory: Path?
5762
}
5863

59-
/// The path of an output directory where the plugin or the build commands
60-
/// it constructs can write anything it wants to. This includes generated
61-
/// source files that should be further processed, and it could include
62-
/// any caches used by the build tool or by the plugin itself. The plugin
63-
/// is in complete control of what is written under this directory, and
64-
/// the contents are preserved between builds.
64+
/// The path of a writable directory into which the plugin or the build
65+
/// commands it constructs can write anything it wants. This could include
66+
/// any generated source files that should be processed further, and it
67+
/// could include any caches used by the build tool or the plugin itself.
68+
/// The plugin is in complete control of what is written under this di-
69+
/// rectory, and the contents are preserved between builds.
6570
///
6671
/// A plugin would usually create a separate subdirectory of this directory
6772
/// for each command it creates, and the command would be configured to
6873
/// write its outputs to that directory. The plugin may also create other
6974
/// directories for cache files and other file system content that either
7075
/// it or the command will need.
71-
public let outputDirectory: Path
76+
public let pluginWorkDirectory: Path
77+
78+
/// The path of the directory into which built products associated with
79+
/// the target are written.
80+
public let builtProductsDirectory: Path
7281

7382
/// Looks up and returns the path of a named command line executable tool.
74-
/// The executable must be either in the toolchain or in the system search
75-
/// path for executables, or be provided by an executable target or binary
76-
/// target on which the package plugin target depends. Returns nil, but
77-
/// does not throw an error, if the tool isn't found. Plugins that re-
78-
/// quire the tool should emit an error diagnostic if it cannot be found.
83+
/// The executable must be provided by an executable target or a binary
84+
/// target on which the package plugin target depends. This function throws
85+
/// an error if the tool cannot be found. The lookup is case sensitive.
7986
public func tool(named name: String, line: UInt = #line) throws -> Tool {
8087
if let tool = self.tools[name] { return tool }
8188
throw TargetBuildContextError.toolNotFound(name: name, line: line)
@@ -87,6 +94,7 @@ public final class TargetBuildContext: Decodable {
8794

8895
/// Information about a particular tool that is available to a plugin.
8996
public struct Tool: Codable {
97+
9098
/// Name of the tool, suitable for display purposes.
9199
public let name: String
92100

@@ -96,12 +104,14 @@ public final class TargetBuildContext: Decodable {
96104
}
97105

98106
public enum TargetBuildContextError: Error {
107+
99108
/// Could not find a tool with the given name. This could be either because
100109
/// it doesn't exist, or because the plugin doesn't have a dependency on it.
101110
case toolNotFound(name: String, line: UInt)
102111
}
103112

104113
extension TargetBuildContextError: CustomStringConvertible {
114+
105115
public var description: String {
106116
switch self {
107117
case .toolNotFound(let name, let line):

0 commit comments

Comments
 (0)