Skip to content

Commit 7185d6d

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 64b7ca9 commit 7185d6d

File tree

27 files changed

+580
-141
lines changed

27 files changed

+580
-141
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public func GetLibraryName() -> String {
2+
return "MySourceGenRuntimeLib"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import XCTest
2+
import class Foundation.Bundle
3+
4+
final class SwiftyProtobufTests: XCTestCase {
5+
func testExample() throws {
6+
// This is an example of a functional test case.
7+
// Use XCTAssert and related functions to verify your tests produce the correct
8+
// results.
9+
10+
// Some of the APIs that we use below are available in macOS 10.13 and above.
11+
guard #available(macOS 10.13, *) else {
12+
return
13+
}
14+
15+
// Mac Catalyst won't have `Process`, but it is supported for executables.
16+
#if !targetEnvironment(macCatalyst)
17+
18+
let fooBinary = productsDirectory.appendingPathComponent("MySourceGenTool")
19+
20+
let process = Process()
21+
process.executableURL = fooBinary
22+
23+
let pipe = Pipe()
24+
process.standardOutput = pipe
25+
26+
try process.run()
27+
process.waitUntilExit()
28+
29+
let data = pipe.fileHandleForReading.readDataToEndOfFile()
30+
let output = String(data: data, encoding: .utf8)
31+
32+
XCTAssertEqual(output, "Hello, world!\n")
33+
#endif
34+
}
35+
36+
/// Returns path to the built products directory.
37+
var productsDirectory: URL {
38+
#if os(macOS)
39+
for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
40+
return bundle.bundleURL.deletingLastPathComponent()
41+
}
42+
fatalError("couldn't find the products directory")
43+
#else
44+
return Bundle.main.bundleURL
45+
#endif
46+
}
47+
}

Sources/Basics/SwiftVersion.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public struct SwiftVersion {
5858
extension SwiftVersion {
5959
/// The current version of the package manager.
6060
public static let current = SwiftVersion(
61-
version: (5, 10, 0),
61+
version: (5, 11, 0),
6262
isDevelopment: true,
6363
buildIdentifier: getBuildIdentifier()
6464
)

0 commit comments

Comments
 (0)