1 #[cfg(feature = "bindgen")]
2 extern crate bindgen;
3 
4 #[cfg(feature = "pkg-config")]
5 extern crate pkg_config;
6 
7 extern crate cc;
8 
9 use std::ffi::OsStr;
10 use std::path::{Path, PathBuf};
11 use std::{env, fs};
12 
13 #[cfg(feature = "bindgen")]
generate_bindings(defs: Vec<&str>, headerpaths: Vec<PathBuf>)14 fn generate_bindings(defs: Vec<&str>, headerpaths: Vec<PathBuf>) {
15     let bindings = bindgen::Builder::default()
16         .header("zstd.h")
17         .blacklist_type("max_align_t")
18         .size_t_is_usize(true)
19         .use_core()
20         .rustified_enum(".*")
21         .clang_args(
22             headerpaths
23                 .into_iter()
24                 .map(|path| format!("-I{}", path.display())),
25         )
26         .clang_args(defs.into_iter().map(|def| format!("-D{}", def)));
27 
28     #[cfg(feature = "experimental")]
29     let bindings = bindings
30         .clang_arg("-DZSTD_STATIC_LINKING_ONLY")
31         .clang_arg("-DZDICT_STATIC_LINKING_ONLY");
32 
33     #[cfg(not(feature = "std"))]
34     let bindings = bindings.ctypes_prefix("libc");
35 
36     let bindings = bindings.generate().expect("Unable to generate bindings");
37 
38     let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
39     bindings
40         .write_to_file(out_path.join("bindings.rs"))
41         .expect("Could not write bindings");
42 }
43 
44 #[cfg(not(feature = "bindgen"))]
generate_bindings(_: Vec<&str>, _: Vec<PathBuf>)45 fn generate_bindings(_: Vec<&str>, _: Vec<PathBuf>) {}
46 
47 #[cfg(feature = "pkg-config")]
pkg_config() -> (Vec<&'static str>, Vec<PathBuf>)48 fn pkg_config() -> (Vec<&'static str>, Vec<PathBuf>) {
49     let library = pkg_config::Config::new()
50         .statik(true)
51         .cargo_metadata(!cfg!(feature = "non-cargo"))
52         .probe("libzstd")
53         .expect("Can't probe for zstd in pkg-config");
54     (vec!["PKG_CONFIG"], library.include_paths)
55 }
56 
57 #[cfg(not(feature = "pkg-config"))]
pkg_config() -> (Vec<&'static str>, Vec<PathBuf>)58 fn pkg_config() -> (Vec<&'static str>, Vec<PathBuf>) {
59     unimplemented!()
60 }
61 
62 #[cfg(not(feature = "legacy"))]
set_legacy(_config: &mut cc::Build)63 fn set_legacy(_config: &mut cc::Build) {}
64 
65 #[cfg(feature = "legacy")]
set_legacy(config: &mut cc::Build)66 fn set_legacy(config: &mut cc::Build) {
67     config.define("ZSTD_LEGACY_SUPPORT", Some("1"));
68     config.include("zstd/lib/legacy");
69 }
70 
71 #[cfg(feature = "zstdmt")]
set_pthread(config: &mut cc::Build)72 fn set_pthread(config: &mut cc::Build) {
73     config.flag("-pthread");
74 }
75 
76 #[cfg(not(feature = "zstdmt"))]
set_pthread(_config: &mut cc::Build)77 fn set_pthread(_config: &mut cc::Build) {}
78 
79 #[cfg(feature = "zstdmt")]
enable_threading(config: &mut cc::Build)80 fn enable_threading(config: &mut cc::Build) {
81     config.define("ZSTD_MULTITHREAD", Some(""));
82 }
83 
84 #[cfg(not(feature = "zstdmt"))]
enable_threading(_config: &mut cc::Build)85 fn enable_threading(_config: &mut cc::Build) {}
86 
compile_zstd()87 fn compile_zstd() {
88     let mut config = cc::Build::new();
89 
90     // Search the following directories for C files to add to the compilation.
91     for dir in &[
92         "zstd/lib/common",
93         "zstd/lib/compress",
94         "zstd/lib/decompress",
95         "zstd/lib/dictBuilder",
96         #[cfg(feature = "legacy")] "zstd/lib/legacy",
97     ] {
98         for entry in fs::read_dir(dir).unwrap() {
99             let path = entry.unwrap().path();
100             // Skip xxhash*.c files: since we are using the "PRIVATE API"
101             // mode, it will be inlined in the headers.
102             if path
103                 .file_name()
104                 .and_then(|p| p.to_str())
105                 .map_or(false, |p| p.contains("xxhash"))
106             {
107                 continue;
108             }
109             if path.extension() == Some(OsStr::new("c")) {
110                 config.file(path);
111             }
112         }
113     }
114 
115     // Some extra parameters
116     config.opt_level(3);
117     config.include("zstd/lib/");
118     config.include("zstd/lib/common");
119     config.warnings(false);
120 
121     config.define("ZSTD_LIB_DEPRECATED", Some("0"));
122 
123     #[cfg(feature = "thin")] {
124         config.define("HUF_FORCE_DECOMPRESS_X1", Some("1"));
125         config.define("ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT", Some("1"));
126         config.define("ZSTD_NO_INLINE ", Some("1"));
127         config.flag_if_supported("-flto=thin");
128         config.flag_if_supported("-Oz");
129     }
130 
131     // Hide symbols from resulting library,
132     // so we can be used with another zstd-linking lib.
133     // See https://github.com/gyscos/zstd-rs/issues/58
134     config.flag("-fvisibility=hidden");
135     config.define("XXH_PRIVATE_API", Some(""));
136     config.define("ZSTDLIB_VISIBILITY", Some(""));
137     config.define("ZDICTLIB_VISIBILITY", Some(""));
138     config.define("ZSTDERRORLIB_VISIBILITY", Some(""));
139 
140     // https://github.com/facebook/zstd/blob/d69d08ed6c83563b57d98132e1e3f2487880781e/lib/common/debug.h#L60
141     /* recommended values for DEBUGLEVEL :
142      * 0 : release mode, no debug, all run-time checks disabled
143      * 1 : enables assert() only, no display
144      * 2 : reserved, for currently active debug path
145      * 3 : events once per object lifetime (CCtx, CDict, etc.)
146      * 4 : events once per frame
147      * 5 : events once per block
148      * 6 : events once per sequence (verbose)
149      * 7+: events at every position (*very* verbose)
150      */
151     #[cfg(feature = "debug")]
152     config.define("DEBUGLEVEL", Some("5"));
153 
154     set_pthread(&mut config);
155     set_legacy(&mut config);
156     enable_threading(&mut config);
157 
158     // Compile!
159     config.compile("libzstd.a");
160 
161     let src = env::current_dir().unwrap().join("zstd").join("lib");
162     let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
163     let include = dst.join("include");
164     fs::create_dir_all(&include).unwrap();
165     fs::copy(src.join("zstd.h"), include.join("zstd.h")).unwrap();
166     fs::copy(
167         src.join("common").join("zstd_errors.h"),
168         include.join("zstd_errors.h"),
169     )
170     .unwrap();
171     fs::copy(
172         src.join("dictBuilder").join("zdict.h"),
173         include.join("zdict.h"),
174     )
175     .unwrap();
176     println!("cargo:root={}", dst.display());
177 }
178 
main()179 fn main() {
180     let target_arch =
181         std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
182     let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
183 
184     if target_arch == "wasm32" || target_os == "hermit" {
185         println!("cargo:rustc-cfg=feature=\"std\"");
186     }
187 
188     // println!("cargo:rustc-link-lib=zstd");
189     let (defs, headerpaths) = if cfg!(feature = "pkg-config") {
190         pkg_config()
191     } else {
192         if !Path::new("zstd/lib").exists() {
193             panic!("Folder 'zstd/lib' does not exists. Maybe you forgot to clone the 'zstd' submodule?");
194         }
195 
196         let manifest_dir = PathBuf::from(
197             env::var("CARGO_MANIFEST_DIR")
198                 .expect("Manifest dir is always set by cargo"),
199         );
200 
201         compile_zstd();
202         (vec![], vec![manifest_dir.join("zstd/lib")])
203     };
204 
205     let includes: Vec<_> = headerpaths
206         .iter()
207         .map(|p| p.display().to_string())
208         .collect();
209     println!("cargo:include={}", includes.join(";"));
210 
211     generate_bindings(defs, headerpaths);
212 }
213