From 3424c6b92182b45e7572f88e8e6c0fea35bf95dc Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Wed, 26 Mar 2025 15:40:03 -0700 Subject: [PATCH 1/4] Delete functionality for the obsolete merge-modules job --- Sources/SwiftDriver/CMakeLists.txt | 1 - Sources/SwiftDriver/Jobs/MergeModuleJob.swift | 83 ------------------- Sources/SwiftDriver/Jobs/Planning.swift | 62 ++------------ 3 files changed, 9 insertions(+), 137 deletions(-) delete mode 100644 Sources/SwiftDriver/Jobs/MergeModuleJob.swift 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/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..1ba82807d 100644 --- a/Sources/SwiftDriver/Jobs/Planning.swift +++ b/Sources/SwiftDriver/Jobs/Planning.swift @@ -239,13 +239,7 @@ extension Driver { 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 +248,6 @@ extension Driver { addLinkerInput(jobOutput) case .llvmBitcode where acceptBitcodeAsLinkerInput: addLinkerInput(jobOutput) - case .swiftModule: - addModuleInputFromJobOutputs(jobOutput) default: break @@ -296,7 +288,7 @@ extension Driver { try addPostModuleFilesJobs(emitModuleJob) try addWrapJobOrMergeOutputs( - mergeJob: emitModuleJob, + emitModuleJob: emitModuleJob, debugInfo: debugInfo, addJob: addJobAfterCompiles, addLinkerInput: addLinkerInput) @@ -311,7 +303,6 @@ extension Driver { try addJobsForPrimaryInputs( addCompileJob: addCompileJob, - addModuleInput: addModuleInput, addLinkerInput: addLinkerInput, addJobOutputs: addJobOutputs, pchCompileJob: jobCreatingPch) @@ -319,20 +310,6 @@ extension Driver { 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 } @@ -361,7 +338,6 @@ extension Driver { private mutating func addJobsForPrimaryInputs( addCompileJob: (Job) -> Void, - addModuleInput: (TypedVirtualPath) -> Void, addLinkerInput: (TypedVirtualPath) -> Void, addJobOutputs: ([TypedVirtualPath]) -> Void, pchCompileJob: Job?) @@ -374,7 +350,6 @@ extension Driver { try addJobForPrimaryInput( input: input, addCompileJob: addCompileJob, - addModuleInput: addModuleInput, addLinkerInput: addLinkerInput, addJobOutputs: addJobOutputs, pchCompileJob: pchCompileJob, @@ -385,7 +360,6 @@ extension Driver { private mutating func addJobForPrimaryInput( input: TypedVirtualPath, addCompileJob: (Job) -> Void, - addModuleInput: (TypedVirtualPath) -> Void, addLinkerInput: (TypedVirtualPath) -> Void, addJobOutputs: ([TypedVirtualPath]) -> Void, pchCompileJob: Job?, @@ -416,13 +390,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 { @@ -456,19 +425,6 @@ extension Driver { 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 { @@ -611,22 +567,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) From c66959d7b2d8730cec29dec96bb78cd9b1b9c1b0 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Fri, 28 Mar 2025 16:06:04 -0700 Subject: [PATCH 2/4] [Explicit Module Builds][Target Variant] Refactor build planning to allow for a second, separte emit-module task for the target variant This change allows builds that specify a variant triple and which must emit a target variant module to do so from within a single `swiftc` invocation when using Explicit Module Builds. This is an extensive refactor of the build planning procedure with respect to Explicit Module Builds: - Remove 'ExplicitDependencyBuildPlanner' global state from the 'Driver' instance - Instantiate a 'ExplicitDependencyBuildPlanner' instance at the beginning if build planning and propagate/thread this instance to all the required compilation task creation routines, all the way to 'addCommonFrontendOptions' - For '-target-variant' builds which emit a variant triple module, instantiate a whole separate explicit build planner, including performing a secondary dependency scan using the variant triple. Using this information: - Schedule variant triple module pre-compile dependency tasks - Schedule a variant triple PCH compilation task, if necessary - Schedule the variant emit-module task - Schedule the variant textual module verification task, if necessary --- Sources/SwiftDriver/Driver/Driver.swift | 57 +--- .../ExplicitDependencyBuildPlanner.swift | 263 +++++++++++------- .../CommonDependencyOperations.swift | 77 +---- .../InterModuleDependencyGraph.swift | 4 +- .../ModuleDependencyScanning.swift | 38 ++- .../FirstWaveComputer.swift | 12 +- ...IncrementalCompilationProtectedState.swift | 13 +- ...crementalCompilationState+Extensions.swift | 2 +- .../IncrementalCompilationState.swift | 11 +- Sources/SwiftDriver/Jobs/CompileJob.swift | 9 +- Sources/SwiftDriver/Jobs/EmitModuleJob.swift | 29 +- .../SwiftDriver/Jobs/FrontendJobHelpers.swift | 58 ++-- Sources/SwiftDriver/Jobs/GeneratePCHJob.swift | 34 ++- Sources/SwiftDriver/Jobs/GeneratePCMJob.swift | 12 +- Sources/SwiftDriver/Jobs/InterpretJob.swift | 6 +- Sources/SwiftDriver/Jobs/Job.swift | 10 +- Sources/SwiftDriver/Jobs/LinkJob.swift | 5 +- Sources/SwiftDriver/Jobs/Planning.swift | 249 ++++++++++------- Sources/SwiftDriver/Jobs/ReplJob.swift | 3 +- .../Jobs/VerifyModuleInterfaceJob.swift | 8 +- .../SwiftScan/DependencyGraphBuilder.swift | 6 +- .../SwiftDriverTests/CachingBuildTests.swift | 9 +- .../ExplicitModuleBuildTests.swift | 85 ++---- Tests/SwiftDriverTests/SwiftDriverTests.swift | 206 +------------- 24 files changed, 483 insertions(+), 723 deletions(-) diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index db11ab3cc..ab1f90c88 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -341,38 +341,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 +598,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 +1334,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 +1725,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 +1799,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 +1991,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 +2016,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] { 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/Planning.swift b/Sources/SwiftDriver/Jobs/Planning.swift index 1ba82807d..689fef6a6 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,30 @@ 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, + supportsBridgingHeaderPCHCommand: + interModuleDependencyOracle.supportsBridgingHeaderPCHCommand) } else { return nil } @@ -122,7 +131,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 +155,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 +232,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 +270,8 @@ extension Driver { if emitModuleSeparately || isVariantModule { let emitJob = try emitModuleJob( pchCompileJob: pchCompileJob, - isVariantJob: isVariantModule) + explicitModulePlanner: explicitModulePlanner, + forVariantModule: isVariantModule) addJobBeforeCompiles(emitJob) return emitJob } @@ -232,9 +280,10 @@ 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]() @@ -268,23 +317,23 @@ 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( @@ -294,18 +343,12 @@ extension Driver { addLinkerInput: addLinkerInput) } - if variantModuleOutputInfo != nil { - _ = try addEmitModuleJob( - addJobBeforeCompiles: addJobBeforeCompiles, - pchCompileJob: jobCreatingPch, - isVariantModule: true) - } - try addJobsForPrimaryInputs( addCompileJob: addCompileJob, addLinkerInput: addLinkerInput, addJobOutputs: addJobOutputs, - pchCompileJob: jobCreatingPch) + pchCompileJob: pchCompileJob, + explicitModulePlanner: explicitModulePlanner) try addAutolinkExtractJob(linkerInputs: linkerInputs, addLinkerInput: addLinkerInput, @@ -319,7 +362,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 }) @@ -331,7 +375,8 @@ extension Driver { addJobOutputs: addJobOutputs, pchCompileJob: pchCompileJob, emitModuleTrace: emitModuleTrace, - produceCacheKey: true) + produceCacheKey: true, + explicitModulePlanner: explicitModulePlanner) addJob(compile) return compile } @@ -340,7 +385,8 @@ extension Driver { addCompileJob: (Job) -> Void, addLinkerInput: (TypedVirtualPath) -> Void, addJobOutputs: ([TypedVirtualPath]) -> Void, - pchCompileJob: Job?) + pchCompileJob: Job?, + explicitModulePlanner: ExplicitDependencyBuildPlanner?) throws { let loadedModuleTraceInputIndex = inputFiles.firstIndex(where: { $0.type.isPartOfSwiftCompilation && loadedModuleTracePath != nil @@ -353,7 +399,8 @@ extension Driver { addLinkerInput: addLinkerInput, addJobOutputs: addJobOutputs, pchCompileJob: pchCompileJob, - emitModuleTrace: index == loadedModuleTraceInputIndex) + emitModuleTrace: index == loadedModuleTraceInputIndex, + explicitModulePlanner: explicitModulePlanner) } } @@ -363,7 +410,8 @@ extension Driver { addLinkerInput: (TypedVirtualPath) -> Void, addJobOutputs: ([TypedVirtualPath]) -> Void, pchCompileJob: Job?, - emitModuleTrace: Bool + emitModuleTrace: Bool, + explicitModulePlanner: ExplicitDependencyBuildPlanner? ) throws { switch input.type { @@ -380,7 +428,8 @@ extension Driver { canSkipIfOnlyModule: canSkipIfOnlyModule, pchCompileJob: pchCompileJob, addCompileJob: addCompileJob, - addJobOutputs: addJobOutputs) + addJobOutputs: addJobOutputs, + explicitModulePlanner: explicitModulePlanner) case .object, .autolink, .llvmBitcode, .tbd: if linkerOutputType != nil { @@ -409,7 +458,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. @@ -421,7 +471,8 @@ extension Driver { addJobOutputs: addJobOutputs, pchCompileJob: pchCompileJob, emitModuleTrace: emitModuleTrace, - produceCacheKey: !compilerMode.isBatchCompile) + produceCacheKey: !compilerMode.isBatchCompile, + explicitModulePlanner: explicitModulePlanner) addCompileJob(compile) } @@ -472,7 +523,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. @@ -515,13 +568,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) @@ -538,13 +597,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) } } @@ -603,12 +664,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 } @@ -629,26 +692,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 @@ -703,7 +746,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) @@ -729,13 +772,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: @@ -745,13 +786,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) } @@ -777,7 +820,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. @@ -832,7 +876,8 @@ extension Driver { addJobOutputs: {_ in }, pchCompileJob: jobCreatingPch, emitModuleTrace: constituentsEmittedModuleTrace, - produceCacheKey: true) + produceCacheKey: true, + explicitModulePlanner: explicitModulePlanner) } return batchedCompileJobs + noncompileJobs } diff --git a/Sources/SwiftDriver/Jobs/ReplJob.swift b/Sources/SwiftDriver/Jobs/ReplJob.swift index e7ff79996..c527dcf15 100644 --- a/Sources/SwiftDriver/Jobs/ReplJob.swift +++ b/Sources/SwiftDriver/Jobs/ReplJob.swift @@ -15,7 +15,8 @@ extension Driver { var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) } var inputs: [TypedVirtualPath] = [] - try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .repl) + try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .repl, + explicitModulePlanner: nil) try addRuntimeLibraryFlags(commandLine: &commandLine) try commandLine.appendLast(.importObjcHeader, from: &parsedOptions) 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/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..23441cdd8 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() diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index 95c435ae9..a7675c9e8 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"]) From ecefd238bf401746c978ffff31bcca7135b7a7b5 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Wed, 16 Apr 2025 15:46:31 -0700 Subject: [PATCH 3/4] Add new 'VirtualPath' variant for a driver-computed build artifact with known contents Similarly to a 'temporaryWithKnownContents', but at an explicitly-determined-by-the-driver 'AbsolutePath'. This is useful for when the driver is able to determine that build planning will produce an input to compilation which must be placed in a specific location. For example, the chained bridging header content and output path are determined during build planning (specifically, dependency scanning) and must be fed into compilation tasks as inputs. This new file type will allow the driver to compute the new file and its contents and then have them get written to disk when resolving input paths to said compilation tasks. --- Sources/SwiftDriver/Driver/Driver.swift | 16 +- .../SwiftDriver/Execution/ArgsResolver.swift | 8 +- Sources/SwiftDriver/Jobs/Planning.swift | 1 + .../SwiftDriver/Utilities/VirtualPath.swift | 53 ++++-- .../ExplicitModuleBuildTests.swift | 161 ++++++++++++++++++ Tests/SwiftDriverTests/SwiftDriverTests.swift | 4 + 6 files changed, 220 insertions(+), 23 deletions(-) diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index ab1f90c88..372a30e35 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -330,7 +330,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.") } } @@ -3087,15 +3087,11 @@ 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) + } + return try VirtualPath.createBuildProductFileWithKnownContents(path, chainedHeader.content.data(using: .utf8)!).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/Jobs/Planning.swift b/Sources/SwiftDriver/Jobs/Planning.swift index 689fef6a6..6fe800827 100644 --- a/Sources/SwiftDriver/Jobs/Planning.swift +++ b/Sources/SwiftDriver/Jobs/Planning.swift @@ -120,6 +120,7 @@ extension Driver { supportsExplicitInterfaceBuild: isFrontendArgSupported(.explicitInterfaceModuleBuild), cas: cas, + prefixMap: prefixMapping, supportsBridgingHeaderPCHCommand: interModuleDependencyOracle.supportsBridgingHeaderPCHCommand) } else { diff --git a/Sources/SwiftDriver/Utilities/VirtualPath.swift b/Sources/SwiftDriver/Utilities/VirtualPath.swift index 665147850..c97ced357 100644 --- a/Sources/SwiftDriver/Utilities/VirtualPath.swift +++ b/Sources/SwiftDriver/Utilities/VirtualPath.swift @@ -47,6 +47,9 @@ public enum VirtualPath: Hashable { /// Standard output case standardOutput + /// ACTODO: Comment + 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 +75,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 +87,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 +96,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 +116,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 +124,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 +136,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 +148,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 +167,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 +185,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 +206,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 +302,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 +436,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 +525,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 +548,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 +586,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 +616,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 +635,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 +655,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 +678,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 +727,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/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index 23441cdd8..ddd9b3160 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -2457,6 +2457,167 @@ final class ExplicitModuleBuildTests: XCTestCase { } } + 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", + "-disable-implicit-concurrency-module-import", + "-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", + "-disable-implicit-concurrency-module-import", + "-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 a7675c9e8..2d6215c69 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -8193,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 { From 91811bb94a0fc1a420273c296e627d08f3cc3491 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Tue, 22 Apr 2025 14:20:07 -0700 Subject: [PATCH 4/4] Propagate driver-specified clang-target flags even when no SDK is specified --- Sources/SwiftDriver/Driver/Driver.swift | 8 +- Sources/SwiftDriver/Jobs/ReplJob.swift | 3 +- .../Toolchains/DarwinToolchain.swift | 92 ++++++----- .../SwiftDriver/Utilities/VirtualPath.swift | 3 +- TestInputs/mock-sdk.sdk/SDKSettings.json | 4 +- .../ExplicitModuleBuildTests.swift | 144 +++++++++++++++++- 6 files changed, 203 insertions(+), 51 deletions(-) diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index 372a30e35..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)"} ?? "") @@ -3091,7 +3094,10 @@ extension Driver { if !fileSystem.exists(path.parentDirectory) { try fileSystem.createDirectory(path.parentDirectory, recursive: true) } - return try VirtualPath.createBuildProductFileWithKnownContents(path, chainedHeader.content.data(using: .utf8)!).intern() + 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/Jobs/ReplJob.swift b/Sources/SwiftDriver/Jobs/ReplJob.swift index c527dcf15..e7ff79996 100644 --- a/Sources/SwiftDriver/Jobs/ReplJob.swift +++ b/Sources/SwiftDriver/Jobs/ReplJob.swift @@ -15,8 +15,7 @@ extension Driver { var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) } var inputs: [TypedVirtualPath] = [] - try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .repl, - explicitModulePlanner: nil) + try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .repl) try addRuntimeLibraryFlags(commandLine: &commandLine) try commandLine.appendLast(.importObjcHeader, from: &parsedOptions) 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 c97ced357..6746d11d1 100644 --- a/Sources/SwiftDriver/Utilities/VirtualPath.swift +++ b/Sources/SwiftDriver/Utilities/VirtualPath.swift @@ -47,7 +47,8 @@ public enum VirtualPath: Hashable { /// Standard output case standardOutput - /// ACTODO: Comment + /// 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. 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/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index ddd9b3160..62aad7e5b 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -2457,6 +2457,142 @@ 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 = @@ -2489,8 +2625,8 @@ final class ExplicitModuleBuildTests: XCTestCase { "-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", - "-disable-implicit-concurrency-module-import", - "-disable-implicit-string-processing-module-import", + "-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), @@ -2591,8 +2727,8 @@ final class ExplicitModuleBuildTests: XCTestCase { "-emit-module", "-emit-module-path", "foo.swiftmodule/target.swiftmodule", "-emit-variant-module-path", "foo.swiftmodule/variant.swiftmodule", - "-disable-implicit-concurrency-module-import", - "-disable-implicit-string-processing-module-import", + "-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),