From 7278d3e2a182d4a69146d616f673bde3f2a22404 Mon Sep 17 00:00:00 2001
From: yyy <37452715+editso@users.noreply.github.com>
Date: Sun, 18 Aug 2024 11:47:36 +0800
Subject: [PATCH] feat: Support building on the `Android` platform, using the
`CC` library to build dependency libraries instead
---
build.rs | 368 ++++++++++++++++++++++++++++++++++---------------------
1 file changed, 227 insertions(+), 141 deletions(-)
diff --git a/build.rs b/build.rs
index a5c5a63..f98f2d4 100644
--- a/build.rs
+++ b/build.rs
@@ -2,11 +2,11 @@
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 std::process::ExitStatus;
use nix::fcntl;
@@ -70,7 +70,6 @@ fn generate_bindings(src_dir: path::PathBuf) {
.allowlist_function("perf_.+")
.allowlist_function("ring_buffer_.+")
.allowlist_function("user_ring_buffer_.+")
- .allowlist_function("vdprintf")
.allowlist_type("bpf_.+")
.allowlist_type("btf_.+")
.allowlist_type("xdp_.+")
@@ -79,6 +78,10 @@ fn generate_bindings(src_dir: path::PathBuf) {
.allowlist_var("BTF_.+")
.allowlist_var("XDP_.+")
.allowlist_var("PERF_.+")
+ .allowlist_type("__va_list_tag")
+ .blocklist_type("vdprintf")
+ .blocklist_type("libbpf_print_fn_t")
+ .blocklist_function("libbpf_set_print")
.parse_callbacks(Box::new(ignored_macros))
.header("bindings.h")
.clang_arg(format!("-I{}", src_dir.join("libbpf/include").display()))
@@ -114,16 +117,20 @@ 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");
+ let android = build_android();
+
+ let vendored_libbpf = cfg!(feature = "vendored-libbpf") || android;
+ let vendored_libelf = cfg!(feature = "vendored-libelf") || android;
+ let vendored_zlib = cfg!(feature = "vendored-zlib") || android;
+
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");
+ let static_libbpf = cfg!(feature = "static-libbpf") || android;
+ let static_libelf = cfg!(feature = "static-libelf") || android;
+ let static_zlib = cfg!(feature = "static-zlib") || android;
+
println!("Using feature static-libbpf={}", static_libbpf);
println!("Using feature static-libelf={}", static_libelf);
println!("Using feature static-zlib={}", static_zlib);
@@ -146,10 +153,11 @@ fn main() {
pkg_check("flex");
pkg_check("bison");
pkg_check("gawk");
+ pkg_check("aclocal");
}
let (compiler, mut cflags) = if vendored_libbpf || vendored_libelf || vendored_zlib {
- pkg_check("make");
+ // pkg_check("make");
pkg_check("pkg-config");
let compiler = cc::Build::new().try_get_compiler().expect(
@@ -208,165 +216,243 @@ fn main() {
}
}
-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();
+fn build_android() -> bool {
+ if cfg!(feature = "android") {
+ true
+ } else {
+ env::var("CARGO_CFG_TARGET_OS")
+ .expect("CARGO_CFG_TARGET_OS not set")
+ .eq("android")
+ }
+}
- 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)
+fn subproc(prog: &str, workdir: &str, args: &[&str]) -> ExitStatus {
+ process::Command::new(prog)
+ .current_dir(workdir)
+ .args(args)
.status()
- .expect("could not execute make");
+ .expect(&format!("could not execute `{prog}`"))
+}
- assert!(status.success(), "make failed");
+fn configure
(project_dir: P, args: &[&str])
+where
+ P: AsRef,
+{
+ let project = project_dir.as_ref();
- let status = process::Command::new("make")
- .arg("install")
- .arg("-j")
- .arg(&format!("{}", num_cpus()))
- .current_dir(&src_dir)
- .status()
- .expect("could not execute make");
+ let prog = "./configure";
- assert!(status.success(), "make failed");
+ let _ = subproc("chmod", project, &["+x", prog]);
- let status = process::Command::new("make")
- .arg("distclean")
- .current_dir(&src_dir)
- .status()
- .expect("could not execute make");
+ let status = subproc(prog, project, args);
+
+ assert!(
+ status.success(),
+ "configure({}) failed: {}",
+ project,
+ status
+ );
+}
+
+fn autoconf(project_dir: P)
+where
+ P: AsRef,
+{
+ let project = project_dir.as_ref();
+
+ let status = subproc("autoreconf", project, &["--install", "--force"]);
+
+ assert!(
+ status.success(),
+ "autoreconfig({}) failed: {}",
+ project,
+ status
+ );
+}
+
+fn make_zlib(compiler: &cc::Tool, src_dir: &path::Path, _: &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("README.md")).unwrap();
+ let _lock = fcntl::Flock::lock(file, fcntl::FlockArg::LockExclusive).unwrap();
+
+ let zlib_sources = [
+ "adler32.c",
+ "compress.c",
+ "crc32.c",
+ "deflate.c",
+ "gzclose.c",
+ "gzlib.c",
+ "gzread.c",
+ "gzwrite.c",
+ "infback.c",
+ "inffast.c",
+ "inflate.c",
+ "inftrees.c",
+ "trees.c",
+ "uncompr.c",
+ "zutil.c",
+ ];
+
+ let cflags = [
+ // We do support hidden visibility, so turn that on.
+ "-DHAVE_HIDDEN",
+ // We do support const, so turn that on.
+ "-DZLIB_CONST",
+ // Enable -O3 as per chromium.
+ "-O3",
+ // "-Wall",
+ // "-Werror",
+ // "-Wno-deprecated-non-prototype",
+ // "-Wno-unused",
+ // "-Wno-unused-parameter",
+ ];
+
+ let project_dir = src_dir.join("zlib");
+ let project_dir = project_dir.to_str().unwrap();
+
+ configure(project_dir, &[]);
+
+ let mut builder = cc::Build::new();
+
+ builder.include(project_dir).files({
+ zlib_sources
+ .iter()
+ .map(|source| format!("{project_dir}/{source}"))
+ });
+
+ if build_android() {
+ for flag in cflags {
+ builder.flag(flag);
+ }
+ } else {
+ for flag in compiler.args() {
+ builder.flag(flag);
+ }
+ }
+
+ builder.flag_if_supported("-w").warnings(false).compile("z");
- 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) {
+fn make_elfutils(compiler: &cc::Tool, src_dir: &path::Path, _: &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 project_dir = src_dir.join("elfutils");
+ let project = project_dir.to_str().unwrap();
+
+ autoconf(project);
+
+ configure(
+ project,
+ &[
+ "--enable-maintainer-mode",
+ "--disable-debuginfod",
+ "--disable-libdebuginfod",
+ "--without-lzma",
+ "--without-bzlib",
+ "--without-zstd",
+ ],
+ );
- 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");
+ let mut builder = cc::Build::new();
+
+ builder
+ .include(project)
+ .include(src_dir.join("zlib"))
+ .include(format!("{project}/lib"))
+ .include(format!("{project}/include"))
+ .include(format!("{project}/libelf"));
+
+ if build_android() {
+ builder
+ .flag("-DHAVE_CONFIG_H")
+ .flag("-D_GNU_SOURCE")
+ .flag("-DNAMES=1000")
+ .flag("-std=gnu99")
+ .flag("-D_FILE_OFFSET_BITS=64")
+ .flag("-includeAndroidFixup.h")
+ .include(src_dir.join("android"));
+ } else {
+ #[cfg(target_arch = "aarch64")]
+ builder.flag("-Wno-error=stringop-overflow");
- assert!(status.success(), "make failed");
+ builder.compiler(compiler.path());
- 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");
+ for flag in compiler.args() {
+ if flag.ne("-static") {
+ builder.flag(flag);
+ }
+ }
+ }
- assert!(status.success(), "make failed");
+ for entry in std::fs::read_dir(project_dir.join("libelf")).expect("Failed to `read_dir`") {
+ let entry = entry.expect("Failed to `read_dir`");
+ if entry.file_type().unwrap().is_file()
+ && entry.file_name().to_str().unwrap().ends_with(".c")
+ {
+ builder.file(entry.path());
+ }
+ }
- let status = process::Command::new("make")
- .arg("distclean")
- .current_dir(&src_dir.join("elfutils"))
- .status()
- .expect("could not execute make");
+ builder
+ .flag_if_supported("-w")
+ .warnings(false)
+ .compile("elf");
- 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");
+fn make_libbpf(compiler: &cc::Tool, _: &ffi::OsStr, src_dir: &path::Path, _: &path::Path) {
+ let project_dir = src_dir.join("libbpf");
- assert!(status.success(), "make failed");
+ let project = project_dir.to_str().unwrap();
- let status = process::Command::new("make")
- .arg("clean")
- .current_dir(&src_dir)
- .status()
- .expect("could not execute make");
+ let mut builder = cc::Build::new();
- assert!(status.success(), "make failed");
- emit_rerun_directives_for_contents(&src_dir);
-}
+ builder
+ .include(src_dir)
+ .include(src_dir.join("zlib"))
+ .include(src_dir.join("elfutils").join("libelf"))
+ .include(format!("{project}/src"))
+ .include(format!("{project}/include"))
+ .include(format!("{project}/include/uapi"));
+
+ if build_android() {
+ let cflags = ["-DCOMPAT_NEED_REALLOCARRAY"];
+
+ builder.flag("-includeandroid/android.h");
-fn num_cpus() -> usize {
- std::thread::available_parallelism().map_or(1, |count| count.get())
+ for flag in cflags {
+ builder.flag(flag);
+ }
+ } else {
+ builder.compiler(compiler.path());
+
+ for flag in compiler.args() {
+ builder.flag(flag);
+ }
+ }
+
+ for entry in std::fs::read_dir(project_dir.join("src")).expect("Failed to `read_dir`") {
+ let entry = entry.unwrap();
+ if entry.file_type().unwrap().is_file()
+ && entry.file_name().to_str().unwrap().ends_with(".c")
+ {
+ builder.file(entry.path());
+ }
+ }
+
+ builder
+ .flag_if_supported("-w")
+ .warnings(false)
+ .compile("bpf");
+
+ emit_rerun_directives_for_contents(&src_dir);
}