From daaaa8899843c29cc5f569f3461d09b3415ba973 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Mon, 29 Jan 2024 16:13:20 -0800 Subject: [PATCH] [Explicit Module Builds] Cache explicit dependency additions to main module command-lines Cache input files and command-line argument additions describing main module explicit module dependencies to each individual job. Instead of re-computing them for each compile task, which includes serializing a whole new `.json` file with the inputs. --- .../ExplicitDependencyBuildPlanner.swift | 42 +++++++++++----- .../SwiftDriver/Jobs/FrontendJobHelpers.swift | 23 +++------ .../ExplicitModuleBuildTests.swift | 49 +++++++++++++++++++ 3 files changed, 87 insertions(+), 27 deletions(-) diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift index b54371946..275c1c56e 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift @@ -55,6 +55,13 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT /// Does this compile support `.explicitInterfaceModuleBuild` private var 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 + public init(dependencyGraph: InterModuleDependencyGraph, toolchain: Toolchain, dependencyOracle: InterModuleDependencyOracle, @@ -396,18 +403,31 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT /// inputs and command line flags. public mutating func resolveMainModuleDependencies(inputs: inout [TypedVirtualPath], commandLine: inout [Job.ArgTemplate]) throws { - let mainModuleId: ModuleDependencyId = .swift(dependencyGraph.mainModuleName) - - let mainModuleDetails = try dependencyGraph.swiftModuleDetails(of: mainModuleId) - if let additionalArgs = mainModuleDetails.commandLine { - additionalArgs.forEach { commandLine.appendFlag($0) } + // 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 { commandLine.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 + ) } - commandLine.appendFlags("-disable-implicit-swift-modules", - "-Xcc", "-fno-implicit-modules", - "-Xcc", "-fno-implicit-module-maps") - try resolveExplicitModuleDependencies(moduleId: mainModuleId, - inputs: &inputs, - commandLine: &commandLine) + guard let mainModuleDependenciesArgs = resolvedMainModuleDependenciesArgs else { + fatalError("Failed to compute resolved explicit dependency arguments.") + } + inputs.append(contentsOf: mainModuleDependenciesArgs.inputs) + commandLine.append(contentsOf: mainModuleDependenciesArgs.commandLine) } /// Resolve all module dependencies of the main module and add them to the lists of diff --git a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift index e872f0e5a..f45c41d1c 100644 --- a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift +++ b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift @@ -736,30 +736,21 @@ extension Driver { /// Adds all dependencies required for an explicit module build /// to inputs and command line arguments of a compile job. - func addExplicitModuleBuildArguments(inputs: inout [TypedVirtualPath], - commandLine: inout [Job.ArgTemplate]) throws { - guard var dependencyPlanner = explicitDependencyBuildPlanner else { - fatalError("No dependency planner in Explicit Module Build mode.") - } - try dependencyPlanner.resolveMainModuleDependencies(inputs: &inputs, commandLine: &commandLine) + 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. - func addExplicitPCHBuildArguments(inputs: inout [TypedVirtualPath], - commandLine: inout [Job.ArgTemplate]) throws { - guard var dependencyPlanner = explicitDependencyBuildPlanner else { - fatalError("No dependency planner in Explicit Module Build mode.") - } - try dependencyPlanner.resolveBridgingHeaderDependencies(inputs: &inputs, commandLine: &commandLine) + mutating func addExplicitPCHBuildArguments(inputs: inout [TypedVirtualPath], + commandLine: inout [Job.ArgTemplate]) throws { + try explicitDependencyBuildPlanner?.resolveBridgingHeaderDependencies(inputs: &inputs, commandLine: &commandLine) } /// If explicit dependency planner supports creating bridging header pch command. public func supportsBridgingHeaderPCHCommand() throws -> Bool { - guard let dependencyPlanner = explicitDependencyBuildPlanner else { - return false - } - return try dependencyPlanner.supportsBridgingHeaderPCHCommand() + return try explicitDependencyBuildPlanner?.supportsBridgingHeaderPCHCommand() ?? false } /// In Explicit Module Build mode, distinguish between main module jobs and intermediate dependency build jobs, diff --git a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index f0763a926..1b0e1e6cd 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -230,6 +230,55 @@ final class ExplicitModuleBuildTests: XCTestCase { } } + func testModuleDependencyBuildCommandUniqueDepFile() throws { + try withTemporaryDirectory { path in + let source0 = path.appending(component: "testModuleDependencyBuildCommandUniqueDepFile1.swift") + let source1 = path.appending(component: "testModuleDependencyBuildCommandUniqueDepFile2.swift") + try localFileSystem.writeFileContents(source0, bytes: + """ + import C; + """ + ) + try localFileSystem.writeFileContents(source1, bytes: + """ + import G; + """ + ) + + let cHeadersPath: AbsolutePath = + try testInputsPath.appending(component: "ExplicitModuleBuilds") + .appending(component: "CHeaders") + let bridgingHeaderpath: AbsolutePath = + cHeadersPath.appending(component: "Bridging.h") + let swiftModuleInterfacesPath: AbsolutePath = + try testInputsPath.appending(component: "ExplicitModuleBuilds") + .appending(component: "Swift") + let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? [] + var driver = try Driver(args: ["swiftc", + "-target", "x86_64-apple-macosx11.0", + "-I", cHeadersPath.nativePathString(escaped: true), + "-I", swiftModuleInterfacesPath.nativePathString(escaped: true), + "-explicit-module-build", + "-import-objc-header", bridgingHeaderpath.nativePathString(escaped: true), + source0.nativePathString(escaped: true), + source1.nativePathString(escaped: true)] + sdkArgumentsForTesting) + + let jobs = try driver.planBuild() + let compileJobs = jobs.filter({ $0.kind == .compile }) + XCTAssertEqual(compileJobs.count, 2) + let compileJob0 = compileJobs[0] + let compileJob1 = compileJobs[1] + let explicitDepsFlag = SwiftDriver.Job.ArgTemplate.flag(String("-explicit-swift-module-map-file")) + XCTAssert(compileJob0.commandLine.contains(explicitDepsFlag)) + XCTAssert(compileJob1.commandLine.contains(explicitDepsFlag)) + let jsonDeps0PathIndex = compileJob0.commandLine.firstIndex(of: explicitDepsFlag) + let jsonDeps0PathArg = compileJob0.commandLine[jsonDeps0PathIndex! + 1] + let jsonDeps1PathIndex = compileJob1.commandLine.firstIndex(of: explicitDepsFlag) + let jsonDeps1PathArg = compileJob1.commandLine[jsonDeps1PathIndex! + 1] + XCTAssertEqual(jsonDeps0PathArg, jsonDeps1PathArg) + } + } + private func pathMatchesSwiftModule(path: VirtualPath, _ name: String) -> Bool { return path.basenameWithoutExt.starts(with: "\(name)-") && path.extension! == FileType.swiftModule.rawValue