diff --git a/Sources/SwiftDriver/CMakeLists.txt b/Sources/SwiftDriver/CMakeLists.txt index 7cb21771c..37caf97a7 100644 --- a/Sources/SwiftDriver/CMakeLists.txt +++ b/Sources/SwiftDriver/CMakeLists.txt @@ -85,7 +85,6 @@ add_library(SwiftDriver Jobs/InterpretJob.swift Jobs/Job.swift Jobs/LinkJob.swift - Jobs/MergeModuleJob.swift Jobs/ModuleWrapJob.swift Jobs/Planning.swift Jobs/PrintTargetInfoJob.swift diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index db11ab3cc..9dfd37180 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -59,6 +59,7 @@ public struct Driver { case cannotSpecify_OForMultipleOutputs case conflictingOptions(Option, Option) case unableToLoadOutputFileMap(String, String) + case malformedChainedBridgingHeader(String) case unableToDecodeFrontendTargetInfo(String?, [String], String) case failedToRetrieveFrontendTargetInfo case failedToRunFrontendToRetrieveTargetInfo(Int, String?) @@ -111,6 +112,8 @@ public struct Driver { return "failed to retrieve frontend target info" case .unableToReadFrontendTargetInfo: return "could not read frontend target info" + case .malformedChainedBridgingHeader(let content): + return "could not convert bridging header content to utf-8: \(content)" case let .failedToRunFrontendToRetrieveTargetInfo(returnCode, stderr): return "frontend job retrieving target info failed with code \(returnCode)" + (stderr.map {": \($0)"} ?? "") @@ -330,7 +333,7 @@ public struct Driver { case .relative(let path): let cwd = workingDirectory ?? fileSystem.currentWorkingDirectory return cwd.map { AbsolutePath($0, path) } - case .standardInput, .standardOutput, .temporary, .temporaryWithKnownContents, .fileList: + case .standardInput, .standardOutput, .temporary, .temporaryWithKnownContents, .fileList, .buildArtifactWithKnownContents: fatalError("Frontend target information will never include a path of this type.") } } @@ -341,38 +344,15 @@ public struct Driver { /// Original ObjC Header passed from command-line let originalObjCHeaderFile: VirtualPath.Handle? - /// Enable bridging header chaining. let bridgingHeaderChaining: Bool - /// The path to the imported Objective-C header. - lazy var importedObjCHeader: VirtualPath.Handle? = { - assert(explicitDependencyBuildPlanner != nil || - !parsedOptions.hasArgument(.driverExplicitModuleBuild) || - !inputFiles.contains { $0.type == .swift }, - "should not be queried before scanning") - let chainedBridgingHeader = try? explicitDependencyBuildPlanner?.getChainedBridgingHeaderFile() - return try? computeImportedObjCHeader(&parsedOptions, compilerMode: compilerMode, - chainedBridgingHeader: chainedBridgingHeader) ?? originalObjCHeaderFile - }() - /// The directory to emit PCH file. lazy var bridgingPrecompiledHeaderOutputDir: VirtualPath? = { return try? computePrecompiledBridgingHeaderDir(&parsedOptions, compilerMode: compilerMode) }() - /// The path to the pch for the imported Objective-C header. - lazy var bridgingPrecompiledHeader: VirtualPath.Handle? = { - let contextHash = try? explicitDependencyBuildPlanner?.getMainModuleContextHash() - return computeBridgingPrecompiledHeader(&parsedOptions, - compilerMode: compilerMode, - importedObjCHeader: importedObjCHeader, - outputFileMap: outputFileMap, - outputDirectory: bridgingPrecompiledHeaderOutputDir, - contextHash: contextHash) - }() - /// Path to the dependencies file. let dependenciesFilePath: VirtualPath.Handle? @@ -621,14 +601,6 @@ public struct Driver { /// The mode the API digester should run in. let digesterMode: DigesterMode - // FIXME: We should soon be able to remove this from being in the Driver's state. - // Its only remaining use outside of actual dependency build planning is in - // command-line input option generation for the explicit main module compile job. - /// Planner for constructing module build jobs using Explicit Module Builds. - /// Constructed during the planning phase only when all module dependencies will be prebuilt and treated - /// as explicit inputs by the various compilation jobs. - @_spi(Testing) public var explicitDependencyBuildPlanner: ExplicitDependencyBuildPlanner? = nil - /// A reference to the instance of libSwiftScan which is shared with the driver's /// `InterModuleDependencyOracle`, but also used for non-scanning tasks, such as target info /// and supported compiler feature queries @@ -1365,9 +1337,9 @@ public struct Driver { } public mutating func planBuild() throws -> [Job] { - let (jobs, incrementalCompilationState, intermoduleDependencyGraph) = try planPossiblyIncrementalBuild() + let (jobs, incrementalCompilationState, explicitModulePlanner) = try planPossiblyIncrementalBuild() self.incrementalCompilationState = incrementalCompilationState - self.intermoduleDependencyGraph = intermoduleDependencyGraph + self.intermoduleDependencyGraph = explicitModulePlanner?.dependencyGraph return jobs } } @@ -1756,11 +1728,9 @@ extension Diagnostic.Message { } extension Driver { - func explainModuleDependency(_ explainModuleName: String, allPaths: Bool) throws { - guard let dependencyPlanner = explicitDependencyBuildPlanner else { - fatalError("Cannot explain dependency without Explicit Build Planner") - } - guard let dependencyPaths = try dependencyPlanner.explainDependency(explainModuleName, allPaths: allPaths) else { + func explainModuleDependency(_ explainModuleName: String, allPaths: Bool, + moduleDependencyGraph: InterModuleDependencyGraph) throws { + guard let dependencyPaths = try moduleDependencyGraph.explainDependency(explainModuleName, allPaths: allPaths) else { diagnosticEngine.emit(.remark("No such module dependency found: '\(explainModuleName)'")) return } @@ -1832,13 +1802,6 @@ extension Driver { return } - // If we're only supposed to explain a dependency on a given module, do so now. - if let explainModuleName = parsedOptions.getLastArgument(.explainModuleDependencyDetailed) { - try explainModuleDependency(explainModuleName.asSingle, allPaths: true) - } else if let explainModuleNameDetailed = parsedOptions.getLastArgument(.explainModuleDependency) { - try explainModuleDependency(explainModuleNameDetailed.asSingle, allPaths: false) - } - if parsedOptions.contains(.driverPrintOutputFileMap) { if let outputFileMap = self.outputFileMap { stderrStream.send(outputFileMap.description) @@ -2031,7 +1994,7 @@ extension Driver { // Put bridging header as first input if we have it let allInputs: [TypedVirtualPath] - if let objcHeader = importedObjCHeader, bridgingPrecompiledHeader != nil { + if let objcHeader = originalObjCHeaderFile { allInputs = [TypedVirtualPath(file: objcHeader, type: .objcHeader)] + inputFiles } else { allInputs = inputFiles @@ -2056,10 +2019,7 @@ extension Driver { // All input action IDs for this action. var inputIds = [UInt]() - var jobInputs = job.primaryInputs.isEmpty ? job.inputs : job.primaryInputs - if let pchPath = bridgingPrecompiledHeader, job.kind == .compile { - jobInputs.append(TypedVirtualPath(file: pchPath, type: .pch)) - } + let jobInputs = job.primaryInputs.isEmpty ? job.inputs : job.primaryInputs // Collect input job IDs. for input in jobInputs { if let id = inputIdMap[input] { @@ -3130,15 +3090,14 @@ extension Driver { chainedBridgingHeader: ChainedBridgingHeaderFile?) throws -> VirtualPath.Handle? { // handle chained bridging header. if let chainedHeader = chainedBridgingHeader, !chainedHeader.path.isEmpty { - let path = try VirtualPath(path: chainedHeader.path) - let dirExists = try fileSystem.exists(path.parentDirectory) - if !dirExists, let dirToCreate = path.parentDirectory.absolutePath { - try fileSystem.createDirectory(dirToCreate, recursive: true) - } - try fileSystem.writeFileContents(path, - bytes: ByteString(encodingAsUTF8: chainedHeader.content), - atomically: true) - return path.intern() + let path = try AbsolutePath(validating: chainedHeader.path) + if !fileSystem.exists(path.parentDirectory) { + try fileSystem.createDirectory(path.parentDirectory, recursive: true) + } + guard let headerData = chainedHeader.content.data(using: .utf8) else { + throw Driver.Error.malformedChainedBridgingHeader(chainedHeader.content) + } + return try VirtualPath.createBuildProductFileWithKnownContents(path, headerData).intern() } return originalObjCHeaderFile } diff --git a/Sources/SwiftDriver/Execution/ArgsResolver.swift b/Sources/SwiftDriver/Execution/ArgsResolver.swift index 5c84b3c2f..50564f67c 100644 --- a/Sources/SwiftDriver/Execution/ArgsResolver.swift +++ b/Sources/SwiftDriver/Execution/ArgsResolver.swift @@ -134,7 +134,7 @@ public final class ArgsResolver { try createFileList(path: actualPath, contents: items) case let .fileList(_, .outputFileMap(map)): try createFileList(path: actualPath, outputFileMap: map) - case .relative, .absolute, .standardInput, .standardOutput: + case .relative, .absolute, .standardInput, .standardOutput, .buildArtifactWithKnownContents: fatalError("Not a temporary path.") } @@ -143,6 +143,12 @@ public final class ArgsResolver { return result } + // First time resolving a build product with a known path and driver-computed + // contents, write it to the filesystem. + if case let .buildArtifactWithKnownContents(absolutePath, contents) = path { + try fileSystem.writeFileContents(absolutePath, bytes: .init(contents)) + } + // Otherwise, return the path. let result = path.name pathMapping[path] = result diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift index 42c3617e4..4f8fa7399 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift @@ -12,7 +12,7 @@ import struct TSCBasic.SHA256 import struct TSCBasic.AbsolutePath - +import protocol TSCBasic.FileSystem import struct Foundation.Data import class Foundation.JSONEncoder @@ -52,7 +52,6 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT private let integratedDriver: Bool private let mainModuleName: String? private let cas: SwiftScanCAS? - private let swiftScanOracle: InterModuleDependencyOracle private let prefixMap: [(AbsolutePath, AbsolutePath)] /// Clang PCM names contain a hash of the command-line arguments that were used to build them. @@ -60,36 +59,58 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT private var hashedModuleNameCache: [String: String] = [:] /// Does this compile support `.explicitInterfaceModuleBuild` - private var supportsExplicitInterfaceBuild: Bool + private let supportsExplicitInterfaceBuild: Bool /// Cached command-line additions for all main module compile jobs private struct ResolvedModuleDependenciesCommandLineComponents { let inputs: [TypedVirtualPath] let commandLine: [Job.ArgTemplate] } - private var resolvedMainModuleDependenciesArgs: ResolvedModuleDependenciesCommandLineComponents? = nil + + /// Resolved additions to all main source module compile commands + /// specifying explicit module dependency module paths + private let resolvedMainModuleDependenciesArgs: ResolvedModuleDependenciesCommandLineComponents + + /// Resolved additions to the main source module PCH compile command + /// specifying explicit module dependency module paths + private let resolvedPCHModuleDependenciesArgs: ResolvedModuleDependenciesCommandLineComponents + + /// The computed path to a chained bridging header + let chainedBridgingHeaderFile: ChainedBridgingHeaderFile? + + /// Does this compile support resolving bridging header pch command from swiftScan. + let supportsBridgingHeaderPCHCommand: Bool public init(dependencyGraph: InterModuleDependencyGraph, toolchain: Toolchain, - dependencyOracle: InterModuleDependencyOracle, integratedDriver: Bool = true, supportsExplicitInterfaceBuild: Bool = false, cas: SwiftScanCAS? = nil, - prefixMap: [(AbsolutePath, AbsolutePath)] = []) throws { + prefixMap: [(AbsolutePath, AbsolutePath)] = [], + supportsBridgingHeaderPCHCommand: Bool = false) throws { self.dependencyGraph = dependencyGraph self.toolchain = toolchain - self.swiftScanOracle = dependencyOracle self.integratedDriver = integratedDriver self.mainModuleName = dependencyGraph.mainModuleName self.reachabilityMap = try dependencyGraph.computeTransitiveClosure() self.supportsExplicitInterfaceBuild = supportsExplicitInterfaceBuild + self.supportsBridgingHeaderPCHCommand = supportsBridgingHeaderPCHCommand self.cas = cas self.prefixMap = prefixMap - } - - /// Supports resolving bridging header pch command from swiftScan. - public var supportsBridgingHeaderPCHCommand: Bool { - return swiftScanOracle.supportsBridgingHeaderPCHCommand + let mainModuleId: ModuleDependencyId = .swift(dependencyGraph.mainModuleName) + let mainModuleDetails = try dependencyGraph.swiftModuleDetails(of: mainModuleId) + if let path = mainModuleDetails.chainedBridgingHeaderPath, + let content = mainModuleDetails.chainedBridgingHeaderContent { + self.chainedBridgingHeaderFile = ChainedBridgingHeaderFile(path: path, content: content) + } else { + self.chainedBridgingHeaderFile = nil + } + self.resolvedMainModuleDependenciesArgs = try Self.resolveMainModuleDependencies(in: dependencyGraph, + reachabilityMap: reachabilityMap, + cas: cas) + self.resolvedPCHModuleDependenciesArgs = try Self.resolveBridgingHeaderDependencies(in: dependencyGraph, + reachabilityMap: reachabilityMap, + cas: cas) } /// Generate build jobs for all dependencies of the main module. @@ -158,9 +179,12 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT moduleDetails.commandLine?.forEach { commandLine.appendFlags($0) } // Resolve all dependency module inputs for this Swift module - try resolveExplicitModuleDependencies(moduleId: moduleId, - inputs: &inputs, - commandLine: &commandLine) + try Self.resolveExplicitModuleDependencies(moduleId: moduleId, + in: dependencyGraph, + reachabilityMap: reachabilityMap, + cas: cas, + inputs: &inputs, + commandLine: &commandLine) // Build the .swiftinterfaces file using a list of command line options specified in the // `details` field. @@ -230,8 +254,10 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT moduleDetails.commandLine.forEach { commandLine.appendFlags($0) } // Resolve all dependency module inputs for this Clang module - try resolveExplicitModuleDependencies(moduleId: moduleId, inputs: &inputs, - commandLine: &commandLine) + try Self.resolveExplicitModuleDependencies(moduleId: moduleId, in: dependencyGraph, + reachabilityMap: reachabilityMap, + cas: cas, inputs: &inputs, + commandLine: &commandLine) let moduleMapPath = TypedVirtualPath(file: moduleDetails.moduleMapPath.path, type: .clangModuleMap) let modulePCMPath = TypedVirtualPath(file: moduleInfo.modulePath.path, type: .pcm) @@ -269,13 +295,18 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT /// For the specified module, update the given command line flags and inputs /// to use explicitly-built module dependencies. - private mutating func resolveExplicitModuleDependencies(moduleId: ModuleDependencyId, - inputs: inout [TypedVirtualPath], - commandLine: inout [Job.ArgTemplate]) throws { + private static func resolveExplicitModuleDependencies(moduleId: ModuleDependencyId, + in dependencyGraph: InterModuleDependencyGraph, + reachabilityMap: [ModuleDependencyId : Set], + cas: SwiftScanCAS?, + inputs: inout [TypedVirtualPath], + commandLine: inout [Job.ArgTemplate]) throws { // Prohibit the frontend from implicitly building textual modules into binary modules. var swiftDependencyArtifacts: Set = [] var clangDependencyArtifacts: Set = [] - try addModuleDependencies(of: moduleId, + try Self.addModuleDependencies(of: moduleId, + in: dependencyGraph, + reachabilityMap: reachabilityMap, clangDependencyArtifacts: &clangDependencyArtifacts, swiftDependencyArtifacts: &swiftDependencyArtifacts) @@ -296,10 +327,10 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT // SwiftModuleArtifactInfo guard moduleId == .swift(dependencyGraph.mainModuleName) else { return } let dependencyFileContent = - try serializeModuleDependencies(for: moduleId, - swiftDependencyArtifacts: swiftDependencyArtifacts, - clangDependencyArtifacts: clangDependencyArtifacts) - if let cas = self.cas { + try Self.serializeModuleDependencies(for: moduleId, + swiftDependencyArtifacts: swiftDependencyArtifacts, + clangDependencyArtifacts: clangDependencyArtifacts) + if let cas = cas { // When using a CAS, write JSON into CAS and pass the ID on command-line. let casID = try cas.store(data: dependencyFileContent) commandLine.appendFlag("-explicit-swift-module-map-file") @@ -316,11 +347,12 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT } } - private mutating func addModuleDependency(of moduleId: ModuleDependencyId, - dependencyId: ModuleDependencyId, - clangDependencyArtifacts: inout Set, - swiftDependencyArtifacts: inout Set, - bridgingHeaderDeps: Set? = nil + private static func addModuleDependency(of moduleId: ModuleDependencyId, + in dependencyGraph: InterModuleDependencyGraph, + dependencyId: ModuleDependencyId, + clangDependencyArtifacts: inout Set, + swiftDependencyArtifacts: inout Set, + bridgingHeaderDeps: Set? = nil ) throws { switch dependencyId { case .swift: @@ -371,7 +403,10 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT /// Collect the Set of all Clang module dependencies which are dependencies of either /// the `moduleId` bridging header or dependencies of bridging headers /// of any prebuilt binary Swift modules in the dependency graph. - private func collectHeaderModuleDeps(of moduleId: ModuleDependencyId) throws -> Set? { + private static func collectHeaderModuleDeps(of moduleId: ModuleDependencyId, + in dependencyGraph: InterModuleDependencyGraph, + reachabilityMap: [ModuleDependencyId : Set] + ) throws -> Set? { var bridgingHeaderDeps: Set? = nil guard let moduleDependencies = reachabilityMap[moduleId] else { fatalError("Expected reachability information for the module: \(moduleId.moduleName).") @@ -396,18 +431,22 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT /// Add a specific module dependency as an input and a corresponding command /// line flag. - private mutating func addModuleDependencies(of moduleId: ModuleDependencyId, - clangDependencyArtifacts: inout Set, - swiftDependencyArtifacts: inout Set + private static func addModuleDependencies(of moduleId: ModuleDependencyId, + in dependencyGraph: InterModuleDependencyGraph, + reachabilityMap: [ModuleDependencyId : Set], + clangDependencyArtifacts: inout Set, + swiftDependencyArtifacts: inout Set ) throws { guard let moduleDependencies = reachabilityMap[moduleId] else { fatalError("Expected reachability information for the module: \(moduleId.moduleName).") } for dependencyId in moduleDependencies { - try addModuleDependency(of: moduleId, dependencyId: dependencyId, - clangDependencyArtifacts: &clangDependencyArtifacts, - swiftDependencyArtifacts: &swiftDependencyArtifacts, - bridgingHeaderDeps: try collectHeaderModuleDeps(of: moduleId)) + try Self.addModuleDependency(of: moduleId, in: dependencyGraph, dependencyId: dependencyId, + clangDependencyArtifacts: &clangDependencyArtifacts, + swiftDependencyArtifacts: &swiftDependencyArtifacts, + bridgingHeaderDeps: try collectHeaderModuleDeps(of: moduleId, + in: dependencyGraph, + reachabilityMap: reachabilityMap)) } } @@ -434,35 +473,37 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT } } + private static func resolveMainModuleDependencies(in dependencyGraph: InterModuleDependencyGraph, + reachabilityMap: [ModuleDependencyId : Set], + cas: SwiftScanCAS?) throws -> ResolvedModuleDependenciesCommandLineComponents { + var inputAdditions: [TypedVirtualPath] = [] + var commandLineAdditions: [Job.ArgTemplate] = [] + let mainModuleId: ModuleDependencyId = .swift(dependencyGraph.mainModuleName) + let mainModuleDetails = try dependencyGraph.swiftModuleDetails(of: mainModuleId) + if let additionalArgs = mainModuleDetails.commandLine { + additionalArgs.forEach { commandLineAdditions.appendFlag($0) } + } + commandLineAdditions.appendFlags("-disable-implicit-swift-modules", + "-Xcc", "-fno-implicit-modules", + "-Xcc", "-fno-implicit-module-maps") + try Self.resolveExplicitModuleDependencies(moduleId: mainModuleId, + in: dependencyGraph, + reachabilityMap: reachabilityMap, + cas: cas, + inputs: &inputAdditions, + commandLine: &commandLineAdditions) + return ResolvedModuleDependenciesCommandLineComponents( + inputs: inputAdditions, + commandLine: commandLineAdditions + ) + } + /// Resolve all module dependencies of the main module and add them to the lists of /// inputs and command line flags. - public mutating func resolveMainModuleDependencies(inputs: inout [TypedVirtualPath], - commandLine: inout [Job.ArgTemplate]) throws { - // If not previously computed, gather all dependency input files and command-line arguments - if resolvedMainModuleDependenciesArgs == nil { - var inputAdditions: [TypedVirtualPath] = [] - var commandLineAdditions: [Job.ArgTemplate] = [] - let mainModuleId: ModuleDependencyId = .swift(dependencyGraph.mainModuleName) - let mainModuleDetails = try dependencyGraph.swiftModuleDetails(of: mainModuleId) - if let additionalArgs = mainModuleDetails.commandLine { - additionalArgs.forEach { commandLineAdditions.appendFlag($0) } - } - commandLineAdditions.appendFlags("-disable-implicit-swift-modules", - "-Xcc", "-fno-implicit-modules", - "-Xcc", "-fno-implicit-module-maps") - try resolveExplicitModuleDependencies(moduleId: mainModuleId, - inputs: &inputAdditions, - commandLine: &commandLineAdditions) - resolvedMainModuleDependenciesArgs = ResolvedModuleDependenciesCommandLineComponents( - inputs: inputAdditions, - commandLine: commandLineAdditions - ) - } - guard let mainModuleDependenciesArgs = resolvedMainModuleDependenciesArgs else { - fatalError("Failed to compute resolved explicit dependency arguments.") - } - inputs.append(contentsOf: mainModuleDependenciesArgs.inputs) - commandLine.append(contentsOf: mainModuleDependenciesArgs.commandLine) + public func resolveMainModuleDependencies(inputs: inout [TypedVirtualPath], + commandLine: inout [Job.ArgTemplate]) { + inputs.append(contentsOf: resolvedMainModuleDependenciesArgs.inputs) + commandLine.append(contentsOf: resolvedMainModuleDependenciesArgs.commandLine) } /// Get the context hash for the main module. @@ -472,21 +513,14 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT return mainModuleDetails.contextHash } - /// Get the chained bridging header info - public func getChainedBridgingHeaderFile() throws -> ChainedBridgingHeaderFile? { - let mainModuleId: ModuleDependencyId = .swift(dependencyGraph.mainModuleName) - let mainModuleDetails = try dependencyGraph.swiftModuleDetails(of: mainModuleId) - guard let path = mainModuleDetails.chainedBridgingHeaderPath, - let content = mainModuleDetails.chainedBridgingHeaderContent else{ - return nil - } - return ChainedBridgingHeaderFile(path: path, content: content) - } - /// Resolve all module dependencies of the main module and add them to the lists of /// inputs and command line flags. - public mutating func resolveBridgingHeaderDependencies(inputs: inout [TypedVirtualPath], - commandLine: inout [Job.ArgTemplate]) throws { + private static func resolveBridgingHeaderDependencies(in dependencyGraph: InterModuleDependencyGraph, + reachabilityMap: [ModuleDependencyId : Set], + cas: SwiftScanCAS?) throws -> ResolvedModuleDependenciesCommandLineComponents { + var inputAdditions: [TypedVirtualPath] = [] + var commandLineAdditions: [Job.ArgTemplate] = [] + let mainModuleId: ModuleDependencyId = .swift(dependencyGraph.mainModuleName) var swiftDependencyArtifacts: Set = [] var clangDependencyArtifacts: Set = [] @@ -503,12 +537,14 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT continue } addedDependencies.insert(bridgingHeaderDepID) - try addModuleDependency(of: mainModuleId, dependencyId: bridgingHeaderDepID, - clangDependencyArtifacts: &clangDependencyArtifacts, - swiftDependencyArtifacts: &swiftDependencyArtifacts) - try addModuleDependencies(of: bridgingHeaderDepID, - clangDependencyArtifacts: &clangDependencyArtifacts, - swiftDependencyArtifacts: &swiftDependencyArtifacts) + try Self.addModuleDependency(of: mainModuleId, in: dependencyGraph, + dependencyId: bridgingHeaderDepID, + clangDependencyArtifacts: &clangDependencyArtifacts, + swiftDependencyArtifacts: &swiftDependencyArtifacts) + try Self.addModuleDependencies(of: bridgingHeaderDepID, in: dependencyGraph, + reachabilityMap: reachabilityMap, + clangDependencyArtifacts: &clangDependencyArtifacts, + swiftDependencyArtifacts: &swiftDependencyArtifacts) let depInfo = try dependencyGraph.moduleInfo(of: bridgingHeaderDepID) dependenciesWorklist.append(contentsOf: depInfo.allDependencies) } @@ -518,39 +554,55 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT let clangModulePath = TypedVirtualPath(file: moduleArtifactInfo.clangModulePath.path, type: .pcm) - inputs.append(clangModulePath) + inputAdditions.append(clangModulePath) } // Return if depscanner provided build commands. if let scannerPCHArgs = mainModuleDetails.bridgingPchCommandLine { - scannerPCHArgs.forEach { commandLine.appendFlag($0) } - return + scannerPCHArgs.forEach { commandLineAdditions.appendFlag($0) } + return ResolvedModuleDependenciesCommandLineComponents( + inputs: inputAdditions, + commandLine: commandLineAdditions + ) } assert(cas == nil, "Caching build should always return command-line from scanner") // Prohibit the frontend from implicitly building textual modules into binary modules. - commandLine.appendFlags("-disable-implicit-swift-modules", - "-Xcc", "-fno-implicit-modules", - "-Xcc", "-fno-implicit-module-maps") + commandLineAdditions.appendFlags("-disable-implicit-swift-modules", + "-Xcc", "-fno-implicit-modules", + "-Xcc", "-fno-implicit-module-maps") let dependencyFileContent = - try serializeModuleDependencies(for: mainModuleId, - swiftDependencyArtifacts: swiftDependencyArtifacts, - clangDependencyArtifacts: clangDependencyArtifacts) + try Self.serializeModuleDependencies(for: mainModuleId, + swiftDependencyArtifacts: swiftDependencyArtifacts, + clangDependencyArtifacts: clangDependencyArtifacts) let dependencyFile = try VirtualPath.createUniqueTemporaryFileWithKnownContents(.init(validating: "\(mainModuleId.moduleName)-dependencies.json"), dependencyFileContent) - commandLine.appendFlag("-explicit-swift-module-map-file") - commandLine.appendPath(dependencyFile) - inputs.append(TypedVirtualPath(file: dependencyFile.intern(), + commandLineAdditions.appendFlag("-explicit-swift-module-map-file") + commandLineAdditions.appendPath(dependencyFile) + inputAdditions.append(TypedVirtualPath(file: dependencyFile.intern(), type: .jsonSwiftArtifacts)) + + return ResolvedModuleDependenciesCommandLineComponents( + inputs: inputAdditions, + commandLine: commandLineAdditions + ) + } + + /// Resolve all module dependencies of the main module and add them to the lists of + /// inputs and command line flags. + public func resolveBridgingHeaderDependencies(inputs: inout [TypedVirtualPath], + commandLine: inout [Job.ArgTemplate]) { + inputs.append(contentsOf: resolvedPCHModuleDependenciesArgs.inputs) + commandLine.append(contentsOf: resolvedPCHModuleDependenciesArgs.commandLine) } /// Serialize the output file artifacts for a given module in JSON format. - private func serializeModuleDependencies(for moduleId: ModuleDependencyId, - swiftDependencyArtifacts: Set, - clangDependencyArtifacts: Set + private static func serializeModuleDependencies(for moduleId: ModuleDependencyId, + swiftDependencyArtifacts: Set, + clangDependencyArtifacts: Set ) throws -> Data { // The module dependency map in CAS needs to be stable. // Sort the dependencies by name. @@ -600,10 +652,6 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT } internal extension ExplicitDependencyBuildPlanner { - func explainDependency(_ dependencyModuleName: String, allPaths: Bool) throws -> [[ModuleDependencyId]]? { - return try dependencyGraph.explainDependency(dependencyModuleName: dependencyModuleName, allPaths: allPaths) - } - func findPath(from source: ModuleDependencyId, to destination: ModuleDependencyId) throws -> [ModuleDependencyId]? { guard dependencyGraph.modules.contains(where: { $0.key == destination }) else { return nil } var result: [ModuleDependencyId]? = nil @@ -616,6 +664,15 @@ internal extension ExplicitDependencyBuildPlanner { } } +internal extension ExplicitDependencyBuildPlanner { + func filterMandatoryModuleDependencyCompileJobs(_ allJobs: [Job], + fileSystem: FileSystem, + cas: SwiftScanCAS?, + reporter: IncrementalCompilationState.Reporter? = nil) throws -> [Job] { + return try dependencyGraph.filterMandatoryModuleDependencyCompileJobs(allJobs, fileSystem: fileSystem, cas: cas, reporter: reporter) + } +} + // InterModuleDependencyGraph printing, useful for debugging internal extension InterModuleDependencyGraph { func prettyPrintString() throws -> String { diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift index 9ea3b5e1b..37752ff97 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift @@ -13,49 +13,6 @@ import func TSCBasic.topologicalSort import protocol TSCBasic.FileSystem -@_spi(Testing) public extension InterModuleDependencyGraph { - /// 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 externalTargetModuleDetailsMap: ExternalTargetModuleDetailsMap) - throws { - for (externalModuleId, externalModuleDetails) in externalTargetModuleDetailsMap { - let externalModulePath = externalModuleDetails.path - // Replace the occurrence 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) - let prebuiltModuleId: ModuleDependencyId = .swiftPrebuiltExternal(externalModuleId.moduleName) - if let currentInfo = modules[swiftModuleId], - externalModuleId.moduleName != mainModuleName { - let newExternalModuleDetails = - 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, - linkLibraries: currentInfo.linkLibraries, - details: .swiftPrebuiltExternal(newExternalModuleDetails)) - Self.replaceModule(originalId: swiftModuleId, replacementId: prebuiltModuleId, - replacementInfo: newInfo, in: &modules) - } else if let currentPrebuiltInfo = modules[prebuiltModuleId] { - // Just update the isFramework bit on this prebuilt module dependency - let newExternalModuleDetails = - SwiftPrebuiltExternalModuleDetails(compiledModulePath: - TextualVirtualPath(path: VirtualPath.absolute(externalModulePath).intern()), - isFramework: externalModuleDetails.isFramework) - let newInfo = ModuleInfo(modulePath: TextualVirtualPath(path: VirtualPath.absolute(externalModulePath).intern()), - sourceFiles: [], - directDependencies: currentPrebuiltInfo.directDependencies, - linkLibraries: currentPrebuiltInfo.linkLibraries, - details: .swiftPrebuiltExternal(newExternalModuleDetails)) - Self.replaceModule(originalId: prebuiltModuleId, replacementId: prebuiltModuleId, - replacementInfo: newInfo, in: &modules) - } - } - } -} - extension InterModuleDependencyGraph { var topologicalSorting: [ModuleDependencyId] { get throws { @@ -94,38 +51,6 @@ extension InterModuleDependencyGraph { } } -@_spi(Testing) public extension InterModuleDependencyGraph { - - /// Replace an existing module in the moduleInfoMap - static func replaceModule(originalId: ModuleDependencyId, replacementId: ModuleDependencyId, - replacementInfo: ModuleInfo, - in moduleInfoMap: inout ModuleInfoMap) { - precondition(moduleInfoMap[originalId] != nil) - moduleInfoMap.removeValue(forKey: originalId) - moduleInfoMap[replacementId] = replacementInfo - if originalId != replacementId { - updateDependencies(from: originalId, to: replacementId, in: &moduleInfoMap) - } - } - - /// Replace all references to the original module in other externalModules' dependencies with the new module. - static func updateDependencies(from originalId: ModuleDependencyId, - to replacementId: ModuleDependencyId, - in moduleInfoMap: inout ModuleInfoMap) { - for moduleId in moduleInfoMap.keys { - var moduleInfo = moduleInfoMap[moduleId]! - // Skip over placeholders, they do not have dependencies - if case .swiftPlaceholder(_) = moduleId { - continue - } - if let originalModuleIndex = moduleInfo.directDependencies?.firstIndex(of: originalId) { - moduleInfo.directDependencies![originalModuleIndex] = replacementId; - moduleInfoMap[moduleId] = moduleInfo - } - } - } -} - /// Incremental Build Machinery internal extension InterModuleDependencyGraph { /// We must determine if any of the module dependencies require re-compilation @@ -350,7 +275,7 @@ internal extension InterModuleDependencyGraph { } internal extension InterModuleDependencyGraph { - func explainDependency(dependencyModuleName: String, allPaths: Bool) throws -> [[ModuleDependencyId]]? { + func explainDependency(_ dependencyModuleName: String, allPaths: Bool) throws -> [[ModuleDependencyId]]? { guard modules.contains(where: { $0.key.moduleName == dependencyModuleName }) else { return nil } var result: Set<[ModuleDependencyId]> = [] if allPaths { diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift index 85b9b04c2..70218250e 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift @@ -337,10 +337,10 @@ public extension ModuleInfo { /// all of the Swift and C modules and source files it depends on. public struct InterModuleDependencyGraph: Codable { /// The name of the main module. - public var mainModuleName: String + public let mainModuleName: String /// The complete set of modules discovered - public var modules: ModuleInfoMap = [:] + public let modules: ModuleInfoMap /// Information about the main module. public var mainModule: ModuleInfo { modules[.swift(mainModuleName)]! } diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift index d6f488f62..cc128102b 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift @@ -50,27 +50,15 @@ extension Diagnostic.Message { @_spi(Testing) public extension Driver { /// Scan the current module's input source-files to compute its direct and transitive /// module dependencies. - mutating func gatherModuleDependencies() + mutating func scanModuleDependencies(forVariantModule: Bool = false) throws -> InterModuleDependencyGraph { - var dependencyGraph = try performDependencyScan() + let dependencyGraph = try performDependencyScan(forVariantModule: forVariantModule) if parsedOptions.hasArgument(.printPreprocessedExplicitDependencyGraph) { try stdoutStream.send(dependencyGraph.toJSONString()) stdoutStream.flush() } - 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. - // This is currently disabled because we are investigating it being unnecessary - // try resolveVersionedClangDependencies(dependencyGraph: &dependencyGraph) - - // Set dependency modules' paths to be saved in the module cache. - // try resolveDependencyModulePaths(dependencyGraph: &dependencyGraph) - if parsedOptions.hasArgument(.printExplicitDependencyGraph) { let outputFormat = parsedOptions.getLastArgument(.explicitDependencyGraphFormat)?.asSingle if outputFormat == nil || outputFormat == "json" { @@ -81,6 +69,13 @@ extension Diagnostic.Message { stdoutStream.flush() } + // If we're only supposed to explain a dependency on a given module, do so now. + if let explainModuleName = parsedOptions.getLastArgument(.explainModuleDependencyDetailed) { + try explainModuleDependency(explainModuleName.asSingle, allPaths: true, moduleDependencyGraph: dependencyGraph) + } else if let explainModuleNameDetailed = parsedOptions.getLastArgument(.explainModuleDependency) { + try explainModuleDependency(explainModuleNameDetailed.asSingle, allPaths: false, moduleDependencyGraph: dependencyGraph) + } + return dependencyGraph } } @@ -89,8 +84,8 @@ public extension Driver { /// Precompute the dependencies for a given Swift compilation, producing a /// dependency graph including all Swift and C module files and /// source files. - mutating func dependencyScanningJob() throws -> Job { - let (inputs, commandLine) = try dependencyScannerInvocationCommand() + mutating func dependencyScanningJob(forVariantModule: Bool = false) throws -> Job { + let (inputs, commandLine) = try dependencyScannerInvocationCommand(forVariantModule: forVariantModule) // Construct the scanning job. return Job(moduleName: moduleOutputInfo.name, @@ -105,7 +100,7 @@ public extension Driver { /// Generate a full command-line invocation to be used for the dependency scanning action /// on the target module. - @_spi(Testing) mutating func dependencyScannerInvocationCommand() + @_spi(Testing) mutating func dependencyScannerInvocationCommand(forVariantModule: Bool = false) throws -> ([TypedVirtualPath],[Job.ArgTemplate]) { // Aggregate the fast dependency scanner arguments var inputs: [TypedVirtualPath] = [] @@ -114,7 +109,8 @@ public extension Driver { commandLine.appendFlag("-scan-dependencies") try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .scanDependencies, bridgingHeaderHandling: .parsed, - moduleDependencyGraphUse: .dependencyScan) + explicitModulePlanner: nil, + forVariantEmitModule: forVariantModule) try addRuntimeLibraryFlags(commandLine: &commandLine) // Pass in external target dependencies to be treated as placeholder dependencies by the scanner @@ -304,8 +300,8 @@ public extension Driver { } } - mutating func performDependencyScan() throws -> InterModuleDependencyGraph { - let scannerJob = try dependencyScanningJob() + mutating func performDependencyScan(forVariantModule: Bool = false) throws -> InterModuleDependencyGraph { + let scannerJob = try dependencyScanningJob(forVariantModule: forVariantModule) let forceResponseFiles = parsedOptions.hasArgument(.driverForceResponseFiles) let dependencyGraph: InterModuleDependencyGraph @@ -358,7 +354,7 @@ public extension Driver { commandLine.appendFlag("-import-prescan") try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .scanDependencies, bridgingHeaderHandling: .parsed, - moduleDependencyGraphUse: .dependencyScan) + explicitModulePlanner: nil) try addRuntimeLibraryFlags(commandLine: &commandLine) // Pass on the input files diff --git a/Sources/SwiftDriver/IncrementalCompilation/FirstWaveComputer.swift b/Sources/SwiftDriver/IncrementalCompilation/FirstWaveComputer.swift index 770cde663..bef951f22 100644 --- a/Sources/SwiftDriver/IncrementalCompilation/FirstWaveComputer.swift +++ b/Sources/SwiftDriver/IncrementalCompilation/FirstWaveComputer.swift @@ -25,7 +25,7 @@ extension IncrementalCompilationState { let fileSystem: FileSystem let showJobLifecycle: Bool let alwaysRebuildDependents: Bool - let interModuleDependencyGraph: InterModuleDependencyGraph? + let explicitModulePlanner: ExplicitDependencyBuildPlanner? /// If non-null outputs information for `-driver-show-incremental` for input path private let reporter: Reporter? @@ -33,7 +33,7 @@ extension IncrementalCompilationState { initialState: IncrementalCompilationState.InitialStateForPlanning, jobsInPhases: JobsInPhases, driver: Driver, - interModuleDependencyGraph: InterModuleDependencyGraph?, + explicitModulePlanner: ExplicitDependencyBuildPlanner?, reporter: Reporter? ) { self.moduleDependencyGraph = initialState.graph @@ -45,7 +45,7 @@ extension IncrementalCompilationState { self.showJobLifecycle = driver.showJobLifecycle self.alwaysRebuildDependents = initialState.incrementalOptions.contains( .alwaysRebuildDependents) - self.interModuleDependencyGraph = interModuleDependencyGraph + self.explicitModulePlanner = explicitModulePlanner self.reporter = reporter } @@ -102,7 +102,8 @@ extension IncrementalCompilationState.FirstWaveComputer { batchJobFormer.formBatchedJobs( mandatoryCompileJobsInOrder, showJobLifecycle: showJobLifecycle, - jobCreatingPch: jobCreatingPch) + jobCreatingPch: jobCreatingPch, + explicitModulePlanner: explicitModulePlanner) moduleDependencyGraph.setPhase(to: .buildingAfterEachCompilation) return (initiallySkippedCompileJobs: [:], @@ -131,7 +132,8 @@ extension IncrementalCompilationState.FirstWaveComputer { let batchedCompilationJobs = try batchJobFormer.formBatchedJobs( mandatoryCompileJobsInOrder, showJobLifecycle: showJobLifecycle, - jobCreatingPch: jobCreatingPch) + jobCreatingPch: jobCreatingPch, + explicitModulePlanner: explicitModulePlanner) // In the case where there are no compilation jobs to run on this build (no source-files were changed), // we can skip running `beforeCompiles` jobs if we also ensure that none of the `afterCompiles` jobs diff --git a/Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationProtectedState.swift b/Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationProtectedState.swift index 90d482292..4f07bca2e 100644 --- a/Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationProtectedState.swift +++ b/Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationProtectedState.swift @@ -34,18 +34,24 @@ extension IncrementalCompilationState { /// fileprivate in order to control concurrency. fileprivate let moduleDependencyGraph: ModuleDependencyGraph + /// The explicit dependency planner used for formulating compilation + /// commands on re-bached compile tasks. + fileprivate let explicitModulePlanner: ExplicitDependencyBuildPlanner? + fileprivate let jobCreatingPch: Job? fileprivate let reporter: Reporter? init(skippedCompileJobs: [TypedVirtualPath: Job], _ moduleDependencyGraph: ModuleDependencyGraph, _ jobCreatingPch: Job?, - _ driver: inout Driver) { + _ driver: inout Driver, + _ explicitModulePlanner: ExplicitDependencyBuildPlanner?) { self.skippedCompileJobs = skippedCompileJobs self.moduleDependencyGraph = moduleDependencyGraph self.reporter = moduleDependencyGraph.info.reporter self.jobCreatingPch = jobCreatingPch self.driver = driver + self.explicitModulePlanner = explicitModulePlanner } } } @@ -64,7 +70,10 @@ extension IncrementalCompilationState.ProtectedState { mutationSafetyPrecondition() // batch in here to protect the Driver from concurrent access return try collectUnbatchedJobsDiscoveredToBeNeededAfterFinishing(job: finishedJob) - .map {try driver.formBatchedJobs($0, showJobLifecycle: driver.showJobLifecycle, jobCreatingPch: jobCreatingPch)} + .map {try driver.formBatchedJobs($0, + showJobLifecycle: driver.showJobLifecycle, + jobCreatingPch: jobCreatingPch, + explicitModulePlanner: explicitModulePlanner)} } /// Remember a job (group) that is before a compile or a compile itself. diff --git a/Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState+Extensions.swift b/Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState+Extensions.swift index 8ee2b86c6..d8cecc457 100644 --- a/Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState+Extensions.swift +++ b/Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState+Extensions.swift @@ -157,7 +157,7 @@ extension IncrementalCompilationState { extension IncrementalCompilationState { /// Only used when no compilations have run; otherwise the caller assumes every post-compile /// job is needed, and saves the cost of the filesystem accesses by not calling this function. - /// (For instance, if a build is cancelled in the merge-module phase, the compilations may be up-to-date + /// (For instance, if a build is cancelled, the compilations may be up-to-date /// but the postcompile-jobs (e.g. link-edit) may still need to be run. /// Since the use-case is rare, this function can afford to be expensive. /// Unlike the check in `IncrementalStateComputer.computeChangedInputs`, diff --git a/Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState.swift b/Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState.swift index 25b08290a..e80764753 100644 --- a/Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState.swift +++ b/Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState.swift @@ -49,15 +49,13 @@ public final class IncrementalCompilationState { public let info: IncrementalCompilationState.IncrementalDependencyAndInputSetup - internal let upToDateInterModuleDependencyGraph: InterModuleDependencyGraph? - // MARK: - Creating IncrementalCompilationState /// Return nil if not compiling incrementally internal init( driver: inout Driver, jobsInPhases: JobsInPhases, initialState: InitialStateForPlanning, - interModuleDepGraph: InterModuleDependencyGraph? + explicitModulePlanner: ExplicitDependencyBuildPlanner? ) throws { let reporter = initialState.incrementalOptions.contains(.showIncremental) ? Reporter(diagnosticEngine: driver.diagnosticEngine, @@ -68,17 +66,18 @@ public final class IncrementalCompilationState { initialState: initialState, jobsInPhases: jobsInPhases, driver: driver, - interModuleDependencyGraph: interModuleDepGraph, + explicitModulePlanner: explicitModulePlanner, reporter: reporter) .compute(batchJobFormer: &driver) self.info = initialState.graph.info - self.upToDateInterModuleDependencyGraph = interModuleDepGraph + self.protectedState = ProtectedState( skippedCompileJobs: firstWave.initiallySkippedCompileJobs, initialState.graph, jobsInPhases.allJobs.first(where: {$0.kind == .generatePCH}), - &driver) + &driver, + explicitModulePlanner) self.mandatoryJobsInOrder = firstWave.mandatoryJobsInOrder self.jobsAfterCompiles = firstWave.jobsAfterCompiles self.skippedJobsNonCompile = firstWave.skippedNonCompileJobs diff --git a/Sources/SwiftDriver/Jobs/CompileJob.swift b/Sources/SwiftDriver/Jobs/CompileJob.swift index 1b6cfb171..f61dc67d9 100644 --- a/Sources/SwiftDriver/Jobs/CompileJob.swift +++ b/Sources/SwiftDriver/Jobs/CompileJob.swift @@ -232,7 +232,8 @@ extension Driver { addJobOutputs: ([TypedVirtualPath]) -> Void, pchCompileJob: Job?, emitModuleTrace: Bool, - produceCacheKey: Bool) + produceCacheKey: Bool, + explicitModulePlanner: ExplicitDependencyBuildPlanner?) throws -> Job { var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) } var inputs: [TypedVirtualPath] = [] @@ -292,7 +293,8 @@ extension Driver { commandLine.appendFlag(.disableObjcAttrRequiresFoundationModule) } - try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .compile) + try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .compile, + explicitModulePlanner: explicitModulePlanner) try addRuntimeLibraryFlags(commandLine: &commandLine) if Driver.canDoCrossModuleOptimization(parsedOptions: &parsedOptions) && @@ -389,7 +391,8 @@ extension Driver { addJobOutputs(outputs) // Bridging header is needed for compiling these .swift sources. - if let pchPath = bridgingPrecompiledHeader { + let (_, precompiledObjCHeader) = try computeCanonicalObjCHeader(explicitModulePlanner: explicitModulePlanner) + if let pchPath = precompiledObjCHeader { let pchInput = TypedVirtualPath(file: pchPath, type: .pch) inputs.append(pchInput) } diff --git a/Sources/SwiftDriver/Jobs/EmitModuleJob.swift b/Sources/SwiftDriver/Jobs/EmitModuleJob.swift index 0a8fde6db..6de179a08 100644 --- a/Sources/SwiftDriver/Jobs/EmitModuleJob.swift +++ b/Sources/SwiftDriver/Jobs/EmitModuleJob.swift @@ -17,8 +17,7 @@ extension Driver { mutating func addCommonModuleOptions( commandLine: inout [Job.ArgTemplate], outputs: inout [TypedVirtualPath], - moduleOutputPaths: SupplementalModuleTargetOutputPaths, - isMergeModule: Bool + moduleOutputPaths: SupplementalModuleTargetOutputPaths ) throws { // Add supplemental outputs. func addSupplementalOutput(path: VirtualPath.Handle?, flag: String, type: FileType) { @@ -39,13 +38,7 @@ extension Driver { addSupplementalOutput(path: objcGeneratedHeaderPath, flag: "-emit-objc-header-path", type: .objcHeader) addSupplementalOutput(path: tbdPath, flag: "-emit-tbd-path", type: .tbd) addSupplementalOutput(path: moduleOutputPaths.apiDescriptorFilePath, flag: "-emit-api-descriptor-path", type: .jsonAPIDescriptor) - - if isMergeModule { - return - } - addSupplementalOutput(path: emitModuleSerializedDiagnosticsFilePath, flag: "-serialize-diagnostics-path", type: .emitModuleDiagnostics) - addSupplementalOutput(path: emitModuleDependenciesFilePath, flag: "-emit-dependencies-path", type: .emitModuleDependencies) // Skip files created by other jobs when emitting a module and building at the same time @@ -79,13 +72,15 @@ extension Driver { } /// Form a job that emits a single module - @_spi(Testing) public mutating func emitModuleJob(pchCompileJob: Job?, isVariantJob: Bool = false) throws -> Job { - precondition(!isVariantJob || (isVariantJob && + @_spi(Testing) public mutating func emitModuleJob(pchCompileJob: Job?, + explicitModulePlanner: ExplicitDependencyBuildPlanner?, + forVariantModule: Bool = false) throws -> Job { + precondition(!forVariantModule || (forVariantModule && variantModuleOutputInfo != nil && variantModuleOutputPaths != nil), "target variant module requested without a target variant") - let moduleOutputPath = isVariantJob ? variantModuleOutputInfo!.output!.outputPath : moduleOutputInfo.output!.outputPath - let moduleOutputPaths = isVariantJob ? variantModuleOutputPaths! : moduleOutputPaths + let moduleOutputPath = forVariantModule ? variantModuleOutputInfo!.output!.outputPath : moduleOutputInfo.output!.outputPath + let moduleOutputPaths = forVariantModule ? variantModuleOutputPaths! : moduleOutputPaths var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) } var inputs: [TypedVirtualPath] = [] var outputs: [TypedVirtualPath] = [ @@ -100,19 +95,21 @@ extension Driver { inputs.append(input) } - if let pchPath = bridgingPrecompiledHeader { + let (_, precompiledObjCHeader) = try computeCanonicalObjCHeader(explicitModulePlanner: explicitModulePlanner) + if let pchPath = precompiledObjCHeader { inputs.append(TypedVirtualPath(file: pchPath, type: .pch)) } try addBridgingHeaderPCHCacheKeyArguments(commandLine: &commandLine, pchCompileJob: pchCompileJob) - try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .emitModule) + try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .emitModule, + explicitModulePlanner: explicitModulePlanner, + forVariantEmitModule: forVariantModule) // FIXME: Add MSVC runtime library flags try addCommonModuleOptions( commandLine: &commandLine, outputs: &outputs, - moduleOutputPaths: moduleOutputPaths, - isMergeModule: false) + moduleOutputPaths: moduleOutputPaths) try addCommonSymbolGraphOptions(commandLine: &commandLine) diff --git a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift index 532dd49dc..ca1feecb5 100644 --- a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift +++ b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift @@ -42,15 +42,6 @@ extension Driver { /// Use the precompiled bridging header. case precompiled } - /// Whether the driver has already constructed a module dependency graph or is in the process - /// of doing so - enum ModuleDependencyGraphUse { - /// Even though the driver may be in ExplicitModuleBuild mode, the dependency graph has not yet - /// been constructed, omit processing module dependencies - case dependencyScan - /// If the driver is in Explicit Module Build mode, the dependency graph has been computed - case computed - } /// If the given option is specified but the frontend doesn't support it, throw an error. func verifyFrontendSupportsOptionIfNecessary(_ option: Option) throws { @@ -66,14 +57,20 @@ extension Driver { inputs: inout [TypedVirtualPath], kind: Job.Kind, bridgingHeaderHandling: BridgingHeaderHandling = .precompiled, - moduleDependencyGraphUse: ModuleDependencyGraphUse = .computed + explicitModulePlanner: ExplicitDependencyBuildPlanner? = nil, + forVariantEmitModule: Bool = false ) throws { // Only pass -target to the REPL or immediate modes if it was explicitly // specified on the command line. switch compilerMode { case .standardCompile, .singleCompile, .batchCompile, .compilePCM, .dumpPCM: commandLine.appendFlag(.target) - commandLine.appendFlag(targetTriple.triple) + if forVariantEmitModule, + let variant = parsedOptions.getLastArgument(.targetVariant)?.asSingle { + commandLine.appendFlag(Triple(variant, normalizing: true).triple) + } else { + commandLine.appendFlag(targetTriple.triple) + } case .repl, .immediate: if parsedOptions.hasArgument(.target) { @@ -84,21 +81,21 @@ extension Driver { break } - let isPlanJobForExplicitModule = parsedOptions.contains(.driverExplicitModuleBuild) && - moduleDependencyGraphUse == .computed + let isPlanJobForExplicitModule = parsedOptions.contains(.driverExplicitModuleBuild) let jobNeedPathRemap: Bool // If in ExplicitModuleBuild mode and the dependency graph has been computed, add module // dependencies. // May also be used for generation of the dependency graph itself in ExplicitModuleBuild mode. - if isPlanJobForExplicitModule { + if isPlanJobForExplicitModule, + let explicitModulePlanner = explicitModulePlanner { switch kind { case .generatePCH: - try addExplicitPCHBuildArguments(inputs: &inputs, commandLine: &commandLine) + explicitModulePlanner.resolveBridgingHeaderDependencies(inputs: &inputs, commandLine: &commandLine) jobNeedPathRemap = true case .compile, .emitModule, .interpret, .verifyModuleInterface: - try addExplicitModuleBuildArguments(inputs: &inputs, commandLine: &commandLine) + explicitModulePlanner.resolveMainModuleDependencies(inputs: &inputs, commandLine: &commandLine) jobNeedPathRemap = true - case .backend, .mergeModule, .compileModuleFromInterface, + case .backend, .compileModuleFromInterface, .generatePCM, .dumpPCM, .repl, .printTargetInfo, .versionRequest, .autolinkExtract, .generateDSYM, .help, .link, .verifyDebugInfo, .scanDependencies, @@ -122,7 +119,8 @@ extension Driver { commandLine.appendFlag(flag) } - if let variant = parsedOptions.getLastArgument(.targetVariant)?.asSingle { + if !forVariantEmitModule, + let variant = parsedOptions.getLastArgument(.targetVariant)?.asSingle { commandLine.appendFlag(.targetVariant) commandLine.appendFlag(Triple(variant, normalizing: true).triple) } @@ -473,9 +471,11 @@ extension Driver { try commandLine.appendAll(.Xcc, from: &parsedOptions) } + let (importedObjCHeader, precompiledObjCHeader) = + try computeCanonicalObjCHeader(explicitModulePlanner: explicitModulePlanner) let objcHeaderFile = (kind == .scanDependencies) ? originalObjCHeaderFile : importedObjCHeader if let importedObjCHeader = objcHeaderFile, bridgingHeaderHandling != .ignored { - if bridgingHeaderHandling == .precompiled, let pch = bridgingPrecompiledHeader { + if bridgingHeaderHandling == .precompiled, let pch = precompiledObjCHeader { // For explicit module build, we directly pass the compiled pch to // swift-frontend, rather than rely on swift-frontend to locate // the pch in the pchOutputDir and can start an implicit build in case @@ -923,20 +923,6 @@ extension Driver { entries[inputEntry, default: [:]][output.type] = output.fileHandle } - /// Adds all dependencies required for an explicit module build - /// to inputs and command line arguments of a compile job. - mutating func addExplicitModuleBuildArguments(inputs: inout [TypedVirtualPath], - commandLine: inout [Job.ArgTemplate]) throws { - try explicitDependencyBuildPlanner?.resolveMainModuleDependencies(inputs: &inputs, commandLine: &commandLine) - } - - /// Adds all dependencies required for an explicit module build of the bridging header - /// to inputs and command line arguments of a compile job. - mutating func addExplicitPCHBuildArguments(inputs: inout [TypedVirtualPath], - commandLine: inout [Job.ArgTemplate]) throws { - try explicitDependencyBuildPlanner?.resolveBridgingHeaderDependencies(inputs: &inputs, commandLine: &commandLine) - } - mutating func addPluginPathArguments(commandLine: inout [Job.ArgTemplate]) throws { guard isFrontendArgSupported(.pluginPath) else { return @@ -961,12 +947,6 @@ extension Driver { commandLine.appendPath(pluginPathRoot.localPluginPath) } - - /// If explicit dependency planner supports creating bridging header pch command. - public var supportsBridgingHeaderPCHCommand: Bool { - return explicitDependencyBuildPlanner?.supportsBridgingHeaderPCHCommand ?? false - } - /// In Explicit Module Build mode, distinguish between main module jobs and intermediate dependency build jobs, /// such as Swift modules built from .swiftmodule files and Clang PCMs. public func isExplicitMainModuleJob(job: Job) -> Bool { diff --git a/Sources/SwiftDriver/Jobs/GeneratePCHJob.swift b/Sources/SwiftDriver/Jobs/GeneratePCHJob.swift index a4ff2943d..2d70cd22d 100644 --- a/Sources/SwiftDriver/Jobs/GeneratePCHJob.swift +++ b/Sources/SwiftDriver/Jobs/GeneratePCHJob.swift @@ -13,28 +13,33 @@ import struct TSCBasic.RelativePath extension Driver { - mutating func addGeneratePCHFlags(commandLine: inout [Job.ArgTemplate], inputs: inout [TypedVirtualPath]) throws { + mutating func addGeneratePCHFlags(commandLine: inout [Job.ArgTemplate], inputs: inout [TypedVirtualPath], + explicitModulePlanner: ExplicitDependencyBuildPlanner?) throws { commandLine.appendFlag("-frontend") try addCommonFrontendOptions( - commandLine: &commandLine, inputs: &inputs, kind: .generatePCH, bridgingHeaderHandling: .parsed) + commandLine: &commandLine, inputs: &inputs, kind: .generatePCH, bridgingHeaderHandling: .parsed, + explicitModulePlanner: explicitModulePlanner) try commandLine.appendLast(.indexStorePath, from: &parsedOptions) commandLine.appendFlag(.emitPch) } - mutating func generatePCHJob(input: TypedVirtualPath, output: TypedVirtualPath) throws -> Job { + mutating func generatePCHJob(input: TypedVirtualPath, output: TypedVirtualPath, + explicitModulePlanner: ExplicitDependencyBuildPlanner?) throws -> Job { var inputs = [TypedVirtualPath]() var outputs = [TypedVirtualPath]() var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) } - if supportsBridgingHeaderPCHCommand { - try addExplicitPCHBuildArguments(inputs: &inputs, commandLine: &commandLine) + if let explicitModulePlanner = explicitModulePlanner, + explicitModulePlanner.supportsBridgingHeaderPCHCommand { + explicitModulePlanner.resolveBridgingHeaderDependencies(inputs: &inputs, commandLine: &commandLine) addCacheReplayMapping(to: &commandLine) } else { - try addGeneratePCHFlags(commandLine: &commandLine, inputs: &inputs) + try addGeneratePCHFlags(commandLine: &commandLine, inputs: &inputs, + explicitModulePlanner: explicitModulePlanner) } try addRuntimeLibraryFlags(commandLine: &commandLine) @@ -86,3 +91,20 @@ extension Driver { ) } } + +extension Driver { + mutating func computeCanonicalObjCHeader(explicitModulePlanner: ExplicitDependencyBuildPlanner?) + throws -> (VirtualPath.Handle?, VirtualPath.Handle?) { + let contextHash = try? explicitModulePlanner?.getMainModuleContextHash() + let chainedBridgingHeader = explicitModulePlanner?.chainedBridgingHeaderFile + let importedObjCHeader = try computeImportedObjCHeader(&parsedOptions, compilerMode: compilerMode, + chainedBridgingHeader: chainedBridgingHeader) ?? originalObjCHeaderFile + let precompiledHeader = computeBridgingPrecompiledHeader(&parsedOptions, + compilerMode: compilerMode, + importedObjCHeader: importedObjCHeader, + outputFileMap: outputFileMap, + outputDirectory: bridgingPrecompiledHeaderOutputDir, + contextHash: contextHash) + return (importedObjCHeader, precompiledHeader) + } +} diff --git a/Sources/SwiftDriver/Jobs/GeneratePCMJob.swift b/Sources/SwiftDriver/Jobs/GeneratePCMJob.swift index 6e25aa72b..1014bd612 100644 --- a/Sources/SwiftDriver/Jobs/GeneratePCMJob.swift +++ b/Sources/SwiftDriver/Jobs/GeneratePCMJob.swift @@ -18,7 +18,8 @@ extension Driver { /// (https://clang.llvm.org/docs/Modules.html#module-map-language) and the /// output is a compiled module that also includes the additional information /// needed by Swift's Clang importer, e.g., the Swift name lookup tables. - mutating func generateEmitPCMJob(input: TypedVirtualPath) throws -> Job { + mutating func generateEmitPCMJob(input: TypedVirtualPath, + explicitModulePlanner: ExplicitDependencyBuildPlanner?) throws -> Job { var inputs = [TypedVirtualPath]() var outputs = [TypedVirtualPath]() @@ -48,7 +49,8 @@ extension Driver { commandLine.appendPath(output.file) try addCommonFrontendOptions( - commandLine: &commandLine, inputs: &inputs, kind: .generatePCM, bridgingHeaderHandling: .ignored) + commandLine: &commandLine, inputs: &inputs, kind: .generatePCM, bridgingHeaderHandling: .ignored, + explicitModulePlanner: explicitModulePlanner) try commandLine.appendLast(.indexStorePath, from: &parsedOptions) let cacheKeys = try computeOutputCacheKeyForJob(commandLine: commandLine, inputs: [(input, 0)]) @@ -69,7 +71,8 @@ extension Driver { /// Create a job that dumps information about a Clang module /// /// The input is a Clang Pre-compiled module file (.pcm). - mutating func generateDumpPCMJob(input: TypedVirtualPath) throws -> Job { + mutating func generateDumpPCMJob(input: TypedVirtualPath, + explicitModulePlanner: ExplicitDependencyBuildPlanner?) throws -> Job { var inputs = [TypedVirtualPath]() var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) } @@ -81,7 +84,8 @@ extension Driver { commandLine.appendPath(input.file) try addCommonFrontendOptions( - commandLine: &commandLine, inputs: &inputs, kind: .generatePCM, bridgingHeaderHandling: .ignored) + commandLine: &commandLine, inputs: &inputs, kind: .generatePCM, bridgingHeaderHandling: .ignored, + explicitModulePlanner: explicitModulePlanner) return Job( moduleName: moduleOutputInfo.name, diff --git a/Sources/SwiftDriver/Jobs/InterpretJob.swift b/Sources/SwiftDriver/Jobs/InterpretJob.swift index 73508c1fd..aebf5e487 100644 --- a/Sources/SwiftDriver/Jobs/InterpretJob.swift +++ b/Sources/SwiftDriver/Jobs/InterpretJob.swift @@ -11,7 +11,8 @@ //===----------------------------------------------------------------------===// extension Driver { - mutating func interpretJob(inputs allInputs: [TypedVirtualPath]) throws -> Job { + mutating func interpretJob(inputs allInputs: [TypedVirtualPath], + explicitModulePlanner: ExplicitDependencyBuildPlanner?) throws -> Job { var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) } var inputs: [TypedVirtualPath] = [] @@ -27,7 +28,8 @@ extension Driver { commandLine.appendFlag(.disableObjcAttrRequiresFoundationModule) } - try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .interpret) + try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .interpret, + explicitModulePlanner: explicitModulePlanner) try addRuntimeLibraryFlags(commandLine: &commandLine) try commandLine.appendLast(.parseSil, from: &parsedOptions) diff --git a/Sources/SwiftDriver/Jobs/Job.swift b/Sources/SwiftDriver/Jobs/Job.swift index 1313f8e2a..1da02090c 100644 --- a/Sources/SwiftDriver/Jobs/Job.swift +++ b/Sources/SwiftDriver/Jobs/Job.swift @@ -18,7 +18,6 @@ public struct Job: Codable, Equatable, Hashable { public enum Kind: String, Codable { case compile case backend - case mergeModule = "merge-module" case link case generateDSYM = "generate-dSYM" case autolinkExtract = "autolink-extract" @@ -180,9 +179,6 @@ extension Job : CustomStringConvertible { case .compile: return join("Compiling \(moduleName)", displayInputs.first?.file.basename) - case .mergeModule: - return "Merging module \(moduleName)" - case .link: return "Linking \(moduleName)" @@ -268,7 +264,7 @@ extension Job.Kind { /// Whether this job kind uses the Swift frontend. public var isSwiftFrontend: Bool { switch self { - case .backend, .compile, .mergeModule, .emitModule, .compileModuleFromInterface, .generatePCH, + case .backend, .compile, .emitModule, .compileModuleFromInterface, .generatePCH, .generatePCM, .dumpPCM, .interpret, .repl, .printTargetInfo, .versionRequest, .emitSupportedFeatures, .scanDependencies, .verifyModuleInterface: return true @@ -284,7 +280,7 @@ extension Job.Kind { switch self { case .compile: return true - case .backend, .mergeModule, .emitModule, .generatePCH, .compileModuleFromInterface, + case .backend, .emitModule, .generatePCH, .compileModuleFromInterface, .generatePCM, .dumpPCM, .interpret, .repl, .printTargetInfo, .versionRequest, .autolinkExtract, .generateDSYM, .help, .link, .verifyDebugInfo, .scanDependencies, @@ -301,7 +297,7 @@ extension Job.Kind { case .compile, .emitModule, .generatePCH, .compileModuleFromInterface, .generatePCM, .verifyModuleInterface: return true - case .backend, .mergeModule, .dumpPCM, .interpret, .repl, .printTargetInfo, + case .backend, .dumpPCM, .interpret, .repl, .printTargetInfo, .versionRequest, .autolinkExtract, .generateDSYM, .help, .link, .verifyDebugInfo, .scanDependencies, .emitSupportedFeatures, .moduleWrap, .generateAPIBaseline, .generateABIBaseline, .compareAPIBaseline, diff --git a/Sources/SwiftDriver/Jobs/LinkJob.swift b/Sources/SwiftDriver/Jobs/LinkJob.swift index fa7d40eb5..afc599ec4 100644 --- a/Sources/SwiftDriver/Jobs/LinkJob.swift +++ b/Sources/SwiftDriver/Jobs/LinkJob.swift @@ -46,7 +46,8 @@ extension Driver { } /// Link the given inputs. - mutating func linkJob(inputs: [TypedVirtualPath]) throws -> Job { + mutating func linkJob(inputs: [TypedVirtualPath], + explicitModulePlanner: ExplicitDependencyBuildPlanner?) throws -> Job { var commandLine: [Job.ArgTemplate] = [] // Compute the final output file @@ -76,7 +77,7 @@ extension Driver { ) if parsedOptions.hasArgument(.explicitAutoLinking) { - try explicitDependencyBuildPlanner?.getLinkLibraryLoadCommandFlags(&commandLine) + try explicitModulePlanner?.getLinkLibraryLoadCommandFlags(&commandLine) } return Job( diff --git a/Sources/SwiftDriver/Jobs/MergeModuleJob.swift b/Sources/SwiftDriver/Jobs/MergeModuleJob.swift deleted file mode 100644 index 718623bab..000000000 --- a/Sources/SwiftDriver/Jobs/MergeModuleJob.swift +++ /dev/null @@ -1,83 +0,0 @@ -//===--------------- MergeModuleJob.swift - Swift Module Merging ----------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 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 struct TSCBasic.RelativePath - -extension Driver { - mutating func mergeModuleJob(inputs providedInputs: [TypedVirtualPath], - inputsFromOutputs: [TypedVirtualPath]) throws -> Job { - var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) } - var inputs: [TypedVirtualPath] = [] - var outputs: [TypedVirtualPath] = [ - TypedVirtualPath(file: moduleOutputInfo.output!.outputPath, type: .swiftModule) - ] - - commandLine.appendFlags("-frontend", "-merge-modules", "-emit-module") - - // Input file list. - if shouldUseInputFileList { - commandLine.appendFlag(.filelist) - let fileList = try VirtualPath.createUniqueFilelist(RelativePath(validating: "inputs"), - .list(inputsFromOutputs.map { $0.file })) - commandLine.appendPath(fileList) - inputs.append(contentsOf: inputsFromOutputs) - - for input in providedInputs { - assert(input.type == .swiftModule) - commandLine.append(.path(input.file)) - inputs.append(input) - } - } else { - // Add the inputs. - for input in providedInputs + inputsFromOutputs { - assert(input.type == .swiftModule) - commandLine.append(.path(input.file)) - inputs.append(input) - } - } - - // Tell all files to parse as library, which is necessary to load them as - // serialized ASTs. - commandLine.appendFlag(.parseAsLibrary) - - // Disable SIL optimization passes; we've already optimized the code in each - // partial mode. - commandLine.appendFlag(.disableDiagnosticPasses) - commandLine.appendFlag(.disableSilPerfOptzns) - - try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .mergeModule, bridgingHeaderHandling: .parsed) - try addRuntimeLibraryFlags(commandLine: &commandLine) - - try addCommonModuleOptions(commandLine: &commandLine, outputs: &outputs, moduleOutputPaths: moduleOutputPaths, isMergeModule: true) - - try addCommonSymbolGraphOptions(commandLine: &commandLine) - - let outputPath = VirtualPath.lookup(moduleOutputInfo.output!.outputPath) - commandLine.appendFlag(.o) - commandLine.appendPath(outputPath) - - if let abiPath = moduleOutputPaths.abiDescriptorFilePath { - commandLine.appendFlag(.emitAbiDescriptorPath) - commandLine.appendPath(abiPath.file) - outputs.append(abiPath) - } - return Job( - moduleName: moduleOutputInfo.name, - kind: .mergeModule, - tool: try toolchain.resolvedTool(.swiftCompiler), - commandLine: commandLine, - inputs: inputs, - primaryInputs: [], - outputs: outputs - ) - } -} diff --git a/Sources/SwiftDriver/Jobs/Planning.swift b/Sources/SwiftDriver/Jobs/Planning.swift index 6ec49dda6..6fe800827 100644 --- a/Sources/SwiftDriver/Jobs/Planning.swift +++ b/Sources/SwiftDriver/Jobs/Planning.swift @@ -61,7 +61,7 @@ extension Driver { /// Plan a standard compilation, which produces jobs for compiling separate /// primary files. private mutating func planStandardCompile() throws - -> ([Job], IncrementalCompilationState?, InterModuleDependencyGraph?) { + -> ([Job], IncrementalCompilationState?, ExplicitDependencyBuildPlanner?) { precondition(compilerMode.isStandardCompilationForPlanning, "compiler mode \(compilerMode) is handled elsewhere") // Determine the initial state for incremental compilation that is required during @@ -71,10 +71,10 @@ extension Driver { try IncrementalCompilationState.computeIncrementalStateForPlanning(driver: &self) // For an explicit build, compute the inter-module dependency graph - let interModuleDependencyGraph = try computeInterModuleDependencyGraph() + let explicitModulePlanner = try configureExplicitModulePlanner() // Compute the set of all jobs required to build this module - let jobsInPhases = try computeJobsForPhasedStandardBuild(moduleDependencyGraph: interModuleDependencyGraph, + let jobsInPhases = try computeJobsForPhasedStandardBuild(explicitModulePlanner: explicitModulePlanner, initialIncrementalState: initialIncrementalState) // Determine the state for incremental compilation @@ -85,7 +85,7 @@ extension Driver { try IncrementalCompilationState(driver: &self, jobsInPhases: jobsInPhases, initialState: initialState, - interModuleDepGraph: interModuleDependencyGraph) + explicitModulePlanner: explicitModulePlanner) } else { incrementalCompilationState = nil } @@ -98,21 +98,31 @@ extension Driver { } else { batchedJobs = try formBatchedJobs(jobsInPhases.allJobs, showJobLifecycle: showJobLifecycle, - jobCreatingPch: jobsInPhases.allJobs.first(where: {$0.kind == .generatePCH})) + jobCreatingPch: jobsInPhases.allJobs.first(where: {$0.kind == .generatePCH}), + explicitModulePlanner: explicitModulePlanner) } - return (batchedJobs, incrementalCompilationState, interModuleDependencyGraph) + return (batchedJobs, incrementalCompilationState, explicitModulePlanner) } /// If performing an explicit module build, compute an inter-module dependency graph. /// If performing an incremental build, and the initial incremental state contains a valid /// graph already, it is safe to re-use without repeating the scan. - private mutating func computeInterModuleDependencyGraph() - throws -> InterModuleDependencyGraph? { + private mutating func configureExplicitModulePlanner(forVariantModule: Bool = false) + throws -> ExplicitDependencyBuildPlanner? { if (parsedOptions.contains(.driverExplicitModuleBuild) || parsedOptions.contains(.explainModuleDependency)) && inputFiles.contains(where: { $0.type.isPartOfSwiftCompilation }) { - return try gatherModuleDependencies() + let interModuleDependencyGraph = try scanModuleDependencies(forVariantModule: forVariantModule) + return try ExplicitDependencyBuildPlanner(dependencyGraph: interModuleDependencyGraph, + toolchain: toolchain, + integratedDriver: integratedDriver, + supportsExplicitInterfaceBuild: + isFrontendArgSupported(.explicitInterfaceModuleBuild), + cas: cas, + prefixMap: prefixMapping, + supportsBridgingHeaderPCHCommand: + interModuleDependencyOracle.supportsBridgingHeaderPCHCommand) } else { return nil } @@ -122,7 +132,7 @@ extension Driver { /// At build time, incremental state will be used to distinguish which of these jobs must run. /// /// For Explicitly-Built module dependencies, filter out all up-to-date modules. - @_spi(Testing) public mutating func computeJobsForPhasedStandardBuild(moduleDependencyGraph: InterModuleDependencyGraph?, + @_spi(Testing) public mutating func computeJobsForPhasedStandardBuild(explicitModulePlanner: ExplicitDependencyBuildPlanner?, initialIncrementalState: IncrementalCompilationState.InitialStateForPlanning? = nil) throws -> JobsInPhases { @@ -146,35 +156,72 @@ extension Driver { jobsAfterCompiles.append(job) } - try addPrecompileModuleDependenciesJobs(dependencyGraph: moduleDependencyGraph, - initialIncrementalState: initialIncrementalState, + try addPrecompileModuleDependenciesJobs(explicitModulePlanner: explicitModulePlanner, + incrementalRemarks: initialIncrementalState != nil && initialIncrementalState!.incrementalOptions.contains(.showIncremental), addJob: addJobBeforeCompiles) - try addPrecompileBridgingHeaderJob(addJob: addJobBeforeCompiles) + let pchCompileJob = try addPrecompileBridgingHeaderJob(explicitModulePlanner: explicitModulePlanner) + if pchCompileJob != nil { + addJobBeforeCompiles(pchCompileJob!) + } let linkerInputs = try addJobsFeedingLinker( addJobBeforeCompiles: addJobBeforeCompiles, - jobsBeforeCompiles: jobsBeforeCompiles, + pchCompileJob: pchCompileJob, addCompileJob: addCompileJob, - addJobAfterCompiles: addJobAfterCompiles) + addJobAfterCompiles: addJobAfterCompiles, + explicitModulePlanner: explicitModulePlanner) + try addVariantModuleJobs(addJobBeforeCompiles: addJobBeforeCompiles, + addJobAfterCompiles: addJobAfterCompiles) try addAPIDigesterJobs(addJob: addJobAfterCompiles) try addLinkAndPostLinkJobs(linkerInputs: linkerInputs, debugInfo: debugInfo, - addJob: addJobAfterCompiles) + addJob: addJobAfterCompiles, + explicitModulePlanner: explicitModulePlanner) return JobsInPhases(beforeCompiles: jobsBeforeCompiles, compileJobs: compileJobs, afterCompiles: jobsAfterCompiles) } + private mutating func addVariantModuleJobs(addJobBeforeCompiles: (Job) -> Void, + addJobAfterCompiles: (Job) -> Void) throws { + guard variantModuleOutputInfo != nil else { + return + } + + let explicitModulePlanner: ExplicitDependencyBuildPlanner? = + try configureExplicitModulePlanner(forVariantModule: true) + try addPrecompileModuleDependenciesJobs(explicitModulePlanner: explicitModulePlanner, + incrementalRemarks: false, + addJob: addJobBeforeCompiles) + + let pchCompileJob = try addPrecompileBridgingHeaderJob(explicitModulePlanner: explicitModulePlanner) + if pchCompileJob != nil { + addJobBeforeCompiles(pchCompileJob!) + } + + let emitVariantModuleJob = try addEmitModuleJob( + addJobBeforeCompiles: addJobBeforeCompiles, + pchCompileJob: pchCompileJob, + explicitModulePlanner: explicitModulePlanner, + isVariantModule: true)! + + if emitVariantModuleJob.outputs.contains(where: { out in out.type == .swiftInterface }) { + try addVerifyJobs(emitModuleJob: emitVariantModuleJob, addJob: addJobAfterCompiles, + explicitModulePlanner: explicitModulePlanner, + forVariantModule: true) + + } + } + private mutating func addPrecompileModuleDependenciesJobs( - dependencyGraph: InterModuleDependencyGraph?, - initialIncrementalState: IncrementalCompilationState.InitialStateForPlanning?, + explicitModulePlanner: ExplicitDependencyBuildPlanner?, + incrementalRemarks: Bool, addJob: (Job) -> Void) throws { - guard let resolvedDependencyGraph = dependencyGraph else { + guard var resolvedModulePlanner = explicitModulePlanner else { return } - let modulePrebuildJobs = - try generateExplicitModuleDependenciesJobs(dependencyGraph: resolvedDependencyGraph) + let modulePrebuildJobs = try resolvedModulePlanner.generateExplicitModuleDependenciesBuildJobs() // If asked, add jobs to precompile module dependencies. Otherwise exit. // We may have a dependency graph but not be required to add pre-compile jobs to the build plan, // for example when `-explain-dependency` is being used. @@ -186,36 +233,37 @@ extension Driver { if parsedOptions.contains(.alwaysRebuildModuleDependencies) { mandatoryModuleCompileJobs = modulePrebuildJobs } else { - let enableIncrementalRemarks = initialIncrementalState != nil && initialIncrementalState!.incrementalOptions.contains(.showIncremental) - let reporter: IncrementalCompilationState.Reporter? = enableIncrementalRemarks ? + let reporter: IncrementalCompilationState.Reporter? = incrementalRemarks ? IncrementalCompilationState.Reporter(diagnosticEngine: diagnosticEngine, outputFileMap: outputFileMap) : nil mandatoryModuleCompileJobs = - try resolvedDependencyGraph.filterMandatoryModuleDependencyCompileJobs(modulePrebuildJobs, - fileSystem: fileSystem, - cas: cas, - reporter: reporter) + try resolvedModulePlanner.filterMandatoryModuleDependencyCompileJobs(modulePrebuildJobs, + fileSystem: fileSystem, + cas: cas, + reporter: reporter) } mandatoryModuleCompileJobs.forEach(addJob) } - private mutating func addPrecompileBridgingHeaderJob(addJob: (Job) -> Void) throws { + private mutating func addPrecompileBridgingHeaderJob(explicitModulePlanner: ExplicitDependencyBuildPlanner?) throws -> Job? { + let (importedObjCHeader, precompiledObjCHeader) = + try computeCanonicalObjCHeader(explicitModulePlanner: explicitModulePlanner) guard let importedObjCHeader = importedObjCHeader, - let bridgingPrecompiledHeader = bridgingPrecompiledHeader - else { return } + let bridgingPrecompiledHeader = precompiledObjCHeader + else { return nil } - addJob( - try generatePCHJob(input: .init(file: importedObjCHeader, - type: .objcHeader), - output: .init(file: bridgingPrecompiledHeader, - type: .pch)) - ) + return try generatePCHJob(input: .init(file: importedObjCHeader, + type: .objcHeader), + output: .init(file: bridgingPrecompiledHeader, + type: .pch), + explicitModulePlanner: explicitModulePlanner) } private mutating func addEmitModuleJob( addJobBeforeCompiles: (Job) -> Void, pchCompileJob: Job?, + explicitModulePlanner: ExplicitDependencyBuildPlanner?, isVariantModule: Bool = false) throws -> Job? { // The target variant module is always emitted separately, so we need to // add an explicit job regardless of whether the primary target was @@ -223,7 +271,8 @@ extension Driver { if emitModuleSeparately || isVariantModule { let emitJob = try emitModuleJob( pchCompileJob: pchCompileJob, - isVariantJob: isVariantModule) + explicitModulePlanner: explicitModulePlanner, + forVariantModule: isVariantModule) addJobBeforeCompiles(emitJob) return emitJob } @@ -232,20 +281,15 @@ extension Driver { private mutating func addJobsFeedingLinker( addJobBeforeCompiles: (Job) -> Void, - jobsBeforeCompiles: [Job], + pchCompileJob: Job?, addCompileJob: (Job) -> Void, - addJobAfterCompiles: (Job) -> Void + addJobAfterCompiles: (Job) -> Void, + explicitModulePlanner: ExplicitDependencyBuildPlanner? ) throws -> [TypedVirtualPath] { var linkerInputs = [TypedVirtualPath]() func addLinkerInput(_ li: TypedVirtualPath) { linkerInputs.append(li) } - - var moduleInputs = [TypedVirtualPath]() let acceptBitcodeAsLinkerInput = lto == .llvmThin || lto == .llvmFull - func addModuleInput(_ mi: TypedVirtualPath) { moduleInputs.append(mi) } - var moduleInputsFromJobOutputs = [TypedVirtualPath]() - func addModuleInputFromJobOutputs(_ mis: TypedVirtualPath) { - moduleInputsFromJobOutputs.append(mis) } func addJobOutputs(_ jobOutputs: [TypedVirtualPath]) { for jobOutput in jobOutputs { @@ -254,8 +298,6 @@ extension Driver { addLinkerInput(jobOutput) case .llvmBitcode where acceptBitcodeAsLinkerInput: addLinkerInput(jobOutput) - case .swiftModule: - addModuleInputFromJobOutputs(jobOutput) default: break @@ -276,63 +318,42 @@ extension Driver { assert(jobCreatingSwiftModule == nil) jobCreatingSwiftModule = emitModuleJob - try addVerifyJobs(emitModuleJob: emitModuleJob, addJob: addJobAfterCompiles) + try addVerifyJobs(emitModuleJob: emitModuleJob, addJob: addJobAfterCompiles, + explicitModulePlanner: explicitModulePlanner) } - // Try to see if we scheduled a pch compile job. If so, pass it to the comile jobs. - let jobCreatingPch: Job? = jobsBeforeCompiles.first(where: {$0.kind == .generatePCH}) - // Whole-module if let compileJob = try addSingleCompileJobs(addJob: addJobBeforeCompiles, addJobOutputs: addJobOutputs, - pchCompileJob: jobCreatingPch, - emitModuleTrace: loadedModuleTracePath != nil) { + pchCompileJob: pchCompileJob, + emitModuleTrace: loadedModuleTracePath != nil, + explicitModulePlanner: explicitModulePlanner) { try addPostModuleFilesJobs(compileJob) } // Emit-module-separately if let emitModuleJob = try addEmitModuleJob(addJobBeforeCompiles: addJobBeforeCompiles, - pchCompileJob: jobCreatingPch) { + pchCompileJob: pchCompileJob, + explicitModulePlanner: explicitModulePlanner) { try addPostModuleFilesJobs(emitModuleJob) try addWrapJobOrMergeOutputs( - mergeJob: emitModuleJob, + emitModuleJob: emitModuleJob, debugInfo: debugInfo, addJob: addJobAfterCompiles, addLinkerInput: addLinkerInput) } - if variantModuleOutputInfo != nil { - _ = try addEmitModuleJob( - addJobBeforeCompiles: addJobBeforeCompiles, - pchCompileJob: jobCreatingPch, - isVariantModule: true) - } - try addJobsForPrimaryInputs( addCompileJob: addCompileJob, - addModuleInput: addModuleInput, addLinkerInput: addLinkerInput, addJobOutputs: addJobOutputs, - pchCompileJob: jobCreatingPch) + pchCompileJob: pchCompileJob, + explicitModulePlanner: explicitModulePlanner) try addAutolinkExtractJob(linkerInputs: linkerInputs, addLinkerInput: addLinkerInput, addJob: addJobAfterCompiles) - - // Merge-module - if let mergeJob = try mergeModuleJob( - moduleInputs: moduleInputs, - moduleInputsFromJobOutputs: moduleInputsFromJobOutputs) { - addJobAfterCompiles(mergeJob) - try addPostModuleFilesJobs(mergeJob) - - try addWrapJobOrMergeOutputs( - mergeJob: mergeJob, - debugInfo: debugInfo, - addJob: addJobAfterCompiles, - addLinkerInput: addLinkerInput) - } return linkerInputs } @@ -342,7 +363,8 @@ extension Driver { addJob: (Job) -> Void, addJobOutputs: ([TypedVirtualPath]) -> Void, pchCompileJob: Job?, - emitModuleTrace: Bool + emitModuleTrace: Bool, + explicitModulePlanner: ExplicitDependencyBuildPlanner? ) throws -> Job? { guard case .singleCompile = compilerMode, inputFiles.contains(where: { $0.type.isPartOfSwiftCompilation }) @@ -354,17 +376,18 @@ extension Driver { addJobOutputs: addJobOutputs, pchCompileJob: pchCompileJob, emitModuleTrace: emitModuleTrace, - produceCacheKey: true) + produceCacheKey: true, + explicitModulePlanner: explicitModulePlanner) addJob(compile) return compile } private mutating func addJobsForPrimaryInputs( addCompileJob: (Job) -> Void, - addModuleInput: (TypedVirtualPath) -> Void, addLinkerInput: (TypedVirtualPath) -> Void, addJobOutputs: ([TypedVirtualPath]) -> Void, - pchCompileJob: Job?) + pchCompileJob: Job?, + explicitModulePlanner: ExplicitDependencyBuildPlanner?) throws { let loadedModuleTraceInputIndex = inputFiles.firstIndex(where: { $0.type.isPartOfSwiftCompilation && loadedModuleTracePath != nil @@ -374,22 +397,22 @@ extension Driver { try addJobForPrimaryInput( input: input, addCompileJob: addCompileJob, - addModuleInput: addModuleInput, addLinkerInput: addLinkerInput, addJobOutputs: addJobOutputs, pchCompileJob: pchCompileJob, - emitModuleTrace: index == loadedModuleTraceInputIndex) + emitModuleTrace: index == loadedModuleTraceInputIndex, + explicitModulePlanner: explicitModulePlanner) } } private mutating func addJobForPrimaryInput( input: TypedVirtualPath, addCompileJob: (Job) -> Void, - addModuleInput: (TypedVirtualPath) -> Void, addLinkerInput: (TypedVirtualPath) -> Void, addJobOutputs: ([TypedVirtualPath]) -> Void, pchCompileJob: Job?, - emitModuleTrace: Bool + emitModuleTrace: Bool, + explicitModulePlanner: ExplicitDependencyBuildPlanner? ) throws { switch input.type { @@ -406,7 +429,8 @@ extension Driver { canSkipIfOnlyModule: canSkipIfOnlyModule, pchCompileJob: pchCompileJob, addCompileJob: addCompileJob, - addJobOutputs: addJobOutputs) + addJobOutputs: addJobOutputs, + explicitModulePlanner: explicitModulePlanner) case .object, .autolink, .llvmBitcode, .tbd: if linkerOutputType != nil { @@ -416,13 +440,8 @@ extension Driver { } case .swiftModule: - if moduleOutputInfo.output != nil && linkerOutputType == nil { - // When generating a .swiftmodule as a top-level output (as opposed - // to, for example, linking an image), treat .swiftmodule files as - // inputs to a MergeModule action. - addModuleInput(input) - } else if linkerOutputType != nil { - // Otherwise, if linking, pass .swiftmodule files as inputs to the + if linkerOutputType != nil { + // If linking, pass .swiftmodule files as inputs to the // linker, so that their debug info is available. addLinkerInput(input) } else { @@ -440,7 +459,8 @@ extension Driver { canSkipIfOnlyModule: Bool, pchCompileJob: Job?, addCompileJob: (Job) -> Void, - addJobOutputs: ([TypedVirtualPath]) -> Void + addJobOutputs: ([TypedVirtualPath]) -> Void, + explicitModulePlanner: ExplicitDependencyBuildPlanner? ) throws { // We can skip the compile jobs if all we want is a module when it's // built separately. @@ -452,23 +472,11 @@ extension Driver { addJobOutputs: addJobOutputs, pchCompileJob: pchCompileJob, emitModuleTrace: emitModuleTrace, - produceCacheKey: !compilerMode.isBatchCompile) + produceCacheKey: !compilerMode.isBatchCompile, + explicitModulePlanner: explicitModulePlanner) addCompileJob(compile) } - /// Need a merge module job if there are module inputs - private mutating func mergeModuleJob( - moduleInputs: [TypedVirtualPath], - moduleInputsFromJobOutputs: [TypedVirtualPath] - ) throws -> Job? { - guard moduleOutputInfo.output != nil, - !(moduleInputs.isEmpty && moduleInputsFromJobOutputs.isEmpty), - compilerMode.usesPrimaryFileInputs, - !emitModuleSeparately - else { return nil } - return try mergeModuleJob(inputs: moduleInputs, inputsFromOutputs: moduleInputsFromJobOutputs) - } - func getAdopterConfigPathFromXcodeDefaultToolchain() -> AbsolutePath? { let swiftPath = try? toolchain.resolvedTool(.swiftCompiler).path guard var swiftPath = swiftPath else { @@ -516,7 +524,9 @@ extension Driver { return Set(allModules) } - private mutating func addVerifyJobs(emitModuleJob: Job, addJob: (Job) -> Void ) + private mutating func addVerifyJobs(emitModuleJob: Job, addJob: (Job) -> Void, + explicitModulePlanner: ExplicitDependencyBuildPlanner?, + forVariantModule: Bool = false) throws { guard // Only verify modules with library evolution. @@ -559,13 +569,19 @@ extension Driver { case Public, Private, Package } - func addVerifyJob(for mode: InterfaceMode) throws { + func addVerifyJob(for mode: InterfaceMode, + explicitModulePlanner: ExplicitDependencyBuildPlanner?, + forVariantModule: Bool) throws { var isNeeded = false var outputType: FileType switch mode { case .Public: - isNeeded = parsedOptions.hasArgument(.emitModuleInterface, .emitModuleInterfacePath) + if forVariantModule { + isNeeded = parsedOptions.hasArgument(.emitVariantModuleInterfacePath) + } else { + isNeeded = parsedOptions.hasArgument(.emitModuleInterface, .emitModuleInterfacePath) + } outputType = FileType.swiftInterface case .Private: isNeeded = parsedOptions.hasArgument(.emitPrivateModuleInterfacePath) @@ -582,13 +598,15 @@ extension Driver { "Merge module job should only have one swiftinterface output") let job = try verifyModuleInterfaceJob(interfaceInput: mergeInterfaceOutputs[0], emitModuleJob: emitModuleJob, - reportAsError: reportAsError) + reportAsError: reportAsError, + explicitModulePlanner: explicitModulePlanner, + forVariantModule: forVariantModule) addJob(job) } - try addVerifyJob(for: .Public) - try addVerifyJob(for: .Private) + try addVerifyJob(for: .Public, explicitModulePlanner: explicitModulePlanner, forVariantModule: forVariantModule) + try addVerifyJob(for: .Private, explicitModulePlanner: explicitModulePlanner, forVariantModule: forVariantModule) if parsedOptions.hasArgument(.packageName) { - try addVerifyJob(for: .Package) + try addVerifyJob(for: .Package, explicitModulePlanner: explicitModulePlanner, forVariantModule: forVariantModule) } } @@ -611,22 +629,22 @@ extension Driver { } } - private mutating func addWrapJobOrMergeOutputs(mergeJob: Job, + private mutating func addWrapJobOrMergeOutputs(emitModuleJob: Job, debugInfo: DebugInfo, addJob: (Job) -> Void, addLinkerInput: (TypedVirtualPath) -> Void) throws { guard case .astTypes = debugInfo.level else { return } - let mergeModuleOutputs = mergeJob.outputs.filter { $0.type == .swiftModule } - assert(mergeModuleOutputs.count == 1, - "Merge module job should only have one swiftmodule output") + let moduleOutputs = emitModuleJob.outputs.filter { $0.type == .swiftModule } + assert(moduleOutputs.count == 1, + "Emit module job should only have one swiftmodule output") if targetTriple.objectFormat == .macho { - addLinkerInput(mergeModuleOutputs[0]) + addLinkerInput(moduleOutputs[0]) } else { // Module wrapping is required. - let wrapJob = try moduleWrapJob(moduleInput: mergeModuleOutputs[0]) + let wrapJob = try moduleWrapJob(moduleInput: moduleOutputs[0]) addJob(wrapJob) wrapJob.outputs.forEach(addLinkerInput) @@ -647,12 +665,14 @@ extension Driver { private mutating func addLinkAndPostLinkJobs( linkerInputs: [TypedVirtualPath], debugInfo: DebugInfo, - addJob: (Job) -> Void + addJob: (Job) -> Void, + explicitModulePlanner: ExplicitDependencyBuildPlanner? ) throws { guard linkerOutputType != nil && !linkerInputs.isEmpty else { return } - let linkJ = try linkJob(inputs: linkerInputs) + let linkJ = try linkJob(inputs: linkerInputs, + explicitModulePlanner: explicitModulePlanner) addJob(linkJ) guard targetTriple.isDarwin else { return } @@ -673,26 +693,6 @@ extension Driver { addJob(try verifyDebugInfoJob(inputs: dsymJob.outputs)) } } - - /// Prescan the source files to produce a module dependency graph and turn it into a set - /// of jobs required to build all dependencies. - /// Preprocess the graph by resolving placeholder dependencies, if any are present and - /// by re-scanning all Clang modules against all possible targets they will be built against. - public mutating func generateExplicitModuleDependenciesJobs(dependencyGraph: InterModuleDependencyGraph) - throws -> [Job] { - // Plan build jobs for all direct and transitive module dependencies of the current target - explicitDependencyBuildPlanner = - try ExplicitDependencyBuildPlanner(dependencyGraph: dependencyGraph, - toolchain: toolchain, - dependencyOracle: interModuleDependencyOracle, - integratedDriver: integratedDriver, - supportsExplicitInterfaceBuild: - isFrontendArgSupported(.explicitInterfaceModuleBuild), - cas: cas, - prefixMap: prefixMapping) - - return try explicitDependencyBuildPlanner!.generateExplicitModuleDependenciesBuildJobs() - } } /// MARK: Planning @@ -747,7 +747,7 @@ extension Driver { /// Plan a build by producing a set of jobs to complete the build. /// Should be private, but compiler bug /*private*/ mutating func planPossiblyIncrementalBuild() throws - -> ([Job], IncrementalCompilationState?, InterModuleDependencyGraph?) { + -> ([Job], IncrementalCompilationState?, ExplicitDependencyBuildPlanner?) { if let job = try immediateForwardingJob() { return ([job], nil, nil) @@ -773,13 +773,11 @@ extension Driver { case .immediate: var jobs: [Job] = [] // Run the dependency scanner if this is an explicit module build - let moduleDependencyGraph = - try parsedOptions.contains(.driverExplicitModuleBuild) ? - gatherModuleDependencies() : nil - try addPrecompileModuleDependenciesJobs(dependencyGraph: moduleDependencyGraph, - initialIncrementalState: nil, + let explicitModulePlanner = parsedOptions.contains(.driverExplicitModuleBuild) ? try configureExplicitModulePlanner() : nil + try addPrecompileModuleDependenciesJobs(explicitModulePlanner: explicitModulePlanner, + incrementalRemarks: false, addJob: { jobs.append($0) }) - jobs.append(try interpretJob(inputs: inputFiles)) + jobs.append(try interpretJob(inputs: inputFiles, explicitModulePlanner: explicitModulePlanner)) return (jobs, nil, nil) case .standardCompile, .batchCompile, .singleCompile: @@ -789,13 +787,15 @@ extension Driver { if inputFiles.count != 1 { throw PlanningError.emitPCMWrongInputFiles } - return ([try generateEmitPCMJob(input: inputFiles.first!)], nil, nil) + return ([try generateEmitPCMJob(input: inputFiles.first!, + explicitModulePlanner: nil)], nil, nil) case .dumpPCM: if inputFiles.count != 1 { throw PlanningError.dumpPCMWrongInputFiles } - return ([try generateDumpPCMJob(input: inputFiles.first!)], nil, nil) + return ([try generateDumpPCMJob(input: inputFiles.first!, + explicitModulePlanner: nil)], nil, nil) case .intro: return (try helpIntroJobs(), nil, nil) } @@ -821,7 +821,8 @@ extension Driver { /// /// So, in order to avoid making jobs and rebatching, the code would have to just get outputs for each /// compilation. But `compileJob` intermixes the output computation with other stuff. - mutating func formBatchedJobs(_ jobs: [Job], showJobLifecycle: Bool, jobCreatingPch: Job?) throws -> [Job] { + mutating func formBatchedJobs(_ jobs: [Job], showJobLifecycle: Bool, jobCreatingPch: Job?, + explicitModulePlanner: ExplicitDependencyBuildPlanner?) throws -> [Job] { guard compilerMode.isBatchCompile else { // Don't even go through the logic so as to not print out confusing // "batched foobar" messages. @@ -876,7 +877,8 @@ extension Driver { addJobOutputs: {_ in }, pchCompileJob: jobCreatingPch, emitModuleTrace: constituentsEmittedModuleTrace, - produceCacheKey: true) + produceCacheKey: true, + explicitModulePlanner: explicitModulePlanner) } return batchedCompileJobs + noncompileJobs } diff --git a/Sources/SwiftDriver/Jobs/VerifyModuleInterfaceJob.swift b/Sources/SwiftDriver/Jobs/VerifyModuleInterfaceJob.swift index 1ecb9bd6c..3ba4009a1 100644 --- a/Sources/SwiftDriver/Jobs/VerifyModuleInterfaceJob.swift +++ b/Sources/SwiftDriver/Jobs/VerifyModuleInterfaceJob.swift @@ -30,12 +30,16 @@ extension Driver { return isFrontendArgSupported(.inputFileKey) } - mutating func verifyModuleInterfaceJob(interfaceInput: TypedVirtualPath, emitModuleJob: Job, reportAsError: Bool) throws -> Job { + mutating func verifyModuleInterfaceJob(interfaceInput: TypedVirtualPath, emitModuleJob: Job, reportAsError: Bool, + explicitModulePlanner: ExplicitDependencyBuildPlanner?, + forVariantModule: Bool) throws -> Job { var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) } var inputs: [TypedVirtualPath] = [interfaceInput] commandLine.appendFlags("-frontend", "-typecheck-module-from-interface") try addPathArgument(interfaceInput.file, to: &commandLine) - try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .verifyModuleInterface, bridgingHeaderHandling: .ignored) + try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .verifyModuleInterface, bridgingHeaderHandling: .ignored, + explicitModulePlanner: explicitModulePlanner, + forVariantEmitModule: forVariantModule) try addRuntimeLibraryFlags(commandLine: &commandLine) // Output serialized diagnostics for this job, if specifically requested diff --git a/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift b/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift index 4e2cb06c2..b91b122d4 100644 --- a/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift +++ b/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift @@ -27,7 +27,7 @@ internal extension SwiftScan { throw DependencyScanningError.missingField("dependency_graph.dependencies") } - var resultGraph = InterModuleDependencyGraph(mainModuleName: mainModuleName) + var moduleInfoMap: ModuleInfoMap = [:] // Turn the `swiftscan_dependency_set_t` into an array of `swiftscan_dependency_info_t` // references we can iterate through in order to construct `ModuleInfo` objects. let moduleRefArray = Array(UnsafeBufferPointer(start: dependencySetRef.pointee.modules, @@ -37,10 +37,10 @@ internal extension SwiftScan { throw DependencyScanningError.missingField("dependency_set_t.modules[_]") } let (moduleId, moduleInfo) = try constructModuleInfo(from: moduleRef, moduleAliases: moduleAliases) - resultGraph.modules[moduleId] = moduleInfo + moduleInfoMap[moduleId] = moduleInfo } - return resultGraph + return InterModuleDependencyGraph(mainModuleName: mainModuleName, modules: moduleInfoMap) } /// From a reference to a binary-format set of module imports return by libSwiftScan pre-scan query, diff --git a/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift b/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift index 2382c5798..3a96ed770 100644 --- a/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift +++ b/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift @@ -395,8 +395,57 @@ public final class DarwinToolchain: Toolchain { driver: inout Driver, skipMacroOptions: Bool ) throws { - guard let sdkPath = frontendTargetInfo.sdkPath?.path, - let sdkInfo = getTargetSDKInfo(sdkPath: sdkPath) else { return } + let sdkPath = frontendTargetInfo.sdkPath?.path + let sdkInfo: DarwinSDKInfo? = sdkPath != nil ? getTargetSDKInfo(sdkPath: sdkPath!) : nil + + // Pass down -clang-target. + // If not specified otherwise, we should use the same triple as -target + if !driver.parsedOptions.hasArgument(.disableClangTarget) && + driver.isFrontendArgSupported(.clangTarget) && + driver.parsedOptions.contains(.driverExplicitModuleBuild) { + // The common target triple for all Clang dependencies of this compilation, + // both direct and transitive is computed as: + // 1. An explicitly-specified `-clang-target` argument to this driver invocation + // 2. (On Darwin) The target triple of the selected SDK + var clangTargetTriple: String? = nil + if let explicitClangTripleArg = driver.parsedOptions.getLastArgument(.clangTarget)?.asSingle { + clangTargetTriple = explicitClangTripleArg + } else if let sdkInfo = sdkInfo { + let currentTriple = frontendTargetInfo.target.triple + let sdkVersionedOSString = currentTriple.osNameUnversioned + sdkInfo.sdkVersion(for: currentTriple).sdkVersionString + clangTargetTriple = currentTriple.triple.replacingOccurrences(of: currentTriple.osName, with: sdkVersionedOSString) + } + + if let clangTargetTriple { + commandLine.appendFlag(.clangTarget) + commandLine.appendFlag(clangTargetTriple) + } + + // Repeat the above for the '-target-variant' flag + if driver.parsedOptions.contains(.targetVariant), + driver.isFrontendArgSupported(.clangTargetVariant), + let targetVariantTripleStr = frontendTargetInfo.targetVariant?.triple + { + var clangTargetVariantTriple: String? = nil + if let explicitClangTargetVariantArg = driver.parsedOptions.getLastArgument(.clangTargetVariant)?.asSingle { + clangTargetVariantTriple = explicitClangTargetVariantArg + } else if let sdkInfo { + let currentVariantTriple = targetVariantTripleStr + let sdkVersionedOSSString = + currentVariantTriple.osNameUnversioned + + sdkInfo.sdkVersion(for: currentVariantTriple).sdkVersionString + clangTargetVariantTriple = currentVariantTriple.triple.replacingOccurrences( + of: currentVariantTriple.osName, with: sdkVersionedOSSString) + } + + if let clangTargetVariantTriple { + commandLine.appendFlag(.clangTargetVariant) + commandLine.appendFlag(clangTargetVariantTriple) + } + } + } + + guard let sdkPath, let sdkInfo else { return } commandLine.append(.flag("-target-sdk-version")) commandLine.append(.flag(sdkInfo.sdkVersion(for: frontendTargetInfo.target.triple).sdkVersionString)) @@ -439,45 +488,6 @@ public final class DarwinToolchain: Toolchain { commandLine.appendPath(prebuiltModulesPath) } - // Pass down -clang-target. - // If not specified otherwise, we should use the same triple as -target - if !driver.parsedOptions.hasArgument(.disableClangTarget) && - driver.isFrontendArgSupported(.clangTarget) && - driver.parsedOptions.contains(.driverExplicitModuleBuild) { - // The common target triple for all Clang dependencies of this compilation, - // both direct and transitive is computed as: - // 1. An explicitly-specified `-clang-target` argument to this driver invocation - // 2. (On Darwin) The target triple of the selected SDK - let clangTargetTriple: String - if let explicitClangTripleArg = driver.parsedOptions.getLastArgument(.clangTarget)?.asSingle { - clangTargetTriple = explicitClangTripleArg - } else { - let currentTriple = frontendTargetInfo.target.triple - let sdkVersionedOSString = currentTriple.osNameUnversioned + sdkInfo.sdkVersion(for: currentTriple).sdkVersionString - clangTargetTriple = currentTriple.triple.replacingOccurrences(of: currentTriple.osName, with: sdkVersionedOSString) - } - - commandLine.appendFlag(.clangTarget) - commandLine.appendFlag(clangTargetTriple) - - // Repeat the above for the '-target-variant' flag - if driver.parsedOptions.contains(.targetVariant), - driver.isFrontendArgSupported(.clangTargetVariant), - let targetVariantTripleStr = frontendTargetInfo.targetVariant?.triple { - let clangTargetVariantTriple: String - if let explicitClangTargetVariantArg = driver.parsedOptions.getLastArgument(.clangTargetVariant)?.asSingle { - clangTargetVariantTriple = explicitClangTargetVariantArg - } else { - let currentVariantTriple = targetVariantTripleStr - let sdkVersionedOSSString = currentVariantTriple.osNameUnversioned + sdkInfo.sdkVersion(for: currentVariantTriple).sdkVersionString - clangTargetVariantTriple = currentVariantTriple.triple.replacingOccurrences(of: currentVariantTriple.osName, with: sdkVersionedOSSString) - } - - commandLine.appendFlag(.clangTargetVariant) - commandLine.appendFlag(clangTargetVariantTriple) - } - } - if driver.isFrontendArgSupported(.externalPluginPath) && !skipMacroOptions { // If the PLATFORM_DIR environment variable is set, also add plugin // paths into it. Since this is a user override, it comes beore the diff --git a/Sources/SwiftDriver/Utilities/VirtualPath.swift b/Sources/SwiftDriver/Utilities/VirtualPath.swift index 665147850..6746d11d1 100644 --- a/Sources/SwiftDriver/Utilities/VirtualPath.swift +++ b/Sources/SwiftDriver/Utilities/VirtualPath.swift @@ -47,6 +47,10 @@ public enum VirtualPath: Hashable { /// Standard output case standardOutput + /// A file with a known absolute path and contents computed by + /// the driver, it gets written to the filesystem at resolution time + case buildArtifactWithKnownContents(AbsolutePath, Data) + /// We would like to direct clients to use the temporary file creation utilities `createUniqueTemporaryFile`, etc. /// To ensure temporary files are unique. /// TODO: If/When Swift gains enum access control, we can prohibit direct instantiation of temporary file cases, @@ -72,6 +76,8 @@ public enum VirtualPath: Hashable { case .relative(let path), .temporary(let path), .temporaryWithKnownContents(let path, _), .fileList(let path, _): return path.extension + case .buildArtifactWithKnownContents(let path, _): + return path.extension case .absolute(let path): return path.extension case .standardInput, .standardOutput: @@ -82,7 +88,7 @@ public enum VirtualPath: Hashable { /// Whether this virtual path is to a temporary. public var isTemporary: Bool { switch self { - case .relative, .absolute, .standardInput, .standardOutput: + case .relative, .absolute, .standardInput, .standardOutput, .buildArtifactWithKnownContents: return false case .temporary, .temporaryWithKnownContents, .fileList: return true @@ -91,7 +97,7 @@ public enum VirtualPath: Hashable { public var absolutePath: AbsolutePath? { switch self { - case let .absolute(absolutePath): + case .absolute(let absolutePath), .buildArtifactWithKnownContents(let absolutePath, _): return absolutePath case .relative, .temporary, .temporaryWithKnownContents, .fileList, .standardInput, .standardOutput: return nil @@ -111,7 +117,7 @@ public enum VirtualPath: Hashable { .fileList(let name, _), .temporaryWithKnownContents(let name, _): return name - case .absolute, .relative, .standardInput, .standardOutput: + case .absolute, .relative, .standardInput, .standardOutput, .buildArtifactWithKnownContents: return nil } } @@ -119,7 +125,7 @@ public enum VirtualPath: Hashable { /// Retrieve the basename of the path. public var basename: String { switch self { - case .absolute(let path): + case .absolute(let path), .buildArtifactWithKnownContents(let path, _): return path.basename case .relative(let path), .temporary(let path), .temporaryWithKnownContents(let path, _), .fileList(let path, _): return path.basename @@ -131,7 +137,7 @@ public enum VirtualPath: Hashable { /// Retrieve the basename of the path without the extension. public var basenameWithoutExt: String { switch self { - case .absolute(let path): + case .absolute(let path), .buildArtifactWithKnownContents(let path, _): return path.basenameWithoutExt case .relative(let path), .temporary(let path), .temporaryWithKnownContents(let path, _), .fileList(let path, _): return path.basenameWithoutExt @@ -143,7 +149,7 @@ public enum VirtualPath: Hashable { /// Retrieve the path to the parent directory. public var parentDirectory: VirtualPath { switch self { - case .absolute(let path): + case .absolute(let path), .buildArtifactWithKnownContents(let path, _): return .absolute(path.parentDirectory) case .relative(let path): return .relative(try! RelativePath(validating: path.dirname)) @@ -162,7 +168,7 @@ public enum VirtualPath: Hashable { /// This should not be used with `.standardInput` or `.standardOutput`. public func appending(component: String) -> VirtualPath { switch self { - case .absolute(let path): + case .absolute(let path), .buildArtifactWithKnownContents(let path, _): return .absolute(path.appending(component: component)) case .relative(let path): return .relative(path.appending(component: component)) @@ -180,7 +186,7 @@ public enum VirtualPath: Hashable { public func appending(components: String...) -> VirtualPath { switch self { - case .absolute(let path): + case .absolute(let path), .buildArtifactWithKnownContents(let path, _): return .absolute(path.appending(components: components)) case .relative(let path): return .relative(path.appending(components: components)) @@ -201,7 +207,7 @@ public enum VirtualPath: Hashable { /// This should not be used with `.standardInput` or `.standardOutput`. public func appendingToBaseName(_ suffix: String) throws -> VirtualPath { switch self { - case let .absolute(path): + case let .absolute(path), .buildArtifactWithKnownContents(let path, _): return .absolute(try AbsolutePath(validating: path.pathString + suffix)) case let .relative(path): return .relative(try RelativePath(validating: path.pathString + suffix)) @@ -297,6 +303,8 @@ extension VirtualPath { return path.pathString case .absolute(let path): return path.pathString + case .buildArtifactWithKnownContents(let path, _): + return "buildArtifactWithKnownContents:" + path.pathString case .temporary(let path): // N.B. Mangle in a discrimintor for temporaries so they intern apart // from normal kinds of paths. @@ -429,6 +437,13 @@ extension VirtualPath { } } +extension VirtualPath { + public static func createBuildProductFileWithKnownContents(_ path: AbsolutePath, _ data: Data) + throws -> VirtualPath { + return .buildArtifactWithKnownContents(path, data) + } +} + // MARK: Temporary File Creation /// Most client contexts require temporary files they request to be unique (e.g. auxiliary compile outputs). @@ -511,7 +526,7 @@ extension VirtualPath.Handle: Hashable {} extension VirtualPath: Codable { private enum CodingKeys: String, CodingKey { case relative, absolute, standardInput, standardOutput, temporary, - temporaryWithKnownContents, fileList + temporaryWithKnownContents, buildProductWithKnownContents, fileList } public func encode(to encoder: Encoder) throws { @@ -534,6 +549,10 @@ extension VirtualPath: Codable { var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .temporaryWithKnownContents) try unkeyedContainer.encode(path) try unkeyedContainer.encode(contents) + case let .buildArtifactWithKnownContents(path, contents): + var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .buildProductWithKnownContents) + try unkeyedContainer.encode(path) + try unkeyedContainer.encode(contents) case .fileList(let path, let fileList): var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .fileList) try unkeyedContainer.encode(path) @@ -568,6 +587,11 @@ extension VirtualPath: Codable { let path = try unkeyedValues.decode(RelativePath.self) let contents = try unkeyedValues.decode(Data.self) self = .temporaryWithKnownContents(path, contents) + case .buildProductWithKnownContents: + var unkeyedValues = try values.nestedUnkeyedContainer(forKey: key) + let path = try unkeyedValues.decode(AbsolutePath.self) + let contents = try unkeyedValues.decode(Data.self) + self = .buildArtifactWithKnownContents(path, contents) case .fileList: var unkeyedValues = try values.nestedUnkeyedContainer(forKey: key) let path = try unkeyedValues.decode(RelativePath.self) @@ -593,7 +617,7 @@ public struct TextualVirtualPath: Codable, Hashable { public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch VirtualPath.lookup(self.path) { - case .absolute(let path): + case .absolute(let path), .buildArtifactWithKnownContents(let path, _): try container.encode(path.pathString) case .relative(let path): try container.encode(path.pathString) @@ -612,7 +636,7 @@ extension VirtualPath: CustomStringConvertible { case .relative(let path): return path.pathString - case .absolute(let path): + case .absolute(let path), .buildArtifactWithKnownContents(let path, _): return path.pathString case .standardInput, .standardOutput: @@ -632,6 +656,8 @@ extension VirtualPath: CustomDebugStringConvertible { return ".relative(\(path.pathString))" case .absolute(let path): return ".absolute(\(path.pathString))" + case .buildArtifactWithKnownContents(let path, _): + return "buildProductWithKnownContents(\(path.pathString))" case .standardInput: return ".standardInput" case .standardOutput: @@ -653,6 +679,8 @@ extension VirtualPath { switch self { case let .absolute(path): return .absolute(try AbsolutePath(validating: path.pathString.withoutExt(path.extension).appendingFileTypeExtension(fileType))) + case let .buildArtifactWithKnownContents(path, content): + return .buildArtifactWithKnownContents(try AbsolutePath(validating: path.pathString.withoutExt(path.extension).appendingFileTypeExtension(fileType)), content) case let .relative(path): return .relative(try RelativePath(validating: path.pathString.withoutExt(path.extension).appendingFileTypeExtension(fileType))) case let .temporary(path): @@ -700,6 +728,8 @@ extension TSCBasic.FileSystem { switch path { case let .absolute(absPath): return try f(absPath) + case let .buildArtifactWithKnownContents(absPath, _): + return try f(absPath) case let .relative(relPath): guard let cwd = currentWorkingDirectory else { throw FileSystemError.noCurrentWorkingDirectory diff --git a/TestInputs/mock-sdk.sdk/SDKSettings.json b/TestInputs/mock-sdk.sdk/SDKSettings.json index 83d658d17..040b56a51 100644 --- a/TestInputs/mock-sdk.sdk/SDKSettings.json +++ b/TestInputs/mock-sdk.sdk/SDKSettings.json @@ -1,8 +1,8 @@ { "Version": "10.15", "VersionMap": { - "macOS_iOSMac": {}, - "iOSMac_macOS": {} + "macOS_iOSMac": {"10.15":"13.1"}, + "iOSMac_macOS": {"13.1":"10.15"} }, "CanonicalName": "macosx10.15" } diff --git a/Tests/SwiftDriverTests/CachingBuildTests.swift b/Tests/SwiftDriverTests/CachingBuildTests.swift index 4ae722949..d56c193f4 100644 --- a/Tests/SwiftDriverTests/CachingBuildTests.swift +++ b/Tests/SwiftDriverTests/CachingBuildTests.swift @@ -246,7 +246,7 @@ final class CachingBuildTests: XCTestCase { main.nativePathString(escaped: true)] + sdkArgumentsForTesting) let jobs = try driver.planBuild() - let dependencyGraph = try driver.gatherModuleDependencies() + let dependencyGraph = try driver.scanModuleDependencies() let mainModuleInfo = try dependencyGraph.moduleInfo(of: .swift("testCachingBuildJobs")) guard case .swift(_) = mainModuleInfo.details else { XCTFail("Main module does not have Swift details field") @@ -502,7 +502,7 @@ final class CachingBuildTests: XCTestCase { let jobs = try driver.planBuild() // Figure out which Triples to use. - let dependencyGraph = try driver.gatherModuleDependencies() + let dependencyGraph = try driver.scanModuleDependencies() let mainModuleInfo = try dependencyGraph.moduleInfo(of: .swift("testExplicitModuleVerifyInterfaceJobs")) guard case .swift(_) = mainModuleInfo.details else { XCTFail("Main module does not have Swift details field") @@ -684,10 +684,9 @@ final class CachingBuildTests: XCTestCase { "-working-directory", path.nativePathString(escaped: true), foo.nativePathString(escaped: true), "-emit-module", "-wmo", "-module-name", "Foo", - "-emit-module-path", FooInstallPath.nativePathString(escaped: true), + "-emit-module-path", FooInstallPath.appending(component: "Foo.swiftmodule").nativePathString(escaped: true), "-import-objc-header", fooHeader.nativePathString(escaped: true), - "-pch-output-dir", PCHPath.nativePathString(escaped: true), - FooInstallPath.appending(component: "Foo.swiftmodule").nativePathString(escaped: true)] + "-pch-output-dir", PCHPath.nativePathString(escaped: true)] + sdkArgumentsForTesting, interModuleDependencyOracle: dependencyOracle) diff --git a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index 4d420fc59..62aad7e5b 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -169,19 +169,18 @@ func getStdlibShimsPaths(_ driver: Driver) throws -> (AbsolutePath, AbsolutePath final class ExplicitModuleBuildTests: XCTestCase { func testModuleDependencyBuildCommandGeneration() throws { do { - var driver = try Driver(args: ["swiftc", "-explicit-module-build", + let driver = try Driver(args: ["swiftc", "-explicit-module-build", "-module-name", "testModuleDependencyBuildCommandGeneration", "test.swift"]) let moduleDependencyGraph = try JSONDecoder().decode( InterModuleDependencyGraph.self, from: ModuleDependenciesInputs.fastDependencyScannerOutput.data(using: .utf8)!) - driver.explicitDependencyBuildPlanner = + var explicitDependencyBuildPlanner = try ExplicitDependencyBuildPlanner(dependencyGraph: moduleDependencyGraph, - toolchain: driver.toolchain, - dependencyOracle: driver.interModuleDependencyOracle) + toolchain: driver.toolchain) let modulePrebuildJobs = - try driver.explicitDependencyBuildPlanner!.generateExplicitModuleDependenciesBuildJobs() + try explicitDependencyBuildPlanner.generateExplicitModuleDependenciesBuildJobs() XCTAssertEqual(modulePrebuildJobs.count, 4) for job in modulePrebuildJobs { XCTAssertEqual(job.outputs.count, 1) @@ -219,56 +218,6 @@ final class ExplicitModuleBuildTests: XCTestCase { } } - func testModuleDependencyBuildCommandGenerationWithExternalFramework() throws { - do { - let externalDetails: ExternalTargetModuleDetailsMap = - [.swiftPrebuiltExternal("A"): ExternalTargetModuleDetails(path: try AbsolutePath(validating: "/tmp/A.swiftmodule"), - isFramework: true), - .swiftPrebuiltExternal("K"): ExternalTargetModuleDetails(path: try AbsolutePath(validating: "/tmp/K.swiftmodule"), - isFramework: true), - .swiftPrebuiltExternal("simpleTestModule"): ExternalTargetModuleDetails(path: try AbsolutePath(validating: "/tmp/simpleTestModule.swiftmodule"), - isFramework: true)] - var driver = try Driver(args: ["swiftc", "-explicit-module-build", - "-module-name", "simpleTestModule", - "test.swift"]) - var moduleDependencyGraph = - try JSONDecoder().decode( - InterModuleDependencyGraph.self, - from: ModuleDependenciesInputs.simpleDependencyGraphInput.data(using: .utf8)!) - // Key part of this test, using the external info to generate dependency pre-build jobs - try moduleDependencyGraph.resolveExternalDependencies(for: externalDetails) - - // Ensure the main module was not overriden by an external dependency - XCTAssertNotNil(moduleDependencyGraph.modules[.swift("simpleTestModule")]) - - // Ensure the "K" module's framework status got resolved via `externalDetails` - guard case .swiftPrebuiltExternal(let kPrebuiltDetails) = moduleDependencyGraph.modules[.swiftPrebuiltExternal("K")]?.details else { - XCTFail("Expected prebuilt module details for module \"K\"") - return - } - XCTAssertTrue(kPrebuiltDetails.isFramework) - let jobsInPhases = try driver.computeJobsForPhasedStandardBuild(moduleDependencyGraph: moduleDependencyGraph) - let job = try XCTUnwrap(jobsInPhases.allJobs.first(where: { $0.kind == .compile })) - // Load the dependency JSON and verify this dependency was encoded correctly - XCTAssertJobInvocationMatches(job, .flag("-explicit-swift-module-map-file")) - let jsonDepsPathIndex = try XCTUnwrap(job.commandLine.firstIndex(of: .flag("-explicit-swift-module-map-file"))) - let jsonDepsPathArg = job.commandLine[jsonDepsPathIndex + 1] - guard case .path(let jsonDepsPath) = jsonDepsPathArg else { - return XCTFail("No JSON dependency file path found.") - } - guard case let .temporaryWithKnownContents(_, contents) = jsonDepsPath else { - return XCTFail("Unexpected path type") - } - let dependencyInfoList = try JSONDecoder().decode(Array.self, - from: contents) - XCTAssertEqual(dependencyInfoList.count, 2) - let dependencyArtifacts = - dependencyInfoList.first(where:{ $0.moduleName == "A" })! - // Ensure this is a framework, as specified by the externalDetails above. - XCTAssertEqual(dependencyArtifacts.isFramework, true) - } - } - func testModuleDependencyBuildCommandUniqueDepFile() throws { let (stdlibPath, shimsPath, _, _) = try getDriverArtifactsForScanning() try withTemporaryDirectory { path in @@ -430,10 +379,9 @@ final class ExplicitModuleBuildTests: XCTestCase { "-working-directory", path.nativePathString(escaped: true), foo.nativePathString(escaped: true), "-emit-module", "-wmo", "-module-name", "Foo", - "-emit-module-path", FooInstallPath.nativePathString(escaped: true), + "-emit-module-path", FooInstallPath.appending(component: "Foo.swiftmodule").nativePathString(escaped: true), "-import-objc-header", fooHeader.nativePathString(escaped: true), - "-pch-output-dir", PCHPath.nativePathString(escaped: true), - FooInstallPath.appending(component: "Foo.swiftmodule").nativePathString(escaped: true)] + "-pch-output-dir", PCHPath.nativePathString(escaped: true)] + sdkArgumentsForTesting) let fooJobs = try fooBuildDriver.planBuild() @@ -540,9 +488,8 @@ final class ExplicitModuleBuildTests: XCTestCase { if driver.isFrontendArgSupported(.scannerModuleValidation) { driver = try Driver(args: args + ["-scanner-module-validation"]) } - let _ = try driver.planBuild() - let dependencyGraph = try XCTUnwrap(driver.explicitDependencyBuildPlanner?.dependencyGraph) + let dependencyGraph = try XCTUnwrap(driver.intermoduleDependencyGraph) let checkForLinkLibrary = { (info: ModuleInfo, linkName: String, isFramework: Bool, shouldForceLoad: Bool) in let linkLibraries = try XCTUnwrap(info.linkLibraries) @@ -624,7 +571,7 @@ final class ExplicitModuleBuildTests: XCTestCase { let jobs = try driver.planBuild() // Figure out which Triples to use. - let dependencyGraph = try driver.gatherModuleDependencies() + let dependencyGraph = try driver.scanModuleDependencies() let mainModuleInfo = try dependencyGraph.moduleInfo(of: .swift("testExplicitModuleBuildJobs")) guard case .swift(_) = mainModuleInfo.details else { XCTFail("Main module does not have Swift details field") @@ -896,7 +843,7 @@ final class ExplicitModuleBuildTests: XCTestCase { } let jobs = try driver.planBuild() // Figure out which Triples to use. - let dependencyGraph = try driver.gatherModuleDependencies() + let dependencyGraph = try driver.scanModuleDependencies() let mainModuleInfo = try dependencyGraph.moduleInfo(of: .swift("testExplicitModuleVerifyInterfaceJobs")) guard case .swift(_) = mainModuleInfo.details else { XCTFail("Main module does not have Swift details field") @@ -1031,7 +978,7 @@ final class ExplicitModuleBuildTests: XCTestCase { let jobs = try driver.planBuild() // Figure out which Triples to use. - let dependencyGraph = try driver.gatherModuleDependencies() + let dependencyGraph = try driver.scanModuleDependencies() let mainModuleInfo = try dependencyGraph.moduleInfo(of: .swift("testExplicitModuleBuildPCHOutputJobs")) guard case .swift(_) = mainModuleInfo.details else { XCTFail("Main module does not have Swift details field") @@ -1166,7 +1113,7 @@ final class ExplicitModuleBuildTests: XCTestCase { XCTAssertJobInvocationMatches(interpretJob, .flag("-Xcc"), .flag("-fno-implicit-modules")) // Figure out which Triples to use. - let dependencyGraph = try driver.gatherModuleDependencies() + let dependencyGraph = try driver.scanModuleDependencies() let mainModuleInfo = try dependencyGraph.moduleInfo(of: .swift("testExplicitModuleBuildJobs")) guard case .swift(_) = mainModuleInfo.details else { XCTFail("Main module does not have Swift details field") @@ -1299,7 +1246,7 @@ final class ExplicitModuleBuildTests: XCTestCase { ] + sdkArgumentsForTesting) // Resulting graph should contain the real module name Bar - let dependencyGraphA = try driverA.gatherModuleDependencies() + let dependencyGraphA = try driverA.scanModuleDependencies() XCTAssertTrue(dependencyGraphA.modules.contains { (key: ModuleDependencyId, value: ModuleInfo) in key.moduleName == "Bar" }) @@ -1326,7 +1273,7 @@ final class ExplicitModuleBuildTests: XCTestCase { ] + sdkArgumentsForTesting) // Resulting graph should contain the real module name Bar - let dependencyGraphB = try driverB.gatherModuleDependencies() + let dependencyGraphB = try driverB.scanModuleDependencies() XCTAssertTrue(dependencyGraphB.modules.contains { (key: ModuleDependencyId, value: ModuleInfo) in key.moduleName == "Bar" }) @@ -1373,7 +1320,7 @@ final class ExplicitModuleBuildTests: XCTestCase { } // Resulting graph should contain the real module name Bar - let dependencyGraphA = try driverA.gatherModuleDependencies() + let dependencyGraphA = try driverA.scanModuleDependencies() XCTAssertTrue(dependencyGraphA.modules.contains { (key: ModuleDependencyId, value: ModuleInfo) in key.moduleName == "E" }) @@ -1399,7 +1346,7 @@ final class ExplicitModuleBuildTests: XCTestCase { ] + sdkArgumentsForTesting) // Resulting graph should contain the real module name Bar - let dependencyGraphB = try driverB.gatherModuleDependencies() + let dependencyGraphB = try driverB.scanModuleDependencies() XCTAssertTrue(dependencyGraphB.modules.contains { (key: ModuleDependencyId, value: ModuleInfo) in key.moduleName == "E" }) @@ -2497,7 +2444,7 @@ final class ExplicitModuleBuildTests: XCTestCase { "-explicit-module-build", "-module-name", "Test", "-module-cache-path", moduleCachePath.nativePathString(escaped: true), "-working-directory", path.nativePathString(escaped: true), - "-emit-module", outputModule.nativePathString(escaped: true), + "-emit-module", "-emit-module-path", outputModule.nativePathString(escaped: true), "-experimental-emit-module-separately", fileA.nativePathString(escaped: true), fileB.nativePathString(escaped: true)] + sdkArgumentsForTesting) let jobs = try driver.planBuild() @@ -2510,6 +2457,303 @@ final class ExplicitModuleBuildTests: XCTestCase { } } + func testClangTargetOptionsExplicit() throws { + let (stdlibPath, shimsPath, _, _) = try getDriverArtifactsForScanning() + let cHeadersPath: AbsolutePath = + try testInputsPath.appending(component: "ExplicitModuleBuilds") + .appending(component: "CHeaders") + let swiftModuleInterfacesPath: AbsolutePath = + try testInputsPath.appending(component: "ExplicitModuleBuilds") + .appending(component: "Swift") + let mockSDKPath: AbsolutePath = + try testInputsPath.appending(component: "mock-sdk.sdk") + + // Only '-target' is specified, the driver infers '-clang-target' from SDK deployment target + do { + try withTemporaryDirectory { path in + let main = path.appending(component: "testDependencyScanning.swift") + try localFileSystem.writeFileContents(main, bytes: + """ + import A; + """ + ) + var driver = try Driver(args: ["swiftc", + "-target", "x86_64-apple-macosx10.10", + "-Xfrontend", "-disable-implicit-concurrency-module-import", + "-Xfrontend", "-disable-implicit-string-processing-module-import", + "-emit-module", + "-emit-module-path", "foo.swiftmodule/target.swiftmodule", + "-I", cHeadersPath.nativePathString(escaped: true), + "-I", swiftModuleInterfacesPath.nativePathString(escaped: true), + "-I", stdlibPath.nativePathString(escaped: true), + "-I", shimsPath.nativePathString(escaped: true), + "-explicit-module-build", + "-sdk", mockSDKPath.nativePathString(escaped: true), + main.pathString]) + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + let emitModuleJob = try XCTUnwrap(plannedJobs.findJobs(.emitModule).spm_only) + XCTAssertTrue(emitModuleJob.commandLine.contains(subsequence: [.flag("-sdk"), .path(.absolute(mockSDKPath))])) + XCTAssertTrue(emitModuleJob.commandLine.contains(subsequence: [.flag("-clang-target"), .flag("x86_64-apple-macosx10.15")])) + } + } + + // User-specified '-clang-target' + do { + try withTemporaryDirectory { path in + let main = path.appending(component: "testDependencyScanning.swift") + try localFileSystem.writeFileContents(main, bytes: + """ + import A; + """ + ) + var driver = try Driver(args: ["swiftc", + "-target", "x86_64-apple-macosx10.10", + "-clang-target", "x86_64-apple-macosx10.12", + "-Xfrontend", "-disable-implicit-concurrency-module-import", + "-Xfrontend", "-disable-implicit-string-processing-module-import", + "-emit-module", + "-emit-module-path", "foo.swiftmodule/target.swiftmodule", + "-I", cHeadersPath.nativePathString(escaped: true), + "-I", swiftModuleInterfacesPath.nativePathString(escaped: true), + "-I", stdlibPath.nativePathString(escaped: true), + "-I", shimsPath.nativePathString(escaped: true), + "-explicit-module-build", + "-sdk", mockSDKPath.nativePathString(escaped: true), + main.pathString]) + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + let emitModuleJob = try XCTUnwrap(plannedJobs.findJobs(.emitModule).spm_only) + XCTAssertTrue(emitModuleJob.commandLine.contains(subsequence: [.flag("-sdk"), .path(.absolute(mockSDKPath))])) + XCTAssertTrue(emitModuleJob.commandLine.contains(subsequence: [.flag("-clang-target"), .flag("x86_64-apple-macosx10.12")])) + } + } + + // Only '-target' and '-target-variant' is specified, the driver infers '-clang-target' from SDK deployment target + // and '-clang-target-variant' form the + do { + try withTemporaryDirectory { path in + let main = path.appending(component: "testDependencyScanning.swift") + try localFileSystem.writeFileContents(main, bytes: + """ + import A; + """ + ) + var driver = try Driver(args: ["swiftc", + "-target", "x86_64-apple-macosx10.10", + "-target-variant", "x86_64-apple-ios13.0-macabi", + "-Xfrontend", "-disable-implicit-concurrency-module-import", + "-Xfrontend", "-disable-implicit-string-processing-module-import", + "-emit-module", + "-emit-module-path", "foo.swiftmodule/target.swiftmodule", + "-I", cHeadersPath.nativePathString(escaped: true), + "-I", swiftModuleInterfacesPath.nativePathString(escaped: true), + "-I", stdlibPath.nativePathString(escaped: true), + "-I", shimsPath.nativePathString(escaped: true), + "-explicit-module-build", + "-sdk", mockSDKPath.nativePathString(escaped: true), + main.pathString]) + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + let emitModuleJob = try XCTUnwrap(plannedJobs.findJobs(.emitModule).spm_only) + XCTAssertTrue(emitModuleJob.commandLine.contains(subsequence: [.flag("-sdk"), .path(.absolute(mockSDKPath))])) + XCTAssertTrue(emitModuleJob.commandLine.contains(subsequence: [.flag("-clang-target"), .flag("x86_64-apple-macosx10.15")])) + XCTAssertTrue(emitModuleJob.commandLine.contains(subsequence: [.flag("-clang-target-variant"), .flag("x86_64-apple-ios13.1-macabi")])) + } + } + + // User-specified '-clang-target' and '-clang-target-variant' + do { + try withTemporaryDirectory { path in + let main = path.appending(component: "testDependencyScanning.swift") + try localFileSystem.writeFileContents(main, bytes: + """ + import A; + """ + ) + var driver = try Driver(args: ["swiftc", + "-target", "x86_64-apple-macosx10.10", + "-target-variant", "x86_64-apple-ios13.0-macabi", + "-clang-target", "x86_64-apple-macosx10.12", + "-clang-target-variant", "x86_64-apple-ios14.0-macabi", + "-Xfrontend", "-disable-implicit-concurrency-module-import", + "-Xfrontend", "-disable-implicit-string-processing-module-import", + "-emit-module", + "-emit-module-path", "foo.swiftmodule/target.swiftmodule", + "-I", cHeadersPath.nativePathString(escaped: true), + "-I", swiftModuleInterfacesPath.nativePathString(escaped: true), + "-I", stdlibPath.nativePathString(escaped: true), + "-I", shimsPath.nativePathString(escaped: true), + "-explicit-module-build", + "-sdk", mockSDKPath.nativePathString(escaped: true), + main.pathString]) + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + let emitModuleJob = try XCTUnwrap(plannedJobs.findJobs(.emitModule).spm_only) + XCTAssertTrue(emitModuleJob.commandLine.contains(subsequence: [.flag("-sdk"), .path(.absolute(mockSDKPath))])) + XCTAssertTrue(emitModuleJob.commandLine.contains(subsequence: [.flag("-clang-target"), .flag("x86_64-apple-macosx10.12")])) + XCTAssertTrue(emitModuleJob.commandLine.contains(subsequence: [.flag("-clang-target-variant"), .flag("x86_64-apple-ios14.0-macabi")])) + } + } + } + + func testTargetVariantEmitModuleExplicit() throws { + let (stdlibPath, shimsPath, _, _) = try getDriverArtifactsForScanning() + let cHeadersPath: AbsolutePath = + try testInputsPath.appending(component: "ExplicitModuleBuilds") + .appending(component: "CHeaders") + let swiftModuleInterfacesPath: AbsolutePath = + try testInputsPath.appending(component: "ExplicitModuleBuilds") + .appending(component: "Swift") + let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? [] + + // Ensure we produce two separate module precompilation task graphs + // one for the main triple, one for the variant triple + do { + try withTemporaryDirectory { path in + let main = path.appending(component: "testDependencyScanning.swift") + try localFileSystem.writeFileContents(main, bytes: + """ + import C;\ + import E;\ + import G; + """ + ) + var driver = try Driver(args: ["swiftc", + "-target", "x86_64-apple-macosx10.14", + "-target-variant", "x86_64-apple-ios13.1-macabi", + "-clang-target", "x86_64-apple-macosx12.14", + "-clang-target-variant", "x86_64-apple-ios15.1-macabi", + "-enable-library-evolution", "-emit-module", "-emit-module-interface", + "-emit-module-path", "foo.swiftmodule/target.swiftmodule", + "-emit-variant-module-path", "foo.swiftmodule/variant.swiftmodule", + "-emit-module-interface-path", "foo.swiftmodule/target.swiftinterface", + "-emit-variant-module-interface-path", "foo.swiftmodule/variant.swiftinterface", + "-Xfrontend", "-disable-implicit-concurrency-module-import", + "-Xfrontend", "-disable-implicit-string-processing-module-import", + "-I", cHeadersPath.nativePathString(escaped: true), + "-I", swiftModuleInterfacesPath.nativePathString(escaped: true), + "-I", stdlibPath.nativePathString(escaped: true), + "-I", shimsPath.nativePathString(escaped: true), + "-explicit-module-build", + main.pathString] + sdkArgumentsForTesting) + + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + let emitModuleJobs = try plannedJobs.findJobs(.emitModule) + let targetModuleJob = emitModuleJobs[0] + let variantModuleJob = emitModuleJobs[1] + + XCTAssert(targetModuleJob.commandLine.contains(.flag("-emit-module"))) + XCTAssert(variantModuleJob.commandLine.contains(.flag("-emit-module"))) + + XCTAssert(targetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.swiftdoc"))))) + XCTAssert(targetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.swiftsourceinfo"))))) + XCTAssert(targetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.abi.json"))))) + XCTAssertTrue(targetModuleJob.commandLine.contains(subsequence: [.flag("-o"), .path(.relative(try .init(validating: "foo.swiftmodule/target.swiftmodule")))])) + + XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftdoc"))))) + XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftsourceinfo"))))) + XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.abi.json"))))) + XCTAssertTrue(variantModuleJob.commandLine.contains(subsequence: [.flag("-o"), .path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftmodule")))])) + + let verifyModuleJobs = try plannedJobs.findJobs(.verifyModuleInterface) + let verifyTargetModuleJob = verifyModuleJobs[0] + let verifyVariantModuleJob = verifyModuleJobs[1] + XCTAssert(verifyTargetModuleJob.commandLine.contains(.flag("-typecheck-module-from-interface"))) + XCTAssert(verifyVariantModuleJob.commandLine.contains(.flag("-typecheck-module-from-interface"))) + + XCTAssert(verifyTargetModuleJob.commandLine.contains(.flag("-target"))) + XCTAssert(verifyTargetModuleJob.commandLine.contains(.flag("x86_64-apple-macosx10.14"))) + XCTAssert(verifyTargetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.swiftinterface"))))) + + XCTAssert(verifyVariantModuleJob.commandLine.contains(.flag("-target"))) + XCTAssert(verifyVariantModuleJob.commandLine.contains(.flag("x86_64-apple-ios13.1-macabi"))) + XCTAssert(verifyVariantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftinterface"))))) + + let interfaceCompilationJobs = try plannedJobs.findJobs(.compileModuleFromInterface) + let _ = try XCTUnwrap(interfaceCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("A")]) && + $0.commandLine.contains(subsequence: [.flag("-target"), .flag("x86_64-apple-macosx10.14")])}) + let _ = try XCTUnwrap(interfaceCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("A")]) && + $0.commandLine.contains(subsequence: [.flag("-target"), .flag("x86_64-apple-ios13.1-macabi")])}) + + let _ = try XCTUnwrap(interfaceCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("G")]) && + $0.commandLine.contains(subsequence: [.flag("-target"), .flag("x86_64-apple-macosx10.14")])}) + let _ = try XCTUnwrap(interfaceCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("G")]) && + $0.commandLine.contains(subsequence: [.flag("-target"), .flag("x86_64-apple-ios13.1-macabi")])}) + + let _ = try XCTUnwrap(interfaceCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("E")]) && + $0.commandLine.contains(subsequence: [.flag("-target"), .flag("x86_64-apple-macosx10.14")])}) + let _ = try XCTUnwrap(interfaceCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("E")]) && + $0.commandLine.contains(subsequence: [.flag("-target"), .flag("x86_64-apple-ios13.1-macabi")])}) + + let pcmCompilationJobs = try plannedJobs.findJobs(.generatePCM) + let _ = try XCTUnwrap(pcmCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("A")]) && + $0.commandLine.contains(subsequence: [.flag("-Xcc"), .flag("-triple"), .flag("-Xcc"), .flag("x86_64-apple-macosx12.14.0")]) && + !$0.commandLine.contains(.flag("-darwin-target-variant-triple"))}) + let _ = try XCTUnwrap(pcmCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("A")]) && + $0.commandLine.contains(subsequence: [.flag("-Xcc"), .flag("-darwin-target-variant-triple"), .flag("-Xcc"), .flag("x86_64-apple-ios15.1-macabi")])}) + + let _ = try XCTUnwrap(pcmCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("C")]) && + $0.commandLine.contains(subsequence: [.flag("-Xcc"), .flag("-triple"), .flag("-Xcc"), .flag("x86_64-apple-macosx12.14.0")]) && + !$0.commandLine.contains(.flag("-darwin-target-variant-triple"))}) + let _ = try XCTUnwrap(pcmCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("C")]) && + $0.commandLine.contains(subsequence: [.flag("-Xcc"), .flag("-darwin-target-variant-triple"), .flag("-Xcc"), .flag("x86_64-apple-ios15.1-macabi")])}) + + let _ = try XCTUnwrap(pcmCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("G")]) && + $0.commandLine.contains(subsequence: [.flag("-Xcc"), .flag("-triple"), .flag("-Xcc"), .flag("x86_64-apple-macosx12.14.0")]) && + !$0.commandLine.contains(.flag("-darwin-target-variant-triple"))}) + let _ = try XCTUnwrap(pcmCompilationJobs.first { $0.commandLine.contains(subsequence: [.flag("-module-name"), .flag("G")]) && + $0.commandLine.contains(subsequence: [.flag("-Xcc"), .flag("-darwin-target-variant-triple"), .flag("-Xcc"), .flag("x86_64-apple-ios15.1-macabi")])}) + } + } + + // Ensure each emit-module gets a distinct PCH file + do { + try withTemporaryDirectory { path in + let main = path.appending(component: "testDependencyScanning.swift") + try localFileSystem.writeFileContents(main, bytes: + """ + import C;\ + import E;\ + import G; + """ + ) + let PCHPath = path.appending(component: "PCH") + let fooHeader = path.appending(component: "foo.h") + try localFileSystem.writeFileContents(fooHeader) { + $0.send("struct Profiler { void* ptr; };") + } + var driver = try Driver(args: ["swiftc", + "-target", "x86_64-apple-macosx10.14", + "-target-variant", "x86_64-apple-ios13.1-macabi", + "-clang-target", "x86_64-apple-macosx12.14", + "-clang-target-variant", "x86_64-apple-ios15.1-macabi", + "-emit-module", + "-emit-module-path", "foo.swiftmodule/target.swiftmodule", + "-emit-variant-module-path", "foo.swiftmodule/variant.swiftmodule", + "-Xfrontend", "-disable-implicit-concurrency-module-import", + "-Xfrontend", "-disable-implicit-string-processing-module-import", + "-I", cHeadersPath.nativePathString(escaped: true), + "-I", swiftModuleInterfacesPath.nativePathString(escaped: true), + "-I", stdlibPath.nativePathString(escaped: true), + "-I", shimsPath.nativePathString(escaped: true), + "-import-objc-header", fooHeader.nativePathString(escaped: true), + "-pch-output-dir", PCHPath.nativePathString(escaped: true), + "-explicit-module-build", + main.pathString] + sdkArgumentsForTesting) + + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + let emitModuleJobs = try plannedJobs.findJobs(.emitModule) + let targetModuleJob = emitModuleJobs[0] + let variantModuleJob = emitModuleJobs[1] + + let pchJobs = try plannedJobs.findJobs(.generatePCH) + let pchTargetJob = try XCTUnwrap(pchJobs.first { $0.commandLine.contains(subsequence: [.flag("-Xcc"), .flag("-triple"), .flag("-Xcc"), .flag("x86_64-apple-macosx12.14.0")]) && + $0.commandLine.contains(subsequence: [.flag("-Xcc"), .flag("-darwin-target-variant-triple"), .flag("-Xcc"),.flag("x86_64-apple-ios15.1-macabi")])}) + let pchVariantJob = try XCTUnwrap(pchJobs.first { $0.commandLine.contains(subsequence: [.flag("-Xcc"), .flag("-triple"), .flag("-Xcc"), .flag("x86_64-apple-macosx12.14.0")]) && + !$0.commandLine.contains(.flag("-darwin-target-variant-triple"))}) + XCTAssertTrue(targetModuleJob.inputs.contains(try XCTUnwrap(pchTargetJob.outputs.first))) + XCTAssertTrue(variantModuleJob.inputs.contains(try XCTUnwrap(pchVariantJob.outputs.first))) + } + } + } + // We only care about prebuilt modules in macOS. #if os(macOS) func testPrebuiltModuleGenerationJobs() throws { diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index 95c435ae9..2d6215c69 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -1237,23 +1237,6 @@ final class SwiftDriverTests: XCTestCase { } } - func testMergeModuleEmittingDependencies() throws { - var driver1 = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Foo", "-emit-dependencies", "-emit-module", "-serialize-diagnostics", "-driver-filelist-threshold=9999", "-no-emit-module-separately"]) - let plannedJobs = try driver1.planBuild().removingAutolinkExtractJobs() - - XCTAssertEqual(plannedJobs[0].kind, .compile) - XCTAssertJobInvocationMatches(plannedJobs[0], .flag("-emit-dependencies-path")) - XCTAssertJobInvocationMatches(plannedJobs[0], .flag("-serialize-diagnostics-path")) - - XCTAssertEqual(plannedJobs[1].kind, .compile) - XCTAssertJobInvocationMatches(plannedJobs[1], .flag("-emit-dependencies-path")) - XCTAssertJobInvocationMatches(plannedJobs[1], .flag("-serialize-diagnostics-path")) - - XCTAssertEqual(plannedJobs[2].kind, .mergeModule) - XCTAssertFalse(plannedJobs[2].commandLine.contains(.flag("-emit-dependencies-path"))) - XCTAssertFalse(plannedJobs[2].commandLine.contains(.flag("-serialize-diagnostics-path"))) - } - func testEmitModuleEmittingDependencies() throws { var driver1 = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Foo", "-emit-dependencies", "-emit-module", "-serialize-diagnostics", "-driver-filelist-threshold=9999", "-experimental-emit-module-separately"]) let plannedJobs = try driver1.planBuild().removingAutolinkExtractJobs() @@ -1855,32 +1838,6 @@ final class SwiftDriverTests: XCTestCase { } } - // Compile + no separate emit module job - do { - let resolver = try ArgsResolver(fileSystem: localFileSystem) - var driver = try Driver( - args: ["swiftc", "-emit-module", "-no-emit-module-separately"] + manyArgs - + ["-module-name", "foo", "foo.swift", "bar.swift"]) - let jobs = try driver.planBuild().removingAutolinkExtractJobs() - XCTAssertEqual(jobs.count, 3) - XCTAssertEqual(Set(jobs.map { $0.kind }), Set([.compile, .mergeModule])) - - let mergeModuleJob = try jobs.findJob(.mergeModule) - let mergeModuleResolvedArgs: [String] = - try resolver.resolveArgumentList(for: mergeModuleJob) - XCTAssertEqual(mergeModuleResolvedArgs.count, 3) - XCTAssertEqual(mergeModuleResolvedArgs[2].first, "@") - - let compileJobs = jobs.filter { $0.kind == .compile } - XCTAssertEqual(compileJobs.count, 2) - for compileJob in compileJobs { - let compileResolvedArgs: [String] = - try resolver.resolveArgumentList(for: compileJob) - XCTAssertEqual(compileResolvedArgs.count, 3) - XCTAssertEqual(compileResolvedArgs[2].first, "@") - } - } - // Generate PCM (precompiled Clang module) job do { let resolver = try ArgsResolver(fileSystem: localFileSystem) @@ -3166,15 +3123,6 @@ final class SwiftDriverTests: XCTestCase { XCTAssertEqual(plannedJobs[0].kind, .emitModule) XCTAssertJobInvocationMatches(plannedJobs[0], .flag("-emit-abi-descriptor-path")) } - do { - var driver = try Driver(args: ["swiftc", "-module-name=ThisModule", "main.swift", "multi-threaded.swift", "-emit-module", "-o", "test.swiftmodule", "-no-emit-module-separately"]) - let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() - - XCTAssertEqual(plannedJobs.count, 3) - - XCTAssertEqual(plannedJobs[2].kind, .mergeModule) - XCTAssertJobInvocationMatches(plannedJobs[2], .flag("-emit-abi-descriptor-path")) - } } func testWMOWithNonSourceInput() throws { @@ -3463,98 +3411,6 @@ final class SwiftDriverTests: XCTestCase { } } - func testMergeModulesOnly() throws { - do { - var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module", "-disable-bridging-pch", "-import-objc-header", "TestInputHeader.h", "-emit-dependencies", "-emit-module-source-info-path", "/foo/bar/Test.swiftsourceinfo", "-no-emit-module-separately"]) - let plannedJobs = try driver.planBuild() - XCTAssertEqual(plannedJobs.count, 3) - XCTAssertEqual(Set(plannedJobs.map { $0.kind }), Set([.compile, .mergeModule])) - XCTAssertEqual(plannedJobs[0].outputs.count, 4) - - XCTAssertTrue(matchTemporary(plannedJobs[0].outputs[0].file, "foo.swiftmodule")) - XCTAssertTrue(matchTemporary(plannedJobs[0].outputs[1].file, "foo.swiftdoc")) - XCTAssertTrue(matchTemporary(plannedJobs[0].outputs[2].file, "foo.swiftsourceinfo")) - XCTAssertTrue(matchTemporary(plannedJobs[0].outputs[3].file, "foo.d")) - XCTAssert(plannedJobs[0].commandLine.contains(.flag("-import-objc-header"))) - - XCTAssertEqual(plannedJobs[1].outputs.count, 4) - XCTAssertTrue(matchTemporary(plannedJobs[1].outputs[0].file, "bar.swiftmodule")) - XCTAssertTrue(matchTemporary(plannedJobs[1].outputs[1].file, "bar.swiftdoc")) - XCTAssertTrue(matchTemporary(plannedJobs[1].outputs[2].file, "bar.swiftsourceinfo")) - XCTAssertTrue(matchTemporary(plannedJobs[1].outputs[3].file, "bar.d")) - XCTAssert(plannedJobs[1].commandLine.contains(.flag("-import-objc-header"))) - - XCTAssertTrue(plannedJobs[2].tool.name.contains("swift")) - XCTAssertEqual(plannedJobs[2].outputs.count, driver.targetTriple.isDarwin ? 4 : 3) - XCTAssertEqual(plannedJobs[2].outputs[0].file, try toPath("Test.swiftmodule")) - XCTAssertEqual(plannedJobs[2].outputs[1].file, try toPath("Test.swiftdoc")) - XCTAssertEqual(plannedJobs[2].outputs[2].file, .absolute(try .init(validating: "/foo/bar/Test.swiftsourceinfo"))) - if driver.targetTriple.isDarwin { - XCTAssertEqual(plannedJobs[2].outputs[3].file, try toPath("Test.abi.json")) - } - XCTAssert(plannedJobs[2].commandLine.contains(.flag("-import-objc-header"))) - } - - do { - let root = localFileSystem.currentWorkingDirectory!.appending(components: "foo", "bar") - - var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module-path", rebase("Test.swiftmodule", at: root), "-no-emit-module-separately"]) - let plannedJobs = try driver.planBuild() - XCTAssertEqual(plannedJobs.count, 3) - XCTAssertTrue(plannedJobs[2].tool.name.contains("swift")) - XCTAssertEqual(plannedJobs[2].outputs.count, driver.targetTriple.isDarwin ? 4 : 3) - XCTAssertEqual(plannedJobs[2].outputs[0].file, .absolute(try .init(validating: rebase("Test.swiftmodule", at: root)))) - XCTAssertEqual(plannedJobs[2].outputs[1].file, .absolute(try .init(validating: rebase("Test.swiftdoc", at: root)))) - XCTAssertEqual(plannedJobs[2].outputs[2].file, .absolute(try .init(validating: rebase("Test.swiftsourceinfo", at: root)))) - if driver.targetTriple.isDarwin { - XCTAssertEqual(plannedJobs[2].outputs[3].file, .absolute(try .init(validating: rebase("Test.abi.json", at: root)))) - } - } - - do { - // Make sure the swiftdoc path is correct for a relative module - var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module-path", "Test.swiftmodule", "-no-emit-module-separately"]) - let plannedJobs = try driver.planBuild() - XCTAssertEqual(plannedJobs.count, 3) - XCTAssertTrue(plannedJobs[2].tool.name.contains("swift")) - XCTAssertEqual(plannedJobs[2].outputs.count, driver.targetTriple.isDarwin ? 4 : 3) - XCTAssertEqual(plannedJobs[2].outputs[0].file, try toPath("Test.swiftmodule")) - XCTAssertEqual(plannedJobs[2].outputs[1].file, try toPath("Test.swiftdoc")) - XCTAssertEqual(plannedJobs[2].outputs[2].file, try toPath("Test.swiftsourceinfo")) - if driver.targetTriple.isDarwin { - XCTAssertEqual(plannedJobs[2].outputs[3].file, try toPath("Test.abi.json")) - } - } - - do { - // Make sure the swiftdoc path is correct for an inferred module - var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module", "-no-emit-module-separately"]) - let plannedJobs = try driver.planBuild() - XCTAssertEqual(plannedJobs.count, 3) - XCTAssertTrue(plannedJobs[2].tool.name.contains("swift")) - XCTAssertEqual(plannedJobs[2].outputs.count, driver.targetTriple.isDarwin ? 4 : 3) - XCTAssertEqual(plannedJobs[2].outputs[0].file, try toPath("Test.swiftmodule")) - XCTAssertEqual(plannedJobs[2].outputs[1].file, try toPath("Test.swiftdoc")) - XCTAssertEqual(plannedJobs[2].outputs[2].file, try toPath("Test.swiftsourceinfo")) - if driver.targetTriple.isDarwin { - XCTAssertEqual(plannedJobs[2].outputs[3].file, try toPath("Test.abi.json")) - } - } - - do { - // -o specified - var driver = try Driver(args: ["swiftc", "-emit-module", "-o", "/tmp/test.swiftmodule", "input.swift", "-no-emit-module-separately"]) - let plannedJobs = try driver.planBuild() - - XCTAssertEqual(plannedJobs.count, 2) - XCTAssertEqual(plannedJobs[0].kind, .compile) - XCTAssertTrue(matchTemporary(plannedJobs[0].outputs[0].file, "input.swiftmodule")) - XCTAssertEqual(plannedJobs[1].kind, .mergeModule) - XCTAssertTrue(matchTemporary(plannedJobs[1].inputs[0].file, "input.swiftmodule")) - XCTAssertEqual(plannedJobs[1].outputs[0].file, .absolute(try .init(validating: "/tmp/test.swiftmodule"))) - } - } - func testEmitModuleSeparately() throws { var envVars = ProcessEnv.vars envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) @@ -3600,14 +3456,6 @@ final class SwiftDriverTests: XCTestCase { } } - do { - // Specifying -no-emit-module-separately uses a mergeModule job. - var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module-path", "/foo/bar/Test.swiftmodule", "-experimental-emit-module-separately", "-no-emit-module-separately" ]) - let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() - XCTAssertEqual(plannedJobs.count, 3) - XCTAssertEqual(Set(plannedJobs.map { $0.kind }), Set([.compile, .mergeModule])) - } - do { // Calls using the driver to link a library shouldn't trigger an emit-module job, like in LLDB tests. var driver = try Driver(args: ["swiftc", "-emit-library", "foo.swiftmodule", "foo.o", "-emit-module-path", "foo.swiftmodule", "-experimental-emit-module-separately", "-target", "x86_64-apple-macosx10.15", "-module-name", "Test"], @@ -4194,11 +4042,16 @@ final class SwiftDriverTests: XCTestCase { "foo.swift"]) let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() - // emit module, emit module, compile foo.swift, - // verify target.swiftinterface, - // verify target.private.swiftinterface, - // verify target.package.swiftinterface, - XCTAssertEqual(plannedJobs.count, 6) + // emit module + // emit module + // compile foo.swift + // verify target.swiftinterface + // verify target.private.swiftinterface + // verify target.package.swiftinterface + // verify variant.swiftinterface + // verify variant.private.swiftinterface + // verify variant.package.swiftinterface + XCTAssertEqual(plannedJobs.count, 9) let targetModuleJob: Job = plannedJobs[0] let variantModuleJob = plannedJobs[1] @@ -6258,17 +6111,6 @@ final class SwiftDriverTests: XCTestCase { } } - // Disabled by default in merge-module - do { - var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name", - "foo", "-emit-module-interface", - "-enable-library-evolution", - "-no-emit-module-separately"], env: envVars) - let plannedJobs = try driver.planBuild() - XCTAssertEqual(plannedJobs.count, 2) - XCTAssertFalse(plannedJobs.containsJob(.verifyModuleInterface)) - } - // Emit-module separately do { var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name", @@ -6722,34 +6564,6 @@ final class SwiftDriverTests: XCTestCase { XCTAssertEqual(plannedJobs[1].inputs[0].file, try toPath("foo.swift")) } - // Ensure the merge-module step is not passed the precompiled header - do { - var driver = try Driver(args: ["swiftc", "-emit-module", "-import-objc-header", "header.h", "foo.swift", "-no-emit-module-separately"]) - let plannedJobs = try driver.planBuild() - XCTAssertEqual(plannedJobs.count, 3) - - XCTAssertEqual(plannedJobs[0].kind, .generatePCH) - XCTAssertEqual(plannedJobs[0].inputs.count, 1) - XCTAssertEqual(plannedJobs[0].inputs[0].file, try toPath("header.h")) - XCTAssertEqual(plannedJobs[0].inputs[0].type, .objcHeader) - XCTAssertEqual(plannedJobs[0].outputs.count, 1) - XCTAssertTrue(matchTemporary(plannedJobs[0].outputs[0].file, "header.pch")) - XCTAssertEqual(plannedJobs[0].outputs[0].type, .pch) - XCTAssertJobInvocationMatches(plannedJobs[0], .flag("-emit-pch")) - XCTAssertTrue(commandContainsFlagTemporaryPathSequence(plannedJobs[0].commandLine, - flag: "-o", filename: "header.pch")) - - XCTAssertEqual(plannedJobs[1].kind, .compile) - XCTAssertTrue(commandContainsFlagTemporaryPathSequence(plannedJobs[1].commandLine, - flag: "-import-objc-header", - filename: "header.pch") || - commandContainsFlagTemporaryPathSequence(plannedJobs[1].commandLine, - flag: "-import-pch", - filename: "header.pch")) - XCTAssertEqual(plannedJobs[2].kind, .mergeModule) - try XCTAssertJobInvocationMatches(plannedJobs[2], .flag("-import-objc-header"), toPathOption("header.h")) - } - // Immediate mode doesn't generate a pch do { var driver = try Driver(args: ["swift", "-import-objc-header", "TestInputHeader.h", "foo.swift"]) @@ -8379,6 +8193,10 @@ extension Array where Element == Job { func findJob(_ kind: Job.Kind) throws -> Job { return try XCTUnwrap(first(where: { $0.kind == kind })) } + + func findJobs(_ kind: Job.Kind) throws -> [Job] { + return try XCTUnwrap(filter({ $0.kind == kind })) + } } private extension Array where Element == Job.ArgTemplate {