1#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")]
2
3fn main() {
4 #[cfg(target_os = "macos")]
5 macos_build::run();
6}
7
8#[cfg(target_os = "macos")]
9mod macos_build {
10 use std::{
11 env,
12 path::{Path, PathBuf},
13 };
14
15 use cbindgen::Config;
16
17 pub fn run() {
18 let header_path = generate_shader_bindings();
19
20 #[cfg(feature = "runtime_shaders")]
21 emit_stitched_shaders(&header_path);
22 #[cfg(not(feature = "runtime_shaders"))]
23 compile_metal_shaders(&header_path);
24 }
25
26 fn generate_shader_bindings() -> PathBuf {
27 let output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("scene.h");
28
29 let gpui_dir = find_gpui_crate_dir();
30
31 let mut config = Config {
32 include_guard: Some("SCENE_H".into()),
33 language: cbindgen::Language::C,
34 no_includes: true,
35 ..Default::default()
36 };
37 config.export.include.extend([
38 "Bounds".into(),
39 "Corners".into(),
40 "Edges".into(),
41 "Size".into(),
42 "Pixels".into(),
43 "PointF".into(),
44 "Hsla".into(),
45 "ContentMask".into(),
46 "Uniforms".into(),
47 "AtlasTile".into(),
48 "PathRasterizationInputIndex".into(),
49 "PathVertex_ScaledPixels".into(),
50 "PathRasterizationVertex".into(),
51 "ShadowInputIndex".into(),
52 "Shadow".into(),
53 "QuadInputIndex".into(),
54 "Underline".into(),
55 "UnderlineInputIndex".into(),
56 "Quad".into(),
57 "BorderStyle".into(),
58 "SpriteInputIndex".into(),
59 "MonochromeSprite".into(),
60 "PolychromeSprite".into(),
61 "PathSprite".into(),
62 "SurfaceInputIndex".into(),
63 "SurfaceBounds".into(),
64 "TransformationMatrix".into(),
65 ]);
66 config.no_includes = true;
67 config.enumeration.prefix_with_name = true;
68
69 let mut builder = cbindgen::Builder::new();
70
71 let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
72
73 // Source files from gpui that define types used in shaders
74 let gpui_src_paths = [
75 gpui_dir.join("src/scene.rs"),
76 gpui_dir.join("src/geometry.rs"),
77 gpui_dir.join("src/color.rs"),
78 gpui_dir.join("src/window.rs"),
79 gpui_dir.join("src/platform.rs"),
80 ];
81
82 // Source files from this crate
83 let local_src_paths = [crate_dir.join("src/metal_renderer.rs")];
84
85 for src_path in gpui_src_paths.iter().chain(local_src_paths.iter()) {
86 println!("cargo:rerun-if-changed={}", src_path.display());
87 builder = builder.with_src(src_path);
88 }
89
90 builder
91 .with_config(config)
92 .generate()
93 .expect("Unable to generate bindings")
94 .write_to_file(&output_path);
95
96 output_path
97 }
98
99 /// Locate the gpui crate directory relative to this crate.
100 fn find_gpui_crate_dir() -> PathBuf {
101 gpui::GPUI_MANIFEST_DIR.into()
102 }
103
104 /// To enable runtime compilation, we need to "stitch" the shaders file with the generated header
105 /// so that it is self-contained.
106 #[cfg(feature = "runtime_shaders")]
107 fn emit_stitched_shaders(header_path: &Path) {
108 fn stitch_header(header: &Path, shader_path: &Path) -> std::io::Result<PathBuf> {
109 let header_contents = std::fs::read_to_string(header)?;
110 let shader_contents = std::fs::read_to_string(shader_path)?;
111 let stitched_contents = format!("{header_contents}\n{shader_contents}");
112 let out_path =
113 PathBuf::from(env::var("OUT_DIR").unwrap()).join("stitched_shaders.metal");
114 std::fs::write(&out_path, stitched_contents)?;
115 Ok(out_path)
116 }
117 let shader_source_path = "./src/shaders.metal";
118 let shader_path = PathBuf::from(shader_source_path);
119 stitch_header(header_path, &shader_path).unwrap();
120 println!("cargo:rerun-if-changed={}", &shader_source_path);
121 }
122
123 #[cfg(not(feature = "runtime_shaders"))]
124 fn compile_metal_shaders(header_path: &Path) {
125 use std::process::{self, Command};
126 let shader_path = "./src/shaders.metal";
127 let air_output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.air");
128 let metallib_output_path =
129 PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.metallib");
130 println!("cargo:rerun-if-changed={}", shader_path);
131
132 let output = Command::new("xcrun")
133 .args([
134 "-sdk",
135 "macosx",
136 "metal",
137 "-gline-tables-only",
138 "-mmacosx-version-min=10.15.7",
139 "-MO",
140 "-c",
141 shader_path,
142 "-include",
143 (header_path.to_str().unwrap()),
144 "-o",
145 ])
146 .arg(&air_output_path)
147 .output()
148 .unwrap();
149
150 if !output.status.success() {
151 println!(
152 "cargo::error=metal shader compilation failed:\n{}",
153 String::from_utf8_lossy(&output.stderr)
154 );
155 process::exit(1);
156 }
157
158 let output = Command::new("xcrun")
159 .args(["-sdk", "macosx", "metallib"])
160 .arg(air_output_path)
161 .arg("-o")
162 .arg(metallib_output_path)
163 .output()
164 .unwrap();
165
166 if !output.status.success() {
167 println!(
168 "cargo::error=metallib compilation failed:\n{}",
169 String::from_utf8_lossy(&output.stderr)
170 );
171 process::exit(1);
172 }
173 }
174}