diff --git a/Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift b/Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift index 26e43bb9..f5856959 100644 --- a/Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift +++ b/Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift @@ -870,16 +870,8 @@ public class ClangCompilerSpec : CompilerSpec, SpecIdentifierType, GCCCompatible let buildSettingEnabled = cbc.scope.evaluate(BuiltinMacros.CLANG_ENABLE_COMPILE_CACHE) - // If a blocklist is provided in the toolchain, use it to determine the default for the current project - guard let blocklist = clangInfo?.clangCachingBlocklist else { - return buildSettingEnabled - } - - // If this project is on the blocklist, override the blocklist default enable for it - if blocklist.isProjectListed(cbc.scope) { - return false - } - return buildSettingEnabled + // If this project is on the blocklist, override the blocklist default enable for it. + return clangInfo?.isCachingBlocked(cbc.scope) == true ? false : buildSettingEnabled } private func createExplicitModulesActionAndPayload(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate, _ compilerLauncher: Path?, _ input: FileToBuild, _ language: GCCCompatibleLanguageDialect?, commandLine: [String], scanningOutputPath: Path, isForPCHTask: Bool, clangInfo: DiscoveredClangToolSpecInfo?) -> (action: (any PlannedTaskAction)?, usesExecutionInputs: Bool, payload: ClangExplicitModulesPayload?, signatureData: String?) { diff --git a/Sources/SWBCore/ToolInfo/ClangToolInfo.swift b/Sources/SWBCore/ToolInfo/ClangToolInfo.swift index 468bce3b..c348f191 100644 --- a/Sources/SWBCore/ToolInfo/ClangToolInfo.swift +++ b/Sources/SWBCore/ToolInfo/ClangToolInfo.swift @@ -10,9 +10,40 @@ // //===----------------------------------------------------------------------===// +public import SWBMacro public import SWBUtil import Foundation +public struct ClangBlocklists : Sendable { + + public struct CachingBlocklistInfo : ProjectFailuresBlockList, Codable, Sendable { + /// A blocklist of project names that do not support the `CLANG_ENABLE_COMPILE_CACHE` build setting. + let KnownFailures: [String] + + enum CodingKeys: String, CodingKey { + case KnownFailures + } + } + + var caching: CachingBlocklistInfo? = nil + + public struct BuiltinModuleVerifierInfo : ProjectFailuresBlockList, Codable, Sendable { + /// A blocklist of project names that do not support the `MODULE_VERIFIER_KIND=builtin` build setting. + let KnownFailures: [String] + enum CodingKeys: String, CodingKey { + case KnownFailures + } + } + + var builtinModuleVerify: BuiltinModuleVerifierInfo? = nil + + /// Helper method for determining if a given functionality is blocklisted for the active scope. + func isBlocked(_ scope: MacroEvaluationScope, info: BlockListT?) -> Bool { + guard let blocklistInfo = info else { return false } + return blocklistInfo.isProjectListed(scope) + } +} + public struct DiscoveredClangToolSpecInfo: DiscoveredCommandLineToolSpecInfo { public let toolPath: Path public let clangVersion: Version? @@ -21,8 +52,8 @@ public struct DiscoveredClangToolSpecInfo: DiscoveredCommandLineToolSpecInfo { public var toolVersion: Version? { return self.clangVersion } - /// `compilerClientsConfig` Clang caching blocklist - public let clangCachingBlocklist: ClangCachingBlockListInfo? + /// `compilerClientsConfig` blocklists for Clang + public let blocklists: ClangBlocklists public enum FeatureFlag: String, CaseIterable, Sendable { case allowPcmWithCompilerErrors = "allow-pcm-with-compiler-errors" @@ -66,13 +97,13 @@ public struct DiscoveredClangToolSpecInfo: DiscoveredCommandLineToolSpecInfo { public func deploymentTargetEnvironmentVariableNames() -> Set { Set(toolFeatures.value(.deploymentTargetEnvironmentVariables)?.stringArrayValue ?? []) } -} -public struct ClangCachingBlockListInfo : ProjectFailuresBlockList, Codable, Sendable { - let KnownFailures: [String] + public func isCachingBlocked(_ scope: MacroEvaluationScope) -> Bool { + return blocklists.isBlocked(scope, info: blocklists.caching) + } - enum CodingKeys: String, CodingKey { - case KnownFailures + public func isBuiltinModuleVerifyBlocked(_ scope: MacroEvaluationScope) -> Bool { + return blocklists.isBlocked(scope, info: blocklists.builtinModuleVerify) } } @@ -91,7 +122,7 @@ public func discoveredClangToolInfo( ) async throws -> DiscoveredClangToolSpecInfo { // Check that we call a clang variant, 'clang', 'clang++' etc. Note that a test sets `CC` to `/usr/bin/yes` so avoid calling that here. guard toolPath.basename.starts(with: "clang") else { - return DiscoveredClangToolSpecInfo(toolPath: toolPath, clangVersion: nil, llvmVersion: nil, isAppleClang: false, clangCachingBlocklist: nil, toolFeatures: .none) + return DiscoveredClangToolSpecInfo(toolPath: toolPath, clangVersion: nil, llvmVersion: nil, isAppleClang: false, blocklists: ClangBlocklists(), toolFeatures: .none) } // Construct the command line to invoke. @@ -169,13 +200,17 @@ public func discoveredClangToolInfo( delegate: delegate ) } + var blocklists = ClangBlocklists() + blocklists.caching = getBlocklist(type: ClangBlocklists.CachingBlocklistInfo.self, toolchainFilename: "clang-caching.json", delegate: delegate) + blocklists.builtinModuleVerify = getBlocklist(type: ClangBlocklists.BuiltinModuleVerifierInfo.self, toolchainFilename: "clang-builtin-module-verify.json", delegate: delegate) + return DiscoveredClangToolSpecInfo( toolPath: toolPath, clangVersion: clangVersion, llvmVersion: llvmVersion, isAppleClang: isAppleClang, - clangCachingBlocklist: getBlocklist(type: ClangCachingBlockListInfo.self, toolchainFilename: "clang-caching.json", delegate: delegate), + blocklists: blocklists, toolFeatures: getFeatures(at: toolPath) ) }) diff --git a/Sources/SWBTaskConstruction/TaskProducers/OtherTaskProducers/ModuleVerifierTaskProducer.swift b/Sources/SWBTaskConstruction/TaskProducers/OtherTaskProducers/ModuleVerifierTaskProducer.swift index f0080228..82628e96 100644 --- a/Sources/SWBTaskConstruction/TaskProducers/OtherTaskProducers/ModuleVerifierTaskProducer.swift +++ b/Sources/SWBTaskConstruction/TaskProducers/OtherTaskProducers/ModuleVerifierTaskProducer.swift @@ -149,6 +149,11 @@ final class ModuleVerifierTaskProducer: PhasedTaskProducer, TaskProducer { fallbackToExternal = true return } + // Fallback to external verifier if current scope is blocklisted. + if clangInfo?.isBuiltinModuleVerifyBlocked(scope) == true { + fallbackToExternal = true + return + } } catch { delegate.error(error) return diff --git a/Tests/SWBTaskConstructionTests/ModuleVerifierTaskConstructionTests.swift b/Tests/SWBTaskConstructionTests/ModuleVerifierTaskConstructionTests.swift index e01298a5..41504f17 100644 --- a/Tests/SWBTaskConstructionTests/ModuleVerifierTaskConstructionTests.swift +++ b/Tests/SWBTaskConstructionTests/ModuleVerifierTaskConstructionTests.swift @@ -659,6 +659,77 @@ fileprivate struct ModuleVerifierTaskConstructionTests: CoreBasedTests { } } + @Test(.requireSDKs(.macOS)) + func builtinModuleVerifyList() async throws { + try await withTemporaryDirectory { tmpDirPath in + let blockListFilePath = tmpDirPath.join("clang-builtin-module-verify.json") + let targetName = "Orange" + let testProject = + TestProject( + "aProject", + sourceRoot: tmpDirPath.join("Test"), + groupTree: TestGroup( + "Group", + path: "Sources", + children: [ + TestFile("Orange.h"), + TestFile("Orange.defs"), + TestFile("main.m"), + ]), + targets: [ + TestStandardTarget( + targetName, + type: .framework, + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + "ARCHS": "arm64", + "DEFINES_MODULE": "YES", + "ENABLE_MODULE_VERIFIER": "YES", + "MODULE_VERIFIER_KIND": "builtin", + "GENERATE_INFOPLIST_FILE": "YES", + "MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS": "gnu11", + "MODULE_VERIFIER_SUPPORTED_LANGUAGES": "objective-c", + "MODULE_VERIFIER_VERBOSE": "YES", + "PRODUCT_NAME": "$(TARGET_NAME)", + "BLOCKLISTS_PATH": tmpDirPath.str, + ]), + ], + buildPhases: [ + TestHeadersBuildPhase([ + TestBuildFile("Orange.h", headerVisibility: .public) + ]), + TestSourcesBuildPhase([ + "main.m", + ]), + ]), + ]) + + let core = try await getCore() + let tester = try await BuildOperationTester(core, testProject, simulated: false) + let SRCROOT = tester.workspace.projects[0].sourceRoot + for inputFile in ["Sources/Orange.h", "Sources/Orange.defs", "Sources/main.m"] { + try await tester.fs.writeFileContents(SRCROOT.join(inputFile)) { stream in + stream <<< "" + } + } + try await tester.fs.writeFileContents(blockListFilePath) { file in + file <<< + """ + { "KnownFailures": ["aProject"]} + """ + } + /// Verify that while the project has enabled `MODULE_VERIFIER_KIND=builtin` the external module verifier is invoked because the project is on the blocklist. + try await tester.checkBuild(runDestination: .macOS) { results in + results.checkNoDiagnostics() + results.checkTask(.matchRuleType("VerifyModule")) { task in + task.checkRuleInfo(["VerifyModule", "\(SRCROOT.str)/build/Debug/Orange.framework"]) + task.checkCommandLineMatches([.suffix("modules-verifier"), "\(SRCROOT.str)/build/Debug/Orange.framework", .anySequence, "--clang", .suffix("clang"), .anySequence]) + } + results.checkNoErrors() + } + } + } + @Test(.requireSDKs(.macOS)) func externalModuleVerifier() async throws { let archs = ["arm64", "x86_64"]