diff --git a/Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift b/Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift index 7e33921d1..0afa782d2 100644 --- a/Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift +++ b/Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift @@ -24,10 +24,12 @@ public class PrebuitModuleGenerationDelegate: JobExecutionDelegate { let diagnosticsEngine: DiagnosticsEngine let verbose: Bool var failingCriticalOutputs: Set - public init(_ jobs: [Job], _ diagnosticsEngine: DiagnosticsEngine, _ verbose: Bool) { + let logPath: AbsolutePath? + public init(_ jobs: [Job], _ diagnosticsEngine: DiagnosticsEngine, _ verbose: Bool, logPath: AbsolutePath?) { self.diagnosticsEngine = diagnosticsEngine self.verbose = verbose self.failingCriticalOutputs = Set(jobs.compactMap(PrebuitModuleGenerationDelegate.getCriticalOutput)) + self.logPath = logPath } /// Dangling jobs are macabi-only modules. We should run those jobs if foundation @@ -35,22 +37,27 @@ public class PrebuitModuleGenerationDelegate: JobExecutionDelegate { public var shouldRunDanglingJobs: Bool { return !failingCriticalOutputs.contains(where: isIosMac) } - func printJobInfo(_ job: Job, _ start: Bool) { - guard verbose else { - return - } + + func getInputInterfacePath(_ job: Job) -> AbsolutePath { for arg in job.commandLine { if case .path(let p) = arg { if p.extension == "swiftinterface" { - Driver.stdErrQueue.sync { - stderrStream <<< (start ? "started: " : "finished: ") - stderrStream <<< p.absolutePath!.pathString <<< "\n" - stderrStream.flush() - } - return + return p.absolutePath! } } } + fatalError() + } + + func printJobInfo(_ job: Job, _ start: Bool) { + guard verbose else { + return + } + Driver.stdErrQueue.sync { + stderrStream <<< (start ? "started: " : "finished: ") + stderrStream <<< getInputInterfacePath(job).pathString <<< "\n" + stderrStream.flush() + } } static func getCriticalOutput(_ job: Job) -> VirtualPath? { @@ -66,6 +73,24 @@ public class PrebuitModuleGenerationDelegate: JobExecutionDelegate { return !failingCriticalOutputs.isEmpty } + fileprivate func logOutput(_ job: Job, _ result: ProcessResult, _ stdout: Bool) throws { + guard let logPath = logPath else { + return + } + let content = stdout ? try result.utf8Output() : try result.utf8stderrOutput() + guard !content.isEmpty else { + return + } + if !localFileSystem.exists(logPath) { + try localFileSystem.createDirectory(logPath, recursive: true) + } + let interfaceBase = getInputInterfacePath(job).basenameWithoutExt + let fileName = "\(job.moduleName)-\(interfaceBase)-\(stdout ? "out" : "err").txt" + try localFileSystem.writeFileContents(logPath.appending(component: fileName)) { + $0 <<< content + } + } + public func jobFinished(job: Job, result: ProcessResult, pid: Int) { switch result.exitStatus { case .terminated(code: let code): @@ -86,6 +111,15 @@ public class PrebuitModuleGenerationDelegate: JobExecutionDelegate { diagnosticsEngine.emit(.remark("\(job.moduleName) interrupted")) #endif } + do { + try logOutput(job, result, true) + try logOutput(job, result, false) + } catch { + Driver.stdErrQueue.sync { + stderrStream <<< "Failed to generate log file" + stderrStream.flush() + } + } } public func jobSkipped(job: Job) { diff --git a/Sources/swift-build-sdk-interfaces/main.swift b/Sources/swift-build-sdk-interfaces/main.swift index 6cb8418df..3521a9863 100644 --- a/Sources/swift-build-sdk-interfaces/main.swift +++ b/Sources/swift-build-sdk-interfaces/main.swift @@ -137,7 +137,7 @@ do { if skipExecution { exit(0) } - let delegate = PrebuitModuleGenerationDelegate(jobs, diagnosticsEngine, verbose) + let delegate = PrebuitModuleGenerationDelegate(jobs, diagnosticsEngine, verbose, logPath: try getArgumentAsPath("-log-path")) do { try executor.execute(workload: DriverExecutorWorkload.init(jobs, nil, continueBuildingAfterErrors: true), delegate: delegate, numParallelJobs: 128)