Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
build.rs (9.8K)
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 // Already loaded through wlroots' XWM; linked for the one property read
47 // wlroots does not parse (`XwaylandWindow::motif_hints`).
48 println!("cargo:rustc-link-lib=dylib=xcb");
49
50 let wlroots = pkg_config::Config::new()
51 .atleast_version("0.20.0")
52 .probe("wlroots-0.20")
53 .expect("wlroots-0.20 is required");
54
55 let wl_server = pkg_config::probe_library("wayland-server")
56 .expect("wayland-server is required");
57
58 let xkb = pkg_config::probe_library("xkbcommon")
59 .expect("xkbcommon is required");
60
61 let pixman = pkg_config::probe_library("pixman-1")
62 .expect("pixman-1 is required");
63
64 let libinput = pkg_config::probe_library("libinput")
65 .expect("libinput is required");
66
67 let libevdev = pkg_config::probe_library("libevdev")
68 .expect("libevdev is required");
69
70 let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
71
72 // Helper to fix XML files starting with comments instead of the XML declaration
73 let clean_xml = |src: &str, dst: &std::path::Path| {
74 let content = std::fs::read_to_string(src).expect("Failed to read XML source");
75 let mut lines: Vec<String> = content.lines().map(|s| s.to_string()).collect();
76 if lines.len() > 1 && lines[0].starts_with("<!--") && lines[1].starts_with("<?xml") {
77 lines.swap(0, 1);
78 }
79 let cleaned = lines.join("\n");
80 std::fs::write(dst, cleaned).expect("Failed to write cleaned XML");
81 };
82
83 // Generate upstream protocol headers (header-only)
84 let upstream_protocols = vec![
85 ("wlr-layer-shell-unstable-v1.xml", "protocol/upstream/wlr-layer-shell-unstable-v1.xml"),
86 ("wlr-output-power-management-unstable-v1.xml", "protocol/upstream/wlr-output-power-management-unstable-v1.xml"),
87 ("virtual-keyboard-unstable-v1.xml", "protocol/upstream/virtual-keyboard-unstable-v1.xml"),
88 ("tablet-v2.xml", "/usr/share/wayland-protocols/stable/tablet/tablet-v2.xml"),
89 ("xdg-shell.xml", "/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml"),
90 ("color-management-v1.xml", "/usr/share/wayland-protocols/staging/color-management/color-management-v1.xml"),
91 ("content-type-v1.xml", "/usr/share/wayland-protocols/staging/content-type/content-type-v1.xml"),
92 ("cursor-shape-v1.xml", "/usr/share/wayland-protocols/staging/cursor-shape/cursor-shape-v1.xml"),
93 ("ext-image-copy-capture-v1.xml", "/usr/share/wayland-protocols/staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml"),
94 ("pointer-constraints-unstable-v1.xml", "/usr/share/wayland-protocols/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"),
95 ("tearing-control-v1.xml", "/usr/share/wayland-protocols/staging/tearing-control/tearing-control-v1.xml"),
96 ];
97
98 for (name, path) in upstream_protocols {
99 let temp_xml = out_dir.join(format!("{}-temp.xml", name));
100 clean_xml(path, &temp_xml);
101 let header_name = name.replace(".xml", "-protocol.h");
102 let status = std::process::Command::new("wayland-scanner")
103 .args(&[
104 "server-header",
105 temp_xml.to_str().unwrap(),
106 out_dir.join(&header_name).to_str().unwrap(),
107 ])
108 .status()
109 .expect("failed to execute wayland-scanner");
110 assert!(status.success(), "wayland-scanner server-header failed for {}", name);
111 }
112
113 // Generate custom river protocol headers and private-code C files
114 let custom_protocols = vec![
115 ("river-xkb-bindings-v1.xml", "protocol/river-xkb-bindings-v1.xml"),
116 ("river-layer-shell-v1.xml", "protocol/river-layer-shell-v1.xml"),
117 ("river-input-management-v1.xml", "protocol/river-input-management-v1.xml"),
118 ("river-libinput-config-v1.xml", "protocol/river-libinput-config-v1.xml"),
119 ("river-xkb-config-v1.xml", "protocol/river-xkb-config-v1.xml"),
120 ("cce-inspector-v1.xml", "protocol/cce-inspector-v1.xml"),
121 ("cce-window-management-v1.xml", "protocol/cce-window-management-v1.xml"),
122 ];
123
124 let mut generated_c_files = Vec::new();
125
126 for (name, path) in custom_protocols {
127 let temp_xml = out_dir.join(format!("{}-temp.xml", name));
128 clean_xml(path, &temp_xml);
129
130 let header_name = name.replace(".xml", "-protocol.h");
131 let status_h = std::process::Command::new("wayland-scanner")
132 .args(&[
133 "server-header",
134 temp_xml.to_str().unwrap(),
135 out_dir.join(&header_name).to_str().unwrap(),
136 ])
137 .status()
138 .expect("failed to execute wayland-scanner");
139 assert!(status_h.success(), "wayland-scanner server-header failed for {}", name);
140
141 let code_name = name.replace(".xml", "-protocol.c");
142 let code_path = out_dir.join(&code_name);
143 let status_c = std::process::Command::new("wayland-scanner")
144 .args(&[
145 "private-code",
146 temp_xml.to_str().unwrap(),
147 code_path.to_str().unwrap(),
148 ])
149 .status()
150 .expect("failed to execute wayland-scanner");
151 assert!(status_c.success(), "wayland-scanner private-code failed for {}", name);
152
153 generated_c_files.push(code_path);
154 }
155
156 // Compile the C wrapper and protocol C files
157 let mut build = cc::Build::new();
158 build.file("src/server/wlroots_log_wrapper.c")
159 .define("WLR_USE_UNSTABLE", None)
160 .flag("-std=c99")
161 .flag("-O2")
162 .include(&out_dir);
163
164 for c_file in generated_c_files {
165 build.file(c_file);
166 }
167
168 // Add include paths for scenefx, wlroots, and wayland-server
169 for path in &scenefx_include_paths {
170 build.include(path);
171 }
172 for path in &wlroots.include_paths {
173 build.include(path);
174 }
175 for path in &wl_server.include_paths {
176 build.include(path);
177 }
178 build.compile("wlroots_log_wrapper");
179
180
181 // Setup bindgen
182 let mut builder = bindgen::Builder::default()
183 .header("wrapper.h")
184 .clang_arg("-DWLR_USE_UNSTABLE")
185 .layout_tests(false)
186 .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()));
187
188 // Pass include paths to bindgen clang argument parser
189 let mut include_paths = vec![out_dir.clone()];
190 include_paths.extend(scenefx_include_paths.clone());
191 include_paths.extend(wlroots.include_paths.clone());
192 include_paths.extend(wl_server.include_paths.clone());
193 include_paths.extend(xkb.include_paths.clone());
194 include_paths.extend(pixman.include_paths.clone());
195 include_paths.extend(libinput.include_paths.clone());
196 include_paths.extend(libevdev.include_paths.clone());
197
198 for path in include_paths {
199 builder = builder.clang_arg(format!("-I{}", path.display()));
200 }
201
202 let bindings = builder
203 .blocklist_item("FP_NAN")
204 .blocklist_item("FP_INFINITE")
205 .blocklist_item("FP_ZERO")
206 .blocklist_item("FP_SUBNORMAL")
207 .blocklist_item("FP_NORMAL")
208 .blocklist_item("wl_listener")
209 .blocklist_item("wlr_addon")
210 .blocklist_item("wlr_input_device")
211 .blocklist_item("pixman_region32")
212 .blocklist_item("pixman_region32_t")
213 .blocklist_item("pixman_box32")
214 .blocklist_item("pixman_box32_t")
215 .blocklist_item("pixman_rectangle32")
216 .blocklist_item("pixman_rectangle32_t")
217 .blocklist_item("pixman_region32_data")
218 .blocklist_item("pixman_region32_data_t")
219 .generate()
220 .expect("Unable to generate bindings");
221
222 let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
223 bindings
224 .write_to_file(out_path.join("bindings.rs"))
225 .expect("Couldn't write bindings!");
226 }