File: DeferredExecutionTaskAction.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (70 lines) | stat: -rw-r--r-- 3,680 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

public import SWBCore
import SWBUtil
public import SWBLLBuild
import Foundation
import SWBProtocol

public final class DeferredExecutionTaskAction: TaskAction {
    public override class var toolIdentifier: String {
        "deferrable-shell-task"
    }

    public override func performTaskAction(_ task: any ExecutableTask, dynamicExecutionDelegate: any DynamicTaskExecutionDelegate, executionDelegate: any TaskExecutionDelegate, clientDelegate: any TaskExecutionClientDelegate, outputDelegate: any TaskOutputDelegate) async -> CommandResult {
        let processDelegate = TaskProcessDelegate(outputDelegate: outputDelegate)
        do {
            try await spawn(commandLine: Array(task.commandLineAsStrings), environment: task.environment.bindingsDictionary, workingDirectory: task.workingDirectory, dynamicExecutionDelegate: dynamicExecutionDelegate, clientDelegate: clientDelegate, processDelegate: processDelegate)
        } catch {
            outputDelegate.error(error.localizedDescription)
            return .failed
        }
        if let error = processDelegate.executionError {
            outputDelegate.error(error)
            return .failed
        }
        return processDelegate.commandResult ?? .failed
    }
}

fileprivate extension CommandResult {
    init(_ exitStatus: Processes.ExitStatus) {
        if exitStatus.isSuccess {
            self = .succeeded
        } else if exitStatus.wasCanceled {
            self = .cancelled
        } else {
            self = .failed
        }
    }
}

extension TaskAction {
    func spawn(commandLine: [String], environment: [String: String], workingDirectory: Path, dynamicExecutionDelegate: any DynamicTaskExecutionDelegate, clientDelegate: any TaskExecutionClientDelegate, processDelegate: any ProcessDelegate) async throws {
        guard dynamicExecutionDelegate.allowsExternalToolExecution else {
            try await dynamicExecutionDelegate.spawn(commandLine: commandLine, environment: environment, workingDirectory: workingDirectory, processDelegate: processDelegate)
            return
        }
        switch try await clientDelegate.executeExternalTool(commandLine: commandLine, workingDirectory: workingDirectory, environment: environment) {
        case .deferred:
            try await dynamicExecutionDelegate.spawn(commandLine: commandLine, environment: environment, workingDirectory: workingDirectory, processDelegate: processDelegate)
        case let .result(status, stdout, stderr):
            // NOTE: This is not strictly correct, as we really should forward the merged output in the same order it was emitted, rather than all of stdout and then all of stderr. But we need much better APIs in order to do that, and for the current (Swift Playgrounds) use case it shouldn't matter in practice.
            let pid = llbuild_pid_t.invalid
            processDelegate.processStarted(pid: pid)
            processDelegate.processHadOutput(output: Array(stdout))
            processDelegate.processHadOutput(output: Array(stderr))
            processDelegate.processFinished(result: .init(result: .init(status), exitStatus: status.isSuccess ? 0 : 1, pid: pid))
        }
    }
}