1 extern crate cc;
2 extern crate pkg_config;
3 #[cfg(target_env = "msvc")]
4 extern crate vcpkg;
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
17 let host_and_target_contain = |s| host.contains(s) && target.contains(s);
18
19 let want_ng = cfg!(feature = "zlib-ng") && !cfg!(feature = "stock-zlib");
20
21 if want_ng && target != "wasm32-unknown-unknown" {
22 return build_zlib_ng(&target);
23 }
24
25 // Don't run pkg-config if we're linking statically (we'll build below) and
26 // also don't run pkg-config on macOS/FreeBSD/DragonFly. That'll end up printing
27 // `-L /usr/lib` which wreaks havoc with linking to an OpenSSL in /usr/local/lib
28 // (Homebrew, Ports, etc.)
29 let want_static =
30 cfg!(feature = "static") || env::var("LIBZ_SYS_STATIC").unwrap_or(String::new()) == "1";
31 if !want_static &&
32 !target.contains("msvc") && // pkg-config just never works here
33 !(host_and_target_contain("apple") ||
34 host_and_target_contain("freebsd") ||
35 host_and_target_contain("dragonfly"))
36 {
37 // Don't print system lib dirs to cargo since this interferes with other
38 // packages adding non-system search paths to link against libraries
39 // that are also found in a system-wide lib dir.
40 let zlib = pkg_config::Config::new()
41 .cargo_metadata(true)
42 .print_system_libs(false)
43 .probe("zlib");
44 if zlib.is_ok() {
45 return;
46 }
47 }
48
49 if target.contains("msvc") {
50 if try_vcpkg() {
51 return;
52 }
53 }
54
55 // All android compilers should come with libz by default, so let's just use
56 // the one already there.
57 if target.contains("android") {
58 println!("cargo:rustc-link-lib=z");
59 return;
60 }
61
62 let mut cfg = cc::Build::new();
63
64 // Situations where we build unconditionally.
65 //
66 // MSVC basically never has it preinstalled, MinGW picks up a bunch of weird
67 // paths we don't like, `want_static` may force us, cross compiling almost
68 // never has a prebuilt version, and musl is almost always static.
69 if target.contains("msvc")
70 || target.contains("pc-windows-gnu")
71 || want_static
72 || target != host
73 || target.contains("musl")
74 {
75 return build_zlib(&mut cfg, &target);
76 }
77
78 // If we've gotten this far we're probably a pretty standard platform.
79 // Almost all platforms here ship libz by default, but some don't have
80 // pkg-config files that we would find above.
81 //
82 // In any case test if zlib is actually installed and if so we link to it,
83 // otherwise continue below to build things.
84 if zlib_installed(&mut cfg) {
85 println!("cargo:rustc-link-lib=z");
86 return;
87 }
88
89 build_zlib(&mut cfg, &target)
90 }
91
build_zlib(cfg: &mut cc::Build, target: &str)92 fn build_zlib(cfg: &mut cc::Build, target: &str) {
93 let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
94 let build = dst.join("build");
95
96 cfg.warnings(false).out_dir(&build).include("src/zlib");
97
98 cfg.file("src/zlib/adler32.c")
99 .file("src/zlib/compress.c")
100 .file("src/zlib/crc32.c")
101 .file("src/zlib/deflate.c")
102 .file("src/zlib/infback.c")
103 .file("src/zlib/inffast.c")
104 .file("src/zlib/inflate.c")
105 .file("src/zlib/inftrees.c")
106 .file("src/zlib/trees.c")
107 .file("src/zlib/uncompr.c")
108 .file("src/zlib/zutil.c");
109
110 if !cfg!(feature = "libc") || target == "wasm32-unknown-unknown" {
111 cfg.define("Z_SOLO", None);
112 } else {
113 cfg.file("src/zlib/gzclose.c")
114 .file("src/zlib/gzlib.c")
115 .file("src/zlib/gzread.c")
116 .file("src/zlib/gzwrite.c");
117 }
118
119 if !target.contains("windows") {
120 cfg.define("STDC", None);
121 cfg.define("_LARGEFILE64_SOURCE", None);
122 cfg.define("_POSIX_SOURCE", None);
123 cfg.flag("-fvisibility=hidden");
124 }
125 if target.contains("apple") {
126 cfg.define("_C99_SOURCE", None);
127 }
128 if target.contains("solaris") {
129 cfg.define("_XOPEN_SOURCE", "700");
130 }
131
132 cfg.compile("z");
133
134 fs::create_dir_all(dst.join("lib/pkgconfig")).unwrap();
135 fs::create_dir_all(dst.join("include")).unwrap();
136 fs::copy("src/zlib/zlib.h", dst.join("include/zlib.h")).unwrap();
137 fs::copy("src/zlib/zconf.h", dst.join("include/zconf.h")).unwrap();
138
139 fs::write(
140 dst.join("lib/pkgconfig/zlib.pc"),
141 fs::read_to_string("src/zlib/zlib.pc.in")
142 .unwrap()
143 .replace("@prefix@", dst.to_str().unwrap()),
144 )
145 .unwrap();
146
147 println!("cargo:root={}", dst.to_str().unwrap());
148 println!("cargo:include={}/include", dst.to_str().unwrap());
149 }
150
151 #[cfg(not(feature = "zlib-ng"))]
build_zlib_ng(_target: &str)152 fn build_zlib_ng(_target: &str) {}
153
154 #[cfg(feature = "zlib-ng")]
build_zlib_ng(target: &str)155 fn build_zlib_ng(target: &str) {
156 let install_dir = cmake::Config::new("src/zlib-ng")
157 .define("BUILD_SHARED_LIBS", "OFF")
158 .define("ZLIB_COMPAT", "ON")
159 .define("WITH_GZFILEOP", "ON")
160 .build();
161 let includedir = install_dir.join("include");
162 let libdir = install_dir.join("lib");
163 println!(
164 "cargo:rustc-link-search=native={}",
165 libdir.to_str().unwrap()
166 );
167 let libname = if target.contains("windows") {
168 if target.contains("msvc") && env::var("OPT_LEVEL").unwrap() == "0" {
169 "zlibd"
170 } else {
171 "zlib"
172 }
173 } else {
174 "z"
175 };
176 println!("cargo:rustc-link-lib=static={}", libname);
177 println!("cargo:root={}", install_dir.to_str().unwrap());
178 println!("cargo:include={}", includedir.to_str().unwrap());
179 }
180
181 #[cfg(not(target_env = "msvc"))]
try_vcpkg() -> bool182 fn try_vcpkg() -> bool {
183 false
184 }
185
186 #[cfg(target_env = "msvc")]
try_vcpkg() -> bool187 fn try_vcpkg() -> bool {
188 // see if there is a vcpkg tree with zlib installed
189 match vcpkg::Config::new()
190 .emit_includes(true)
191 .lib_names("zlib", "zlib1")
192 .probe("zlib")
193 {
194 Ok(_) => true,
195 Err(e) => {
196 println!("note, vcpkg did not find zlib: {}", e);
197 false
198 }
199 }
200 }
201
zlib_installed(cfg: &mut cc::Build) -> bool202 fn zlib_installed(cfg: &mut cc::Build) -> bool {
203 let compiler = cfg.get_compiler();
204 let mut cmd = Command::new(compiler.path());
205 cmd.arg("src/smoke.c").arg("-o").arg("/dev/null").arg("-lz");
206
207 println!("running {:?}", cmd);
208 if let Ok(status) = cmd.status() {
209 if status.success() {
210 return true;
211 }
212 }
213
214 false
215 }
216