Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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?) {
Expand Down
53 changes: 44 additions & 9 deletions Sources/SWBCore/ToolInfo/ClangToolInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<BlockListT: ProjectFailuresBlockList>(_ 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?
Expand All @@ -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"
Expand Down Expand Up @@ -66,13 +97,13 @@ public struct DiscoveredClangToolSpecInfo: DiscoveredCommandLineToolSpecInfo {
public func deploymentTargetEnvironmentVariableNames() -> Set<String> {
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)
}
}

Expand All @@ -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.
Expand Down Expand Up @@ -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)
)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down