From 2e2e0880bcd86a767e933c540baffe990c6f386b Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Fri, 4 Jun 2021 16:33:52 -0700 Subject: [PATCH] [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-.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 --- Package.resolved | 2 +- Sources/SwiftDriver/Driver/Driver.swift | 3 + .../InterModuleDependencyGraph.swift | 5 + Sources/SwiftDriver/Jobs/Planning.swift | 78 ++++-- .../SwiftScan/DependencyGraphBuilder.swift | 5 + .../ExplicitModuleBuildTests.swift | 235 ++++++++++-------- 6 files changed, 207 insertions(+), 121 deletions(-) diff --git a/Package.resolved b/Package.resolved index 7de9acd16..de7ee6f55 100644 --- a/Package.resolved +++ b/Package.resolved @@ -11,7 +11,7 @@ } }, { - "package": "llbuild", + "package": "swift-llbuild", "repositoryURL": "https://github.com/apple/swift-llbuild.git", "state": { "branch": "main", diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index 70b5c60dd..ff28c4375 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -37,6 +37,7 @@ public struct Driver { case malformedModuleDependency(String, String) case missingPCMArguments(String) case missingModuleDependency(String) + case missingContextHashOnSwiftDependency(String) case dependencyScanningFailure(Int, String) case missingExternalDependency(String) @@ -88,6 +89,8 @@ public struct Driver { return "Missing extraPcmArgs to build Clang module: \(moduleName)" case .missingModuleDependency(let moduleName): return "Missing Module Dependency Info: \(moduleName)" + case .missingContextHashOnSwiftDependency(let moduleName): + return "Missing Context Hash for Swift dependency: \(moduleName)" case .dependencyScanningFailure(let code, let error): return "Module Dependency Scanner returned with non-zero exit status: \(code), \(error)" case .unableToLoadOutputFileMap(let path): diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift index a2feb8fe3..22dbf55d2 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift @@ -97,6 +97,11 @@ public struct SwiftModuleDetails: Codable { /// Options to the compile command public var commandLine: [String]? = [] + /// The context hash for this module that encodes the producing interface's path, + /// target triple, etc. This field is optional because it is absent for the ModuleInfo + /// corresponding to the main module being built. + public var contextHash: String? + /// To build a PCM to be used by this Swift module, we need to append these /// arguments to the generic PCM build arguments reported from the dependency /// graph. diff --git a/Sources/SwiftDriver/Jobs/Planning.swift b/Sources/SwiftDriver/Jobs/Planning.swift index 97f3ca6a2..bee0fe299 100644 --- a/Sources/SwiftDriver/Jobs/Planning.swift +++ b/Sources/SwiftDriver/Jobs/Planning.swift @@ -527,7 +527,7 @@ extension Driver { try resolveVersionedClangDependencies(dependencyGraph: &dependencyGraph) // Set dependency modules' paths to be saved in the module cache. - try updateDependencyModulesWithModuleCachePath(dependencyGraph: &dependencyGraph) + try resolveDependencyModulePaths(dependencyGraph: &dependencyGraph) // Update the dependency oracle, adding this new dependency graph to its store try interModuleDependencyOracle.mergeModules(from: dependencyGraph) @@ -536,29 +536,61 @@ extension Driver { } /// Update the given inter-module dependency graph to set module paths to be within the module cache, - /// if one is present. - private mutating func updateDependencyModulesWithModuleCachePath(dependencyGraph: - inout InterModuleDependencyGraph) + /// if one is present, and for Swift modules to use the context hash in the file name. + private mutating func resolveDependencyModulePaths(dependencyGraph: inout InterModuleDependencyGraph) throws { - let moduleCachePath = parsedOptions.getLastArgument(.moduleCachePath)?.asSingle - if moduleCachePath != nil { - for (moduleId, moduleInfo) in dependencyGraph.modules { - // Output path on the main module is determined by the invocation arguments. - guard moduleId.moduleName != dependencyGraph.mainModuleName else { - continue - } - let modulePath = VirtualPath.lookup(moduleInfo.modulePath.path) - // Only update paths on modules which do not already specify a path beyond their module name - // and a file extension. - if modulePath.description == moduleId.moduleName + ".swiftmodule" || - modulePath.description == moduleId.moduleName + ".pcm" { - // Use VirtualPath to get the OS-specific path separators right. - let modulePathInCache = - try VirtualPath(path: moduleCachePath!) - .appending(component: modulePath.description) - dependencyGraph.modules[moduleId]!.modulePath = - TextualVirtualPath(path: modulePathInCache.intern()) - } + // For Swift module dependencies, set the output path to include + // the module's context hash + try resolveSwiftDependencyModuleFileNames(dependencyGraph: &dependencyGraph) + + // If a module cache path is specified, update all module dependencies + // to be output into it. + if let moduleCachePath = parsedOptions.getLastArgument(.moduleCachePath)?.asSingle { + try resolveDependencyModulePathsRelativeToModuleCache(dependencyGraph: &dependencyGraph, + moduleCachePath: moduleCachePath) + } + } + + /// For Swift module dependencies, set the output path to include the module's context hash + private mutating func resolveSwiftDependencyModuleFileNames(dependencyGraph: inout InterModuleDependencyGraph) + throws { + for (moduleId, moduleInfo) in dependencyGraph.modules { + // Output path on the main module is determined by the invocation arguments. + guard moduleId.moduleName != dependencyGraph.mainModuleName else { + continue + } + guard case .swift(let swiftDetails) = moduleInfo.details else { + continue + } + guard let contextHash = swiftDetails.contextHash else { + throw Driver.Error.missingContextHashOnSwiftDependency(moduleId.moduleName) + } + let plainPath = VirtualPath.lookup(dependencyGraph.modules[moduleId]!.modulePath.path) + let updatedPath = plainPath.parentDirectory.appending(component: "\(plainPath.basenameWithoutExt)-\(contextHash).\(plainPath.extension!)") + dependencyGraph.modules[moduleId]!.modulePath = TextualVirtualPath(path: updatedPath.intern()) + } + } + + /// Resolve all paths to dependency binary module files to be relative to the module cache path. + private mutating func resolveDependencyModulePathsRelativeToModuleCache(dependencyGraph: inout InterModuleDependencyGraph, + moduleCachePath: String) + throws { + for (moduleId, moduleInfo) in dependencyGraph.modules { + // Output path on the main module is determined by the invocation arguments. + guard moduleId.moduleName != dependencyGraph.mainModuleName else { + continue + } + let modulePath = VirtualPath.lookup(moduleInfo.modulePath.path) + // Only update paths on modules which do not already specify a path beyond their module name + // and a file extension. + if modulePath.description == moduleId.moduleName + ".swiftmodule" || + modulePath.description == moduleId.moduleName + ".pcm" { + // Use VirtualPath to get the OS-specific path separators right. + let modulePathInCache = + try VirtualPath(path: moduleCachePath) + .appending(component: modulePath.description) + dependencyGraph.modules[moduleId]!.modulePath = + TextualVirtualPath(path: modulePathInCache.intern()) } } } diff --git a/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift b/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift index f95ab2c47..671533f7a 100644 --- a/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift +++ b/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift @@ -158,12 +158,17 @@ private extension SwiftScan { try getStringArrayDetail(from: moduleDetailsRef, using: api.swiftscan_swift_textual_detail_get_extra_pcm_args, fieldName: "extraPCMArgs") + let contextHash = + try getOptionalStringDetail(from: moduleDetailsRef, + using: api.swiftscan_swift_textual_detail_get_context_hash) let isFramework = api.swiftscan_swift_textual_detail_get_is_framework(moduleDetailsRef) + return SwiftModuleDetails(moduleInterfacePath: moduleInterfacePath, compiledModuleCandidates: compiledModuleCandidates, bridgingHeaderPath: bridgingHeaderPath, bridgingSourceFiles: bridgingSourceFiles, commandLine: commandLine, + contextHash: contextHash, extraPcmArgs: extraPcmArgs, isFramework: isFramework) } diff --git a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index 7dc3f2a00..191495e1c 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -167,7 +167,7 @@ final class ExplicitModuleBuildTests: XCTestCase { let scanLibPath = try Driver.getScanLibPath(of: driver.toolchain, hostTriple: driver.hostTriple, env: ProcessEnv.vars) - try dependencyOracle + let _ = try dependencyOracle .verifyOrCreateScannerInstance(fileSystem: localFileSystem, swiftScanLibPath: scanLibPath) try dependencyOracle.mergeModules(from: moduleDependencyGraph) @@ -257,7 +257,7 @@ final class ExplicitModuleBuildTests: XCTestCase { let scanLibPath = try Driver.getScanLibPath(of: driver.toolchain, hostTriple: driver.hostTriple, env: ProcessEnv.vars) - try dependencyOracle + let _ = try dependencyOracle .verifyOrCreateScannerInstance(fileSystem: localFileSystem, swiftScanLibPath: scanLibPath) @@ -306,6 +306,11 @@ final class ExplicitModuleBuildTests: XCTestCase { } } + private func pathMatchesSwiftModule(path: VirtualPath, _ name: String) -> Bool { + return path.basenameWithoutExt.starts(with: "\(name)-") && + path.extension! == FileType.swiftModule.rawValue + } + /// Test generation of explicit module build jobs for dependency modules when the driver /// is invoked with -experimental-explicit-module-build func testExplicitModuleBuildJobs() throws { @@ -352,90 +357,105 @@ final class ExplicitModuleBuildTests: XCTestCase { try! driver.explicitDependencyBuildPlanner!.targetEncodedClangModuleName(for: moduleName, hashParts: hashParts) } + for job in jobs { XCTAssertEqual(job.outputs.count, 1) let outputFilePath = job.outputs[0].file - switch (outputFilePath) { - case .relative(RelativePath("A.swiftmodule")): + + // Swift dependencies + if outputFilePath.extension != nil, + outputFilePath.extension! == FileType.swiftModule.rawValue { + if pathMatchesSwiftModule(path: outputFilePath, "A") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("A"), dependencyOracle: dependencyOracle, pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("E.swiftmodule")): + } else if pathMatchesSwiftModule(path: outputFilePath, "E") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("E"), dependencyOracle: dependencyOracle, pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("G.swiftmodule")): + } else if pathMatchesSwiftModule(path: outputFilePath, "G") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("G"), dependencyOracle: dependencyOracle, pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("Swift.swiftmodule")): + } else if pathMatchesSwiftModule(path: outputFilePath, "Swift") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("Swift"), dependencyOracle: dependencyOracle, pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("_Concurrency.swiftmodule")): + } else if pathMatchesSwiftModule(path: outputFilePath, "_Concurrency") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("_Concurrency"), dependencyOracle: dependencyOracle, pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("SwiftOnoneSupport.swiftmodule")): + } else if pathMatchesSwiftModule(path: outputFilePath, "SwiftOnoneSupport") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("SwiftOnoneSupport"), dependencyOracle: dependencyOracle, pcmFileEncoder: pcmFileEncoder) - case .relative(pcmArgsEncodedRelativeModulePath(for: "A", with: pcmArgsCurrent, - pcmModuleNameEncoder: pcmModuleNameEncoder)): - try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("A"), - dependencyOracle: dependencyOracle, - pcmFileEncoder: pcmFileEncoder) - case .relative(pcmArgsEncodedRelativeModulePath(for: "B", with: pcmArgsCurrent, - pcmModuleNameEncoder: pcmModuleNameEncoder)): - try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("B"), - dependencyOracle: dependencyOracle, - pcmFileEncoder: pcmFileEncoder) - case .relative(pcmArgsEncodedRelativeModulePath(for: "C", with: pcmArgsCurrent, - pcmModuleNameEncoder: pcmModuleNameEncoder)): - try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("C"), - dependencyOracle: dependencyOracle, - pcmFileEncoder: pcmFileEncoder) - case .relative(pcmArgsEncodedRelativeModulePath(for: "G", with: pcmArgsCurrent, - pcmModuleNameEncoder: pcmModuleNameEncoder)): - try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("G"), - dependencyOracle: dependencyOracle, - pcmFileEncoder: pcmFileEncoder) - case .relative(pcmArgsEncodedRelativeModulePath(for: "G", with: pcmArgs9, - pcmModuleNameEncoder: pcmModuleNameEncoder)): - try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("G"), - dependencyOracle: dependencyOracle, - pcmFileEncoder: pcmFileEncoder) - // Module X is a dependency from Clang module "G" discovered only via versioned PCM - // re-scan. - case .relative(pcmArgsEncodedRelativeModulePath(for: "X", with: pcmArgsCurrent, - pcmModuleNameEncoder: pcmModuleNameEncoder)): - try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("X"), - dependencyOracle: dependencyOracle, - pcmFileEncoder: pcmFileEncoder) - case .relative(pcmArgsEncodedRelativeModulePath(for: "X", with: pcmArgs9, - pcmModuleNameEncoder: pcmModuleNameEncoder)): - try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("X"), - dependencyOracle: dependencyOracle, - pcmFileEncoder: pcmFileEncoder) - case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs9, - pcmModuleNameEncoder: pcmModuleNameEncoder)): - try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("SwiftShims"), - dependencyOracle: dependencyOracle, - pcmFileEncoder: pcmFileEncoder) - case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgsCurrent, - pcmModuleNameEncoder: pcmModuleNameEncoder)): - try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("SwiftShims"), - dependencyOracle: dependencyOracle, - pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("testExplicitModuleBuildJobs")): - XCTAssertTrue(driver.isExplicitMainModuleJob(job: job)) - XCTAssertEqual(job.kind, .link) - case .temporary(_): - let baseName = "testExplicitModuleBuildJobs" - XCTAssertTrue(matchTemporary(outputFilePath, basename: baseName, fileExtension: "o") || - matchTemporary(outputFilePath, basename: baseName, fileExtension: "autolink")) - default: - XCTFail("Unexpected module dependency build job output: \(outputFilePath)") + } + // Clang Dependencies + } else if outputFilePath.extension != nil, + outputFilePath.extension! == FileType.pcm.rawValue { + switch (outputFilePath) { + case .relative(pcmArgsEncodedRelativeModulePath(for: "A", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("A"), + dependencyOracle: dependencyOracle, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "B", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("B"), + dependencyOracle: dependencyOracle, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "C", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("C"), + dependencyOracle: dependencyOracle, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "G", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("G"), + dependencyOracle: dependencyOracle, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "G", with: pcmArgs9, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("G"), + dependencyOracle: dependencyOracle, + pcmFileEncoder: pcmFileEncoder) + // Module X is a dependency from Clang module "G" discovered only via versioned PCM + // re-scan. + case .relative(pcmArgsEncodedRelativeModulePath(for: "X", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("X"), + dependencyOracle: dependencyOracle, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "X", with: pcmArgs9, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("X"), + dependencyOracle: dependencyOracle, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs9, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("SwiftShims"), + dependencyOracle: dependencyOracle, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("SwiftShims"), + dependencyOracle: dependencyOracle, + pcmFileEncoder: pcmFileEncoder) + default: + XCTFail("Unexpected module dependency build job output: \(outputFilePath)") + } + } else { + switch (outputFilePath) { + case .relative(RelativePath("testExplicitModuleBuildJobs")): + XCTAssertTrue(driver.isExplicitMainModuleJob(job: job)) + XCTAssertEqual(job.kind, .link) + case .temporary(_): + let baseName = "testExplicitModuleBuildJobs" + XCTAssertTrue(matchTemporary(outputFilePath, basename: baseName, fileExtension: "o") || + matchTemporary(outputFilePath, basename: baseName, fileExtension: "autolink")) + default: + XCTFail("Unexpected module dependency build job output: \(outputFilePath)") + } } } } @@ -495,50 +515,71 @@ final class ExplicitModuleBuildTests: XCTestCase { for job in jobs { guard job.kind != .interpret else { continue } XCTAssertEqual(job.outputs.count, 1) - switch (job.outputs[0].file) { - case .relative(RelativePath("A.swiftmodule")): + let outputFilePath = job.outputs[0].file + // Swift dependencies + if outputFilePath.extension != nil, + outputFilePath.extension! == FileType.swiftModule.rawValue { + if pathMatchesSwiftModule(path: outputFilePath, "A") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("A"), dependencyOracle: dependencyOracle, pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("Swift.swiftmodule")): + } else if pathMatchesSwiftModule(path: outputFilePath, "Swift") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("Swift"), dependencyOracle: dependencyOracle, pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("_Concurrency.swiftmodule")): + } else if pathMatchesSwiftModule(path: outputFilePath, "_Concurrency") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("_Concurrency"), dependencyOracle: dependencyOracle, pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("SwiftOnoneSupport.swiftmodule")): + } else if pathMatchesSwiftModule(path: outputFilePath, "SwiftOnoneSupport") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("SwiftOnoneSupport"), dependencyOracle: dependencyOracle, pcmFileEncoder: pcmFileEncoder) - case .relative(pcmArgsEncodedRelativeModulePath(for: "A", with: pcmArgsCurrent, - pcmModuleNameEncoder: pcmModuleNameEncoder)): - try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("A"), - dependencyOracle: dependencyOracle, - pcmFileEncoder: pcmFileEncoder) - case .relative(pcmArgsEncodedRelativeModulePath(for: "B", with: pcmArgsCurrent, - pcmModuleNameEncoder: pcmModuleNameEncoder)): - try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("B"), - dependencyOracle: dependencyOracle, - pcmFileEncoder: pcmFileEncoder) - case .relative(pcmArgsEncodedRelativeModulePath(for: "C", with: pcmArgsCurrent, - pcmModuleNameEncoder: pcmModuleNameEncoder)): - try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("C"), - dependencyOracle: dependencyOracle, - pcmFileEncoder: pcmFileEncoder) - case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs9, - pcmModuleNameEncoder: pcmModuleNameEncoder)): - try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("SwiftShims"), - dependencyOracle: dependencyOracle, - pcmFileEncoder: pcmFileEncoder) - case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgsCurrent, - pcmModuleNameEncoder: pcmModuleNameEncoder)): - try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("SwiftShims"), - dependencyOracle: dependencyOracle, - pcmFileEncoder: pcmFileEncoder) - default: - XCTFail("Unexpected module dependency build job output: \(job.outputs[0].file)") + } + // Clang Dependencies + } else if outputFilePath.extension != nil, + outputFilePath.extension! == FileType.pcm.rawValue { + switch (outputFilePath) { + case .relative(pcmArgsEncodedRelativeModulePath(for: "A", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("A"), + dependencyOracle: dependencyOracle, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "B", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("B"), + dependencyOracle: dependencyOracle, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "C", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("C"), + dependencyOracle: dependencyOracle, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs9, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("SwiftShims"), + dependencyOracle: dependencyOracle, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("SwiftShims"), + dependencyOracle: dependencyOracle, + pcmFileEncoder: pcmFileEncoder) + default: + XCTFail("Unexpected module dependency build job output: \(outputFilePath)") + } + } else { + switch (outputFilePath) { + case .relative(RelativePath("testExplicitModuleBuildJobs")): + XCTAssertTrue(driver.isExplicitMainModuleJob(job: job)) + XCTAssertEqual(job.kind, .link) + case .temporary(_): + let baseName = "testExplicitModuleBuildJobs" + XCTAssertTrue(matchTemporary(outputFilePath, basename: baseName, fileExtension: "o") || + matchTemporary(outputFilePath, basename: baseName, fileExtension: "autolink")) + default: + XCTFail("Unexpected module dependency build job output: \(outputFilePath)") + } } } }