Skip to content

Commit 3b56acc

Browse files
authored
Merge pull request #2803 from artemcm/PMGraphVizBiz
Add an option to output a dot graph of the build manifest.
2 parents cd5ed28 + 4b545be commit 3b56acc

File tree

6 files changed

+91
-3
lines changed

6 files changed

+91
-3
lines changed

Sources/Build/ManifestBuilder.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ public class LLBuildManifestBuilder {
7272
createProductCommand(description)
7373
}
7474

75+
// Output a dot graph
76+
if buildParameters.printManifestGraphviz {
77+
var serializer = DOTManifestSerializer(manifest: manifest)
78+
serializer.writeDOT(to: &stdoutStream)
79+
stdoutStream.flush()
80+
}
81+
7582
try ManifestWriter().write(manifest, at: path)
7683
}
7784

Sources/Commands/Options.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ public class ToolOptions {
103103
/// Whether to use the explicit module build flow (with the integrated driver)
104104
public var useExplicitModuleBuild: Bool = false
105105

106+
/// Whether to output a graphviz file visualization of the combined job graph for all targets
107+
public var printManifestGraphviz: Bool = false
108+
106109
/// The build system to use.
107110
public var buildSystem: BuildSystemKind {
108111
// Force the Xcode build system if we want to build more than one arch.

Sources/Commands/SwiftTool.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,11 @@ public class SwiftTool<Options: ToolOptions> {
464464
option: parser.add(option: "--experimental-explicit-module-build", kind: Bool.self, usage: nil),
465465
to: { $0.useExplicitModuleBuild = $1 })
466466

467+
binder.bind(
468+
option: parser.add(option: "--print-manifest-job-graph", kind: Bool.self,
469+
usage: "Write the command graph for the build manifest as a graphviz file"),
470+
to: { $0.printManifestGraphviz = $1 })
471+
467472
binder.bind(
468473
option: parser.add(option: "--build-system", kind: BuildSystemKind.self, usage: nil),
469474
to: { $0._buildSystem = $1 })
@@ -822,7 +827,8 @@ public class SwiftTool<Options: ToolOptions> {
822827
emitSwiftModuleSeparately: options.emitSwiftModuleSeparately,
823828
useIntegratedSwiftDriver: options.useIntegratedSwiftDriver,
824829
useExplicitModuleBuild: options.useExplicitModuleBuild,
825-
isXcodeBuildSystemEnabled: options.buildSystem == .xcode
830+
isXcodeBuildSystemEnabled: options.buildSystem == .xcode,
831+
printManifestGraphviz: options.printManifestGraphviz
826832
)
827833
})
828834
}()

Sources/LLBuildManifest/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_library(LLBuildManifest
1010
BuildManifest.swift
1111
Command.swift
1212
ManifestWriter.swift
13+
DOTManifestSerializer.swift
1314
Node.swift
1415
Target.swift
1516
Tools.swift)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import TSCBasic
12+
13+
/// Serializes an LLBuildManifest graph to a .dot file
14+
public struct DOTManifestSerializer {
15+
var kindCounter = [String: Int]()
16+
var hasEmittedStyling = Set<String>()
17+
let manifest: BuildManifest
18+
19+
/// Creates a serializer that will serialize the given manifest.
20+
public init(manifest: BuildManifest) {
21+
self.manifest = manifest
22+
}
23+
24+
/// Gets a unique label for a job name
25+
mutating func label(for command: Command) -> String {
26+
let toolName = "\(type(of: command.tool).name)"
27+
var label = toolName
28+
if let count = kindCounter[label] {
29+
label += " \(count)"
30+
}
31+
kindCounter[toolName, default: 0] += 1
32+
return label
33+
}
34+
35+
/// Quote the name and escape the quotes and backslashes
36+
func quoteName(_ name: String) -> String {
37+
return "\"" + name.replacingOccurrences(of: "\"", with: "\\\"")
38+
.replacingOccurrences(of: "\\", with: "\\\\") + "\""
39+
}
40+
41+
public mutating func writeDOT<Stream: TextOutputStream>(to stream: inout Stream) {
42+
stream.write("digraph Jobs {\n")
43+
for (name, command) in manifest.commands {
44+
let jobName = quoteName(label(for: command))
45+
if !hasEmittedStyling.contains(jobName) {
46+
stream.write(" \(jobName) [style=bold];")
47+
stream.write("// \(name)\n")
48+
}
49+
for input in command.tool.inputs {
50+
let inputName = quoteName(input.name)
51+
if hasEmittedStyling.insert(inputName).inserted {
52+
stream.write(" \(inputName) [fontsize=12];\n")
53+
}
54+
stream.write(" \(inputName) -> \(jobName) [color=blue];\n")
55+
}
56+
for output in command.tool.outputs {
57+
let outputName = quoteName(output.name)
58+
if hasEmittedStyling.insert(outputName).inserted {
59+
stream.write(" \(outputName) [fontsize=12];\n")
60+
}
61+
stream.write(" \(jobName) -> \(outputName) [color=green];\n")
62+
}
63+
}
64+
stream.write("}\n")
65+
}
66+
}

Sources/SPMBuildCore/BuildParameters.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,12 @@ public struct BuildParameters: Encodable {
9595
/// to a separate process.
9696
public var useIntegratedSwiftDriver: Bool
9797

98-
// Whether to use the explicit module build flow (with the integrated driver)
98+
/// Whether to use the explicit module build flow (with the integrated driver)
9999
public var useExplicitModuleBuild: Bool
100100

101+
/// Whether to output a graphviz file visualization of the combined job graph for all targets
102+
public var printManifestGraphviz: Bool
103+
101104
/// Whether to create dylibs for dynamic library products.
102105
public var shouldCreateDylibForDynamicProducts: Bool
103106

@@ -147,7 +150,8 @@ public struct BuildParameters: Encodable {
147150
emitSwiftModuleSeparately: Bool = false,
148151
useIntegratedSwiftDriver: Bool = false,
149152
useExplicitModuleBuild: Bool = false,
150-
isXcodeBuildSystemEnabled: Bool = false
153+
isXcodeBuildSystemEnabled: Bool = false,
154+
printManifestGraphviz: Bool = false
151155
) {
152156
self.dataPath = dataPath
153157
self.configuration = configuration
@@ -171,6 +175,7 @@ public struct BuildParameters: Encodable {
171175
self.useIntegratedSwiftDriver = useIntegratedSwiftDriver
172176
self.useExplicitModuleBuild = useExplicitModuleBuild
173177
self.isXcodeBuildSystemEnabled = isXcodeBuildSystemEnabled
178+
self.printManifestGraphviz = printManifestGraphviz
174179
}
175180

176181
/// The path to the build directory (inside the data directory).

0 commit comments

Comments
 (0)