Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
build.rs (9.6K)
1 use std::env;
2 use std::path::PathBuf;
3
4 fn main() {
5 println!("cargo:rerun-if-changed=src/server/wlroots_log_wrapper.c");
6 println!("cargo:rerun-if-changed=wrapper.h");
7 // Vendored scenefx sources: without these, editing a scenefx .c/.h
8 // silently ships a stale static lib (meson only reruns when build.rs
9 // does; meson compile is a fast no-op when nothing changed).
10 println!("cargo:rerun-if-changed=scenefx/types");
11 println!("cargo:rerun-if-changed=scenefx/render");
12 println!("cargo:rerun-if-changed=scenefx/include");
13 println!("cargo:rerun-if-changed=protocol/river-xkb-bindings-v1.xml");
14 println!("cargo:rerun-if-changed=protocol/river-layer-shell-v1.xml");
15 println!("cargo:rerun-if-changed=protocol/river-input-management-v1.xml");
16
17 // Build local scenefx
18 let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
19 if !std::path::Path::new("scenefx/build").exists() {
20 let status = std::process::Command::new("meson")
21 .args(&["setup", "build", "--buildtype=release", "--default-library=static"])
22 .current_dir("scenefx")
23 .status()
24 .expect("Failed to run meson setup");
25 assert!(status.success(), "meson setup failed");
26 }
27 let status = std::process::Command::new("meson")
28 .args(&["compile", "-C", "build"])
29 .current_dir("scenefx")
30 .status()
31 .expect("Failed to run meson compile");
32 assert!(status.success(), "meson compile failed");
33
34 let scenefx_inc1 = PathBuf::from(&manifest_dir).join("scenefx/include");
35 let scenefx_inc2 = PathBuf::from(&manifest_dir).join("scenefx/build/include");
36 let scenefx_inc3 = PathBuf::from(&manifest_dir).join("scenefx/build/protocol");
37 let scenefx_include_paths = vec![scenefx_inc1, scenefx_inc2, scenefx_inc3];
38
39 println!("cargo:rustc-link-search=native={}/scenefx/build", manifest_dir);
40 println!("cargo:rustc-link-lib=static=scenefx-0.5");
41 println!("cargo:rustc-link-lib=dylib=GLESv2");
42 println!("cargo:rustc-link-lib=dylib=EGL");
43 println!("cargo:rustc-link-lib=dylib=drm");
44 println!("cargo:rustc-link-lib=dylib=gbm");
45 println!("cargo:rustc-link-lib=dylib=lcms2");
46
47 let wlroots = pkg_config::Config::new()
48 .atleast_version("0.20.0")
49 .probe("wlroots-0.20")
50 .expect("wlroots-0.20 is required");
51
52 let wl_server = pkg_config::probe_library("wayland-server")
53 .expect("wayland-server is required");
54
55 let xkb = pkg_config::probe_library("xkbcommon")
56 .expect("xkbcommon is required");
57
58 let pixman = pkg_config::probe_library("pixman-1")
59 .expect("pixman-1 is required");
60
61 let libinput = pkg_config::probe_library("libinput")
62 .expect("libinput is required");
63
64 let libevdev = pkg_config::probe_library("libevdev")
65 .expect("libevdev is required");
66
67 let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
68
69 // Helper to fix XML files starting with comments instead of the XML declaration
70 let clean_xml = |src: &str, dst: &std::path::Path| {
71 let content = std::fs::read_to_string(src).expect("Failed to read XML source");
72 let mut lines: Vec<String> = content.lines().map(|s| s.to_string()).collect();
73 if lines.len() > 1 && lines[0].starts_with("<!--") && lines[1].starts_with("<?xml") {
74 lines.swap(0, 1);
75 }
76 let cleaned = lines.join("\n");
77 std::fs::write(dst, cleaned).expect("Failed to write cleaned XML");
78 };
79
80 // Generate upstream protocol headers (header-only)
81 let upstream_protocols = vec![
82 ("wlr-layer-shell-unstable-v1.xml", "protocol/upstream/wlr-layer-shell-unstable-v1.xml"),
83 ("wlr-output-power-management-unstable-v1.xml", "protocol/upstream/wlr-output-power-management-unstable-v1.xml"),
84 ("virtual-keyboard-unstable-v1.xml", "protocol/upstream/virtual-keyboard-unstable-v1.xml"),
85 ("tablet-v2.xml", "/usr/share/wayland-protocols/stable/tablet/tablet-v2.xml"),
86 ("xdg-shell.xml", "/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml"),
87 ("color-management-v1.xml", "/usr/share/wayland-protocols/staging/color-management/color-management-v1.xml"),
88 ("content-type-v1.xml", "/usr/share/wayland-protocols/staging/content-type/content-type-v1.xml"),
89 ("cursor-shape-v1.xml", "/usr/share/wayland-protocols/staging/cursor-shape/cursor-shape-v1.xml"),
90 ("ext-image-copy-capture-v1.xml", "/usr/share/wayland-protocols/staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml"),
91 ("pointer-constraints-unstable-v1.xml", "/usr/share/wayland-protocols/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"),
92 ("tearing-control-v1.xml", "/usr/share/wayland-protocols/staging/tearing-control/tearing-control-v1.xml"),
93 ];
94
95 for (name, path) in upstream_protocols {
96 let temp_xml = out_dir.join(format!("{}-temp.xml", name));
97 clean_xml(path, &temp_xml);
98 let header_name = name.replace(".xml", "-protocol.h");
99 let status = std::process::Command::new("wayland-scanner")
100 .args(&[
101 "server-header",
102 temp_xml.to_str().unwrap(),
103 out_dir.join(&header_name).to_str().unwrap(),
104 ])
105 .status()
106 .expect("failed to execute wayland-scanner");
107 assert!(status.success(), "wayland-scanner server-header failed for {}", name);
108 }
109
110 // Generate custom river protocol headers and private-code C files
111 let custom_protocols = vec![
112 ("river-xkb-bindings-v1.xml", "protocol/river-xkb-bindings-v1.xml"),
113 ("river-layer-shell-v1.xml", "protocol/river-layer-shell-v1.xml"),
114 ("river-input-management-v1.xml", "protocol/river-input-management-v1.xml"),
115 ("river-libinput-config-v1.xml", "protocol/river-libinput-config-v1.xml"),
116 ("river-xkb-config-v1.xml", "protocol/river-xkb-config-v1.xml"),
117 ("cce-inspector-v1.xml", "protocol/cce-inspector-v1.xml"),
118 ("cce-window-management-v1.xml", "protocol/cce-window-management-v1.xml"),
119 ];
120
121 let mut generated_c_files = Vec::new();
122
123 for (name, path) in custom_protocols {
124 let temp_xml = out_dir.join(format!("{}-temp.xml", name));
125 clean_xml(path, &temp_xml);
126
127 let header_name = name.replace(".xml", "-protocol.h");
128 let status_h = std::process::Command::new("wayland-scanner")
129 .args(&[
130 "server-header",
131 temp_xml.to_str().unwrap(),
132 out_dir.join(&header_name).to_str().unwrap(),
133 ])
134 .status()
135 .expect("failed to execute wayland-scanner");
136 assert!(status_h.success(), "wayland-scanner server-header failed for {}", name);
137
138 let code_name = name.replace(".xml", "-protocol.c");
139 let code_path = out_dir.join(&code_name);
140 let status_c = std::process::Command::new("wayland-scanner")
141 .args(&[
142 "private-code",
143 temp_xml.to_str().unwrap(),
144 code_path.to_str().unwrap(),
145 ])
146 .status()
147 .expect("failed to execute wayland-scanner");
148 assert!(status_c.success(), "wayland-scanner private-code failed for {}", name);
149
150 generated_c_files.push(code_path);
151 }
152
153 // Compile the C wrapper and protocol C files
154 let mut build = cc::Build::new();
155 build.file("src/server/wlroots_log_wrapper.c")
156 .define("WLR_USE_UNSTABLE", None)
157 .flag("-std=c99")
158 .flag("-O2")
159 .include(&out_dir);
160
161 for c_file in generated_c_files {
162 build.file(c_file);
163 }
164
165 // Add include paths for scenefx, wlroots, and wayland-server
166 for path in &scenefx_include_paths {
167 build.include(path);
168 }
169 for path in &wlroots.include_paths {
170 build.include(path);
171 }
172 for path in &wl_server.include_paths {
173 build.include(path);
174 }
175 build.compile("wlroots_log_wrapper");
176
177
178 // Setup bindgen
179 let mut builder = bindgen::Builder::default()
180 .header("wrapper.h")
181 .clang_arg("-DWLR_USE_UNSTABLE")
182 .layout_tests(false)
183 .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()));
184
185 // Pass include paths to bindgen clang argument parser
186 let mut include_paths = vec![out_dir.clone()];
187 include_paths.extend(scenefx_include_paths.clone());
188 include_paths.extend(wlroots.include_paths.clone());
189 include_paths.extend(wl_server.include_paths.clone());
190 include_paths.extend(xkb.include_paths.clone());
191 include_paths.extend(pixman.include_paths.clone());
192 include_paths.extend(libinput.include_paths.clone());
193 include_paths.extend(libevdev.include_paths.clone());
194
195 for path in include_paths {
196 builder = builder.clang_arg(format!("-I{}", path.display()));
197 }
198
199 let bindings = builder
200 .blocklist_item("FP_NAN")
201 .blocklist_item("FP_INFINITE")
202 .blocklist_item("FP_ZERO")
203 .blocklist_item("FP_SUBNORMAL")
204 .blocklist_item("FP_NORMAL")
205 .blocklist_item("wl_listener")
206 .blocklist_item("wlr_addon")
207 .blocklist_item("wlr_input_device")
208 .blocklist_item("pixman_region32")
209 .blocklist_item("pixman_region32_t")
210 .blocklist_item("pixman_box32")
211 .blocklist_item("pixman_box32_t")
212 .blocklist_item("pixman_rectangle32")
213 .blocklist_item("pixman_rectangle32_t")
214 .blocklist_item("pixman_region32_data")
215 .blocklist_item("pixman_region32_data_t")
216 .generate()
217 .expect("Unable to generate bindings");
218
219 let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
220 bindings
221 .write_to_file(out_path.join("bindings.rs"))
222 .expect("Couldn't write bindings!");
223 }