Skip to content

Commit ffdb487

Browse files
committed
Replace custom path type with URL in plugin API
Plugins introduced their own custom path type which doesn't have Windows support, causing several issues with plugins on Windows. We should rectify this situation by using Foundation's URL type instead which already works fine on Windows. rdar://117230149
1 parent d2eb0ca commit ffdb487

File tree

24 files changed

+542
-97
lines changed

24 files changed

+542
-97
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// swift-tools-version: 5.11
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "MySourceGenPlugin",
6+
products: [
7+
// The product that vends MySourceGenBuildToolPlugin to client packages.
8+
.plugin(
9+
name: "MySourceGenBuildToolPlugin",
10+
targets: ["MySourceGenBuildToolPlugin"]
11+
),
12+
// The product that vends the MySourceGenBuildTool executable to client packages.
13+
.executable(
14+
name: "MySourceGenBuildTool",
15+
targets: ["MySourceGenBuildTool"]
16+
),
17+
// The product that vends MySourceGenPrebuildPlugin to client packages.
18+
.plugin(
19+
name: "MySourceGenPrebuildPlugin",
20+
targets: ["MySourceGenPrebuildPlugin"]
21+
),
22+
],
23+
targets: [
24+
// A local tool that uses a build tool plugin.
25+
.executableTarget(
26+
name: "MyLocalTool",
27+
plugins: [
28+
"MySourceGenBuildToolPlugin",
29+
]
30+
),
31+
// A local tool that uses a prebuild plugin.
32+
.executableTarget(
33+
name: "MyOtherLocalTool",
34+
plugins: [
35+
"MySourceGenPrebuildPlugin",
36+
]
37+
),
38+
// The plugin that generates build tool commands to invoke MySourceGenBuildTool.
39+
.plugin(
40+
name: "MySourceGenBuildToolPlugin",
41+
capability: .buildTool(),
42+
dependencies: [
43+
"MySourceGenBuildTool",
44+
]
45+
),
46+
// The plugin that generates prebuild commands (currently to invoke a system tool).
47+
.plugin(
48+
name: "MySourceGenPrebuildPlugin",
49+
capability: .buildTool()
50+
),
51+
// The command line tool that generates source files.
52+
.executableTarget(
53+
name: "MySourceGenBuildTool",
54+
dependencies: [
55+
"MySourceGenBuildToolLib",
56+
]
57+
),
58+
// A library used by MySourceGenBuildTool (not the client).
59+
.target(
60+
name: "MySourceGenBuildToolLib"
61+
),
62+
// A runtime library that the client needs to link against.
63+
.target(
64+
name: "MySourceGenRuntimeLib"
65+
),
66+
// Unit tests for the plugin.
67+
.testTarget(
68+
name: "MySourceGenPluginTests",
69+
dependencies: [
70+
"MySourceGenRuntimeLib",
71+
],
72+
plugins: [
73+
"MySourceGenBuildToolPlugin",
74+
"MySourceGenPrebuildPlugin",
75+
]
76+
)
77+
]
78+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import PackagePlugin
2+
3+
@main
4+
struct MyPlugin: BuildToolPlugin {
5+
#if USE_CREATE
6+
let verb = "Creating"
7+
#else
8+
let verb = "Generating"
9+
#endif
10+
11+
func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] {
12+
print("Hello from the Build Tool Plugin!")
13+
guard let target = target as? SourceModuleTarget else { return [] }
14+
return try target.sourceFiles.map{ $0.url }.compactMap {
15+
guard $0.pathExtension == "dat" else { return .none }
16+
let outputName = $0.deletingPathExtension().lastPathComponent + ".swift"
17+
let outputPath = context.pluginWorkDirectoryURL.appendingPathComponent(outputName)
18+
return .buildCommand(
19+
displayName:
20+
"\(verb) \(outputName) from \($0.lastPathComponent)",
21+
executable:
22+
try context.tool(named: "MySourceGenBuildTool").url,
23+
arguments: [
24+
"\($0)",
25+
"\(outputPath.path)"
26+
],
27+
inputFiles: [
28+
$0,
29+
],
30+
outputFiles: [
31+
outputPath
32+
]
33+
)
34+
}
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Foundation
2+
import PackagePlugin
3+
4+
@main
5+
struct MyPlugin: BuildToolPlugin {
6+
7+
func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] {
8+
print("Hello from the Prebuild Plugin!")
9+
guard let target = target as? SourceModuleTarget else { return [] }
10+
let outputPaths: [URL] = target.sourceFiles.filter{ $0.url.pathExtension == "dat" }.map { file in
11+
context.pluginWorkDirectoryURL.appendingPathComponent(file.url.lastPathComponent + ".swift")
12+
}
13+
var commands: [Command] = []
14+
if !outputPaths.isEmpty {
15+
commands.append(.prebuildCommand(
16+
displayName:
17+
"Running prebuild command for target \(target.name)",
18+
executable:
19+
URL(fileURLWithPath: "/usr/bin/touch"),
20+
arguments:
21+
outputPaths.map{ $0.path },
22+
outputFilesDirectory:
23+
context.pluginWorkDirectoryURL
24+
))
25+
}
26+
return commands
27+
}
28+
}
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: '\(foo)'")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
I am Bar!
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
I am Baz!
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// print("Generated string Bar: '\(bar)'")
2+
// print("Generated string Baz: '\(baz)'")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import Foundation
2+
import MySourceGenBuildToolLib
3+
4+
// 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. The input file is the first argument and the output file is the second.
5+
if ProcessInfo.processInfo.arguments.count != 3 {
6+
print("usage: MySourceGenBuildTool <input> <output>")
7+
exit(1)
8+
}
9+
let inputFile = ProcessInfo.processInfo.arguments[1]
10+
let outputFile = ProcessInfo.processInfo.arguments[2]
11+
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 \(variableName) = \(dataAsHex.quotedForSourceCode)\n"
17+
let outputData = outputString.data(using: .utf8)
18+
FileManager.default.createFile(atPath: outputFile, contents: outputData)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import Foundation
2+
3+
extension String {
4+
5+
public var quotedForSourceCode: String {
6+
return "\"" + self
7+
.replacingOccurrences(of: "\\", with: "\\\\")
8+
.replacingOccurrences(of: "\"", with: "\\\"")
9+
+ "\""
10+
}
11+
}

0 commit comments

Comments
 (0)