blob: 898c95c0fb1623a5a3ebe4b7bcba38b0f9007673 [file] [log] [blame] [edit]
use std::env;
use std::fs;
use std::path::PathBuf;
fn main() {
println!("cargo:rerun-if-env-changed=LIBZ_SYS_STATIC");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=zng/cmake.rs");
println!("cargo:rerun-if-changed=zng/cc.rs");
let host = env::var("HOST").unwrap();
let target = env::var("TARGET").unwrap();
let host_and_target_contain = |s| host.contains(s) && target.contains(s);
let want_ng = cfg!(any(
feature = "zlib-ng",
feature = "zlib-ng-no-cmake-experimental-community-maintained"
)) && !cfg!(feature = "stock-zlib");
if want_ng && target != "wasm32-unknown-unknown" {
return build_zlib_ng(&target, true);
}
// Don't run pkg-config if we're linking statically (we'll build below) and
// also don't run pkg-config on FreeBSD/DragonFly. That'll end up printing
// `-L /usr/lib` which wreaks havoc with linking to an OpenSSL in /usr/local/lib
// (Ports, etc.)
let want_static =
cfg!(feature = "static") || env::var("LIBZ_SYS_STATIC").unwrap_or(String::new()) == "1";
if !want_static &&
!target.contains("msvc") && // pkg-config just never works here
!(host_and_target_contain("freebsd") ||
host_and_target_contain("dragonfly"))
{
// Don't print system lib dirs to cargo since this interferes with other
// packages adding non-system search paths to link against libraries
// that are also found in a system-wide lib dir.
let zlib = pkg_config::Config::new()
.cargo_metadata(true)
.print_system_libs(false)
.probe("zlib");
match zlib {
Ok(zlib) => {
if !zlib.include_paths.is_empty() {
let paths = zlib
.include_paths
.iter()
.map(|s| s.display().to_string())
.collect::<Vec<_>>();
println!("cargo:include={}", paths.join(","));
}
}
Err(e) => {
println!("cargo-warning={}", e.to_string())
}
}
}
if target.contains("windows") {
if try_vcpkg() {
return;
}
}
// All android compilers should come with libz by default, so let's just use
// the one already there. Likewise, Haiku always ships with libz, so we can
// link to it even when cross-compiling.
if target.contains("android") || target.contains("haiku") {
println!("cargo:rustc-link-lib=z");
return;
}
let mut cfg = cc::Build::new();
// Situations where we build unconditionally.
//
// MSVC basically never has it preinstalled, MinGW picks up a bunch of weird
// paths we don't like, `want_static` may force us, and cross compiling almost
// never has a prebuilt version.
//
// Apple platforms have libz.1.dylib, and it's usually available even when
// cross compiling (via fat binary or in the target's Xcode SDK)
let cross_compiling = target != host;
if target.contains("msvc")
|| target.contains("pc-windows-gnu")
|| want_static
|| (cross_compiling && !target.contains("-apple-"))
{
return build_zlib(&mut cfg, &target);
}
// If we've gotten this far we're probably a pretty standard platform.
// Almost all platforms here ship libz by default, but some don't have
// pkg-config files that we would find above.
//
// In any case test if zlib is actually installed and if so we link to it,
// otherwise continue below to build things.
if zlib_installed(&mut cfg) {
println!("cargo:rustc-link-lib=z");
return;
}
build_zlib(&mut cfg, &target)
}
fn build_zlib(cfg: &mut cc::Build, target: &str) {
let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let lib = dst.join("lib");
cfg.warnings(false).out_dir(&lib).include("src/zlib");
cfg.file("src/zlib/adler32.c")
.file("src/zlib/compress.c")
.file("src/zlib/crc32.c")
.file("src/zlib/deflate.c")
.file("src/zlib/infback.c")
.file("src/zlib/inffast.c")
.file("src/zlib/inflate.c")
.file("src/zlib/inftrees.c")
.file("src/zlib/trees.c")
.file("src/zlib/uncompr.c")
.file("src/zlib/zutil.c");
if !cfg!(feature = "libc") || target.starts_with("wasm32") {
cfg.define("Z_SOLO", None);
} else {
cfg.file("src/zlib/gzclose.c")
.file("src/zlib/gzlib.c")
.file("src/zlib/gzread.c")
.file("src/zlib/gzwrite.c");
}
if !target.contains("windows") {
cfg.define("STDC", None);
cfg.define("_LARGEFILE64_SOURCE", None);
cfg.define("_POSIX_SOURCE", None);
cfg.flag("-fvisibility=hidden");
}
if target.contains("apple") {
cfg.define("_C99_SOURCE", None);
}
if target.contains("solaris") {
cfg.define("_XOPEN_SOURCE", "700");
}
cfg.compile("z");
fs::create_dir_all(dst.join("include")).unwrap();
fs::copy("src/zlib/zlib.h", dst.join("include/zlib.h")).unwrap();
fs::copy("src/zlib/zconf.h", dst.join("include/zconf.h")).unwrap();
fs::create_dir_all(lib.join("pkgconfig")).unwrap();
let zlib_h = fs::read_to_string(dst.join("include/zlib.h")).unwrap();
let version = zlib_h
.lines()
.find(|l| l.contains("ZLIB_VERSION"))
.unwrap()
.split("\"")
.nth(1)
.unwrap();
fs::write(
lib.join("pkgconfig/zlib.pc"),
fs::read_to_string("src/zlib/zlib.pc.in")
.unwrap()
.replace("@prefix@", dst.to_str().unwrap())
.replace("@includedir@", "${prefix}/include")
.replace("@libdir@", "${prefix}/lib")
.replace("@VERSION@", version),
)
.unwrap();
println!("cargo:root={}", dst.to_str().unwrap());
println!("cargo:rustc-link-search=native={}", lib.to_str().unwrap());
println!("cargo:include={}/include", dst.to_str().unwrap());
}
#[cfg(any(
feature = "zlib-ng",
feature = "zlib-ng-no-cmake-experimental-community-maintained"
))]
mod zng {
#[cfg_attr(feature = "zlib-ng", path = "cmake.rs")]
#[cfg_attr(
all(
feature = "zlib-ng-no-cmake-experimental-community-maintained",
not(feature = "zlib-ng")
),
path = "cc.rs"
)]
mod build_zng;
pub(super) use build_zng::build_zlib_ng;
}
fn build_zlib_ng(_target: &str, _compat: bool) {
#[cfg(any(
feature = "zlib-ng",
feature = "zlib-ng-no-cmake-experimental-community-maintained"
))]
zng::build_zlib_ng(_target, _compat);
}
fn try_vcpkg() -> bool {
// see if there is a vcpkg tree with zlib installed
match vcpkg::Config::new()
.emit_includes(true)
.find_package("zlib")
{
Ok(_) => true,
Err(e) => {
println!("note, vcpkg did not find zlib: {}", e);
false
}
}
}
fn zlib_installed(cfg: &mut cc::Build) -> bool {
let mut cmd = cfg.get_compiler().to_command();
cmd.arg("src/smoke.c")
.arg("-g0")
.arg("-o")
.arg("/dev/null")
.arg("-lz");
println!("running {:?}", cmd);
if let Ok(status) = cmd.status() {
if status.success() {
return true;
}
}
false
}