| // build.rs |
| |
| use std::env; |
| use std::ffi; |
| use std::fs; |
| use std::fs::read_dir; |
| use std::path; |
| use std::path::Path; |
| use std::process; |
| |
| use nix::fcntl; |
| |
| |
| fn emit_rerun_directives_for_contents(dir: &Path) { |
| for result in read_dir(dir).unwrap() { |
| let file = result.unwrap(); |
| println!("cargo:rerun-if-changed={}", file.path().display()); |
| } |
| } |
| |
| #[cfg(feature = "bindgen")] |
| fn generate_bindings(src_dir: path::PathBuf) { |
| use std::collections::HashSet; |
| |
| #[derive(Debug)] |
| struct IgnoreMacros(HashSet<&'static str>); |
| |
| impl bindgen::callbacks::ParseCallbacks for IgnoreMacros { |
| fn will_parse_macro(&self, name: &str) -> bindgen::callbacks::MacroParsingBehavior { |
| if self.0.contains(name) { |
| bindgen::callbacks::MacroParsingBehavior::Ignore |
| } else { |
| bindgen::callbacks::MacroParsingBehavior::Default |
| } |
| } |
| } |
| |
| let ignored_macros = IgnoreMacros( |
| vec![ |
| "BTF_KIND_FUNC", |
| "BTF_KIND_FUNC_PROTO", |
| "BTF_KIND_VAR", |
| "BTF_KIND_DATASEC", |
| "BTF_KIND_FLOAT", |
| "BTF_KIND_DECL_TAG", |
| "BTF_KIND_TYPE_TAG", |
| "BTF_KIND_ENUM64", |
| ] |
| .into_iter() |
| .collect(), |
| ); |
| |
| #[cfg(feature = "bindgen-source")] |
| let out_dir = &src_dir.join("src"); |
| #[cfg(not(feature = "bindgen-source"))] |
| let out_dir = |
| &path::PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR should always be set")); |
| |
| bindgen::Builder::default() |
| .derive_default(true) |
| .explicit_padding(true) |
| .default_enum_style(bindgen::EnumVariation::Consts) |
| .size_t_is_usize(false) |
| .prepend_enum_name(false) |
| .layout_tests(false) |
| .generate_comments(false) |
| .emit_builtins() |
| .allowlist_function("bpf_.+") |
| .allowlist_function("btf_.+") |
| .allowlist_function("libbpf_.+") |
| .allowlist_function("perf_.+") |
| .allowlist_function("ring_buffer_.+") |
| .allowlist_function("user_ring_buffer_.+") |
| .allowlist_function("vdprintf") |
| .allowlist_type("bpf_.+") |
| .allowlist_type("btf_.+") |
| .allowlist_type("xdp_.+") |
| .allowlist_type("perf_.+") |
| .allowlist_var("BPF_.+") |
| .allowlist_var("BTF_.+") |
| .allowlist_var("XDP_.+") |
| .allowlist_var("PERF_.+") |
| .parse_callbacks(Box::new(ignored_macros)) |
| .header("bindings.h") |
| .clang_arg(format!("-I{}", src_dir.join("libbpf/include").display())) |
| .clang_arg(format!( |
| "-I{}", |
| src_dir.join("libbpf/include/uapi").display() |
| )) |
| .generate() |
| .expect("Unable to generate bindings") |
| .write_to_file(out_dir.join("bindings.rs")) |
| .expect("Couldn't write bindings"); |
| } |
| |
| #[cfg(not(feature = "bindgen"))] |
| fn generate_bindings(_: path::PathBuf) {} |
| |
| fn pkg_check(pkg: &str) { |
| if process::Command::new(pkg) |
| .stdout(process::Stdio::null()) |
| .stderr(process::Stdio::null()) |
| .status() |
| .is_err() |
| { |
| panic!( |
| "{} is required to compile libbpf-sys with the selected set of features", |
| pkg |
| ); |
| } |
| } |
| |
| fn main() { |
| let src_dir = path::PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); |
| |
| generate_bindings(src_dir.clone()); |
| |
| let vendored_libbpf = cfg!(feature = "vendored-libbpf"); |
| let vendored_libelf = cfg!(feature = "vendored-libelf"); |
| let vendored_zlib = cfg!(feature = "vendored-zlib"); |
| println!("Using feature vendored-libbpf={}", vendored_libbpf); |
| println!("Using feature vendored-libelf={}", vendored_libelf); |
| println!("Using feature vendored-zlib={}", vendored_zlib); |
| |
| let static_libbpf = cfg!(feature = "static-libbpf"); |
| let static_libelf = cfg!(feature = "static-libelf"); |
| let static_zlib = cfg!(feature = "static-zlib"); |
| println!("Using feature static-libbpf={}", static_libbpf); |
| println!("Using feature static-libelf={}", static_libelf); |
| println!("Using feature static-zlib={}", static_zlib); |
| |
| if cfg!(feature = "novendor") { |
| println!("cargo:warning=the `novendor` feature of `libbpf-sys` is deprecated; build without features instead"); |
| println!( |
| "cargo:rustc-link-lib={}bpf", |
| if static_libbpf { "static=" } else { "" } |
| ); |
| return; |
| } |
| |
| let out_dir = path::PathBuf::from(env::var_os("OUT_DIR").unwrap()); |
| |
| // check for all necessary compilation tools |
| if vendored_libelf { |
| pkg_check("autoreconf"); |
| pkg_check("autopoint"); |
| pkg_check("flex"); |
| pkg_check("bison"); |
| pkg_check("gawk"); |
| } |
| |
| let (compiler, mut cflags) = if vendored_libbpf || vendored_libelf || vendored_zlib { |
| pkg_check("make"); |
| pkg_check("pkg-config"); |
| |
| let compiler = cc::Build::new().try_get_compiler().expect( |
| "a C compiler is required to compile libbpf-sys using the vendored copy of libbpf", |
| ); |
| let cflags = compiler.cflags_env(); |
| (Some(compiler), cflags) |
| } else { |
| (None, ffi::OsString::new()) |
| }; |
| |
| if vendored_zlib { |
| make_zlib(compiler.as_ref().unwrap(), &src_dir, &out_dir); |
| cflags.push(&format!(" -I{}/zlib/", src_dir.display())); |
| } |
| |
| if vendored_libelf { |
| make_elfutils(compiler.as_ref().unwrap(), &src_dir, &out_dir); |
| cflags.push(&format!(" -I{}/elfutils/libelf/", src_dir.display())); |
| } |
| |
| if vendored_libbpf { |
| make_libbpf(compiler.as_ref().unwrap(), &cflags, &src_dir, &out_dir); |
| } |
| |
| println!( |
| "cargo:rustc-link-search=native={}", |
| out_dir.to_string_lossy() |
| ); |
| println!( |
| "cargo:rustc-link-lib={}elf", |
| if static_libelf { "static=" } else { "" } |
| ); |
| println!( |
| "cargo:rustc-link-lib={}z", |
| if static_zlib { "static=" } else { "" } |
| ); |
| println!( |
| "cargo:rustc-link-lib={}bpf", |
| if static_libbpf { "static=" } else { "" } |
| ); |
| println!("cargo:include={}/include", out_dir.to_string_lossy()); |
| |
| println!("cargo:rerun-if-env-changed=LD_LIBRARY_PATH"); |
| if let Ok(ld_path) = env::var("LD_LIBRARY_PATH") { |
| for path in ld_path.split(':') { |
| if !path.is_empty() { |
| println!("cargo:rustc-link-search=native={}", path); |
| } |
| } |
| } |
| } |
| |
| fn make_zlib(compiler: &cc::Tool, src_dir: &path::Path, out_dir: &path::Path) { |
| let src_dir = src_dir.join("zlib"); |
| // lock README such that if two crates are trying to compile |
| // this at the same time (eg libbpf-rs libbpf-cargo) |
| // they wont trample each other |
| let file = std::fs::File::open(src_dir.join("README")).unwrap(); |
| let _lock = fcntl::Flock::lock(file, fcntl::FlockArg::LockExclusive).unwrap(); |
| |
| let status = process::Command::new("./configure") |
| .arg("--static") |
| .arg("--prefix") |
| .arg(".") |
| .arg("--libdir") |
| .arg(out_dir) |
| .env("CC", compiler.path()) |
| .env("CFLAGS", compiler.cflags_env()) |
| .current_dir(&src_dir) |
| .status() |
| .expect("could not execute make"); |
| |
| assert!(status.success(), "make failed"); |
| |
| let status = process::Command::new("make") |
| .arg("install") |
| .arg("-j") |
| .arg(&format!("{}", num_cpus())) |
| .current_dir(&src_dir) |
| .status() |
| .expect("could not execute make"); |
| |
| assert!(status.success(), "make failed"); |
| |
| let status = process::Command::new("make") |
| .arg("distclean") |
| .current_dir(&src_dir) |
| .status() |
| .expect("could not execute make"); |
| |
| assert!(status.success(), "make failed"); |
| emit_rerun_directives_for_contents(&src_dir); |
| } |
| |
| fn make_elfutils(compiler: &cc::Tool, src_dir: &path::Path, out_dir: &path::Path) { |
| // lock README such that if two crates are trying to compile |
| // this at the same time (eg libbpf-rs libbpf-cargo) |
| // they wont trample each other |
| let file = std::fs::File::open(src_dir.join("elfutils/README")).unwrap(); |
| let _lock = fcntl::Flock::lock(file, fcntl::FlockArg::LockExclusive).unwrap(); |
| |
| let flags = compiler |
| .cflags_env() |
| .into_string() |
| .expect("failed to get cflags"); |
| let mut cflags: String = flags |
| .split_whitespace() |
| .filter_map(|arg| { |
| if arg != "-static" { |
| // compilation fails with -static flag |
| Some(format!(" {arg}")) |
| } else { |
| None |
| } |
| }) |
| .collect(); |
| |
| #[cfg(target_arch = "aarch64")] |
| cflags.push_str(" -Wno-error=stringop-overflow"); |
| cflags.push_str(&format!(" -I{}/zlib/", src_dir.display())); |
| |
| let status = process::Command::new("autoreconf") |
| .arg("--install") |
| .arg("--force") |
| .current_dir(&src_dir.join("elfutils")) |
| .status() |
| .expect("could not execute make"); |
| |
| assert!(status.success(), "make failed"); |
| |
| // location of libz.a |
| let out_lib = format!("-L{}", out_dir.display()); |
| let status = process::Command::new("./configure") |
| .arg("--enable-maintainer-mode") |
| .arg("--disable-debuginfod") |
| .arg("--disable-libdebuginfod") |
| .arg("--without-zstd") |
| .arg("--prefix") |
| .arg(&src_dir.join("elfutils/prefix_dir")) |
| .arg("--libdir") |
| .arg(out_dir) |
| .env("CC", compiler.path()) |
| .env("CXX", compiler.path()) |
| .env("CFLAGS", &cflags) |
| .env("CXXFLAGS", &cflags) |
| .env("LDFLAGS", &out_lib) |
| .current_dir(&src_dir.join("elfutils")) |
| .status() |
| .expect("could not execute make"); |
| |
| assert!(status.success(), "make failed"); |
| |
| let status = process::Command::new("make") |
| .arg("install") |
| .arg("-j") |
| .arg(&format!("{}", num_cpus())) |
| .arg("BUILD_STATIC_ONLY=y") |
| .current_dir(&src_dir.join("elfutils")) |
| .status() |
| .expect("could not execute make"); |
| |
| assert!(status.success(), "make failed"); |
| |
| let status = process::Command::new("make") |
| .arg("distclean") |
| .current_dir(&src_dir.join("elfutils")) |
| .status() |
| .expect("could not execute make"); |
| |
| assert!(status.success(), "make failed"); |
| emit_rerun_directives_for_contents(&src_dir.join("elfutils").join("src")); |
| } |
| |
| fn make_libbpf( |
| compiler: &cc::Tool, |
| cflags: &ffi::OsStr, |
| src_dir: &path::Path, |
| out_dir: &path::Path, |
| ) { |
| let src_dir = src_dir.join("libbpf/src"); |
| // create obj_dir if it doesn't exist |
| let obj_dir = path::PathBuf::from(&out_dir.join("obj").into_os_string()); |
| let _ = fs::create_dir(&obj_dir); |
| |
| let status = process::Command::new("make") |
| .arg("install") |
| .arg("-j") |
| .arg(&format!("{}", num_cpus())) |
| .env("BUILD_STATIC_ONLY", "y") |
| .env("PREFIX", "/") |
| .env("LIBDIR", "") |
| .env("OBJDIR", &obj_dir) |
| .env("DESTDIR", out_dir) |
| .env("CC", compiler.path()) |
| .env("CFLAGS", cflags) |
| .current_dir(&src_dir) |
| .status() |
| .expect("could not execute make"); |
| |
| assert!(status.success(), "make failed"); |
| |
| let status = process::Command::new("make") |
| .arg("clean") |
| .current_dir(&src_dir) |
| .status() |
| .expect("could not execute make"); |
| |
| assert!(status.success(), "make failed"); |
| emit_rerun_directives_for_contents(&src_dir); |
| } |
| |
| fn num_cpus() -> usize { |
| std::thread::available_parallelism().map_or(1, |count| count.get()) |
| } |