Skip to content

Commit 2e2e088

Browse files
committed
[Explicit Module Builds] Use context hash in Swift dependency module file names
This change ensures that explicit swift module dependencies are built into binary modules with filenames that encode their context hash (as reported by the dependency scanner). e.g. `A.swiftinterface` --> `A-<CONTEXT_HASH>.swiftmodule`. This is required because in some contexts builds of the same module for different contexts (e.g. target architectures) otherwise overlap and cause chaos. Resolves rdar://78820404
1 parent 29ee108 commit 2e2e088

File tree

6 files changed

+207
-121
lines changed

6 files changed

+207
-121
lines changed

Package.resolved

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public struct Driver {
3737
case malformedModuleDependency(String, String)
3838
case missingPCMArguments(String)
3939
case missingModuleDependency(String)
40+
case missingContextHashOnSwiftDependency(String)
4041
case dependencyScanningFailure(Int, String)
4142
case missingExternalDependency(String)
4243

@@ -88,6 +89,8 @@ public struct Driver {
8889
return "Missing extraPcmArgs to build Clang module: \(moduleName)"
8990
case .missingModuleDependency(let moduleName):
9091
return "Missing Module Dependency Info: \(moduleName)"
92+
case .missingContextHashOnSwiftDependency(let moduleName):
93+
return "Missing Context Hash for Swift dependency: \(moduleName)"
9194
case .dependencyScanningFailure(let code, let error):
9295
return "Module Dependency Scanner returned with non-zero exit status: \(code), \(error)"
9396
case .unableToLoadOutputFileMap(let path):

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ public struct SwiftModuleDetails: Codable {
9797
/// Options to the compile command
9898
public var commandLine: [String]? = []
9999

100+
/// The context hash for this module that encodes the producing interface's path,
101+
/// target triple, etc. This field is optional because it is absent for the ModuleInfo
102+
/// corresponding to the main module being built.
103+
public var contextHash: String?
104+
100105
/// To build a PCM to be used by this Swift module, we need to append these
101106
/// arguments to the generic PCM build arguments reported from the dependency
102107
/// graph.

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ extension Driver {
527527
try resolveVersionedClangDependencies(dependencyGraph: &dependencyGraph)
528528

529529
// Set dependency modules' paths to be saved in the module cache.
530-
try updateDependencyModulesWithModuleCachePath(dependencyGraph: &dependencyGraph)
530+
try resolveDependencyModulePaths(dependencyGraph: &dependencyGraph)
531531

532532
// Update the dependency oracle, adding this new dependency graph to its store
533533
try interModuleDependencyOracle.mergeModules(from: dependencyGraph)
@@ -536,29 +536,61 @@ extension Driver {
536536
}
537537

538538
/// Update the given inter-module dependency graph to set module paths to be within the module cache,
539-
/// if one is present.
540-
private mutating func updateDependencyModulesWithModuleCachePath(dependencyGraph:
541-
inout InterModuleDependencyGraph)
539+
/// if one is present, and for Swift modules to use the context hash in the file name.
540+
private mutating func resolveDependencyModulePaths(dependencyGraph: inout InterModuleDependencyGraph)
542541
throws {
543-
let moduleCachePath = parsedOptions.getLastArgument(.moduleCachePath)?.asSingle
544-
if moduleCachePath != nil {
545-
for (moduleId, moduleInfo) in dependencyGraph.modules {
546-
// Output path on the main module is determined by the invocation arguments.
547-
guard moduleId.moduleName != dependencyGraph.mainModuleName else {
548-
continue
549-
}
550-
let modulePath = VirtualPath.lookup(moduleInfo.modulePath.path)
551-
// Only update paths on modules which do not already specify a path beyond their module name
552-
// and a file extension.
553-
if modulePath.description == moduleId.moduleName + ".swiftmodule" ||
554-
modulePath.description == moduleId.moduleName + ".pcm" {
555-
// Use VirtualPath to get the OS-specific path separators right.
556-
let modulePathInCache =
557-
try VirtualPath(path: moduleCachePath!)
558-
.appending(component: modulePath.description)
559-
dependencyGraph.modules[moduleId]!.modulePath =
560-
TextualVirtualPath(path: modulePathInCache.intern())
561-
}
542+
// For Swift module dependencies, set the output path to include
543+
// the module's context hash
544+
try resolveSwiftDependencyModuleFileNames(dependencyGraph: &dependencyGraph)
545+
546+
// If a module cache path is specified, update all module dependencies
547+
// to be output into it.
548+
if let moduleCachePath = parsedOptions.getLastArgument(.moduleCachePath)?.asSingle {
549+
try resolveDependencyModulePathsRelativeToModuleCache(dependencyGraph: &dependencyGraph,
550+
moduleCachePath: moduleCachePath)
551+
}
552+
}
553+
554+
/// For Swift module dependencies, set the output path to include the module's context hash
555+
private mutating func resolveSwiftDependencyModuleFileNames(dependencyGraph: inout InterModuleDependencyGraph)
556+
throws {
557+
for (moduleId, moduleInfo) in dependencyGraph.modules {
558+
// Output path on the main module is determined by the invocation arguments.
559+
guard moduleId.moduleName != dependencyGraph.mainModuleName else {
560+
continue
561+
}
562+
guard case .swift(let swiftDetails) = moduleInfo.details else {
563+
continue
564+
}
565+
guard let contextHash = swiftDetails.contextHash else {
566+
throw Driver.Error.missingContextHashOnSwiftDependency(moduleId.moduleName)
567+
}
568+
let plainPath = VirtualPath.lookup(dependencyGraph.modules[moduleId]!.modulePath.path)
569+
let updatedPath = plainPath.parentDirectory.appending(component: "\(plainPath.basenameWithoutExt)-\(contextHash).\(plainPath.extension!)")
570+
dependencyGraph.modules[moduleId]!.modulePath = TextualVirtualPath(path: updatedPath.intern())
571+
}
572+
}
573+
574+
/// Resolve all paths to dependency binary module files to be relative to the module cache path.
575+
private mutating func resolveDependencyModulePathsRelativeToModuleCache(dependencyGraph: inout InterModuleDependencyGraph,
576+
moduleCachePath: String)
577+
throws {
578+
for (moduleId, moduleInfo) in dependencyGraph.modules {
579+
// Output path on the main module is determined by the invocation arguments.
580+
guard moduleId.moduleName != dependencyGraph.mainModuleName else {
581+
continue
582+
}
583+
let modulePath = VirtualPath.lookup(moduleInfo.modulePath.path)
584+
// Only update paths on modules which do not already specify a path beyond their module name
585+
// and a file extension.
586+
if modulePath.description == moduleId.moduleName + ".swiftmodule" ||
587+
modulePath.description == moduleId.moduleName + ".pcm" {
588+
// Use VirtualPath to get the OS-specific path separators right.
589+
let modulePathInCache =
590+
try VirtualPath(path: moduleCachePath)
591+
.appending(component: modulePath.description)
592+
dependencyGraph.modules[moduleId]!.modulePath =
593+
TextualVirtualPath(path: modulePathInCache.intern())
562594
}
563595
}
564596
}

Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,17 @@ private extension SwiftScan {
158158
try getStringArrayDetail(from: moduleDetailsRef,
159159
using: api.swiftscan_swift_textual_detail_get_extra_pcm_args,
160160
fieldName: "extraPCMArgs")
161+
let contextHash =
162+
try getOptionalStringDetail(from: moduleDetailsRef,
163+
using: api.swiftscan_swift_textual_detail_get_context_hash)
161164
let isFramework = api.swiftscan_swift_textual_detail_get_is_framework(moduleDetailsRef)
165+
162166
return SwiftModuleDetails(moduleInterfacePath: moduleInterfacePath,
163167
compiledModuleCandidates: compiledModuleCandidates,
164168
bridgingHeaderPath: bridgingHeaderPath,
165169
bridgingSourceFiles: bridgingSourceFiles,
166170
commandLine: commandLine,
171+
contextHash: contextHash,
167172
extraPcmArgs: extraPcmArgs,
168173
isFramework: isFramework)
169174
}

0 commit comments

Comments
 (0)