diff --git a/build.rs b/build.rs index 2cb07678..3c14b2c5 100644 --- a/build.rs +++ b/build.rs @@ -4,19 +4,8 @@ extern crate vcpkg; extern crate cc; use std::env; -use std::ffi::OsString; -use std::fs::{self, File}; -use std::io::prelude::*; -use std::io; -use std::path::{Path, PathBuf}; -use std::process::Command; - -macro_rules! t { - ($e:expr) => (match $e { - Ok(n) => n, - Err(e) => panic!("\n{} failed with {}\n", stringify!($e), e), - }) -} +use std::fs; +use std::path::PathBuf; fn main() { println!("cargo:rerun-if-env-changed=LIBZ_SYS_STATIC"); @@ -40,233 +29,85 @@ fn main() { return } - // Practically all platforms come with libz installed already, but MSVC is - // one of those sole platforms that doesn't! if target.contains("msvc") { if try_vcpkg() { return; } - - build_msvc_zlib(&target); - } else if target.contains("pc-windows-gnu") { - build_zlib_mingw(); - } else if (target.contains("musl") || - target != host || - want_static) && - !target.contains("windows-gnu") && - !target.contains("android") { - build_zlib(&host, &target); - } else { - println!("cargo:rustc-link-lib=z"); } -} -fn build_zlib(host: &str, target: &str) { - let src = env::current_dir().unwrap().join("src/zlib"); - let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap()); - let build = dst.join("build"); - t!(fs::create_dir_all(&build)); - cp_r(&src, &build); - - run(configure(host, target) - .current_dir(&build) - .arg(format!("--prefix={}", dst.display())), "sh"); - run(make() - .current_dir(&build) - .arg("libz.a"), "make"); - - t!(fs::create_dir_all(dst.join("lib/pkgconfig"))); - t!(fs::create_dir_all(dst.join("include"))); - t!(fs::copy(build.join("libz.a"), dst.join("lib/libz.a"))); - t!(fs::copy(build.join("zlib.h"), dst.join("include/zlib.h"))); - t!(fs::copy(build.join("zconf.h"), dst.join("include/zconf.h"))); - t!(fs::copy(build.join("zlib.pc"), dst.join("lib/pkgconfig/zlib.pc"))); - - println!("cargo:rustc-link-lib=static=z"); - println!("cargo:rustc-link-search={}/lib", dst.to_string_lossy()); - println!("cargo:root={}", dst.to_string_lossy()); - println!("cargo:include={}/include", dst.to_string_lossy()); -} - -fn configure(host: &str, target: &str) -> Command { - let compiler = cc::Build::new().get_compiler(); - let mut cflags = OsString::new(); - for arg in compiler.args() { - cflags.push(arg); - cflags.push(" "); - } - - let mut cmd = Command::new("./configure"); - cmd.env("CC", compiler.path()); - cmd.env("CFLAGS", cflags); - - if host != target { - cmd.env("CHOST", target); + // All android compilers should come with libz by default, so let's just use + // the one already there. + if target.contains("android") { + println!("cargo:rustc-link-lib=z"); + return } - return cmd -} - -fn make() -> Command { - let cmd = if cfg!(any(target_os = "freebsd", target_os = "dragonfly")) {"gmake"} else {"make"}; - let mut cmd = Command::new(cmd); - - // We're using the MSYS make which doesn't work with the mingw32-make-style - // MAKEFLAGS, so remove that from the env if present. - if cfg!(windows) { - cmd.env_remove("MAKEFLAGS").env_remove("MFLAGS"); - } else if let Some(makeflags) = env::var_os("CARGO_MAKEFLAGS") { - cmd.env("MAKEFLAGS", makeflags); + // Whitelist a bunch of 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, cross compiling almost + // never has a prebuilt version, and musl is almost always static. + if target.contains("msvc") || + target.contains("pc-windows-gnu") || + want_static || + target != host || + target.contains("musl") + { + return build_zlib(&target); } - return cmd + // If we've gotten this far we're probably a pretty standard platform. + // Almost all platforms here ship libz by default, so just assume it exists. + // This is buggy if zlib1g-dev isn't installed on Linux, we should fix that. + println!("cargo:rustc-link-lib=z"); } -// We have to run a few shell scripts, which choke quite a bit on both `\` -// characters and on `C:\` paths, so normalize both of them away. -fn sanitize_sh(path: &Path) -> String { - let path = path.to_str().unwrap().replace("\\", "/"); - return change_drive(&path).unwrap_or(path); - - fn change_drive(s: &str) -> Option { - let mut ch = s.chars(); - let drive = ch.next().unwrap_or('C'); - if ch.next() != Some(':') { - return None - } - if ch.next() != Some('/') { - return None - } - Some(format!("/{}/{}", drive, &s[drive.len_utf8() + 2..])) - } -} - -fn build_zlib_mingw() { - let src = env::current_dir().unwrap().join("src/zlib"); +fn build_zlib(target: &str) { let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let build = dst.join("build"); - t!(fs::create_dir_all(&build)); - cp_r(&src, &build); - let compiler = cc::Build::new().get_compiler(); - let mut cflags = OsString::new(); - for arg in compiler.args() { - cflags.push(arg); - cflags.push(" "); - } - let gcc = sanitize_sh(compiler.path()); - let mut cmd = make(); - cmd.arg("-f").arg("win32/Makefile.gcc") - .current_dir(&build) - .arg("install") - .arg(format!("prefix={}", sanitize_sh(&dst))) - .arg("IMPLIB=") - .arg(format!("INCLUDE_PATH={}", sanitize_sh(&dst.join("include")))) - .arg(format!("LIBRARY_PATH={}", sanitize_sh(&dst.join("lib")))) - .arg(format!("BINARY_PATH={}", sanitize_sh(&dst.join("bin")))) - .env("CFLAGS", cflags); - - if gcc != "gcc" { - match gcc.find("gcc") { - Some(0) => {} - Some(i) => { - cmd.arg(format!("PREFIX={}", &gcc[..i])); - } - None => {} - } - } - run(&mut cmd, "make"); - - t!(fs::create_dir_all(dst.join("lib/pkgconfig"))); - - println!("cargo:rustc-link-lib=static=z"); - println!("cargo:rustc-link-search={}/lib", dst.to_string_lossy()); - println!("cargo:root={}", dst.to_string_lossy()); - println!("cargo:include={}/include", dst.to_string_lossy()); -} - -fn cp_r(dir: &Path, dst: &Path) { - for entry in t!(fs::read_dir(dir)) { - let entry = t!(entry); - let path = entry.path(); - let dst = dst.join(path.file_name().unwrap()); - if t!(fs::metadata(&path)).is_file() { - t!(fs::copy(path, dst)); - } else { - t!(fs::create_dir_all(&dst)); - cp_r(&path, &dst); - } - } -} -fn build_msvc_zlib(target: &str) { - let src = t!(env::current_dir()).join("src/zlib"); - let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap()); - - t!(fs::create_dir_all(dst.join("lib"))); - t!(fs::create_dir_all(dst.join("include"))); - t!(fs::create_dir_all(dst.join("build"))); - cp_r(&src, &dst.join("build")); - - let features = env::var("CARGO_CFG_TARGET_FEATURE") - .unwrap_or(String::new()); - if features.contains("crt-static") { - let mut makefile = String::new(); - let makefile_path = dst.join("build/win32/Makefile.msc"); - t!(t!(File::open(&makefile_path)).read_to_string(&mut makefile)); - let new_makefile = makefile.replace(" -MD ", " -MT "); - t!(t!(File::create(&makefile_path)).write_all(new_makefile.as_bytes())); + let mut cfg = cc::Build::new(); + cfg.warnings(false) + .out_dir(&build) + .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/gzclose.c") + .file("src/zlib/gzlib.c") + .file("src/zlib/gzread.c") + .file("src/zlib/gzwrite.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 !target.contains("windows") { + cfg.define("STDC", None); + cfg.define("_LARGEFILE64_SOURCE", None); + cfg.define("_POSIX_SOURCE", None); } - let nmake = cc::windows_registry::find(target, "nmake.exe"); - let mut nmake = nmake.unwrap_or(Command::new("nmake.exe")); - - // These env vars are intended for mingw32-make, not `namek`, which chokes - // on them anyway. - nmake.env_remove("MAKEFLAGS") - .env_remove("MFLAGS"); - - run(nmake.current_dir(dst.join("build")) - .arg("/nologo") - .arg("/f") - .arg(dst.join("build/win32/Makefile.msc")) - .arg("zlib.lib"), "nmake.exe"); - - for file in t!(fs::read_dir(&dst.join("build"))) { - let file = t!(file).path(); - if let Some(s) = file.file_name().and_then(|s| s.to_str()) { - if s.ends_with(".h") { - t!(fs::copy(&file, dst.join("include").join(s))); - } - } - } - t!(fs::copy(dst.join("build/zlib.lib"), dst.join("lib/zlib.lib"))); + cfg.compile("z"); - println!("cargo:rustc-link-lib=static=zlib"); - println!("cargo:rustc-link-search={}/lib", dst.to_string_lossy()); - println!("cargo:root={}", dst.to_string_lossy()); - println!("cargo:include={}/include", dst.to_string_lossy()); -} + fs::create_dir_all(dst.join("lib/pkgconfig")).unwrap(); + 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(); -fn run(cmd: &mut Command, program: &str) { - println!("running: {:?}", cmd); - let status = match cmd.status() { - Ok(status) => status, - Err(ref e) if e.kind() == io::ErrorKind::NotFound => { - fail(&format!("failed to execute command: {}\nIs `{}` \ - not installed?", - e, - program)); - } - Err(e) => fail(&format!("failed to execute command: {}", e)), - }; - if !status.success() { - fail(&format!("command did not execute successfully, got: {}", status)); - } -} + fs::write( + dst.join("lib/pkgconfig/zlib.pc"), + fs::read_to_string("src/zlib/zlib.pc.in") + .unwrap() + .replace("@prefix@", dst.to_str().unwrap()), + ).unwrap(); -fn fail(s: &str) -> ! { - println!("\n\n{}\n\n", s); - std::process::exit(1); + println!("cargo:root={}", dst.to_str().unwrap()); + println!("cargo:include={}/include", dst.to_str().unwrap()); } #[cfg(not(target_env = "msvc"))]