• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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