From 3c5f7fca64e549c022e317792ea15066f8cd51b5 Mon Sep 17 00:00:00 2001 From: Volodymyr Sapsai Date: Tue, 20 May 2025 16:50:43 -0700 Subject: [PATCH] [Explicit Module Builds] Add a setting to allow creating a reproducer for errors. The idea is the same as Clang flag `-gen-reproducer=error` but for explicit module builds. The name of the setting is a subject to change depending on how it ends up being used. Not all module-related issues are crashes, so it is useful to allow creating and sharing reproducers for errors. rdar://158780565 --- Sources/SWBCore/Settings/BuiltinMacros.swift | 2 ++ .../SpecImplementations/Tools/CCompiler.swift | 13 +++++++++---- Sources/SWBCore/TaskResult.swift | 7 +++++++ .../TaskActions/ClangCompileTaskAction.swift | 5 ++++- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Sources/SWBCore/Settings/BuiltinMacros.swift b/Sources/SWBCore/Settings/BuiltinMacros.swift index 6ed2af16..432b3a5c 100644 --- a/Sources/SWBCore/Settings/BuiltinMacros.swift +++ b/Sources/SWBCore/Settings/BuiltinMacros.swift @@ -508,6 +508,7 @@ public final class BuiltinMacros { public static let CLANG_EXPLICIT_MODULES_IGNORE_LIBCLANG_VERSION_MISMATCH = BuiltinMacros.declareBooleanMacro("CLANG_EXPLICIT_MODULES_IGNORE_LIBCLANG_VERSION_MISMATCH") public static let CLANG_EXPLICIT_MODULES_OUTPUT_PATH = BuiltinMacros.declarePathMacro("CLANG_EXPLICIT_MODULES_OUTPUT_PATH") public static let SWIFT_EXPLICIT_MODULES_OUTPUT_PATH = BuiltinMacros.declarePathMacro("SWIFT_EXPLICIT_MODULES_OUTPUT_PATH") + public static let CLANG_EXPLICIT_MODULES_ENABLE_REPRODUCER_FOR_ERRORS = BuiltinMacros.declareBooleanMacro("_EXPERIMENTAL_CLANG_EXPLICIT_MODULES_ENABLE_REPRODUCER_FOR_ERRORS") public static let CLANG_ENABLE_COMPILE_CACHE = BuiltinMacros.declareBooleanMacro("CLANG_ENABLE_COMPILE_CACHE") public static let CLANG_CACHE_FINE_GRAINED_OUTPUTS = BuiltinMacros.declareEnumMacro("CLANG_CACHE_FINE_GRAINED_OUTPUTS") as EnumMacroDeclaration public static let CLANG_CACHE_FINE_GRAINED_OUTPUTS_VERIFICATION = BuiltinMacros.declareEnumMacro("CLANG_CACHE_FINE_GRAINED_OUTPUTS_VERIFICATION") as EnumMacroDeclaration @@ -1510,6 +1511,7 @@ public final class BuiltinMacros { CLANG_EXPLICIT_MODULES_IGNORE_LIBCLANG_VERSION_MISMATCH, CLANG_EXPLICIT_MODULES_OUTPUT_PATH, SWIFT_EXPLICIT_MODULES_OUTPUT_PATH, + CLANG_EXPLICIT_MODULES_ENABLE_REPRODUCER_FOR_ERRORS, CLANG_EXTRACT_API_EXEC, CLANG_GENERATE_OPTIMIZATION_REMARKS, CLANG_GENERATE_OPTIMIZATION_REMARKS_FILTER, diff --git a/Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift b/Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift index 537358d5..a6e472a3 100644 --- a/Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift +++ b/Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift @@ -361,8 +361,9 @@ public struct ClangExplicitModulesPayload: Serializable, Encodable, Sendable { public let dependencyFilteringRootPath: Path? public let reportRequiredTargetDependencies: BooleanWarningLevel public let verifyingModule: String? + public let shouldGenerateReproducerForErrors: Bool - fileprivate init(uniqueID: String, sourcePath: Path, libclangPath: Path, usesCompilerLauncher: Bool, outputPath: Path, scanningOutputPath: Path, casOptions: CASOptions?, cacheFallbackIfNotAvailable: Bool, dependencyFilteringRootPath: Path?, reportRequiredTargetDependencies: BooleanWarningLevel, verifyingModule: String?) { + fileprivate init(uniqueID: String, sourcePath: Path, libclangPath: Path, usesCompilerLauncher: Bool, outputPath: Path, scanningOutputPath: Path, casOptions: CASOptions?, cacheFallbackIfNotAvailable: Bool, dependencyFilteringRootPath: Path?, reportRequiredTargetDependencies: BooleanWarningLevel, verifyingModule: String?, shouldGenerateReproducerForErrors: Bool) { self.uniqueID = uniqueID self.sourcePath = sourcePath self.libclangPath = libclangPath @@ -374,10 +375,11 @@ public struct ClangExplicitModulesPayload: Serializable, Encodable, Sendable { self.dependencyFilteringRootPath = dependencyFilteringRootPath self.reportRequiredTargetDependencies = reportRequiredTargetDependencies self.verifyingModule = verifyingModule + self.shouldGenerateReproducerForErrors = shouldGenerateReproducerForErrors } public func serialize(to serializer: T) { - serializer.serializeAggregate(11) { + serializer.serializeAggregate(12) { serializer.serialize(uniqueID) serializer.serialize(sourcePath) serializer.serialize(libclangPath) @@ -389,11 +391,12 @@ public struct ClangExplicitModulesPayload: Serializable, Encodable, Sendable { serializer.serialize(dependencyFilteringRootPath) serializer.serialize(reportRequiredTargetDependencies) serializer.serialize(verifyingModule) + serializer.serialize(shouldGenerateReproducerForErrors) } } public init(from deserializer: any Deserializer) throws { - try deserializer.beginAggregate(11) + try deserializer.beginAggregate(12) self.uniqueID = try deserializer.deserialize() self.sourcePath = try deserializer.deserialize() self.libclangPath = try deserializer.deserialize() @@ -405,6 +408,7 @@ public struct ClangExplicitModulesPayload: Serializable, Encodable, Sendable { self.dependencyFilteringRootPath = try deserializer.deserialize() self.reportRequiredTargetDependencies = try deserializer.deserialize() self.verifyingModule = try deserializer.deserialize() + self.shouldGenerateReproducerForErrors = try deserializer.deserialize() } } @@ -997,7 +1001,8 @@ public class ClangCompilerSpec : CompilerSpec, SpecIdentifierType, GCCCompatible // To match the behavior of -MMD, our scan task should filter out headers in the SDK when discovering dependencies. In the long run, libclang should do this for us. dependencyFilteringRootPath: isForPCHTask ? nil : cbc.producer.sdk?.path, reportRequiredTargetDependencies: cbc.scope.evaluate(BuiltinMacros.DIAGNOSE_MISSING_TARGET_DEPENDENCIES), - verifyingModule: verifyingModule(cbc) + verifyingModule: verifyingModule(cbc), + shouldGenerateReproducerForErrors: cbc.scope.evaluate(BuiltinMacros.CLANG_EXPLICIT_MODULES_ENABLE_REPRODUCER_FOR_ERRORS) ) let explicitModulesSignatureData = cachedBuild ? "cached" : nil diff --git a/Sources/SWBCore/TaskResult.swift b/Sources/SWBCore/TaskResult.swift index e9d4803f..e52dd640 100644 --- a/Sources/SWBCore/TaskResult.swift +++ b/Sources/SWBCore/TaskResult.swift @@ -49,6 +49,13 @@ public enum TaskResult: Hashable, Sendable { } } + public var isCrashed: Bool { + if !self.isCancelled, case .exit(exitStatus: .uncaughtSignal, metrics: _) = self { + return true + } + return false + } + public var metrics: CommandMetrics? { guard case let .exit(_, metrics) = self else { return nil diff --git a/Sources/SWBTaskExecution/TaskActions/ClangCompileTaskAction.swift b/Sources/SWBTaskExecution/TaskActions/ClangCompileTaskAction.swift index ab121ba1..96dffa9f 100644 --- a/Sources/SWBTaskExecution/TaskActions/ClangCompileTaskAction.swift +++ b/Sources/SWBTaskExecution/TaskActions/ClangCompileTaskAction.swift @@ -319,7 +319,10 @@ public final class ClangCompileTaskAction: TaskAction, BuildValueValidatingTaskA outputDelegate.emitOutput(ByteString(encodingAsUTF8: commandString) + "\n") } - if case .some(.failed) = lastResult, case .some(.exit(.uncaughtSignal, _)) = outputDelegate.result { + let shouldGenerateReproducer = (lastResult == .failed) && + (explicitModulesPayload.shouldGenerateReproducerForErrors || + (outputDelegate.result?.isCrashed ?? false)) + if shouldGenerateReproducer { do { if let reproducerMessage = try clangModuleDependencyGraph.generateReproducer( forFailedDependency: dependencyInfo,