From 9498ed6494d4864b4aac7a560d33c5244ee3569a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Wed, 6 Dec 2023 09:19:02 -0800 Subject: [PATCH] Overhaul vendoring/linking features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change overhauls the vendoring & static linking features that the library exposes, in an attempt to make everything more to the point. Please refer to the larger discussion [0] for additional context, but in short: we introduce separate features for vendoring/linking each of the three system library dependencies: libbpf, libelf, zlib. "static" and "vendored" meta-features are still available, which apply to all three libraries in unison. The remaining dependencies are expressed declaratively via dependent features. E.g., because zlib is only a dependency of libbpf (and not a direct one), linking it statically implies linking libbpf statically. In the future, this design would make it possible to enable additional configurations. For example, currently vendoring any library implies linking it statically, because we only build the static version. This is more of a simplification than a strict requirement and if needed, we could support dynamic linking when using a vendored copy. The default features mirror the previous default and no behavior change should occur. The existing novendor feature is kept but deprecated and should be removed in the future (a warning will be printed as part of the build). It was certainly one of the main causes of confusion where novendor and vendored could co-exist and it was hard to understand what would or wouldn't happen. In the new world, users are advised to simply build with all features disabled. I tested everything on a binary depending on libbpf-sys with various features enabled and spot checked the expected dynamic library dependencies. We could enshrine that as a CI step, but given that this logic is expected to change infrequently I didn't go down that road. [0] https://github.com/libbpf/libbpf-sys/pull/64#issuecomment-1731836233 Signed-off-by: Daniel Müller --- .github/workflows/ci.yml | 4 +- Cargo.toml | 23 +++++++++- build.rs | 93 ++++++++++++++++++++++++---------------- src/lib.rs | 3 +- 4 files changed, 80 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc78fd9..ea4e1b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - rust-target: x86_64-unknown-linux-gnu os-target: x86_64-linux-gnu os-arch: amd64 - args: --features=novendor + args: --no-default-features install-sys-libbpf: y - rust-target: aarch64-unknown-linux-gnu @@ -87,7 +87,7 @@ jobs: matrix: include: - args: '' - - args: --features=novendor + - args: --no-default-features install-sys-libbpf: y env: CARGO_TERM_VERBOSE: 'true' diff --git a/Cargo.toml b/Cargo.toml index fe37ccd..68a8818 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,25 @@ num_cpus = "^1.16.0" crate-type = ["lib", "staticlib"] [features] +default = ["vendored-libbpf"] +# Don't vendor anything. This feature is deprecated. Simply build without +# any features instead. novendor = [] -static = [] -vendored = ["static"] +# Meta-feature to use vendored versions of all dependencies. +vendored = ["vendored-libbpf", "vendored-libelf", "vendored-zlib"] +# Use vendored `libbpf`. Implies linking it statically. +vendored-libbpf = ["static-libbpf"] +# Use vendored `libelf`. Implies linking it statically. +vendored-libelf = ["static-libelf"] +# Use vendored `zlib`. Implies linking it statically. +vendored-zlib = ["static-zlib"] +# Meta-feature to link against all dependencies statically. +static = ["static-libbpf", "static-libelf", "static-zlib"] +# Link libbpf statically. +static-libbpf = [] +# Link libelf statically. Implies linking libbpf statically, because libbpf is +# the libelf consumer. +static-libelf = ["static-libbpf"] +# Link zlib statically. Implies linking libbpf statically, because libbpf is +# the libelf consumer. +static-zlib = ["static-libbpf"] diff --git a/build.rs b/build.rs index 4f4fd15..b3ec767 100644 --- a/build.rs +++ b/build.rs @@ -2,6 +2,7 @@ use std::env; use std::ffi; +use std::ffi::OsString; use std::fs; use std::os::fd::AsRawFd as _; use std::path; @@ -80,16 +81,6 @@ fn generate_bindings(src_dir: path::PathBuf) { #[cfg(not(feature = "bindgen"))] fn generate_bindings(_: path::PathBuf) {} -#[cfg(feature = "static")] -fn library_prefix() -> String { - "static=".to_string() -} - -#[cfg(not(feature = "static"))] -fn library_prefix() -> String { - "".to_string() -} - fn pkg_check(pkg: &str) { if process::Command::new(pkg) .stdout(process::Stdio::null()) @@ -98,7 +89,7 @@ fn pkg_check(pkg: &str) { .is_err() { panic!( - "{} is required to compile libbpf-sys using the vendored copy of libbpf", + "{} is required to compile libbpf-sys with the selected set of features", pkg ); } @@ -109,57 +100,83 @@ fn main() { 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:rustc-link-lib={}bpf\n", library_prefix()); + 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 - pkg_check("make"); - pkg_check("pkg-config"); - if cfg!(feature = "vendored") { + if vendored_libelf { pkg_check("autoreconf"); pkg_check("autopoint"); pkg_check("flex"); pkg_check("bison"); - pkg_check("gawk"); } - let compiler = match cc::Build::new().try_get_compiler() { - Ok(compiler) => compiler, - Err(_) => panic!( - "a C compiler is required to compile libbpf-sys using the vendored copy of libbpf" - ), + let (compiler, mut cflags) = if vendored_libbpf || vendored_libelf || vendored_zlib { + pkg_check("make"); + pkg_check("pkg-config"); + pkg_check("gawk"); + + 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, OsString::new()) }; - if cfg!(feature = "vendored") { - make_zlib(&compiler, &src_dir, &out_dir); - make_elfutils(&compiler, &src_dir, &out_dir); + if vendored_zlib { + make_zlib(compiler.as_ref().unwrap(), &src_dir, &out_dir); + cflags.push(&format!(" -I{}/zlib/", src_dir.display())); } - let cflags = if cfg!(feature = "vendored") { - // make sure that the headerfiles from libelf and zlib - // for libbpf come from the vendorized version - - let mut cflags = compiler.cflags_env(); + if vendored_libelf { + make_elfutils(compiler.as_ref().unwrap(), &src_dir, &out_dir); cflags.push(&format!(" -I{}/elfutils/libelf/", src_dir.display())); - cflags.push(&format!(" -I{}/zlib/", src_dir.display())); - cflags - } else { - compiler.cflags_env() - }; + } - make_libbpf(&compiler, &cflags, &src_dir, &out_dir); + 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", library_prefix()); - println!("cargo:rustc-link-lib={}z", library_prefix()); - println!("cargo:rustc-link-lib=static=bpf"); + 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"); diff --git a/src/lib.rs b/src/lib.rs index 33702d2..a067b87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ include!("bindings.rs"); +#[cfg(feature = "vendor-libbpf")] macro_rules! header { ($file:literal) => { ($file, include_str!(concat!("../libbpf/src/", $file))) @@ -15,7 +16,7 @@ macro_rules! header { /// Vendored libbpf headers /// /// Tuple format is: (header filename, header contents) -#[cfg(not(feature = "novendor"))] +#[cfg(feature = "vendor-libbpf")] pub const API_HEADERS: [(&str, &str); 10] = [ header!("bpf.h"), header!("libbpf.h"),