1 extern crate pkg_config;
2 #[cfg(target_env = "msvc")]
3 extern crate vcpkg;
4 extern crate cc;
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 let wants_asm = cfg!(feature = "asm");
17
18 let host_and_target_contain = |s| host.contains(s) && target.contains(s);
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 !wants_asm &&
27 !want_static &&
28 !target.contains("msvc") && // pkg-config just never works here
29 !(host_and_target_contain("apple") ||
30 host_and_target_contain("freebsd") ||
31 host_and_target_contain("dragonfly")) &&
32 pkg_config::Config::new().cargo_metadata(true).probe("zlib").is_ok() {
33 return
34 }
35
36 if target.contains("msvc") {
37 if !wants_asm && try_vcpkg() {
38 return;
39 }
40 }
41
42 // All android compilers should come with libz by default, so let's just use
43 // the one already there.
44 if !wants_asm && target.contains("android") {
45 println!("cargo:rustc-link-lib=z");
46 return
47 }
48
49 let mut cfg = cc::Build::new();
50
51 // Whitelist a bunch of situations where we build unconditionally.
52 //
53 // MSVC basically never has it preinstalled, MinGW picks up a bunch of weird
54 // paths we don't like, `want_static` may force us, cross compiling almost
55 // never has a prebuilt version, and musl is almost always static.
56 if wants_asm ||
57 target.contains("msvc") ||
58 target.contains("pc-windows-gnu") ||
59 want_static ||
60 target != host ||
61 target.contains("musl")
62 {
63 return build_zlib(&mut cfg, &target);
64 }
65
66 // If we've gotten this far we're probably a pretty standard platform.
67 // Almost all platforms here ship libz by default, but some don't have
68 // pkg-config files that we would find above.
69 //
70 // In any case test if zlib is actually installed and if so we link to it,
71 // otherwise continue below to build things.
72 if zlib_installed(&mut cfg) {
73 println!("cargo:rustc-link-lib=z");
74 return
75 }
76
77 build_zlib(&mut cfg, &target)
78 }
79
build_zlib(cfg: &mut cc::Build, target: &str)80 fn build_zlib(cfg: &mut cc::Build, target: &str) {
81 let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
82 let build = dst.join("build");
83 let asm = cfg!(feature = "asm");
84
85 cfg.warnings(false)
86 .out_dir(&build)
87 .include("src/zlib");
88
89 cfg.file("src/zlib/adler32.c")
90 .file("src/zlib/compress.c")
91 .file("src/zlib/crc32.c")
92 .file("src/zlib/deflate.c")
93 .file("src/zlib/gzclose.c")
94 .file("src/zlib/gzlib.c")
95 .file("src/zlib/gzread.c")
96 .file("src/zlib/gzwrite.c")
97 .file("src/zlib/infback.c")
98 .file("src/zlib/inffast.c")
99 .file("src/zlib/inflate.c")
100 .file("src/zlib/inftrees.c")
101 .file("src/zlib/trees.c")
102 .file("src/zlib/uncompr.c")
103 .file("src/zlib/zutil.c");
104 if !target.contains("windows") {
105 cfg.define("STDC", None);
106 cfg.define("_LARGEFILE64_SOURCE", None);
107 cfg.define("_POSIX_SOURCE", None);
108 cfg.flag("-fvisibility=hidden");
109 }
110 if target.contains("ios") {
111 cfg.define("_C99_SOURCE", None);
112 }
113 if target.contains("solaris") {
114 cfg.define("_XOPEN_SOURCE", "700");
115 }
116
117 if asm {
118 if target.contains("windows-msvc") {
119 if target.starts_with("x86_64") {
120 cfg.file("src/zlib/contrib/masmx64/inffasx64.asm")
121 .file("src/zlib/contrib/masmx64/gvmat64.asm")
122 .define("ASMV", None)
123 .define("ASMINF", None);
124 } else if target.starts_with("i686") {
125 cfg.file("src/zlib/contrib/masmx86/inffas32.asm")
126 .file("src/zlib/contrib/masmx86/match686.asm")
127 .define("ASMV", None)
128 .define("ASMINF", None);
129 }
130 } else {
131 if target.starts_with("x86_64") {
132 cfg.file("src/zlib/contrib/amd64/amd64-match.S")
133 .define("ASMV", None);
134 } else if target.starts_with("i686") {
135 cfg.file("src/zlib/contrib/inflate86/inffast.S")
136 .define("ASMINF", None);
137 }
138 }
139 }
140
141 cfg.compile("z");
142
143 fs::create_dir_all(dst.join("lib/pkgconfig")).unwrap();
144 fs::create_dir_all(dst.join("include")).unwrap();
145 fs::copy("src/zlib/zlib.h", dst.join("include/zlib.h")).unwrap();
146 fs::copy("src/zlib/zconf.h", dst.join("include/zconf.h")).unwrap();
147
148 fs::write(
149 dst.join("lib/pkgconfig/zlib.pc"),
150 fs::read_to_string("src/zlib/zlib.pc.in")
151 .unwrap()
152 .replace("@prefix@", dst.to_str().unwrap()),
153 ).unwrap();
154
155 println!("cargo:root={}", dst.to_str().unwrap());
156 println!("cargo:include={}/include", dst.to_str().unwrap());
157 }
158
159 #[cfg(not(target_env = "msvc"))]
try_vcpkg() -> bool160 fn try_vcpkg() -> bool {
161 false
162 }
163
164 #[cfg(target_env = "msvc")]
try_vcpkg() -> bool165 fn try_vcpkg() -> bool {
166 // see if there is a vcpkg tree with zlib installed
167 match vcpkg::Config::new()
168 .emit_includes(true)
169 .lib_names("zlib", "zlib1")
170 .probe("zlib") {
171 Ok(_) => { true },
172 Err(e) => {
173 println!("note, vcpkg did not find zlib: {}", e);
174 false
175 },
176 }
177 }
178
zlib_installed(cfg: &mut cc::Build) -> bool179 fn zlib_installed(cfg: &mut cc::Build) -> bool {
180 let compiler = cfg.get_compiler();
181 let mut cmd = Command::new(compiler.path());
182 cmd.arg("src/smoke.c")
183 .arg("-o").arg("/dev/null")
184 .arg("-lz");
185
186 println!("running {:?}", cmd);
187 if let Ok(status) = cmd.status() {
188 if status.success() {
189 return true
190 }
191 }
192
193 false
194 }
195