Skip to content

Commit f26cb05

Browse files
committed
Initial API/ABI baseline generation support
This adds support for generating an API and/or ABI baseline as part of a driver build plan if a module is being emitted. As a result, build systems and higher-level tools no longer need to interact with swift-api-digester directly.
1 parent b754520 commit f26cb05

File tree

13 files changed

+366
-9
lines changed

13 files changed

+366
-9
lines changed

Sources/SwiftDriver/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ add_library(SwiftDriver
5757
"IncrementalCompilation/SourceFileDependencyGraph.swift"
5858
"IncrementalCompilation/TwoDMap.swift"
5959

60+
Jobs/APIDigesterJobs.swift
6061
Jobs/AutolinkExtractJob.swift
6162
Jobs/BackendJob.swift
6263
Jobs/CommandLineArguments.swift

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public struct Driver {
4141
case missingContextHashOnSwiftDependency(String)
4242
case dependencyScanningFailure(Int, String)
4343
case missingExternalDependency(String)
44+
case baselineGenerationRequiresTopLevelModule(String)
4445

4546
public var description: String {
4647
switch self {
@@ -100,6 +101,8 @@ public struct Driver {
100101
return "unable to load output file map '\(path)': no such file or directory"
101102
case .missingExternalDependency(let moduleName):
102103
return "Missing External dependency info for module: \(moduleName)"
104+
case .baselineGenerationRequiresTopLevelModule(let arg):
105+
return "generating a baseline with '\(arg)' is only supported with '-emit-module' or '-emit-module-path'"
103106
}
104107
}
105108
}
@@ -288,6 +291,12 @@ public struct Driver {
288291
/// Path to the Swift module source information file.
289292
let moduleSourceInfoPath: VirtualPath.Handle?
290293

294+
/// Path to the module API baseline file.
295+
let apiBaselinePath: VirtualPath.Handle?
296+
297+
/// Path to the module ABI baseline file.
298+
let abiBaselinePath: VirtualPath.Handle?
299+
291300
/// Force the driver to emit the module first and then run compile jobs. This could be used to unblock
292301
/// dependencies in parallel builds.
293302
var forceEmitModuleBeforeCompile: Bool = false
@@ -587,7 +596,9 @@ public struct Driver {
587596
diagnosticEngine: diagnosticEngine,
588597
toolchain: toolchain,
589598
targetInfo: frontendTargetInfo)
590-
599+
600+
Self.validateDigesterArgs(&parsedOptions, moduleOutputInfo: moduleOutputInfo, diagnosticEngine: diagnosticsEngine)
601+
591602
Self.validateSanitizerAddressUseOdrIndicatorFlag(&parsedOptions, diagnosticEngine: diagnosticsEngine, addressSanitizerEnabled: enabledSanitizers.contains(.address))
592603

593604
Self.validateSanitizerRecoverArgValues(&parsedOptions, diagnosticEngine: diagnosticsEngine, enabledSanitizers: enabledSanitizers)
@@ -663,6 +674,28 @@ public struct Driver {
663674
outputFileMap: self.outputFileMap,
664675
moduleName: moduleOutputInfo.name,
665676
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(
689+
&parsedOptions,
690+
moduleOutputPath: self.moduleOutputInfo.output?.outputPath,
691+
type: .jsonABIBaseline,
692+
isOutput: .emitABIBaseline,
693+
outputPath: .emitABIBaselinePath,
694+
compilerOutputType: compilerOutputType,
695+
compilerMode: compilerMode,
696+
outputFileMap: self.outputFileMap,
697+
moduleName: moduleOutputInfo.name,
698+
projectDirectory: projectDirectory)
666699
self.swiftInterfacePath = try Self.computeSupplementaryOutputPath(
667700
&parsedOptions, type: .swiftInterface, isOutputOptions: [.emitModuleInterface],
668701
outputPath: .emitModuleInterfacePath,
@@ -2196,6 +2229,18 @@ extension Driver {
21962229
}
21972230
}
21982231

2232+
static func validateDigesterArgs(_ parsedOptions: inout ParsedOptions,
2233+
moduleOutputInfo: ModuleOutputInfo,
2234+
diagnosticEngine: DiagnosticsEngine) {
2235+
if moduleOutputInfo.output?.isTopLevel != true {
2236+
for arg in parsedOptions.arguments(for: .emitAPIBaseline, .emitAPIBaselinePath,
2237+
.emitABIBaseline, .emitABIBaselinePath) {
2238+
diagnosticEngine.emit(Error.baselineGenerationRequiresTopLevelModule(arg.option.spelling))
2239+
}
2240+
}
2241+
}
2242+
2243+
21992244
static func validateProfilingArgs(_ parsedOptions: inout ParsedOptions,
22002245
fileSystem: FileSystem,
22012246
workingDirectory: AbsolutePath?,
@@ -2650,6 +2695,33 @@ extension Driver {
26502695
projectDirectory: projectDirectory)
26512696
}
26522697

2698+
static func computeDigesterBaselineOutputPath(
2699+
_ parsedOptions: inout ParsedOptions,
2700+
moduleOutputPath: VirtualPath.Handle?,
2701+
type: FileType,
2702+
isOutput: Option,
2703+
outputPath: Option,
2704+
compilerOutputType: FileType?,
2705+
compilerMode: CompilerMode,
2706+
outputFileMap: OutputFileMap?,
2707+
moduleName: String,
2708+
projectDirectory: VirtualPath.Handle?
2709+
) throws -> VirtualPath.Handle? {
2710+
// Only emit a baseline if at least of the arguments was provided.
2711+
guard parsedOptions.hasArgument(isOutput, outputPath) else { return nil }
2712+
return try computeModuleAuxiliaryOutputPath(&parsedOptions,
2713+
moduleOutputPath: moduleOutputPath,
2714+
type: type,
2715+
isOutput: isOutput,
2716+
outputPath: outputPath,
2717+
compilerOutputType: compilerOutputType,
2718+
compilerMode: compilerMode,
2719+
outputFileMap: outputFileMap,
2720+
moduleName: moduleName,
2721+
projectDirectory: projectDirectory)
2722+
}
2723+
2724+
26532725

26542726
/// Determine the output path for a module auxiliary output.
26552727
static func computeModuleAuxiliaryOutputPath(
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//===- APIDigesterJobs.swift - Baseline Generation and API/ABI Comparison -===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import TSCBasic
14+
15+
enum DigesterMode {
16+
case api, abi
17+
18+
var baselineFileType: FileType {
19+
switch self {
20+
case .api:
21+
return .jsonAPIBaseline
22+
case .abi:
23+
return .jsonABIBaseline
24+
}
25+
}
26+
27+
var baselineGenerationJobKind: Job.Kind {
28+
switch self {
29+
case .api:
30+
return .generateAPIBaseline
31+
case .abi:
32+
return .generateABIBaseline
33+
}
34+
}
35+
}
36+
37+
extension Driver {
38+
mutating func digesterBaselineGenerationJob(modulePath: VirtualPath.Handle, outputPath: VirtualPath.Handle, mode: DigesterMode) throws -> Job {
39+
var commandLine = [Job.ArgTemplate]()
40+
commandLine.appendFlag("-dump-sdk")
41+
42+
if mode == .abi {
43+
commandLine.appendFlag("-abi")
44+
}
45+
46+
commandLine.appendFlag("-module")
47+
commandLine.appendFlag(moduleOutputInfo.name)
48+
49+
commandLine.appendFlag(.I)
50+
commandLine.appendPath(VirtualPath.lookup(modulePath).parentDirectory)
51+
52+
commandLine.appendFlag(.target)
53+
commandLine.appendFlag(targetTriple.triple)
54+
55+
if let sdkPath = frontendTargetInfo.sdkPath?.path {
56+
commandLine.appendFlag(.sdk)
57+
commandLine.append(.path(VirtualPath.lookup(sdkPath)))
58+
}
59+
60+
// Resource directory.
61+
commandLine.appendFlag(.resourceDir)
62+
commandLine.appendPath(VirtualPath.lookup(frontendTargetInfo.runtimeResourcePath.path))
63+
64+
try commandLine.appendAll(.I, from: &parsedOptions)
65+
try commandLine.appendAll(.F, from: &parsedOptions)
66+
for systemFramework in parsedOptions.arguments(for: .Fsystem) {
67+
commandLine.appendFlag(.iframework)
68+
commandLine.appendFlag(systemFramework.argument.asSingle)
69+
}
70+
71+
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+
)
86+
}
87+
}

Sources/SwiftDriver/Jobs/CompileJob.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ extension Driver {
9393
.privateSwiftInterface, .swiftSourceInfoFile, .diagnostics, .objcHeader, .swiftDeps,
9494
.remap, .tbd, .moduleTrace, .yamlOptimizationRecord, .bitstreamOptimizationRecord, .pcm,
9595
.pch, .clangModuleMap, .jsonCompilerFeatures, .jsonTargetInfo, .jsonSwiftArtifacts,
96-
.indexUnitOutputPath, .modDepCache, nil:
96+
.indexUnitOutputPath, .modDepCache, .jsonAPIBaseline, .jsonABIBaseline, nil:
9797
return false
9898
}
9999
}
@@ -444,7 +444,7 @@ extension FileType {
444444
.diagnostics, .objcHeader, .image, .swiftDeps, .moduleTrace, .tbd,
445445
.yamlOptimizationRecord, .bitstreamOptimizationRecord, .swiftInterface,
446446
.privateSwiftInterface, .swiftSourceInfoFile, .clangModuleMap, .jsonSwiftArtifacts,
447-
.indexUnitOutputPath, .modDepCache:
447+
.indexUnitOutputPath, .modDepCache, .jsonAPIBaseline, .jsonABIBaseline:
448448
fatalError("Output type can never be a primary output")
449449
}
450450
}

Sources/SwiftDriver/Jobs/Job.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public struct Job: Codable, Equatable, Hashable {
3737
case scanDependencies = "scan-dependencies"
3838
case verifyModuleInterface = "verify-emitted-module-interface"
3939
case help
40+
case generateAPIBaseline = "generate-api-baseline"
41+
case generateABIBaseline = "generate-abi-baseline"
4042
}
4143

4244
public enum ArgTemplate: Equatable, Hashable {
@@ -220,6 +222,12 @@ extension Job : CustomStringConvertible {
220222

221223
case .verifyModuleInterface:
222224
return "Verifying emitted module interface for module \(moduleName)"
225+
226+
case .generateAPIBaseline:
227+
return "Generating API baseline file for module \(moduleName)"
228+
229+
case .generateABIBaseline:
230+
return "Generating ABI baseline file for module \(moduleName)"
223231
}
224232
}
225233

@@ -242,7 +250,8 @@ extension Job.Kind {
242250
.versionRequest, .emitSupportedFeatures, .scanDependencies, .verifyModuleInterface:
243251
return true
244252

245-
case .autolinkExtract, .generateDSYM, .help, .link, .verifyDebugInfo, .moduleWrap:
253+
case .autolinkExtract, .generateDSYM, .help, .link, .verifyDebugInfo, .moduleWrap,
254+
.generateAPIBaseline, .generateABIBaseline:
246255
return false
247256
}
248257
}
@@ -256,7 +265,8 @@ extension Job.Kind {
256265
.generatePCM, .dumpPCM, .interpret, .repl, .printTargetInfo,
257266
.versionRequest, .autolinkExtract, .generateDSYM,
258267
.help, .link, .verifyDebugInfo, .scanDependencies,
259-
.emitSupportedFeatures, .moduleWrap, .verifyModuleInterface:
268+
.emitSupportedFeatures, .moduleWrap, .verifyModuleInterface,
269+
.generateAPIBaseline, .generateABIBaseline:
260270
return false
261271
}
262272
}

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ extension Driver {
147147
addJobBeforeCompiles: addJobBeforeCompiles,
148148
addCompileJobGroup: addCompileJobGroup,
149149
addJobAfterCompiles: addJobAfterCompiles)
150+
try addAPIDigesterJobs(addJob: addJobAfterCompiles)
150151
try addLinkAndPostLinkJobs(linkerInputs: linkerInputs,
151152
debugInfo: debugInfo,
152153
addJob: addJobAfterCompiles)
@@ -462,6 +463,16 @@ extension Driver {
462463
}
463464
}
464465

