use std::env;
use std::path::PathBuf;
fn main() {
println!("cargo:rerun-if-changed=libarchive/");
build_libarchive();
generate_bindings();
}
fn build_libarchive() {
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let target = env::var("TARGET").unwrap();
let mut config = cmake::Config::new("libarchive");
config
.define("BUILD_SHARED_LIBS", "OFF")
.define("ENABLE_TEST", "OFF")
.define("ENABLE_TAR", "OFF")
.define("ENABLE_CPIO", "OFF")
.define("ENABLE_CAT", "OFF")
.define("ENABLE_UNZIP", "OFF")
.define("ENABLE_WERROR", "OFF");
if target.contains("android") {
let ndk_home = env::var("ANDROID_NDK_HOME")
.or_else(|_| env::var("NDK_HOME"))
.or_else(|_| env::var("ANDROID_NDK"))
.expect("ANDROID_NDK_HOME environment variable must be set for Android builds");
let ndk_path = PathBuf::from(&ndk_home);
let android_abi = if target.contains("aarch64") {
"arm64-v8a"
} else if target.contains("armv7") {
"armeabi-v7a"
} else if target.contains("i686") {
"x86"
} else if target.contains("x86_64") {
"x86_64"
} else {
"arm64-v8a"
};
let toolchain_file = ndk_path.join("build/cmake/android.toolchain.cmake");
if toolchain_file.exists() {
let ndk_bin = ndk_path.join("toolchains/llvm/prebuilt/darwin-x86_64/bin");
let arch_prefix = if target.contains("armv7") {
"armv7a-linux-androideabi"
} else if target.contains("aarch64") {
"aarch64-linux-android"
} else if target.contains("i686") {
"i686-linux-android"
} else if target.contains("x86_64") {
"x86_64-linux-android"
} else {
"aarch64-linux-android"
};
let c_compiler = ndk_bin.join(format!("{}21-clang", arch_prefix));
let cpp_compiler = ndk_bin.join(format!("{}21-clang++", arch_prefix));
if c_compiler.exists() {
unsafe {
env::set_var(format!("CC_{}", target.replace("-", "_")), &c_compiler);
}
}
if cpp_compiler.exists() {
unsafe {
env::set_var(format!("CXX_{}", target.replace("-", "_")), &cpp_compiler);
}
}
config.init_c_cfg(cc::Build::new());
config.define("CMAKE_TOOLCHAIN_FILE", toolchain_file.to_str().unwrap());
config.define("ANDROID_ABI", android_abi);
config.define("ANDROID_PLATFORM", "android-21"); config.define("ANDROID_NDK", &ndk_home);
config.define("CMAKE_SYSTEM_NAME", "Android");
}
config
.define("ENABLE_ACL", "ON")
.define("ENABLE_XATTR", "ON")
.define("ENABLE_ZLIB", "ON")
.define("ENABLE_BZip2", "ON")
.define("ENABLE_LZMA", "ON")
.define("ENABLE_ZSTD", "ON")
.define("ENABLE_LZ4", "ON");
} else if target.contains("linux") && !cfg!(target_os = "linux") {
let linux_gcc_names = vec!["x86_64-unknown-linux-gnu-gcc", "x86_64-linux-gnu-gcc"];
for gcc_name in &linux_gcc_names {
if let Ok(gcc_path) = which::which(gcc_name) {
let gxx_name = gcc_name.replace("gcc", "g++");
if let Ok(gxx_path) = which::which(&gxx_name) {
unsafe {
env::set_var(format!("CC_{}", target.replace("-", "_")), &gcc_path);
env::set_var(format!("CXX_{}", target.replace("-", "_")), &gxx_path);
}
break;
}
}
}
config.init_c_cfg(cc::Build::new());
config
.define("ENABLE_ACL", "ON")
.define("ENABLE_XATTR", "ON")
.define("ENABLE_ZLIB", "ON")
.define("ENABLE_BZip2", "ON")
.define("ENABLE_LZMA", "ON")
.define("ENABLE_ZSTD", "ON")
.define("ENABLE_LZ4", "ON");
} else if target.contains("windows") && !cfg!(target_os = "windows") {
if let Ok(mingw_gcc) = which::which("x86_64-w64-mingw32-gcc")
&& let Ok(mingw_gxx) = which::which("x86_64-w64-mingw32-g++")
{
unsafe {
env::set_var(format!("CC_{}", target.replace("-", "_")), &mingw_gcc);
env::set_var(format!("CXX_{}", target.replace("-", "_")), &mingw_gxx);
}
}
config.init_c_cfg(cc::Build::new());
config
.define("ENABLE_ACL", "ON")
.define("ENABLE_XATTR", "ON")
.define("ENABLE_ZLIB", "ON")
.define("ENABLE_BZip2", "ON")
.define("ENABLE_LZMA", "ON")
.define("ENABLE_ZSTD", "ON")
.define("ENABLE_LZ4", "ON");
} else if target.contains("wasm") {
panic!(
"WASM target is not supported by libarchive v3.8.1. \
The library requires POSIX types and system calls that are not available in WebAssembly. \
Supported platforms: macOS, Windows, Linux, iOS, Android"
);
} else if target.contains("windows") && cfg!(target_os = "windows") {
if let Ok(vcpkg_root) = env::var("VCPKG_INSTALLATION_ROOT") {
let toolchain_file =
PathBuf::from(&vcpkg_root).join("scripts/buildsystems/vcpkg.cmake");
if toolchain_file.exists() {
config.define("CMAKE_TOOLCHAIN_FILE", toolchain_file.to_str().unwrap());
}
}
config
.define("ENABLE_ACL", "ON")
.define("ENABLE_XATTR", "ON")
.define("ENABLE_ZLIB", "ON")
.define("ENABLE_BZip2", "ON")
.define("ENABLE_LZMA", "ON")
.define("ENABLE_ZSTD", "ON")
.define("ENABLE_LZ4", "ON")
.define("ENABLE_OPENSSL", "ON");
} else {
config
.define("ENABLE_ACL", "ON")
.define("ENABLE_XATTR", "ON")
.define("ENABLE_ZLIB", "ON")
.define("ENABLE_BZip2", "ON")
.define("ENABLE_LZMA", "ON")
.define("ENABLE_ZSTD", "ON")
.define("ENABLE_LZ4", "ON");
}
config.build_target("archive_static");
let _ = config.build();
let build_dir = out_dir.join("build");
if target.contains("windows") && target.contains("msvc") {
println!(
"cargo:rustc-link-search=native={}/libarchive/Debug",
build_dir.display()
);
println!(
"cargo:rustc-link-search=native={}/libarchive/Release",
build_dir.display()
);
println!(
"cargo:rustc-link-search=native={}/libarchive",
build_dir.display()
);
println!("cargo:rustc-link-lib=static=archive");
} else {
println!(
"cargo:rustc-link-search=native={}/libarchive",
build_dir.display()
);
println!("cargo:rustc-link-lib=static=archive");
}
let target = env::var("TARGET").unwrap();
if target.contains("apple-darwin") {
println!("cargo:rustc-link-search=native=/opt/homebrew/lib");
println!("cargo:rustc-link-search=native=/usr/local/lib");
println!("cargo:rustc-link-lib=iconv");
println!("cargo:rustc-link-lib=xml2");
println!("cargo:rustc-link-lib=b2");
println!("cargo:rustc-link-lib=framework=CoreFoundation");
println!("cargo:rustc-link-lib=z");
println!("cargo:rustc-link-lib=bz2");
println!("cargo:rustc-link-lib=lzma");
println!("cargo:rustc-link-lib=zstd");
println!("cargo:rustc-link-lib=lz4");
} else if target.contains("apple-ios") {
println!("cargo:rustc-link-lib=iconv");
println!("cargo:rustc-link-lib=xml2");
println!("cargo:rustc-link-lib=framework=CoreFoundation");
println!("cargo:rustc-link-lib=z");
println!("cargo:rustc-link-lib=bz2");
println!("cargo:rustc-link-lib=lzma");
println!("cargo:rustc-link-lib=zstd");
println!("cargo:rustc-link-lib=lz4");
} else if target.contains("linux-android") {
println!("cargo:rustc-link-lib=z");
println!("cargo:rustc-link-lib=bz2");
println!("cargo:rustc-link-lib=lzma");
println!("cargo:rustc-link-lib=zstd");
println!("cargo:rustc-link-lib=lz4");
} else if target.contains("linux") {
println!("cargo:rustc-link-lib=pthread");
println!("cargo:rustc-link-lib=z");
println!("cargo:rustc-link-lib=bz2");
println!("cargo:rustc-link-lib=lzma");
println!("cargo:rustc-link-lib=zstd");
println!("cargo:rustc-link-lib=lz4");
println!("cargo:rustc-link-lib=xml2"); println!("cargo:rustc-link-lib=crypto"); println!("cargo:rustc-link-lib=acl"); } else if target.contains("windows") {
if target.contains("msvc") {
if let Ok(vcpkg_root) = env::var("VCPKG_INSTALLATION_ROOT") {
let vcpkg_lib = PathBuf::from(vcpkg_root)
.join("installed")
.join("x64-windows")
.join("lib");
if vcpkg_lib.exists() {
println!("cargo:rustc-link-search=native={}", vcpkg_lib.display());
}
}
println!("cargo:rustc-link-lib=zlib");
println!("cargo:rustc-link-lib=bz2");
println!("cargo:rustc-link-lib=lzma");
println!("cargo:rustc-link-lib=zstd");
println!("cargo:rustc-link-lib=lz4");
println!("cargo:rustc-link-lib=libcrypto"); println!("cargo:rustc-link-lib=bcrypt"); println!("cargo:rustc-link-lib=advapi32");
println!("cargo:rustc-link-lib=xmllite"); println!("cargo:rustc-link-lib=ole32"); } else {
println!("cargo:rustc-link-lib=z");
println!("cargo:rustc-link-lib=bz2");
println!("cargo:rustc-link-lib=lzma");
println!("cargo:rustc-link-lib=zstd");
println!("cargo:rustc-link-lib=lz4");
println!("cargo:rustc-link-lib=crypto"); println!("cargo:rustc-link-lib=bcrypt");
println!("cargo:rustc-link-lib=advapi32");
println!("cargo:rustc-link-lib=xmllite"); println!("cargo:rustc-link-lib=ole32"); }
} else if target.contains("wasm") {
println!("cargo:rustc-link-lib=z");
}
}
fn generate_bindings() {
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let libarchive_include = PathBuf::from("libarchive/libarchive");
let target = env::var("TARGET").unwrap();
let mut builder = bindgen::Builder::default()
.header("wrapper.h")
.clang_arg(format!("-I{}", libarchive_include.display()))
.allowlist_type("archive.*")
.allowlist_type("archive_entry.*")
.allowlist_function("archive.*")
.allowlist_var("ARCHIVE.*")
.allowlist_var("AE_.*")
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.derive_debug(true)
.derive_default(true)
.size_t_is_usize(true)
.layout_tests(false);
if target.contains("windows") {
builder = builder
.clang_arg("--target=x86_64-pc-windows-gnu")
.clang_arg(format!("-I{}/build", out_dir.display()));
if let Ok(mingw_gcc) = which::which("x86_64-w64-mingw32-gcc") {
if let Ok(real_path) = std::fs::canonicalize(&mingw_gcc) {
if let Some(bin_dir) = real_path.parent()
&& let Some(toolchain_dir) = bin_dir.parent()
{
let mingw_include = toolchain_dir.join("x86_64-w64-mingw32/include");
if mingw_include.exists() {
builder = builder.clang_arg(format!("-I{}", mingw_include.display()));
}
}
}
}
} else if target.contains("android") {
builder = builder.clang_arg(format!("-I{}/build", out_dir.display()));
let ndk_triple = if target.contains("aarch64") {
"aarch64-linux-android"
} else if target.contains("armv7") {
"armv7a-linux-androideabi"
} else if target.contains("i686") {
"i686-linux-android"
} else if target.contains("x86_64") {
"x86_64-linux-android"
} else {
"aarch64-linux-android"
};
builder = builder.clang_arg(format!("--target={}", ndk_triple));
let ndk_home = env::var("ANDROID_NDK_HOME")
.or_else(|_| env::var("NDK_HOME"))
.or_else(|_| env::var("ANDROID_NDK"))
.ok();
if let Some(ndk_home) = ndk_home {
let ndk_path = PathBuf::from(ndk_home);
let sysroot = ndk_path.join("toolchains/llvm/prebuilt/darwin-x86_64/sysroot");
if sysroot.exists() {
builder = builder
.clang_arg(format!("--sysroot={}", sysroot.display()))
.clang_arg(format!("-I{}/usr/include", sysroot.display()))
.clang_arg(format!(
"-I{}/usr/include/{}",
sysroot.display(),
ndk_triple
));
}
}
} else if target.contains("linux") && !cfg!(target_os = "linux") {
builder = builder
.clang_arg("--target=x86_64-unknown-linux-gnu")
.clang_arg(format!("-I{}/build", out_dir.display()));
let possible_gcc_names = vec!["x86_64-unknown-linux-gnu-gcc", "x86_64-linux-gnu-gcc"];
for gcc_name in possible_gcc_names {
if let Ok(linux_gcc) = which::which(gcc_name) {
if let Ok(real_path) = std::fs::canonicalize(&linux_gcc) {
let sysroot = real_path
.parent()
.and_then(|p| p.parent())
.map(|p| p.join("x86_64-unknown-linux-gnu/sysroot"));
if let Some(sysroot) = sysroot
&& sysroot.exists()
{
builder = builder
.clang_arg(format!("--sysroot={}", sysroot.display()))
.clang_arg(format!("-I{}/usr/include", sysroot.display()));
break;
}
}
}
}
} else if target.contains("wasm") {
builder = builder.clang_arg("--target=wasm32-unknown-emscripten");
}
let bindings = builder.generate().expect("Unable to generate bindings");
bindings
.write_to_file(out_dir.join("bindings.rs"))
.expect("Couldn't write bindings!");
}