File: SignatureCollectionTaskAction.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 (160 lines) | stat: -rw-r--r-- 6,171 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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

import struct Foundation.Data
import struct Foundation.URL
import class Foundation.PropertyListEncoder

public import SWBCore
import SWBLibc
public import SWBUtil

/// Pulls out the relevant signature information from a library and stores it in an external metadata file that can be collected later.
public final class SignatureCollectionTaskAction: TaskAction {
    public override class var toolIdentifier: String {
        return "signature-collection"
    }

    fileprivate struct Options {
        static func emitUsage(_ name: String, _ outputDelegate: any TaskOutputDelegate) {
            outputDelegate.emitOutput { stream in
                stream <<< "usage: \(name) --input <path> --output <path> --outdir <path> -- {input-file}*\n"
                stream <<< "  --input <path>\n"
                stream <<< "      Specify the library to gather signature metadata for.\n"
                stream <<< "  --output <path>\n"
                stream <<< "      The output file for the signature metadata file.\n"
                stream <<< "  --info <key> <value>\n"
                stream <<< "      Additional data that should be added to the signature metadata file.\n"
            }
        }

        let input: Path
        let output: Path
        let additionalInfo: [String:String]
        let skipValidation: Bool

        init?(_ commandLine: AnySequence<String>, _ outputDelegate: any TaskOutputDelegate) {
            var inputArg: String? = nil
            var outputArg: String? = nil
            var additionalInfo: [String:String] = [:]
            var skipSignatureValidation: Bool = false

            var hadErrors: Bool = false
            func error(_ message: String) {
                outputDelegate.emitError(message)
                hadErrors = true
            }

            // Parse the arguments until we reach a '--'.
            let generator = commandLine.makeIterator()

            // Skip the executable.
            let programName = generator.next() ?? "<<missing program name>>"
            argumentParsing: while let arg = generator.next() {
                switch arg {
                case "--":
                    break argumentParsing

                case "--input":
                    guard let value = generator.next() else {
                        error("missing argument for option: \(arg)")
                        continue
                    }
                    inputArg = value

                case "--output":
                    guard let value = generator.next() else {
                        error("missing argument for option: \(arg)")
                        continue
                    }
                    outputArg = value

                // --info baseName foo.framework
                case "--info":
                    guard let key = generator.next() else {
                        error("missing key argument for option: \(arg)")
                        continue
                    }
                    guard let value = generator.next() else {
                        error("missing value argument for option: \(arg)")
                        continue
                    }
                    additionalInfo[key] = value

                // This is primarily used as an internal mechanism to disable verification for testing purposes.
                case "--skip-signature-validation":
                    skipSignatureValidation = true

                default:
                    error("unrecognized argument: \(arg)")
                }
            }

            if !hadErrors && inputArg == nil {
                error("missing required argument: --input")
            }

            if !hadErrors && outputArg == nil {
                error("missing required argument: --output")
            }

            if hadErrors {
                outputDelegate.emitOutput("\n")
                Options.emitUsage(programName, outputDelegate)
                return nil
            }

            self.input = Path(inputArg!)
            self.output = Path(outputArg!)
            self.additionalInfo = additionalInfo
            self.skipValidation = skipSignatureValidation
        }
    }

    public override init() {
        super.init()
    }

    override public func performTaskAction(_ task: any ExecutableTask, dynamicExecutionDelegate: any DynamicTaskExecutionDelegate, executionDelegate: any TaskExecutionDelegate, clientDelegate: any TaskExecutionClientDelegate, outputDelegate: any TaskOutputDelegate) async -> CommandResult {

        guard let options = Options(task.commandLineAsStrings, outputDelegate) else {
            return .failed
        }

        do {
            let info = try await CodeSignatureInfo.load(from: options.input, additionalInfo: options.additionalInfo.isEmpty ? nil : options.additionalInfo, skipValidation: options.skipValidation)
            let encoder = PropertyListEncoder()
            encoder.outputFormat = .xml
            let data = try encoder.encode(info)

            let base = options.output.dirname
            try executionDelegate.fs.createDirectory(base, recursive: true)
            try executionDelegate.fs.write(options.output, contents: ByteString(data))
        }
        catch {
            outputDelegate.emitError("signature-collection failed: \(error.localizedDescription)")
            return .failed
        }

        return .succeeded
    }

    // Serialization

    public override func serialize<T: Serializer>(to serializer: T) {
        super.serialize(to: serializer)
    }

    public required init(from deserializer: any Deserializer) throws {
        try super.init(from: deserializer)
    }
}