diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift index c03918353..154cb11fe 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import class Foundation.JSONEncoder + /// A map from a module identifier to its info public typealias ModuleInfoMap = [ModuleDependencyId: ModuleInfo] @@ -272,6 +274,21 @@ public struct InterModuleDependencyGraph: Codable { public var mainModule: ModuleInfo { modules[.swift(mainModuleName)]! } } +internal extension InterModuleDependencyGraph { + func toJSONString() throws -> String { + let encoder = JSONEncoder() +#if os(Linux) || os(Android) + encoder.outputFormatting = [.prettyPrinted] +#else + if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + encoder.outputFormatting = [.prettyPrinted, .withoutEscapingSlashes] + } +#endif + let data = try encoder.encode(self) + return String(data: data, encoding: .utf8)! + } +} + public struct InterModuleDependencyImports: Codable { public var imports: [String] diff --git a/Sources/SwiftDriver/Jobs/Planning.swift b/Sources/SwiftDriver/Jobs/Planning.swift index 9a9242f2a..73b2098cd 100644 --- a/Sources/SwiftDriver/Jobs/Planning.swift +++ b/Sources/SwiftDriver/Jobs/Planning.swift @@ -637,6 +637,11 @@ extension Driver { throws -> InterModuleDependencyGraph { var dependencyGraph = try performDependencyScan() + if parsedOptions.hasArgument(.printPreprocessedExplicitDependencyGraph) { + try stdoutStream <<< dependencyGraph.toJSONString() + stdoutStream.flush() + } + if let externalTargetDetails = externalTargetModuleDetailsMap { // Resolve external dependencies in the dependency graph, if any. try dependencyGraph.resolveExternalDependencies(for: externalTargetDetails) @@ -648,6 +653,11 @@ extension Driver { // Set dependency modules' paths to be saved in the module cache. try resolveDependencyModulePaths(dependencyGraph: &dependencyGraph) + if parsedOptions.hasArgument(.printExplicitDependencyGraph) { + try stdoutStream <<< dependencyGraph.toJSONString() + stdoutStream.flush() + } + return dependencyGraph } diff --git a/Sources/SwiftOptions/ExtraOptions.swift b/Sources/SwiftOptions/ExtraOptions.swift index 590c47bfc..6cfbc24fa 100644 --- a/Sources/SwiftOptions/ExtraOptions.swift +++ b/Sources/SwiftOptions/ExtraOptions.swift @@ -22,6 +22,8 @@ extension Option { public static let emitModuleSerializeDiagnosticsPath: Option = Option("-emit-module-serialize-diagnostics-path", .separate, attributes: [.argumentIsPath, .supplementaryOutput], metaVar: "", helpText: "Emit a serialized diagnostics file for the emit-module task to ") public static let emitModuleDependenciesPath: Option = Option("-emit-module-dependencies-path", .separate, attributes: [.argumentIsPath, .supplementaryOutput], metaVar: "", helpText: "Emit a discovered dependencies file for the emit-module task to ") public static let useFrontendParseableOutput: Option = Option("-use-frontend-parseable-output", .flag, attributes: [.helpHidden], helpText: "Emit parseable-output from swift-frontend jobs instead of from the driver") + 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") + public static let printPreprocessedExplicitDependencyGraph: Option = Option("-print-preprocessed-explicit-dependency-graph", .flag, attributes: [.helpHidden], helpText: "Print the result of module dependency scanning to output") // API digester operations public static let emitDigesterBaseline: Option = Option("-emit-digester-baseline", .flag, attributes: [.noInteractive, .supplementaryOutput], helpText: "Emit a baseline file for the module using the API digester") @@ -42,6 +44,8 @@ extension Option { Option.emitModuleSeparatelyWMO, Option.noEmitModuleSeparatelyWMO, Option.useFrontendParseableOutput, + Option.printExplicitDependencyGraph, + Option.printPreprocessedExplicitDependencyGraph, Option.emitDigesterBaseline, Option.emitDigesterBaselinePath, Option.compareToBaselinePath,