Skip to content

Commit 58f2a89

Browse files
committed
[Explicit Module Builds] Add option to invoke the command-line executable dependency scanner instead of using libSwiftScan
1 parent 7b7b2f0 commit 58f2a89

File tree

2 files changed

+89
-17
lines changed

2 files changed

+89
-17
lines changed

Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift

Lines changed: 87 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -99,31 +99,101 @@ internal extension Driver {
9999
let scannerJob = try dependencyScanningJob()
100100
let forceResponseFiles = parsedOptions.hasArgument(.driverForceResponseFiles)
101101
let cwd = workingDirectory ?? fileSystem.currentWorkingDirectory!
102-
var command = try itemizedJobCommand(of: scannerJob,
103-
forceResponseFiles: forceResponseFiles,
104-
using: executor.resolver)
105-
// Remove the tool executable to only leave the arguments
106-
command.removeFirst()
107-
let dependencyGraph =
108-
try interModuleDependencyOracle.getDependencies(workingDirectory: cwd,
109-
commandLine: command)
102+
103+
let dependencyGraph: InterModuleDependencyGraph
104+
105+
if (parsedOptions.hasArgument(.driverScanDependenciesNonLib)) {
106+
107+
var command = try itemizedJobCommand(of: scannerJob,
108+
forceResponseFiles: forceResponseFiles,
109+
using: executor.resolver)
110+
// Remove the tool executable to only leave the arguments
111+
command.removeFirst()
112+
dependencyGraph =
113+
try interModuleDependencyOracle.getDependencies(workingDirectory: cwd,
114+
commandLine: command)
115+
} else {
116+
// Fallback to legacy invocation of the dependency scanner with
117+
// `swift-frontend -scan-dependencies`
118+
print("Dependency Scanner invocation:")
119+
print(try executor.description(of: scannerJob, forceResponseFiles: false))
120+
dependencyGraph =
121+
try self.executor.execute(job: scannerJob,
122+
capturingJSONOutputAs: InterModuleDependencyGraph.self,
123+
forceResponseFiles: forceResponseFiles,
124+
recordedInputModificationDates: recordedInputModificationDates)
125+
}
110126
return dependencyGraph
111127
}
112128

