Skip to content

Commit b8c7ccf

Browse files
authored
Merge pull request #1386 from artemcm/ExplicitModule59CherrySeason
[5.9 🍒] Miscellaneous Dependency Scanner and Explicit Modules Improvements
2 parents 216ba83 + 122976b commit b8c7ccf

File tree

11 files changed

+214
-22
lines changed

11 files changed

+214
-22
lines changed

Sources/CSwiftScan/include/swiftscan_header.h

+2
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ typedef struct {
122122
(*swiftscan_swift_textual_detail_get_context_hash)(swiftscan_module_details_t);
123123
bool
124124
(*swiftscan_swift_textual_detail_get_is_framework)(swiftscan_module_details_t);
125+
swiftscan_string_set_t *
126+
(*swiftscan_swift_textual_detail_get_swift_overlay_dependencies)(swiftscan_module_details_t);
125127

126128
//=== Swift Binary Module Details query APIs ------------------------------===//
127129
swiftscan_string_ref_t

Sources/SwiftDriver/Driver/Driver.swift

+31
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,37 @@ extension Driver {
13321332
return
13331333
}
13341334

1335+
// If we're only supposed to explain a dependency on a given module, do so now.
1336+
if let explainModuleName = parsedOptions.getLastArgument(.explainModuleDependency) {
1337+
guard let dependencyPlanner = explicitDependencyBuildPlanner else {
1338+
fatalError("Cannot explain dependency without Explicit Build Planner")
1339+
}
1340+
guard let dependencyPaths = try dependencyPlanner.explainDependency(explainModuleName.asSingle) else {
1341+
diagnosticEngine.emit(.remark("No such module dependency found: '\(explainModuleName.asSingle)'"))
1342+
return
1343+
}
1344+
diagnosticEngine.emit(.remark("Module '\(moduleOutputInfo.name)' depends on '\(explainModuleName.asSingle)'"))
1345+
for path in dependencyPaths {
1346+
var pathString:String = ""
1347+
for (index, moduleId) in path.enumerated() {
1348+
switch moduleId {
1349+
case .swift(let moduleName):
1350+
pathString = pathString + "[" + moduleName + "]"
1351+
case .swiftPrebuiltExternal(let moduleName):
1352+
pathString = pathString + "[" + moduleName + "]"
1353+
case .clang(let moduleName):
1354+
pathString = pathString + "[" + moduleName + "](ObjC)"
1355+
case .swiftPlaceholder(_):
1356+
fatalError("Unexpected unresolved Placeholder module")
1357+
}
1358+
if index < path.count - 1 {
1359+
pathString = pathString + " -> "
1360+
}
1361+
}
1362+
diagnosticEngine.emit(.note(pathString))
1363+
}
1364+
}
1365+
13351366
if parsedOptions.contains(.driverPrintOutputFileMap) {
13361367
if let outputFileMap = self.outputFileMap {
13371368
stderrStream <<< outputFileMap.description

Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift

+6-1
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

+59-1
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,22 @@ extension InterModuleDependencyGraph {
7575
}
7676
// Traverse the set of modules in reverse topological order, assimilating transitive closures
7777
for moduleId in topologicalIdList.reversed() {
78-
for dependencyId in try moduleInfo(of: moduleId).directDependencies! {
78+
let moduleInfo = try moduleInfo(of: moduleId)
79+
for dependencyId in moduleInfo.directDependencies! {
7980
transitiveClosureMap[moduleId]!.formUnion(transitiveClosureMap[dependencyId]!)
8081
}
82+
// For Swift dependencies, their corresponding Swift Overlay dependencies
83+
// and bridging header dependencies are equivalent to direct dependencies.
84+
if case .swift(let swiftModuleDetails) = moduleInfo.details {
85+
let swiftOverlayDependencies = swiftModuleDetails.swiftOverlayDependencies ?? []
86+
for dependencyId in swiftOverlayDependencies {
87+
transitiveClosureMap[moduleId]!.formUnion(transitiveClosureMap[dependencyId]!)
88+
}
89+
let bridgingHeaderDependencies = swiftModuleDetails.bridgingHeaderDependencies ?? []
90+
for dependencyId in bridgingHeaderDependencies {
91+
transitiveClosureMap[moduleId]!.formUnion(transitiveClosureMap[dependencyId]!)
92+
}
93+
}
8194
}
8295
// For ease of use down-the-line, remove the node's self from its set of reachable nodes
8396
for (key, _) in transitiveClosureMap {
@@ -222,3 +235,48 @@ extension InterModuleDependencyGraph {
222235
details: .clang(combinedModuleDetails))
223236
}
224237
}
238+
239+
internal extension InterModuleDependencyGraph {
240+
func explainDependency(dependencyModuleName: String) throws -> [[ModuleDependencyId]]? {
241+
guard modules.contains(where: { $0.key.moduleName == dependencyModuleName }) else { return nil }
242+
var results = [[ModuleDependencyId]]()
243+
try findAllPaths(source: .swift(mainModuleName),
244+
to: dependencyModuleName,
245+
pathSoFar: [.swift(mainModuleName)],
246+
results: &results)
247+
return Array(results)
248+
}
249+
250+
251+
private func findAllPaths(source: ModuleDependencyId,
252+
to moduleName: String,
253+
pathSoFar: [ModuleDependencyId],
254+
results: inout [[ModuleDependencyId]]) throws {
255+
let sourceInfo = try moduleInfo(of: source)
256+
// If the source is our target, we are done
257+
guard source.moduleName != moduleName else {
258+
// If the source is a target Swift module, also check if it
259+
// depends on a corresponding Clang module with the same name.
260+
// If it does, add it to the path as well.
261+
var completePath = pathSoFar
262+
if let dependencies = sourceInfo.directDependencies,
263+
dependencies.contains(.clang(moduleName)) {
264+
completePath.append(.clang(moduleName))
265+
}
266+
results.append(completePath)
267+
return
268+
}
269+
270+
// If no more dependencies, this is a leaf node, we are done
271+
guard let dependencies = sourceInfo.directDependencies else {
272+
return
273+
}
274+
275+
for dependency in dependencies {
276+
try findAllPaths(source: dependency,
277+
to: moduleName,
278+
pathSoFar: pathSoFar + [dependency],
279+
results: &results)
280+
}
281+
}
282+
}

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift

+15-7
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ extension ModuleDependencyId: Codable {
7979
/// Bridging header
8080
public struct BridgingHeader: Codable {
8181
var path: TextualVirtualPath
82+
/// The source files referenced by the bridging header.
8283
var sourceFiles: [TextualVirtualPath]
84+
/// Modules that the bridging header specifically depends on
8385
var moduleDependencies: [String]
8486
}
8587

@@ -92,13 +94,16 @@ public struct SwiftModuleDetails: Codable {
9294
public var compiledModuleCandidates: [TextualVirtualPath]?
9395

9496
/// The bridging header, if any.
95-
public var bridgingHeaderPath: TextualVirtualPath?
96-
97-
/// The source files referenced by the bridging header.
98-
public var bridgingSourceFiles: [TextualVirtualPath]? = []
99-
100-
/// Modules that the bridging header specifically depends on
101-
public var bridgingHeaderDependencies: [ModuleDependencyId]? = []
97+
public var bridgingHeader: BridgingHeader?
98+
public var bridgingHeaderPath: TextualVirtualPath? {
99+
bridgingHeader?.path
100+
}
101+
public var bridgingSourceFiles: [TextualVirtualPath]? {
102+
bridgingHeader?.sourceFiles
103+
}
104+
public var bridgingHeaderDependencies: [ModuleDependencyId]? {
105+
bridgingHeader?.moduleDependencies.map { .clang($0) }
106+
}
102107

103108
/// Options to the compile command
104109
public var commandLine: [String]? = []
@@ -115,6 +120,9 @@ public struct SwiftModuleDetails: Codable {
115120

116121
/// A flag to indicate whether or not this module is a framework.
117122
public var isFramework: Bool?
123+
124+
/// A set of Swift Overlays of Clang Module Dependencies
125+
var swiftOverlayDependencies: [ModuleDependencyId]?
118126
}
119127

120128
/// Details specific to Swift placeholder dependencies.

Sources/SwiftDriver/Jobs/Planning.swift

+7-4
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/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift

+31-8
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ private extension SwiftScan {
123123
guard let moduleDetailsRef = api.swiftscan_module_info_get_details(moduleInfoRef) else {
124124
throw DependencyScanningError.missingField("modules[\(moduleId)].details")
125125
}
126-
let details = try constructModuleDetails(from: moduleDetailsRef)
126+
let details = try constructModuleDetails(from: moduleDetailsRef,
127+
moduleAliases: moduleAliases)
127128

128129
return (moduleId, ModuleInfo(modulePath: modulePath, sourceFiles: sourceFiles,
129130
directDependencies: directDependencies,
@@ -133,12 +134,14 @@ private extension SwiftScan {
133134
/// From a reference to a binary-format module info details object info returned by libSwiftScan,
134135
/// construct an instance of an `ModuleInfo`.Details as used by the driver.
135136
/// The object returned by libSwiftScan is a union so ensure to execute dependency-specific queries.
136-
func constructModuleDetails(from moduleDetailsRef: swiftscan_module_details_t)
137+
func constructModuleDetails(from moduleDetailsRef: swiftscan_module_details_t,
138+
moduleAliases: [String: String]?)
137139
throws -> ModuleInfo.Details {
138140
let moduleKind = api.swiftscan_module_detail_get_kind(moduleDetailsRef)
139141
switch moduleKind {
140142
case SWIFTSCAN_DEPENDENCY_INFO_SWIFT_TEXTUAL:
141-
return .swift(try constructSwiftTextualModuleDetails(from: moduleDetailsRef))
143+
return .swift(try constructSwiftTextualModuleDetails(from: moduleDetailsRef,
144+
moduleAliases: moduleAliases))
142145
case SWIFTSCAN_DEPENDENCY_INFO_SWIFT_BINARY:
143146
return .swiftPrebuiltExternal(try constructSwiftBinaryModuleDetails(from: moduleDetailsRef))
144147
case SWIFTSCAN_DEPENDENCY_INFO_SWIFT_PLACEHOLDER:
@@ -151,7 +154,8 @@ private extension SwiftScan {
151154
}
152155

153156
/// Construct a `SwiftModuleDetails` from a `swiftscan_module_details_t` reference
154-
func constructSwiftTextualModuleDetails(from moduleDetailsRef: swiftscan_module_details_t)
157+
func constructSwiftTextualModuleDetails(from moduleDetailsRef: swiftscan_module_details_t,
158+
moduleAliases: [String: String]?)
155159
throws -> SwiftModuleDetails {
156160
let moduleInterfacePath =
157161
try getOptionalPathDetail(from: moduleDetailsRef,
@@ -168,6 +172,15 @@ private extension SwiftScan {
168172
let bridgingHeaderDependencies =
169173
try getOptionalStringArrayDetail(from: moduleDetailsRef,
170174
using: api.swiftscan_swift_textual_detail_get_bridging_module_dependencies)
175+
let bridgingHeader: BridgingHeader?
176+
if let resolvedBridgingHeaderPath = bridgingHeaderPath {
177+
bridgingHeader = BridgingHeader(path: resolvedBridgingHeaderPath,
178+
sourceFiles: bridgingSourceFiles ?? [],
179+
moduleDependencies: bridgingHeaderDependencies ?? [])
180+
} else {
181+
bridgingHeader = nil
182+
}
183+
171184
let commandLine =
172185
try getOptionalStringArrayDetail(from: moduleDetailsRef,
173186
using: api.swiftscan_swift_textual_detail_get_command_line)
@@ -180,15 +193,25 @@ private extension SwiftScan {
180193
using: api.swiftscan_swift_textual_detail_get_context_hash)
181194
let isFramework = api.swiftscan_swift_textual_detail_get_is_framework(moduleDetailsRef)
182195

196+
// Decode all dependencies of this module
197+
let swiftOverlayDependencies: [ModuleDependencyId]?
198+
if supportsSeparateSwiftOverlayDependencies(),
199+
let encodedOverlayDepsRef = api.swiftscan_swift_textual_detail_get_swift_overlay_dependencies(moduleDetailsRef) {
200+
let encodedOverlayDependencies = try toSwiftStringArray(encodedOverlayDepsRef.pointee)
201+
swiftOverlayDependencies =
202+
try encodedOverlayDependencies.map { try decodeModuleNameAndKind(from: $0, moduleAliases: moduleAliases) }
203+
} else {
204+
swiftOverlayDependencies = nil
205+
}
206+
183207
return SwiftModuleDetails(moduleInterfacePath: moduleInterfacePath,
184208
compiledModuleCandidates: compiledModuleCandidates,
185-
bridgingHeaderPath: bridgingHeaderPath,
186-
bridgingSourceFiles: bridgingSourceFiles,
187-
bridgingHeaderDependencies: bridgingHeaderDependencies?.map { .clang($0) },
209+
bridgingHeader: bridgingHeader,
188210
commandLine: commandLine,
189211
contextHash: contextHash,
190212
extraPcmArgs: extraPcmArgs,
191-
isFramework: isFramework)
213+
isFramework: isFramework,
214+
swiftOverlayDependencies: swiftOverlayDependencies)
192215
}
193216

194217
/// Construct a `SwiftPrebuiltExternalModuleDetails` from a `swiftscan_module_details_t` reference

Sources/SwiftDriver/SwiftScan/SwiftScan.swift

+8
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,10 @@ internal extension swiftscan_diagnostic_severity_t {
263263
func resetScannerCache() {
264264
api.swiftscan_scanner_cache_reset(scanner)
265265
}
266+
267+
@_spi(Testing) public func supportsSeparateSwiftOverlayDependencies() -> Bool {
268+
return api.swiftscan_swift_textual_detail_get_swift_overlay_dependencies != nil
269+
}
266270

267271
@_spi(Testing) public func supportsScannerDiagnostics() -> Bool {
268272
return api.swiftscan_scanner_diagnostics_query != nil &&
@@ -419,6 +423,10 @@ private extension swiftscan_functions_t {
419423
self.swiftscan_swift_binary_detail_get_is_framework =
420424
try loadOptional("swiftscan_swift_binary_detail_get_is_framework")
421425

426+
// Swift Overlay Dependencies
427+
self.swiftscan_swift_textual_detail_get_swift_overlay_dependencies =
428+
try loadOptional("swiftscan_swift_textual_detail_get_swift_overlay_dependencies")
429+
422430
// MARK: Required Methods
423431
func loadRequired<T>(_ symbol: String) throws -> T {
424432
guard let sym: T = Loader.lookup(symbol: symbol, in: swiftscan) else {

0 commit comments

Comments
 (0)