1 use std::env;
2 use std::fs;
3 use std::path::PathBuf;
4
main()5 fn main() {
6 println!("cargo:rerun-if-env-changed=LIBZ_SYS_STATIC");
7 println!("cargo:rerun-if-changed=build.rs");
8 println!("cargo:rerun-if-changed=zng/cmake.rs");
9 println!("cargo:rerun-if-changed=zng/cc.rs");
10
11 let host = env::var("HOST").unwrap();
12 let target = env::var("TARGET").unwrap();
13
14 let host_and_target_contain = |s| host.contains(s) && target.contains(s);
15
16 let want_ng = cfg!(any(
17 feature = "zlib-ng",
18 feature = "zlib-ng-no-cmake-experimental-community-maintained"
19 )) && !cfg!(feature = "stock-zlib");
20
21 if want_ng && target != "wasm32-unknown-unknown" {
22 return build_zlib_ng(&target, true);
23 }
24
25 // All android compilers should come with libz by default, so let's just use
26 // the one already there. Likewise, Haiku and OpenHarmony always ship with libz,
27 // so we can link to it even when cross-compiling.
28 if target.contains("android") || target.contains("haiku") || target.ends_with("-ohos") {
29 println!("cargo:rustc-link-lib=z");
30 return;
31 }
32
33 // Don't run pkg-config if we're linking statically (we'll build below) and
34 // also don't run pkg-config on FreeBSD/DragonFly. That'll end up printing
35 // `-L /usr/lib` which wreaks havoc with linking to an OpenSSL in /usr/local/lib
36 // (Ports, etc.)
37 let want_static =
38 cfg!(feature = "static") || env::var("LIBZ_SYS_STATIC").unwrap_or(String::new()) == "1";
39 if !want_static &&
40 !target.contains("msvc") && // pkg-config just never works here
41 !(host_and_target_contain("freebsd") ||
42 host_and_target_contain("dragonfly"))
43 {
44 // Don't print system lib dirs to cargo since this interferes with other
45 // packages adding non-system search paths to link against libraries
46 // that are also found in a system-wide lib dir.
47 let zlib = pkg_config::Config::new()
48 .cargo_metadata(true)
49 .print_system_libs(false)
50 .probe("zlib");
51 match zlib {
52 Ok(zlib) => {
53 if !zlib.include_paths.is_empty() {
54 let paths = zlib
55 .include_paths
56 .iter()
57 .map(|s| s.display().to_string())
58 .collect::<Vec<_>>();
59 println!("cargo:include={}", paths.join(","));
60 }
61 }
62 Err(e) => {
63 println!("cargo:warning=Could not find zlib include paths via pkg-config: {}", e)
64 }
65 }
66 }
67
68 if target.contains("windows") {
69 if try_vcpkg() {
70 return;
71 }
72 }
73
74 let mut cfg = cc::Build::new();
75
76 // Situations where we build unconditionally.
77 //
78 // MSVC basically never has it preinstalled, MinGW picks up a bunch of weird
79 // paths we don't like, `want_static` may force us, and cross compiling almost
80 // never has a prebuilt version.
81 //
82 // Apple platforms have libz.1.dylib, and it's usually available even when
83 // cross compiling (via fat binary or in the target's Xcode SDK)
84 let cross_compiling = target != host;
85 if target.contains("msvc")
86 || target.contains("pc-windows-gnu")
87 || want_static
88 || (cross_compiling && !target.contains("-apple-"))
89 {
90 return build_zlib(&mut cfg, &target);
91 }
92
93 // If we've gotten this far we're probably a pretty standard platform.
94 // Almost all platforms here ship libz by default, but some don't have
95 // pkg-config files that we would find above.
96 //
97 // In any case test if zlib is actually installed and if so we link to it,
98 // otherwise continue below to build things.
99 if zlib_installed(&mut cfg) {
100 println!("cargo:rustc-link-lib=z");
101 return;
102 }
103
104 build_zlib(&mut cfg, &target)
105 }
106
build_zlib(cfg: &mut cc::Build, target: &str)107 fn build_zlib(cfg: &mut cc::Build, target: &str) {
108 let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
109 let lib = dst.join("lib");
110
111 cfg.warnings(false).out_dir(&lib).include("src/zlib");
112
113 cfg.file("src/zlib/adler32.c")
114 .file("src/zlib/compress.c")
115 .file("src/zlib/crc32.c")
116 .file("src/zlib/deflate.c")
117 .file("src/zlib/infback.c")
118 .file("src/zlib/inffast.c")
119 .file("src/zlib/inflate.c")
120 .file("src/zlib/inftrees.c")
121 .file("src/zlib/trees.c")
122 .file("src/zlib/uncompr.c")
123 .file("src/zlib/zutil.c");
124
125 if !cfg!(feature = "libc") || target.starts_with("wasm32") {
126 cfg.define("Z_SOLO", None);
127 } else {
128 cfg.file("src/zlib/gzclose.c")
129 .file("src/zlib/gzlib.c")
130 .file("src/zlib/gzread.c")
131 .file("src/zlib/gzwrite.c");
132 }
133
134 if !target.contains("windows") {
135 cfg.define("STDC", None);
136 cfg.define("_LARGEFILE64_SOURCE", None);
137 cfg.define("_POSIX_SOURCE", None);
138 cfg.flag("-fvisibility=hidden");
139 }
140 if target.contains("apple") {
141 cfg.define("_C99_SOURCE", None);
142 }
143 if target.contains("solaris") {
144 cfg.define("_XOPEN_SOURCE", "700");
145 }
146
147 cfg.compile("z");
148
149 fs::create_dir_all(dst.join("include")).unwrap();
150 fs::copy("src/zlib/zlib.h", dst.join("include/zlib.h")).unwrap();
151 fs::copy("src/zlib/zconf.h", dst.join("include/zconf.h")).unwrap();
152
153 fs::create_dir_all(lib.join("pkgconfig")).unwrap();
154 let zlib_h = fs::read_to_string(dst.join("include/zlib.h")).unwrap();
155 let version = zlib_h
156 .lines()
157 .find(|l| l.contains("ZLIB_VERSION"))
158 .unwrap()
159 .split("\"")
160 .nth(1)
161 .unwrap();
162 fs::write(
163 lib.join("pkgconfig/zlib.pc"),
164 fs::read_to_string("src/zlib/zlib.pc.in")
165 .unwrap()
166 .replace("@prefix@", dst.to_str().unwrap())
167 .replace("@includedir@", "${prefix}/include")
168 .replace("@libdir@", "${prefix}/lib")
169 .replace("@VERSION@", version),
170 )
171 .unwrap();
172
173 println!("cargo:root={}", dst.to_str().unwrap());
174 println!("cargo:rustc-link-search=native={}", lib.to_str().unwrap());
175 println!("cargo:include={}/include", dst.to_str().unwrap());
176 }
177
178 #[cfg(any(
179 feature = "zlib-ng",
180 feature = "zlib-ng-no-cmake-experimental-community-maintained"
181 ))]
182 mod zng {
183 #[cfg_attr(feature = "zlib-ng", path = "cmake.rs")]
184 #[cfg_attr(
185 all(
186 feature = "zlib-ng-no-cmake-experimental-community-maintained",
187 not(feature = "zlib-ng")
188 ),
189 path = "cc.rs"
190 )]
191 mod build_zng;
192
193 pub(super) use build_zng::build_zlib_ng;
194 }
195
build_zlib_ng(_target: &str, _compat: bool)196 fn build_zlib_ng(_target: &str, _compat: bool) {
197 #[cfg(any(
198 feature = "zlib-ng",
199 feature = "zlib-ng-no-cmake-experimental-community-maintained"
200 ))]
201 zng::build_zlib_ng(_target, _compat);
202 }
203
try_vcpkg() -> bool204 fn try_vcpkg() -> bool {
205 // see if there is a vcpkg tree with zlib installed
206 match vcpkg::Config::new()
207 .emit_includes(true)
208 .find_package("zlib")
209 {
210 Ok(zlib) => {
211 if !zlib.include_paths.is_empty() {
212 let paths = zlib
213 .include_paths
214 .iter()
215 .map(|s| s.display().to_string())
216 .collect::<Vec<_>>();
217 println!("cargo:include={}", paths.join(","));
218 }
219 true
220 }
221 Err(e) => {
222 println!("note, vcpkg did not find zlib: {}", e);
223 false
224 }
225 }
226 }
227
zlib_installed(cfg: &mut cc::Build) -> bool228 fn zlib_installed(cfg: &mut cc::Build) -> bool {
229 let mut cmd = cfg.get_compiler().to_command();
230 cmd.arg("src/smoke.c")
231 .arg("-g0")
232 .arg("-o")
233 .arg("/dev/null")
234 .arg("-lz");
235
236 println!("running {:?}", cmd);
237 if let Ok(status) = cmd.status() {
238 if status.success() {
239 return true;
240 }
241 }
242
243 false
244 }
245