Skip to content

Commit 6c71f58

Browse files
authored
Merge pull request #1181 from artemcm/EmitInterModRealG
Add support for '-explicit-dependency-graph-format=' flag
2 parents 5cf131f + 64ec062 commit 6c71f58

File tree

5 files changed

+217
-72
lines changed

5 files changed

+217
-72
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,7 @@ public struct Driver {
634634
fileSystem: fileSystem,
635635
workingDirectory: workingDirectory,
636636
diagnosticEngine: diagnosticEngine)
637+
Self.validateEmitDependencyGraphArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
637638
Self.validateParseableOutputArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
638639
Self.validateCompilationConditionArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
639640
Self.validateFrameworkSearchPathArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
@@ -2646,6 +2647,28 @@ extension Driver {
26462647
}
26472648
}
26482649

2650+
static func validateEmitDependencyGraphArgs(_ parsedOptions: inout ParsedOptions,
2651+
diagnosticEngine: DiagnosticsEngine) {
2652+
// '-print-explicit-dependency-graph' requires '-explicit-module-build'
2653+
if parsedOptions.hasArgument(.printExplicitDependencyGraph) &&
2654+
!parsedOptions.hasArgument(.driverExplicitModuleBuild) {
2655+
diagnosticEngine.emit(Error.optionRequiresAnother(Option.printExplicitDependencyGraph.spelling,
2656+
Option.driverExplicitModuleBuild.spelling))
2657+
}
2658+
// '-explicit-dependency-graph-format=' requires '-print-explicit-dependency-graph'
2659+
if parsedOptions.hasArgument(.explicitDependencyGraphFormat) &&
2660+
!parsedOptions.hasArgument(.printExplicitDependencyGraph) {
2661+
diagnosticEngine.emit(Error.optionRequiresAnother(Option.explicitDependencyGraphFormat.spelling,
2662+
Option.printExplicitDependencyGraph.spelling))
2663+
}
2664+
// '-explicit-dependency-graph-format=' only supports values 'json' and 'dot'
2665+
if let formatArg = parsedOptions.getLastArgument(.explicitDependencyGraphFormat)?.asSingle {
2666+
if formatArg != "json" && formatArg != "dot" {
2667+
diagnosticEngine.emit(.error_unsupported_argument(argument: formatArg,
2668+
option: .explicitDependencyGraphFormat))
2669+
}
2670+
}
2671+
}
26492672

