Skip to content

Commit

Permalink
VENDORIZE: added zlib and elfutils as vendored copies
Browse files Browse the repository at this point in the history
When the static_dependencies feature is applied,
libbpf-sys will now compile static versions of elfutils and zlib,
and will link against those newly compiled static libraries.

Justification: Many mainstream distributions are not shipping static versions
of libelf or zlib (eg Fedora).  Including them in libbpf-sys
makes developing rust bpf applications much easier than forcing
developers to do all this work themselves.

signed-off-by: Michael Mullin <[email protected]>
  • Loading branch information
mmullin-halcyon authored and alexforster committed Jul 4, 2023
1 parent b18f953 commit 2640a5f
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 7 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@ maintenance = { status = "passively-maintained" }
bindgen = { version = "^0.63", optional = true }
cc = "^1.0.3"
pkg-config = "^0.3.3"
nix = "^0.26.2"
num_cpus = "^1.15.0"

[lib]
crate-type = ["lib", "staticlib"]

[features]
novendor = []
static = []
vendored = ["static"]
168 changes: 161 additions & 7 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ fn library_prefix() -> String {
"".to_string()
}

fn pkg_check(pkg: &str) {
if let Err(_) = process::Command::new(pkg).status() {
panic!("{} is required to compile libbpf-sys using the vendored copy of libbpf", pkg);
}
}

fn main() {
let src_dir = path::PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());

Expand All @@ -98,12 +104,15 @@ fn main() {

let out_dir = path::PathBuf::from(env::var_os("OUT_DIR").unwrap());

if let Err(_) = process::Command::new("make").status() {
panic!("make is required to compile libbpf-sys using the vendored copy of libbpf");
}

if let Err(_) = process::Command::new("pkg-config").status() {
panic!("pkg-config is required to compile libbpf-sys using the vendored copy of libbpf");
// check for all necessary compilation tools
pkg_check("make");
pkg_check("pkg-config");
if cfg!(feature = "vendored") {
pkg_check("autoreconf");
pkg_check("autopoint");
pkg_check("flex");
pkg_check("bison");
pkg_check("gawk");
}

let compiler = match cc::Build::new().try_get_compiler() {
Expand All @@ -117,15 +126,35 @@ fn main() {
let obj_dir = path::PathBuf::from(&out_dir.join("obj").into_os_string());
let _ = fs::create_dir(&obj_dir);

// compile static zlib and static libelf
#[cfg(feature = "vendored")]
make_zlib(&compiler, &src_dir, &out_dir);
#[cfg(feature = "vendored")]
make_elfutils(&compiler, &src_dir, &out_dir);

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();
cflags.push(&format!(" -I{}/elfutils/libelf/", src_dir.display()));
cflags.push(&format!(" -I{}/zlib/", src_dir.display()));
cflags
} else {
compiler.cflags_env()
};

let status = process::Command::new("make")
.arg("install")
.arg("-j")
.arg(&format!("{}", num_cpus::get()))
.env("BUILD_STATIC_ONLY", "y")
.env("PREFIX", "/")
.env("LIBDIR", "")
.env("OBJDIR", &obj_dir)
.env("DESTDIR", &out_dir)
.env("CC", compiler.path())
.env("CFLAGS", compiler.cflags_env())
.env("CFLAGS", cflags)
.current_dir(&src_dir.join("libbpf/src"))
.status()
.expect("could not execute make");
Expand Down Expand Up @@ -157,3 +186,128 @@ fn main() {
}
}
}

#[cfg(feature = "vendored")]
fn make_zlib(compiler: &cc::Tool, src_dir: &path::PathBuf, out_dir: &path::PathBuf) {
use nix::fcntl;
use std::os::fd::AsRawFd;

// 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("zlib/README")).unwrap();
let fd = file.as_raw_fd();
fcntl::flock(fd, 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.join("zlib"))
.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::get()))
.current_dir(&src_dir.join("zlib"))
.status()
.expect("could not execute make");

assert!(status.success(), "make failed");

let status = process::Command::new("make")
.arg("distclean")
.current_dir(&src_dir.join("zlib"))
.status()
.expect("could not execute make");

assert!(status.success(), "make failed");
}

#[cfg(feature = "vendored")]
fn make_elfutils(compiler: &cc::Tool, src_dir: &path::PathBuf, out_dir: &path::PathBuf) {
use nix::fcntl;
use std::os::fd::AsRawFd;

// 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 fd = file.as_raw_fd();
fcntl::flock(fd, 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();
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::get()))
.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");
}

0 comments on commit 2640a5f

Please sign in to comment.