@@ -41,6 +41,116 @@ extension Diagnostic.Message {
41
41
}
42
42
}
43
43
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
+
44
154
public extension Driver {
45
155
/// Precompute the dependencies for a given Swift compilation, producing a
46
156
/// dependency graph including all Swift and C module files and
0 commit comments