Skip to content

Commit ed34738

Browse files
committed
PrebuiltModuleGen: teach the tool to dump a .dot file for module dependency visualization
1 parent 68bd331 commit ed34738

File tree

2 files changed

+65
-2
lines changed

2 files changed

+65
-2
lines changed

Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,57 @@ public struct SDKPrebuiltModuleInputsCollector {
236236
}
237237
}
238238

239+
extension InterModuleDependencyGraph {
240+
func dumpDotGraph(_ path: AbsolutePath, _ includingPCM: Bool) throws {
241+
func isPCM(_ dep: ModuleDependencyId) -> Bool {
242+
switch dep {
243+
case .clang:
244+
return true
245+
default:
246+
return false
247+
}
248+
}
249+
func dumpModuleName(_ stream: WritableByteStream, _ dep: ModuleDependencyId) {
250+
switch dep {
251+
case .swift(let name):
252+
stream <<< "\"\(name).swiftmodule\""
253+
case .clang(let name):
254+
stream <<< "\"\(name).pcm\""
255+
default:
256+
break
257+
}
258+
}
259+
try localFileSystem.writeFileContents(path) {Stream in
260+
Stream <<< "digraph {\n"
261+
for key in modules.keys {
262+
switch key {
263+
case .swift(let name):
264+
if name == mainModuleName {
265+
break
266+
}
267+
fallthrough
268+
case .clang:
269+
if !includingPCM && isPCM(key) {
270+
break
271+
}
272+
modules[key]!.directDependencies?.forEach { dep in
273+
if !includingPCM && isPCM(dep) {
274+
return
275+
}
276+
dumpModuleName(Stream, key)
277+
Stream <<< " -> "
278+
dumpModuleName(Stream, dep)
279+
Stream <<< ";\n"
280+
}
281+
default:
282+
break
283+
}
284+
}
285+
Stream <<< "}\n"
286+
}
287+
}
288+
}
289+
239290
extension Driver {
240291

241292
private mutating func generateSingleModuleBuildingJob(_ moduleName: String, _ prebuiltModuleDir: AbsolutePath,
@@ -282,12 +333,16 @@ extension Driver {
282333

283334
public mutating func generatePrebuitModuleGenerationJobs(with inputMap: [String: [PrebuiltModuleInput]],
284335
into prebuiltModuleDir: AbsolutePath,
285-
exhaustive: Bool) throws -> ([Job], [Job]) {
336+
exhaustive: Bool,
337+
dotGraphPath: AbsolutePath? = nil) throws -> ([Job], [Job]) {
286338
assert(sdkPath != nil)
287339
// Run the dependency scanner and update the dependency oracle with the results
288340
// We only need Swift dependencies here, so we don't need to invoke gatherModuleDependencies,
289341
// which also resolves versioned clang modules.
290342
let dependencyGraph = try performDependencyScan()
343+
if let dotGraphPath = dotGraphPath {
344+
try dependencyGraph.dumpDotGraph(dotGraphPath, false)
345+
}
291346
var jobs: [Job] = []
292347
var danglingJobs: [Job] = []
293348
var inputCount = 0

Sources/swift-build-sdk-interfaces/main.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ func getArgument(_ flag: String) -> String? {
3232
return nil
3333
}
3434

35+
func getArgumentAsPath(_ flag: String) throws -> AbsolutePath? {
36+
if let raw = getArgument(flag) {
37+
return try VirtualPath(path: raw).absolutePath
38+
}
39+
return nil
40+
}
41+
3542
guard let rawOutputDir = getArgument("-o") else {
3643
diagnosticsEngine.emit(.error("need to specify -o"))
3744
exit(1)
@@ -119,7 +126,8 @@ do {
119126
diagnosticsEngine: diagnosticsEngine,
120127
executor: executor,
121128
compilerExecutableDir: swiftcPath.parentDirectory)
122-
let (jobs, danglingJobs) = try driver.generatePrebuitModuleGenerationJobs(with: inputMap, into: outputDir, exhaustive: !coreMode)
129+
let (jobs, danglingJobs) = try driver.generatePrebuitModuleGenerationJobs(with: inputMap,
130+
into: outputDir, exhaustive: !coreMode, dotGraphPath: getArgumentAsPath("-dot-graph-path"))
123131
if verbose {
124132
Driver.stdErrQueue.sync {
125133
stderrStream <<< "job count: \(jobs.count + danglingJobs.count)\n"

0 commit comments

Comments
 (0)