Skip to content

Commit 510e270

Browse files
committed
Add option to explain a given module dependency
Adds support for '-explain-dependency <MODULE_NAME>', which causes the driver to run a dependency scan, and find all dependency paths that cause the main module to depend, directly or transitively, on a given module dependency.
1 parent 1c3cde8 commit 510e270

File tree

7 files changed

+149
-7
lines changed

7 files changed

+149
-7
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,37 @@ extension Driver {
13701370
return
13711371
}
13721372

1373+
// If we're only supposed to explain a dependency on a given module, do so now.
1374+
if let explainModuleName = parsedOptions.getLastArgument(.explainModuleDependency) {
1375+
guard let dependencyPlanner = explicitDependencyBuildPlanner else {
1376+
fatalError("Cannot explain dependency without Explicit Build Planner")
1377+
}
1378+
guard let dependencyPaths = try dependencyPlanner.explainDependency(explainModuleName.asSingle) else {
1379+
diagnosticEngine.emit(.remark("No such module dependency found: '\(explainModuleName.asSingle)'"))
1380+
return
1381+
}
1382+
diagnosticEngine.emit(.remark("Module '\(moduleOutputInfo.name)' depends on '\(explainModuleName.asSingle)'"))
1383+
for path in dependencyPaths {
1384+
var pathString:String = ""
1385+
for (index, moduleId) in path.enumerated() {
1386+
switch moduleId {
1387+
case .swift(let moduleName):
1388+
pathString = pathString + "[" + moduleName + "]"
1389+
case .swiftPrebuiltExternal(let moduleName):
1390+
pathString = pathString + "[" + moduleName + "]"
1391+
case .clang(let moduleName):
1392+
pathString = pathString + "[" + moduleName + "](ObjC)"
1393+
case .swiftPlaceholder(_):
1394+
fatalError("Unexpected unresolved Placeholder module")
1395+
}
1396+
if index < path.count - 1 {
1397+
pathString = pathString + " -> "
1398+
}
1399+
}
1400+
diagnosticEngine.emit(.note(pathString))
1401+
}
1402+
}
1403+
13731404
if parsedOptions.contains(.driverPrintOutputFileMap) {
13741405
if let outputFileMap = self.outputFileMap {
13751406
stderrStream <<< outputFileMap.description

Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
124124
}
125125
for moduleId in swiftDependencies {
126126
let moduleInfo = try dependencyGraph.moduleInfo(of: moduleId)
127-
let pcmArgs = try dependencyGraph.swiftModulePCMArgs(of: moduleId)
128127
var inputs: [TypedVirtualPath] = []
129128
let outputs: [TypedVirtualPath] = [
130129
TypedVirtualPath(file: moduleInfo.modulePath.path, type: .swiftModule)
@@ -488,6 +487,12 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
488487
}
489488
}
490489

490+
internal extension ExplicitDependencyBuildPlanner {
491+
func explainDependency(_ dependencyModuleName: String) throws -> [[ModuleDependencyId]]? {
492+
return try dependencyGraph.explainDependency(dependencyModuleName: dependencyModuleName)
493+
}
494+
}
495+
491496
// InterModuleDependencyGraph printing, useful for debugging
492497
internal extension InterModuleDependencyGraph {
493498
func prettyPrintString() throws -> String {

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,48 @@ extension InterModuleDependencyGraph {
222222
details: .clang(combinedModuleDetails))
223223
}
224224
}
225+
226+
internal extension InterModuleDependencyGraph {
227+
func explainDependency(dependencyModuleName: String) throws -> [[ModuleDependencyId]]? {
228+
guard modules.contains(where: { $0.key.moduleName == dependencyModuleName }) else { return nil }
229+
var results = [[ModuleDependencyId]]()
230+
try findAllPaths(source: .swift(mainModuleName),
231+
to: dependencyModuleName,
232+
pathSoFar: [.swift(mainModuleName)],
233+
results: &results)
234+
return Array(results)
235+
}
236+
237+
238+
private func findAllPaths(source: ModuleDependencyId,
239+
to moduleName: String,
240+
pathSoFar: [ModuleDependencyId],
241+
results: inout [[ModuleDependencyId]]) throws {
242+
let sourceInfo = try moduleInfo(of: source)
243+
// If the source is our target, we are done
244+
guard source.moduleName != moduleName else {
245+
// If the source is a target Swift module, also check if it
246+
// depends on a corresponding Clang module with the same name.
247+
// If it does, add it to the path as well.
248+
var completePath = pathSoFar
249+
if let dependencies = sourceInfo.directDependencies,
250+
dependencies.contains(.clang(moduleName)) {
251+
completePath.append(.clang(moduleName))
252+
}
253+
results.append(completePath)
254+
return
255+
}
256+
257+
// If no more dependencies, this is a leaf node, we are done
258+
guard let dependencies = sourceInfo.directDependencies else {
259+
return
260+
}
261+
262+
for dependency in dependencies {
263+
try findAllPaths(source: dependency,
264+
to: moduleName,
265+
pathSoFar: pathSoFar + [dependency],
266+
results: &results)
267+
}
268+
}
269+
}

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ extension Driver {
140140
IncrementalCompilationState.InitialStateForPlanning?)
141141
throws -> InterModuleDependencyGraph? {
142142
let interModuleDependencyGraph: InterModuleDependencyGraph?
143-
if parsedOptions.contains(.driverExplicitModuleBuild) {
143+
if parsedOptions.contains(.driverExplicitModuleBuild) ||
144+
parsedOptions.contains(.explainModuleDependency) {
144145
interModuleDependencyGraph =
145146
try initialIncrementalState?.maybeUpToDatePriorInterModuleDependencyGraph ??
146147
gatherModuleDependencies()
@@ -195,13 +196,15 @@ extension Driver {
195196
dependencyGraph: InterModuleDependencyGraph?,
196197
addJob: (Job) -> Void)
197198
throws {
198-
// If asked, add jobs to precompile module dependencies
199-
guard parsedOptions.contains(.driverExplicitModuleBuild) else { return }
200199
guard let resolvedDependencyGraph = dependencyGraph else {
201-
fatalError("Attempting to plan explicit dependency build without a dependency graph")
200+
return
202201
}
203202
let modulePrebuildJobs =
204203
try generateExplicitModuleDependenciesJobs(dependencyGraph: resolvedDependencyGraph)
204+
// If asked, add jobs to precompile module dependencies. Otherwise exit.
205+
// We may have a dependency graph but not be required to add pre-compile jobs to the build plan,
206+
// for example when `-explain-dependency` is being used.
207+
guard parsedOptions.contains(.driverExplicitModuleBuild) else { return }
205208
modulePrebuildJobs.forEach(addJob)
206209
}
207210

Sources/SwiftOptions/Options.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ extension Option {
2727
public static let accessNotesPathEQ: Option = Option("-access-notes-path=", .joined, alias: Option.accessNotesPath, attributes: [.frontend, .argumentIsPath])
2828
public static let accessNotesPath: Option = Option("-access-notes-path", .separate, attributes: [.frontend, .argumentIsPath], helpText: "Specify YAML file to override attributes on Swift declarations in this module")
2929
public static let aliasModuleNamesInModuleInterface: Option = Option("-alias-module-names-in-module-interface", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "When emitting a module interface, disambiguate modules using distinct alias names")
30+
public static let allowUnstableCacheKeyForTesting: Option = Option("-allow-unstable-cache-key-for-testing", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Allow compilation caching with unstable inputs for testing purpose")
3031
public static let allowableClient: Option = Option("-allowable-client", .separate, attributes: [.frontend], metaVar: "<vers>", helpText: "Module names that are allowed to import this module")
3132
public static let alwaysCompileOutputFiles: Option = Option("-always-compile-output-files", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Always compile output files even it might not change the results")
3233
public static let analyzeRequirementMachine: Option = Option("-analyze-requirement-machine", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Print out requirement machine statistics at the end of the compilation job")
@@ -59,6 +60,8 @@ extension Option {
5960
public static let buildModuleFromParseableInterface: Option = Option("-build-module-from-parseable-interface", .flag, alias: Option.compileModuleFromInterface, attributes: [.helpHidden, .frontend, .noDriver], group: .modes)
6061
public static let bypassBatchModeChecks: Option = Option("-bypass-batch-mode-checks", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Bypass checks for batch-mode errors.")
6162
public static let candidateModuleFile: Option = Option("-candidate-module-file", .separate, attributes: [.helpHidden, .frontend, .noDriver], metaVar: "<path>", helpText: "Specify Swift module may be ready to use for an interface")
63+
public static let casFs: Option = Option("-cas-fs", .separate, attributes: [.frontend, .noDriver], metaVar: "<cas-id>", helpText: "Root CASID for CAS FileSystem")
64+
public static let casPath: Option = Option("-cas-path", .separate, attributes: [.frontend], metaVar: "<path>", helpText: "Path to CAS")
6265
public static let checkApiAvailabilityOnly: Option = Option("-check-api-availability-only", .flag, attributes: [.helpHidden, .frontend, .noInteractive], helpText: "Deprecated, has no effect")
6366
public static let checkOnoneCompleteness: Option = Option("-check-onone-completeness", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Print errors if the compile OnoneSupport module is missing symbols")
6467
public static let clangBuildSessionFile: Option = Option("-clang-build-session-file", .separate, attributes: [.frontend, .argumentIsPath], helpText: "Use the last modification time of <file> as the underlying Clang build session timestamp")
@@ -81,7 +84,7 @@ extension Option {
8184
public static let CrossModuleOptimization: Option = Option("-cross-module-optimization", .flag, attributes: [.helpHidden, .frontend], helpText: "Perform cross-module optimization")
8285
public static let crosscheckUnqualifiedLookup: Option = Option("-crosscheck-unqualified-lookup", .flag, attributes: [.frontend, .noDriver], helpText: "Compare legacy DeclContext- to ASTScope-based unqualified name lookup (for debugging)")
8386
public static let cxxInteropGettersSettersAsProperties: Option = Option("-cxx-interop-getters-setters-as-properties", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Import getters and setters as computed properties in Swift")
84-
public static let cxxInteroperabilityMode: Option = Option("-cxx-interoperability-mode=", .joined, attributes: [.frontend, .moduleInterface], helpText: "Enables C++ interoperability; requires compatbility version to be specified.")
87+
public static let cxxInteroperabilityMode: Option = Option("-cxx-interoperability-mode=", .joined, attributes: [.frontend, .moduleInterface], helpText: "Enables C++ interoperability; pass 'default' to enable or 'off' to disable")
8588
public static let c: Option = Option("-c", .flag, alias: Option.emitObject, attributes: [.frontend, .noInteractive], group: .modes)
8689
public static let debugAssertAfterParse: Option = Option("-debug-assert-after-parse", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Force an assertion failure after parsing", group: .debugCrash)
8790
public static let debugAssertImmediately: Option = Option("-debug-assert-immediately", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Force an assertion failure immediately", group: .debugCrash)
@@ -336,6 +339,7 @@ extension Option {
336339
public static let enableBatchMode: Option = Option("-enable-batch-mode", .flag, attributes: [.helpHidden, .frontend, .noInteractive], helpText: "Enable combining frontend jobs into batches")
337340
public static let enableBridgingPch: Option = Option("-enable-bridging-pch", .flag, attributes: [.helpHidden], helpText: "Enable automatic generation of bridging PCH files")
338341
public static let enableBuiltinModule: Option = Option("-enable-builtin-module", .flag, attributes: [.frontend, .moduleInterface], helpText: "Enables the explicit import of the Builtin module")
342+
public static let enableCas: Option = Option("-enable-cas", .flag, attributes: [.frontend, .noDriver], helpText: "Enable CAS for swift-frontend")
339343
public static let enableCollocateMetadataFunctions: Option = Option("-enable-collocate-metadata-functions", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable collocate metadata functions")
340344
public static let enableColocateTypeDescriptors: Option = Option("-enable-colocate-type-descriptors", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable colocate type descriptors")
341345
public static let enableConformanceAvailabilityErrors: Option = Option("-enable-conformance-availability-errors", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Diagnose conformance availability violations as errors")
@@ -424,13 +428,14 @@ extension Option {
424428
public static let driverExperimentalExplicitModuleBuild: Option = Option("-experimental-explicit-module-build", .flag, alias: Option.driverExplicitModuleBuild, attributes: [.helpHidden], helpText: "Prebuild module dependencies to make them explicit")
425429
public static let experimentalHermeticSealAtLink: Option = Option("-experimental-hermetic-seal-at-link", .flag, attributes: [.helpHidden, .frontend], helpText: "Library code can assume that all clients are visible at linktime, and aggressively strip unused code")
426430
public static let experimentalOneWayClosureParams: Option = Option("-experimental-one-way-closure-params", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable experimental support for one-way closure parameters")
427-
public static let ExperimentalPerformanceAnnotations: Option = Option("-experimental-performance-annotations", .flag, attributes: [.helpHidden, .frontend], helpText: "Enable experimental performance annotations")
431+
public static let ExperimentalPerformanceAnnotations: Option = Option("-experimental-performance-annotations", .flag, attributes: [.helpHidden, .frontend], helpText: "Deprecated, has no effect")
428432
public static let experimentalPrintFullConvention: Option = Option("-experimental-print-full-convention", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "When emitting a module interface or SIL, emit additional @convention arguments, regardless of whether they were written in the source. Also requires -use-clang-function-types to be enabled.")
429433
public static let experimentalSkipAllFunctionBodies: Option = Option("-experimental-skip-all-function-bodies", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Skip type-checking function bodies and all SIL generation")
430434
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")
431435
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")
432436
public static let experimentalSpiImports: Option = Option("-experimental-spi-imports", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable experimental support for SPI imports")
433437
public static let experimentalSpiOnlyImports: Option = Option("-experimental-spi-only-imports", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable use of @_spiOnly imports")
438+
public static let explainModuleDependency: Option = Option("-explain-module-dependency", .separate, attributes: [], helpText: "Emit remark/notes describing why compilaiton may depend on a module with a given name.")
434439
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'")
435440
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")
436441
public static let driverExplicitModuleBuild: Option = Option("-explicit-module-build", .flag, attributes: [.helpHidden], helpText: "Prebuild module dependencies to make them explicit")
@@ -800,6 +805,7 @@ extension Option {
800805
Option.accessNotesPathEQ,
801806
Option.accessNotesPath,
802807
Option.aliasModuleNamesInModuleInterface,
808+
Option.allowUnstableCacheKeyForTesting,
803809
Option.allowableClient,
804810
Option.alwaysCompileOutputFiles,
805811
Option.analyzeRequirementMachine,
@@ -832,6 +838,8 @@ extension Option {
832838
Option.buildModuleFromParseableInterface,
833839
Option.bypassBatchModeChecks,
834840
Option.candidateModuleFile,
841+
Option.casFs,
842+
Option.casPath,
835843
Option.checkApiAvailabilityOnly,
836844
Option.checkOnoneCompleteness,
837845
Option.clangBuildSessionFile,
@@ -1109,6 +1117,7 @@ extension Option {
11091117
Option.enableBatchMode,
11101118
Option.enableBridgingPch,
11111119
Option.enableBuiltinModule,
1120+
Option.enableCas,
11121121
Option.enableCollocateMetadataFunctions,
11131122
Option.enableColocateTypeDescriptors,
11141123
Option.enableConformanceAvailabilityErrors,
@@ -1204,6 +1213,7 @@ extension Option {
12041213
Option.experimentalSkipNonInlinableFunctionBodies,
12051214
Option.experimentalSpiImports,
12061215
Option.experimentalSpiOnlyImports,
1216+
Option.explainModuleDependency,
12071217
Option.explicitDependencyGraphFormat,
12081218
Option.explicitInterfaceModuleBuild,
12091219
Option.driverExplicitModuleBuild,

Sources/makeOptions/makeOptions.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ enum SwiftFlags {
6464
SwiftAPIDigesterOption = (1 << 17),
6565
NewDriverOnlyOption = (1 << 18),
6666
ModuleInterfaceOptionIgnorable = (1 << 19),
67+
ModuleInterfaceOptionIgnorablePrivate = (1 << 20)
6768
};
6869

6970
static std::set<std::string> swiftKeywords = { "internal", "static" };

0 commit comments

Comments
 (0)