in Sources/Commands/APIDigester.swift [57:166]
func emitAPIBaseline(
for modulesToDiff: Set<String>,
at baselineDir: AbsolutePath?,
force: Bool,
outputStream: OutputByteStream,
logLevel: Diagnostic.Severity
) throws -> AbsolutePath {
var modulesToDiff = modulesToDiff
let apiDiffDir = inputBuildParameters.apiDiff
let baselineDir = (baselineDir ?? apiDiffDir).appending(component: baselineRevision.identifier)
let baselinePath: (String)->AbsolutePath = { module in
baselineDir.appending(component: module + ".json")
}
if !force {
// Baselines which already exist don't need to be regenerated.
modulesToDiff = modulesToDiff.filter {
!localFileSystem.exists(baselinePath($0))
}
}
guard !modulesToDiff.isEmpty else {
// If none of the baselines need to be regenerated, return.
return baselineDir
}
// Setup a temporary directory where we can checkout and build the baseline treeish.
let baselinePackageRoot = apiDiffDir.appending(component: "\(baselineRevision.identifier)-checkout")
if localFileSystem.exists(baselinePackageRoot) {
try localFileSystem.removeFileTree(baselinePackageRoot)
}
// Clone the current package in a sandbox and checkout the baseline revision.
let repositoryProvider = GitRepositoryProvider()
let specifier = RepositorySpecifier(path: baselinePackageRoot)
let workingCopy = try repositoryProvider.createWorkingCopy(
repository: specifier,
sourcePath: packageRoot,
at: baselinePackageRoot,
editable: false
)
try workingCopy.checkout(revision: baselineRevision)
// Create the workspace for this package.
let workspace = try Workspace(forRootPackage: baselinePackageRoot)
let graph = try workspace.loadPackageGraph(
rootPath: baselinePackageRoot,
observabilityScope: self.observabilityScope
)
// Don't emit a baseline for a module that didn't exist yet in this revision.
modulesToDiff.formIntersection(graph.apiDigesterModules)
// Abort if we weren't able to load the package graph.
if observabilityScope.errorsReported {
throw Diagnostics.fatalError
}
// Update the data path input build parameters so it's built in the sandbox.
var buildParameters = inputBuildParameters
buildParameters.dataPath = workspace.location.workingDirectory
// Build the baseline module.
// FIXME: We need to implement the build tool invocation closure here so that build tool plugins work with the APIDigester. rdar://86112934
let buildOp = BuildOperation(
buildParameters: buildParameters,
cacheBuildManifest: false,
packageGraphLoader: { graph },
buildToolPluginInvoker: { _ in [:] },
outputStream: outputStream,
logLevel: logLevel,
fileSystem: localFileSystem,
observabilityScope: observabilityScope
)
try buildOp.build()
// Dump the SDK JSON.
try localFileSystem.createDirectory(baselineDir, recursive: true)
let group = DispatchGroup()
let semaphore = DispatchSemaphore(value: Int(buildParameters.jobs))
let errors = ThreadSafeArrayStore<Swift.Error>()
for module in modulesToDiff {
semaphore.wait()
DispatchQueue.sharedConcurrent.async(group: group) {
do {
try apiDigesterTool.emitAPIBaseline(
to: baselinePath(module),
for: module,
buildPlan: buildOp.buildPlan!
)
} catch {
errors.append(error)
}
semaphore.signal()
}
}
group.wait()
for error in errors.get() {
observabilityScope.emit(error)
}
if observabilityScope.errorsReported {
throw Diagnostics.fatalError
}
return baselineDir
}