113129
mutating func performBatchDependencyScan(moduleInfos: [BatchScanModuleInfo])
114130
throws -> [ModuleDependencyId: [InterModuleDependencyGraph]] {
115131
let batchScanningJob = try batchDependencyScanningJob(for: moduleInfos)
116132
let forceResponseFiles = parsedOptions.hasArgument(.driverForceResponseFiles)
117-
let cwd = workingDirectory ?? fileSystem.currentWorkingDirectory!
118-
var command = try itemizedJobCommand(of: batchScanningJob,
119-
forceResponseFiles: forceResponseFiles,
120-
using: executor.resolver)
121-
// Remove the tool executable to only leave the arguments
122-
command.removeFirst()
133+
134+
let moduleVersionedGraphMap: [ModuleDependencyId: [InterModuleDependencyGraph]]
135+
if (!parsedOptions.hasArgument(.driverScanDependenciesNonLib)) {
136+
let cwd = workingDirectory ?? fileSystem.currentWorkingDirectory!
137+
var command = try itemizedJobCommand(of: batchScanningJob,
138+
forceResponseFiles: forceResponseFiles,
139+
using: executor.resolver)
140+
// Remove the tool executable to only leave the arguments
141+
command.removeFirst()
142+
moduleVersionedGraphMap =
143+
try interModuleDependencyOracle.getBatchDependencies(workingDirectory: cwd,
144+
commandLine: command,
145+
batchInfos: moduleInfos)
146+
} else {
147+
// Fallback to legacy invocation of the dependency scanner with
148+
// `swift-frontend -scan-dependencies`
149+
print("Dependency Scanner (batch) invocation:")
150+
print(try executor.description(of: batchScanningJob, forceResponseFiles: false))
151+
moduleVersionedGraphMap = try executeLegacyBatchScan(moduleInfos: moduleInfos,
152+
batchScanningJob: batchScanningJob,
153+
forceResponseFiles: forceResponseFiles)
154+
}
155+
return moduleVersionedGraphMap
156+
}
157+
158+
// Perform a batch scan by invoking the command-line dependency scanner and decoding the resulting
159+
// JSON.
160+
fileprivate func executeLegacyBatchScan(moduleInfos: [BatchScanModuleInfo],
161+
batchScanningJob: Job,
162+
forceResponseFiles: Bool)
163+
throws -> [ModuleDependencyId: [InterModuleDependencyGraph]] {
164+
let batchScanResult =
165+
try self.executor.execute(job: batchScanningJob,
166+
forceResponseFiles: forceResponseFiles,
167+
recordedInputModificationDates: recordedInputModificationDates)
168+
let success = batchScanResult.exitStatus == .terminated(code: EXIT_SUCCESS)
169+
guard success else {
170+
throw JobExecutionError.jobFailedWithNonzeroExitCode(
171+
type(of: executor).computeReturnCode(exitStatus: batchScanResult.exitStatus),
172+
try batchScanResult.utf8stderrOutput())
173+
}
174+
// Decode the resulting dependency graphs and build a dictionary from a moduleId to
175+
// a set of dependency graphs that were built for it
123176
let moduleVersionedGraphMap =
124-
try interModuleDependencyOracle.getBatchDependencies(workingDirectory: cwd,
125-
commandLine: command,
126-
batchInfos: moduleInfos)
177+
try moduleInfos.reduce(into: [ModuleDependencyId: [InterModuleDependencyGraph]]()) {
178+
let moduleId: ModuleDependencyId
179+
let dependencyGraphPath: VirtualPath
180+
switch $1 {
181+
case .swift(let swiftModuleBatchScanInfo):
182+
moduleId = .swift(swiftModuleBatchScanInfo.swiftModuleName)
183+
dependencyGraphPath = try VirtualPath(path: swiftModuleBatchScanInfo.output)
184+
case .clang(let clangModuleBatchScanInfo):
185+
moduleId = .clang(clangModuleBatchScanInfo.clangModuleName)
186+
dependencyGraphPath = try VirtualPath(path: clangModuleBatchScanInfo.output)
187+
}
188+
let contents = try fileSystem.readFileContents(dependencyGraphPath)
189+
let decodedGraph = try JSONDecoder().decode(InterModuleDependencyGraph.self,
190+
from: Data(contents.contents))
191+
if $0[moduleId] != nil {
192+
$0[moduleId]!.append(decodedGraph)
193+
} else {
194+
$0[moduleId] = [decodedGraph]
195+
}
196+
}
127197
return moduleVersionedGraphMap
128198
}
129199

Sources/SwiftOptions/ExtraOptions.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
extension Option {
1313
public static let driverPrintGraphviz: Option = Option("-driver-print-graphviz", .flag, attributes: [.helpHidden, .doesNotAffectIncrementalBuild], helpText: "Write the job graph as a graphviz file", group: .internalDebug)
1414
public static let driverExplicitModuleBuild: Option = Option("-experimental-explicit-module-build", .flag, attributes: [.helpHidden], helpText: "Prebuild module dependencies to make them explicit")
15+
public static let driverScanDependenciesNonLib: Option = Option("-nonlib-dependency-scanner", .flag, attributes: [.helpHidden], helpText: "Use calls to `swift-frontend -scan-dependencies` instead of dedicated dependency scanning library")
1516
public static let driverWarnUnusedOptions: Option = Option("-driver-warn-unused-options", .flag, attributes: [.helpHidden], helpText: "Emit warnings for any provided options which are unused by the driver.")
1617
public static let emitModuleSeparately: Option = Option("-experimental-emit-module-separately", .flag, attributes: [.helpHidden], helpText: "Emit module files as a distinct job")
1718

1819
public static var extraOptions: [Option] {
1920
return [
2021
Option.driverPrintGraphviz,
2122
Option.driverExplicitModuleBuild,
23+
Option.driverScanDependenciesNonLib,
2224
Option.driverWarnUnusedOptions,
2325
Option.emitModuleSeparately
2426
]

0 commit comments

Comments
 (0)