Skip to content

Commit f8fb0ec

Browse files
committed
Default to all targets when plugin --target parameter missing
1 parent 2d5461e commit f8fb0ec

File tree

4 files changed

+277
-2
lines changed

4 files changed

+277
-2
lines changed

Package.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ let package = Package(
139139
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
140140
]
141141
),
142+
.testTarget(
143+
name: "SwiftFormatPluginTests"
144+
),
142145
]
143146
)
144147

Plugins/FormatPlugin/plugin.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ extension FormatPlugin: CommandPlugin {
3838

3939
var argExtractor = ArgumentExtractor(arguments)
4040
let targetNames = argExtractor.extractOption(named: "target")
41-
let targetsToFormat = try context.package.targets(named: targetNames)
41+
let targetsToFormat = targetNames.isEmpty ? context.package.targets : try context.package.targets(named: targetNames)
4242

4343
let configurationFilePath = argExtractor.extractOption(named: "configuration").first
4444

Plugins/LintPlugin/plugin.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ extension LintPlugin: CommandPlugin {
3939
// Extract the arguments that specify what targets to format.
4040
var argExtractor = ArgumentExtractor(arguments)
4141
let targetNames = argExtractor.extractOption(named: "target")
42-
let targetsToFormat = try context.package.targets(named: targetNames)
4342

43+
let targetsToFormat = targetNames.isEmpty ? context.package.targets : try context.package.targets(named: targetNames)
4444
let configurationFilePath = argExtractor.extractOption(named: "configuration").first
4545

4646
let sourceCodeTargets = targetsToFormat.compactMap { $0 as? SourceModuleTarget }
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
import Foundation
2+
import XCTest
3+
4+
final class PluginRunTests: XCTestCase {
5+
6+
var workingDirUrl: URL!
7+
var taskProcess: Process!
8+
9+
var allOutputTxt: String = ""
10+
var stdoutTxt: String = ""
11+
var stderrTxt: String = ""
12+
13+
override func setUp() {
14+
setupProject()
15+
fixCodesigning()
16+
setupProcess()
17+
}
18+
19+
// In a tmp dir, create a SPM project which depends on this project, and build it.
20+
// Project contains targets: executable, library, test, plugin.
21+
func setupProject() {
22+
23+
// currentDirectoryPath: /path/to/swift-format/.build/arm64-apple-macosx/debug
24+
let swiftFormatProjectDirPath = URL(
25+
fileURLWithPath: FileManager.default.currentDirectoryPath,
26+
isDirectory: true
27+
)
28+
.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().path
29+
30+
let packageTxt = """
31+
// swift-tools-version: 5.9
32+
33+
import PackageDescription
34+
35+
let package = Package(
36+
name: "Swift-Format-Plugin-Test",
37+
products: [
38+
.library(
39+
name: "LibraryTarget",
40+
targets: ["LibraryTarget"]
41+
),
42+
.plugin(
43+
name: "PluginTarget",
44+
targets: ["PluginTarget"]
45+
)
46+
],
47+
dependencies: [
48+
.package(path: "\(swiftFormatProjectDirPath)"),
49+
],
50+
targets: [
51+
.executableTarget(
52+
name: "ExecutableTarget",
53+
dependencies: [
54+
"LibraryTarget",
55+
],
56+
path: "Sources/ExecutableTarget"
57+
),
58+
.target(
59+
name: "LibraryTarget",
60+
path: "Sources/LibraryTarget"
61+
),
62+
.plugin(
63+
name: "PluginTarget",
64+
capability: .command(
65+
intent: .custom(
66+
verb: "test-plugin",
67+
description: "A test plugin"
68+
),
69+
permissions: [
70+
.writeToPackageDirectory(reason: "This command generates files")
71+
]
72+
),
73+
dependencies: [
74+
"ExecutableTarget"
75+
],
76+
path: "Plugins/PluginTarget"
77+
),
78+
.testTarget(
79+
name: "TestTarget",
80+
dependencies: [
81+
"LibraryTarget"
82+
]
83+
)
84+
]
85+
)
86+
"""
87+
88+
let tempDirUrl = FileManager.default.temporaryDirectory.appendingPathComponent(
89+
UUID().uuidString)
90+
91+
do {
92+
try FileManager.default.createDirectory(at: tempDirUrl, withIntermediateDirectories: false)
93+
94+
FileManager.default.createFile(
95+
atPath: tempDirUrl.appendingPathComponent("package.swift").path,
96+
contents: packageTxt.data(using: .utf8)
97+
)
98+
99+
try FileManager.default.createDirectory(
100+
at: tempDirUrl.appendingPathComponent("Sources").appendingPathComponent("ExecutableTarget"),
101+
withIntermediateDirectories: true)
102+
FileManager.default.createFile(
103+
atPath: tempDirUrl.appendingPathComponent("Sources").appendingPathComponent(
104+
"ExecutableTarget"
105+
).appendingPathComponent("maain.swift").path,
106+
contents: "".data(using: .utf8)
107+
)
108+
109+
try FileManager.default.createDirectory(
110+
at: tempDirUrl.appendingPathComponent("Sources").appendingPathComponent("LibraryTarget"),
111+
withIntermediateDirectories: true)
112+
FileManager.default.createFile(
113+
atPath: tempDirUrl.appendingPathComponent("Sources").appendingPathComponent("LibraryTarget")
114+
.appendingPathComponent("library.swift").path,
115+
contents: "".data(using: .utf8)
116+
)
117+
118+
try FileManager.default.createDirectory(
119+
at: tempDirUrl.appendingPathComponent("Tests").appendingPathComponent("TestTarget"),
120+
withIntermediateDirectories: true)
121+
FileManager.default.createFile(
122+
atPath: tempDirUrl.appendingPathComponent("Tests").appendingPathComponent("TestTarget")
123+
.appendingPathComponent("test.swift").path,
124+
contents: "".data(using: .utf8)
125+
)
126+
127+
let pluginTxt = """
128+
import Foundation
129+
import PackagePlugin
130+
131+
@main
132+
struct TestPlugin: CommandPlugin {
133+
func performCommand(context: PackagePlugin.PluginContext, arguments: [String]) async throws {
134+
print("TestPlugin is working.")
135+
}
136+
}
137+
"""
138+
139+
try FileManager.default.createDirectory(
140+
at: tempDirUrl.appendingPathComponent("Plugins").appendingPathComponent("PluginTarget"),
141+
withIntermediateDirectories: true)
142+
FileManager.default.createFile(
143+
atPath: tempDirUrl.appendingPathComponent("Plugins").appendingPathComponent("PluginTarget")
144+
.appendingPathComponent("plugin.swift").path,
145+
contents: pluginTxt.data(using: .utf8)
146+
)
147+
148+
setupProcess()
149+
taskProcess.arguments = [
150+
"build"
151+
]
152+
try taskProcess.run()
153+
taskProcess.waitUntilExit()
154+
155+
XCTAssertTrue(stdoutTxt.contains("Build complete!"))
156+
157+
workingDirUrl = tempDirUrl
158+
} catch {
159+
XCTFail("Error setting up test fixture project at: \(tempDirUrl.path)")
160+
}
161+
}
162+
163+
// Prepare a new NSTask/Process with output recorded to string variables.
164+
func setupProcess() {
165+
166+
allOutputTxt = ""
167+
stdoutTxt = ""
168+
stderrTxt = ""
169+
170+
let stdoutHandler = { (file: FileHandle!) -> Void in
171+
let data = file.availableData
172+
173+
guard !data.isEmpty, let output = String(data: data, encoding: .utf8), !output.isEmpty
174+
else {
175+
return
176+
}
177+
self.stdoutTxt += output
178+
self.allOutputTxt += output
179+
}
180+
let stderrHandler = { (file: FileHandle!) -> Void in
181+
let data = file.availableData
182+
guard !data.isEmpty, let output = String(data: data, encoding: .utf8), !output.isEmpty
183+
else {
184+
return
185+
}
186+
self.stderrTxt += output
187+
self.allOutputTxt += output
188+
}
189+
190+
let stdOut = Pipe()
191+
stdOut.fileHandleForReading.readabilityHandler = stdoutHandler
192+
193+
let stderr = Pipe()
194+
stderr.fileHandleForReading.readabilityHandler = stderrHandler
195+
196+
taskProcess = Process()
197+
taskProcess.standardOutput = stdOut
198+
taskProcess.standardError = stderr
199+
taskProcess.currentDirectoryURL = workingDirUrl
200+
201+
taskProcess.launchPath = "/usr/bin/swift"
202+
}
203+
204+
/**
205+
@see https://github.com/apple/swift-package-manager/issues/6872
206+
*/
207+
func fixCodesigning() {
208+
setupProcess()
209+
taskProcess.arguments = [
210+
"package", "plugin", "lint-source-code",
211+
]
212+
try! taskProcess.run()
213+
taskProcess.waitUntilExit()
214+
215+
// Do not alter the codesigning if the bug in #6872 is not present.
216+
if 0 == taskProcess.terminationStatus
217+
|| !allOutputTxt.split(separator: "\n").last!.contains("Build complete!")
218+
{
219+
return
220+
}
221+
222+
setupProcess()
223+
taskProcess.launchPath = "/usr/bin/codesign"
224+
taskProcess.arguments = [
225+
"-s", "-", workingDirUrl.path + "/.build/plugins/Lint Source Code/cache/Lint_Source_Code",
226+
]
227+
try! taskProcess.run()
228+
taskProcess.waitUntilExit()
229+
230+
setupProcess()
231+
taskProcess.arguments = [
232+
"package", "plugin", "lint-source-code",
233+
]
234+
try! taskProcess.run()
235+
taskProcess.waitUntilExit()
236+
}
237+
238+
/**
239+
Confirm the plugin runs without any targets specified.
240+
`swift package plugin lint-source-code`
241+
@see https://github.com/apple/swift-format/issues/483
242+
"error: swift-format invocation failed: NSTaskTerminationReason(rawValue: 1):64"
243+
*/
244+
public func testPluginRun() {
245+
246+
let processFinishExpectation = expectation(description: "process timeout")
247+
248+
taskProcess.arguments = [
249+
"package", "plugin", "lint-source-code",
250+
]
251+
do {
252+
taskProcess.terminationHandler = { (process: Process) in
253+
processFinishExpectation.fulfill()
254+
}
255+
256+
try taskProcess.run()
257+
258+
waitForExpectations(timeout: 30)
259+
} catch {
260+
XCTFail(allOutputTxt)
261+
}
262+
263+
let errorMessage = "swift-format invocation failed: NSTaskTerminationReason(rawValue: 1):64"
264+
if stderrTxt.split(separator: "\n").last == "error: \(errorMessage)" {
265+
XCTFail("\(errorMessage)\nworkingdir: \(workingDirUrl.path)")
266+
}
267+
268+
XCTAssertEqual(
269+
0, Int(taskProcess.terminationStatus), "Non-zero exit code\nworkingdir: \(workingDirUrl.path)"
270+
)
271+
}
272+
}

0 commit comments

Comments
 (0)