Skip to content

Commit 8e1a315

Browse files
authored
ModuleVerifier: Add blocklist for builtin module verifier
The first change refactors the clang-based caching blocklist a bit to more easily support extending it. The second change leverages this to add a blocklist for the built-in module verifier. This is to stage in turning this on by default against preexisting verification violations.
2 parents 1356e5a + a1c9626 commit 8e1a315

File tree

4 files changed

+122
-19
lines changed

4 files changed

+122
-19
lines changed

Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -870,16 +870,8 @@ public class ClangCompilerSpec : CompilerSpec, SpecIdentifierType, GCCCompatible
870870

871871
let buildSettingEnabled = cbc.scope.evaluate(BuiltinMacros.CLANG_ENABLE_COMPILE_CACHE)
872872

873-
// If a blocklist is provided in the toolchain, use it to determine the default for the current project
874-
guard let blocklist = clangInfo?.clangCachingBlocklist else {
875-
return buildSettingEnabled
876-
}
877-
878-
// If this project is on the blocklist, override the blocklist default enable for it
879-
if blocklist.isProjectListed(cbc.scope) {
880-
return false
881-
}
882-
return buildSettingEnabled
873+
// If this project is on the blocklist, override the blocklist default enable for it.
874+
return clangInfo?.isCachingBlocked(cbc.scope) == true ? false : buildSettingEnabled
883875
}
884876

