Skip to content
Closed
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
4 changes: 4 additions & 0 deletions Sources/SWBBuildSystem/BuildOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,10 @@ extension BuildOperation: TaskExecutionDelegate {
package var emitFrontendCommandLines: Bool {
buildDescription.emitFrontendCommandLines
}

package var continueBuildingAfterErrors: Bool {
request.continueBuildingAfterErrors
}
}

// BuildOperation uses reference semantics.
Expand Down
3 changes: 2 additions & 1 deletion Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1368,7 +1368,8 @@ public class ClangCompilerSpec : CompilerSpec, SpecIdentifierType, GCCCompatible
extraInputs = []
}

if let moduleDependenciesContext {
// If validation is set to `YES_ERROR`, individual Clang tasks emit diagnostics, so they need this data in their signature. Otherwise, diagnostics are handled by the per-target validation action so individual compiles are not affected by changes to validation-related build settings.
if let moduleDependenciesContext, moduleDependenciesContext.validate == .yesError {
do {
let jsonData = try JSONEncoder(outputFormatting: [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]).encode(moduleDependenciesContext)
guard let signature = String(data: jsonData, encoding: .utf8) else {
Expand Down
19 changes: 1 addition & 18 deletions Sources/SWBCore/SpecImplementations/Tools/SwiftCompiler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2405,25 +2405,8 @@ public final class SwiftCompilerSpec : CompilerSpec, SpecIdentifierType, SwiftDi
dependencyData = nil
}

let compilationRequirementAdditionalSignatureData: String
if let moduleDependenciesContext = cbc.producer.moduleDependenciesContext {
do {
let jsonData = try JSONEncoder(outputFormatting: [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]).encode(moduleDependenciesContext)
guard let signature = String(data: jsonData, encoding: .utf8) else {
throw StubError.error("non-UTF-8 data")
}
compilationRequirementAdditionalSignatureData = additionalSignatureData + "|\(signature)"
} catch {
delegate.error("failed to serialize 'MODULE_DEPENDENCIES' context information: \(error)")
return
}
}
else {
compilationRequirementAdditionalSignatureData = additionalSignatureData
}

// Compilation Requirements
delegate.createTask(type: self, dependencyData: dependencyData, payload: payload, ruleInfo: ruleInfo("SwiftDriver Compilation Requirements", targetName), additionalSignatureData: compilationRequirementAdditionalSignatureData, commandLine: ["builtin-Swift-Compilation-Requirements", "--"] + args, environment: environmentBindings, workingDirectory: compilerWorkingDirectory(cbc), inputs: allInputsNodes, outputs: compilationRequirementOutputs, action: delegate.taskActionCreationDelegate.createSwiftCompilationRequirementTaskAction(), execDescription: archSpecificExecutionDescription(cbc.scope.namespace.parseString("Unblock downstream dependents of $PRODUCT_NAME"), cbc, delegate), preparesForIndexing: true, enableSandboxing: enableSandboxing, additionalTaskOrderingOptions: [.compilation, .compilationRequirement, .linkingRequirement, .blockedByTargetHeaders, .compilationForIndexableSourceFile], usesExecutionInputs: true, showInLog: true)
delegate.createTask(type: self, dependencyData: dependencyData, payload: payload, ruleInfo: ruleInfo("SwiftDriver Compilation Requirements", targetName), additionalSignatureData: additionalSignatureData, commandLine: ["builtin-Swift-Compilation-Requirements", "--"] + args, environment: environmentBindings, workingDirectory: compilerWorkingDirectory(cbc), inputs: allInputsNodes, outputs: compilationRequirementOutputs, action: delegate.taskActionCreationDelegate.createSwiftCompilationRequirementTaskAction(), execDescription: archSpecificExecutionDescription(cbc.scope.namespace.parseString("Unblock downstream dependents of $PRODUCT_NAME"), cbc, delegate), preparesForIndexing: true, enableSandboxing: enableSandboxing, additionalTaskOrderingOptions: [.compilation, .compilationRequirement, .linkingRequirement, .blockedByTargetHeaders, .compilationForIndexableSourceFile], usesExecutionInputs: true, showInLog: true)

if case .compile = compilationMode {
// Unblocking compilation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

private import Foundation
public import SWBUtil
import SWBMacro

Expand Down Expand Up @@ -39,8 +40,21 @@ public final class ValidateDependenciesSpec: CommandLineToolSpec, SpecImplementa
guard let configuredTarget = cbc.producer.configuredTarget else {
return
}

let jsonData: Data
do {
jsonData = try JSONEncoder(outputFormatting: [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]).encode(payload)
} catch {
delegate.error("Error serializing \(payload): \(error)")
return
}
guard let signature = String(data: jsonData, encoding: .utf8) else {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always use String(decoding: jsonData, as: UTF8.self) which is non-failable

delegate.error("non-UTF-8 data in signature for ValidateDependencies")
return
}

let output = delegate.createVirtualNode("ValidateDependencies \(configuredTarget.guid)")
delegate.createTask(type: self, payload: payload, ruleInfo: ["ValidateDependencies"], commandLine: ["builtin-validate-dependencies"] + dependencyInfos.map { $0.path.str }, environment: EnvironmentBindings(), workingDirectory: cbc.producer.defaultWorkingDirectory, inputs: dependencyInfos + cbc.commandOrderingInputs, outputs: [output], action: delegate.taskActionCreationDelegate.createValidateDependenciesTaskAction(), preparesForIndexing: false, enableSandboxing: false)
delegate.createTask(type: self, payload: payload, ruleInfo: ["ValidateDependencies"], additionalSignatureData: signature, commandLine: ["builtin-validate-dependencies"] + dependencyInfos.map { $0.path.str }, environment: EnvironmentBindings(), workingDirectory: cbc.producer.defaultWorkingDirectory, inputs: dependencyInfos + cbc.commandOrderingInputs, outputs: [output], action: delegate.taskActionCreationDelegate.createValidateDependenciesTaskAction(), preparesForIndexing: false, enableSandboxing: false)
}
}

Expand Down
2 changes: 2 additions & 0 deletions Sources/SWBTaskExecution/Task.swift
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,8 @@ public protocol TaskExecutionDelegate

var namespace: MacroNamespace { get }

var continueBuildingAfterErrors: Bool { get }

/// Notifies the delegate that this task has discovered a target dependency which must exist to ensure deterministic builds.
func taskDiscoveredRequiredTargetDependency(target: ConfiguredTarget, antecedent: ConfiguredTarget, reason: RequiredTargetDependencyReason, warningLevel: BooleanWarningLevel)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,20 @@ public final class ClangCompileTaskAction: TaskAction, BuildValueValidatingTaskA
moduleDependenciesContext = nil
dependencyValidationOutputPath = nil
}

// Emit per-task diagnostics if validation is set to `YES_ERROR` and we're not continuing to build after errors.
let moduleDependenciesEmitPerTaskDiagnostics = moduleDependenciesContext?.validate == .yesError && !executionDelegate.continueBuildingAfterErrors
if moduleDependenciesEmitPerTaskDiagnostics, let dependencyValidationOutputPath {
// Since we're emitting per task diagnostics, communicate empty diagnostics to the validation task.
let validationInfo = DependencyValidationInfo(payload: .clangDependencies(files: []))
_ = try executionDelegate.fs.writeIfChanged(
dependencyValidationOutputPath,
contents: ByteString(
JSONEncoder(outputFormatting: .sortedKeys).encode(validationInfo)
)
)
}

if let traceFilePath {
// Remove the trace output file if it already exists.
if executionDelegate.fs.exists(traceFilePath) {
Expand Down Expand Up @@ -325,28 +339,41 @@ public final class ClangCompileTaskAction: TaskAction, BuildValueValidatingTaskA
}
}

if lastResult == .succeeded {
if lastResult == .succeeded, let moduleDependenciesContext {
// Verify the dependencies from the trace data.
let payload: DependencyValidationInfo.Payload
var allFiles = Set<Path>()
if let traceFilePath {
let fs = executionDelegate.fs
let traceData = try JSONDecoder().decode(Array<TraceData>.self, from: Data(fs.read(traceFilePath)))

var allFiles = Set<Path>()
traceData.forEach { allFiles.formUnion(Set($0.includes)) }
payload = .clangDependencies(files: allFiles.map { $0.str })
} else {
payload = .unsupported
}

if let dependencyValidationOutputPath {
let validationInfo = DependencyValidationInfo(payload: payload)
_ = try executionDelegate.fs.writeIfChanged(
dependencyValidationOutputPath,
contents: ByteString(
JSONEncoder(outputFormatting: .sortedKeys).encode(validationInfo)
if moduleDependenciesEmitPerTaskDiagnostics {
let missingDeps = moduleDependenciesContext.computeMissingDependencies(files: Array(allFiles))
let diagnostics = moduleDependenciesContext.makeDiagnostics(missingDependencies: missingDeps)

for diagnostic in diagnostics {
outputDelegate.emit(diagnostic)
}

if diagnostics.contains(where: { $0.behavior == .error }) {
return .failed
}
} else {
if let dependencyValidationOutputPath {
let validationInfo = DependencyValidationInfo(payload: payload)
_ = try executionDelegate.fs.writeIfChanged(
dependencyValidationOutputPath,
contents: ByteString(
JSONEncoder(outputFormatting: .sortedKeys).encode(validationInfo)
)
)
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ struct MockExecutionDelegate: TaskExecutionDelegate {
var namespace: MacroNamespace
var requestContext: SWBCore.BuildRequestContext { fatalError() }
var emitFrontendCommandLines: Bool { false }
var continueBuildingAfterErrors: Bool { false }
private var core: Core?

func taskDiscoveredRequiredTargetDependency(target: ConfiguredTarget, antecedent: ConfiguredTarget, reason: RequiredTargetDependencyReason, warningLevel: BooleanWarningLevel) {}
Expand Down