1 // build.rs
2
3 use std::env;
4 use std::ffi;
5 use std::fs;
6 use std::fs::read_dir;
7 use std::path;
8 use std::path::Path;
9 use std::process;
10
11 use nix::fcntl;
12
13
emit_rerun_directives_for_contents(dir: &Path)14 fn emit_rerun_directives_for_contents(dir: &Path) {
15 for result in read_dir(dir).unwrap() {
16 let file = result.unwrap();
17 println!("cargo:rerun-if-changed={}", file.path().display());
18 }
19 }
20
21 #[cfg(feature = "bindgen")]
generate_bindings(src_dir: path::PathBuf)22 fn generate_bindings(src_dir: path::PathBuf) {
23 use std::collections::HashSet;
24
25 #[derive(Debug)]
26 struct IgnoreMacros(HashSet<&'static str>);
27
28 impl bindgen::callbacks::ParseCallbacks for IgnoreMacros {
29 fn will_parse_macro(&self, name: &str) -> bindgen::callbacks::MacroParsingBehavior {
30 if self.0.contains(name) {
31 bindgen::callbacks::MacroParsingBehavior::Ignore
32 } else {
33 bindgen::callbacks::MacroParsingBehavior::Default
34 }
35 }
36 }
37
38 let ignored_macros = IgnoreMacros(
39 vec![
40 "BTF_KIND_FUNC",
41 "BTF_KIND_FUNC_PROTO",
42 "BTF_KIND_VAR",
43 "BTF_KIND_DATASEC",
44 "BTF_KIND_FLOAT",
45 "BTF_KIND_DECL_TAG",
46 "BTF_KIND_TYPE_TAG",
47 "BTF_KIND_ENUM64",
48 ]
49 .into_iter()
50 .collect(),
51 );
52
53 #[cfg(feature = "bindgen-source")]
54 let out_dir = &src_dir.join("src");
55 #[cfg(not(feature = "bindgen-source"))]
56 let out_dir =
57 &path::PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR should always be set"));
58
59 bindgen::Builder::default()
60 .derive_default(true)
61 .explicit_padding(true)
62 .default_enum_style(bindgen::EnumVariation::Consts)
63 .size_t_is_usize(false)
64 .prepend_enum_name(false)
65 .layout_tests(false)
66 .generate_comments(false)
67 .emit_builtins()
68 .allowlist_function("bpf_.+")
69 .allowlist_function("btf_.+")
70 .allowlist_function("libbpf_.+")
71 .allowlist_function("perf_.+")
72 .allowlist_function("ring_buffer_.+")
73 .allowlist_function("user_ring_buffer_.+")
74 .allowlist_function("vdprintf")
75 .allowlist_type("bpf_.+")
76 .allowlist_type("btf_.+")
77 .allowlist_type("xdp_.+")
78 .allowlist_type("perf_.+")
79 .allowlist_var("BPF_.+")
80 .allowlist_var("BTF_.+")
81 .allowlist_var("XDP_.+")
82 .allowlist_var("PERF_.+")
83 .parse_callbacks(Box::new(ignored_macros))
84 .header("bindings.h")
85 .clang_arg(format!("-I{}", src_dir.join("libbpf/include").display()))
86 .clang_arg(format!(
87 "-I{}",
88 src_dir.join("libbpf/include/uapi").display()
89 ))
90 .generate()
91 .expect("Unable to generate bindings")
92 .write_to_file(out_dir.join("bindings.rs"))
93 .expect("Couldn't write bindings");
94 }
95
96 #[cfg(not(feature = "bindgen"))]
generate_bindings(_: path::PathBuf)97 fn generate_bindings(_: path::PathBuf) {}
98
pkg_check(pkg: &str)99 fn pkg_check(pkg: &str) {
100 if process::Command::new(pkg)
101 .stdout(process::Stdio::null())
102 .stderr(process::Stdio::null())
103 .status()
104 .is_err()
105 {
106 panic!(
107 "{} is required to compile libbpf-sys with the selected set of features",
108 pkg
109 );
110 }
111 }
112
main()113 fn main() {
114 let src_dir = path::PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
115
116 generate_bindings(src_dir.clone());
117
118 let vendored_libbpf = cfg!(feature = "vendored-libbpf");
119 let vendored_libelf = cfg!(feature = "vendored-libelf");
120 let vendored_zlib = cfg!(feature = "vendored-zlib");
121 println!("Using feature vendored-libbpf={}", vendored_libbpf);
122 println!("Using feature vendored-libelf={}", vendored_libelf);
123 println!("Using feature vendored-zlib={}", vendored_zlib);
124
125 let static_libbpf = cfg!(feature = "static-libbpf");
126 let static_libelf = cfg!(feature = "static-libelf");
127 let static_zlib = cfg!(feature = "static-zlib");
128 println!("Using feature static-libbpf={}", static_libbpf);
129 println!("Using feature static-libelf={}", static_libelf);
130 println!("Using feature static-zlib={}", static_zlib);
131
132 if cfg!(feature = "novendor") {
133 println!("cargo:warning=the `novendor` feature of `libbpf-sys` is deprecated; build without features instead");
134 println!(
135 "cargo:rustc-link-lib={}bpf",
136 if static_libbpf { "static=" } else { "" }
137 );
138 return;
139 }
140
141 let out_dir = path::PathBuf::from(env::var_os("OUT_DIR").unwrap());
142
143 // check for all necessary compilation tools
144 if vendored_libelf {
145 pkg_check("autoreconf");
146 pkg_check("autopoint");
147 pkg_check("flex");
148 pkg_check("bison");
149 pkg_check("gawk");
150 }
151
152 let (compiler, mut cflags) = if vendored_libbpf || vendored_libelf || vendored_zlib {
153 pkg_check("make");
154 pkg_check("pkg-config");
155
156 let compiler = cc::Build::new().try_get_compiler().expect(
157 "a C compiler is required to compile libbpf-sys using the vendored copy of libbpf",
158 );
159 let cflags = compiler.cflags_env();
160 (Some(compiler), cflags)
161 } else {
162 (None, ffi::OsString::new())
163 };
164
165 if vendored_zlib {
166 make_zlib(compiler.as_ref().unwrap(), &src_dir, &out_dir);
167 cflags.push(&format!(" -I{}/zlib/", src_dir.display()));
168 }
169
170 if vendored_libelf {
171 make_elfutils(compiler.as_ref().unwrap(), &src_dir, &out_dir);
172 cflags.push(&format!(" -I{}/elfutils/libelf/", src_dir.display()));
173 }
174
175 if vendored_libbpf {
176 make_libbpf(compiler.as_ref().unwrap(), &cflags, &src_dir, &out_dir);
177 }
178
179 println!(
180 "cargo:rustc-link-search=native={}",
181 out_dir.to_string_lossy()
182 );
183 println!(
184 "cargo:rustc-link-lib={}elf",
185 if static_libelf { "static=" } else { "" }
186 );
187 println!(
188 "cargo:rustc-link-lib={}z",
189 if static_zlib { "static=" } else { "" }
190 );
191 println!(
192 "cargo:rustc-link-lib={}bpf",
193 if static_libbpf { "static=" } else { "" }
194 );
195 println!("cargo:include={}/include", out_dir.to_string_lossy());
196
197 println!("cargo:rerun-if-env-changed=LD_LIBRARY_PATH");
198 if let Ok(ld_path) = env::var("LD_LIBRARY_PATH") {
199 for path in ld_path.split(':') {
200 if !path.is_empty() {
201 println!("cargo:rustc-link-search=native={}", path);
202 }
203 }
204 }
205 }
206
make_zlib(compiler: &cc::Tool, src_dir: &path::Path, out_dir: &path::Path)207 fn make_zlib(compiler: &cc::Tool, src_dir: &path::Path, out_dir: &path::Path) {
208 let src_dir = src_dir.join("zlib");
209 // lock README such that if two crates are trying to compile
210 // this at the same time (eg libbpf-rs libbpf-cargo)
211 // they wont trample each other
212 let file = std::fs::File::open(src_dir.join("README")).unwrap();
213 let _lock = fcntl::Flock::lock(file, fcntl::FlockArg::LockExclusive).unwrap();
214
215 let status = process::Command::new("./configure")
216 .arg("--static")
217 .arg("--prefix")
218 .arg(".")
219 .arg("--libdir")
220 .arg(out_dir)
221 .env("CC", compiler.path())
222 .env("CFLAGS", compiler.cflags_env())
223 .current_dir(&src_dir)
224 .status()
225 .expect("could not execute make");
226
227 assert!(status.success(), "make failed");
228
229 let status = process::Command::new("make")
230 .arg("install")
231 .arg("-j")
232 .arg(&format!("{}", num_cpus()))
233 .current_dir(&src_dir)
234 .status()
235 .expect("could not execute make");
236
237 assert!(status.success(), "make failed");
238
239 let status = process::Command::new("make")
240 .arg("distclean")
241 .current_dir(&src_dir)
242 .status()
243 .expect("could not execute make");
244
245 assert!(status.success(), "make failed");
246 emit_rerun_directives_for_contents(&src_dir);
247 }
248
make_elfutils(compiler: &cc::Tool, src_dir: &path::Path, out_dir: &path::Path)249 fn make_elfutils(compiler: &cc::Tool, src_dir: &path::Path, out_dir: &path::Path) {
250 // lock README such that if two crates are trying to compile
251 // this at the same time (eg libbpf-rs libbpf-cargo)
252 // they wont trample each other
253 let file = std::fs::File::open(src_dir.join("elfutils/README")).unwrap();
254 let _lock = fcntl::Flock::lock(file, fcntl::FlockArg::LockExclusive).unwrap();
255
256 let flags = compiler
257 .cflags_env()
258 .into_string()
259 .expect("failed to get cflags");
260 let mut cflags: String = flags
261 .split_whitespace()
262 .filter_map(|arg| {
263 if arg != "-static" {
264 // compilation fails with -static flag
265 Some(format!(" {arg}"))
266 } else {
267 None
268 }
269 })
270 .collect();
271
272 #[cfg(target_arch = "aarch64")]
273 cflags.push_str(" -Wno-error=stringop-overflow");
274 cflags.push_str(&format!(" -I{}/zlib/", src_dir.display()));
275
276 let status = process::Command::new("autoreconf")
277 .arg("--install")
278 .arg("--force")
279 .current_dir(&src_dir.join("elfutils"))
280 .status()
281 .expect("could not execute make");
282
283 assert!(status.success(), "make failed");
284
285 // location of libz.a
286 let out_lib = format!("-L{}", out_dir.display());
287 let status = process::Command::new("./configure")
288 .arg("--enable-maintainer-mode")
289 .arg("--disable-debuginfod")
290 .arg("--disable-libdebuginfod")
291 .arg("--without-zstd")
292 .arg("--prefix")
293 .arg(&src_dir.join("elfutils/prefix_dir"))
294 .arg("--libdir")
295 .arg(out_dir)
296 .env("CC", compiler.path())
297 .env("CXX", compiler.path())
298 .env("CFLAGS", &cflags)
299 .env("CXXFLAGS", &cflags)
300 .env("LDFLAGS", &out_lib)
301 .current_dir(&src_dir.join("elfutils"))
302 .status()
303 .expect("could not execute make");
304
305 assert!(status.success(), "make failed");
306
307 let status = process::Command::new("make")
308 .arg("install")
309 .arg("-j")
310 .arg(&format!("{}", num_cpus()))
311 .arg("BUILD_STATIC_ONLY=y")
312 .current_dir(&src_dir.join("elfutils"))
313 .status()
314 .expect("could not execute make");
315
316 assert!(status.success(), "make failed");
317
318 let status = process::Command::new("make")
319 .arg("distclean")
320 .current_dir(&src_dir.join("elfutils"))
321 .status()
322 .expect("could not execute make");
323
324 assert!(status.success(), "make failed");
325 emit_rerun_directives_for_contents(&src_dir.join("elfutils").join("src"));
326 }
327
make_libbpf( compiler: &cc::Tool, cflags: &ffi::OsStr, src_dir: &path::Path, out_dir: &path::Path, )328 fn make_libbpf(
329 compiler: &cc::Tool,
330 cflags: &ffi::OsStr,
331 src_dir: &path::Path,
332 out_dir: &path::Path,
333 ) {
334 let src_dir = src_dir.join("libbpf/src");
335 // create obj_dir if it doesn't exist
336 let obj_dir = path::PathBuf::from(&out_dir.join("obj").into_os_string());
337 let _ = fs::create_dir(&obj_dir);
338
339 let status = process::Command::new("make")
340 .arg("install")
341 .arg("-j")
342 .arg(&format!("{}", num_cpus()))
343 .env("BUILD_STATIC_ONLY", "y")
344 .env("PREFIX", "/")
345 .env("LIBDIR", "")
346 .env("OBJDIR", &obj_dir)
347 .env("DESTDIR", out_dir)
348 .env("CC", compiler.path())
349 .env("CFLAGS", cflags)
350 .current_dir(&src_dir)
351 .status()
352 .expect("could not execute make");
353
354 assert!(status.success(), "make failed");
355
356 let status = process::Command::new("make")
357 .arg("clean")
358 .current_dir(&src_dir)
359 .status()
360 .expect("could not execute make");
361
362 assert!(status.success(), "make failed");
363 emit_rerun_directives_for_contents(&src_dir);
364 }
365
num_cpus() -> usize366 fn num_cpus() -> usize {
367 std::thread::available_parallelism().map_or(1, |count| count.get())
368 }
369