Skip to content

Commit ed5b74f

Browse files
committed
Build metal shaders for the minimum selected macOS version
This fixes crashes on older OSes when building apps built with newer OSes
1 parent a529811 commit ed5b74f

File tree

3 files changed

+17
-6
lines changed

3 files changed

+17
-6
lines changed

Sources/swift-bundler/Bundler/MetalCompiler.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ enum MetalCompiler {
55
/// Compiles any metal shaders present in a directory into a `default.metallib` file (in the same directory).
66
/// - Parameters:
77
/// - directory: The directory to compile shaders from.
8+
/// - minimumMacOSVersion: The macOS version that the built shaders should target.
89
/// - keepSources: If `false`, the sources will get deleted after compilation.
910
/// - Returns: If an error occurs, a failure is returned.
10-
static func compileMetalShaders(in directory: URL, keepSources: Bool) -> Result<Void, MetalCompilerError> {
11+
static func compileMetalShaders(in directory: URL, minimumMacOSVersion: String, keepSources: Bool) -> Result<Void, MetalCompilerError> {
1112
guard let enumerator = FileManager.default.enumerator(at: directory, includingPropertiesForKeys: []) else {
1213
return .failure(.failedToEnumerateShaders(directory: directory))
1314
}
@@ -24,7 +25,7 @@ enum MetalCompiler {
2425
log.info("Compiling metal shaders")
2526

2627
// Compile metal shaders, and if successful, delete all shader sources
27-
return compileMetalShaders(shaderSources, destination: directory)
28+
return compileMetalShaders(shaderSources, destination: directory, minimumMacOSVersion: minimumMacOSVersion)
2829
.flatMap { _ in
2930
if keepSources {
3031
return .success()
@@ -46,8 +47,9 @@ enum MetalCompiler {
4647
/// - Parameters:
4748
/// - sources: The source files to comile.
4849
/// - destination: The directory to output `default.metallib` to.
50+
/// - minimumMacOSVersion: The macOS version that the built shaders should target.
4951
/// - Returns: Returns the location of the resulting `metallib`. If an error occurs, a failure is returned.
50-
static func compileMetalShaders(_ sources: [URL], destination: URL) -> Result<URL, MetalCompilerError> {
52+
static func compileMetalShaders(_ sources: [URL], destination: URL, minimumMacOSVersion: String) -> Result<URL, MetalCompilerError> {
5153
// Create a temporary directory for compilation
5254
let tempDirectory = FileManager.default.temporaryDirectory
5355
.appendingPathComponent("metal_compilation-\(UUID().uuidString)")
@@ -62,7 +64,7 @@ enum MetalCompiler {
6264
for shaderSource in sources {
6365
let outputFileName = shaderSource.deletingPathExtension().appendingPathExtension("air").lastPathComponent
6466
let outputFile = tempDirectory.appendingPathComponent(outputFileName)
65-
if case let .failure(error) = compileShader(shaderSource, to: outputFile) {
67+
if case let .failure(error) = compileShader(shaderSource, to: outputFile, minimumMacOSVersion: minimumMacOSVersion) {
6668
return .failure(error)
6769
}
6870
airFiles.append(outputFile)
@@ -89,12 +91,14 @@ enum MetalCompiler {
8991
/// - Parameters:
9092
/// - shader: The shader file to compile.
9193
/// - outputFile: The resulting `air` file.
94+
/// - minimumMacOSVersion: The macOS version that the built shader should target.
9295
/// - Returns: If an error occurs, a failure is returned.
93-
static func compileShader(_ shader: URL, to outputFile: URL) -> Result<Void, MetalCompilerError> {
96+
static func compileShader(_ shader: URL, to outputFile: URL, minimumMacOSVersion: String) -> Result<Void, MetalCompilerError> {
9497
let process = Process.create(
9598
"/usr/bin/xcrun",
9699
arguments: [
97100
"-sdk", "macosx", "metal",
101+
"-mmacosx-version-min=\(minimumMacOSVersion)",
98102
"-o", outputFile.path,
99103
"-c", shader.path
100104
])

Sources/swift-bundler/Bundler/ResourceBundler.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,17 @@ enum ResourceBundler {
8383
) -> Result<Void, ResourceBundlerError> {
8484
log.info("Fixing and copying resource bundle '\(bundle.lastPathComponent)'")
8585

86+
guard let minimumMacOSVersion = minimumMacOSVersion else {
87+
return .failure(.mustProvideMinimimMacOSVersion)
88+
}
89+
8690
let destinationBundle = destination.appendingPathComponent(bundle.lastPathComponent)
8791
let destinationBundleResources = destinationBundle
8892
.appendingPathComponent("Contents")
8993
.appendingPathComponent("Resources")
9094

9195
let compileMetalShaders: () -> Result<Void, ResourceBundlerError> = {
92-
MetalCompiler.compileMetalShaders(in: destinationBundleResources, keepSources: false)
96+
MetalCompiler.compileMetalShaders(in: destinationBundleResources, minimumMacOSVersion: minimumMacOSVersion, keepSources: false)
9397
.mapError { error in
9498
.failedToCompileMetalShaders(error)
9599
}

Sources/swift-bundler/Bundler/ResourceBundlerError.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ enum ResourceBundlerError: LocalizedError {
99
case failedToCopyResource(source: URL, destination: URL)
1010
case failedToEnumerateBundleContents(directory: URL, Error)
1111
case failedToCompileMetalShaders(MetalCompilerError)
12+
case mustProvideMinimimMacOSVersion
1213

1314
var errorDescription: String? {
1415
switch self {
@@ -26,6 +27,8 @@ enum ResourceBundlerError: LocalizedError {
2627
return "Failed to enumerate bundle contents at '\(directory.relativePath)'"
2728
case .failedToCompileMetalShaders(let metalCompilerError):
2829
return "Failed to compile Metal shaders: \(metalCompilerError.localizedDescription)"
30+
case .mustProvideMinimimMacOSVersion:
31+
return "Must provide 'minimum_macos_version' when app contains resources"
2932
}
3033
}
3134
}

0 commit comments

Comments
 (0)