885877
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?) {

Sources/SWBCore/ToolInfo/ClangToolInfo.swift

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,40 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
public import SWBMacro
1314
public import SWBUtil
1415
import Foundation
1516

17+
public struct ClangBlocklists : Sendable {
18+
19+
public struct CachingBlocklistInfo : ProjectFailuresBlockList, Codable, Sendable {
20+
/// A blocklist of project names that do not support the `CLANG_ENABLE_COMPILE_CACHE` build setting.
21+
let KnownFailures: [String]
22+
23+
enum CodingKeys: String, CodingKey {
24+
case KnownFailures
25+
}
26+
}
27+
28+
var caching: CachingBlocklistInfo? = nil
29+
30+
public struct BuiltinModuleVerifierInfo : ProjectFailuresBlockList, Codable, Sendable {
31+
/// A blocklist of project names that do not support the `MODULE_VERIFIER_KIND=builtin` build setting.
32+
let KnownFailures: [String]
33+
enum CodingKeys: String, CodingKey {
34+
case KnownFailures
35+
}
36+
}
37+
38+
var builtinModuleVerify: BuiltinModuleVerifierInfo? = nil
39+
40+
/// Helper method for determining if a given functionality is blocklisted for the active scope.
41+
func isBlocked<BlockListT: ProjectFailuresBlockList>(_ scope: MacroEvaluationScope, info: BlockListT?) -> Bool {
42+
guard let blocklistInfo = info else { return false }
43+
return blocklistInfo.isProjectListed(scope)
44+
}
45+
}
46+
1647
public struct DiscoveredClangToolSpecInfo: DiscoveredCommandLineToolSpecInfo {
1748
public let toolPath: Path
1849
public let clangVersion: Version?
@@ -21,8 +52,8 @@ public struct DiscoveredClangToolSpecInfo: DiscoveredCommandLineToolSpecInfo {
2152

2253
public var toolVersion: Version? { return self.clangVersion }
2354

24-
/// `compilerClientsConfig` Clang caching blocklist
25-
public let clangCachingBlocklist: ClangCachingBlockListInfo?
55+
/// `compilerClientsConfig` blocklists for Clang
56+
public let blocklists: ClangBlocklists
2657

2758
public enum FeatureFlag: String, CaseIterable, Sendable {
2859
case allowPcmWithCompilerErrors = "allow-pcm-with-compiler-errors"
@@ -66,13 +97,13 @@ public struct DiscoveredClangToolSpecInfo: DiscoveredCommandLineToolSpecInfo {
6697
public func deploymentTargetEnvironmentVariableNames() -> Set<String> {
6798
Set(toolFeatures.value(.deploymentTargetEnvironmentVariables)?.stringArrayValue ?? [])
6899
}
69-
}
70100

71-
public struct ClangCachingBlockListInfo : ProjectFailuresBlockList, Codable, Sendable {
72-
let KnownFailures: [String]
101+
public func isCachingBlocked(_ scope: MacroEvaluationScope) -> Bool {
102+
return blocklists.isBlocked(scope, info: blocklists.caching)
103+
}
73104

74-
enum CodingKeys: String, CodingKey {
75-
case KnownFailures
105+
public func isBuiltinModuleVerifyBlocked(_ scope: MacroEvaluationScope) -> Bool {
106+
return blocklists.isBlocked(scope, info: blocklists.builtinModuleVerify)
76107
}
77108
}
78109

@@ -91,7 +122,7 @@ public func discoveredClangToolInfo(
91122
) async throws -> DiscoveredClangToolSpecInfo {
92123
// 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.
93124
guard toolPath.basename.starts(with: "clang") else {
94-
return DiscoveredClangToolSpecInfo(toolPath: toolPath, clangVersion: nil, llvmVersion: nil, isAppleClang: false, clangCachingBlocklist: nil, toolFeatures: .none)
125+
return DiscoveredClangToolSpecInfo(toolPath: toolPath, clangVersion: nil, llvmVersion: nil, isAppleClang: false, blocklists: ClangBlocklists(), toolFeatures: .none)
95126
}
96127

97128
// Construct the command line to invoke.
@@ -169,13 +200,17 @@ public func discoveredClangToolInfo(
169200
delegate: delegate
170201
)
171202
}
203+
var blocklists = ClangBlocklists()
204+
blocklists.caching = getBlocklist(type: ClangBlocklists.CachingBlocklistInfo.self, toolchainFilename: "clang-caching.json", delegate: delegate)
205+
blocklists.builtinModuleVerify = getBlocklist(type: ClangBlocklists.BuiltinModuleVerifierInfo.self, toolchainFilename: "clang-builtin-module-verify.json", delegate: delegate)
206+
172207

173208
return DiscoveredClangToolSpecInfo(
174209
toolPath: toolPath,
175210
clangVersion: clangVersion,
176211
llvmVersion: llvmVersion,
177212
isAppleClang: isAppleClang,
178-
clangCachingBlocklist: getBlocklist(type: ClangCachingBlockListInfo.self, toolchainFilename: "clang-caching.json", delegate: delegate),
213+
blocklists: blocklists,
179214
toolFeatures: getFeatures(at: toolPath)
180215
)
181216
})

Sources/SWBTaskConstruction/TaskProducers/OtherTaskProducers/ModuleVerifierTaskProducer.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ final class ModuleVerifierTaskProducer: PhasedTaskProducer, TaskProducer {
149149
fallbackToExternal = true
150150
return
151151
}
152+
// Fallback to external verifier if current scope is blocklisted.
153+
if clangInfo?.isBuiltinModuleVerifyBlocked(scope) == true {
154+
fallbackToExternal = true
155+
return
156+
}
152157
} catch {
153158
delegate.error(error)
154159
return

Tests/SWBTaskConstructionTests/ModuleVerifierTaskConstructionTests.swift

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,77 @@ fileprivate struct ModuleVerifierTaskConstructionTests: CoreBasedTests {
659659
}
660660
}
661661

662+
@Test(.requireSDKs(.macOS))
663+
func builtinModuleVerifyList() async throws {
664+
try await withTemporaryDirectory { tmpDirPath in
665+
let blockListFilePath = tmpDirPath.join("clang-builtin-module-verify.json")
666+
let targetName = "Orange"
667+
let testProject =
668+
TestProject(
669+
"aProject",
670+
sourceRoot: tmpDirPath.join("Test"),
671+
groupTree: TestGroup(
672+
"Group",
673+
path: "Sources",
674+
children: [
675+
TestFile("Orange.h"),
676+
TestFile("Orange.defs"),
677+
TestFile("main.m"),
678+
]),
679+
targets: [
680+
TestStandardTarget(
681+
targetName,
682+
type: .framework,
683+
buildConfigurations: [
684+
TestBuildConfiguration("Debug", buildSettings: [
685+
"ARCHS": "arm64",
686+
"DEFINES_MODULE": "YES",
687+
"ENABLE_MODULE_VERIFIER": "YES",
688+
"MODULE_VERIFIER_KIND": "builtin",
689+
"GENERATE_INFOPLIST_FILE": "YES",
690+
"MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS": "gnu11",
691+
"MODULE_VERIFIER_SUPPORTED_LANGUAGES": "objective-c",
692+
"MODULE_VERIFIER_VERBOSE": "YES",
693+
"PRODUCT_NAME": "$(TARGET_NAME)",
694+
"BLOCKLISTS_PATH": tmpDirPath.str,
695+
]),
696+
],
697+
buildPhases: [
698+
TestHeadersBuildPhase([
699+
TestBuildFile("Orange.h", headerVisibility: .public)
700+
]),
701+
TestSourcesBuildPhase([
702+
"main.m",
703+
]),
704+
]),
705+
])
706+
707+
let core = try await getCore()
708+
let tester = try await BuildOperationTester(core, testProject, simulated: false)
709+
let SRCROOT = tester.workspace.projects[0].sourceRoot
710+
for inputFile in ["Sources/Orange.h", "Sources/Orange.defs", "Sources/main.m"] {
711+
try await tester.fs.writeFileContents(SRCROOT.join(inputFile)) { stream in
712+
stream <<< ""
713+
}
714+
}
715+
try await tester.fs.writeFileContents(blockListFilePath) { file in
716+
file <<<
717+
"""
718+
{ "KnownFailures": ["aProject"]}
719+
"""
720+
}
721+
/// Verify that while the project has enabled `MODULE_VERIFIER_KIND=builtin` the external module verifier is invoked because the project is on the blocklist.
722+
try await tester.checkBuild(runDestination: .macOS) { results in
723+
results.checkNoDiagnostics()
724+
results.checkTask(.matchRuleType("VerifyModule")) { task in
725+
task.checkRuleInfo(["VerifyModule", "\(SRCROOT.str)/build/Debug/Orange.framework"])
726+
task.checkCommandLineMatches([.suffix("modules-verifier"), "\(SRCROOT.str)/build/Debug/Orange.framework", .anySequence, "--clang", .suffix("clang"), .anySequence])
727+
}
728+
results.checkNoErrors()
729+
}
730+
}
731+
}
732+
662733
@Test(.requireSDKs(.macOS))
663734
func externalModuleVerifier() async throws {
664735
let archs = ["arm64", "x86_64"]

0 commit comments

Comments
 (0)