1 #[cfg(not(feature = "build_bindings"))]
main()2 fn main() {
3 println!("cargo:rerun-if-changed=build.rs"); // never rerun
4 }
5
6 #[cfg(feature = "build_bindings")]
main()7 fn main() {
8 println!("cargo:rerun-if-changed=build.rs"); // avoids double-build when we output into src
9 generate::generate().unwrap();
10 }
11
12 #[cfg(feature = "build_bindings")]
13 mod generate {
14 use std::error::Error;
15 use std::io::Write;
16 use std::process::{Command, Stdio};
17
18 use regex::Regex;
19 use std::env;
20 use std::fs::File;
21 use std::path::Path;
22
23 static CONST_PREFIX: &'static str = "DRM_FOURCC_";
24
generate() -> Result<(), Box<dyn Error + Sync + Send>>25 pub fn generate() -> Result<(), Box<dyn Error + Sync + Send>> {
26 let out_dir = env::var("OUT_DIR").unwrap();
27 let wrapper_path = Path::new(&out_dir).join("wrapper.h");
28
29 // First get all the macros in drm_fourcc.h
30
31 let mut cmd = Command::new("clang")
32 .arg("-E") // run pre-processor only
33 .arg("-dM") // output all macros defined
34 .arg("-") // take input from stdin
35 .stdin(Stdio::piped())
36 .stdout(Stdio::piped())
37 .spawn()?;
38
39 {
40 let stdin = cmd.stdin.as_mut().expect("failed to open stdin");
41 stdin.write_all(b"#include <drm/drm_fourcc.h>\n")?;
42 }
43
44 let result = cmd.wait_with_output()?;
45 let stdout = String::from_utf8(result.stdout)?;
46 if !result.status.success() {
47 panic!("Clang failed with output: {}", stdout)
48 }
49
50 // Then get the names of the format macros
51
52 let fmt_re = Regex::new(r"^\s*#define (?P<full>DRM_FORMAT_(?P<short>[A-Z0-9_]+)) ")?;
53 let format_names: Vec<(&str, &str)> = stdout
54 .lines()
55 .filter_map(|line| {
56 if line.contains("DRM_FORMAT_RESERVED")
57 || line.contains("INVALID")
58 || line.contains("_MOD_")
59 {
60 return None;
61 }
62
63 fmt_re.captures(line).map(|caps| {
64 let full = caps.name("full").unwrap().as_str();
65 let short = caps.name("short").unwrap().as_str();
66
67 (full, short)
68 })
69 })
70 .collect();
71
72 let vendor_re =
73 Regex::new(r"^\s*#define (?P<full>DRM_FORMAT_MOD_VENDOR_(?P<short>[A-Z0-9_]+)) ")?;
74 let vendor_names: Vec<(&str, &str)> = stdout
75 .lines()
76 .filter_map(|line| {
77 if line.contains("DRM_FORMAT_MOD_VENDOR_NONE") {
78 return None;
79 }
80
81 vendor_re.captures(line).map(|caps| {
82 let full = caps.name("full").unwrap().as_str();
83 let short = caps.name("short").unwrap().as_str();
84
85 (full, short)
86 })
87 })
88 .collect();
89
90 let mod_re =
91 Regex::new(r"^\s*#define (?P<full>(DRM|I915)_FORMAT_MOD_(?P<short>[A-Z0-9_]+)) ")?;
92 let modifier_names: Vec<(&str, String)> = stdout
93 .lines()
94 .filter_map(|line| {
95 if line.contains("DRM_FORMAT_MOD_NONE")
96 || line.contains("DRM_FORMAT_MOD_RESERVED")
97 || line.contains("VENDOR")
98 // grrr..
99 || line.contains("ARM_TYPE")
100 {
101 return None;
102 }
103
104 mod_re.captures(line).map(|caps| {
105 let full = caps.name("full").unwrap().as_str();
106 let short = caps.name("short").unwrap().as_str();
107
108 (
109 full,
110 if full.contains("I915") {
111 format!("I915_{}", short)
112 } else {
113 String::from(short)
114 },
115 )
116 })
117 })
118 .collect();
119
120 // Then create a file with a variable defined for every format macro
121
122 let mut wrapper = File::create(&wrapper_path)?;
123
124 wrapper.write_all(b"#include <stdint.h>\n")?;
125 wrapper.write_all(b"#include <drm/drm_fourcc.h>\n")?;
126
127 for (full, short) in &format_names {
128 writeln!(wrapper, "uint32_t {}{} = {};\n", CONST_PREFIX, short, full)?;
129 }
130 for (full, short) in &vendor_names {
131 writeln!(wrapper, "uint8_t {}{} = {};\n", CONST_PREFIX, short, full)?;
132 }
133 for (full, short) in &modifier_names {
134 writeln!(wrapper, "uint64_t {}{} = {};\n", CONST_PREFIX, short, full)?;
135 }
136
137 wrapper.flush()?;
138
139 // Then generate bindings from that file
140 bindgen::builder()
141 .ctypes_prefix("crate::_fake_ctypes")
142 .header(wrapper_path.as_os_str().to_str().unwrap())
143 .whitelist_var("DRM_FOURCC_.*")
144 .generate()
145 .unwrap()
146 .write_to_file("src/consts.rs")?;
147
148 // Then generate our enums
149 fn write_enum(
150 as_enum: &mut File,
151 name: &str,
152 repr: &str,
153 names: Vec<(&str, &str)>,
154 ) -> Result<(), std::io::Error> {
155 as_enum.write_all(b"#[derive(Copy, Clone, Eq, PartialEq, Hash)]")?;
156 as_enum.write_all(
157 b"#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]",
158 )?;
159 writeln!(as_enum, "#[repr({})]", repr)?;
160 writeln!(as_enum, "pub enum {} {{", name)?;
161
162 let members: Vec<(String, String)> = names
163 .iter()
164 .map(|(_, short)| {
165 (
166 enum_member_case(short),
167 format!("consts::{}{}", CONST_PREFIX, short),
168 )
169 })
170 .collect();
171
172 for (member, value) in &members {
173 writeln!(as_enum, "{} = {},", member, value)?;
174 }
175
176 as_enum.write_all(b"}\n")?;
177
178 writeln!(as_enum, "impl {} {{", name)?;
179 writeln!(
180 as_enum,
181 "pub(crate) fn from_{}(n: {}) -> Option<Self> {{\n",
182 repr, repr
183 )?;
184 as_enum.write_all(b"match n {\n")?;
185
186 for (member, value) in &members {
187 writeln!(as_enum, "{} => Some(Self::{}),", value, member)?;
188 }
189
190 writeln!(as_enum, "_ => None")?;
191 as_enum.write_all(b"}}}\n")?;
192
193 Ok(())
194 }
195
196 let as_enum_path = "src/as_enum.rs";
197 {
198 let mut as_enum = File::create(as_enum_path)?;
199
200 as_enum.write_all(b"// Automatically generated by build.rs\n")?;
201 as_enum.write_all(b"use crate::consts;")?;
202
203 write_enum(&mut as_enum, "DrmFourcc", "u32", format_names)?;
204
205 as_enum.write_all(b"#[derive(Debug)]")?;
206 write_enum(&mut as_enum, "DrmVendor", "u8", vendor_names)?;
207
208 // modifiers can overlap
209
210 as_enum.write_all(b"#[derive(Debug, Copy, Clone)]")?;
211 as_enum.write_all(
212 b"#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]",
213 )?;
214 as_enum.write_all(b"pub enum DrmModifier {\n")?;
215
216 let modifier_members: Vec<(String, String)> = modifier_names
217 .iter()
218 .map(|(_, short)| {
219 (
220 enum_member_case(short),
221 format!("consts::{}{}", CONST_PREFIX, short),
222 )
223 })
224 .collect();
225 for (member, _) in &modifier_members {
226 writeln!(as_enum, "{},", member)?;
227 }
228 as_enum.write_all(b"Unrecognized(u64)")?;
229
230 as_enum.write_all(b"}\n")?;
231
232 as_enum.write_all(b"impl DrmModifier {\n")?;
233 as_enum.write_all(b"pub(crate) fn from_u64(n: u64) -> Self {\n")?;
234 as_enum.write_all(b"#[allow(unreachable_patterns)]\n")?;
235 as_enum.write_all(b"match n {\n")?;
236
237 for (member, value) in &modifier_members {
238 writeln!(as_enum, "{} => Self::{},", value, member)?;
239 }
240 as_enum.write_all(b"x => Self::Unrecognized(x)\n")?;
241
242 as_enum.write_all(b"}}\n")?;
243 as_enum.write_all(b"pub(crate) fn into_u64(&self) -> u64 {\n")?;
244 as_enum.write_all(b"match self {\n")?;
245
246 for (member, value) in &modifier_members {
247 writeln!(as_enum, "Self::{} => {},", member, value)?;
248 }
249 as_enum.write_all(b"Self::Unrecognized(x) => *x,\n")?;
250
251 as_enum.write_all(b"}}}\n")?;
252 }
253
254 Command::new("rustfmt").arg(as_enum_path).spawn()?.wait()?;
255
256 Ok(())
257 }
258
enum_member_case(s: &str) -> String259 fn enum_member_case(s: &str) -> String {
260 let (first, rest) = s.split_at(1);
261 format!("{}{}", first, rest.to_ascii_lowercase())
262 }
263 }
264