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