Skip to content

Commit dc414f2

Browse files
committed
Add template support for creating a build tool package plugin
rdar://107288352
1 parent 1779745 commit dc414f2

File tree

3 files changed

+63
-11
lines changed

3 files changed

+63
-11
lines changed

Sources/Commands/PackageTools/Init.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ extension SwiftPackageTool {
3131
tool - A package with an executable that uses
3232
Swift Argument Parser. Use this template if you
3333
plan to have a rich set of command-line arguments.
34+
build-tool-plugin - A package that vends a build tool plugin.
3435
command-plugin - A package that vends a command plugin.
3536
macro - A package that vends a macro.
3637
empty - An empty package with a Package.swift manifest.

Sources/Workspace/InitPackage.swift

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public final class InitPackage {
4444
case library = "library"
4545
case executable = "executable"
4646
case tool = "tool"
47+
case buildToolPlugin = "build-tool-plugin"
4748
case commandPlugin = "command-plugin"
4849
case macro = "macro"
4950

@@ -271,6 +272,14 @@ public final class InitPackage {
271272
]),
272273
]
273274
"""
275+
} else if packageType == .buildToolPlugin {
276+
param += """
277+
.plugin(
278+
name: "\(typeName)",
279+
capability: .buildTool()
280+
),
281+
]
282+
"""
274283
} else if packageType == .commandPlugin {
275284
param += """
276285
.plugin(
@@ -362,7 +371,7 @@ public final class InitPackage {
362371

363372
private func writePlugins() throws {
364373
switch packageType {
365-
case .commandPlugin:
374+
case .buildToolPlugin, .commandPlugin:
366375
let plugins = destinationPath.appending(component: "Plugins")
367376
guard self.fileSystem.exists(plugins) == false else {
368377
return
@@ -376,16 +385,30 @@ public final class InitPackage {
376385
let sourceFileName = "plugin.swift"
377386
let sourceFile = try AbsolutePath(validating: sourceFileName, relativeTo: moduleDir)
378387

379-
let content = """
388+
var content = """
380389
import PackagePlugin
381390
382391
@main
383-
struct \(typeName): CommandPlugin {
384-
func performCommand(context: PluginContext, arguments: [String]) async throws {
385-
print("Hello, World!")
392+
"""
393+
if packageType == .buildToolPlugin {
394+
content += """
395+
struct \(typeName): BuildToolPlugin {
396+
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
397+
print("Hello, World!")
398+
return []
399+
}
386400
}
387-
}
388-
"""
401+
"""
402+
}
403+
else {
404+
content += """
405+
struct \(typeName): CommandPlugin {
406+
func performCommand(context: PluginContext, arguments: [String]) async throws {
407+
print("Hello, World!")
408+
}
409+
}
410+
"""
411+
}
389412

390413
try writePackageFile(sourceFile) { stream in
391414
stream.write(content)
@@ -397,7 +420,7 @@ public final class InitPackage {
397420
}
398421

399422
private func writeSources() throws {
400-
if packageType == .empty || packageType == .commandPlugin {
423+
if packageType == .empty || packageType == .buildToolPlugin || packageType == .commandPlugin {
401424
return
402425
}
403426

@@ -473,7 +496,7 @@ public final class InitPackage {
473496
public macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "\(moduleName)Macros", type: "StringifyMacro")
474497
"""
475498

476-
case .empty, .commandPlugin:
499+
case .empty, .buildToolPlugin, .commandPlugin:
477500
throw InternalError("invalid packageType \(packageType)")
478501
}
479502

@@ -489,7 +512,7 @@ public final class InitPackage {
489512

490513
private func writeTests() throws {
491514
switch packageType {
492-
case .empty, .executable, .tool, .commandPlugin: return
515+
case .empty, .executable, .tool, .buildToolPlugin, .commandPlugin: return
493516
default: break
494517
}
495518
let tests = destinationPath.appending("Tests")
@@ -635,7 +658,7 @@ public final class InitPackage {
635658

636659
let testClassFile = try AbsolutePath(validating: "\(moduleName)Tests.swift", relativeTo: testModule)
637660
switch packageType {
638-
case .empty, .commandPlugin, .executable, .tool: break
661+
case .empty, .buildToolPlugin, .commandPlugin, .executable, .tool: break
639662
case .library:
640663
try writeLibraryTestsFile(testClassFile)
641664
case .macro:

Tests/WorkspaceTests/InitTests.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,34 @@ class InitTests: XCTestCase {
177177
}
178178
}
179179

180+
func testInitPackageBuildToolPlugin() throws {
181+
try testWithTemporaryDirectory { tmpPath in
182+
let fs = localFileSystem
183+
let path = tmpPath.appending("MyBuildToolPlugin")
184+
let name = path.basename
185+
try fs.createDirectory(path)
186+
187+
// Create the package
188+
try InitPackage(
189+
name: name,
190+
packageType: .buildToolPlugin,
191+
destinationPath: path,
192+
fileSystem: localFileSystem
193+
).writePackageStructure()
194+
195+
// Verify basic file system content that we expect in the package
196+
let manifest = path.appending("Package.swift")
197+
XCTAssertFileExists(manifest)
198+
let manifestContents: String = try localFileSystem.readFileContents(manifest)
199+
XCTAssertMatch(manifestContents, .and(.contains(".plugin("), .contains("capability: .buildTool()")))
200+
201+
let source = path.appending("Plugins", "MyBuildToolPlugin", "plugin.swift")
202+
XCTAssertFileExists(source)
203+
let sourceContents: String = try localFileSystem.readFileContents(source)
204+
XCTAssertMatch(sourceContents, .contains("struct MyBuildToolPlugin: BuildToolPlugin"))
205+
}
206+
}
207+
180208
// MARK: Special case testing
181209

182210
func testInitPackageNonc99Directory() throws {

0 commit comments

Comments
 (0)