From 6c0220321e4374f369154ee32a08d11a425246f8 Mon Sep 17 00:00:00 2001 From: Volodymyr Sapsai Date: Thu, 6 Nov 2025 15:23:11 -0800 Subject: [PATCH] [Explicit Module Builds] Add a setting to specify a reproducer location. The idea is the same as Clang flag `-fcrash-diagnostics-dir` but for explicit module builds. Making it a build setting instead of an environment variable on purpose. The plan is to have this setting more visible and not hidden. rdar://164202916 --- Sources/SWBCSupport/CLibclang.cpp | 3 ++- Sources/SWBCSupport/CLibclang.h | 2 +- Sources/SWBCore/LibclangVendored/Libclang.swift | 5 +++-- Sources/SWBCore/Settings/BuiltinMacros.swift | 2 ++ .../SpecImplementations/Tools/CCompiler.swift | 13 +++++++++---- .../ClangModuleDependencyGraph.swift | 4 ++-- .../PrecompileClangModuleDynamicTaskSpec.swift | 14 +++++++++++--- .../TaskActions/ClangCompileTaskAction.swift | 6 ++++-- .../PrecompileClangModuleTaskAction.swift | 6 ++++-- 9 files changed, 38 insertions(+), 17 deletions(-) diff --git a/Sources/SWBCSupport/CLibclang.cpp b/Sources/SWBCSupport/CLibclang.cpp index ab636b32..ffc1af1b 100644 --- a/Sources/SWBCSupport/CLibclang.cpp +++ b/Sources/SWBCSupport/CLibclang.cpp @@ -2213,11 +2213,12 @@ extern "C" { bool libclang_scanner_generate_reproducer(libclang_scanner_t scanner, int argc, char *const *argv, const char *workingDirectory, + const char *reproducerLocation, const char **message) { auto lib = scanner->scanner->lib; LibclangFunctions::CXString messageString; auto reproducerOpts = lib->fns.clang_experimental_DependencyScannerReproducerOptions_create( - argc, argv, /*ModuleName=*/nullptr, workingDirectory, /*ReproducerLocation=*/nullptr, /*UseUniqueReproducerName=*/true); + argc, argv, /*ModuleName=*/nullptr, workingDirectory, reproducerLocation, /*UseUniqueReproducerName=*/true); auto result = lib->fns.clang_experimental_DependencyScanner_generateReproducer( reproducerOpts, &messageString); lib->fns.clang_experimental_DependencyScannerReproducerOptions_dispose(reproducerOpts); diff --git a/Sources/SWBCSupport/CLibclang.h b/Sources/SWBCSupport/CLibclang.h index d5b5646f..4cda6fc5 100644 --- a/Sources/SWBCSupport/CLibclang.h +++ b/Sources/SWBCSupport/CLibclang.h @@ -222,7 +222,7 @@ CSUPPORT_EXPORT bool libclang_scanner_scan_dependencies( /// \returns True on success, false if something failed (see \p message for more details). CSUPPORT_EXPORT bool libclang_scanner_generate_reproducer( libclang_scanner_t scanner, int argc, char *const *argv, const char *workingDirectory, - const char **message); + const char *reproducerLocation, const char **message); /// Get the list of commands invoked by the given Clang driver command line. /// diff --git a/Sources/SWBCore/LibclangVendored/Libclang.swift b/Sources/SWBCore/LibclangVendored/Libclang.swift index 7d57bd7b..06125dd9 100644 --- a/Sources/SWBCore/LibclangVendored/Libclang.swift +++ b/Sources/SWBCore/LibclangVendored/Libclang.swift @@ -291,13 +291,14 @@ public final class DependencyScanner { public func generateReproducer( commandLine: [String], - workingDirectory: String + workingDirectory: String, + location: String? ) throws -> String { let args = CStringArray(commandLine) var messageUnsafe: UnsafePointer! defer { messageUnsafe?.deallocate() } // The count is `- 1` here, because CStringArray appends a trailing nullptr. - let success = libclang_scanner_generate_reproducer(scanner, CInt(args.cArray.count - 1), args.cArray, workingDirectory, &messageUnsafe); + let success = libclang_scanner_generate_reproducer(scanner, CInt(args.cArray.count - 1), args.cArray, workingDirectory, location, &messageUnsafe); let message = String(cString: messageUnsafe) guard success else { throw message.isEmpty ? Error.dependencyScanUnknownError : Error.dependencyScanErrorString(message) diff --git a/Sources/SWBCore/Settings/BuiltinMacros.swift b/Sources/SWBCore/Settings/BuiltinMacros.swift index 247f44de..3b24428e 100644 --- a/Sources/SWBCore/Settings/BuiltinMacros.swift +++ b/Sources/SWBCore/Settings/BuiltinMacros.swift @@ -510,6 +510,7 @@ public final class BuiltinMacros { 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_EXPLICIT_MODULES_REPRODUCER_OUTPUT_PATH = BuiltinMacros.declarePathMacro("CLANG_CRASH_DIAGNOSTICS_DIR") 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 @@ -1515,6 +1516,7 @@ public final class BuiltinMacros { CLANG_EXPLICIT_MODULES_OUTPUT_PATH, SWIFT_EXPLICIT_MODULES_OUTPUT_PATH, CLANG_EXPLICIT_MODULES_ENABLE_REPRODUCER_FOR_ERRORS, + CLANG_EXPLICIT_MODULES_REPRODUCER_OUTPUT_PATH, 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 d91c21a2..ead99dcf 100644 --- a/Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift +++ b/Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift @@ -368,8 +368,9 @@ public struct ClangExplicitModulesPayload: Serializable, Encodable, Sendable { public let reportRequiredTargetDependencies: BooleanWarningLevel public let verifyingModule: String? public let shouldGenerateReproducerForErrors: Bool + public let reproducerOutputPath: Path? - 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) { + 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, reproducerOutputPath: Path?) { self.uniqueID = uniqueID self.sourcePath = sourcePath self.libclangPath = libclangPath @@ -382,10 +383,11 @@ public struct ClangExplicitModulesPayload: Serializable, Encodable, Sendable { self.reportRequiredTargetDependencies = reportRequiredTargetDependencies self.verifyingModule = verifyingModule self.shouldGenerateReproducerForErrors = shouldGenerateReproducerForErrors + self.reproducerOutputPath = reproducerOutputPath } public func serialize(to serializer: T) { - serializer.serializeAggregate(12) { + serializer.serializeAggregate(13) { serializer.serialize(uniqueID) serializer.serialize(sourcePath) serializer.serialize(libclangPath) @@ -398,11 +400,12 @@ public struct ClangExplicitModulesPayload: Serializable, Encodable, Sendable { serializer.serialize(reportRequiredTargetDependencies) serializer.serialize(verifyingModule) serializer.serialize(shouldGenerateReproducerForErrors) + serializer.serialize(reproducerOutputPath) } } public init(from deserializer: any Deserializer) throws { - try deserializer.beginAggregate(12) + try deserializer.beginAggregate(13) self.uniqueID = try deserializer.deserialize() self.sourcePath = try deserializer.deserialize() self.libclangPath = try deserializer.deserialize() @@ -415,6 +418,7 @@ public struct ClangExplicitModulesPayload: Serializable, Encodable, Sendable { self.reportRequiredTargetDependencies = try deserializer.deserialize() self.verifyingModule = try deserializer.deserialize() self.shouldGenerateReproducerForErrors = try deserializer.deserialize() + self.reproducerOutputPath = try deserializer.deserialize() } } @@ -1028,7 +1032,8 @@ public class ClangCompilerSpec : CompilerSpec, SpecIdentifierType, GCCCompatible dependencyFilteringRootPath: isForPCHTask ? nil : cbc.producer.sdk?.path, reportRequiredTargetDependencies: cbc.scope.evaluate(BuiltinMacros.DIAGNOSE_MISSING_TARGET_DEPENDENCIES), verifyingModule: verifyingModule(cbc), - shouldGenerateReproducerForErrors: cbc.scope.evaluate(BuiltinMacros.CLANG_EXPLICIT_MODULES_ENABLE_REPRODUCER_FOR_ERRORS) + shouldGenerateReproducerForErrors: cbc.scope.evaluate(BuiltinMacros.CLANG_EXPLICIT_MODULES_ENABLE_REPRODUCER_FOR_ERRORS), + reproducerOutputPath: cbc.scope.evaluate(BuiltinMacros.CLANG_EXPLICIT_MODULES_REPRODUCER_OUTPUT_PATH).nilIfEmpty ) let explicitModulesSignatureData = cachedBuild ? "cached" : nil diff --git a/Sources/SWBTaskExecution/DynamicTaskSpecs/ClangModuleDependencyGraph.swift b/Sources/SWBTaskExecution/DynamicTaskSpecs/ClangModuleDependencyGraph.swift index c4420761..2f787667 100644 --- a/Sources/SWBTaskExecution/DynamicTaskSpecs/ClangModuleDependencyGraph.swift +++ b/Sources/SWBTaskExecution/DynamicTaskSpecs/ClangModuleDependencyGraph.swift @@ -572,7 +572,7 @@ package final class ClangModuleDependencyGraph { } package func generateReproducer(forFailedDependency dependency: DependencyInfo, - libclangPath: Path, casOptions: CASOptions?) throws -> String? { + libclangPath: Path, casOptions: CASOptions?, location: String?) throws -> String? { let clangWithScanner = try libclangWithScanner( forPath: libclangPath, casOptions: casOptions, @@ -583,7 +583,7 @@ package final class ClangModuleDependencyGraph { return nil } return try clangWithScanner.scanner.generateReproducer( - commandLine: dependency.scanningCommandLine, workingDirectory: dependency.workingDirectory.str) + commandLine: dependency.scanningCommandLine, workingDirectory: dependency.workingDirectory.str, location: location) } package var isEmpty: Bool { diff --git a/Sources/SWBTaskExecution/DynamicTaskSpecs/PrecompileClangModuleDynamicTaskSpec.swift b/Sources/SWBTaskExecution/DynamicTaskSpecs/PrecompileClangModuleDynamicTaskSpec.swift index 45447edb..6bc16e1f 100644 --- a/Sources/SWBTaskExecution/DynamicTaskSpecs/PrecompileClangModuleDynamicTaskSpec.swift +++ b/Sources/SWBTaskExecution/DynamicTaskSpecs/PrecompileClangModuleDynamicTaskSpec.swift @@ -22,6 +22,7 @@ public struct PrecompileClangModuleTaskKey: Serializable, CustomDebugStringConve let casOptions: CASOptions? let verifyingModule: String? let fileNameMapPath: Path? + let reproducerOutputPath: Path? init( dependencyInfoPath: Path, @@ -29,7 +30,8 @@ public struct PrecompileClangModuleTaskKey: Serializable, CustomDebugStringConve libclangPath: Path, casOptions: CASOptions?, verifyingModule: String?, - fileNameMapPath: Path? + fileNameMapPath: Path?, + reproducerOutputPath: Path? ) { self.dependencyInfoPath = dependencyInfoPath self.usesSerializedDiagnostics = usesSerializedDiagnostics @@ -37,27 +39,30 @@ public struct PrecompileClangModuleTaskKey: Serializable, CustomDebugStringConve self.casOptions = casOptions self.verifyingModule = verifyingModule self.fileNameMapPath = fileNameMapPath + self.reproducerOutputPath = reproducerOutputPath } public func serialize(to serializer: T) { - serializer.serializeAggregate(6) { + serializer.serializeAggregate(7) { serializer.serialize(dependencyInfoPath) serializer.serialize(usesSerializedDiagnostics) serializer.serialize(libclangPath) serializer.serialize(casOptions) serializer.serialize(verifyingModule) serializer.serialize(fileNameMapPath) + serializer.serialize(reproducerOutputPath) } } public init(from deserializer: any Deserializer) throws { - try deserializer.beginAggregate(6) + try deserializer.beginAggregate(7) self.dependencyInfoPath = try deserializer.deserialize() self.usesSerializedDiagnostics = try deserializer.deserialize() self.libclangPath = try deserializer.deserialize() self.casOptions = try deserializer.deserialize() self.verifyingModule = try deserializer.deserialize() self.fileNameMapPath = try deserializer.deserialize() + self.reproducerOutputPath = try deserializer.deserialize() } public var debugDescription: String { @@ -68,6 +73,9 @@ public struct PrecompileClangModuleTaskKey: Serializable, CustomDebugStringConve if let fileNameMapPath { result += " fileNameMap=\(fileNameMapPath)" } + if let reproducerOutputPath { + result += " reproducerOutputPath=\(reproducerOutputPath)" + } result += ">" return result } diff --git a/Sources/SWBTaskExecution/TaskActions/ClangCompileTaskAction.swift b/Sources/SWBTaskExecution/TaskActions/ClangCompileTaskAction.swift index 96dffa9f..b81d59f3 100644 --- a/Sources/SWBTaskExecution/TaskActions/ClangCompileTaskAction.swift +++ b/Sources/SWBTaskExecution/TaskActions/ClangCompileTaskAction.swift @@ -138,7 +138,8 @@ public final class ClangCompileTaskAction: TaskAction, BuildValueValidatingTaskA libclangPath: explicitModulesPayload.libclangPath, casOptions: explicitModulesPayload.casOptions, verifyingModule: explicitModulesPayload.verifyingModule, - fileNameMapPath: payload.fileNameMapPath + fileNameMapPath: payload.fileNameMapPath, + reproducerOutputPath: explicitModulesPayload.reproducerOutputPath ) dynamicExecutionDelegate.requestDynamicTask( @@ -327,7 +328,8 @@ public final class ClangCompileTaskAction: TaskAction, BuildValueValidatingTaskA if let reproducerMessage = try clangModuleDependencyGraph.generateReproducer( forFailedDependency: dependencyInfo, libclangPath: explicitModulesPayload.libclangPath, - casOptions: explicitModulesPayload.casOptions) { + casOptions: explicitModulesPayload.casOptions, + location: explicitModulesPayload.reproducerOutputPath?.str) { outputDelegate.emitOutput(ByteString(encodingAsUTF8: reproducerMessage) + "\n") } } catch { diff --git a/Sources/SWBTaskExecution/TaskActions/PrecompileClangModuleTaskAction.swift b/Sources/SWBTaskExecution/TaskActions/PrecompileClangModuleTaskAction.swift index 3d66bb2b..598c1a54 100644 --- a/Sources/SWBTaskExecution/TaskActions/PrecompileClangModuleTaskAction.swift +++ b/Sources/SWBTaskExecution/TaskActions/PrecompileClangModuleTaskAction.swift @@ -93,7 +93,8 @@ final public class PrecompileClangModuleTaskAction: TaskAction, BuildValueValida libclangPath: key.libclangPath, casOptions: key.casOptions, verifyingModule: key.verifyingModule, - fileNameMapPath: key.fileNameMapPath + fileNameMapPath: key.fileNameMapPath, + reproducerOutputPath: key.reproducerOutputPath ) dynamicExecutionDelegate.requestDynamicTask( @@ -232,7 +233,8 @@ final public class PrecompileClangModuleTaskAction: TaskAction, BuildValueValida if let reproducerMessage = try clangModuleDependencyGraph.generateReproducer( forFailedDependency: dependencyInfo, libclangPath: key.libclangPath, - casOptions: key.casOptions) { + casOptions: key.casOptions, + location: key.reproducerOutputPath?.str) { outputDelegate.emitOutput(ByteString(encodingAsUTF8: reproducerMessage) + "\n") } } catch {