@@ -41,6 +41,116 @@ extension Diagnostic.Message {
4141 }
4242}
4343
44+ @_spi ( Testing) public extension Driver {
45+ /// Scan the current module's input source-files to compute its direct and transitive
46+ /// module dependencies.
47+ mutating func gatherModuleDependencies( )
48+ throws -> InterModuleDependencyGraph {
49+ var dependencyGraph = try performDependencyScan ( )
50+
51+ if parsedOptions. hasArgument ( . printPreprocessedExplicitDependencyGraph) {
52+ try stdoutStream <<< dependencyGraph. toJSONString ( )
53+ stdoutStream. flush ( )
54+ }
55+
56+ if let externalTargetDetails = externalTargetModuleDetailsMap {
57+ // Resolve external dependencies in the dependency graph, if any.
58+ try dependencyGraph. resolveExternalDependencies ( for: externalTargetDetails)
59+ }
60+
61+ // Re-scan Clang modules at all the targets they will be built against.
62+ // This is currently disabled because we are investigating it being unnecessary
63+ // try resolveVersionedClangDependencies(dependencyGraph: &dependencyGraph)
64+
65+ // Set dependency modules' paths to be saved in the module cache.
66+ try resolveDependencyModulePaths ( dependencyGraph: & dependencyGraph)
67+
68+ if parsedOptions. hasArgument ( . printExplicitDependencyGraph) {
69+ let outputFormat = parsedOptions. getLastArgument ( . explicitDependencyGraphFormat) ? . asSingle
70+ if outputFormat == nil || outputFormat == " json " {
71+ try stdoutStream <<< dependencyGraph. toJSONString ( )
72+ } else if outputFormat == " dot " {
73+ DOTModuleDependencyGraphSerializer ( dependencyGraph) . writeDOT ( to: & stdoutStream)
74+ }
75+ stdoutStream. flush ( )
76+ }
77+
78+ return dependencyGraph
79+ }
80+
81+ /// Update the given inter-module dependency graph to set module paths to be within the module cache,
82+ /// if one is present, and for Swift modules to use the context hash in the file name.
83+ private mutating func resolveDependencyModulePaths( dependencyGraph: inout InterModuleDependencyGraph )
84+ throws {
85+ // If a module cache path is specified, update all module dependencies
86+ // to be output into it.
87+ if let moduleCachePath = parsedOptions. getLastArgument ( . moduleCachePath) ? . asSingle {
88+ try resolveDependencyModulePathsRelativeToModuleCache ( dependencyGraph: & dependencyGraph,
89+ moduleCachePath: moduleCachePath)
90+ }
91+
92+ // Set the output path to include the module's context hash
93+ try resolveDependencyModuleFileNamesWithContextHash ( dependencyGraph: & dependencyGraph)
94+ }
95+
96+ /// For Swift module dependencies, set the output path to include the module's context hash
97+ private mutating func resolveDependencyModuleFileNamesWithContextHash( dependencyGraph: inout InterModuleDependencyGraph )
98+ throws {
99+ for (moduleId, moduleInfo) in dependencyGraph. modules {
100+ // Output path on the main module is determined by the invocation arguments.
101+ guard moduleId. moduleName != dependencyGraph. mainModuleName else {
102+ continue
103+ }
104+
105+ let plainPath = VirtualPath . lookup ( dependencyGraph. modules [ moduleId] !. modulePath. path)
106+ if case . swift( let swiftDetails) = moduleInfo. details {
107+ guard let contextHash = swiftDetails. contextHash else {
108+ throw Driver . Error. missingContextHashOnSwiftDependency ( moduleId. moduleName)
109+ }
110+ let updatedPath = plainPath. parentDirectory. appending ( component: " \( plainPath. basenameWithoutExt) - \( contextHash) . \( plainPath. extension!) " )
111+ dependencyGraph. modules [ moduleId] !. modulePath = TextualVirtualPath ( path: updatedPath. intern ( ) )
112+ }
113+ // TODO: Remove this once toolchain is updated
114+ else if case . clang( let clangDetails) = moduleInfo. details {
115+ if !moduleInfo. modulePath. path. description. contains ( clangDetails. contextHash) {
116+ let contextHash = clangDetails. contextHash
117+ let updatedPath = plainPath. parentDirectory. appending ( component: " \( plainPath. basenameWithoutExt) - \( contextHash) . \( plainPath. extension!) " )
118+ dependencyGraph. modules [ moduleId] !. modulePath = TextualVirtualPath ( path: updatedPath. intern ( ) )
119+ }
120+ }
121+ }
122+ }
123+
124+ /// Resolve all paths to dependency binary module files to be relative to the module cache path.
125+ private mutating func resolveDependencyModulePathsRelativeToModuleCache( dependencyGraph: inout InterModuleDependencyGraph ,
126+ moduleCachePath: String )
127+ throws {
128+ for (moduleId, moduleInfo) in dependencyGraph. modules {
129+ // Output path on the main module is determined by the invocation arguments.
130+ if case . swift( let name) = moduleId {
131+ if name == dependencyGraph. mainModuleName {
132+ continue
133+ }
134+ let modulePath = VirtualPath . lookup ( moduleInfo. modulePath. path)
135+ // Use VirtualPath to get the OS-specific path separators right.
136+ let modulePathInCache =
137+ try VirtualPath ( path: moduleCachePath) . appending ( component: modulePath. basename)
138+ dependencyGraph. modules [ moduleId] !. modulePath =
139+ TextualVirtualPath ( path: modulePathInCache. intern ( ) )
140+ }
141+ // TODO: Remove this once toolchain is updated
142+ else if case . clang( _) = moduleId {
143+ let modulePath = VirtualPath . lookup ( moduleInfo. modulePath. path)
144+ // Use VirtualPath to get the OS-specific path separators right.
145+ let modulePathInCache =
146+ try VirtualPath ( path: moduleCachePath) . appending ( component: modulePath. basename)
147+ dependencyGraph. modules [ moduleId] !. modulePath =
148+ TextualVirtualPath ( path: modulePathInCache. intern ( ) )
149+ }
150+ }
151+ }
152+ }
153+
44154public extension Driver {
45155 /// Precompute the dependencies for a given Swift compilation, producing a
46156 /// dependency graph including all Swift and C module files and
0 commit comments