Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-renderer/build.rs
1// We can use `std::process:Command` here because this is invoked within a build script,
2// _not_ within the Warp binary (where it could cause a terminal to temporarily flash on
3// Windows).
4#![allow(clippy::disallowed_types)]
5 
6use std::{env, fs, path::PathBuf, process::Command};
7 
8use cfg_aliases::cfg_aliases;
9 
10fn main() {
11 cfg_aliases! {
12 macos: { target_os = "macos" },
13 // We use winit on all platforms other than mac, where we have a custom
14 // AppKit-based platform implementation.
15 winit: { not(macos) },
16 // We use wgpu for rendering on all platforms where we use winit, but
17 // we can also use it on macOS, if enabled.
18 wgpu: { any(winit, feature = "experimental-wgpu-renderer") },
19 native: { not(target_family = "wasm") },
20 }
21 
22 if env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos") {
23 bindgen_shader_types();
24 compile_metal_shaders();
25 compile_objc_lib();
26 }
27}
28 
29fn bindgen_shader_types() {
30 let header_path = "src/platform/mac/rendering/metal/shaders/shader_types.h";
31 println!("cargo:rerun-if-changed={header_path}");
32 let bindings = bindgen::Builder::default()
33 .header(header_path)
34 .allowlist_type("vector_float2")
35 .allowlist_type("Uniforms")
36 .allowlist_type("PerRectUniforms")
37 .allowlist_type("PerGlyphUniforms")
38 .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
39 // Disable intrinsic headers that define types containing 16-bit floats (`_Float16`,
40 // `__m512h`, etc.) via preprocessor directive. `bindgen` doesn't know how to process
41 // these types and panics at compile time. The types aren't used by our shader headers,
42 // so suppressing the declarations is safe.
43 //
44 // - Xcode 15+: avx512fp16intrin.h, avx512vlfp16intrin.h
45 // - Xcode 26+ (clang 21): amxavx512intrin.h, avx10_2convertintrin.h,
46 // avx10_2_512convertintrin.h
47 //
48 // TODO(charlespierce): Remove once https://github.com/rust-lang/rust-bindgen/issues/2500
49 // is resolved.
50 .clang_args([
51 "-D__AVX512VLFP16INTRIN_H",
52 "-D__AVX512FP16INTRIN_H",
53 "-D__AMX_AVX512INTRIN_H",
54 "-D__AVX10_2CONVERTINTRIN_H",
55 "-D__AVX10_2_512CONVERTINTRIN_H",
56 "-D__AVX10_2_512MINMAXINTRIN_H",
57 "-D__AVX10_2_512NIINTRIN_H",
58 "-D__AVX10_2_512SATCVTINTRIN_H",
59 ])
60 .generate()
61 .unwrap_or_else(|_| panic!("unable to generate bindings for {header_path}"));
62 
63 let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
64 bindings
65 .write_to_file(out_path.join("shader_types.rs"))
66 .expect("Couldn't write shader type bindings!");
67}
68 
69fn compile_metal_shaders() {
70 let header_path = "src/platform/mac/rendering/metal/shaders/shader_types.h";
71 let metal_path = "src/platform/mac/rendering/metal/shaders/shaders.metal";
72 let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
73 
74 let air_path = out_path.join("shaders.air");
75 let air_path = air_path.to_str().unwrap();
76 
77 let lib_path = out_path.join("shaders.metallib");
78 let lib_path = lib_path.to_str().unwrap();
79 
80 println!("cargo:rerun-if-changed={header_path}");
81 println!("cargo:rerun-if-changed={metal_path}");
82 
83 let metal_available = Command::new("xcrun")
84 .args(["-sdk", "macosx", "-find", "metal"])
85 .output()
86 .is_ok_and(|output| output.status.success());
87 
88 if !metal_available {
89 println!(
90 "cargo:warning=Metal toolchain not found; writing placeholder shaders.metallib for cargo check"
91 );
92 fs::write(lib_path, []).expect("could not write placeholder Metal library");
93 return;
94 }
95 
96 let mut compile_args = vec!["-sdk", "macosx", "metal", "-c", metal_path, "-o", air_path];
97 if cfg!(feature = "enable-metal-frame-capture") {
98 compile_args.push("-frecord-sources");
99 compile_args.push("-gline-tables-only");
100 }
101 let result = Command::new("xcrun")
102 .args(&compile_args)
103 .output()
104 .expect("error compiling metal shaders to .air");
105 if !result.status.success() {
106 println!(
107 "cargo:warning=Metal shader compilation failed; writing placeholder shaders.metallib for cargo check: {}",
108 std::str::from_utf8(&result.stderr).unwrap_or("<non-utf8 stderr>")
109 );
110 fs::write(lib_path, []).expect("could not write placeholder Metal library");
111 return;
112 }
113 
114 let result = Command::new("xcrun")
115 .args(["-sdk", "macosx", "metallib", air_path, "-o", lib_path])
116 .output()
117 .expect("error compiling metal shaders to .metallib");
118 assert!(
119 result.status.success(),
120 "error compling metal shaders to .metallib; {}",
121 std::str::from_utf8(&result.stderr).unwrap(),
122 );
123}
124 
125fn compile_objc_lib() {
126 println!("cargo:rustc-link-lib=framework=UserNotifications");
127 println!("cargo:rustc-link-lib=framework=Carbon");
128 println!("cargo:rustc-link-lib=framework=SystemConfiguration");
129 println!("cargo:rustc-link-lib=framework=UniformTypeIdentifiers");
130 println!("cargo:rustc-link-lib=framework=AVFoundation");
131 println!("cargo:rustc-link-lib=framework=ServiceManagement");
132 println!("cargo:rerun-if-changed=src/platform/mac/objc/app.h");
133 println!("cargo:rerun-if-changed=src/platform/mac/objc/app.m");
134 println!("cargo:rerun-if-changed=src/platform/mac/objc/keycode.m");
135 println!("cargo:rerun-if-changed=src/platform/mac/objc/host_view.m");
136 println!("cargo:rerun-if-changed=src/platform/mac/objc/host_view.h");
137 println!("cargo:rerun-if-changed=src/platform/mac/objc/hotkey.h");
138 println!("cargo:rerun-if-changed=src/platform/mac/objc/hotkey.m");
139 println!("cargo:rerun-if-changed=src/platform/mac/objc/menus.h");
140 println!("cargo:rerun-if-changed=src/platform/mac/objc/menus.m");
141 println!("cargo:rerun-if-changed=src/platform/mac/objc/notifications/notifications.h");
142 println!("cargo:rerun-if-changed=src/platform/mac/objc/notifications/notifications.m");
143 println!("cargo:rerun-if-changed=src/platform/mac/objc/window.m");
144 println!("cargo:rerun-if-changed=src/platform/mac/objc/window_blur.m");
145 println!("cargo:rerun-if-changed=src/platform/mac/objc/window_blur.h");
146 // Referenced from https://github.com/tonymillion/Reachability
147 println!("cargo:rerun-if-changed=src/platform/mac/objc/reachability.h");
148 println!("cargo:rerun-if-changed=src/platform/mac/objc/reachability.m");
149 println!("cargo:rerun-if-changed=src/platform/mac/objc/alert.m");
150 println!("cargo:rerun-if-changed=src/platform/mac/objc/alert.h");
151 println!("cargo:rerun-if-changed=src/platform/mac/objc/fullscreen_queue.h");
152 println!("cargo:rerun-if-changed=src/platform/mac/objc/fullscreen_queue.m");
153 
154 // Link against the clang_rt library so that the @available keyword
155 // doesn't produce linker errors.
156 //
157 // See: https://github.com/alexcrichton/curl-rust/issues/279
158 if let Some(path) = macos_link_search_path() {
159 println!("cargo:rustc-link-lib=clang_rt.osx");
160 println!("cargo:rustc-link-search={path}");
161 }
162 
163 cc::Build::new()
164 .file("src/platform/mac/objc/app.m")
165 .file("src/platform/mac/objc/host_view.m")
166 .file("src/platform/mac/objc/hotkey.m")
167 .file("src/platform/mac/objc/reachability.m")
168 .file("src/platform/mac/objc/keycode.m")
169 .file("src/platform/mac/objc/menus.m")
170 .file("src/platform/mac/objc/notifications/notifications.m")
171 .file("src/platform/mac/objc/window.m")
172 .file("src/platform/mac/objc/fullscreen_queue.m")
173 .file("src/platform/mac/objc/window_blur.m")
174 .file("src/platform/mac/objc/alert.m")
175 .compile("warp_objc");
176}
177 
178/// Determine the path containing the macOS standard libraries by querying
179/// clang's library search paths.
180fn macos_link_search_path() -> Option<String> {
181 let output = Command::new("clang")
182 .arg("--print-search-dirs")
183 .output()
184 .ok()?;
185 if !output.status.success() {
186 println!(
187 "failed to run 'clang --print-search-dirs', continuing without a link search path"
188 );
189 return None;
190 }
191 
192 let stdout = String::from_utf8_lossy(&output.stdout);
193 for line in stdout.lines() {
194 if line.contains("libraries: =") {
195 let path = line.split('=').nth(1)?;
196 return Some(format!("{path}/lib/darwin"));
197 }
198 }
199 
200 println!("failed to determine link search path, continuing without it");
201 None
202}
203