From a0ab72ff898eaec58769073a6a6a7b0269e16ba9 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Thu, 3 Jun 2021 15:43:03 -0700 Subject: [PATCH 1/9] [Explicit Module Builds] Add libSwiftScan API for scanner cache serialization/deserialization https://github.com/apple/swift/pull/37723 added API to libSwiftScan to save and restore the dependency scanner's state on the filesystem. This PR adds this API to the corresponding client code in the driver. --- Sources/CSwiftScan/include/swiftscan_header.h | 9 ++- .../InterModuleDependencyOracle.swift | 28 +++++++++ Sources/SwiftDriver/Jobs/CompileJob.swift | 4 +- Sources/SwiftDriver/SwiftScan/SwiftScan.swift | 29 ++++++++++ Sources/SwiftDriver/Utilities/FileType.swift | 22 ++++--- .../ExplicitModuleBuildTests.swift | 58 +++++++++++++++++++ 6 files changed, 138 insertions(+), 12 deletions(-) diff --git a/Sources/CSwiftScan/include/swiftscan_header.h b/Sources/CSwiftScan/include/swiftscan_header.h index 27f391859..30381c176 100644 --- a/Sources/CSwiftScan/include/swiftscan_header.h +++ b/Sources/CSwiftScan/include/swiftscan_header.h @@ -200,17 +200,20 @@ typedef struct { //=== Scanner Functions ---------------------------------------------------===// swiftscan_scanner_t (*swiftscan_scanner_create)(void); void (*swiftscan_scanner_dispose)(swiftscan_scanner_t); - swiftscan_dependency_graph_t (*swiftscan_dependency_graph_create)(swiftscan_scanner_t, swiftscan_scan_invocation_t); - swiftscan_batch_scan_result_t * (*swiftscan_batch_scan_result_create)(swiftscan_scanner_t, swiftscan_batch_scan_input_t *, swiftscan_scan_invocation_t); - swiftscan_import_set_t (*swiftscan_import_set_create)(swiftscan_scanner_t, swiftscan_scan_invocation_t); + + //=== Scanner Cache Functions ---------------------------------------------===// + void (*swiftscan_scanner_cache_serialize)(swiftscan_scanner_t scanner, const char * path); + bool (*swiftscan_scanner_cache_load)(swiftscan_scanner_t scanner, const char * path); + void (*swiftscan_scanner_cache_reset)(swiftscan_scanner_t scanner); + } swiftscan_functions_t; #endif // SWIFT_C_DEPENDENCY_SCAN_H diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift index 2d2a2e2dd..f5e1133b1 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift @@ -83,6 +83,34 @@ public class InterModuleDependencyOracle { } } + @_spi(Testing) public func serializeScannerCache(to path: AbsolutePath) { + guard let swiftScan = swiftScanLibInstance else { + fatalError("Attempting to serialize scanner cache with no scanner instance.") + } + if swiftScan.canLoadStoreScannerCache() { + swiftScan.serializeScannerCache(to: path) + } + } + + @_spi(Testing) public func loadScannerCache(from path: AbsolutePath) -> Bool { + guard let swiftScan = swiftScanLibInstance else { + fatalError("Attempting to load scanner cache with no scanner instance.") + } + if swiftScan.canLoadStoreScannerCache() { + return swiftScan.loadScannerCache(from: path) + } + return false + } + + @_spi(Testing) public func resetScannerCache() { + guard let swiftScan = swiftScanLibInstance else { + fatalError("Attempting to reset scanner cache with no scanner instance.") + } + if swiftScan.canLoadStoreScannerCache() { + swiftScan.resetScannerCache() + } + } + private var hasScannerInstance: Bool { self.swiftScanLibInstance != nil } /// Queue to sunchronize accesses to the scanner diff --git a/Sources/SwiftDriver/Jobs/CompileJob.swift b/Sources/SwiftDriver/Jobs/CompileJob.swift index 894b32b0a..01bdc3883 100644 --- a/Sources/SwiftDriver/Jobs/CompileJob.swift +++ b/Sources/SwiftDriver/Jobs/CompileJob.swift @@ -93,7 +93,7 @@ extension Driver { .privateSwiftInterface, .swiftSourceInfoFile, .diagnostics, .objcHeader, .swiftDeps, .remap, .tbd, .moduleTrace, .yamlOptimizationRecord, .bitstreamOptimizationRecord, .pcm, .pch, .clangModuleMap, .jsonCompilerFeatures, .jsonTargetInfo, .jsonSwiftArtifacts, - .indexUnitOutputPath, nil: + .indexUnitOutputPath, .modDepCache, nil: return false } } @@ -444,7 +444,7 @@ extension FileType { .diagnostics, .objcHeader, .image, .swiftDeps, .moduleTrace, .tbd, .yamlOptimizationRecord, .bitstreamOptimizationRecord, .swiftInterface, .privateSwiftInterface, .swiftSourceInfoFile, .clangModuleMap, .jsonSwiftArtifacts, - .indexUnitOutputPath: + .indexUnitOutputPath, .modDepCache: fatalError("Output type can never be a primary output") } } diff --git a/Sources/SwiftDriver/SwiftScan/SwiftScan.swift b/Sources/SwiftDriver/SwiftScan/SwiftScan.swift index a66819972..82922fa8d 100644 --- a/Sources/SwiftDriver/SwiftScan/SwiftScan.swift +++ b/Sources/SwiftDriver/SwiftScan/SwiftScan.swift @@ -208,6 +208,26 @@ internal final class SwiftScan { return resultGraphMap } + @_spi(Testing) public func canLoadStoreScannerCache() -> Bool { + return api.swiftscan_scanner_cache_load != nil && + api.swiftscan_scanner_cache_serialize != nil && + api.swiftscan_scanner_cache_reset != nil + } + + func serializeScannerCache(to path: AbsolutePath) { + api.swiftscan_scanner_cache_serialize(scanner, + path.description.cString(using: String.Encoding.utf8)) + } + + func loadScannerCache(from path: AbsolutePath) -> Bool { + return api.swiftscan_scanner_cache_load(scanner, + path.description.cString(using: String.Encoding.utf8)) + } + + func resetScannerCache() { + api.swiftscan_scanner_cache_reset(scanner) + } + @_spi(Testing) public func canQuerySupportedArguments() -> Bool { return api.swiftscan_compiler_supported_arguments_query != nil && api.swiftscan_string_set_dispose != nil @@ -253,6 +273,7 @@ private extension swiftscan_functions_t { } return sym } + // Supported features/flags query self.swiftscan_string_set_dispose = try loadOptional("swiftscan_string_set_dispose") self.swiftscan_compiler_supported_arguments_query = @@ -260,6 +281,14 @@ private extension swiftscan_functions_t { self.swiftscan_compiler_supported_features_query = try loadOptional("swiftscan_compiler_supported_features_query") + // Dependency scanner serialization/deserialization features + self.swiftscan_scanner_cache_serialize = + try loadOptional("swiftscan_scanner_cache_serialize") + self.swiftscan_scanner_cache_load = + try loadOptional("swiftscan_scanner_cache_load") + self.swiftscan_scanner_cache_reset = + try loadOptional("swiftscan_scanner_cache_reset") + // MARK: Required Methods func loadRequired(_ symbol: String) throws -> T { guard let sym: T = dlsym(swiftscan, symbol: symbol) else { diff --git a/Sources/SwiftDriver/Utilities/FileType.swift b/Sources/SwiftDriver/Utilities/FileType.swift index 2cac81fc4..fd84698da 100644 --- a/Sources/SwiftDriver/Utilities/FileType.swift +++ b/Sources/SwiftDriver/Utilities/FileType.swift @@ -81,6 +81,9 @@ public enum FileType: String, Hashable, CaseIterable, Codable { /// Swift dependencies file. case swiftDeps = "swiftdeps" + /// Serialized dependency scanner state + case modDepCache = "moddepcache" + /// Remapping file case remap @@ -109,9 +112,9 @@ public enum FileType: String, Hashable, CaseIterable, Codable { /// Swift section of the internal wiki. case moduleTrace = "trace.json" - /// Indexing data directory. + /// Indexing data directory /// - /// The extension isn't real. + /// The extension isn't real, rather this FileType specifies a directory path. case indexData /// Output path to record in the indexing data store @@ -119,10 +122,10 @@ public enum FileType: String, Hashable, CaseIterable, Codable { /// This is only needed for use as a key in the output file map. case indexUnitOutputPath - /// Optimization record. + /// Optimization record case yamlOptimizationRecord = "opt.yaml" - /// Bitstream optimization record. + /// Bitstream optimization record case bitstreamOptimizationRecord = "opt.bitstream" /// Clang compiler module file @@ -169,6 +172,9 @@ extension FileType: CustomStringConvertible { case .swiftDeps: return "swift-dependencies" + case .modDepCache: + return "dependency-scanner-cache" + case .jsonDependencies: return "json-dependencies" @@ -218,7 +224,7 @@ extension FileType { .swiftDeps, .moduleTrace, .tbd, .yamlOptimizationRecord, .bitstreamOptimizationRecord, .swiftInterface, .privateSwiftInterface, .swiftSourceInfoFile, .jsonDependencies, .clangModuleMap, .jsonTargetInfo, .jsonCompilerFeatures, .jsonSwiftArtifacts, - .indexUnitOutputPath: + .indexUnitOutputPath, .modDepCache: return false } } @@ -289,6 +295,8 @@ extension FileType { return "objc-header" case .swiftDeps: return "swift-dependencies" + case .modDepCache: + return "dependency-scanner-cache" case .jsonDependencies: return "json-dependencies" case .jsonTargetInfo: @@ -327,7 +335,7 @@ extension FileType { case .image, .object, .dSYM, .pch, .sib, .raw_sib, .swiftModule, .swiftDocumentation, .swiftSourceInfoFile, .llvmBitcode, .diagnostics, .pcm, .swiftDeps, .remap, .indexData, .bitstreamOptimizationRecord, - .indexUnitOutputPath: + .indexUnitOutputPath, .modDepCache: return false } } @@ -341,7 +349,7 @@ extension FileType { case .swift, .sil, .sib, .ast, .image, .dSYM, .dependencies, .autolink, .swiftModule, .swiftDocumentation, .swiftInterface, .privateSwiftInterface, .swiftSourceInfoFile, .raw_sil, .raw_sib, .diagnostics, .objcHeader, .swiftDeps, .remap, - .importedModules, .tbd, .moduleTrace, .indexData, .yamlOptimizationRecord, + .importedModules, .tbd, .moduleTrace, .indexData, .yamlOptimizationRecord, .modDepCache, .bitstreamOptimizationRecord, .pcm, .pch, .jsonDependencies, .clangModuleMap, .jsonCompilerFeatures, .jsonTargetInfo, .jsonSwiftArtifacts, .indexUnitOutputPath: return false diff --git a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index 9ab056d2c..fcf61d868 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -748,6 +748,64 @@ final class ExplicitModuleBuildTests: XCTestCase { } } + + /// Test the libSwiftScan dependency scanning. + func testDependencyScanReuseCache() throws { + let (stdLibPath, shimsPath, toolchain, hostTriple) = try getDriverArtifactsForScanning() + try withTemporaryDirectory { path in + let cacheSavePath = path.appending(component: "saved.moddepcache") + let main = path.appending(component: "testDependencyScanning.swift") + try localFileSystem.writeFileContents(main) { + $0 <<< "import C;" + $0 <<< "import E;" + $0 <<< "import G;" + } + let packageRootPath = URL(fileURLWithPath: #file).pathComponents + .prefix(while: { $0 != "Tests" }).joined(separator: "/").dropFirst() + let testInputsPath = packageRootPath + "/TestInputs" + let cHeadersPath : String = testInputsPath + "/ExplicitModuleBuilds/CHeaders" + let swiftModuleInterfacesPath : String = testInputsPath + "/ExplicitModuleBuilds/Swift" + let scannerCommand = ["-scan-dependencies", + "-I", cHeadersPath, + "-I", swiftModuleInterfacesPath, + "-I", stdLibPath.description, + "-I", shimsPath.description, + main.pathString] + + let scanLibPath = try Driver.getScanLibPath(of: toolchain, + hostTriple: hostTriple, + env: ProcessEnv.vars) + // Run the first scan and serialize the cache contents. + let firstDependencyOracle = InterModuleDependencyOracle() + guard try firstDependencyOracle + .verifyOrCreateScannerInstance(fileSystem: localFileSystem, + swiftScanLibPath: scanLibPath) else { + XCTFail("Dependency scanner library not found") + return + } + + let firstScanGraph = + try! firstDependencyOracle.getDependencies(workingDirectory: path, + commandLine: scannerCommand) + firstDependencyOracle.serializeScannerCache(to: cacheSavePath) + + // Run the second scan, re-using the serialized cache contents. + let secondDependencyOracle = InterModuleDependencyOracle() + guard try secondDependencyOracle + .verifyOrCreateScannerInstance(fileSystem: localFileSystem, + swiftScanLibPath: scanLibPath) else { + XCTFail("Dependency scanner library not found") + return + } + XCTAssertFalse(secondDependencyOracle.loadScannerCache(from: cacheSavePath)) + let secondScanGraph = + try! secondDependencyOracle.getDependencies(workingDirectory: path, + commandLine: scannerCommand) + + XCTAssertTrue(firstScanGraph.modules.count == secondScanGraph.modules.count) + } + } + func testDependencyGraphMerge() throws { let moduleDependencyGraph1 = try JSONDecoder().decode( From 6186a5dea6fe9e2ed5a6aa9d36dabda144e04658 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Fri, 4 Jun 2021 16:33:52 -0700 Subject: [PATCH 2/9] [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 --- Sources/SwiftDriver/Driver/Driver.swift | 3 + .../InterModuleDependencyGraph.swift | 5 + Sources/SwiftDriver/Jobs/Planning.swift | 78 ++++-- .../SwiftScan/DependencyGraphBuilder.swift | 5 + .../ExplicitModuleBuildTests.swift | 235 ++++++++++-------- 5 files changed, 206 insertions(+), 120 deletions(-) diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index 2d84bb84c..96161623f 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -38,6 +38,7 @@ public struct Driver { case malformedModuleDependency(String, String) case missingPCMArguments(String) case missingModuleDependency(String) + case missingContextHashOnSwiftDependency(String) case dependencyScanningFailure(Int, String) case missingExternalDependency(String) @@ -91,6 +92,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 16fe0b872..61a14a27b 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 7ae82edca..2f9084465 100644 --- a/Sources/SwiftDriver/Jobs/Planning.swift +++ b/Sources/SwiftDriver/Jobs/Planning.swift @@ -526,7 +526,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) @@ -535,29 +535,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 ec9754217..1773d0f48 100644 --- a/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift +++ b/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift @@ -168,12 +168,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 fcf61d868..236536ed3 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)") + } } } } From 33596e322b1ab44ce19826224e212bbc6c6cdcd5 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Thu, 1 Jul 2021 09:49:13 -0700 Subject: [PATCH 3/9] Unblock package bots by making tests more resilient to environment changes. - Pass down SDK paths to explicit module tests - Remove "fake" stdlib files, making sure dependency scanning tests rely on the real ones - Gate a couple of tests on frontend flag availability --- .../Swift/Swift.swiftinterface | 4 -- .../Swift/SwiftOnoneSupport.swiftinterface | 4 -- .../ExplicitModuleBuildTests.swift | 30 ++++++++-- Tests/SwiftDriverTests/SwiftDriverTests.swift | 56 +++++++++++++++---- 4 files changed, 69 insertions(+), 25 deletions(-) delete mode 100644 TestInputs/ExplicitModuleBuilds/Swift/Swift.swiftinterface delete mode 100644 TestInputs/ExplicitModuleBuilds/Swift/SwiftOnoneSupport.swiftinterface diff --git a/TestInputs/ExplicitModuleBuilds/Swift/Swift.swiftinterface b/TestInputs/ExplicitModuleBuilds/Swift/Swift.swiftinterface deleted file mode 100644 index 8c4f4558d..000000000 --- a/TestInputs/ExplicitModuleBuilds/Swift/Swift.swiftinterface +++ /dev/null @@ -1,4 +0,0 @@ -// swift-interface-format-version: 1.0 -// swift-compiler-version: Swift version 5.4-dev (LLVM bd2476a8056e227, Swift 68ac381af6ca0e3) -// swift-module-flags: -disable-objc-attr-requires-foundation-module -enable-objc-interop -enable-library-evolution -module-link-name swiftCore -parse-stdlib -swift-version 5 -O -enforce-exclusivity=unchecked -enable-experimental-concise-pound-file -module-name Swift -import SwiftShims diff --git a/TestInputs/ExplicitModuleBuilds/Swift/SwiftOnoneSupport.swiftinterface b/TestInputs/ExplicitModuleBuilds/Swift/SwiftOnoneSupport.swiftinterface deleted file mode 100644 index 242516c51..000000000 --- a/TestInputs/ExplicitModuleBuilds/Swift/SwiftOnoneSupport.swiftinterface +++ /dev/null @@ -1,4 +0,0 @@ -// swift-interface-format-version: 1.0 -// swift-compiler-version: Swift version 5.4-dev (LLVM bd2476a8056e227, Swift 68ac381af6ca0e3) -// swift-module-flags: -disable-objc-attr-requires-foundation-module -enable-objc-interop -enable-library-evolution -module-link-name swiftSwiftOnoneSupport -parse-stdlib -swift-version 5 -O -enforce-exclusivity=unchecked -module-name SwiftOnoneSupport -import Swift diff --git a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index 236536ed3..cd00631cc 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -327,12 +327,13 @@ final class ExplicitModuleBuildTests: XCTestCase { let testInputsPath = packageRootPath + "/TestInputs" let cHeadersPath : String = testInputsPath + "/ExplicitModuleBuilds/CHeaders" let swiftModuleInterfacesPath : String = testInputsPath + "/ExplicitModuleBuilds/Swift" + let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? [] var driver = try Driver(args: ["swiftc", "-target", "x86_64-apple-macosx11.0", "-I", cHeadersPath, "-I", swiftModuleInterfacesPath, "-experimental-explicit-module-build", - main.pathString]) + main.pathString] + sdkArgumentsForTesting) let jobs = try driver.planBuild() // Figure out which Triples to use. @@ -346,8 +347,10 @@ final class ExplicitModuleBuildTests: XCTestCase { let pcmArgsCurrent = mainModuleSwiftDetails.extraPcmArgs var pcmArgs9 = ["-Xcc","-target","-Xcc","x86_64-apple-macosx10.9"] + var pcmArgs15 = ["-Xcc","-target","-Xcc","x86_64-apple-macosx10.15"] if driver.targetTriple.isDarwin { pcmArgs9.append(contentsOf: ["-Xcc", "-fapinotes-swift-version=5"]) + pcmArgs15.append(contentsOf: ["-Xcc", "-fapinotes-swift-version=5"]) } let pcmFileEncoder = { (moduleInfo: ModuleInfo, hashParts: [String]) -> VirtualPath.Handle in try! driver.explicitDependencyBuildPlanner!.targetEncodedClangModuleFilePath(for: moduleInfo, @@ -436,6 +439,11 @@ final class ExplicitModuleBuildTests: XCTestCase { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("SwiftShims"), dependencyOracle: dependencyOracle, pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs15, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs15, 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"), @@ -473,12 +481,13 @@ final class ExplicitModuleBuildTests: XCTestCase { let testInputsPath = packageRootPath + "/TestInputs" let cHeadersPath : String = testInputsPath + "/ExplicitModuleBuilds/CHeaders" let swiftModuleInterfacesPath : String = testInputsPath + "/ExplicitModuleBuilds/Swift" + let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? [] var driver = try Driver(args: ["swift", "-target", "x86_64-apple-macosx11.0", "-I", cHeadersPath, "-I", swiftModuleInterfacesPath, "-experimental-explicit-module-build", - main.pathString]) + main.pathString] + sdkArgumentsForTesting) let jobs = try driver.planBuild() @@ -501,8 +510,10 @@ final class ExplicitModuleBuildTests: XCTestCase { let pcmArgsCurrent = mainModuleSwiftDetails.extraPcmArgs var pcmArgs9 = ["-Xcc","-target","-Xcc","x86_64-apple-macosx10.9"] + var pcmArgs15 = ["-Xcc","-target","-Xcc","x86_64-apple-macosx10.15"] if driver.targetTriple.isDarwin { pcmArgs9.append(contentsOf: ["-Xcc", "-fapinotes-swift-version=5"]) + pcmArgs15.append(contentsOf: ["-Xcc", "-fapinotes-swift-version=5"]) } let pcmFileEncoder = { (moduleInfo: ModuleInfo, hashParts: [String]) -> VirtualPath.Handle in try! driver.explicitDependencyBuildPlanner!.targetEncodedClangModuleFilePath(for: moduleInfo, @@ -560,6 +571,11 @@ final class ExplicitModuleBuildTests: XCTestCase { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("SwiftShims"), dependencyOracle: dependencyOracle, pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs15, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs15, 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"), @@ -689,13 +705,14 @@ final class ExplicitModuleBuildTests: XCTestCase { let testInputsPath = packageRootPath + "/TestInputs" let cHeadersPath : String = testInputsPath + "/ExplicitModuleBuilds/CHeaders" let swiftModuleInterfacesPath : String = testInputsPath + "/ExplicitModuleBuilds/Swift" + let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? [] let scannerCommand = ["-scan-dependencies", "-import-prescan", "-I", cHeadersPath, "-I", swiftModuleInterfacesPath, "-I", stdLibPath.description, "-I", shimsPath.description, - main.pathString] + main.pathString] + sdkArgumentsForTesting let imports = try! dependencyOracle.getImports(workingDirectory: path, @@ -737,12 +754,13 @@ final class ExplicitModuleBuildTests: XCTestCase { let testInputsPath = packageRootPath + "/TestInputs" let cHeadersPath : String = testInputsPath + "/ExplicitModuleBuilds/CHeaders" let swiftModuleInterfacesPath : String = testInputsPath + "/ExplicitModuleBuilds/Swift" + let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? [] let scannerCommand = ["-scan-dependencies", "-I", cHeadersPath, "-I", swiftModuleInterfacesPath, "-I", stdLibPath.description, "-I", shimsPath.description, - main.pathString] + main.pathString] + sdkArgumentsForTesting // Here purely to dump diagnostic output in a reasonable fashion when things go wrong. let lock = NSLock() @@ -806,12 +824,14 @@ final class ExplicitModuleBuildTests: XCTestCase { let testInputsPath = packageRootPath + "/TestInputs" let cHeadersPath : String = testInputsPath + "/ExplicitModuleBuilds/CHeaders" let swiftModuleInterfacesPath : String = testInputsPath + "/ExplicitModuleBuilds/Swift" + let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? [] let scannerCommand = ["-scan-dependencies", + "-disable-implicit-concurrency-module-import", "-I", cHeadersPath, "-I", swiftModuleInterfacesPath, "-I", stdLibPath.description, "-I", shimsPath.description, - main.pathString] + main.pathString] + sdkArgumentsForTesting let scanLibPath = try Driver.getScanLibPath(of: toolchain, hostTriple: hostTriple, diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index 518e12bc4..a9e4c3c12 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -769,21 +769,21 @@ final class SwiftDriverTests: XCTestCase { XCTAssertFalse(plannedJobs[2].commandLine.contains(.flag("-emit-dependencies-path"))) XCTAssertFalse(plannedJobs[2].commandLine.contains(.flag("-serialize-diagnostics-path"))) } - + func testReferenceDependencies() throws { var driver = try Driver(args: ["swiftc", "foo.swift", "-incremental"]) let plannedJobs = try driver.planBuild() XCTAssertTrue(plannedJobs[0].kind == .compile) XCTAssertTrue(plannedJobs[0].commandLine.contains(.flag("-emit-reference-dependencies-path"))) } - + func testDuplicateName() throws { assertDiagnostics { diagnosticsEngine, verify in _ = try? Driver(args: ["swiftc", "-c", "foo.swift", "foo.swift"], diagnosticsEngine: diagnosticsEngine) verify.expect(.error("filename \"foo.swift\" used twice: 'foo.swift' and 'foo.swift'")) verify.expect(.note("filenames are used to distinguish private declarations with the same name")) } - + assertDiagnostics { diagnosticsEngine, verify in _ = try? Driver(args: ["swiftc", "-c", "foo.swift", "foo/foo.swift"], diagnosticsEngine: diagnosticsEngine) verify.expect(.error("filename \"foo.swift\" used twice: 'foo.swift' and 'foo/foo.swift'")) @@ -1235,7 +1235,7 @@ final class SwiftDriverTests: XCTestCase { XCTAssertFalse(cmd.contains(.flag("-dylib"))) XCTAssertFalse(cmd.contains(.flag("-shared"))) } - + do { // lto linking // Locating relevant libraries is dependent on being a macOS host @@ -1258,7 +1258,7 @@ final class SwiftDriverTests: XCTestCase { var driver3 = try Driver(args: commonArgs + ["-emit-executable", "-target", "x86_64-unknown-linux", "-lto=llvm-full"], env: env) let plannedJobs3 = try driver3.planBuild() XCTAssertFalse(plannedJobs3.contains(where: { $0.kind == .autolinkExtract })) - + let compileJob3 = try XCTUnwrap(plannedJobs3.first(where: { $0.kind == .compile })) XCTAssertTrue(compileJob3.outputs.contains { $0.file.basename.hasSuffix(".bc") }) @@ -1630,14 +1630,14 @@ final class SwiftDriverTests: XCTestCase { #endif } } - + func testSanitizerRecoverArgs() throws { let commonArgs = ["swiftc", "foo.swift", "bar.swift",] do { // address sanitizer + address sanitizer recover var driver = try Driver(args: commonArgs + ["-sanitize=address", "-sanitize-recover=address"]) let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() - + XCTAssertEqual(plannedJobs.count, 3) let compileJob = plannedJobs[0] @@ -2499,6 +2499,20 @@ final class SwiftDriverTests: XCTestCase { } } + func testClangTarget() throws { + var driver = try Driver(args: ["swiftc", "-target", + "x86_64-apple-macosx10.14", "foo.swift", "bar.swift"]) + guard driver.isFrontendArgSupported(.clangTarget) else { + throw XCTSkip("Skipping: compiler does not support '-clang-target'") + } + let plannedJobs = try driver.planBuild() + XCTAssertEqual(plannedJobs.count, 3) + XCTAssert(plannedJobs[0].commandLine.contains(.flag("-target"))) + XCTAssert(plannedJobs[0].commandLine.contains(.flag("-clang-target"))) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-target"))) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-clang-target"))) + } + func testDisableClangTargetForImplicitModule() throws { var driver = try Driver(args: ["swiftc", "-target", "x86_64-apple-macosx10.14", "foo.swift"]) @@ -2561,7 +2575,7 @@ final class SwiftDriverTests: XCTestCase { return } } - + XCTAssertThrowsError(try Driver(args: ["swiftc", "-c", "-static-stdlib", "-target", "x86_64-apple-macosx10.14", "foo.swift"])) { error in guard case DarwinToolchain.ToolchainValidationError.argumentNotSupported("-static-stdlib") = error else { @@ -2577,7 +2591,7 @@ final class SwiftDriverTests: XCTestCase { return } } - + XCTAssertThrowsError(try Driver(args: ["swiftc", "-c", "-target", "x86_64-apple-macosx10.14", "-experimental-cxx-stdlib", "libstdc++", "foo.swift"])) { error in guard case DarwinToolchain.ToolchainValidationError.darwinOnlySupportsLibCxx = error else { @@ -3359,7 +3373,7 @@ final class SwiftDriverTests: XCTestCase { XCTAssertFalse(jobs[0].commandLine.contains("-wmo")) XCTAssertTrue(jobs[0].commandLine.contains("-dump-ast")) } - + try assertDriverDiagnostics(args: ["swiftc", "-index-file", "-dump-ast", "foo.swift", "-index-file-path", "foo.swift", @@ -3487,7 +3501,7 @@ final class SwiftDriverTests: XCTestCase { do { struct MockExecutor: DriverExecutor { let resolver: ArgsResolver - + func execute(job: Job, forceResponseFiles: Bool, recordedInputModificationDates: [TypedVirtualPath : Date]) throws -> ProcessResult { return ProcessResult(arguments: [], environment: [:], exitStatus: .terminated(code: 0), output: .success(Array("bad JSON".utf8)), stderrOutput: .success([])) } @@ -3795,6 +3809,24 @@ final class SwiftDriverTests: XCTestCase { } } + func testUserModuleVersion() throws { + do { + var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name", + "foo", "-user-module-version", "12.21"]) + guard driver.isFrontendArgSupported(.userModuleVersion) else { + throw XCTSkip("Skipping: compiler does not support '-user-module-version'") + } + let plannedJobs = try driver.planBuild() + XCTAssertEqual(plannedJobs.count, 2) + let compileJob = plannedJobs[0] + let mergeJob = plannedJobs[1] + XCTAssertEqual(compileJob.kind, .compile) + XCTAssertEqual(mergeJob.kind, .mergeModule) + XCTAssertTrue(mergeJob.commandLine.contains(.flag("-user-module-version"))) + XCTAssertTrue(mergeJob.commandLine.contains(.flag("12.21"))) + } + } + func testVerifyEmittedInterfaceJob() throws { // Evolution enabled do { @@ -4572,7 +4604,7 @@ final class SwiftDriverTests: XCTestCase { XCTAssert(jobs.count == 1) XCTAssertEqual(jobs.first!.tool.name, "/usr/bin/nonexistent-swift-help") } - + func testSourceInfoFileEmitOption() throws { // implicit do { From 0247bccb859b5e5867145a8e2834353d3e52c2f7 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Wed, 7 Jul 2021 11:17:35 -0700 Subject: [PATCH 4/9] Gardening: delete deprecated API for tracking external targets. This was in-place as a temporary shim while SwiftPM adapted the `InterModuleDependencyOracle` API and a lighter-weight `ExternalTargetModulePathMap`. --- Sources/SwiftDriver/CMakeLists.txt | 1 - Sources/SwiftDriver/Driver/Driver.swift | 37 ++-- .../ExplicitDependencyBuildPlanner.swift | 8 +- .../CommonDependencyOperations.swift | 51 +---- .../InterModuleDependencyOracle.swift | 39 ---- .../ModuleDependencyScanning.swift | 37 ---- .../PlaceholderDependencyResolution.swift | 170 --------------- Sources/SwiftDriver/Jobs/Planning.swift | 23 +- .../ExplicitModuleBuildTests.swift | 203 ++++-------------- 9 files changed, 71 insertions(+), 498 deletions(-) delete mode 100644 Sources/SwiftDriver/ExplicitModuleBuilds/PlaceholderDependencyResolution.swift diff --git a/Sources/SwiftDriver/CMakeLists.txt b/Sources/SwiftDriver/CMakeLists.txt index b39d96d1d..43ec230b0 100644 --- a/Sources/SwiftDriver/CMakeLists.txt +++ b/Sources/SwiftDriver/CMakeLists.txt @@ -10,7 +10,6 @@ add_library(SwiftDriver "ExplicitModuleBuilds/ClangVersionedDependencyResolution.swift" "ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift" "ExplicitModuleBuilds/ModuleDependencyScanning.swift" - "ExplicitModuleBuilds/PlaceholderDependencyResolution.swift" "ExplicitModuleBuilds/SerializableModuleArtifacts.swift" "ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift" "ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift" diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index 96161623f..a284acf50 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -305,12 +305,9 @@ public struct Driver { /// is shared across many targets; otherwise, a new instance is created by the driver itself. @_spi(Testing) public let interModuleDependencyOracle: InterModuleDependencyOracle - // TODO: Once the clients have transitioned to using the InterModuleDependencyOracle API, - // this must convey information about the externally-prebuilt targets only - /// All external artifacts a build system (e.g. SwiftPM) may pass in as input to the explicit - /// build of the current module. Consists of a map of externally-built targets, and a map of all previously - /// discovered/scanned modules and their infos. - @_spi(Testing) public var externalBuildArtifacts: ExternalBuildArtifacts? = nil + /// A dictionary of external targets that are a part of the same build, mapping to filesystem paths + /// of their module files + @_spi(Testing) public var externalTargetModulePathMap: ExternalTargetModulePathMap? = nil /// A collection of all the flags the selected toolchain's `swift-frontend` supports public let supportedFrontendFlags: Set @@ -387,9 +384,14 @@ public struct Driver { /// expand response files, etc. By default this is the local filesystem. /// - Parameter executor: Used by the driver to execute jobs. The default argument /// is present to streamline testing, it shouldn't be used in production. - /// - Parameter externalBuildArtifacts: All external artifacts a build system may pass in as input to the explicit - /// build of the current module. Consists of a map of externally-built targets, and a map of all previously - /// discovered/scanned modules. + /// - Parameter integratedDriver: Used to distinguish whether the driver is being used as + /// an executable or as a library. + /// - Parameter compilerExecutableDir: Directory that contains the compiler executable to be used. + /// Used when in `integratedDriver` mode as a substitute for the driver knowing its executable path. + /// - Parameter externalTargetModulePathMap: A dictionary of external targets that are a part of + /// the same build, mapping to filesystem paths of their module files. + /// - Parameter interModuleDependencyOracle: An oracle for querying inter-module dependencies, + /// shared across different module builds by a build system. public init( args: [String], env: [String: String] = ProcessEnv.vars, @@ -398,9 +400,6 @@ public struct Driver { executor: DriverExecutor, integratedDriver: Bool = true, compilerExecutableDir: AbsolutePath? = nil, - // FIXME: Duplication with externalBuildArtifacts and externalTargetModulePathMap - // is a temporary backwards-compatibility shim to help transition SwiftPM to the new API - externalBuildArtifacts: ExternalBuildArtifacts? = nil, externalTargetModulePathMap: ExternalTargetModulePathMap? = nil, interModuleDependencyOracle: InterModuleDependencyOracle? = nil ) throws { @@ -411,10 +410,8 @@ public struct Driver { self.diagnosticEngine = diagnosticsEngine self.executor = executor - if let externalArtifacts = externalBuildArtifacts { - self.externalBuildArtifacts = externalArtifacts - } else if let externalTargetPaths = externalTargetModulePathMap { - self.externalBuildArtifacts = (externalTargetPaths, [:]) + if let externalTargetPaths = externalTargetModulePathMap { + self.externalTargetModulePathMap = externalTargetPaths } if case .subcommand = try Self.invocationRunMode(forArgs: args).mode { @@ -507,14 +504,6 @@ public struct Driver { self.interModuleDependencyOracle = dependencyOracle } else { self.interModuleDependencyOracle = InterModuleDependencyOracle() - - // This is a shim for backwards-compatibility with ModuleInfoMap-based API - // used by SwiftPM - if let externalArtifacts = externalBuildArtifacts { - if !externalArtifacts.1.isEmpty { - try self.interModuleDependencyOracle.mergeModules(from: externalArtifacts.1) - } - } } self.fileListThreshold = try Self.computeFileListThreshold(&self.parsedOptions, diagnosticsEngine: diagnosticsEngine) diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift index ca9019a79..5040ec1f2 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift @@ -16,12 +16,6 @@ import Foundation /// A map from a module identifier to a path to its .swiftmodule file. public typealias ExternalTargetModulePathMap = [ModuleDependencyId: AbsolutePath] -// FIXME: ExternalBuildArtifacts is a temporary backwards-compatibility shim -// to help transition SwiftPM to the new API. -/// A tuple all external artifacts a build system may pass in as input to the explicit build of the current module -/// Consists of a map of externally-built targets, and a map of all previously discovered/scanned modules. -public typealias ExternalBuildArtifacts = (ExternalTargetModulePathMap, ModuleInfoMap) - /// In Explicit Module Build mode, this planner is responsible for generating and providing /// build jobs for all module dependencies and providing compile command options /// that specify said explicit module dependencies. @@ -444,7 +438,7 @@ extension ExplicitDependencyBuildPlanner { /// Encapsulates some of the common queries of the ExplicitDependencyBuildPlanner with error-checking /// on the dependency graph's structure. -internal extension InterModuleDependencyGraph { +@_spi(Testing) public extension InterModuleDependencyGraph { func moduleInfo(of moduleId: ModuleDependencyId) throws -> ModuleInfo { guard let moduleInfo = modules[moduleId] else { throw Driver.Error.missingModuleDependency(moduleId.moduleName) diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift index 4a2819d74..72d7e3bd7 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift @@ -15,11 +15,9 @@ import TSCBasic /// For targets that are built alongside the driver's current module, the scanning action will report them as /// textual targets to be built from source. Because we can rely on these targets to have been built prior /// to the driver's current target, we resolve such external targets as prebuilt binary modules, in the graph. - mutating func resolveExternalDependencies(for externalBuildArtifacts: ExternalBuildArtifacts) + mutating func resolveExternalDependencies(for externalTargetModulePathMap: ExternalTargetModulePathMap) throws { - let externalTargetModulePathMap = externalBuildArtifacts.0 - - for (externalModuleId, externalModulePath) in externalTargetModulePathMap { + for (externalModuleId, externalModulePath) in externalTargetModulePathMap { // Replace the occurence of a Swift module to-be-built from source-file // to an info that describes a pre-built binary module. let swiftModuleId: ModuleDependencyId = .swift(externalModuleId.moduleName) @@ -30,57 +28,22 @@ import TSCBasic // a dependency on a target that is not actually used. continue } - - let newModuleId: ModuleDependencyId = .swiftPrebuiltExternal(externalModuleId.moduleName) + + let newModuleId: ModuleDependencyId = .swiftPrebuiltExternal(externalModuleId.moduleName) let newExternalModuleDetails = - try SwiftPrebuiltExternalModuleDetails(compiledModulePath: - TextualVirtualPath(path: VirtualPath.absolute(externalModulePath).intern())) + try SwiftPrebuiltExternalModuleDetails(compiledModulePath: + TextualVirtualPath(path: VirtualPath.absolute(externalModulePath).intern())) let newInfo = ModuleInfo(modulePath: TextualVirtualPath(path: VirtualPath.absolute(externalModulePath).intern()), sourceFiles: [], directDependencies: currentInfo.directDependencies, details: .swiftPrebuiltExternal(newExternalModuleDetails)) - Self.replaceModule(originalId: swiftModuleId, replacementId: newModuleId, + Self.replaceModule(originalId: swiftModuleId, replacementId: newModuleId, replacementInfo: newInfo, in: &modules) } } } -@_spi(Testing) public extension InterModuleDependencyOracle { - /// An API to allow clients to accumulate InterModuleDependencyGraphs across mutiple main externalModules/targets - /// into a single collection of discovered externalModules. - func mergeModules(from dependencyGraph: InterModuleDependencyGraph) throws { - try queue.sync { - for (moduleId, moduleInfo) in dependencyGraph.modules { - try InterModuleDependencyGraph.mergeModule(moduleId, moduleInfo, into: &externalModules) - } - } - } - - // This is a backwards-compatibility shim to handle existing ModuleInfoMap-based API - // used by SwiftPM - func mergeModules(from moduleInfoMap: ModuleInfoMap) throws { - try queue.sync { - for (moduleId, moduleInfo) in moduleInfoMap { - try InterModuleDependencyGraph.mergeModule(moduleId, moduleInfo, into: &externalModules) - } - } - } -} - -public extension InterModuleDependencyGraph { - // This is a shim for backwards-compatibility with existing API used by SwiftPM. - // TODO: After SwiftPM switches to using the oracle, this should be deleted. - static func mergeModules( - from dependencyGraph: InterModuleDependencyGraph, - into discoveredModules: inout ModuleInfoMap - ) throws { - for (moduleId, moduleInfo) in dependencyGraph.modules { - try mergeModule(moduleId, moduleInfo, into: &discoveredModules) - } - } -} - extension InterModuleDependencyGraph { /// Compute a set of modules that are "reachable" (form direct or transitive dependency) /// from each module in the graph. diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift index f5e1133b1..e6ad7b303 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift @@ -118,44 +118,5 @@ public class InterModuleDependencyOracle { /// A reference to an instance of the compiler's libSwiftScan shared library private var swiftScanLibInstance: SwiftScan? = nil - - // The below API is a legacy implementation of the oracle that is in-place to allow clients to - // transition to the new API. It is to be removed once that transition is complete. - /// The complete set of modules discovered so far, spanning potentially multiple targets, - /// accumulated across builds of multiple targets. - /// TODO: This is currently only used for placeholder resolution. libSwiftScan should allow us to move away - /// from the concept of a placeholder module so we should be able to get rid of this in the future. - internal var externalModules: ModuleInfoMap = [:] - /// Query the ModuleInfo of a module with a given ID - @_spi(Testing) public func getExternalModuleInfo(of moduleId: ModuleDependencyId) -> ModuleInfo? { - queue.sync { - return externalModules[moduleId] - } - } } -// This is a shim for backwards-compatibility with existing API used by SwiftPM. -// TODO: After SwiftPM switches to using the oracle, this should be deleted. -extension Driver { - public var interModuleDependencyGraph: InterModuleDependencyGraph? { - let mainModuleId : ModuleDependencyId = .swift(moduleOutputInfo.name) - var mainModuleDependencyGraph = - InterModuleDependencyGraph(mainModuleName: moduleOutputInfo.name) - - addModule(moduleId: mainModuleId, - moduleInfo: interModuleDependencyOracle.getExternalModuleInfo(of: mainModuleId)!, - into: &mainModuleDependencyGraph) - return mainModuleDependencyGraph - } - - private func addModule(moduleId: ModuleDependencyId, - moduleInfo: ModuleInfo, - into dependencyGraph: inout InterModuleDependencyGraph) { - dependencyGraph.modules[moduleId] = moduleInfo - moduleInfo.directDependencies?.forEach { dependencyId in - addModule(moduleId: dependencyId, - moduleInfo: interModuleDependencyOracle.getExternalModuleInfo(of: dependencyId)!, - into: &dependencyGraph) - } - } -} diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift index 7d31c9479..a17bd5544 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift @@ -52,48 +52,11 @@ public extension Driver { moduleDependencyGraphUse: .dependencyScan) // FIXME: MSVC runtime flags - // Pass in external target dependencies to be treated as placeholder dependencies by the scanner - if let externalBuildArtifacts = externalBuildArtifacts { - let dependencyPlaceholderMapFile = - try serializeExternalDependencyArtifacts(externalBuildArtifacts: externalBuildArtifacts) - commandLine.appendFlag("-placeholder-dependency-module-map-file") - commandLine.appendPath(dependencyPlaceholderMapFile) - } - // Pass on the input files commandLine.append(contentsOf: inputFiles.map { .path($0.file) }) return (inputs, commandLine) } - /// Serialize a map of placeholder (external) dependencies for the dependency scanner. - private func serializeExternalDependencyArtifacts(externalBuildArtifacts: ExternalBuildArtifacts) - throws -> VirtualPath { - let (externalTargetModulePathMap, externalModuleInfoMap) = externalBuildArtifacts - var placeholderArtifacts: [SwiftModuleArtifactInfo] = [] - - // Explicit external targets - for (moduleId, binaryModulePath) in externalTargetModulePathMap { - let modPath = TextualVirtualPath(path: VirtualPath.absolute(binaryModulePath).intern()) - placeholderArtifacts.append( - SwiftModuleArtifactInfo(name: moduleId.moduleName, - modulePath: modPath)) - } - - // All other already-scanned Swift modules - for (moduleId, moduleInfo) in externalModuleInfoMap - where !externalTargetModulePathMap.keys.contains(moduleId) { - guard case .swift(_) = moduleId else { continue } - placeholderArtifacts.append( - SwiftModuleArtifactInfo(name: moduleId.moduleName, - modulePath: moduleInfo.modulePath)) - } - let encoder = JSONEncoder() - encoder.outputFormatting = [.prettyPrinted] - let contents = try encoder.encode(placeholderArtifacts) - return VirtualPath.createUniqueTemporaryFileWithKnownContents(.init("\(moduleOutputInfo.name)-placeholder-modules.json"), - contents) - } - /// Returns false if the lib is available and ready to use private func initSwiftScanLib() throws -> Bool { // If `-nonlib-dependency-scanner` was specified or the libSwiftScan library cannot be found, diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/PlaceholderDependencyResolution.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/PlaceholderDependencyResolution.swift deleted file mode 100644 index 2ef4f8ed3..000000000 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/PlaceholderDependencyResolution.swift +++ /dev/null @@ -1,170 +0,0 @@ -//===--------------- PlaceholderDependencyResolution.swift ----------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -import TSCBasic -import TSCUtility -import Foundation - -@_spi(Testing) public extension InterModuleDependencyGraph { - // Building a Swift module in Explicit Module Build mode requires passing all of its module - // dependencies as explicit arguments to the build command. - // - // When the driver's clients (build systems) are planning a build that involves multiple - // Swift modules, planning for each individual module may take place before its dependencies - // have been built. This means that the dependency scanning action will not be able to - // discover such modules. In such cases, the clients must provide the driver with information - // about such external dependencies, including the path to where their compiled .swiftmodule - // will be located, once built, and a full inter-module dependency graph for each such dependence. - // - // The driver will pass down the information about such external dependencies to the scanning - // action, which will generate `placeholder` swift modules for them in the resulting dependency - // graph. The driver will then use the complete dependency graph provided by - // the client for each external dependency and use it to "resolve" the dependency's "placeholder" - // module. - // - // Consider an example SwiftPM package with two targets: target B, and target A, where A - // depends on B: - // SwiftPM will process targets in a topological order and “bubble-up” each target’s - // inter-module dependency graph to its dependees. First, SwiftPM will process B, and be - // able to plan its full build because it does not have any target dependencies. Then the - // driver is tasked with planning a build for A. SwiftPM will pass as input to the driver - // the module dependency graph of its target’s dependencies, in this case, just the - // dependency graph of B. The scanning action for module A will contain a placeholder module B, - // which the driver will then resolve using B's full dependency graph provided by the client. - - /// Resolve all placeholder dependencies using external dependency information provided by the client - mutating func resolvePlaceholderDependencies(for externalBuildArtifacts: ExternalBuildArtifacts, - using dependencyOracle: InterModuleDependencyOracle) - throws { - let externalTargetModulePathMap = externalBuildArtifacts.0 - let placeholderFilter : (ModuleDependencyId) -> Bool = { - if case .swiftPlaceholder(_) = $0 { - return true - } - return false - } - var placeholderModules = modules.keys.filter(placeholderFilter) - - // Resolve all target placeholder modules - let placeholderTargetModules = placeholderModules.filter { externalTargetModulePathMap[$0] != nil } - for moduleId in placeholderTargetModules { - guard let placeholderModulePath = externalTargetModulePathMap[moduleId] else { - throw Driver.Error.missingExternalDependency(moduleId.moduleName) - } - try resolveTargetPlaceholder(placeholderId: moduleId, - placeholderPath: placeholderModulePath, - dependencyOracle: dependencyOracle) - } - - // Process remaining placeholders until there are none left - placeholderModules = modules.keys.filter(placeholderFilter) - while !placeholderModules.isEmpty { - let moduleId = placeholderModules.first! - let swiftModuleId = ModuleDependencyId.swift(moduleId.moduleName) - guard let moduleInfo = dependencyOracle.getExternalModuleInfo(of: swiftModuleId) else { - throw Driver.Error.missingExternalDependency(moduleId.moduleName) - } - - // Insert the resolved module, replacing the placeholder. - try Self.mergeModule(swiftModuleId, moduleInfo, into: &modules) - - // Traverse and add all of this external module's dependencies to the current graph. - try resolvePlaceholderModuleDependencies(moduleId: swiftModuleId, - dependencyOracle: dependencyOracle) - - // Update the set of remaining placeholders to resolve - placeholderModules = modules.keys.filter(placeholderFilter) - } - } -} - -fileprivate extension InterModuleDependencyGraph { - /// Resolve a placeholder dependency that is an external target. - mutating func resolveTargetPlaceholder(placeholderId: ModuleDependencyId, - placeholderPath: AbsolutePath, - dependencyOracle: InterModuleDependencyOracle) - throws { - // For this placeholder dependency, generate a new module info containing only the pre-compiled - // module path, and insert it into the current module's dependency graph, - // replacing equivalent placeholder module. - // - // For all dependencies of this placeholder (direct and transitive), insert them - // into this module's graph. - // - Swift dependencies are inserted as-is - // - Clang dependencies are inserted as-is, if a matching Clang module is already found - // in this module's graph, we merge moduleInfos of the two modules, in order to obtain - // a super-set of their dependencies at all possible PCMArgs variants. - // - // The placeholder is resolved into a .swiftPrebuiltExternal module in the dependency graph. - // The placeholder's corresponding module may appear in the externalModuleInfoMap as either - // a .swift module or a .swiftPrebuiltExternal module if it had been resolved earlier - // in the multi-module build planning context. - let swiftModuleId = ModuleDependencyId.swift(placeholderId.moduleName) - let swiftPrebuiltModuleId = ModuleDependencyId.swiftPrebuiltExternal(placeholderId.moduleName) - let externalModuleId: ModuleDependencyId - let externalModuleInfo: ModuleInfo - if let moduleInfo = dependencyOracle.getExternalModuleInfo(of: swiftModuleId) { - externalModuleId = swiftModuleId - externalModuleInfo = moduleInfo - } else if let prebuiltModuleInfo = dependencyOracle.getExternalModuleInfo(of: swiftPrebuiltModuleId) { - externalModuleId = swiftPrebuiltModuleId - externalModuleInfo = prebuiltModuleInfo - } else { - throw Driver.Error.missingExternalDependency(placeholderId.moduleName) - } - - let placeholderHandle = try VirtualPath.intern(path: placeholderPath.pathString) - let newExternalModuleDetails = - try SwiftPrebuiltExternalModuleDetails(compiledModulePath: - TextualVirtualPath(path: placeholderHandle)) - let newInfo = ModuleInfo(modulePath: TextualVirtualPath(path: placeholderHandle), - sourceFiles: [], - directDependencies: externalModuleInfo.directDependencies, - details: .swiftPrebuiltExternal(newExternalModuleDetails)) - - // Insert the resolved module, replacing the placeholder. - try Self.mergeModule(swiftPrebuiltModuleId, newInfo, into: &modules) - - // Traverse and add all of this external target's dependencies to the current graph. - try resolvePlaceholderModuleDependencies(moduleId: externalModuleId, - dependencyOracle: dependencyOracle) - } - - /// Resolve all dependencies of a placeholder module (direct and transitive), but merging them into the current graph. - mutating func resolvePlaceholderModuleDependencies(moduleId: ModuleDependencyId, - dependencyOracle: InterModuleDependencyOracle) - throws { - guard let resolvingModuleInfo = dependencyOracle.getExternalModuleInfo(of: moduleId) else { - throw Driver.Error.missingExternalDependency(moduleId.moduleName) - } - - // Breadth-first traversal of all the dependencies of this module - var visited: Set = [] - var toVisit: [ModuleDependencyId] = resolvingModuleInfo.directDependencies ?? [] - var currentIndex = 0 - while let currentId = toVisit[currentIndex...].first { - currentIndex += 1 - visited.insert(currentId) - guard let currentInfo = dependencyOracle.getExternalModuleInfo(of: currentId) else { - throw Driver.Error.missingExternalDependency(currentId.moduleName) - } - - try Self.mergeModule(currentId, currentInfo, into: &modules) - - let currentDependencies = currentInfo.directDependencies ?? [] - for childId in currentDependencies where !visited.contains(childId) { - if !toVisit.contains(childId) { - toVisit.append(childId) - } - } - } - } -} diff --git a/Sources/SwiftDriver/Jobs/Planning.swift b/Sources/SwiftDriver/Jobs/Planning.swift index 2f9084465..ae07acbfd 100644 --- a/Sources/SwiftDriver/Jobs/Planning.swift +++ b/Sources/SwiftDriver/Jobs/Planning.swift @@ -509,17 +509,13 @@ extension Driver { return try explicitDependencyBuildPlanner!.generateExplicitModuleDependenciesBuildJobs() } - mutating func gatherModuleDependencies() + @_spi(Testing) public mutating func gatherModuleDependencies() throws -> InterModuleDependencyGraph { var dependencyGraph = try performDependencyScan() - if externalBuildArtifacts != nil { - // Resolve placeholder dependencies in the dependency graph, if any. - // TODO: Should be deprecated once switched over to libSwiftScan in the clients - try dependencyGraph.resolvePlaceholderDependencies(for: externalBuildArtifacts!, - using: interModuleDependencyOracle) - - try dependencyGraph.resolveExternalDependencies(for: externalBuildArtifacts!) + if let externalTargetPaths = externalTargetModulePathMap { + // Resolve external dependencies in the dependency graph, if any. + try dependencyGraph.resolveExternalDependencies(for: externalTargetPaths) } // Re-scan Clang modules at all the targets they will be built against. @@ -528,9 +524,6 @@ extension Driver { // Set dependency modules' paths to be saved in the module cache. try resolveDependencyModulePaths(dependencyGraph: &dependencyGraph) - // Update the dependency oracle, adding this new dependency graph to its store - try interModuleDependencyOracle.mergeModules(from: dependencyGraph) - return dependencyGraph } @@ -538,16 +531,16 @@ extension Driver { /// 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 { - // 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 + try resolveSwiftDependencyModuleFileNames(dependencyGraph: &dependencyGraph) } /// For Swift module dependencies, set the output path to include the module's context hash diff --git a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index cd00631cc..8f2a13b44 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -21,10 +21,10 @@ import TestUtilities private func checkExplicitModuleBuildJob(job: Job, pcmArgs: [String], moduleId: ModuleDependencyId, - dependencyOracle: InterModuleDependencyOracle, + dependencyGraph: InterModuleDependencyGraph, pcmFileEncoder: (ModuleInfo, [String]) -> VirtualPath.Handle) throws { - let moduleInfo = dependencyOracle.getExternalModuleInfo(of: moduleId)! + let moduleInfo = try dependencyGraph.moduleInfo(of: moduleId) var downstreamPCMArgs = pcmArgs switch moduleInfo.details { case .swift(let swiftModuleDetails): @@ -61,7 +61,7 @@ throws { XCTAssertTrue(job.commandLine.contains(.flag(String("-fno-implicit-modules")))) try checkExplicitModuleBuildJobDependencies(job: job, pcmArgs: downstreamPCMArgs, moduleInfo: moduleInfo, - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) } @@ -70,11 +70,11 @@ throws { private func checkExplicitModuleBuildJobDependencies(job: Job, pcmArgs: [String], moduleInfo : ModuleInfo, - dependencyOracle: InterModuleDependencyOracle, + dependencyGraph: InterModuleDependencyGraph, pcmFileEncoder: (ModuleInfo, [String]) -> VirtualPath.Handle ) throws { for dependencyId in moduleInfo.directDependencies! { - let dependencyInfo = dependencyOracle.getExternalModuleInfo(of: dependencyId)! + let dependencyInfo = try dependencyGraph.moduleInfo(of: dependencyId) switch dependencyInfo.details { case .swift(let swiftDetails): // Load the dependency JSON and verify this dependency was encoded correctly @@ -137,8 +137,8 @@ private func checkExplicitModuleBuildJobDependencies(job: Job, // Ensure all transitive dependencies got added as well. for transitiveDependencyId in dependencyInfo.directDependencies! { try checkExplicitModuleBuildJobDependencies(job: job, pcmArgs: pcmArgs, - moduleInfo: dependencyOracle.getExternalModuleInfo(of: transitiveDependencyId)!, - dependencyOracle: dependencyOracle, + moduleInfo: try dependencyGraph.moduleInfo(of: transitiveDependencyId), + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) } @@ -163,14 +163,6 @@ final class ExplicitModuleBuildTests: XCTestCase { try JSONDecoder().decode( InterModuleDependencyGraph.self, from: ModuleDependenciesInputs.fastDependencyScannerOutput.data(using: .utf8)!) - let dependencyOracle = InterModuleDependencyOracle() - let scanLibPath = try Driver.getScanLibPath(of: driver.toolchain, - hostTriple: driver.hostTriple, - env: ProcessEnv.vars) - let _ = try dependencyOracle - .verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) - try dependencyOracle.mergeModules(from: moduleDependencyGraph) driver.explicitDependencyBuildPlanner = try ExplicitDependencyBuildPlanner(dependencyGraph: moduleDependencyGraph, toolchain: driver.toolchain) @@ -193,28 +185,28 @@ final class ExplicitModuleBuildTests: XCTestCase { pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs, moduleId: .clang("SwiftShims"), - dependencyOracle: dependencyOracle, + dependencyGraph: moduleDependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(pcmArgsEncodedRelativeModulePath(for: "c_simd", with: pcmArgs, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs, moduleId: .clang("c_simd"), - dependencyOracle: dependencyOracle, + dependencyGraph: moduleDependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(RelativePath("Swift.swiftmodule")): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs, moduleId: .swift("Swift"), - dependencyOracle: dependencyOracle, + dependencyGraph: moduleDependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(RelativePath("_Concurrency.swiftmodule")): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs, moduleId: .swift("_Concurrency"), - dependencyOracle: dependencyOracle, + dependencyGraph: moduleDependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(RelativePath("SwiftOnoneSupport.swiftmodule")): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs, moduleId: .swift("SwiftOnoneSupport"), - dependencyOracle: dependencyOracle, + dependencyGraph: moduleDependencyGraph, pcmFileEncoder: pcmFileEncoder) default: XCTFail("Unexpected module dependency build job output: \(job.outputs[0].file)") @@ -223,89 +215,6 @@ final class ExplicitModuleBuildTests: XCTestCase { } } - func testModuleDependencyWithExternalCommandGeneration() throws { - do { - // Construct a faux external dependency input for module B - let inputDependencyGraph = - try JSONDecoder().decode( - InterModuleDependencyGraph.self, - from: ModuleDependenciesInputs.bPlaceHolderInput.data(using: .utf8)!) - let targetModulePathMap: ExternalTargetModulePathMap = - [ModuleDependencyId.swiftPlaceholder("B"):AbsolutePath("/Somewhere/B.swiftmodule")] - - let executor = try SwiftDriverExecutor(diagnosticsEngine: DiagnosticsEngine(handlers: [Driver.stderrDiagnosticsHandler]), - processSet: ProcessSet(), - fileSystem: localFileSystem, - env: ProcessEnv.vars) - let dependencyOracle = InterModuleDependencyOracle() - try dependencyOracle.mergeModules(from: inputDependencyGraph) - - // Construct a module dependency graph that will contain .swiftPlaceholder("B"), - // .swiftPlaceholder("Swift"), .swiftPlaceholder("SwiftOnoneSupport") - var moduleDependencyGraph = - try JSONDecoder().decode( - InterModuleDependencyGraph.self, - from: ModuleDependenciesInputs.fastDependencyScannerPlaceholderOutput.data(using: .utf8)!) - - // Construct the driver with explicit external dependency input - let commandLine = ["swiftc", "-experimental-explicit-module-build", - "test.swift", "-module-name", "A", "-g"] - - var driver = try Driver(args: commandLine, executor: executor, - externalBuildArtifacts: (targetModulePathMap, [:]), - interModuleDependencyOracle: dependencyOracle) - let scanLibPath = try Driver.getScanLibPath(of: driver.toolchain, - hostTriple: driver.hostTriple, - env: ProcessEnv.vars) - let _ = try dependencyOracle - .verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) - - // Plan explicit dependency jobs, after resolving placeholders to actual dependencies. - try moduleDependencyGraph.resolvePlaceholderDependencies(for: (targetModulePathMap, [:]), - using: dependencyOracle) - - // Ensure the graph no longer contains any placeholders - XCTAssertFalse(moduleDependencyGraph.modules.keys.contains { - if case .swiftPlaceholder(_) = $0 { - return true - } - return false - }) - - // Merge the resolved version of the graph into the oracle - try dependencyOracle.mergeModules(from: moduleDependencyGraph) - driver.explicitDependencyBuildPlanner = - try ExplicitDependencyBuildPlanner(dependencyGraph: moduleDependencyGraph, - toolchain: driver.toolchain) - let modulePrebuildJobs = - try driver.explicitDependencyBuildPlanner!.generateExplicitModuleDependenciesBuildJobs() - - XCTAssertEqual(modulePrebuildJobs.count, 2) - let mainModuleJob = try driver.emitModuleJob() - XCTAssertEqual(mainModuleJob.inputs.count, 5) - for input in mainModuleJob.inputs { - switch (input.file) { - case .relative(RelativePath("Swift.swiftmodule")): - continue - case .relative(RelativePath("_Concurrency.swiftmodule")): - continue - case .relative(RelativePath("SwiftOnoneSupport.swiftmodule")): - continue - case .relative(RelativePath("test.swift")): - continue - case .absolute(AbsolutePath("/Somewhere/B.swiftmodule")): - continue - case .temporaryWithKnownContents(_, _): - XCTAssertTrue(matchTemporary(input.file, "A-dependencies.json")) - continue - default: - XCTFail("Unexpected module input: \(input.file)") - } - } - } - } - private func pathMatchesSwiftModule(path: VirtualPath, _ name: String) -> Bool { return path.basenameWithoutExt.starts(with: "\(name)-") && path.extension! == FileType.swiftModule.rawValue @@ -337,9 +246,8 @@ final class ExplicitModuleBuildTests: XCTestCase { let jobs = try driver.planBuild() // Figure out which Triples to use. - let dependencyOracle = driver.interModuleDependencyOracle - let mainModuleInfo = - dependencyOracle.getExternalModuleInfo(of: .swift("testExplicitModuleBuildJobs"))! + let dependencyGraph = try driver.gatherModuleDependencies() + let mainModuleInfo = try dependencyGraph.moduleInfo(of: .swift("testExplicitModuleBuildJobs")) guard case .swift(let mainModuleSwiftDetails) = mainModuleInfo.details else { XCTFail("Main module does not have Swift details field") return @@ -370,27 +278,27 @@ final class ExplicitModuleBuildTests: XCTestCase { outputFilePath.extension! == FileType.swiftModule.rawValue { if pathMatchesSwiftModule(path: outputFilePath, "A") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("A"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) } else if pathMatchesSwiftModule(path: outputFilePath, "E") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("E"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) } else if pathMatchesSwiftModule(path: outputFilePath, "G") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("G"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) } else if pathMatchesSwiftModule(path: outputFilePath, "Swift") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("Swift"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) } else if pathMatchesSwiftModule(path: outputFilePath, "_Concurrency") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("_Concurrency"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) } else if pathMatchesSwiftModule(path: outputFilePath, "SwiftOnoneSupport") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("SwiftOnoneSupport"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) } // Clang Dependencies @@ -400,54 +308,54 @@ final class ExplicitModuleBuildTests: XCTestCase { case .relative(pcmArgsEncodedRelativeModulePath(for: "A", with: pcmArgsCurrent, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("A"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(pcmArgsEncodedRelativeModulePath(for: "B", with: pcmArgsCurrent, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("B"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(pcmArgsEncodedRelativeModulePath(for: "C", with: pcmArgsCurrent, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("C"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(pcmArgsEncodedRelativeModulePath(for: "G", with: pcmArgsCurrent, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("G"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(pcmArgsEncodedRelativeModulePath(for: "G", with: pcmArgs9, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("G"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, 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, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(pcmArgsEncodedRelativeModulePath(for: "X", with: pcmArgs9, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("X"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs9, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("SwiftShims"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs15, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs15, moduleId: .clang("SwiftShims"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgsCurrent, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("SwiftShims"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) default: XCTFail("Unexpected module dependency build job output: \(outputFilePath)") @@ -500,9 +408,8 @@ final class ExplicitModuleBuildTests: XCTestCase { XCTAssertTrue(interpretJob.commandLine.contains(subsequence: ["-Xcc", "-Xclang", "-Xcc", "-fno-implicit-modules"])) // Figure out which Triples to use. - let dependencyOracle = driver.interModuleDependencyOracle - let mainModuleInfo = - dependencyOracle.getExternalModuleInfo(of: .swift("testExplicitModuleBuildJobs"))! + let dependencyGraph = try driver.gatherModuleDependencies() + let mainModuleInfo = try dependencyGraph.moduleInfo(of: .swift("testExplicitModuleBuildJobs")) guard case .swift(let mainModuleSwiftDetails) = mainModuleInfo.details else { XCTFail("Main module does not have Swift details field") return @@ -532,19 +439,19 @@ final class ExplicitModuleBuildTests: XCTestCase { outputFilePath.extension! == FileType.swiftModule.rawValue { if pathMatchesSwiftModule(path: outputFilePath, "A") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("A"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) } else if pathMatchesSwiftModule(path: outputFilePath, "Swift") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("Swift"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) } else if pathMatchesSwiftModule(path: outputFilePath, "_Concurrency") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("_Concurrency"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) } else if pathMatchesSwiftModule(path: outputFilePath, "SwiftOnoneSupport") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("SwiftOnoneSupport"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) } // Clang Dependencies @@ -554,32 +461,32 @@ final class ExplicitModuleBuildTests: XCTestCase { case .relative(pcmArgsEncodedRelativeModulePath(for: "A", with: pcmArgsCurrent, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("A"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(pcmArgsEncodedRelativeModulePath(for: "B", with: pcmArgsCurrent, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("B"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(pcmArgsEncodedRelativeModulePath(for: "C", with: pcmArgsCurrent, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("C"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs9, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("SwiftShims"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs15, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs15, moduleId: .clang("SwiftShims"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgsCurrent, pcmModuleNameEncoder: pcmModuleNameEncoder)): try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("SwiftShims"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) default: XCTFail("Unexpected module dependency build job output: \(outputFilePath)") @@ -867,32 +774,6 @@ final class ExplicitModuleBuildTests: XCTestCase { } } - func testDependencyGraphMerge() throws { - let moduleDependencyGraph1 = - try JSONDecoder().decode( - InterModuleDependencyGraph.self, - from: ModuleDependenciesInputs.mergeGraphInput1.data(using: .utf8)!) - let moduleDependencyGraph2 = - try JSONDecoder().decode( - InterModuleDependencyGraph.self, - from: ModuleDependenciesInputs.mergeGraphInput2.data(using: .utf8)!) - - var accumulatingModuleInfoMap: [ModuleDependencyId: ModuleInfo] = [:] - - try InterModuleDependencyGraph.mergeModules(from: moduleDependencyGraph1, - into: &accumulatingModuleInfoMap) - try InterModuleDependencyGraph.mergeModules(from: moduleDependencyGraph2, - into: &accumulatingModuleInfoMap) - - // Ensure the dependencies of the diplicate clang "B" module are merged - let clangIDs = accumulatingModuleInfoMap.keys.filter { $0.moduleName == "B" } - XCTAssertTrue(clangIDs.count == 1) - let clangBInfo = accumulatingModuleInfoMap[clangIDs[0]]! - XCTAssertTrue(clangBInfo.directDependencies!.count == 2) - XCTAssertTrue(clangBInfo.directDependencies!.contains(ModuleDependencyId.clang("D"))) - XCTAssertTrue(clangBInfo.directDependencies!.contains(ModuleDependencyId.clang("C"))) - } - func testExplicitSwiftModuleMap() throws { let jsonExample : String = """ [ From 827afa0ee5706287a61d181021acd64a0ba7b8c1 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Thu, 15 Jul 2021 16:37:19 -0700 Subject: [PATCH 5/9] [Dependency Scanning] Restore passing external target map as inputs to the scanning action. I got overzealous when deleting code in https://github.com/apple/swift-driver/pull/749 and removed one piece of functionality that we still rely on. Although the external target map is no longer necessary when scanning dependencies using libSwiftScan, we still use it for resolution of `canImport` statements during the scan. We need to figure out a better way to deal with that in the scanner, but in the meantime this will restore functionality. --- .../ModuleDependencyScanning.swift | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift index a17bd5544..5c5578ebd 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift @@ -52,11 +52,37 @@ public extension Driver { moduleDependencyGraphUse: .dependencyScan) // FIXME: MSVC runtime flags + // Pass in external target dependencies to be treated as placeholder dependencies by the scanner + if let externalTargetPaths = externalTargetModulePathMap { + let dependencyPlaceholderMapFile = + try serializeExternalDependencyArtifacts(externalTargetPaths: externalTargetPaths) + commandLine.appendFlag("-placeholder-dependency-module-map-file") + commandLine.appendPath(dependencyPlaceholderMapFile) + } + // Pass on the input files commandLine.append(contentsOf: inputFiles.map { .path($0.file) }) return (inputs, commandLine) } + /// Serialize a map of placeholder (external) dependencies for the dependency scanner. + private func serializeExternalDependencyArtifacts(externalTargetPaths: ExternalTargetModulePathMap) + throws -> VirtualPath { + var placeholderArtifacts: [SwiftModuleArtifactInfo] = [] + // Explicit external targets + for (moduleId, binaryModulePath) in externalTargetPaths { + let modPath = TextualVirtualPath(path: VirtualPath.absolute(binaryModulePath).intern()) + placeholderArtifacts.append( + SwiftModuleArtifactInfo(name: moduleId.moduleName, + modulePath: modPath)) + } + let encoder = JSONEncoder() + encoder.outputFormatting = [.prettyPrinted] + let contents = try encoder.encode(placeholderArtifacts) + return VirtualPath.createUniqueTemporaryFileWithKnownContents(.init("\(moduleOutputInfo.name)-external-modules.json"), + contents) + } + /// Returns false if the lib is available and ready to use private func initSwiftScanLib() throws -> Bool { // If `-nonlib-dependency-scanner` was specified or the libSwiftScan library cannot be found, From aed3e3c0be5195045eb74cef9403cb240d071868 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Fri, 30 Jul 2021 11:50:56 -0700 Subject: [PATCH 6/9] [Explicit Module Builds] Add API to specify richer external target module details Specifically, whether an external target moduel is a framework. This is required to pass this information down to the compiler in order to generate correct auto-linking directives. Part of rdar://81177797 --- Sources/SwiftDriver/Driver/Driver.swift | 23 +++++- .../ExplicitDependencyBuildPlanner.swift | 23 +++++- .../CommonDependencyOperations.swift | 9 ++- .../InterModuleDependencyGraph.swift | 7 +- .../ModuleDependencyScanning.swift | 3 +- Sources/SwiftDriver/Jobs/Planning.swift | 4 +- .../ExplicitModuleBuildTests.swift | 46 +++++++++++ .../ExplicitModuleDependencyBuildInputs.swift | 76 +++++++++++++++++++ 8 files changed, 175 insertions(+), 16 deletions(-) diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index a284acf50..11c154de2 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -23,6 +23,7 @@ public struct Driver { case invalidArgumentValue(String, String) case relativeFrontendPath(String) case subcommandPassedToDriver + case externalTargetDetailsAPIError case integratedReplRemoved case cannotSpecify_OForMultipleOutputs case conflictingOptions(Option, Option) @@ -57,6 +58,8 @@ public struct Driver { return "relative frontend path: \(path)" case .subcommandPassedToDriver: return "subcommand passed to driver" + case .externalTargetDetailsAPIError: + return "Cannot specify both: externalTargetModulePathMap and externalTargetModuleDetailsMap" case .integratedReplRemoved: return "Compiler-internal integrated REPL has been removed; use the LLDB-enhanced REPL instead." case .cannotSpecify_OForMultipleOutputs: @@ -307,7 +310,7 @@ public struct Driver { /// A dictionary of external targets that are a part of the same build, mapping to filesystem paths /// of their module files - @_spi(Testing) public var externalTargetModulePathMap: ExternalTargetModulePathMap? = nil + @_spi(Testing) public var externalTargetModuleDetailsMap: ExternalTargetModuleDetailsMap? = nil /// A collection of all the flags the selected toolchain's `swift-frontend` supports public let supportedFrontendFlags: Set @@ -388,8 +391,11 @@ public struct Driver { /// an executable or as a library. /// - Parameter compilerExecutableDir: Directory that contains the compiler executable to be used. /// Used when in `integratedDriver` mode as a substitute for the driver knowing its executable path. - /// - Parameter externalTargetModulePathMap: A dictionary of external targets that are a part of - /// the same build, mapping to filesystem paths of their module files. + /// - Parameter externalTargetModulePathMap: DEPRECATED: A dictionary of external targets + /// that are a part of the same build, mapping to filesystem paths of their module files. + /// - Parameter externalTargetModuleDetailsMap: A dictionary of external targets that are a part of + /// the same build, mapping to a details value which includes a filesystem path of their + /// `.swiftmodule` and a flag indicating whether the external target is a framework. /// - Parameter interModuleDependencyOracle: An oracle for querying inter-module dependencies, /// shared across different module builds by a build system. public init( @@ -400,7 +406,9 @@ public struct Driver { executor: DriverExecutor, integratedDriver: Bool = true, compilerExecutableDir: AbsolutePath? = nil, + // Deprecated in favour of the below `externalTargetModuleDetailsMap` externalTargetModulePathMap: ExternalTargetModulePathMap? = nil, + externalTargetModuleDetailsMap: ExternalTargetModuleDetailsMap? = nil, interModuleDependencyOracle: InterModuleDependencyOracle? = nil ) throws { self.env = env @@ -410,8 +418,15 @@ public struct Driver { self.diagnosticEngine = diagnosticsEngine self.executor = executor + if externalTargetModulePathMap != nil && externalTargetModuleDetailsMap != nil { + throw Error.externalTargetDetailsAPIError + } if let externalTargetPaths = externalTargetModulePathMap { - self.externalTargetModulePathMap = externalTargetPaths + self.externalTargetModuleDetailsMap = externalTargetPaths.mapValues { + ExternalTargetModuleDetails(path: $0, isFramework: false) + } + } else if let externalTargetDetails = externalTargetModuleDetailsMap { + self.externalTargetModuleDetailsMap = externalTargetDetails } if case .subcommand = try Self.invocationRunMode(forArgs: args).mode { diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift index 5040ec1f2..d5c69df44 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift @@ -14,8 +14,22 @@ import TSCUtility import Foundation /// A map from a module identifier to a path to its .swiftmodule file. +/// Deprecated in favour of the below `ExternalTargetModuleDetails` public typealias ExternalTargetModulePathMap = [ModuleDependencyId: AbsolutePath] +/// Details about an external target, including the path to its .swiftmodule file +/// and whether it is a framework. +public struct ExternalTargetModuleDetails { + @_spi(Testing) public init(path: AbsolutePath, isFramework: Bool) { + self.path = path + self.isFramework = isFramework + } + let path: AbsolutePath + let isFramework: Bool +} + +public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalTargetModuleDetails] + /// In Explicit Module Build mode, this planner is responsible for generating and providing /// build jobs for all module dependencies and providing compile command options /// that specify said explicit module dependencies. @@ -317,16 +331,17 @@ public typealias ExternalTargetModulePathMap = [ModuleDependencyId: AbsolutePath modulePath: TextualVirtualPath(path: clangModulePath), moduleMapPath: dependencyClangModuleDetails.moduleMapPath)) case .swiftPrebuiltExternal: - let compiledModulePath = try dependencyGraph - .swiftPrebuiltDetails(of: dependencyId) - .compiledModulePath + let prebuiltModuleDetails = try dependencyGraph.swiftPrebuiltDetails(of: dependencyId) + let compiledModulePath = prebuiltModuleDetails.compiledModulePath + let isFramework = prebuiltModuleDetails.isFramework let swiftModulePath: TypedVirtualPath = .init(file: compiledModulePath.path, type: .swiftModule) // Accumulate the requried information about this dependency // TODO: add .swiftdoc and .swiftsourceinfo for this module. swiftDependencyArtifacts.append( SwiftModuleArtifactInfo(name: dependencyId.moduleName, - modulePath: TextualVirtualPath(path: swiftModulePath.fileHandle))) + modulePath: TextualVirtualPath(path: swiftModulePath.fileHandle), + isFramework: isFramework)) case .swiftPlaceholder: fatalError("Unresolved placeholder dependencies at planning stage: \(dependencyId) of \(moduleId)") } diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift index 72d7e3bd7..4e985664c 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift @@ -15,9 +15,10 @@ import TSCBasic /// For targets that are built alongside the driver's current module, the scanning action will report them as /// textual targets to be built from source. Because we can rely on these targets to have been built prior /// to the driver's current target, we resolve such external targets as prebuilt binary modules, in the graph. - mutating func resolveExternalDependencies(for externalTargetModulePathMap: ExternalTargetModulePathMap) + mutating func resolveExternalDependencies(for externalTargetModuleDetailsMap: ExternalTargetModuleDetailsMap) throws { - for (externalModuleId, externalModulePath) in externalTargetModulePathMap { + for (externalModuleId, externalModuleDetails) in externalTargetModuleDetailsMap { + let externalModulePath = externalModuleDetails.path // Replace the occurence of a Swift module to-be-built from source-file // to an info that describes a pre-built binary module. let swiftModuleId: ModuleDependencyId = .swift(externalModuleId.moduleName) @@ -32,12 +33,12 @@ import TSCBasic let newModuleId: ModuleDependencyId = .swiftPrebuiltExternal(externalModuleId.moduleName) let newExternalModuleDetails = try SwiftPrebuiltExternalModuleDetails(compiledModulePath: - TextualVirtualPath(path: VirtualPath.absolute(externalModulePath).intern())) + TextualVirtualPath(path: VirtualPath.absolute(externalModulePath).intern()), + isFramework: externalModuleDetails.isFramework) let newInfo = ModuleInfo(modulePath: TextualVirtualPath(path: VirtualPath.absolute(externalModulePath).intern()), sourceFiles: [], directDependencies: currentInfo.directDependencies, details: .swiftPrebuiltExternal(newExternalModuleDetails)) - Self.replaceModule(originalId: swiftModuleId, replacementId: newModuleId, replacementInfo: newInfo, in: &modules) } diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift index 61a14a27b..85214f10c 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift @@ -132,12 +132,17 @@ public struct SwiftPrebuiltExternalModuleDetails: Codable { /// The path to the .swiftSourceInfo file. public var moduleSourceInfoPath: TextualVirtualPath? + /// A flag to indicate whether or not this module is a framework. + public var isFramework: Bool + public init(compiledModulePath: TextualVirtualPath, moduleDocPath: TextualVirtualPath? = nil, - moduleSourceInfoPath: TextualVirtualPath? = nil) throws { + moduleSourceInfoPath: TextualVirtualPath? = nil, + isFramework: Bool = false) throws { self.compiledModulePath = compiledModulePath self.moduleDocPath = moduleDocPath self.moduleSourceInfoPath = moduleSourceInfoPath + self.isFramework = isFramework } } diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift index 5c5578ebd..e0b1d399f 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift @@ -53,7 +53,8 @@ public extension Driver { // FIXME: MSVC runtime flags // Pass in external target dependencies to be treated as placeholder dependencies by the scanner - if let externalTargetPaths = externalTargetModulePathMap { + if let externalTargetDetails = externalTargetModuleDetailsMap { + let externalTargetPaths = externalTargetDetails.mapValues { $0.path } let dependencyPlaceholderMapFile = try serializeExternalDependencyArtifacts(externalTargetPaths: externalTargetPaths) commandLine.appendFlag("-placeholder-dependency-module-map-file") diff --git a/Sources/SwiftDriver/Jobs/Planning.swift b/Sources/SwiftDriver/Jobs/Planning.swift index ae07acbfd..03627adbd 100644 --- a/Sources/SwiftDriver/Jobs/Planning.swift +++ b/Sources/SwiftDriver/Jobs/Planning.swift @@ -513,9 +513,9 @@ extension Driver { throws -> InterModuleDependencyGraph { var dependencyGraph = try performDependencyScan() - if let externalTargetPaths = externalTargetModulePathMap { + if let externalTargetDetails = externalTargetModuleDetailsMap { // Resolve external dependencies in the dependency graph, if any. - try dependencyGraph.resolveExternalDependencies(for: externalTargetPaths) + try dependencyGraph.resolveExternalDependencies(for: externalTargetDetails) } // Re-scan Clang modules at all the targets they will be built against. diff --git a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index 8f2a13b44..ea2beeef0 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -215,6 +215,52 @@ final class ExplicitModuleBuildTests: XCTestCase { } } + func testModuleDependencyBuildCommandGenerationWithExternalFramework() throws { + do { + let externalDetails: ExternalTargetModuleDetailsMap = + [.swiftPrebuiltExternal("A"): ExternalTargetModuleDetails(path: AbsolutePath("/tmp/A.swiftmodule"), + isFramework: true)] + var driver = try Driver(args: ["swiftc", "-experimental-explicit-module-build", + "-module-name", "testModuleDependencyBuildCommandGenerationWithExternalFramework", + "test.swift"]) + var moduleDependencyGraph = + try JSONDecoder().decode( + InterModuleDependencyGraph.self, + from: ModuleDependenciesInputs.simpleDependencyGraphInput.data(using: .utf8)!) + // Key part of this test, using the external info to generate dependency pre-build jobs + try moduleDependencyGraph.resolveExternalDependencies(for: externalDetails) + driver.explicitDependencyBuildPlanner = + try ExplicitDependencyBuildPlanner(dependencyGraph: moduleDependencyGraph, + toolchain: driver.toolchain) + let modulePrebuildJobs = + try driver.explicitDependencyBuildPlanner!.generateExplicitModuleDependenciesBuildJobs() + + XCTAssertEqual(modulePrebuildJobs.count, 1) + let job = modulePrebuildJobs.first! + // Load the dependency JSON and verify this dependency was encoded correctly + let explicitDepsFlag = + SwiftDriver.Job.ArgTemplate.flag(String("-explicit-swift-module-map-file")) + XCTAssert(job.commandLine.contains(explicitDepsFlag)) + let jsonDepsPathIndex = job.commandLine.firstIndex(of: explicitDepsFlag) + let jsonDepsPathArg = job.commandLine[jsonDepsPathIndex! + 1] + guard case .path(let jsonDepsPath) = jsonDepsPathArg else { + XCTFail("No JSON dependency file path found.") + return + } + guard case let .temporaryWithKnownContents(_, contents) = jsonDepsPath else { + XCTFail("Unexpected path type") + return + } + let dependencyInfoList = try JSONDecoder().decode(Array.self, + from: contents) + XCTAssertEqual(dependencyInfoList.count, 1) + let dependencyArtifacts = + dependencyInfoList.first(where:{ $0.moduleName == "A" })! + // Ensure this is a framework, as specified by the externalDetails above. + XCTAssertEqual(dependencyArtifacts.isFramework, true) + } + } + private func pathMatchesSwiftModule(path: VirtualPath, _ name: String) -> Bool { return path.basenameWithoutExt.starts(with: "\(name)-") && path.extension! == FileType.swiftModule.rawValue diff --git a/Tests/SwiftDriverTests/Inputs/ExplicitModuleDependencyBuildInputs.swift b/Tests/SwiftDriverTests/Inputs/ExplicitModuleDependencyBuildInputs.swift index 7b3f89901..a6a37120f 100644 --- a/Tests/SwiftDriverTests/Inputs/ExplicitModuleDependencyBuildInputs.swift +++ b/Tests/SwiftDriverTests/Inputs/ExplicitModuleDependencyBuildInputs.swift @@ -396,6 +396,82 @@ enum ModuleDependenciesInputs { """ } + static var simpleDependencyGraphInput: String { + """ + { + "mainModuleName": "main", + "modules": [ + { + "swift": "main" + }, + { + "modulePath": "main.swiftmodule", + "sourceFiles": [ + "/main/main.swift" + ], + "directDependencies": [ + { + "swift": "B" + }, + ], + "details": { + "swift": { + "isFramework": false, + "extraPcmArgs": [ + "-Xcc", + "-fapinotes-swift-version=5" + ] + } + } + }, + { + "swift" : "B" + }, + { + "modulePath" : "B.swiftmodule", + "sourceFiles": [ + ], + "directDependencies" : [ + { + "swift": "A" + }, + ], + "details" : { + "swift" : { + "moduleInterfacePath": "B.swiftmodule/B.swiftinterface", + "isFramework": false, + "extraPcmArgs": [ + "-Xcc", + "-fapinotes-swift-version=5" + ], + } + } + }, + { + "swift": "A" + }, + { + "modulePath": "/tmp/A.swiftmodule", + "sourceFiles": [ + "/A/A.swift" + ], + "directDependencies" : [ + ], + "details": { + "swift": { + "isFramework": false, + "extraPcmArgs": [ + "-Xcc", + "-fapinotes-swift-version=5" + ] + } + } + } + ] + } + """ + } + static var mergeGraphInput2: String { """ { From 7d74b7c46189e7beaef43bb686cb613632716b07 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Fri, 30 Jul 2021 14:16:42 -0700 Subject: [PATCH 7/9] [Explicit Module Builds] Pass in `-fno-implicit-module-maps` for pre-building Clang explicit dependencies The dependency scanner specifies which exact modulemaps must be used, we do not need Clang to also search for modulemaps. --- .../ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift | 2 +- Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift index d5c69df44..ee8a6c84b 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift @@ -254,7 +254,7 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT commandLine: inout [Job.ArgTemplate]) throws { // Prohibit the frontend from implicitly building textual modules into binary modules. commandLine.appendFlags("-disable-implicit-swift-modules", "-Xcc", "-Xclang", "-Xcc", - "-fno-implicit-modules") + "-fno-implicit-modules", "-Xcc", "-Xclang", "-Xcc", "-fno-implicit-module-maps") var swiftDependencyArtifacts: [SwiftModuleArtifactInfo] = [] var clangDependencyArtifacts: [ClangModuleArtifactInfo] = [] try addModuleDependencies(moduleId: moduleId, pcmArgs: pcmArgs, diff --git a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index ea2beeef0..b2fab289f 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -59,6 +59,8 @@ throws { // Ensure the frontend was prohibited from doing implicit module builds XCTAssertTrue(job.commandLine.contains(.flag(String("-disable-implicit-swift-modules")))) XCTAssertTrue(job.commandLine.contains(.flag(String("-fno-implicit-modules")))) + XCTAssertTrue(job.commandLine.contains(.flag(String("-fno-implicit-module-maps")))) + try checkExplicitModuleBuildJobDependencies(job: job, pcmArgs: downstreamPCMArgs, moduleInfo: moduleInfo, dependencyGraph: dependencyGraph, From ec0462515ee96c0d8fc2020f0967a4e53b4e56fd Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Mon, 2 Aug 2021 11:56:48 -0700 Subject: [PATCH 8/9] Change `ExternalTargetModuleDetails.init` to be public libSwiftDriver clients need to be able to instantiate these objects. --- .../ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift index ee8a6c84b..f624cedf3 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift @@ -20,7 +20,7 @@ public typealias ExternalTargetModulePathMap = [ModuleDependencyId: AbsolutePath /// Details about an external target, including the path to its .swiftmodule file /// and whether it is a framework. public struct ExternalTargetModuleDetails { - @_spi(Testing) public init(path: AbsolutePath, isFramework: Bool) { + public init(path: AbsolutePath, isFramework: Bool) { self.path = path self.isFramework = isFramework } From 5fa09d4d00e57137cb41c2b41ed3ba84220c7a83 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Fri, 6 Aug 2021 13:58:19 -0700 Subject: [PATCH 9/9] [Explicit Module Builds] Include source modulemap path into PCM filename hash. Not doing so can lead to dangerous collisions if a build system uses a cetral cache across different targets that have a common dependency that is found in different places in the filesystem. For example, when building targets `A` and `B`, both of which depend on Clang module `C`, but have different search paths, finding `C` in two different locations, we should produce two distinct PCMs. With today's hashing scheme, the second will overwrite the first. --- .../ExplicitDependencyBuildPlanner.swift | 21 ++++-- .../ModuleDependencyScanning.swift | 6 +- .../ExplicitModuleBuildTests.swift | 65 ++++++++++++------- Tests/SwiftDriverTests/SwiftDriverTests.swift | 14 ---- 4 files changed, 58 insertions(+), 48 deletions(-) diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift index f624cedf3..ff482be63 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift @@ -219,10 +219,12 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT inputs: &inputs, commandLine: &commandLine) + let moduleMapPath = moduleDetails.moduleMapPath.path // Encode the target triple pcm args into the output `.pcm` filename let targetEncodedModulePath = try targetEncodedClangModuleFilePath(for: moduleInfo, - hashParts: getPCMHashParts(pcmArgs: pcmArgs)) + hashParts: getPCMHashParts(pcmArgs: pcmArgs, + moduleMapPath: moduleMapPath.description)) outputs.append(TypedVirtualPath(file: targetEncodedModulePath, type: .pcm)) commandLine.appendFlags("-emit-pcm", "-module-name", moduleId.moduleName, "-o", targetEncodedModulePath.description) @@ -230,7 +232,7 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT // The only required input is the .modulemap for this module. // Command line options in the dependency scanner output will include the // required modulemap, so here we must only add it to the list of inputs. - inputs.append(TypedVirtualPath(file: moduleDetails.moduleMapPath.path, + inputs.append(TypedVirtualPath(file: moduleMapPath, type: .clangModuleMap)) jobs.append(Job( @@ -322,9 +324,11 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT let dependencyInfo = try dependencyGraph.moduleInfo(of: dependencyId) let dependencyClangModuleDetails = try dependencyGraph.clangModuleDetails(of: dependencyId) + let moduleMapPath = dependencyClangModuleDetails.moduleMapPath.path let clangModulePath = try targetEncodedClangModuleFilePath(for: dependencyInfo, - hashParts: getPCMHashParts(pcmArgs: pcmArgs)) + hashParts: getPCMHashParts(pcmArgs: pcmArgs, + moduleMapPath: moduleMapPath.description)) // Accumulate the requried information about this dependency clangDependencyArtifacts.append( ClangModuleArtifactInfo(name: dependencyId.moduleName, @@ -389,11 +393,14 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT return VirtualPath.createUniqueTemporaryFileWithKnownContents(.init("\(moduleId.moduleName)-dependencies.json"), contents) } - private func getPCMHashParts(pcmArgs: [String]) -> [String] { + private func getPCMHashParts(pcmArgs: [String], moduleMapPath: String) -> [String] { + var results: [String] = [] + results.append(moduleMapPath) + results.append(contentsOf: pcmArgs) if integratedDriver { - return pcmArgs + return results } - var results = pcmArgs + // We need this to enable explict modules in the driver-as-executable mode. For instance, // we have two Swift targets A and B, where A depends on X.pcm which in turn depends on Y.pcm, // and B only depends on Y.pcm. In the driver-as-executable mode, the build system isn't aware @@ -445,7 +452,7 @@ extension ExplicitDependencyBuildPlanner { #else hashedArguments = SHA256().hash(hashInput).hexadecimalRepresentation #endif - let resultingName = moduleName + hashedArguments + let resultingName = moduleName + "-" + hashedArguments hashedModuleNameCache[cacheQuery] = resultingName return resultingName } diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift index e0b1d399f..fa7441617 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift @@ -14,8 +14,8 @@ import TSCBasic import SwiftOptions extension Diagnostic.Message { - static func warn_scanner_frontend_fallback() -> Diagnostic.Message { - .warning("Fallback to `swift-frontend` dependency scanner invocation") + static func warn_scanner_frontend_fallback(path: String) -> Diagnostic.Message { + .warning("Fallback to `swift-frontend` dependency scanner invocation, could not open/locate: \(path)") } } @@ -95,7 +95,7 @@ public extension Driver { .verifyOrCreateScannerInstance(fileSystem: fileSystem, swiftScanLibPath: scanLibPath) == false { fallbackToFrontend = true - diagnosticEngine.emit(.warn_scanner_frontend_fallback()) + diagnosticEngine.emit(.warn_scanner_frontend_fallback(path: scanLibPath.pathString)) } return fallbackToFrontend } diff --git a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index b2fab289f..3b6148cc4 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -174,14 +174,8 @@ final class ExplicitModuleBuildTests: XCTestCase { for job in modulePrebuildJobs { XCTAssertEqual(job.outputs.count, 1) XCTAssertFalse(driver.isExplicitMainModuleJob(job: job)) - let pcmFileEncoder = { (moduleInfo: ModuleInfo, hashParts: [String]) -> VirtualPath.Handle in - try! driver.explicitDependencyBuildPlanner!.targetEncodedClangModuleFilePath(for: moduleInfo, - hashParts: hashParts) - } - let pcmModuleNameEncoder = { (moduleName: String, hashParts: [String]) -> String in - try! driver.explicitDependencyBuildPlanner!.targetEncodedClangModuleName(for: moduleName, - hashParts: hashParts) - } + + let (pcmFileEncoder, pcmModuleNameEncoder) = pcmEncoderProducer(dependencyGraph: moduleDependencyGraph, driver: driver) switch (job.outputs[0].file) { case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs, pcmModuleNameEncoder: pcmModuleNameEncoder)): @@ -268,6 +262,39 @@ final class ExplicitModuleBuildTests: XCTestCase { path.extension! == FileType.swiftModule.rawValue } + private func pcmEncoderProducer(dependencyGraph: InterModuleDependencyGraph, + driver: Driver) + -> ((ModuleInfo, [String]) -> VirtualPath.Handle, (String, [String]) -> String) { + var driverCopy = driver + let moduleMapIncludedHashParts = { (_ moduleName: String, _ hashParts: [String]) -> [String] in + let moduleDetails = try? dependencyGraph.clangModuleDetails(of: .clang(moduleName)) + let lookupHashParts: [String] + if let details = moduleDetails { + let moduleMapPath = details.moduleMapPath.path.description + lookupHashParts = [moduleMapPath] + hashParts + } else { + // No such module found, no modulemap + lookupHashParts = hashParts + } + return lookupHashParts + } + + let pcmFileEncoder = { (moduleInfo: ModuleInfo, hashParts: [String]) -> VirtualPath.Handle in + let plainModulePath = VirtualPath.lookup(moduleInfo.modulePath.path) + let moduleName = plainModulePath.basenameWithoutExt + let lookupHashParts = moduleMapIncludedHashParts(moduleName, hashParts) + return try! driverCopy.explicitDependencyBuildPlanner!.targetEncodedClangModuleFilePath(for: moduleInfo, + hashParts: lookupHashParts) + } + + let pcmModuleNameEncoder = { (moduleName: String, hashParts: [String]) -> String in + let lookupHashParts = moduleMapIncludedHashParts(moduleName, hashParts) + return try! driverCopy.explicitDependencyBuildPlanner!.targetEncodedClangModuleName(for: moduleName, + hashParts: lookupHashParts) + } + return (pcmFileEncoder, pcmModuleNameEncoder) + } + /// Test generation of explicit module build jobs for dependency modules when the driver /// is invoked with -experimental-explicit-module-build func testExplicitModuleBuildJobs() throws { @@ -308,14 +335,8 @@ final class ExplicitModuleBuildTests: XCTestCase { pcmArgs9.append(contentsOf: ["-Xcc", "-fapinotes-swift-version=5"]) pcmArgs15.append(contentsOf: ["-Xcc", "-fapinotes-swift-version=5"]) } - let pcmFileEncoder = { (moduleInfo: ModuleInfo, hashParts: [String]) -> VirtualPath.Handle in - try! driver.explicitDependencyBuildPlanner!.targetEncodedClangModuleFilePath(for: moduleInfo, - hashParts: hashParts) - } - let pcmModuleNameEncoder = { (moduleName: String, hashParts: [String]) -> String in - try! driver.explicitDependencyBuildPlanner!.targetEncodedClangModuleName(for: moduleName, - hashParts: hashParts) - } + + let (pcmFileEncoder, pcmModuleNameEncoder) = pcmEncoderProducer(dependencyGraph: dependencyGraph, driver: driver) for job in jobs { XCTAssertEqual(job.outputs.count, 1) @@ -352,6 +373,7 @@ final class ExplicitModuleBuildTests: XCTestCase { // Clang Dependencies } else if outputFilePath.extension != nil, outputFilePath.extension! == FileType.pcm.rawValue { + switch (outputFilePath) { case .relative(pcmArgsEncodedRelativeModulePath(for: "A", with: pcmArgsCurrent, pcmModuleNameEncoder: pcmModuleNameEncoder)): @@ -470,14 +492,9 @@ final class ExplicitModuleBuildTests: XCTestCase { pcmArgs9.append(contentsOf: ["-Xcc", "-fapinotes-swift-version=5"]) pcmArgs15.append(contentsOf: ["-Xcc", "-fapinotes-swift-version=5"]) } - let pcmFileEncoder = { (moduleInfo: ModuleInfo, hashParts: [String]) -> VirtualPath.Handle in - try! driver.explicitDependencyBuildPlanner!.targetEncodedClangModuleFilePath(for: moduleInfo, - hashParts: hashParts) - } - let pcmModuleNameEncoder = { (moduleName: String, hashParts: [String]) -> String in - try! driver.explicitDependencyBuildPlanner!.targetEncodedClangModuleName(for: moduleName, - hashParts: hashParts) - } + + let (pcmFileEncoder, pcmModuleNameEncoder) = pcmEncoderProducer(dependencyGraph: dependencyGraph, driver: driver) + for job in jobs { guard job.kind != .interpret else { continue } XCTAssertEqual(job.outputs.count, 1) diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index a9e4c3c12..4ba275f9c 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -2499,20 +2499,6 @@ final class SwiftDriverTests: XCTestCase { } } - func testClangTarget() throws { - var driver = try Driver(args: ["swiftc", "-target", - "x86_64-apple-macosx10.14", "foo.swift", "bar.swift"]) - guard driver.isFrontendArgSupported(.clangTarget) else { - throw XCTSkip("Skipping: compiler does not support '-clang-target'") - } - let plannedJobs = try driver.planBuild() - XCTAssertEqual(plannedJobs.count, 3) - XCTAssert(plannedJobs[0].commandLine.contains(.flag("-target"))) - XCTAssert(plannedJobs[0].commandLine.contains(.flag("-clang-target"))) - XCTAssert(plannedJobs[1].commandLine.contains(.flag("-target"))) - XCTAssert(plannedJobs[1].commandLine.contains(.flag("-clang-target"))) - } - func testDisableClangTargetForImplicitModule() throws { var driver = try Driver(args: ["swiftc", "-target", "x86_64-apple-macosx10.14", "foo.swift"])