Skip to content

PrebuiltModuleGen: teach the tool to dump a .dot file for module dependency visualization #709

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 56 additions & 1 deletion Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,57 @@ public struct SDKPrebuiltModuleInputsCollector {
}
}

extension InterModuleDependencyGraph {
func dumpDotGraph(_ path: AbsolutePath, _ includingPCM: Bool) throws {
func isPCM(_ dep: ModuleDependencyId) -> Bool {
switch dep {
case .clang:
return true
default:
return false
}
}
func dumpModuleName(_ stream: WritableByteStream, _ dep: ModuleDependencyId) {
switch dep {
case .swift(let name):
stream <<< "\"\(name).swiftmodule\""
case .clang(let name):
stream <<< "\"\(name).pcm\""
default:
break
}
}
try localFileSystem.writeFileContents(path) {Stream in
Stream <<< "digraph {\n"
for key in modules.keys {
switch key {
case .swift(let name):
if name == mainModuleName {
break
}
fallthrough
case .clang:
if !includingPCM && isPCM(key) {
break
}
modules[key]!.directDependencies?.forEach { dep in
if !includingPCM && isPCM(dep) {
return
}
dumpModuleName(Stream, key)
Stream <<< " -> "
dumpModuleName(Stream, dep)
Stream <<< ";\n"
}
default:
break
}
}
Stream <<< "}\n"
}
}
}

extension Driver {

private mutating func generateSingleModuleBuildingJob(_ moduleName: String, _ prebuiltModuleDir: AbsolutePath,
Expand Down Expand Up @@ -285,12 +336,16 @@ extension Driver {

public mutating func generatePrebuitModuleGenerationJobs(with inputMap: [String: [PrebuiltModuleInput]],
into prebuiltModuleDir: AbsolutePath,
exhaustive: Bool) throws -> ([Job], [Job]) {
exhaustive: Bool,
dotGraphPath: AbsolutePath? = nil) throws -> ([Job], [Job]) {
assert(sdkPath != nil)
// Run the dependency scanner and update the dependency oracle with the results
// We only need Swift dependencies here, so we don't need to invoke gatherModuleDependencies,
// which also resolves versioned clang modules.
let dependencyGraph = try performDependencyScan()
if let dotGraphPath = dotGraphPath {
try dependencyGraph.dumpDotGraph(dotGraphPath, false)
}
var jobs: [Job] = []
var danglingJobs: [Job] = []
var inputCount = 0
Expand Down
10 changes: 9 additions & 1 deletion Sources/swift-build-sdk-interfaces/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ func getArgument(_ flag: String) -> String? {
return nil
}

func getArgumentAsPath(_ flag: String) throws -> AbsolutePath? {
if let raw = getArgument(flag) {
return try VirtualPath(path: raw).absolutePath
}
return nil
}

guard let rawOutputDir = getArgument("-o") else {
diagnosticsEngine.emit(.error("need to specify -o"))
exit(1)
Expand Down Expand Up @@ -119,7 +126,8 @@ do {
diagnosticsEngine: diagnosticsEngine,
executor: executor,
compilerExecutableDir: swiftcPath.parentDirectory)
let (jobs, danglingJobs) = try driver.generatePrebuitModuleGenerationJobs(with: inputMap, into: outputDir, exhaustive: !coreMode)
let (jobs, danglingJobs) = try driver.generatePrebuitModuleGenerationJobs(with: inputMap,
into: outputDir, exhaustive: !coreMode, dotGraphPath: getArgumentAsPath("-dot-graph-path"))
if verbose {
Driver.stdErrQueue.sync {
stderrStream <<< "job count: \(jobs.count + danglingJobs.count)\n"
Expand Down