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/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 2d84bb84c..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) @@ -38,6 +39,7 @@ public struct Driver { case malformedModuleDependency(String, String) case missingPCMArguments(String) case missingModuleDependency(String) + case missingContextHashOnSwiftDependency(String) case dependencyScanningFailure(Int, String) case missingExternalDependency(String) @@ -56,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: @@ -91,6 +95,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): @@ -302,12 +308,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 externalTargetModuleDetailsMap: ExternalTargetModuleDetailsMap? = nil /// A collection of all the flags the selected toolchain's `swift-frontend` supports public let supportedFrontendFlags: Set @@ -384,9 +387,17 @@ 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: 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( args: [String], env: [String: String] = ProcessEnv.vars, @@ -395,10 +406,9 @@ 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, + // Deprecated in favour of the below `externalTargetModuleDetailsMap` externalTargetModulePathMap: ExternalTargetModulePathMap? = nil, + externalTargetModuleDetailsMap: ExternalTargetModuleDetailsMap? = nil, interModuleDependencyOracle: InterModuleDependencyOracle? = nil ) throws { self.env = env @@ -408,10 +418,15 @@ 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 externalTargetModulePathMap != nil && externalTargetModuleDetailsMap != nil { + throw Error.externalTargetDetailsAPIError + } + if let externalTargetPaths = externalTargetModulePathMap { + 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 { @@ -504,14 +519,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..ff482be63 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift @@ -14,13 +14,21 @@ 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] -// 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) +/// Details about an external target, including the path to its .swiftmodule file +/// and whether it is a framework. +public struct ExternalTargetModuleDetails { + 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 @@ -211,10 +219,12 @@ public typealias ExternalBuildArtifacts = (ExternalTargetModulePathMap, ModuleIn 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) @@ -222,7 +232,7 @@ public typealias ExternalBuildArtifacts = (ExternalTargetModulePathMap, ModuleIn // 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( @@ -246,7 +256,7 @@ public typealias ExternalBuildArtifacts = (ExternalTargetModulePathMap, ModuleIn 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, @@ -314,25 +324,28 @@ public typealias ExternalBuildArtifacts = (ExternalTargetModulePathMap, ModuleIn 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, 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)") } @@ -380,11 +393,14 @@ public typealias ExternalBuildArtifacts = (ExternalTargetModulePathMap, ModuleIn 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 @@ -436,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 } @@ -444,7 +460,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..4e985664c 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift @@ -15,11 +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 externalBuildArtifacts: ExternalBuildArtifacts) + mutating func resolveExternalDependencies(for externalTargetModuleDetailsMap: ExternalTargetModuleDetailsMap) throws { - let externalTargetModulePathMap = externalBuildArtifacts.0 - - 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) @@ -30,57 +29,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()), + 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, + 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/InterModuleDependencyGraph.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift index 16fe0b872..85214f10c 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. @@ -127,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/InterModuleDependencies/InterModuleDependencyOracle.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift index 2d2a2e2dd..e6ad7b303 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 @@ -90,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..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)") } } @@ -53,9 +53,10 @@ public extension Driver { // FIXME: MSVC runtime flags // Pass in external target dependencies to be treated as placeholder dependencies by the scanner - if let externalBuildArtifacts = externalBuildArtifacts { + if let externalTargetDetails = externalTargetModuleDetailsMap { + let externalTargetPaths = externalTargetDetails.mapValues { $0.path } let dependencyPlaceholderMapFile = - try serializeExternalDependencyArtifacts(externalBuildArtifacts: externalBuildArtifacts) + try serializeExternalDependencyArtifacts(externalTargetPaths: externalTargetPaths) commandLine.appendFlag("-placeholder-dependency-module-map-file") commandLine.appendPath(dependencyPlaceholderMapFile) } @@ -66,33 +67,22 @@ public extension Driver { } /// 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) - } + 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 { @@ -105,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/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/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/Jobs/Planning.swift b/Sources/SwiftDriver/Jobs/Planning.swift index 7ae82edca..03627adbd 100644 --- a/Sources/SwiftDriver/Jobs/Planning.swift +++ b/Sources/SwiftDriver/Jobs/Planning.swift @@ -509,55 +509,80 @@ 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 externalTargetDetails = externalTargetModuleDetailsMap { + // Resolve external dependencies in the dependency graph, if any. + try dependencyGraph.resolveExternalDependencies(for: externalTargetDetails) } // Re-scan Clang modules at all the targets they will be built against. try resolveVersionedClangDependencies(dependencyGraph: &dependencyGraph) // Set dependency modules' paths to be saved in the module cache. - try updateDependencyModulesWithModuleCachePath(dependencyGraph: &dependencyGraph) - - // Update the dependency oracle, adding this new dependency graph to its store - try interModuleDependencyOracle.mergeModules(from: dependencyGraph) + try resolveDependencyModulePaths(dependencyGraph: &dependencyGraph) return dependencyGraph } /// 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()) - } + // 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 + 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/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/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 9ab056d2c..3b6148cc4 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): @@ -59,9 +59,11 @@ 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, - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) } @@ -70,11 +72,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 +139,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 +165,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) - try dependencyOracle - .verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) - try dependencyOracle.mergeModules(from: moduleDependencyGraph) driver.explicitDependencyBuildPlanner = try ExplicitDependencyBuildPlanner(dependencyGraph: moduleDependencyGraph, toolchain: driver.toolchain) @@ -180,41 +174,35 @@ 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)): 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 +211,90 @@ final class ExplicitModuleBuildTests: XCTestCase { } } - func testModuleDependencyWithExternalCommandGeneration() throws { + func testModuleDependencyBuildCommandGenerationWithExternalFramework() 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") + 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.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) - 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) + 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, 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)") - } + 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 + } + + 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 { @@ -322,18 +311,18 @@ 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. - 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 @@ -341,101 +330,118 @@ 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, - 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) 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, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("E.swiftmodule")): + } else if pathMatchesSwiftModule(path: outputFilePath, "E") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("E"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("G.swiftmodule")): + } else if pathMatchesSwiftModule(path: outputFilePath, "G") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("G"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("Swift.swiftmodule")): + } else if pathMatchesSwiftModule(path: outputFilePath, "Swift") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("Swift"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("_Concurrency.swiftmodule")): + } else if pathMatchesSwiftModule(path: outputFilePath, "_Concurrency") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("_Concurrency"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, 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, + dependencyGraph: dependencyGraph, 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"), + dependencyGraph: dependencyGraph, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "B", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("B"), + dependencyGraph: dependencyGraph, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "C", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("C"), + dependencyGraph: dependencyGraph, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "G", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("G"), + dependencyGraph: dependencyGraph, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "G", with: pcmArgs9, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("G"), + 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"), + dependencyGraph: dependencyGraph, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "X", with: pcmArgs9, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("X"), + dependencyGraph: dependencyGraph, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs9, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("SwiftShims"), + dependencyGraph: dependencyGraph, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs15, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs15, moduleId: .clang("SwiftShims"), + dependencyGraph: dependencyGraph, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("SwiftShims"), + dependencyGraph: dependencyGraph, + 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)") + } } } } @@ -453,12 +459,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() @@ -471,9 +478,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 @@ -481,64 +487,87 @@ 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, - 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) - 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, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("Swift.swiftmodule")): + } else if pathMatchesSwiftModule(path: outputFilePath, "Swift") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("Swift"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, pcmFileEncoder: pcmFileEncoder) - case .relative(RelativePath("_Concurrency.swiftmodule")): + } else if pathMatchesSwiftModule(path: outputFilePath, "_Concurrency") { try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .swift("_Concurrency"), - dependencyOracle: dependencyOracle, + dependencyGraph: dependencyGraph, 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, + dependencyGraph: dependencyGraph, 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"), + dependencyGraph: dependencyGraph, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "B", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("B"), + dependencyGraph: dependencyGraph, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "C", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("C"), + dependencyGraph: dependencyGraph, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs9, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs9, moduleId: .clang("SwiftShims"), + dependencyGraph: dependencyGraph, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgs15, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgs15, moduleId: .clang("SwiftShims"), + dependencyGraph: dependencyGraph, + pcmFileEncoder: pcmFileEncoder) + case .relative(pcmArgsEncodedRelativeModulePath(for: "SwiftShims", with: pcmArgsCurrent, + pcmModuleNameEncoder: pcmModuleNameEncoder)): + try checkExplicitModuleBuildJob(job: job, pcmArgs: pcmArgsCurrent, moduleId: .clang("SwiftShims"), + dependencyGraph: dependencyGraph, + 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)") + } } } } @@ -648,13 +677,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, @@ -696,12 +726,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() @@ -748,30 +779,64 @@ 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"))) + + /// 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 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] + sdkArgumentsForTesting + + 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 testExplicitSwiftModuleMap() throws { 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 { """ { diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index 518e12bc4..4ba275f9c 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] @@ -2561,7 +2561,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 +2577,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 +3359,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 +3487,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 +3795,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 +4590,7 @@ final class SwiftDriverTests: XCTestCase { XCTAssert(jobs.count == 1) XCTAssertEqual(jobs.first!.tool.name, "/usr/bin/nonexistent-swift-help") } - + func testSourceInfoFileEmitOption() throws { // implicit do {