diff --git a/Sources/SWBCore/Settings/BuiltinMacros.swift b/Sources/SWBCore/Settings/BuiltinMacros.swift index 5570cc92..a87be483 100644 --- a/Sources/SWBCore/Settings/BuiltinMacros.swift +++ b/Sources/SWBCore/Settings/BuiltinMacros.swift @@ -639,6 +639,7 @@ public final class BuiltinMacros { public static let EMBEDDED_CONTENT_CONTAINS_SWIFT = BuiltinMacros.declareBooleanMacro("EMBEDDED_CONTENT_CONTAINS_SWIFT") public static let EMBEDDED_PROFILE_NAME = BuiltinMacros.declareStringMacro("EMBEDDED_PROFILE_NAME") public static let EMBED_PACKAGE_RESOURCE_BUNDLE_NAMES = BuiltinMacros.declareStringListMacro("EMBED_PACKAGE_RESOURCE_BUNDLE_NAMES") + public static let EMIT_COMPILER_SOURCE_METADATA = BuiltinMacros.declareBooleanMacro("EMIT_COMPILER_SOURCE_METADATA") public static let EMIT_FRONTEND_COMMAND_LINES = BuiltinMacros.declareBooleanMacro("EMIT_FRONTEND_COMMAND_LINES") public static let ENABLE_APPINTENTS_DEPLOYMENT_AWARE_PROCESSING = BuiltinMacros.declareBooleanMacro("ENABLE_APPINTENTS_DEPLOYMENT_AWARE_PROCESSING") public static let ENABLE_ADDITIONAL_CODESIGN_INPUT_TRACKING = BuiltinMacros.declareBooleanMacro("ENABLE_ADDITIONAL_CODESIGN_INPUT_TRACKING") @@ -1717,6 +1718,7 @@ public final class BuiltinMacros { EMBEDDED_CONTENT_CONTAINS_SWIFT, EMBEDDED_PROFILE_NAME, EMBED_PACKAGE_RESOURCE_BUNDLE_NAMES, + EMIT_COMPILER_SOURCE_METADATA, EMIT_FRONTEND_COMMAND_LINES, ENABLE_APPINTENTS_DEPLOYMENT_AWARE_PROCESSING, ENABLE_ADDRESS_SANITIZER, diff --git a/Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift b/Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift index ead99dcf..f3ba1595 100644 --- a/Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift +++ b/Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift @@ -835,6 +835,7 @@ public class ClangCompilerSpec : CompilerSpec, SpecIdentifierType, GCCCompatible "-fmessage-length=", "-fmacro-backtrace-limit=", "-fbuild-session-timestamp=", + "-fdiagnostics-add-output=" ]) static let outputAgnosticCompilerArgumentsWithValues = Set([ @@ -1219,7 +1220,7 @@ public class ClangCompilerSpec : CompilerSpec, SpecIdentifierType, GCCCompatible dependencyData = nil } - let extraOutputs: [any PlannedNode] + var extraOutputs: [any PlannedNode] let moduleDependenciesContext = cbc.producer.moduleDependenciesContext let headerDependenciesContext = cbc.producer.headerDependenciesContext let dependencyValidationOutputPath: Path? @@ -1336,6 +1337,8 @@ public class ClangCompilerSpec : CompilerSpec, SpecIdentifierType, GCCCompatible } } + commandLine += addCompilerMetadataFlags(cbc, outputFileDir.join(outputNode.path.str + ".source-metadata.json"), delegate, &extraOutputs) + // Handle explicit modules build. let scanningOutput = delegate.createNode(outputNode.path.dirname.join(outputNode.path.basename + ".scan")) let (action, usesExecutionInputs, explicitModulesPayload, explicitModulesSignatureData) = createExplicitModulesActionAndPayload(cbc, delegate, launcher, input, resolvedInputFileType.languageDialect, commandLine: commandLine, scanningOutputPath: scanningOutput.path, isForPCHTask: false, clangInfo: clangInfo) @@ -1648,6 +1651,22 @@ public class ClangCompilerSpec : CompilerSpec, SpecIdentifierType, GCCCompatible } } + func addCompilerMetadataFlags(_ cbc: CommandBuildContext, _ outputPath: Path, _ delegate: any TaskGenerationDelegate, _ taskOutputs: inout [any PlannedNode]) -> [String] { + guard cbc.scope.evaluate(BuiltinMacros.EMIT_COMPILER_SOURCE_METADATA) else { + return [] + } + + guard let metadatatype = cbc.producer.lookupFileType(identifier: "text.json.compiler-metadata.source") else { + return [] + } + + let securityMetadataNode = delegate.createNode(outputPath) + let ftb = FileToBuild(absolutePath: securityMetadataNode.path, fileType: metadatatype) + taskOutputs.append(securityMetadataNode) + delegate.declareOutput(ftb) + return ["-fdiagnostics-add-output=sarif:file=" + securityMetadataNode.path.str] + } + /// Specialized function that creates a task for precompiling a particular header. private func precompile(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate, headerPath: Path, language: GCCCompatibleLanguageDialect, inputFileType: FileTypeSpec, extraArgs: [String], precompPath: Path, clangInfo: DiscoveredClangToolSpecInfo?) -> ClangPrefixInfo.PCHInfo { diff --git a/Sources/SWBUniversalPlatform/Specs/StandardFileTypes.xcspec b/Sources/SWBUniversalPlatform/Specs/StandardFileTypes.xcspec index 6afa5833..81bbfdb6 100644 --- a/Sources/SWBUniversalPlatform/Specs/StandardFileTypes.xcspec +++ b/Sources/SWBUniversalPlatform/Specs/StandardFileTypes.xcspec @@ -1720,4 +1720,11 @@ CanSetIncludeInIndex = YES; UTI = "public.protobuf-source"; }, + { + Identifier = text.json.compiler-metadata.source; + Type = FileType; + Name = "Source metadata emitted from compiler"; + UTI = "com.apple.compiler-metadata.source"; + BasedOn = text.json; + }, ) diff --git a/Tests/SWBTaskConstructionTests/TaskConstructionTests.swift b/Tests/SWBTaskConstructionTests/TaskConstructionTests.swift index 244c4949..e684eae6 100644 --- a/Tests/SWBTaskConstructionTests/TaskConstructionTests.swift +++ b/Tests/SWBTaskConstructionTests/TaskConstructionTests.swift @@ -9512,4 +9512,80 @@ fileprivate struct TaskConstructionTests: CoreBasedTests { } } } + + @Test(.requireSDKs(.macOS)) + func testSourceMetadata() async throws { + try await withTemporaryDirectory { (tmpDir: Path) async throws -> Void in + let sources = [ + "SourceFile0.c", + "SourceFile1.mm", + "SourceFile2.m", + "SourceFile3.cpp" + ] + + let testProject = TestProject( + "aProject", + sourceRoot: tmpDir, + groupTree: TestGroup( + "SomeFiles", path: "Sources", + children: sources.map{TestFile($0)} + ), + buildConfigurations: [ + TestBuildConfiguration("Debug") + ], + targets: [ + TestStandardTarget( + "AppTarget", + type: .application, + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + "TARGET_BUILD_DIR": "/tmp/SomeFiles.dst", + "GENERATE_INFOPLIST_FILE": "YES", + "PRODUCT_NAME": "$(TARGET_NAME)", + "ARCHS": "x86_64 arm64", + "EMIT_COMPILER_SOURCE_METADATA": "YES" + ]) + ], + buildPhases: [ + TestSourcesBuildPhase(sources.map{TestBuildFile($0)}) + ] + )] + ) + + let fs = PseudoFS() + + let core = try await getCore() + let tester = try TaskConstructionTester(core, testProject) + + await tester.checkBuild(BuildParameters(configuration: "Debug"), runDestination: .anyMac, fs: fs) { results -> Void in + results.checkTarget("AppTarget") { target -> Void in + let command = "-fdiagnostics-add-output=sarif:file=" + let buildPath = tmpDir.join("build/aProject.build/Debug/AppTarget.build/Objects-normal/") + for arch in ["x86_64", "arm64"] { + let metadataPath = buildPath.join(arch) + let inputs = sources.map{metadataPath.join(Path($0).basenameWithoutSuffix + ".o.source-metadata.json").str} + + for (source, input) in zip(sources, inputs) { + results.checkTask(.matchTarget(target), .matchRuleType("CompileC"), .matchRuleItemBasename(source), .matchRuleItem(arch), body: { task in + task.checkCommandLineContains([command + input]) + task.checkInputs([ + .path(tmpDir.join("Sources").join(source).str), + .namePattern(.and(.prefix("target-"), .suffix("-generated-headers"))), + .namePattern(.and(.prefix("target-"), .suffix("-swift-generated-headers"))), + .namePattern(.and(.prefix("target-"), .suffix("-ModuleVerifierTaskProducer"))), + .namePattern(.and(.prefix("target-"), .suffix("-begin-compiling"))), + .name("WorkspaceHeaderMapVFSFilesWritten"), + ]) + task.checkOutputs([ + .path(buildPath.join(arch).join(Path(source).basenameWithoutSuffix + ".o").str), + .path(metadataPath.join(Path(source).basenameWithoutSuffix + ".o.source-metadata.json").str) + ]) + }) + + } + } + } + } + } + } }