466+
private mutating func addAPIDigesterJobs(addJob: (Job) -> Void) throws {
467+
guard let moduleOutputPath = moduleOutputInfo.output?.outputPath else { return }
468+
if let apiBaselinePath = self.apiBaselinePath {
469+
try addJob(digesterBaselineGenerationJob(modulePath: moduleOutputPath, outputPath: apiBaselinePath, mode: .api))
470+
}
471+
if let abiBaselinePath = self.abiBaselinePath {
472+
try addJob(digesterBaselineGenerationJob(modulePath: moduleOutputPath, outputPath: abiBaselinePath, mode: .abi))
473+
}
474+
}
475+
465476
private mutating func addLinkAndPostLinkJobs(
466477
linkerInputs: [TypedVirtualPath],
467478
debugInfo: DebugInfo,

Sources/SwiftDriver/Toolchains/DarwinToolchain.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ import SwiftOptions
7878
return try lookup(executable: "dwarfdump")
7979
case .swiftHelp:
8080
return try lookup(executable: "swift-help")
81+
case .swiftAPIDigester:
82+
return try lookup(executable: "swift-api-digester")
8183
}
8284
}
8385

Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ import TSCBasic
8383
return try lookup(executable: "dwarfdump")
8484
case .swiftHelp:
8585
return try lookup(executable: "swift-help")
86+
case .swiftAPIDigester:
87+
return try lookup(executable: "swift-api-digester")
8688
}
8789
}
8890

Sources/SwiftDriver/Toolchains/Toolchain.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public enum Tool: Hashable {
2323
case lldb
2424
case dwarfdump
2525
case swiftHelp
26+
case swiftAPIDigester
2627
}
2728

2829
/// Describes a toolchain, which includes information about compilers, linkers

Sources/SwiftDriver/Toolchains/WebAssemblyToolchain.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ import SwiftOptions
107107
return try lookup(executable: "dwarfdump")
108108
case .swiftHelp:
109109
return try lookup(executable: "swift-help")
110+
case .swiftAPIDigester:
111+
return try lookup(executable: "swift-api-digester")
110112
}
111113
}
112114

0 commit comments

Comments
 (0)