Skip to content

Commit 5a50cf2

Browse files
authored
[APIDiff] Add baseline management flags (#3532)
* [APIDiff] Allow overriding the baselines directory * [APIDiff] Support force-regenerating the baseline with --regenerate-baseline
1 parent c1afc88 commit 5a50cf2

File tree

3 files changed

+88
-7
lines changed

3 files changed

+88
-7
lines changed

Sources/Commands/APIDigester.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,20 @@ struct APIDigesterBaselineDumper {
6464
}
6565

6666
/// Emit the API baseline files and return the path to their directory.
67-
func emitAPIBaseline(for modulesToDiff: Set<String>) throws -> AbsolutePath {
67+
func emitAPIBaseline(for modulesToDiff: Set<String>,
68+
at baselineDir: AbsolutePath?,
69+
force: Bool) throws -> AbsolutePath {
6870
var modulesToDiff = modulesToDiff
6971
let apiDiffDir = inputBuildParameters.apiDiff
70-
let baselineDir = apiDiffDir.appending(component: baselineTreeish)
72+
let baselineDir = (baselineDir ?? apiDiffDir).appending(component: baselineTreeish)
7173
let baselinePath: (String)->AbsolutePath = { module in
7274
baselineDir.appending(component: module + ".json")
7375
}
7476

75-
for module in modulesToDiff {
76-
if localFileSystem.exists(baselinePath(module)) {
77-
// If this baseline already exists, we don't need to regenerate it.
78-
modulesToDiff.remove(module)
77+
if !force {
78+
// Baselines which already exist don't need to be regenerated.
79+
modulesToDiff = modulesToDiff.filter {
80+
!localFileSystem.exists(baselinePath($0))
7981
}
8082
}
8183

Sources/Commands/SwiftPackageTool.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,13 @@ extension SwiftPackageTool {
323323
help: "One or more targets to include in the API comparison. If present, only the specified targets (and any products specified using `--products`) will be compared.")
324324
var targets: [String] = []
325325

326+
@Option(name: .customLong("baseline-dir"),
327+
help: "The path to a directory used to store API baseline files. If unspecified, a temporary directory will be used.")
328+
var overrideBaselineDir: AbsolutePath?
329+
330+
@Flag(help: "Regenerate the API baseline, even if an existing one is available.")
331+
var regenerateBaseline: Bool = false
332+
326333
func run(_ swiftTool: SwiftTool) throws {
327334
let apiDigesterPath = try swiftTool.getToolchain().getSwiftAPIDigester()
328335
let apiDigesterTool = SwiftAPIDigester(tool: apiDigesterPath)
@@ -348,7 +355,9 @@ extension SwiftPackageTool {
348355
apiDigesterTool: apiDigesterTool,
349356
diags: swiftTool.diagnostics
350357
)
351-
let baselineDir = try baselineDumper.emitAPIBaseline(for: modulesToDiff)
358+
let baselineDir = try baselineDumper.emitAPIBaseline(for: modulesToDiff,
359+
at: overrideBaselineDir,
360+
force: regenerateBaseline)
352361

353362
let results = ThreadSafeArrayStore<SwiftAPIDigester.ComparisonResult>()
354363
let group = DispatchGroup()

Tests/CommandsTests/APIDiffTests.swift

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,4 +360,74 @@ final class APIDiffTests: XCTestCase {
360360
throw XCTSkip("Test unsupported on current platform")
361361
#endif
362362
}
363+
364+
func testBaselineDirOverride() throws {
365+
#if os(macOS)
366+
guard (try? Resources.default.toolchain.getSwiftAPIDigester()) != nil else {
367+
throw XCTSkip("swift-api-digester not available")
368+
}
369+
fixture(name: "Miscellaneous/APIDiff/") { prefix in
370+
let packageRoot = prefix.appending(component: "Foo")
371+
// Overwrite the existing decl.
372+
try localFileSystem.writeFileContents(packageRoot.appending(component: "Foo.swift")) {
373+
$0 <<< "public let foo = 42"
374+
}
375+
376+
let baselineDir = prefix.appending(component: "Baselines")
377+
378+
XCTAssertThrowsError(try execute(["experimental-api-diff", "1.2.3",
379+
"--baseline-dir", baselineDir.pathString],
380+
packagePath: packageRoot)) { error in
381+
guard case SwiftPMProductError.executionFailure(error: _, output: let output, stderr: _) = error else {
382+
XCTFail("Unexpected error")
383+
return
384+
}
385+
XCTAssertTrue(output.contains("1 breaking change detected in Foo"))
386+
XCTAssertTrue(output.contains("💔 API breakage: func foo() has been removed"))
387+
XCTAssertTrue(localFileSystem.exists(baselineDir.appending(components: "1.2.3", "Foo.json")))
388+
}
389+
}
390+
#else
391+
throw XCTSkip("Test unsupported on current platform")
392+
#endif
393+
}
394+
395+
func testRegenerateBaseline() throws {
396+
#if os(macOS)
397+
guard (try? Resources.default.toolchain.getSwiftAPIDigester()) != nil else {
398+
throw XCTSkip("swift-api-digester not available")
399+
}
400+
fixture(name: "Miscellaneous/APIDiff/") { prefix in
401+
let packageRoot = prefix.appending(component: "Foo")
402+
// Overwrite the existing decl.
403+
try localFileSystem.writeFileContents(packageRoot.appending(component: "Foo.swift")) {
404+
$0 <<< "public let foo = 42"
405+
}
406+
407+
let baselineDir = prefix.appending(component: "Baselines")
408+
let fooBaselinePath = baselineDir.appending(components: "1.2.3", "Foo.json")
409+
410+
try localFileSystem.createDirectory(fooBaselinePath.parentDirectory, recursive: true)
411+
try localFileSystem.writeFileContents(fooBaselinePath) {
412+
$0 <<< "Old Baseline"
413+
}
414+
415+
XCTAssertThrowsError(try execute(["experimental-api-diff", "1.2.3",
416+
"--baseline-dir", baselineDir.pathString,
417+
"--regenerate-baseline"],
418+
packagePath: packageRoot)) { error in
419+
guard case SwiftPMProductError.executionFailure(error: _, output: let output, stderr: _) = error else {
420+
XCTFail("Unexpected error")
421+
return
422+
}
423+
XCTAssertTrue(output.contains("1 breaking change detected in Foo"))
424+
XCTAssertTrue(output.contains("💔 API breakage: func foo() has been removed"))
425+
XCTAssertTrue(localFileSystem.exists(fooBaselinePath))
426+
XCTAssertNotEqual((try! localFileSystem.readFileContents(fooBaselinePath)).description, "Old Baseline")
427+
}
428+
}
429+
#else
430+
throw XCTSkip("Test unsupported on current platform")
431+
#endif
432+
}
363433
}

0 commit comments

Comments
 (0)