Skip to content

Commit eac3fd5

Browse files
committed
Add support for API digester baseline comparison jobs
Remove duplicated API/ABI digester flags Fixes for ABI baselines and comparisons
1 parent f26cb05 commit eac3fd5

File tree

7 files changed

+375
-80
lines changed

7 files changed

+375
-80
lines changed

Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,15 @@ public struct Driver {
3434
case missingProfilingData(String)
3535
case conditionalCompilationFlagHasRedundantPrefix(String)
3636
case conditionalCompilationFlagIsNotValidIdentifier(String)
37+
case baselineGenerationRequiresTopLevelModule(String)
38+
case optionRequiresAnother(String, String)
3739
// Explicit Module Build Failures
3840
case malformedModuleDependency(String, String)
3941
case missingPCMArguments(String)
4042
case missingModuleDependency(String)
4143
case missingContextHashOnSwiftDependency(String)
4244
case dependencyScanningFailure(Int, String)
4345
case missingExternalDependency(String)
44-
case baselineGenerationRequiresTopLevelModule(String)
4546

4647
public var description: String {
4748
switch self {
@@ -103,6 +104,8 @@ public struct Driver {
103104
return "Missing External dependency info for module: \(moduleName)"
104105
case .baselineGenerationRequiresTopLevelModule(let arg):
105106
return "generating a baseline with '\(arg)' is only supported with '-emit-module' or '-emit-module-path'"
107+
case .optionRequiresAnother(let first, let second):
108+
return "'\(first)' cannot be specified if '\(second)' is not present"
106109
}
107110
}
108111
}
@@ -291,11 +294,11 @@ public struct Driver {
291294
/// Path to the Swift module source information file.
292295
let moduleSourceInfoPath: VirtualPath.Handle?
293296

294-
/// Path to the module API baseline file.
295-
let apiBaselinePath: VirtualPath.Handle?
297+
/// Path to the module's digester baseline file.
298+
let digesterBaselinePath: VirtualPath.Handle?
296299

297-
/// Path to the module ABI baseline file.
298-
let abiBaselinePath: VirtualPath.Handle?
300+
/// The mode the API digester should run in.
301+
let digesterMode: DigesterMode
299302

300303
/// Force the driver to emit the module first and then run compile jobs. This could be used to unblock
301304
/// dependencies in parallel builds.
@@ -544,6 +547,16 @@ public struct Driver {
544547
self.numThreads = Self.determineNumThreads(&parsedOptions, compilerMode: compilerMode, diagnosticsEngine: diagnosticEngine)
545548
self.numParallelJobs = Self.determineNumParallelJobs(&parsedOptions, diagnosticsEngine: diagnosticEngine, env: env)
546549

550+
var mode = DigesterMode.api
551+
if let modeArg = parsedOptions.getLastArgument(.digesterMode)?.asSingle {
552+
if let digesterMode = DigesterMode(rawValue: modeArg) {
553+
mode = digesterMode
554+
} else {
555+
diagnosticsEngine.emit(Error.invalidArgumentValue(Option.digesterMode.spelling, modeArg))
556+
}
557+
}
558+
self.digesterMode = mode
559+
547560
Self.validateWarningControlArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
548561
Self.validateProfilingArgs(&parsedOptions,
549562
fileSystem: fileSystem,
@@ -597,8 +610,6 @@ public struct Driver {
597610
toolchain: toolchain,
598611
targetInfo: frontendTargetInfo)
599612

600-
Self.validateDigesterArgs(&parsedOptions, moduleOutputInfo: moduleOutputInfo, diagnosticEngine: diagnosticsEngine)
601-
602613
Self.validateSanitizerAddressUseOdrIndicatorFlag(&parsedOptions, diagnosticEngine: diagnosticsEngine, addressSanitizerEnabled: enabledSanitizers.contains(.address))
603614

604615
Self.validateSanitizerRecoverArgValues(&parsedOptions, diagnosticEngine: diagnosticsEngine, enabledSanitizers: enabledSanitizers)
@@ -674,23 +685,10 @@ public struct Driver {
674685
outputFileMap: self.outputFileMap,
675686
moduleName: moduleOutputInfo.name,
676687
projectDirectory: projectDirectory)
677-
self.apiBaselinePath = try Self.computeDigesterBaselineOutputPath(
678-
&parsedOptions,
679-
moduleOutputPath: self.moduleOutputInfo.output?.outputPath,
680-
type: .jsonAPIBaseline,
681-
isOutput: .emitAPIBaseline,
682-
outputPath: .emitAPIBaselinePath,
683-
compilerOutputType: compilerOutputType,
684-
compilerMode: compilerMode,
685-
outputFileMap: self.outputFileMap,
686-
moduleName: moduleOutputInfo.name,
687-
projectDirectory: projectDirectory)
688-
self.abiBaselinePath = try Self.computeDigesterBaselineOutputPath(
688+
self.digesterBaselinePath = try Self.computeDigesterBaselineOutputPath(
689689
&parsedOptions,
690690
moduleOutputPath: self.moduleOutputInfo.output?.outputPath,
691-
type: .jsonABIBaseline,
692-
isOutput: .emitABIBaseline,
693-
outputPath: .emitABIBaselinePath,
691+
mode: self.digesterMode,
694692
compilerOutputType: compilerOutputType,
695693
compilerMode: compilerMode,
696694
outputFileMap: self.outputFileMap,
@@ -733,6 +731,12 @@ public struct Driver {
733731
outputFileMap: self.outputFileMap,
734732
moduleName: moduleOutputInfo.name)
735733

734+
Self.validateDigesterArgs(&parsedOptions,
735+
moduleOutputInfo: moduleOutputInfo,
736+
digesterMode: self.digesterMode,
737+
swiftInterfacePath: self.swiftInterfacePath,
738+
diagnosticEngine: diagnosticsEngine)
739+
736740
try verifyOutputOptions()
737741
}
738742

@@ -2231,13 +2235,31 @@ extension Driver {
22312235

22322236
static func validateDigesterArgs(_ parsedOptions: inout ParsedOptions,
22332237
moduleOutputInfo: ModuleOutputInfo,
2238+
digesterMode: DigesterMode,
2239+
swiftInterfacePath: VirtualPath.Handle?,
22342240
diagnosticEngine: DiagnosticsEngine) {
22352241
if moduleOutputInfo.output?.isTopLevel != true {
2236-
for arg in parsedOptions.arguments(for: .emitAPIBaseline, .emitAPIBaselinePath,
2237-
.emitABIBaseline, .emitABIBaselinePath) {
2242+
for arg in parsedOptions.arguments(for: .emitDigesterBaseline, .emitDigesterBaselinePath, .compareToBaselinePath) {
22382243
diagnosticEngine.emit(Error.baselineGenerationRequiresTopLevelModule(arg.option.spelling))
22392244
}
22402245
}
2246+
2247+
if parsedOptions.hasArgument(.serializeBreakingChangesPath) && !parsedOptions.hasArgument(.compareToBaselinePath) {
2248+
diagnosticEngine.emit(Error.optionRequiresAnother(Option.serializeBreakingChangesPath.spelling,
2249+
Option.compareToBaselinePath.spelling))
2250+
}
2251+
if parsedOptions.hasArgument(.digesterBreakageAllowlistPath) && !parsedOptions.hasArgument(.compareToBaselinePath) {
2252+
diagnosticEngine.emit(Error.optionRequiresAnother(Option.digesterBreakageAllowlistPath.spelling,
2253+
Option.compareToBaselinePath.spelling))
2254+
}
2255+
if digesterMode == .abi && !parsedOptions.hasArgument(.enableLibraryEvolution) {
2256+
diagnosticEngine.emit(Error.optionRequiresAnother("\(Option.digesterMode.spelling) abi",
2257+
Option.enableLibraryEvolution.spelling))
2258+
}
2259+
if digesterMode == .abi && swiftInterfacePath == nil {
2260+
diagnosticEngine.emit(Error.optionRequiresAnother("\(Option.digesterMode.spelling) abi",
2261+
Option.emitModuleInterface.spelling))
2262+
}
22412263
}
22422264

22432265

@@ -2698,22 +2720,20 @@ extension Driver {
26982720
static func computeDigesterBaselineOutputPath(
26992721
_ parsedOptions: inout ParsedOptions,
27002722
moduleOutputPath: VirtualPath.Handle?,
2701-
type: FileType,
2702-
isOutput: Option,
2703-
outputPath: Option,
2723+
mode: DigesterMode,
27042724
compilerOutputType: FileType?,
27052725
compilerMode: CompilerMode,
27062726
outputFileMap: OutputFileMap?,
27072727
moduleName: String,
27082728
projectDirectory: VirtualPath.Handle?
27092729
) throws -> VirtualPath.Handle? {
27102730
// Only emit a baseline if at least of the arguments was provided.
2711-
guard parsedOptions.hasArgument(isOutput, outputPath) else { return nil }
2731+
guard parsedOptions.hasArgument(.emitDigesterBaseline, .emitDigesterBaselinePath) else { return nil }
27122732
return try computeModuleAuxiliaryOutputPath(&parsedOptions,
27132733
moduleOutputPath: moduleOutputPath,
2714-
type: type,
2715-
isOutput: isOutput,
2716-
outputPath: outputPath,
2734+
type: mode.baselineFileType,
2735+
isOutput: .emitDigesterBaseline,
2736+
outputPath: .emitDigesterBaselinePath,
27172737
compilerOutputType: compilerOutputType,
27182738
compilerMode: compilerMode,
27192739
outputFileMap: outputFileMap,

Sources/SwiftDriver/Jobs/APIDigesterJobs.swift

Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import TSCBasic
1414

15-
enum DigesterMode {
15+
enum DigesterMode: String {
1616
case api, abi
1717

1818
var baselineFileType: FileType {
@@ -32,22 +32,102 @@ enum DigesterMode {
3232
return .generateABIBaseline
3333
}
3434
}
35+
36+
var baselineComparisonJobKind: Job.Kind {
37+
switch self {
38+
case .api:
39+
return .compareAPIBaseline
40+
case .abi:
41+
return .compareABIBaseline
42+
}
43+
}
3544
}
3645

3746
extension Driver {
3847
mutating func digesterBaselineGenerationJob(modulePath: VirtualPath.Handle, outputPath: VirtualPath.Handle, mode: DigesterMode) throws -> Job {
3948
var commandLine = [Job.ArgTemplate]()
4049
commandLine.appendFlag("-dump-sdk")
4150

42-
if mode == .abi {
43-
commandLine.appendFlag("-abi")
51+
try addCommonDigesterOptions(&commandLine, modulePath: modulePath, mode: mode)
52+
53+
commandLine.appendFlag(.o)
54+
commandLine.appendPath(VirtualPath.lookup(outputPath))
55+
56+
return Job(
57+
moduleName: moduleOutputInfo.name,
58+
kind: mode.baselineGenerationJobKind,
59+
tool: .absolute(try toolchain.getToolPath(.swiftAPIDigester)),
60+
commandLine: commandLine,
61+
inputs: [.init(file: modulePath, type: .swiftModule)],
62+
primaryInputs: [],
63+
outputs: [.init(file: outputPath, type: mode.baselineFileType)],
64+
supportsResponseFiles: true
65+
)
66+
}
67+
68+
mutating func digesterCompareToBaselineJob(modulePath: VirtualPath.Handle, baselinePath: VirtualPath.Handle, mode: DigesterMode) throws -> Job {
69+
var commandLine = [Job.ArgTemplate]()
70+
commandLine.appendFlag("-diagnose-sdk")
71+
commandLine.appendFlag("-disable-fail-on-error")
72+
commandLine.appendFlag("-baseline-path")
73+
commandLine.appendPath(VirtualPath.lookup(baselinePath))
74+
75+
try addCommonDigesterOptions(&commandLine, modulePath: modulePath, mode: mode)
76+
77+
var serializedDiagnosticsPath: VirtualPath.Handle?
78+
if let arg = parsedOptions.getLastArgument(.serializeBreakingChangesPath)?.asSingle {
79+
let path = try VirtualPath.intern(path: arg)
80+
commandLine.appendFlag("-serialize-diagnostics-path")
81+
commandLine.appendPath(VirtualPath.lookup(path))
82+
serializedDiagnosticsPath = path
83+
}
84+
if let arg = parsedOptions.getLastArgument(.digesterBreakageAllowlistPath)?.asSingle {
85+
let path = try VirtualPath(path: arg)
86+
commandLine.appendFlag("-breakage-allowlist-path")
87+
commandLine.appendPath(path)
4488
}
4589

90+
var inputs: [TypedVirtualPath] = [.init(file: modulePath, type: .swiftModule),
91+
.init(file: baselinePath, type: mode.baselineFileType)]
92+
// If a module interface was emitted, treat it as an input in ABI mode.
93+
if let interfacePath = self.swiftInterfacePath, mode == .abi {
94+
inputs.append(.init(file: interfacePath, type: .swiftInterface))
95+
}
96+
97+
return Job(
98+
moduleName: moduleOutputInfo.name,
99+
kind: mode.baselineComparisonJobKind,
100+
tool: .absolute(try toolchain.getToolPath(.swiftAPIDigester)),
101+
commandLine: commandLine,
102+
inputs: inputs,
103+
primaryInputs: [],
104+
outputs: [.init(file: serializedDiagnosticsPath ?? VirtualPath.Handle.standardOutput, type: .diagnostics)],
105+
supportsResponseFiles: true
106+
)
107+
}
108+
109+
private mutating func addCommonDigesterOptions(_ commandLine: inout [Job.ArgTemplate],
110+
modulePath: VirtualPath.Handle,
111+
mode: DigesterMode) throws {
46112
commandLine.appendFlag("-module")
47113
commandLine.appendFlag(moduleOutputInfo.name)
114+
if mode == .abi {
115+
commandLine.appendFlag("-abi")
116+
commandLine.appendFlag("-use-interface-for-module")
117+
commandLine.appendFlag(moduleOutputInfo.name)
118+
}
48119

120+
// Add a search path for the emitted module, and its module interface if there is one.
121+
let searchPath = VirtualPath.lookup(modulePath).parentDirectory
49122
commandLine.appendFlag(.I)
50-
commandLine.appendPath(VirtualPath.lookup(modulePath).parentDirectory)
123+
commandLine.appendPath(searchPath)
124+
if let interfacePath = self.swiftInterfacePath {
125+
let interfaceSearchPath = VirtualPath.lookup(interfacePath).parentDirectory
126+
if interfaceSearchPath != searchPath {
127+
commandLine.appendFlag(.I)
128+
commandLine.appendPath(interfaceSearchPath)
129+
}
130+
}
51131

52132
commandLine.appendFlag(.target)
53133
commandLine.appendFlag(targetTriple.triple)
@@ -57,7 +137,6 @@ extension Driver {
57137
commandLine.append(.path(VirtualPath.lookup(sdkPath)))
58138
}
59139

60-
// Resource directory.
61140
commandLine.appendFlag(.resourceDir)
62141
commandLine.appendPath(VirtualPath.lookup(frontendTargetInfo.runtimeResourcePath.path))
63142

@@ -69,19 +148,5 @@ extension Driver {
69148
}
70149

71150
try commandLine.appendLast(.swiftVersion, from: &parsedOptions)
72-
73-
commandLine.appendFlag(.o)
74-
commandLine.appendPath(VirtualPath.lookup(outputPath))
75-
76-
return Job(
77-
moduleName: moduleOutputInfo.name,
78-
kind: mode.baselineGenerationJobKind,
79-
tool: .absolute(try toolchain.getToolPath(.swiftAPIDigester)),
80-
commandLine: commandLine,
81-
inputs: [.init(file: modulePath, type: .swiftModule)],
82-
primaryInputs: [],
83-
outputs: [.init(file: outputPath, type: mode.baselineFileType)],
84-
supportsResponseFiles: true
85-
)
86151
}
87152
}

Sources/SwiftDriver/Jobs/Job.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ public struct Job: Codable, Equatable, Hashable {
3939
case help
4040
case generateAPIBaseline = "generate-api-baseline"
4141
case generateABIBaseline = "generate-abi-baseline"
42+
case compareAPIBaseline = "compare-api-baseline"
43+
case compareABIBaseline = "compare-abi-baseline"
4244
}
4345

4446
public enum ArgTemplate: Equatable, Hashable {
@@ -228,6 +230,12 @@ extension Job : CustomStringConvertible {
228230

229231
case .generateABIBaseline:
230232
return "Generating ABI baseline file for module \(moduleName)"
233+
234+
case .compareAPIBaseline:
235+
return "Comparing API of \(moduleName) to baseline"
236+
237+
case .compareABIBaseline:
238+
return "Comparing ABI of \(moduleName) to baseline"
231239
}
232240
}
233241

@@ -251,7 +259,7 @@ extension Job.Kind {
251259
return true
252260

253261
case .autolinkExtract, .generateDSYM, .help, .link, .verifyDebugInfo, .moduleWrap,
254-
.generateAPIBaseline, .generateABIBaseline:
262+
.generateAPIBaseline, .generateABIBaseline, .compareAPIBaseline, .compareABIBaseline:
255263
return false
256264
}
257265
}
@@ -266,7 +274,8 @@ extension Job.Kind {
266274
.versionRequest, .autolinkExtract, .generateDSYM,
267275
.help, .link, .verifyDebugInfo, .scanDependencies,
268276
.emitSupportedFeatures, .moduleWrap, .verifyModuleInterface,
269-
.generateAPIBaseline, .generateABIBaseline:
277+
.generateAPIBaseline, .generateABIBaseline, .compareAPIBaseline,
278+
.compareABIBaseline:
270279
return false
271280
}
272281
}

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -465,11 +465,12 @@ extension Driver {
465465

466466
private mutating func addAPIDigesterJobs(addJob: (Job) -> Void) throws {
467467
guard let moduleOutputPath = moduleOutputInfo.output?.outputPath else { return }
468-
if let apiBaselinePath = self.apiBaselinePath {
469-
try addJob(digesterBaselineGenerationJob(modulePath: moduleOutputPath, outputPath: apiBaselinePath, mode: .api))
468+
if let apiBaselinePath = self.digesterBaselinePath {
469+
try addJob(digesterBaselineGenerationJob(modulePath: moduleOutputPath, outputPath: apiBaselinePath, mode: digesterMode))
470470
}
471-
if let abiBaselinePath = self.abiBaselinePath {
472-
try addJob(digesterBaselineGenerationJob(modulePath: moduleOutputPath, outputPath: abiBaselinePath, mode: .abi))
471+
if let baselineArg = parsedOptions.getLastArgument(.compareToBaselinePath)?.asSingle,
472+
let baselinePath = try? VirtualPath.intern(path: baselineArg) {
473+
addJob(try digesterCompareToBaselineJob(modulePath: moduleOutputPath, baselinePath: baselinePath, mode: digesterMode))
473474
}
474475
}
475476

0 commit comments

Comments
 (0)