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)") + } } } }