1 extern crate pkg_config;
2 #[cfg(target_env = "msvc")]
3 extern crate vcpkg;
4 extern crate cc;
5 
6 use std::env;
7 use std::fs;
8 use std::path::PathBuf;
9 use std::process::Command;
10 
main()11 fn main() {
12     println!("cargo:rerun-if-env-changed=LIBZ_SYS_STATIC");
13     println!("cargo:rerun-if-changed=build.rs");
14     let host = env::var("HOST").unwrap();
15     let target = env::var("TARGET").unwrap();
16     let wants_asm = cfg!(feature = "asm");
17 
18     let host_and_target_contain = |s| host.contains(s) && target.contains(s);
19 
20     // Don't run pkg-config if we're linking statically (we'll build below) and
21     // also don't run pkg-config on macOS/FreeBSD/DragonFly. That'll end up printing
22     // `-L /usr/lib` which wreaks havoc with linking to an OpenSSL in /usr/local/lib
23     // (Homebrew, Ports, etc.)
24     let want_static =
25         cfg!(feature = "static") || env::var("LIBZ_SYS_STATIC").unwrap_or(String::new()) == "1";
26     if !wants_asm &&
27        !want_static &&
28        !target.contains("msvc") && // pkg-config just never works here
29        !(host_and_target_contain("apple") ||
30          host_and_target_contain("freebsd") ||
31          host_and_target_contain("dragonfly")) &&
32         pkg_config::Config::new().cargo_metadata(true).probe("zlib").is_ok() {
33         return
34     }
35 
36     if target.contains("msvc") {
37         if !wants_asm && try_vcpkg() {
38             return;
39         }
40     }
41 
42     // All android compilers should come with libz by default, so let's just use
43     // the one already there.
44     if !wants_asm && target.contains("android") {
45         println!("cargo:rustc-link-lib=z");
46         return
47     }
48 
49     let mut cfg = cc::Build::new();
50 
51     // Whitelist a bunch of situations where we build unconditionally.
52     //
53     // MSVC basically never has it preinstalled, MinGW picks up a bunch of weird
54     // paths we don't like, `want_static` may force us, cross compiling almost
55     // never has a prebuilt version, and musl is almost always static.
56     if wants_asm ||
57         target.contains("msvc") ||
58         target.contains("pc-windows-gnu") ||
59         want_static ||
60         target != host ||
61         target.contains("musl")
62     {
63         return build_zlib(&mut cfg, &target);
64     }
65 
66     // If we've gotten this far we're probably a pretty standard platform.
67     // Almost all platforms here ship libz by default, but some don't have
68     // pkg-config files that we would find above.
69     //
70     // In any case test if zlib is actually installed and if so we link to it,
71     // otherwise continue below to build things.
72     if zlib_installed(&mut cfg) {
73         println!("cargo:rustc-link-lib=z");
74         return
75     }
76 
77     build_zlib(&mut cfg, &target)
78 }
79 
build_zlib(cfg: &mut cc::Build, target: &str)80 fn build_zlib(cfg: &mut cc::Build, target: &str) {
81     let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
82     let build = dst.join("build");
83     let asm = cfg!(feature = "asm");
84 
85     cfg.warnings(false)
86         .out_dir(&build)
87         .include("src/zlib");
88 
89     cfg.file("src/zlib/adler32.c")
90         .file("src/zlib/compress.c")
91         .file("src/zlib/crc32.c")
92         .file("src/zlib/deflate.c")
93         .file("src/zlib/gzclose.c")
94         .file("src/zlib/gzlib.c")
95         .file("src/zlib/gzread.c")
96         .file("src/zlib/gzwrite.c")
97         .file("src/zlib/infback.c")
98         .file("src/zlib/inffast.c")
99         .file("src/zlib/inflate.c")
100         .file("src/zlib/inftrees.c")
101         .file("src/zlib/trees.c")
102         .file("src/zlib/uncompr.c")
103         .file("src/zlib/zutil.c");
104     if !target.contains("windows") {
105         cfg.define("STDC", None);
106         cfg.define("_LARGEFILE64_SOURCE", None);
107         cfg.define("_POSIX_SOURCE", None);
108         cfg.flag("-fvisibility=hidden");
109     }
110     if target.contains("ios") {
111         cfg.define("_C99_SOURCE", None);
112     }
113     if target.contains("solaris") {
114         cfg.define("_XOPEN_SOURCE", "700");
115     }
116 
117     if asm {
118         if target.contains("windows-msvc") {
119             if target.starts_with("x86_64") {
120                 cfg.file("src/zlib/contrib/masmx64/inffasx64.asm")
121                     .file("src/zlib/contrib/masmx64/gvmat64.asm")
122                     .define("ASMV", None)
123                     .define("ASMINF", None);
124             } else if target.starts_with("i686") {
125                 cfg.file("src/zlib/contrib/masmx86/inffas32.asm")
126                     .file("src/zlib/contrib/masmx86/match686.asm")
127                     .define("ASMV", None)
128                     .define("ASMINF", None);
129             }
130         } else {
131             if target.starts_with("x86_64") {
132                 cfg.file("src/zlib/contrib/amd64/amd64-match.S")
133                     .define("ASMV", None);
134             } else if target.starts_with("i686") {
135                 cfg.file("src/zlib/contrib/inflate86/inffast.S")
136                     .define("ASMINF", None);
137             }
138         }
139     }
140 
141     cfg.compile("z");
142 
143     fs::create_dir_all(dst.join("lib/pkgconfig")).unwrap();
144     fs::create_dir_all(dst.join("include")).unwrap();
145     fs::copy("src/zlib/zlib.h", dst.join("include/zlib.h")).unwrap();
146     fs::copy("src/zlib/zconf.h", dst.join("include/zconf.h")).unwrap();
147 
148     fs::write(
149         dst.join("lib/pkgconfig/zlib.pc"),
150         fs::read_to_string("src/zlib/zlib.pc.in")
151             .unwrap()
152             .replace("@prefix@", dst.to_str().unwrap()),
153     ).unwrap();
154 
155     println!("cargo:root={}", dst.to_str().unwrap());
156     println!("cargo:include={}/include", dst.to_str().unwrap());
157 }
158 
159 #[cfg(not(target_env = "msvc"))]
try_vcpkg() -> bool160 fn try_vcpkg() -> bool {
161     false
162 }
163 
164 #[cfg(target_env = "msvc")]
try_vcpkg() -> bool165 fn try_vcpkg() -> bool {
166     // see if there is a vcpkg tree with zlib installed
167     match vcpkg::Config::new()
168             .emit_includes(true)
169             .lib_names("zlib", "zlib1")
170             .probe("zlib") {
171         Ok(_) => { true },
172         Err(e) => {
173             println!("note, vcpkg did not find zlib: {}", e);
174             false
175         },
176     }
177 }
178 
zlib_installed(cfg: &mut cc::Build) -> bool179 fn zlib_installed(cfg: &mut cc::Build) -> bool {
180     let compiler = cfg.get_compiler();
181     let mut cmd = Command::new(compiler.path());
182     cmd.arg("src/smoke.c")
183         .arg("-o").arg("/dev/null")
184         .arg("-lz");
185 
186     println!("running {:?}", cmd);
187     if let Ok(status) = cmd.status() {
188         if status.success() {
189             return true
190         }
191     }
192 
193     false
194 }
195