Skip to content

Commit b77a275

Browse files
authored
Merge pull request #592 from nkcsgexi/handle-scoped-prebuilt
PrebuiltModuleGen: support a core mode where only Foundation and below are handled.
2 parents 1193229 + 74a6ce2 commit b77a275

File tree

3 files changed

+151
-47
lines changed

3 files changed

+151
-47
lines changed

Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,9 @@ extension Driver {
197197
)
198198
}
199199

200-
public mutating func generatePrebuitModuleGenerationJobs(_ inputMap: [String: [PrebuiltModuleInput]],
201-
_ prebuiltModuleDir: AbsolutePath) throws -> ([Job], [Job]) {
200+
public mutating func generatePrebuitModuleGenerationJobs(with inputMap: [String: [PrebuiltModuleInput]],
201+
into prebuiltModuleDir: AbsolutePath,
202+
exhaustive: Bool) throws -> ([Job], [Job]) {
202203
assert(sdkPath != nil)
203204
// Run the dependency scanner and update the dependency oracle with the results
204205
let dependencyGraph = try gatherModuleDependencies()
@@ -225,22 +226,32 @@ extension Driver {
225226
return (key, outputPaths)
226227
})
227228

228-
func getDependenciesPaths(_ module: String, _ arch: Triple.Arch) throws -> [TypedVirtualPath] {
229-
var results: [TypedVirtualPath] = []
229+
func collectSwiftModuleNames(_ ids: [ModuleDependencyId]) -> [String] {
230+
return ids.compactMap { id in
231+
if case .swift(let module) = id {
232+
return module
233+
}
234+
return nil
235+
}
236+
}
237+
238+
func getSwiftDependencies(for module: String) -> [String] {
230239
let info = dependencyGraph.modules[.swift(module)]!
231240
guard let dependencies = info.directDependencies else {
232-
return results
241+
return []
233242
}
243+
return collectSwiftModuleNames(dependencies)
244+
}
234245

235-
for dep in dependencies {
236-
if case let .swift(moduleName) = dep {
237-
if let outputs = outputMap[moduleName] {
238-
// Depending only those .swiftmodule files with the same arch kind.
239-
// FIXME: handling arm64 and arm64e specifically.
240-
let selectOutputs = outputs.filter ({ $0.arch == arch }).map { $0.path }
241-
results.append(contentsOf: selectOutputs)
242-
}
246+
func getOutputPaths(for modules: [String], with arch: Triple.Arch) throws -> [TypedVirtualPath] {
247+
var results: [TypedVirtualPath] = []
248+
modules.forEach { module in
249+
guard let allOutputs = outputMap[module] else {
250+
diagnosticEngine.emit(error: "cannot find output paths for \(module)")
251+
return
243252
}
253+
let allPaths = allOutputs.filter { $0.arch == arch }.map { $0.path }
254+
results.append(contentsOf: allPaths)
244255
}
245256
return results
246257
}
@@ -252,30 +263,43 @@ extension Driver {
252263
assert(inputPaths.count == outputPaths.count)
253264
assert(!inputPaths.isEmpty)
254265
for i in 0..<inputPaths.count {
255-
try action(inputPaths[i], outputPaths[i])
266+
let (input, output) = (inputPaths[i], outputPaths[i])
267+
assert(input.path.file.basenameWithoutExt == output.path.file.basenameWithoutExt)
268+
try action(input, output)
256269
}
257270
}
258271
}
259-
// Keep track of modules that are not handled.
272+
// Keep track of modules we haven't handled.
260273
var unhandledModules = Set<String>(inputMap.keys)
261-
let moduleInfo = dependencyGraph.mainModule
262-
if let dependencies = moduleInfo.directDependencies {
263-
for dep in dependencies {
264-
switch dep {
265-
case .swift(let moduleName):
266-
// Removed moduleName from the list.
267-
unhandledModules.remove(moduleName)
268-
try forEachInputOutputPair(moduleName) {
269-
jobs.append(try generateSingleModuleBuildingJob(moduleName,
270-
prebuiltModuleDir, $0, $1,
271-
try getDependenciesPaths(moduleName, $0.arch)))
272-
}
273-
default:
274-
continue
274+
if let importedModules = dependencyGraph.mainModule.directDependencies {
275+
// Start from those modules explicitly imported into the file under scanning
276+
var openModules = collectSwiftModuleNames(importedModules)
277+
var idx = 0
278+
while idx != openModules.count {
279+
let module = openModules[idx]
280+
let dependencies = getSwiftDependencies(for: module)
281+
try forEachInputOutputPair(module) { input, output in
282+
jobs.append(try generateSingleModuleBuildingJob(module,
283+
prebuiltModuleDir, input, output,
284+
try getOutputPaths(for: dependencies, with: input.arch)))
275285
}
286+
// For each dependency, add to the list to handle if the list doesn't
287+
// contain this dependency.
288+
dependencies.forEach({ newModule in
289+
if !openModules.contains(newModule) {
290+
diagnosticEngine.emit(note: "\(newModule) is discovered.")
291+
openModules.append(newModule)
292+
}
293+
})
294+
unhandledModules.remove(module)
295+
idx += 1
276296
}
277297
}
278298

299+
// We are done if we don't need to handle all inputs exhaustively.
300+
if !exhaustive {
301+
return (jobs, [])
302+
}
279303
// For each unhandled module, generate dangling jobs for each associated
280304
// interfaces.
281305
// The only known usage of this so for is in macosx SDK where some collected

Sources/swift-build-sdk-interfaces/main.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ if rawOutputDir.isEmpty {
3434
exit(1)
3535
}
3636

37+
/// When -core is specified, only most significant modules are handled. Currently,
38+
/// they are Foundation and anything below.
39+
let coreMode = CommandLine.arguments.contains("-core")
40+
3741
class PrebuitGenDelegate: JobExecutionDelegate {
3842
var failingModules = Set<String>()
3943
var commandMap: [Int: String] = [:]
@@ -55,8 +59,10 @@ class PrebuitGenDelegate: JobExecutionDelegate {
5559
stderrStream.flush()
5660
}
5761
}
62+
#if !os(Windows)
5863
case .signalled:
5964
diagnosticsEngine.emit(.remark("\(job.moduleName) interrupted"))
65+
#endif
6066
}
6167
}
6268

@@ -110,7 +116,7 @@ do {
110116
to: sysVersionFile)
111117
let processSet = ProcessSet()
112118
let inputMap = try collector.collectSwiftInterfaceMap()
113-
let allModules = inputMap.keys
119+
let allModules = coreMode ? ["Foundation"] : Array(inputMap.keys)
114120
try withTemporaryFile(suffix: ".swift") {
115121
let tempPath = $0.path
116122
try localFileSystem.writeFileContents(tempPath, body: {
@@ -129,7 +135,7 @@ do {
129135
diagnosticsEngine: diagnosticsEngine,
130136
executor: executor,
131137
compilerExecutableDir: swiftcPath.parentDirectory)
132-
let (jobs, danglingJobs) = try driver.generatePrebuitModuleGenerationJobs(inputMap, outputDir)
138+
let (jobs, danglingJobs) = try driver.generatePrebuitModuleGenerationJobs(with: inputMap, into: outputDir, exhaustive: !coreMode)
133139
let delegate = PrebuitGenDelegate()
134140
do {
135141
try executor.execute(workload: DriverExecutorWorkload.init(jobs, nil, continueBuildingAfterErrors: true),

Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift

Lines changed: 90 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,22 @@ final class ExplicitModuleBuildTests: XCTestCase {
771771
XCTAssertTrue(input.file.basename == name)
772772
}
773773
}
774+
let packageRootPath = URL(fileURLWithPath: #file).pathComponents
775+
.prefix(while: { $0 != "Tests" }).joined(separator: "/").dropFirst()
776+
let testInputsPath = packageRootPath + "/TestInputs"
777+
let mockSDKPath : String = testInputsPath + "/mock-sdk.sdk"
778+
let diagnosticEnging = DiagnosticsEngine()
779+
let collector = try SDKPrebuiltModuleInputsCollector(VirtualPath(path: mockSDKPath).absolutePath!, diagnosticEnging)
780+
let interfaceMap = try collector.collectSwiftInterfaceMap()
781+
782+
// Check interface map always contain everything
783+
XCTAssertTrue(interfaceMap["Swift"]!.count == 2)
784+
XCTAssertTrue(interfaceMap["A"]!.count == 2)
785+
XCTAssertTrue(interfaceMap["E"]!.count == 2)
786+
XCTAssertTrue(interfaceMap["F"]!.count == 2)
787+
XCTAssertTrue(interfaceMap["G"]!.count == 2)
788+
XCTAssertTrue(interfaceMap["H"]!.count == 2)
789+
774790
try withTemporaryDirectory { path in
775791
let main = path.appending(component: "testPrebuiltModuleGenerationJobs.swift")
776792
try localFileSystem.writeFileContents(main) {
@@ -781,26 +797,13 @@ final class ExplicitModuleBuildTests: XCTestCase {
781797
$0 <<< "import H\n"
782798
$0 <<< "import Swift\n"
783799
}
784-
let packageRootPath = URL(fileURLWithPath: #file).pathComponents
785-
.prefix(while: { $0 != "Tests" }).joined(separator: "/").dropFirst()
786-
let testInputsPath = packageRootPath + "/TestInputs"
787-
let mockSDKPath : String = testInputsPath + "/mock-sdk.sdk"
788800
var driver = try Driver(args: ["swiftc", main.pathString,
789801
"-sdk", mockSDKPath,
790802
])
791803

792-
let diagnosticEnging = DiagnosticsEngine()
793-
let collector = try SDKPrebuiltModuleInputsCollector(VirtualPath(path: mockSDKPath).absolutePath!, diagnosticEnging)
794-
let interfaceMap = try collector.collectSwiftInterfaceMap()
795-
XCTAssertTrue(interfaceMap["Swift"]!.count == 2)
796-
XCTAssertTrue(interfaceMap["A"]!.count == 2)
797-
XCTAssertTrue(interfaceMap["E"]!.count == 2)
798-
XCTAssertTrue(interfaceMap["F"]!.count == 2)
799-
XCTAssertTrue(interfaceMap["G"]!.count == 2)
800-
XCTAssertTrue(interfaceMap["H"]!.count == 2)
801-
802-
let (jobs, danglingJobs) = try driver.generatePrebuitModuleGenerationJobs(interfaceMap,
803-
VirtualPath(path: "/tmp/").absolutePath!)
804+
let (jobs, danglingJobs) = try driver.generatePrebuitModuleGenerationJobs(with: interfaceMap,
805+
into: VirtualPath(path: "/tmp/").absolutePath!,
806+
exhaustive: true)
804807

805808
XCTAssertTrue(danglingJobs.count == 2)
806809
XCTAssertTrue(danglingJobs.allSatisfy { job in
@@ -825,6 +828,77 @@ final class ExplicitModuleBuildTests: XCTestCase {
825828
checkInputOutputIntegrity(GJobs[0])
826829
checkInputOutputIntegrity(GJobs[1])
827830
}
831+
try withTemporaryDirectory { path in
832+
let main = path.appending(component: "testPrebuiltModuleGenerationJobs.swift")
833+
try localFileSystem.writeFileContents(main) {
834+
$0 <<< "import H\n"
835+
}
836+
var driver = try Driver(args: ["swiftc", main.pathString,
837+
"-sdk", mockSDKPath,
838+
])
839+
840+
let (jobs, danglingJobs) = try driver.generatePrebuitModuleGenerationJobs(with: interfaceMap,
841+
into: VirtualPath(path: "/tmp/").absolutePath!,
842+
exhaustive: false)
843+
844+
XCTAssertTrue(danglingJobs.isEmpty)
845+
XCTAssertTrue(jobs.count == 12)
846+
XCTAssertTrue(jobs.allSatisfy {$0.outputs.count == 1})
847+
XCTAssertTrue(jobs.allSatisfy {$0.kind == .compile})
848+
XCTAssertTrue(jobs.allSatisfy {$0.commandLine.contains(.flag("-compile-module-from-interface"))})
849+
let HJobs = jobs.filter { $0.moduleName == "H"}
850+
XCTAssertTrue(HJobs.count == 2)
851+
XCTAssertTrue(getInputModules(HJobs[0]) == ["A", "E", "F", "G", "Swift"])
852+
XCTAssertTrue(getInputModules(HJobs[1]) == ["A", "E", "F", "G", "Swift"])
853+
XCTAssertTrue(getOutputName(HJobs[0]) != getOutputName(HJobs[1]))
854+
checkInputOutputIntegrity(HJobs[0])
855+
checkInputOutputIntegrity(HJobs[1])
856+
let GJobs = jobs.filter { $0.moduleName == "G"}
857+
XCTAssertTrue(GJobs.count == 2)
858+
XCTAssertTrue(getInputModules(GJobs[0]) == ["E", "Swift"])
859+
XCTAssertTrue(getInputModules(GJobs[1]) == ["E", "Swift"])
860+
XCTAssertTrue(getOutputName(GJobs[0]) != getOutputName(GJobs[1]))
861+
checkInputOutputIntegrity(GJobs[0])
862+
checkInputOutputIntegrity(GJobs[1])
863+
}
864+
try withTemporaryDirectory { path in
865+
let main = path.appending(component: "testPrebuiltModuleGenerationJobs.swift")
866+
try localFileSystem.writeFileContents(main) {
867+
$0 <<< "import Swift\n"
868+
}
869+
var driver = try Driver(args: ["swiftc", main.pathString,
870+
"-sdk", mockSDKPath,
871+
])
872+
873+
let (jobs, danglingJobs) = try driver.generatePrebuitModuleGenerationJobs(with: interfaceMap,
874+
into: VirtualPath(path: "/tmp/").absolutePath!,
875+
exhaustive: false)
876+
877+
XCTAssertTrue(danglingJobs.isEmpty)
878+
XCTAssert(jobs.count == 2)
879+
XCTAssert(jobs.allSatisfy { $0.moduleName == "Swift" })
880+
}
881+
try withTemporaryDirectory { path in
882+
let main = path.appending(component: "testPrebuiltModuleGenerationJobs.swift")
883+
try localFileSystem.writeFileContents(main) {
884+
$0 <<< "import F\n"
885+
}
886+
var driver = try Driver(args: ["swiftc", main.pathString,
887+
"-sdk", mockSDKPath,
888+
])
889+
890+
let (jobs, danglingJobs) = try driver.generatePrebuitModuleGenerationJobs(with: interfaceMap,
891+
into: VirtualPath(path: "/tmp/").absolutePath!,
892+
exhaustive: false)
893+
894+
XCTAssertTrue(danglingJobs.isEmpty)
895+
XCTAssertTrue(jobs.count == 6)
896+
jobs.forEach({ job in
897+
// Check we don't pull in other modules than A, F and Swift
898+
XCTAssertTrue(["A", "F", "Swift"].contains(job.moduleName))
899+
checkInputOutputIntegrity(job)
900+
})
901+
}
828902
}
829903
#endif
830904
}

0 commit comments

Comments
 (0)