Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ let package = Package(
.target(
name: "PrivateHeaderKitTooling",
dependencies: [
"PrivateHeaderKitExecutableResolution",
.product(
name: "Subprocess",
package: "swift-subprocess",
Expand Down Expand Up @@ -191,6 +192,7 @@ let package = Package(
.testTarget(
name: "PrivateHeaderKitRawDumpTests",
dependencies: [
"PrivateHeaderKitExecutableResolution",
"PrivateHeaderKitHelperProtocol",
"PrivateHeaderKitRawDumpCore",
"PrivateHeaderKitTestSupport",
Expand Down
183 changes: 142 additions & 41 deletions Sources/PrivateHeaderKitCLI/PrivateHeaderKitGenerationClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,80 +105,181 @@ func runPrivateHeaderKitRawDump(
_ invocation: PrivateHeaderGeneration.RawDumping.Invocation,
processRunner: any CommandRunning
) async throws -> PrivateHeaderGeneration.RawDumping.Result {
let processResult = try await processRunner.runBuffered(
invocation.command,
env: invocation.environment,
cwd: nil
)
let processResult: StreamingCommandResult
do {
processResult = try await processRunner.runBuffered(
invocation.command,
env: invocation.environment,
cwd: nil
)
} catch {
try? FileManager.default.removeItem(at: invocation.processHandshakeReportURL)
try? FileManager.default.removeItem(at: invocation.diagnosticsReportURL)
throw error
}
guard processResult.status == 0, !processResult.wasKilled else {
let handshake = consumeFailedRawDumpProcessHandshake(
at: invocation.processHandshakeReportURL,
expectedInvocationID: invocation.processHandshakeID
)
try? FileManager.default.removeItem(at: invocation.diagnosticsReportURL)
let recognizesSimulatorChildTermination: Bool
if case .simulator = invocation.executionMode {
recognizesSimulatorChildTermination = true
} else {
recognizesSimulatorChildTermination = false
}
return PrivateHeaderGeneration.RawDumping.Result(
terminationStatus: processResult.status,
wasKilled: processResult.wasKilled,
failureSummary: processResult.lastLines.isEmpty
? nil
: processResult.lastLines.joined(separator: "\n")
failureSummary: RawHelperFailureCapsule(
processResult: processResult,
handshake: handshake,
recognizesSimulatorChildTermination: recognizesSimulatorChildTermination
).text
)
}

let report = try consumeRawDumpDiagnosticsReport(
at: invocation.diagnosticsReportURL
)
do {
_ = try consumeSuccessfulRawDumpProcessHandshake(
at: invocation.processHandshakeReportURL,
expectedInvocationID: invocation.processHandshakeID
)
} catch {
try? FileManager.default.removeItem(at: invocation.diagnosticsReportURL)
throw error
}
let report = try consumeRawDumpDiagnosticsReport(at: invocation.diagnosticsReportURL)
return PrivateHeaderGeneration.RawDumping.Result(
terminationStatus: processResult.status,
wasKilled: processResult.wasKilled,
failureSummary: processResult.lastLines.isEmpty
? nil
: processResult.lastLines.joined(separator: "\n"),
failureSummary: nil,
diagnosticsReport: report
)
}

private func consumeRawDumpDiagnosticsReport(
private func consumeSuccessfulRawDumpProcessHandshake(
at reportURL: URL,
expectedInvocationID: UUID,
fileManager: FileManager = .default
) throws -> PrivateHeaderKitRawDumpDiagnosticsReport {
let path = reportURL.path
guard fileManager.fileExists(atPath: path) else {
throw PrivateHeaderGeneration.RawDumping.ContractError.missingDiagnosticsReport(path)
) throws -> PrivateHeaderKitRawDumpProcessHandshake {
let handshake: PrivateHeaderKitRawDumpProcessHandshake
do {
handshake = try readRawDumpProcessHandshake(
at: reportURL,
expectedInvocationID: expectedInvocationID
)
} catch {
try? fileManager.removeItem(at: reportURL)
throw error
}

do {
try fileManager.removeItem(at: reportURL)
} catch {
throw PrivateHeaderGeneration.RawDumping.ContractError
.processHandshakeCleanupFailed(
path: reportURL.path,
reason: String(describing: error)
)
}
return handshake
}

private func consumeFailedRawDumpProcessHandshake(
at reportURL: URL,
expectedInvocationID: UUID,
fileManager: FileManager = .default
) -> RawHelperFailureCapsule.HandshakeObservation {
defer { try? fileManager.removeItem(at: reportURL) }
do {
return .available(
try readRawDumpProcessHandshake(
at: reportURL,
expectedInvocationID: expectedInvocationID
)
)
} catch PrivateHeaderGeneration.RawDumping.ContractError.missingProcessHandshake {
return .missing
} catch {
return .invalid
}
}

private func readRawDumpProcessHandshake(
at reportURL: URL,
expectedInvocationID: UUID
) throws -> PrivateHeaderKitRawDumpProcessHandshake {
let path = reportURL.path
let data: Data
do {
let values = try reportURL.resourceValues(forKeys: [
.isRegularFileKey,
.fileSizeKey,
])
guard values.isRegularFile == true else {
throw PrivateHeaderGeneration.RawDumping.ContractError.invalidDiagnosticsReport(
data = try RawDumpReportIO.read(
at: reportURL,
maximumByteCount: PrivateHeaderKitRawDumpProcessHandshake.maximumEncodedByteCount
)
} catch let error as RawDumpReportIO.Failure {
switch error {
case .missing:
throw PrivateHeaderGeneration.RawDumping.ContractError
.missingProcessHandshake(path)
case .tooLarge(let actual):
throw PrivateHeaderGeneration.RawDumping.ContractError.processHandshakeTooLarge(
path: path,
reason: "report is not a regular file"
actual: actual,
maximum: PrivateHeaderKitRawDumpProcessHandshake.maximumEncodedByteCount
)
}
guard let fileSize = values.fileSize else {
throw PrivateHeaderGeneration.RawDumping.ContractError.invalidDiagnosticsReport(
default:
throw PrivateHeaderGeneration.RawDumping.ContractError.invalidProcessHandshake(
path: path,
reason: "report size is unavailable"
reason: error.description
)
}
guard fileSize <= PrivateHeaderKitRawDumpDiagnosticsReport.maximumEncodedByteCount else {
}

do {
return try PrivateHeaderKitRawDumpProcessHandshake.decode(
data,
expectedInvocationID: expectedInvocationID
)
} catch let error as PrivateHeaderGeneration.RawDumping.ContractError {
throw error
} catch {
throw PrivateHeaderGeneration.RawDumping.ContractError.invalidProcessHandshake(
path: path,
reason: String(describing: error)
)
}
}

private func consumeRawDumpDiagnosticsReport(
at reportURL: URL,
fileManager: FileManager = .default
) throws -> PrivateHeaderKitRawDumpDiagnosticsReport {
let path = reportURL.path
let data: Data
do {
data = try RawDumpReportIO.read(
at: reportURL,
maximumByteCount: PrivateHeaderKitRawDumpDiagnosticsReport.maximumEncodedByteCount
)
} catch let error as RawDumpReportIO.Failure {
try? fileManager.removeItem(at: reportURL)
switch error {
case .missing:
throw PrivateHeaderGeneration.RawDumping.ContractError
.missingDiagnosticsReport(path)
case .tooLarge(let actual):
throw PrivateHeaderGeneration.RawDumping.ContractError.diagnosticsReportTooLarge(
path: path,
actual: fileSize,
actual: actual,
maximum: PrivateHeaderKitRawDumpDiagnosticsReport.maximumEncodedByteCount
)
}
data = try Data(contentsOf: reportURL)
guard data.count <= PrivateHeaderKitRawDumpDiagnosticsReport.maximumEncodedByteCount else {
throw PrivateHeaderGeneration.RawDumping.ContractError.diagnosticsReportTooLarge(
default:
throw PrivateHeaderGeneration.RawDumping.ContractError.invalidDiagnosticsReport(
path: path,
actual: data.count,
maximum: PrivateHeaderKitRawDumpDiagnosticsReport.maximumEncodedByteCount
reason: error.description
)
}
} catch let error as PrivateHeaderGeneration.RawDumping.ContractError {
try? fileManager.removeItem(at: reportURL)
throw error
} catch {
try? fileManager.removeItem(at: reportURL)
throw PrivateHeaderGeneration.RawDumping.ContractError.invalidDiagnosticsReport(
Expand Down
Loading