Skip to content

[6.2] Update metal tests to account for a downloaded metal toolchain #648

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 11, 2025
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
34 changes: 34 additions & 0 deletions Sources/SWBTestSupport/CoreTestSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,40 @@ extension Core {
try POSIX.unsetenv(variable)
}

// Handle Xcodes with an unbundled Metal toolchain by querying `xcodebuild` if needed.
//
// If the given environment already contains `EXTERNAL_TOOLCHAINS_DIR` and `TOOLCHAINS`, we're assuming that we do not have to obtain any toolchain information.
var environment = environment
if (try? ProcessInfo.processInfo.hostOperatingSystem()) == .macOS, !(environment.contains("EXTERNAL_TOOLCHAINS_DIR") && environment.contains("TOOLCHAINS")) {
let activeDeveloperPath: Path
if let developerPath {
activeDeveloperPath = developerPath.path
} else {
activeDeveloperPath = try await Xcode.getActiveDeveloperDirectoryPath()
}
let defaultToolchainPath = activeDeveloperPath.join("Toolchains/XcodeDefault.xctoolchain")

if !localFS.exists(defaultToolchainPath.join("usr/metal/current")) {
struct MetalToolchainInfo: Decodable {
let buildVersion: String
let status: String
let toolchainIdentifier: String
let toolchainSearchPath: String
}

let result = try await Process.getOutput(url: URL(fileURLWithPath: activeDeveloperPath.join("usr/bin/xcodebuild").str), arguments: ["-showComponent", "metalToolchain", "-json"], environment: ["DEVELOPER_DIR": activeDeveloperPath.str])
if result.exitStatus != .exit(0) {
throw StubError.error("xcodebuild failed: \(String(data: result.stdout, encoding: .utf8) ?? "")\n\(String(data: result.stderr, encoding: .utf8) ?? "")")
}

let metalToolchainInfo = try JSONDecoder().decode(MetalToolchainInfo.self, from: result.stdout)
environment.addContents(of: [
"TOOLCHAINS": "\(metalToolchainInfo.toolchainIdentifier) $(inherited)",
"EXTERNAL_TOOLCHAINS_DIR": metalToolchainInfo.toolchainSearchPath,
])
}
}

// When this code is being loaded directly via unit tests *and* we detect the products directory we are running in is for Xcode, then we should run using inferior search paths.
let inferiorProductsPath: Path? = self.inferiorProductsPath()

Expand Down
4 changes: 3 additions & 1 deletion Tests/SWBBuildSystemTests/BuildCommandTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ fileprivate struct BuildCommandTests: CoreBasedTests {

@Test(.requireSDKs(.macOS), .requireXcode16())
func singleFileCompileMetal() async throws {
let core = try await getCore()
try await withTemporaryDirectory { tmpDirPath async throws -> Void in
let testWorkspace = try await TestWorkspace(
"Test",
Expand All @@ -337,6 +338,7 @@ fileprivate struct BuildCommandTests: CoreBasedTests {
"Debug",
buildSettings: ["PRODUCT_NAME": "$(TARGET_NAME)",
"SWIFT_ENABLE_EXPLICIT_MODULES": "NO",
"TOOLCHAINS": core.environment["TOOLCHAINS"] ?? "$(inherited)",
"SWIFT_VERSION": swiftVersion])],
targets: [
TestStandardTarget(
Expand All @@ -348,7 +350,7 @@ fileprivate struct BuildCommandTests: CoreBasedTests {
)
]
)
let tester = try await BuildOperationTester(getCore(), testWorkspace, simulated: false)
let tester = try await BuildOperationTester(core, testWorkspace, simulated: false)

let metalFile = testWorkspace.sourceRoot.join("aProject/Metal.metal")
try await tester.fs.writeFileContents(metalFile) { stream in }
Expand Down
4 changes: 3 additions & 1 deletion Tests/SWBBuildSystemTests/BuildOperationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5756,6 +5756,7 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script

@Test(.requireSDKs(.macOS))
func incrementalMetalLinkWithCodeSign() async throws {
let core = try await getCore()
try await withTemporaryDirectory { tmpDirPath async throws -> Void in
let testWorkspace = try await TestWorkspace(
"Test",
Expand All @@ -5773,6 +5774,7 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
"CODE_SIGN_IDENTITY": "-",
"INFOPLIST_FILE": "Info.plist",
"CODESIGN": "/usr/bin/true",
"TOOLCHAINS": core.environment["TOOLCHAINS"] ?? "$(inherited)",
"SWIFT_VERSION": swiftVersion])],
targets: [
TestStandardTarget(
Expand All @@ -5781,7 +5783,7 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
buildPhases: [
TestSourcesBuildPhase(["SwiftFile.swift", "Metal.metal"]),
])])])
let tester = try await BuildOperationTester(getCore(), testWorkspace, simulated: false, fileSystem: localFS)
let tester = try await BuildOperationTester(core, testWorkspace, simulated: false, fileSystem: localFS)
let signableTargets: Set<String> = ["aFramework"]

// Create the input files.
Expand Down
Loading