• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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. Likewise, Haiku always ships with libz, so we can
57     // link to it even when cross-compiling.
58     if target.contains("android") || target.contains("haiku") {
59         println!("cargo:rustc-link-lib=z");
60         return;
61     }
62 
63     let mut cfg = cc::Build::new();
64 
65     // Situations where we build unconditionally.
66     //
67     // MSVC basically never has it preinstalled, MinGW picks up a bunch of weird
68     // paths we don't like, `want_static` may force us, cross compiling almost
69     // never has a prebuilt version, and musl is almost always static.
70     if target.contains("msvc")
71         || target.contains("pc-windows-gnu")
72         || want_static
73         || target != host
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 lib = dst.join("lib");
95 
96     cfg.warnings(false).out_dir(&lib).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.starts_with("wasm32") {
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("include")).unwrap();
135     fs::copy("src/zlib/zlib.h", dst.join("include/zlib.h")).unwrap();
136     fs::copy("src/zlib/zconf.h", dst.join("include/zconf.h")).unwrap();
137 
138     fs::create_dir_all(lib.join("pkgconfig")).unwrap();
139     fs::write(
140         lib.join("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:rustc-link-search=native={}", lib.to_str().unwrap());
149     println!("cargo:include={}/include", dst.to_str().unwrap());
150 }
151 
152 #[cfg(not(feature = "zlib-ng"))]
build_zlib_ng(_target: &str)153 fn build_zlib_ng(_target: &str) {}
154 
155 #[cfg(feature = "zlib-ng")]
build_zlib_ng(target: &str)156 fn build_zlib_ng(target: &str) {
157     let install_dir = cmake::Config::new("src/zlib-ng")
158         .define("BUILD_SHARED_LIBS", "OFF")
159         .define("ZLIB_COMPAT", "ON")
160         .define("WITH_GZFILEOP", "ON")
161         .build();
162     let includedir = install_dir.join("include");
163     let libdir = install_dir.join("lib");
164     println!(
165         "cargo:rustc-link-search=native={}",
166         libdir.to_str().unwrap()
167     );
168     let libname = if target.contains("windows") {
169         if target.contains("msvc") && env::var("OPT_LEVEL").unwrap() == "0" {
170             "zlibd"
171         } else {
172             "zlib"
173         }
174     } else {
175         "z"
176     };
177     println!("cargo:rustc-link-lib=static={}", libname);
178     println!("cargo:root={}", install_dir.to_str().unwrap());
179     println!("cargo:include={}", includedir.to_str().unwrap());
180 }
181 
182 #[cfg(not(target_env = "msvc"))]
try_vcpkg() -> bool183 fn try_vcpkg() -> bool {
184     false
185 }
186 
187 #[cfg(target_env = "msvc")]
try_vcpkg() -> bool188 fn try_vcpkg() -> bool {
189     // see if there is a vcpkg tree with zlib installed
190     match vcpkg::Config::new()
191         .emit_includes(true)
192         .lib_names("zlib", "zlib1")
193         .probe("zlib")
194     {
195         Ok(_) => true,
196         Err(e) => {
197             println!("note, vcpkg did not find zlib: {}", e);
198             false
199         }
200     }
201 }
202 
zlib_installed(cfg: &mut cc::Build) -> bool203 fn zlib_installed(cfg: &mut cc::Build) -> bool {
204     let compiler = cfg.get_compiler();
205     let mut cmd = Command::new(compiler.path());
206     cmd.arg("src/smoke.c").arg("-o").arg("/dev/null").arg("-lz");
207 
208     println!("running {:?}", cmd);
209     if let Ok(status) = cmd.status() {
210         if status.success() {
211             return true;
212         }
213     }
214 
215     false
216 }
217