Skip to content

Commit 8e37949

Browse files
authored
Merge pull request #793 from nkcsgexi/prebuilt-5.5
[5.5] PrebuiltModuleGen: cherry-pick recent changes
2 parents 63469ce + 926e630 commit 8e37949

File tree

3 files changed

+162
-29
lines changed

3 files changed

+162
-29
lines changed

Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift

Lines changed: 108 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,33 +24,40 @@ public class PrebuitModuleGenerationDelegate: JobExecutionDelegate {
2424
let diagnosticsEngine: DiagnosticsEngine
2525
let verbose: Bool
2626
var failingCriticalOutputs: Set<VirtualPath>
27-
public init(_ jobs: [Job], _ diagnosticsEngine: DiagnosticsEngine, _ verbose: Bool) {
27+
let logPath: AbsolutePath?
28+
public init(_ jobs: [Job], _ diagnosticsEngine: DiagnosticsEngine, _ verbose: Bool, logPath: AbsolutePath?) {
2829
self.diagnosticsEngine = diagnosticsEngine
2930
self.verbose = verbose
3031
self.failingCriticalOutputs = Set<VirtualPath>(jobs.compactMap(PrebuitModuleGenerationDelegate.getCriticalOutput))
32+
self.logPath = logPath
3133
}
3234

3335
/// Dangling jobs are macabi-only modules. We should run those jobs if foundation
3436
/// is built successfully for macabi.
3537
public var shouldRunDanglingJobs: Bool {
3638
return !failingCriticalOutputs.contains(where: isIosMac)
3739
}
38-
func printJobInfo(_ job: Job, _ start: Bool) {
39-
guard verbose else {
40-
return
41-
}
40+
41+
func getInputInterfacePath(_ job: Job) -> AbsolutePath {
4242
for arg in job.commandLine {
4343
if case .path(let p) = arg {
4444
if p.extension == "swiftinterface" {
45-
Driver.stdErrQueue.sync {
46-
stderrStream <<< (start ? "started: " : "finished: ")
47-
stderrStream <<< p.absolutePath!.pathString <<< "\n"
48-
stderrStream.flush()
49-
}
50-
return
45+
return p.absolutePath!
5146
}
5247
}
5348
}
49+
fatalError()
50+
}
51+
52+
func printJobInfo(_ job: Job, _ start: Bool) {
53+
guard verbose else {
54+
return
55+
}
56+
Driver.stdErrQueue.sync {
57+
stderrStream <<< (start ? "started: " : "finished: ")
58+
stderrStream <<< getInputInterfacePath(job).pathString <<< "\n"
59+
stderrStream.flush()
60+
}
5461
}
5562

5663
static func getCriticalOutput(_ job: Job) -> VirtualPath? {
@@ -66,6 +73,24 @@ public class PrebuitModuleGenerationDelegate: JobExecutionDelegate {
6673
return !failingCriticalOutputs.isEmpty
6774
}
6875

76+
fileprivate func logOutput(_ job: Job, _ result: ProcessResult, _ stdout: Bool) throws {
77+
guard let logPath = logPath else {
78+
return
79+
}
80+
let content = stdout ? try result.utf8Output() : try result.utf8stderrOutput()
81+
guard !content.isEmpty else {
82+
return
83+
}
84+
if !localFileSystem.exists(logPath) {
85+
try localFileSystem.createDirectory(logPath, recursive: true)
86+
}
87+
let interfaceBase = getInputInterfacePath(job).basenameWithoutExt
88+
let fileName = "\(job.moduleName)-\(interfaceBase)-\(stdout ? "out" : "err").txt"
89+
try localFileSystem.writeFileContents(logPath.appending(component: fileName)) {
90+
$0 <<< content
91+
}
92+
}
93+
6994
public func jobFinished(job: Job, result: ProcessResult, pid: Int) {
7095
switch result.exitStatus {
7196
case .terminated(code: let code):
@@ -86,6 +111,15 @@ public class PrebuitModuleGenerationDelegate: JobExecutionDelegate {
86111
diagnosticsEngine.emit(.remark("\(job.moduleName) interrupted"))
87112
#endif
88113
}
114+
do {
115+
try logOutput(job, result, true)
116+
try logOutput(job, result, false)
117+
} catch {
118+
Driver.stdErrQueue.sync {
119+
stderrStream <<< "Failed to generate log file"
120+
stderrStream.flush()
121+
}
122+
}
89123
}
90124

91125
public func jobSkipped(job: Job) {
@@ -236,6 +270,57 @@ public struct SDKPrebuiltModuleInputsCollector {
236270
}
237271
}
238272

273+
extension InterModuleDependencyGraph {
274+
func dumpDotGraph(_ path: AbsolutePath, _ includingPCM: Bool) throws {
275+
func isPCM(_ dep: ModuleDependencyId) -> Bool {
276+
switch dep {
277+
case .clang:
278+
return true
279+
default:
280+
return false
281+
}
282+
}
283+
func dumpModuleName(_ stream: WritableByteStream, _ dep: ModuleDependencyId) {
284+
switch dep {
285+
case .swift(let name):
286+
stream <<< "\"\(name).swiftmodule\""
287+
case .clang(let name):
288+
stream <<< "\"\(name).pcm\""
289+
default:
290+
break
291+
}
292+
}
293+
try localFileSystem.writeFileContents(path) {Stream in
294+
Stream <<< "digraph {\n"
295+
for key in modules.keys {
296+
switch key {
297+
case .swift(let name):
298+
if name == mainModuleName {
299+
break
300+
}
301+
fallthrough
302+
case .clang:
303+
if !includingPCM && isPCM(key) {
304+
break
305+
}
306+
modules[key]!.directDependencies?.forEach { dep in
307+
if !includingPCM && isPCM(dep) {
308+
return
309+
}
310+
dumpModuleName(Stream, key)
311+
Stream <<< " -> "
312+
dumpModuleName(Stream, dep)
313+
Stream <<< ";\n"
314+
}
315+
default:
316+
break
317+
}
318+
}
319+
Stream <<< "}\n"
320+
}
321+
}
322+
}
323+
239324
extension Driver {
240325

241326
private mutating func generateSingleModuleBuildingJob(_ moduleName: String, _ prebuiltModuleDir: AbsolutePath,
@@ -261,7 +346,14 @@ extension Driver {
261346
commandLine.appendFlag(.Fsystem)
262347
commandLine.append(.path(iosMacFrameworksSearchPath))
263348
}
349+
// Use the specified module cache dir
350+
if let mcp = parsedOptions.getLastArgument(.moduleCachePath)?.asSingle {
351+
commandLine.appendFlag(.moduleCachePath)
352+
commandLine.append(.path(try VirtualPath(path: mcp)))
353+
}
264354
commandLine.appendFlag(.serializeParseableModuleInterfaceDependencyHashes)
355+
commandLine.appendFlag(.badFileDescriptorRetryCount)
356+
commandLine.appendFlag("30")
265357
return Job(
266358
moduleName: moduleName,
267359
kind: .compile,
@@ -275,12 +367,16 @@ extension Driver {
275367

276368
public mutating func generatePrebuitModuleGenerationJobs(with inputMap: [String: [PrebuiltModuleInput]],
277369
into prebuiltModuleDir: AbsolutePath,
278-
exhaustive: Bool) throws -> ([Job], [Job]) {
370+
exhaustive: Bool,
371+
dotGraphPath: AbsolutePath? = nil) throws -> ([Job], [Job]) {
279372
assert(sdkPath != nil)
280373
// Run the dependency scanner and update the dependency oracle with the results
281374
// We only need Swift dependencies here, so we don't need to invoke gatherModuleDependencies,
282375
// which also resolves versioned clang modules.
283376
let dependencyGraph = try performDependencyScan()
377+
if let dotGraphPath = dotGraphPath {
378+
try dependencyGraph.dumpDotGraph(dotGraphPath, false)
379+
}
284380
var jobs: [Job] = []
285381
var danglingJobs: [Job] = []
286382
var inputCount = 0

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

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,27 @@ import TSCUtility
1717

1818
let diagnosticsEngine = DiagnosticsEngine(handlers: [Driver.stderrDiagnosticsHandler])
1919

20-
guard let sdkPathRaw = ProcessEnv.vars["SDKROOT"] else {
21-
diagnosticsEngine.emit(.error("need to set SDKROOT"))
22-
exit(1)
20+
func getArgument(_ flag: String, _ env: String? = nil) -> String? {
21+
if let id = CommandLine.arguments.firstIndex(of: flag) {
22+
let nextId = id.advanced(by: 1)
23+
if nextId < CommandLine.arguments.count {
24+
return CommandLine.arguments[nextId]
25+
}
26+
}
27+
if let env = env {
28+
return ProcessEnv.vars[env]
29+
}
30+
return nil
2331
}
2432

25-
var rawOutputDir = ""
26-
if let oid = CommandLine.arguments.firstIndex(of: "-o") {
27-
let dirId = oid.advanced(by: 1)
28-
if dirId < CommandLine.arguments.count {
29-
rawOutputDir = CommandLine.arguments[dirId]
33+
func getArgumentAsPath(_ flag: String, _ env: String? = nil) throws -> AbsolutePath? {
34+
if let raw = getArgument(flag, env) {
35+
return try VirtualPath(path: raw).absolutePath
3036
}
37+
return nil
3138
}
32-
if rawOutputDir.isEmpty {
39+
40+
guard let rawOutputDir = getArgument("-o") else {
3341
diagnosticsEngine.emit(.error("need to specify -o"))
3442
exit(1)
3543
}
@@ -41,8 +49,15 @@ let coreMode = CommandLine.arguments.contains("-core")
4149
/// Verbose to print more info
4250
let verbose = CommandLine.arguments.contains("-v")
4351

52+
/// Skip executing the jobs
53+
let skipExecution = CommandLine.arguments.contains("-n")
54+
4455
do {
45-
let sdkPath = try VirtualPath(path: sdkPathRaw).absolutePath!
56+
let sdkPathArg = try getArgumentAsPath("-sdk", "SDKROOT")
57+
guard let sdkPath = sdkPathArg else {
58+
diagnosticsEngine.emit(.error("need to set SDKROOT"))
59+
exit(1)
60+
}
4661
if !localFileSystem.exists(sdkPath) {
4762
diagnosticsEngine.emit(error: "cannot find sdk: \(sdkPath.pathString)")
4863
exit(1)
@@ -56,7 +71,7 @@ do {
5671
outputDir = outputDir.appending(RelativePath(collector.versionString))
5772
}
5873
if !localFileSystem.exists(outputDir) {
59-
try localFileSystem.createDirectory(outputDir)
74+
try localFileSystem.createDirectory(outputDir, recursive: true)
6075
}
6176
let swiftcPathRaw = ProcessEnv.vars["SWIFT_EXEC"]
6277
var swiftcPath: AbsolutePath
@@ -99,15 +114,32 @@ do {
99114
processSet: processSet,
100115
fileSystem: localFileSystem,
101116
env: ProcessEnv.vars)
102-
var driver = try Driver(args: ["swiftc",
103-
"-target", collector.targetTriple,
104-
tempPath.description,
105-
"-sdk", sdkPathRaw],
117+
var args = ["swiftc",
118+
"-target", collector.targetTriple,
119+
tempPath.description,
120+
"-sdk", sdkPath.pathString]
121+
let mcpFlag = "-module-cache-path"
122+
// Append module cache path if given by the client
123+
if let mcp = getArgument(mcpFlag) {
124+
args.append(mcpFlag)
125+
args.append(mcp)
126+
}
127+
var driver = try Driver(args: args,
106128
diagnosticsEngine: diagnosticsEngine,
107129
executor: executor,
108130
compilerExecutableDir: swiftcPath.parentDirectory)
109-
let (jobs, danglingJobs) = try driver.generatePrebuitModuleGenerationJobs(with: inputMap, into: outputDir, exhaustive: !coreMode)
110-
let delegate = PrebuitModuleGenerationDelegate(jobs, diagnosticsEngine, verbose)
131+
let (jobs, danglingJobs) = try driver.generatePrebuitModuleGenerationJobs(with: inputMap,
132+
into: outputDir, exhaustive: !coreMode, dotGraphPath: getArgumentAsPath("-dot-graph-path"))
133+
if verbose {
134+
Driver.stdErrQueue.sync {
135+
stderrStream <<< "job count: \(jobs.count + danglingJobs.count)\n"
136+
stderrStream.flush()
137+
}
138+
}
139+
if skipExecution {
140+
exit(0)
141+
}
142+
let delegate = PrebuitModuleGenerationDelegate(jobs, diagnosticsEngine, verbose, logPath: try getArgumentAsPath("-log-path"))
111143
do {
112144
try executor.execute(workload: DriverExecutorWorkload.init(jobs, nil, continueBuildingAfterErrors: true),
113145
delegate: delegate, numParallelJobs: 128)

Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,8 +939,10 @@ final class ExplicitModuleBuildTests: XCTestCase {
939939
$0 <<< "import H\n"
940940
$0 <<< "import Swift\n"
941941
}
942+
let moduleCachePath = "/tmp/module-cache"
942943
var driver = try Driver(args: ["swiftc", main.pathString,
943944
"-sdk", mockSDKPath,
945+
"-module-cache-path", moduleCachePath
944946
])
945947

946948
let (jobs, danglingJobs) = try driver.generatePrebuitModuleGenerationJobs(with: interfaceMap,
@@ -955,6 +957,9 @@ final class ExplicitModuleBuildTests: XCTestCase {
955957
XCTAssertTrue(jobs.allSatisfy {$0.outputs.count == 1})
956958
XCTAssertTrue(jobs.allSatisfy {$0.kind == .compile})
957959
XCTAssertTrue(jobs.allSatisfy {$0.commandLine.contains(.flag("-compile-module-from-interface"))})
960+
XCTAssertTrue(jobs.allSatisfy {$0.commandLine.contains(.flag("-module-cache-path"))})
961+
XCTAssertTrue(jobs.allSatisfy {$0.commandLine.contains(.flag("-bad-file-descriptor-retry-count"))})
962+
XCTAssertTrue(try jobs.allSatisfy {$0.commandLine.contains(.path(try VirtualPath(path: moduleCachePath)))})
958963
let HJobs = jobs.filter { $0.moduleName == "H"}
959964
XCTAssertTrue(HJobs.count == 2)
960965
XCTAssertTrue(getInputModules(HJobs[0]) == ["A", "E", "F", "G", "Swift"])

0 commit comments

Comments
 (0)