Skip to content

Commit f83e6f5

Browse files
authored
Merge pull request #648 from owenv/owenv/metal-toolchain
[6.2] Update metal tests to account for a downloaded metal toolchain
2 parents 187d0ae + da06329 commit f83e6f5

File tree

3 files changed

+40
-2
lines changed

3 files changed

+40
-2
lines changed

Sources/SWBTestSupport/CoreTestSupport.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,40 @@ extension Core {
7070
try POSIX.unsetenv(variable)
7171
}
7272

73+
// Handle Xcodes with an unbundled Metal toolchain by querying `xcodebuild` if needed.
74+
//
75+
// If the given environment already contains `EXTERNAL_TOOLCHAINS_DIR` and `TOOLCHAINS`, we're assuming that we do not have to obtain any toolchain information.
76+
var environment = environment
77+
if (try? ProcessInfo.processInfo.hostOperatingSystem()) == .macOS, !(environment.contains("EXTERNAL_TOOLCHAINS_DIR") && environment.contains("TOOLCHAINS")) {
78+
let activeDeveloperPath: Path
79+
if let developerPath {
80+
activeDeveloperPath = developerPath.path
81+
} else {
82+
activeDeveloperPath = try await Xcode.getActiveDeveloperDirectoryPath()
83+
}
84+
let defaultToolchainPath = activeDeveloperPath.join("Toolchains/XcodeDefault.xctoolchain")
85+
86+
if !localFS.exists(defaultToolchainPath.join("usr/metal/current")) {
87+
struct MetalToolchainInfo: Decodable {
88+
let buildVersion: String
89+
let status: String
90+
let toolchainIdentifier: String
91+
let toolchainSearchPath: String
92+
}
93+
94+
let result = try await Process.getOutput(url: URL(fileURLWithPath: activeDeveloperPath.join("usr/bin/xcodebuild").str), arguments: ["-showComponent", "metalToolchain", "-json"], environment: ["DEVELOPER_DIR": activeDeveloperPath.str])
95+
if result.exitStatus != .exit(0) {
96+
throw StubError.error("xcodebuild failed: \(String(data: result.stdout, encoding: .utf8) ?? "")\n\(String(data: result.stderr, encoding: .utf8) ?? "")")
97+
}
98+
99+
let metalToolchainInfo = try JSONDecoder().decode(MetalToolchainInfo.self, from: result.stdout)
100+
environment.addContents(of: [
101+
"TOOLCHAINS": "\(metalToolchainInfo.toolchainIdentifier) $(inherited)",
102+
"EXTERNAL_TOOLCHAINS_DIR": metalToolchainInfo.toolchainSearchPath,
103+
])
104+
}
105+
}
106+
73107
// 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.
74108
let inferiorProductsPath: Path? = self.inferiorProductsPath()
75109

Tests/SWBBuildSystemTests/BuildCommandTests.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ fileprivate struct BuildCommandTests: CoreBasedTests {
325325

326326
@Test(.requireSDKs(.macOS), .requireXcode16())
327327
func singleFileCompileMetal() async throws {
328+
let core = try await getCore()
328329
try await withTemporaryDirectory { tmpDirPath async throws -> Void in
329330
let testWorkspace = try await TestWorkspace(
330331
"Test",
@@ -337,6 +338,7 @@ fileprivate struct BuildCommandTests: CoreBasedTests {
337338
"Debug",
338339
buildSettings: ["PRODUCT_NAME": "$(TARGET_NAME)",
339340
"SWIFT_ENABLE_EXPLICIT_MODULES": "NO",
341+
"TOOLCHAINS": core.environment["TOOLCHAINS"] ?? "$(inherited)",
340342
"SWIFT_VERSION": swiftVersion])],
341343
targets: [
342344
TestStandardTarget(
@@ -348,7 +350,7 @@ fileprivate struct BuildCommandTests: CoreBasedTests {
348350
)
349351
]
350352
)
351-
let tester = try await BuildOperationTester(getCore(), testWorkspace, simulated: false)
353+
let tester = try await BuildOperationTester(core, testWorkspace, simulated: false)
352354

353355
let metalFile = testWorkspace.sourceRoot.join("aProject/Metal.metal")
354356
try await tester.fs.writeFileContents(metalFile) { stream in }

Tests/SWBBuildSystemTests/BuildOperationTests.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5756,6 +5756,7 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
57565756

57575757
@Test(.requireSDKs(.macOS))
57585758
func incrementalMetalLinkWithCodeSign() async throws {
5759+
let core = try await getCore()
57595760
try await withTemporaryDirectory { tmpDirPath async throws -> Void in
57605761
let testWorkspace = try await TestWorkspace(
57615762
"Test",
@@ -5773,6 +5774,7 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
57735774
"CODE_SIGN_IDENTITY": "-",
57745775
"INFOPLIST_FILE": "Info.plist",
57755776
"CODESIGN": "/usr/bin/true",
5777+
"TOOLCHAINS": core.environment["TOOLCHAINS"] ?? "$(inherited)",
57765778
"SWIFT_VERSION": swiftVersion])],
57775779
targets: [
57785780
TestStandardTarget(
@@ -5781,7 +5783,7 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
57815783
buildPhases: [
57825784
TestSourcesBuildPhase(["SwiftFile.swift", "Metal.metal"]),
57835785
])])])
5784-
let tester = try await BuildOperationTester(getCore(), testWorkspace, simulated: false, fileSystem: localFS)
5786+
let tester = try await BuildOperationTester(core, testWorkspace, simulated: false, fileSystem: localFS)
57855787
let signableTargets: Set<String> = ["aFramework"]
57865788

57875789
// Create the input files.

0 commit comments

Comments
 (0)