26502673
static func validateProfilingArgs(_ parsedOptions: inout ParsedOptions,
26512674
fileSystem: FileSystem,

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,12 @@ extension Driver {
660660
try resolveDependencyModulePaths(dependencyGraph: &dependencyGraph)
661661

662662
if parsedOptions.hasArgument(.printExplicitDependencyGraph) {
663-
try stdoutStream <<< dependencyGraph.toJSONString()
663+
let outputFormat = parsedOptions.getLastArgument(.explicitDependencyGraphFormat)?.asSingle
664+
if outputFormat == nil || outputFormat == "json" {
665+
try stdoutStream <<< dependencyGraph.toJSONString()
666+
} else if outputFormat == "dot" {
667+
DOTModuleDependencyGraphSerializer(dependencyGraph).writeDOT(to: &stdoutStream)
668+
}
664669
stdoutStream.flush()
665670
}
666671

Sources/SwiftOptions/Options.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ extension Option {
236236
public static let embedTbdForModule: Option = Option("-embed-tbd-for-module", .separate, attributes: [.frontend], helpText: "Embed symbols from the module in the emitted tbd file")
237237
public static let emitAbiDescriptorPath: Option = Option("-emit-abi-descriptor-path", .separate, attributes: [.frontend, .noDriver], metaVar: "<path>", helpText: "Output the ABI descriptor of current module to <path>")
238238
public static let emitAssembly: Option = Option("-emit-assembly", .flag, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild], helpText: "Emit assembly file(s) (-S)", group: .modes)
239+
public static let emitAst: Option = Option("-emit-ast", .flag, alias: Option.dumpAst, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild])
239240
public static let emitBc: Option = Option("-emit-bc", .flag, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild], helpText: "Emit LLVM BC file(s)", group: .modes)
240241
public static let emitClangHeaderPath: Option = Option("-emit-clang-header-path", .separate, alias: Option.emitObjcHeaderPath, attributes: [.frontend, .noDriver, .noInteractive, .argumentIsPath, .supplementaryOutput], helpText: "Emit an Objective-C and C++ header file to <path>")
241242
public static let emitConstValuesPath: Option = Option("-emit-const-values-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput], metaVar: "<path>", helpText: "Emit the extracted compile-time known values to <path>")
@@ -275,6 +276,7 @@ extension Option {
275276
public static let emitObject: Option = Option("-emit-object", .flag, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild], helpText: "Emit object file(s) (-c)", group: .modes)
276277
public static let emitParseableModuleInterfacePath: Option = Option("-emit-parseable-module-interface-path", .separate, alias: Option.emitModuleInterfacePath, attributes: [.helpHidden, .frontend, .noInteractive, .argumentIsPath, .supplementaryOutput])
277278
public static let emitParseableModuleInterface: Option = Option("-emit-parseable-module-interface", .flag, alias: Option.emitModuleInterface, attributes: [.helpHidden, .noInteractive, .supplementaryOutput])
279+
public static let emitParse: Option = Option("-emit-parse", .flag, alias: Option.dumpParse, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild])
278280
public static let emitPch: Option = Option("-emit-pch", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Emit PCH for imported Objective-C header file", group: .modes)
279281
public static let emitPcm: Option = Option("-emit-pcm", .flag, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild], helpText: "Emit a precompiled Clang module from a module map", group: .modes)
280282
public static let emitPrivateModuleInterfacePath: Option = Option("-emit-private-module-interface-path", .separate, attributes: [.helpHidden, .frontend, .noInteractive, .argumentIsPath, .supplementaryOutput], metaVar: "<path>", helpText: "Output private module interface file to <path>")
@@ -319,7 +321,7 @@ extension Option {
319321
public static let enableExperimentalAsyncTopLevel: Option = Option("-enable-experimental-async-top-level", .flag, attributes: [.helpHidden, .frontend, .noDriver, .moduleInterface], helpText: "Enable experimental concurrency in top-level code")
320322
public static let enableExperimentalConcisePoundFile: Option = Option("-enable-experimental-concise-pound-file", .flag, attributes: [.frontend, .moduleInterface], helpText: "Enable experimental concise '#file' identifier")
321323
public static let enableExperimentalConcurrency: Option = Option("-enable-experimental-concurrency", .flag, attributes: [.helpHidden, .frontend, .noDriver, .moduleInterface], helpText: "Enable experimental concurrency model")
322-
public static let enableExperimentalCxxInterop: Option = Option("-enable-experimental-cxx-interop", .flag, attributes: [.helpHidden, .frontend], helpText: "Enable experimental C++ interop code generation and config directives")
324+
public static let enableExperimentalCxxInterop: Option = Option("-enable-experimental-cxx-interop", .flag, attributes: [.helpHidden, .frontend, .moduleInterface], helpText: "Enable experimental C++ interop code generation and config directives")
323325
public static let enableExperimentalDistributed: Option = Option("-enable-experimental-distributed", .flag, attributes: [.helpHidden, .frontend, .noDriver, .moduleInterface], helpText: "Enable experimental 'distributed' actors and functions")
324326
public static let enableExperimentalEagerClangModuleDiagnostics: Option = Option("-enable-experimental-eager-clang-module-diagnostics", .flag, attributes: [.helpHidden, .frontend, .noDriver, .moduleInterface], helpText: "Enable experimental eager diagnostics reporting on the importability of all referenced C, C++, and Objective-C libraries")
325327
public static let enableExperimentalFeature: Option = Option("-enable-experimental-feature", .separate, attributes: [.frontend], helpText: "Enable an experimental feature")
@@ -386,6 +388,8 @@ extension Option {
386388
public static let experimentalSkipNonInlinableFunctionBodiesWithoutTypes: Option = Option("-experimental-skip-non-inlinable-function-bodies-without-types", .flag, attributes: [.helpHidden, .frontend], helpText: "Skip work on non-inlinable function bodies that do not declare nested types")
387389
public static let experimentalSkipNonInlinableFunctionBodies: Option = Option("-experimental-skip-non-inlinable-function-bodies", .flag, attributes: [.helpHidden, .frontend], helpText: "Skip type-checking and SIL generation for non-inlinable function bodies")
388390
public static let experimentalSpiImports: Option = Option("-experimental-spi-imports", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable experimental support for SPI imports")
391+
public static let experimentalSpiOnlyImports: Option = Option("-experimental-spi-only-imports", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable use of @_spiOnly imports")
392+
public static let explicitDependencyGraphFormat: Option = Option("-explicit-dependency-graph-format=", .joined, attributes: [.helpHidden, .doesNotAffectIncrementalBuild], helpText: "Specify the explicit dependency graph output format to either 'json' or 'dot'")
389393
public static let explicitInterfaceModuleBuild: Option = Option("-explicit-interface-module-build", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Use the specified command-line to build the module from interface, instead of flags specified in the interface")
390394
public static let driverExplicitModuleBuild: Option = Option("-explicit-module-build", .flag, attributes: [.helpHidden], helpText: "Prebuild module dependencies to make them explicit")
391395
public static let explicitSwiftModuleMap: Option = Option("-explicit-swift-module-map-file", .separate, attributes: [.frontend, .noDriver], metaVar: "<path>", helpText: "Specify a JSON file containing information of explicit Swift modules")
@@ -545,7 +549,7 @@ extension Option {
545549
public static let printAst: Option = Option("-print-ast", .flag, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild], helpText: "Parse and type-check input file(s) and pretty print AST(s)", group: .modes)
546550
public static let printClangStats: Option = Option("-print-clang-stats", .flag, attributes: [.frontend, .noDriver], helpText: "Print Clang importer statistics")
547551
public static let printEducationalNotes: Option = Option("-print-educational-notes", .flag, attributes: [.frontend, .doesNotAffectIncrementalBuild], helpText: "Include educational notes in printed diagnostic output, if available")
548-
public static let printExplicitDependencyGraph: Option = Option("-print-explicit-dependency-graph", .flag, attributes: [.helpHidden], helpText: "Print the result of module dependency scanning after external module resolution to output")
552+
public static let printExplicitDependencyGraph: Option = Option("-print-explicit-dependency-graph", .flag, attributes: [.helpHidden, .doesNotAffectIncrementalBuild], helpText: "Print the result of module dependency scanning after external module resolution to output")
549553
public static let printInstCounts: Option = Option("-print-inst-counts", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Before IRGen, count all the various SIL instructions. Must be used in conjunction with -print-stats.")
550554
public static let printLlvmInlineTree: Option = Option("-print-llvm-inline-tree", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Print the LLVM inline tree.")
551555
public static let printModule: Option = Option("-print-module", .flag, attributes: [.noDriver], helpText: "Print module names in diagnostics")
@@ -948,6 +952,7 @@ extension Option {
948952
Option.embedTbdForModule,
949953
Option.emitAbiDescriptorPath,
950954
Option.emitAssembly,
955+
Option.emitAst,
951956
Option.emitBc,
952957
Option.emitClangHeaderPath,
953958
Option.emitConstValuesPath,
@@ -987,6 +992,7 @@ extension Option {
987992
Option.emitObject,
988993
Option.emitParseableModuleInterfacePath,
989994
Option.emitParseableModuleInterface,
995+
Option.emitParse,
990996
Option.emitPch,
991997
Option.emitPcm,
992998
Option.emitPrivateModuleInterfacePath,
@@ -1098,6 +1104,8 @@ extension Option {
10981104
Option.experimentalSkipNonInlinableFunctionBodiesWithoutTypes,
10991105
Option.experimentalSkipNonInlinableFunctionBodies,
11001106
Option.experimentalSpiImports,
1107+
Option.experimentalSpiOnlyImports,
1108+
Option.explicitDependencyGraphFormat,
11011109
Option.explicitInterfaceModuleBuild,
11021110
Option.driverExplicitModuleBuild,
11031111
Option.explicitSwiftModuleMap,

Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,8 +1076,7 @@ final class ExplicitModuleBuildTests: XCTestCase {
10761076
return
10771077
}
10781078
guard try dependencyOracle.supportsScannerDiagnostics() else {
1079-
XCTSkip("libSwiftScan does not support diagnostics query.")
1080-
return
1079+
throw XCTSkip("libSwiftScan does not support diagnostics query.")
10811080
}
10821081

10831082
try withTemporaryDirectory { path in
@@ -1240,6 +1239,98 @@ final class ExplicitModuleBuildTests: XCTestCase {
12401239
}
12411240
}
12421241

1242+
func testPrintingExplicitDependencyGraph() throws {
1243+
try withTemporaryDirectory { path in
1244+
let main = path.appending(component: "testPrintingExplicitDependencyGraph.swift")
1245+
try localFileSystem.writeFileContents(main) {
1246+
$0 <<< "import C;"
1247+
$0 <<< "import E;"
1248+
$0 <<< "import G;"
1249+
}
1250+
let cHeadersPath: AbsolutePath = testInputsPath.appending(component: "ExplicitModuleBuilds").appending(component: "CHeaders")
1251+
let swiftModuleInterfacesPath: AbsolutePath = testInputsPath.appending(component: "ExplicitModuleBuilds").appending(component: "Swift")
1252+
let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? []
1253+
1254+
let baseCommandLine = ["swiftc",
1255+
"-target", "x86_64-apple-macosx11.0",
1256+
"-I", cHeadersPath.nativePathString(escaped: true),
1257+
"-I", swiftModuleInterfacesPath.nativePathString(escaped: true),
1258+
main.nativePathString(escaped: true)] + sdkArgumentsForTesting
1259+
do {
1260+
let diagnosticEngine = DiagnosticsEngine()
1261+
var driver = try Driver(args: baseCommandLine + ["-print-explicit-dependency-graph"],
1262+
diagnosticsEngine: diagnosticEngine)
1263+
let _ = try driver.planBuild()
1264+
XCTAssertTrue(diagnosticEngine.hasErrors)
1265+
XCTAssertEqual(diagnosticEngine.diagnostics.first?.message.data.description,
1266+
"'-print-explicit-dependency-graph' cannot be specified if '-explicit-module-build' is not present")
1267+
}
1268+
do {
1269+
let diagnosticEngine = DiagnosticsEngine()
1270+
var driver = try Driver(args: baseCommandLine + ["-explicit-module-build",
1271+
"-explicit-dependency-graph-format=json"],
1272+
diagnosticsEngine: diagnosticEngine)
1273+
let _ = try driver.planBuild()
1274+
XCTAssertTrue(diagnosticEngine.hasErrors)
1275+
XCTAssertEqual(diagnosticEngine.diagnostics.first?.message.data.description,
1276+
"'-explicit-dependency-graph-format=' cannot be specified if '-print-explicit-dependency-graph' is not present")
1277+
}
1278+
do {
1279+
let diagnosticEngine = DiagnosticsEngine()
1280+
var driver = try Driver(args: baseCommandLine + ["-explicit-module-build",
1281+
"-print-explicit-dependency-graph",
1282+
"-explicit-dependency-graph-format=watercolor"],
1283+
diagnosticsEngine: diagnosticEngine)
1284+
let _ = try driver.planBuild()
1285+
XCTAssertTrue(diagnosticEngine.hasErrors)
1286+
XCTAssertEqual(diagnosticEngine.diagnostics.first?.message.data.description,
1287+
"unsupported argument \'watercolor\' to option \'-explicit-dependency-graph-format=\'")
1288+
}
1289+
1290+
let _ = try withHijackedOutputStream {
1291+
let diagnosticEngine = DiagnosticsEngine()
1292+
var driver = try Driver(args: baseCommandLine + ["-explicit-module-build",
1293+
"-print-explicit-dependency-graph",
1294+
"-explicit-dependency-graph-format=json"],
1295+
diagnosticsEngine: diagnosticEngine)
1296+
let _ = try driver.planBuild()
1297+
XCTAssertFalse(diagnosticEngine.hasErrors)
1298+
}
1299+
1300+
let output = try withHijackedOutputStream {
1301+
let diagnosticEngine = DiagnosticsEngine()
1302+
var driver = try Driver(args: baseCommandLine + ["-explicit-module-build",
1303+
"-print-explicit-dependency-graph",
1304+
"-explicit-dependency-graph-format=json"],
1305+
diagnosticsEngine: diagnosticEngine)
1306+
let _ = try driver.planBuild()
1307+
XCTAssertFalse(diagnosticEngine.hasErrors)
1308+
}
1309+
XCTAssertTrue(output.contains("\"mainModuleName\" : \"testPrintingExplicitDependencyGraph\","))
1310+
1311+
let output2 = try withHijackedOutputStream {
1312+
let diagnosticEngine = DiagnosticsEngine()
1313+
var driver = try Driver(args: baseCommandLine + ["-explicit-module-build",
1314+
"-print-explicit-dependency-graph",
1315+
"-explicit-dependency-graph-format=dot"],
1316+
diagnosticsEngine: diagnosticEngine)
1317+
let _ = try driver.planBuild()
1318+
XCTAssertFalse(diagnosticEngine.hasErrors)
1319+
}
1320+
XCTAssertTrue(output2.contains("\"testPrintingExplicitDependencyGraph\" [shape=box, style=bold, color=navy"))
1321+
1322+
let output3 = try withHijackedOutputStream {
1323+
let diagnosticEngine = DiagnosticsEngine()
1324+
var driver = try Driver(args: baseCommandLine + ["-explicit-module-build",
1325+
"-print-explicit-dependency-graph"],
1326+
diagnosticsEngine: diagnosticEngine)
1327+
let _ = try driver.planBuild()
1328+
XCTAssertFalse(diagnosticEngine.hasErrors)
1329+
}
1330+
XCTAssertTrue(output3.contains("\"mainModuleName\" : \"testPrintingExplicitDependencyGraph\","))
1331+
}
1332+
}
1333+
12431334
func testDependencyGraphDotSerialization() throws {
12441335
let (stdlibPath, shimsPath, toolchain, hostTriple) = try getDriverArtifactsForScanning()
12451336
let dependencyOracle = InterModuleDependencyOracle()

0 commit comments

Comments
 (0)