1 use std::env;
2 use std::fs;
3 use std::path::PathBuf;
4 use std::process::Command;
5
main()6 fn main() {
7 println!("cargo:rerun-if-env-changed=LIBZ_SYS_STATIC");
8 println!("cargo:rerun-if-changed=build.rs");
9 let host = env::var("HOST").unwrap();
10 let target = env::var("TARGET").unwrap();
11
12 let host_and_target_contain = |s| host.contains(s) && target.contains(s);
13
14 let want_ng = cfg!(feature = "zlib-ng") && !cfg!(feature = "stock-zlib");
15
16 if want_ng && target != "wasm32-unknown-unknown" {
17 return build_zlib_ng(&target, true);
18 }
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 !want_static &&
27 !target.contains("msvc") && // pkg-config just never works here
28 !(host_and_target_contain("apple") ||
29 host_and_target_contain("freebsd") ||
30 host_and_target_contain("dragonfly"))
31 {
32 // Don't print system lib dirs to cargo since this interferes with other
33 // packages adding non-system search paths to link against libraries
34 // that are also found in a system-wide lib dir.
35 let zlib = pkg_config::Config::new()
36 .cargo_metadata(true)
37 .print_system_libs(false)
38 .probe("zlib");
39 match zlib {
40 Ok(_) => return,
41 Err(e) => {
42 println!("cargo-warning={}", e.to_string())
43 }
44 }
45 }
46
47 if target.contains("msvc") {
48 if try_vcpkg() {
49 return;
50 }
51 }
52
53 // All android compilers should come with libz by default, so let's just use
54 // the one already there. Likewise, Haiku always ships with libz, so we can
55 // link to it even when cross-compiling.
56 if target.contains("android") || target.contains("haiku") {
57 println!("cargo:rustc-link-lib=z");
58 return;
59 }
60
61 let mut cfg = cc::Build::new();
62
63 // Situations where we build unconditionally.
64 //
65 // MSVC basically never has it preinstalled, MinGW picks up a bunch of weird
66 // paths we don't like, `want_static` may force us, and cross compiling almost
67 // never has a prebuilt version.
68 //
69 // Apple platforms have libz.1.dylib, and it's usually available even when
70 // cross compiling (via fat binary or in the target's Xcode SDK)
71 let cross_compiling = target != host;
72 let apple_to_apple = host.contains("-apple-") && target.contains("-apple-");
73 if target.contains("msvc")
74 || target.contains("pc-windows-gnu")
75 || want_static
76 || (cross_compiling && !apple_to_apple)
77 {
78 return build_zlib(&mut cfg, &target);
79 }
80
81 // If we've gotten this far we're probably a pretty standard platform.
82 // Almost all platforms here ship libz by default, but some don't have
83 // pkg-config files that we would find above.
84 //
85 // In any case test if zlib is actually installed and if so we link to it,
86 // otherwise continue below to build things.
87 if zlib_installed(&mut cfg) {
88 println!("cargo:rustc-link-lib=z");
89 return;
90 }
91
92 build_zlib(&mut cfg, &target)
93 }
94
build_zlib(cfg: &mut cc::Build, target: &str)95 fn build_zlib(cfg: &mut cc::Build, target: &str) {
96 let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
97 let lib = dst.join("lib");
98
99 cfg.warnings(false).out_dir(&lib).include("src/zlib");
100
101 cfg.file("src/zlib/adler32.c")
102 .file("src/zlib/compress.c")
103 .file("src/zlib/crc32.c")
104 .file("src/zlib/deflate.c")
105 .file("src/zlib/infback.c")
106 .file("src/zlib/inffast.c")
107 .file("src/zlib/inflate.c")
108 .file("src/zlib/inftrees.c")
109 .file("src/zlib/trees.c")
110 .file("src/zlib/uncompr.c")
111 .file("src/zlib/zutil.c");
112
113 if !cfg!(feature = "libc") || target.starts_with("wasm32") {
114 cfg.define("Z_SOLO", None);
115 } else {
116 cfg.file("src/zlib/gzclose.c")
117 .file("src/zlib/gzlib.c")
118 .file("src/zlib/gzread.c")
119 .file("src/zlib/gzwrite.c");
120 }
121
122 if !target.contains("windows") {
123 cfg.define("STDC", None);
124 cfg.define("_LARGEFILE64_SOURCE", None);
125 cfg.define("_POSIX_SOURCE", None);
126 cfg.flag("-fvisibility=hidden");
127 }
128 if target.contains("apple") {
129 cfg.define("_C99_SOURCE", None);
130 }
131 if target.contains("solaris") {
132 cfg.define("_XOPEN_SOURCE", "700");
133 }
134
135 cfg.compile("z");
136
137 fs::create_dir_all(dst.join("include")).unwrap();
138 fs::copy("src/zlib/zlib.h", dst.join("include/zlib.h")).unwrap();
139 fs::copy("src/zlib/zconf.h", dst.join("include/zconf.h")).unwrap();
140
141 fs::create_dir_all(lib.join("pkgconfig")).unwrap();
142 fs::write(
143 lib.join("pkgconfig/zlib.pc"),
144 fs::read_to_string("src/zlib/zlib.pc.in")
145 .unwrap()
146 .replace("@prefix@", dst.to_str().unwrap()),
147 )
148 .unwrap();
149
150 println!("cargo:root={}", dst.to_str().unwrap());
151 println!("cargo:rustc-link-search=native={}", lib.to_str().unwrap());
152 println!("cargo:include={}/include", dst.to_str().unwrap());
153 }
154
155 #[cfg(not(feature = "zlib-ng"))]
build_zlib_ng(_target: &str, _compat: bool)156 fn build_zlib_ng(_target: &str, _compat: bool) {}
157
158 #[cfg(feature = "zlib-ng")]
159 mod build_zng;
160 #[cfg(feature = "zlib-ng")]
161 use build_zng::build_zlib_ng;
162
163 #[cfg(not(target_env = "msvc"))]
try_vcpkg() -> bool164 fn try_vcpkg() -> bool {
165 false
166 }
167
168 #[cfg(target_env = "msvc")]
try_vcpkg() -> bool169 fn try_vcpkg() -> bool {
170 // see if there is a vcpkg tree with zlib installed
171 match vcpkg::Config::new()
172 .emit_includes(true)
173 .lib_names("zlib", "zlib1")
174 .probe("zlib")
175 {
176 Ok(_) => true,
177 Err(e) => {
178 println!("note, vcpkg did not find zlib: {}", e);
179 false
180 }
181 }
182 }
183
zlib_installed(cfg: &mut cc::Build) -> bool184 fn zlib_installed(cfg: &mut cc::Build) -> bool {
185 let compiler = cfg.get_compiler();
186 let mut cmd = Command::new(compiler.path());
187 cmd.arg("src/smoke.c").arg("-o").arg("/dev/null").arg("-lz");
188
189 println!("running {:?}", cmd);
190 if let Ok(status) = cmd.status() {
191 if status.success() {
192 return true;
193 }
194 }
195
196 false
197 }
198