Skip to content

Commit af4afcb

Browse files
committed
Teach Driver to generate swift-frontend invocations to build prebuilt module cache
We should be able to generate prebuilt module cache by reusing the infrastructure libSwiftDriver already provides. More specifically, we could (1) write a dummy Swift file imports every frameworks/libraries in an SDK, (2) use module dependency scanner in the driver to generate module building commands, and (3) use MultiJobExecutor to execute those commands topologically. This PR is for (2).
1 parent 1430a1f commit af4afcb

File tree

12 files changed

+201
-22
lines changed

12 files changed

+201
-22
lines changed

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,103 @@ extension Driver {
496496
return try explicitDependencyBuildPlanner!.generateExplicitModuleDependenciesBuildJobs()
497497
}
498498

499+
500+
public mutating func generatePrebuitModuleGenerationJobs(prebuiltModuleDir: VirtualPath) throws -> [Job] {
501+
guard let sdkPath = frontendTargetInfo.sdkPath?.path else {
502+
// cannot proceed without SDK path
503+
return []
504+
}
505+
// Run the dependency scanner and update the dependency oracle with the results
506+
let dependencyGraph = try gatherModuleDependencies()
507+
var jobs: [Job] = []
508+
var inputPathsCache: [String: [TypedVirtualPath]] = [:]
509+
510+
// Get the .swiftinterface path of the module in SDK
511+
func getInputPaths(_ module: String) throws -> [TypedVirtualPath] {
512+
if let cachedPaths = inputPathsCache[module] {
513+
return cachedPaths
514+
}
515+
func cache(_ results: [TypedVirtualPath]) -> [TypedVirtualPath] {
516+
inputPathsCache[module] = results
517+
return results
518+
}
519+
guard let info = dependencyGraph.modules[.swift(module)] else {
520+
return cache([])
521+
}
522+
if case .swift(let details) = info.details {
523+
if let interfacePath = details.moduleInterfacePath {
524+
var results: [TypedVirtualPath] = []
525+
let currentPath = TypedVirtualPath(file: interfacePath.path, type: .swiftInterface)
526+
try localFileSystem.getDirectoryContents(currentPath.file.parentDirectory.absolutePath!).forEach {
527+
let rawPath = try VirtualPath.init(path: $0)
528+
if rawPath.extension == "swiftinterface" {
529+
results.append(TypedVirtualPath(file: rawPath.intern(), type: .swiftInterface))
530+
}
531+
}
532+
return cache(results)
533+
}
534+
}
535+
return cache([])
536+
}
537+
538+
// Get the .swiftmodule path of the prebuilt module in the toolchain
539+
func getOutputPath(_ module: String, _ inputPath: TypedVirtualPath) -> TypedVirtualPath {
540+
let baseName = inputPath.file.basenameWithoutExt
541+
return TypedVirtualPath(file: prebuiltModuleDir
542+
.appending(component: module + ".swiftmodule")
543+
.appending(component: baseName + ".swiftmodule").intern(),
544+
type: .swiftModule)
545+
}
546+
547+
func getDependenciesPaths(_ module: String, _ inputPath: TypedVirtualPath) throws -> [TypedVirtualPath] {
548+
var results: [TypedVirtualPath] = []
549+
let info = dependencyGraph.modules[.swift(module)]!
550+
guard let dependencies = info.directDependencies else {
551+
return results
552+
}
553+
for dep in dependencies {
554+
let moduleName = dep.moduleName
555+
if let input = try getInputPaths(moduleName).first(where: { $0.file.basename == inputPath.file.basename }) {
556+
results.append(getOutputPath(moduleName, input))
557+
}
558+
}
559+
return results
560+
}
561+
562+
let moduleInfo = dependencyGraph.mainModule
563+
if let dependencies = moduleInfo.directDependencies {
564+
for dep in dependencies {
565+
let moduleName = dep.moduleName
566+
let inputPaths = try getInputPaths(moduleName)
567+
for inputPath in inputPaths {
568+
let outputPath = getOutputPath(moduleName, inputPath)
569+
var commandLine: [Job.ArgTemplate] = []
570+
commandLine.appendFlag(.compileModuleFromInterface)
571+
commandLine.appendFlag(.sdk)
572+
commandLine.append(.path(VirtualPath.lookup(sdkPath)))
573+
commandLine.appendFlag(.prebuiltModuleCachePath)
574+
commandLine.appendPath(prebuiltModuleDir)
575+
commandLine.appendFlag(.moduleName)
576+
commandLine.appendFlag(moduleName)
577+
commandLine.appendFlag(.o)
578+
commandLine.appendPath(outputPath.file)
579+
commandLine.appendPath(inputPath.file)
580+
jobs.append(Job(
581+
moduleName: moduleName,
582+
kind: .compile,
583+
tool: .absolute(try toolchain.getToolPath(.swiftCompiler)),
584+
commandLine: commandLine,
585+
inputs: try getDependenciesPaths(moduleName, inputPath),
586+
primaryInputs: [],
587+
outputs: [outputPath]
588+
))
589+
}
590+
}
591+
}
592+
593+
return jobs
594+
}
595+
499596
private mutating func gatherModuleDependencies()
500597
throws -> InterModuleDependencyGraph {
501598
var dependencyGraph = try performDependencyScan()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -module-name A
3+
import Swift
4+
public func FuncA() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -module-name A
3+
import Swift
4+
public func FuncA() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -module-name E
3+
import Swift
4+
public func FuncE() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -module-name E
3+
import Swift
4+
public func FuncE() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -module-name F
3+
import Swift
4+
import A
5+
public func FuncF() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -module-name F
3+
import Swift
4+
import A
5+
public func FuncF() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -module-name G
3+
import Swift
4+
import E
5+
public func FuncG() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -module-name G
3+
import Swift
4+
import E
5+
public func FuncG() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -module-name H
3+
import Swift
4+
import A
5+
import E
6+
import F
7+
import G
8+
public func FuncH() { }

0 commit comments

Comments
 (0)