Skip to content

Commit

Permalink
Overhaul vendoring/linking features
Browse files Browse the repository at this point in the history
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] libbpf#64 (comment)

Signed-off-by: Daniel Müller <deso@posteo.net>
  • Loading branch information
d-e-s-o committed Dec 6, 2023
1 parent 25a6a70 commit 9498ed6
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 43 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -87,7 +87,7 @@ jobs:
matrix:
include:
- args: ''
- args: --features=novendor
- args: --no-default-features
install-sys-libbpf: y
env:
CARGO_TERM_VERBOSE: 'true'
Expand Down
23 changes: 21 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
93 changes: 55 additions & 38 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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())
Expand All @@ -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
);
}
Expand All @@ -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");
Expand Down
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

include!("bindings.rs");

#[cfg(feature = "vendor-libbpf")]
macro_rules! header {
($file:literal) => {
($file, include_str!(concat!("../libbpf/src/", $file)))
Expand All @@ -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"),
Expand Down

0 comments on commit 9498ed6

Please sign in to comment.