use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::{env, fmt, fs};
#[cfg(feature = "bindgen")]
fn generate_bindings(defs: Vec<&str>, headerpaths: Vec<PathBuf>) {
let bindings = bindgen::Builder::default().header("zstd.h");
#[cfg(feature = "zdict_builder")]
let bindings = bindings.header("zdict.h");
let bindings = bindings
.blocklist_type("max_align_t")
.size_t_is_usize(true)
.use_core()
.rustified_enum(".*")
.clang_args(
headerpaths
.into_iter()
.map(|path| format!("-I{}", path.display())),
)
.clang_args(defs.into_iter().map(|def| format!("-D{}", def)));
#[cfg(feature = "experimental")]
let bindings = bindings
.clang_arg("-DZSTD_STATIC_LINKING_ONLY")
.clang_arg("-DZDICT_STATIC_LINKING_ONLY")
.clang_arg("-DZSTD_RUST_BINDINGS_EXPERIMENTAL");
#[cfg(not(feature = "std"))]
let bindings = bindings.ctypes_prefix("libc");
let bindings = bindings.generate().expect("Unable to generate bindings");
let out_path = PathBuf::from(env::var_os("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Could not write bindings");
}
#[cfg(not(feature = "bindgen"))]
fn generate_bindings(_: Vec<&str>, _: Vec<PathBuf>) {}
fn pkg_config() -> (Vec<&'static str>, Vec<PathBuf>) {
let library = pkg_config::Config::new()
.statik(true)
.cargo_metadata(!cfg!(feature = "non-cargo"))
.probe("libzstd")
.expect("Can't probe for zstd in pkg-config");
(vec!["PKG_CONFIG"], library.include_paths)
}
#[cfg(not(feature = "legacy"))]
fn set_legacy(_config: &mut cc::Build) {}
#[cfg(feature = "legacy")]
fn set_legacy(config: &mut cc::Build) {
config.define("ZSTD_LEGACY_SUPPORT", Some("1"));
config.include("zstd/lib/legacy");
}
#[cfg(feature = "zstdmt")]
fn set_pthread(config: &mut cc::Build) {
config.flag("-pthread");
}
#[cfg(not(feature = "zstdmt"))]
fn set_pthread(_config: &mut cc::Build) {}
#[cfg(feature = "zstdmt")]
fn enable_threading(config: &mut cc::Build) {
config.define("ZSTD_MULTITHREAD", Some(""));
}
#[cfg(not(feature = "zstdmt"))]
fn enable_threading(_config: &mut cc::Build) {}
#[allow(dead_code)]
fn flag_if_supported_with_fallbacks(config: &mut cc::Build, flags: &[&str]) {
let option = flags
.iter()
.find(|flag| config.is_flag_supported(flag).unwrap_or_default());
if let Some(flag) = option {
config.flag(flag);
}
}
fn compile_zstd() {
let mut config = cc::Build::new();
for dir in &[
"zstd/lib/common",
"zstd/lib/compress",
"zstd/lib/decompress",
#[cfg(feature = "zdict_builder")]
"zstd/lib/dictBuilder",
#[cfg(feature = "legacy")]
"zstd/lib/legacy",
] {
let mut entries: Vec<_> = fs::read_dir(dir)
.unwrap()
.map(Result::unwrap)
.filter_map(|entry| {
let filename = entry.file_name();
if Path::new(&filename).extension() == Some(OsStr::new("c"))
&& !filename.to_string_lossy().contains("xxhash")
{
Some(entry.path())
} else {
None
}
})
.collect();
entries.sort();
config.files(entries);
}
if cfg!(feature = "no_asm") || std::env::var("CARGO_CFG_WINDOWS").is_ok() {
config.define("ZSTD_DISABLE_ASM", Some(""));
} else {
config.file("zstd/lib/decompress/huf_decompress_amd64.S");
}
let need_wasm_shim = !cfg!(feature = "no_wasm_shim")
&& env::var("TARGET").map_or(false, |target| {
target == "wasm32-unknown-unknown" || target.starts_with("wasm32-wasi")
});
if need_wasm_shim {
cargo_print(&"rerun-if-changed=wasm-shim/stdlib.h");
cargo_print(&"rerun-if-changed=wasm-shim/string.h");
config.include("wasm-shim/");
}
config.include("zstd/lib/");
config.include("zstd/lib/common");
config.warnings(false);
config.define("ZSTD_LIB_DEPRECATED", Some("0"));
config
.flag_if_supported("-ffunction-sections")
.flag_if_supported("-fdata-sections")
.flag_if_supported("-fmerge-all-constants");
if cfg!(feature = "fat-lto") {
config.flag_if_supported("-flto");
} else if cfg!(feature = "thin-lto") {
flag_if_supported_with_fallbacks(
&mut config,
&["-flto=thin", "-flto"],
);
}
#[cfg(feature = "thin")]
{
config
.define("HUF_FORCE_DECOMPRESS_X1", Some("1"))
.define("ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT", Some("1"))
.define("ZSTD_NO_INLINE", Some("1"))
.define("ZSTD_STRIP_ERROR_STRINGS", Some("1"));
config.define("DYNAMIC_BMI2", Some("0"));
#[cfg(not(feature = "legacy"))]
config.define("ZSTD_LEGACY_SUPPORT", Some("0"));
config.opt_level_str("z");
}
config.flag("-fvisibility=hidden");
config.define("XXH_PRIVATE_API", Some(""));
config.define("ZSTDLIB_VISIBILITY", Some(""));
#[cfg(feature = "zdict_builder")]
config.define("ZDICTLIB_VISIBILITY", Some(""));
config.define("ZSTDERRORLIB_VISIBILITY", Some(""));
#[cfg(feature = "debug")]
if !is_wasm {
config.define("DEBUGLEVEL", Some("5"));
}
set_pthread(&mut config);
set_legacy(&mut config);
enable_threading(&mut config);
config.compile("libzstd.a");
let src = env::current_dir().unwrap().join("zstd").join("lib");
let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let include = dst.join("include");
fs::create_dir_all(&include).unwrap();
fs::copy(src.join("zstd.h"), include.join("zstd.h")).unwrap();
fs::copy(src.join("zstd_errors.h"), include.join("zstd_errors.h"))
.unwrap();
#[cfg(feature = "zdict_builder")]
fs::copy(src.join("zdict.h"), include.join("zdict.h")).unwrap();
cargo_print(&format_args!("root={}", dst.display()));
}
fn cargo_print(content: &dyn fmt::Display) {
if cfg!(not(feature = "non-cargo")) {
println!("cargo:{}", content);
}
}
fn main() {
cargo_print(&"rerun-if-env-changed=ZSTD_SYS_USE_PKG_CONFIG");
let target_arch =
std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
if target_arch == "wasm32" || target_os == "hermit" {
cargo_print(&"rustc-cfg=feature=\"std\"");
}
let (defs, headerpaths) = if cfg!(feature = "pkg-config")
|| env::var_os("ZSTD_SYS_USE_PKG_CONFIG").is_some()
{
pkg_config()
} else {
if !Path::new("zstd/lib").exists() {
panic!("Folder 'zstd/lib' does not exists. Maybe you forgot to clone the 'zstd' submodule?");
}
let manifest_dir = PathBuf::from(
env::var_os("CARGO_MANIFEST_DIR")
.expect("Manifest dir is always set by cargo"),
);
compile_zstd();
(vec![], vec![manifest_dir.join("zstd/lib")])
};
let includes: Vec<_> = headerpaths
.iter()
.map(|p| p.display().to_string())
.collect();
cargo_print(&format_args!("include={}", includes.join(";")));
generate_bindings(defs, headerpaths);
}