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)
}
}
|