Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/update-logs-on-multijobexecutor #1661

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 53 additions & 29 deletions Sources/SwiftDriverExecution/MultiJobExecutor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import struct TSCBasic.Diagnostic
import struct TSCBasic.ProcessResult
import enum TSCUtility.Diagnostics

// We either import the llbuildSwift shared library or the llbuild framework.
#if canImport(llbuildSwift)
@_implementationOnly import llbuildSwift
@_implementationOnly import llbuild
Expand Down Expand Up @@ -345,6 +344,48 @@ public final class MultiJobExecutor {
}
}

extension ProcessResult {
func utf8stderrOutput() throws -> String {
let stderrData = try self.utf8stderrOutput().get()
return String(decoding: stderrData, as: UTF8.self)
}
}

extension DiagnosticsEngine {
func emit(_ diagnostic: Diagnostic) {
self.emit(diagnostic)
}

static func error_command_failed(kind: Job.Kind, code: Int32, stderr: String) -> Diagnostic {
return Diagnostic(
severity: .error,
message: "command failed with exit code \(code): \(stderr)"
)
}

static func error_command_exception(kind: Job.Kind, exception: String) -> Diagnostic {
return Diagnostic(
severity: .error,
message: "command failed with exception: \(exception)"
)
}

static func error_command_signalled(kind: Job.Kind, signal: Int32, stderr: String) -> Diagnostic {
return Diagnostic(
severity: .error,
message: "command terminated by signal \(signal): \(stderr)"
)
}

static func error_unexpected(error: String) -> Diagnostic {
return Diagnostic(
severity: .error,
message: "unexpected error: \(error)"
)
}
}


struct JobExecutorBuildDelegate: LLBuildEngineDelegate {

let context: MultiJobExecutor.Context
Expand Down Expand Up @@ -592,8 +633,7 @@ class ExecuteJobRule: LLBuildRule {
let arguments: [String] = try resolver.resolveArgumentList(for: job,
useResponseFiles: context.forceResponseFiles ? .forced : .heuristic)


let process : ProcessProtocol
let process: ProcessProtocol
// If the input comes from standard input, forward the driver's input to the compile job.
if job.inputs.contains(TypedVirtualPath(file: .standardInput, type: .swift)) {
let inputFileHandle = context.testInputHandle ?? FileHandle.standardInput
Expand All @@ -609,7 +649,7 @@ class ExecuteJobRule: LLBuildRule {
pid = Int(process.processID)

// Add it to the process set if it's a real process.
if case let realProcess as TSCBasic.Process = process {
if let realProcess = process as? TSCBasic.Process {
try context.processSet?.add(realProcess)
}

Expand All @@ -623,11 +663,12 @@ class ExecuteJobRule: LLBuildRule {
let success = result.exitStatus == .terminated(code: EXIT_SUCCESS)

if !success {
let stderr = try result.utf8stderrOutput()
job.removeOutputsOfFailedCompilation(from: context.fileSystem)
switch result.exitStatus {
case let .terminated(code):
if !job.kind.isCompile || code != EXIT_FAILURE {
context.diagnosticsEngine.emit(.error_command_failed(kind: job.kind, code: code))
context.diagnosticsEngine.emit(.error_command_failed(kind: job.kind, code: code, stderr: stderr))
}
#if os(Windows)
case let .abnormal(exception):
Expand All @@ -636,42 +677,25 @@ class ExecuteJobRule: LLBuildRule {
case let .signalled(signal):
// An interrupt of an individual compiler job means it was deliberately cancelled,
// most likely by the driver itself. This does not constitute an error.
if signal != SIGINT {
context.diagnosticsEngine.emit(.error_command_signalled(kind: job.kind, signal: signal))
}
context.diagnosticsEngine.emit(.error_command_signalled(kind: job.kind, signal: signal, stderr: stderr))
#endif
}
}

// Inform the delegate about job finishing.
context.cancelBuildIfNeeded(result)
context.delegateQueue.sync {
context.executorDelegate.jobFinished(job: job, result: result, pid: pid)
}
pendingFinish = false
context.cancelBuildIfNeeded(result)

value = .jobExecution(success: success)
} catch {
if error is DiagnosticData {
context.diagnosticsEngine.emit(error)
}
// Only inform finished job if the job has been started, otherwise the build
// system may complain about malformed output
if (pendingFinish) {
context.delegateQueue.sync {
let result = ProcessResult(
arguments: [],
environment: env,
exitStatus: .terminated(code: EXIT_FAILURE),
output: Result.success([]),
stderrOutput: Result.success([])
)
context.executorDelegate.jobFinished(job: job, result: result, pid: pid)
}
}
context.diagnosticsEngine.emit(.error_unexpected(error: error.localizedDescription))
value = .jobExecution(success: false)
}

engine.taskIsComplete(value)
if pendingFinish {
engine.taskIsComplete(value)
}
}
}

Expand Down