1 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4 // option. This file may not be copied, modified, or distributed
5 // except according to those terms.
7 #![deny(clippy::pedantic)]
9 #[cfg(feature = "nss")]
12 use serde_derive::Deserialize;
16 path::{Path, PathBuf},
20 const BINDINGS_DIR: &str = "bindings";
21 const BINDINGS_CONFIG: &str = "bindings.toml";
23 // This is the format of a single section of the configuration file.
24 #[derive(Deserialize)]
26 /// types that are explicitly included
29 /// functions that are explicitly included
31 functions: Vec<String>,
32 /// variables (and `#define`s) that are explicitly included
34 variables: Vec<String>,
35 /// types that should be explicitly marked as opaque
38 /// enumerations that are turned into a module (without this, the enum is
39 /// mapped using the default, which means that the individual values are
40 /// formed with an underscore as <enum_type>_<enum_value_name>).
44 /// Any item that is specifically excluded; if none of the types, functions,
45 /// or variables fields are specified, everything defined will be mapped,
46 /// so this can be used to limit that.
50 /// Whether the file is to be interpreted as C++
55 fn is_debug() -> bool {
57 .map(|d| d.parse::<bool>().unwrap_or(false))
61 // bindgen needs access to libclang.
62 // On windows, this doesn't just work, you have to set LIBCLANG_PATH.
63 // Rather than download the 400Mb+ files, like gecko does, let's just reuse their work.
65 if env::consts::OS != "windows" {
68 println!("rerun-if-env-changed=LIBCLANG_PATH");
69 println!("rerun-if-env-changed=MOZBUILD_STATE_PATH");
70 if env::var("LIBCLANG_PATH").is_ok() {
73 let mozbuild_root = if let Ok(dir) = env::var("MOZBUILD_STATE_PATH") {
74 PathBuf::from(dir.trim())
76 eprintln!("warning: Building without a gecko setup is not likely to work.");
77 eprintln!(" A working libclang is needed to build neqo.");
78 eprintln!(" Either LIBCLANG_PATH or MOZBUILD_STATE_PATH needs to be set.");
80 eprintln!(" We recommend checking out https://github.com/mozilla/gecko-dev");
81 eprintln!(" Then run `./mach bootstrap` which will retrieve clang.");
82 eprintln!(" Make sure to export MOZBUILD_STATE_PATH when building.");
85 let libclang_dir = mozbuild_root.join("clang").join("lib");
86 if libclang_dir.is_dir() {
87 env::set_var("LIBCLANG_PATH", libclang_dir.to_str().unwrap());
88 println!("rustc-env:LIBCLANG_PATH={}", libclang_dir.to_str().unwrap());
90 println!("warning: LIBCLANG_PATH isn't set; maybe run ./mach bootstrap with gecko");
94 fn nss_dir() -> Option<PathBuf> {
95 // Note that this returns a relative path because UNC
96 // paths on windows cause certain tools to explode.
97 env::var("NSS_DIR").ok().map(|dir| {
98 let dir = PathBuf::from(dir.trim());
99 assert!(dir.is_dir());
104 fn get_bash() -> PathBuf {
105 // When running under MOZILLABUILD, we need to make sure not to invoke
106 // another instance of bash that might be sitting around (like WSL).
107 match env::var("MOZILLABUILD") {
108 Ok(d) => PathBuf::from(d).join("msys").join("bin").join("bash.exe"),
109 Err(_) => PathBuf::from("bash"),
113 fn run_build_script(dir: &Path) {
114 let mut build_nss = vec![
115 String::from("./build.sh"),
116 String::from("-Ddisable_tests=1"),
117 String::from("-Denable_draft_hpke=1"),
120 build_nss.push(String::from("--static"));
122 build_nss.push(String::from("-o"));
124 if let Ok(d) = env::var("NSS_JOBS") {
125 build_nss.push(String::from("-j"));
128 let status = Command::new(get_bash())
132 .expect("couldn't start NSS build");
133 assert!(status.success(), "NSS build failed");
137 let libs = if env::consts::OS == "windows" {
138 &["nssutil3.dll", "nss3.dll"]
140 &["nssutil3", "nss3"]
142 dynamic_link_both(libs);
145 fn dynamic_link_both(extra_libs: &[&str]) {
146 let nspr_libs = if env::consts::OS == "windows" {
147 &["libplds4", "libplc4", "libnspr4"]
149 &["plds4", "plc4", "nspr4"]
151 for lib in nspr_libs.iter().chain(extra_libs) {
152 println!("cargo:rustc-link-lib=dylib={}", lib);
157 let mut static_libs = vec![
173 if env::consts::OS != "macos" {
174 static_libs.push("sqlite");
176 for lib in static_libs {
177 println!("cargo:rustc-link-lib=static={}", lib);
180 // Dynamic libs that aren't transitively included by NSS libs.
181 let mut other_libs = Vec::new();
182 if env::consts::OS != "windows" {
183 other_libs.extend_from_slice(&["pthread", "dl", "c", "z"]);
185 if env::consts::OS == "macos" {
186 other_libs.push("sqlite3");
188 dynamic_link_both(&other_libs);
191 fn get_includes(nsstarget: &Path, nssdist: &Path) -> Vec<PathBuf> {
192 let nsprinclude = nsstarget.join("include").join("nspr");
193 let nssinclude = nssdist.join("public").join("nss");
194 let includes = vec![nsprinclude, nssinclude];
196 println!("cargo:include={}", i.to_str().unwrap());
201 fn build_bindings(base: &str, bindings: &Bindings, flags: &[String]) {
202 let suffix = if bindings.cplusplus { ".hpp" } else { ".h" };
203 let header_path = PathBuf::from(BINDINGS_DIR).join(String::from(base) + suffix);
204 let header = header_path.to_str().unwrap();
205 let out = PathBuf::from(env::var("OUT_DIR").unwrap()).join(String::from(base) + ".rs");
207 println!("cargo:rerun-if-changed={}", header);
209 let mut builder = Builder::default().header(header);
210 builder = builder.generate_comments(false);
211 builder = builder.size_t_is_usize(true);
213 builder = builder.clang_arg("-v");
215 builder = builder.clang_arg("-DNO_NSPR_10_SUPPORT");
216 if env::consts::OS == "windows" {
217 builder = builder.clang_arg("-DWIN");
218 } else if env::consts::OS == "macos" {
219 builder = builder.clang_arg("-DDARWIN");
220 } else if env::consts::OS == "linux" {
221 builder = builder.clang_arg("-DLINUX");
222 } else if env::consts::OS == "android" {
223 builder = builder.clang_arg("-DLINUX");
224 builder = builder.clang_arg("-DANDROID");
226 if bindings.cplusplus {
227 builder = builder.clang_args(&["-x", "c++", "-std=c++11"]);
230 builder = builder.clang_args(flags);
232 // Apply the configuration.
233 for v in &bindings.types {
234 builder = builder.allowlist_type(v);
236 for v in &bindings.functions {
237 builder = builder.allowlist_function(v);
239 for v in &bindings.variables {
240 builder = builder.allowlist_var(v);
242 for v in &bindings.exclude {
243 builder = builder.blocklist_item(v);
245 for v in &bindings.opaque {
246 builder = builder.opaque_type(v);
248 for v in &bindings.enums {
249 builder = builder.constified_enum_module(v);
252 let bindings = builder.generate().expect("unable to generate bindings");
255 .expect("couldn't write bindings");
258 fn build_nss(nss: &Path) -> Vec<String> {
261 run_build_script(nss);
264 let nssdist = nss.parent().unwrap().join("dist");
265 println!("cargo:rerun-if-env-changed=NSS_TARGET");
266 let nsstarget = env::var("NSS_TARGET")
267 .unwrap_or_else(|_| fs::read_to_string(nssdist.join("latest")).unwrap());
268 let nsstarget = nssdist.join(nsstarget.trim());
270 let includes = get_includes(&nsstarget, &nssdist);
272 let nsslibdir = nsstarget.join("lib");
274 "cargo:rustc-link-search=native={}",
275 nsslibdir.to_str().unwrap()
283 let mut flags: Vec<String> = Vec::new();
285 flags.push(String::from("-I") + i.to_str().unwrap());
291 fn pkg_config() -> Vec<String> {
292 let modversion = Command::new("pkg-config")
293 .args(&["--modversion", "nss"])
295 .expect("pkg-config reports NSS as absent")
297 let modversion_str = String::from_utf8(modversion).expect("non-UTF8 from pkg-config");
298 let mut v = modversion_str.split('.');
302 "NSS version 3.62 or higher is needed (or set $NSS_DIR)"
304 if let Some(minor) = v.next() {
308 .expect("NSS minor version is not a number");
311 "NSS version 3.62 or higher is needed (or set $NSS_DIR)",
315 let cfg = Command::new("pkg-config")
316 .args(&["--cflags", "--libs", "nss"])
318 .expect("NSS flags not returned by pkg-config")
320 let cfg_str = String::from_utf8(cfg).expect("non-UTF8 from pkg-config");
322 let mut flags: Vec<String> = Vec::new();
323 for f in cfg_str.split(' ') {
324 if let Some(include) = f.strip_prefix("-I") {
325 flags.push(String::from(f));
326 println!("cargo:include={}", include);
327 } else if let Some(path) = f.strip_prefix("-L") {
328 println!("cargo:rustc-link-search=native={}", path);
329 } else if let Some(lib) = f.strip_prefix("-l") {
330 println!("cargo:rustc-link-lib=dylib={}", lib);
332 println!("Warning: Unknown flag from pkg-config: {}", f);
339 #[cfg(feature = "gecko")]
340 fn setup_for_gecko() -> Vec<String> {
341 use mozbuild::TOPOBJDIR;
343 let mut flags: Vec<String> = Vec::new();
345 let fold_libs = mozbuild::config::MOZ_FOLD_LIBS;
346 let libs = if fold_libs {
349 vec!["nssutil3", "nss3", "ssl3", "plds4", "plc4", "nspr4"]
353 println!("cargo:rustc-link-lib=dylib={}", lib);
358 "cargo:rustc-link-search=native={}",
359 TOPOBJDIR.join("security").to_str().unwrap()
363 "cargo:rustc-link-search=native={}",
364 TOPOBJDIR.join("dist").join("bin").to_str().unwrap()
366 let nsslib_path = TOPOBJDIR.join("security").join("nss").join("lib");
368 "cargo:rustc-link-search=native={}",
369 nsslib_path.join("nss").join("nss_nss3").to_str().unwrap()
372 "cargo:rustc-link-search=native={}",
373 nsslib_path.join("ssl").join("ssl_ssl3").to_str().unwrap()
376 "cargo:rustc-link-search=native={}",
387 let flags_path = TOPOBJDIR.join("netwerk/socket/neqo/extra-bindgen-flags");
389 println!("cargo:rerun-if-changed={}", flags_path.to_str().unwrap());
390 flags = fs::read_to_string(flags_path)
391 .expect("Failed to read extra-bindgen-flags file")
393 .map(std::borrow::ToOwned::to_owned)
396 flags.push(String::from("-include"));
401 .join("mozilla-config.h")
409 #[cfg(not(feature = "gecko"))]
410 fn setup_for_gecko() -> Vec<String> {
415 println!("cargo:rerun-if-env-changed=NSS_DIR");
416 let flags = if cfg!(feature = "gecko") {
419 nss_dir().map_or_else(pkg_config, |nss| build_nss(&nss))
422 let config_file = PathBuf::from(BINDINGS_DIR).join(BINDINGS_CONFIG);
423 println!("cargo:rerun-if-changed={}", config_file.to_str().unwrap());
424 let config = fs::read_to_string(config_file).expect("unable to read binding configuration");
425 let config: HashMap<String, Bindings> = ::toml::from_str(&config).unwrap();
427 for (k, v) in &config {
428 build_bindings(k, v, &flags[..]);
434 #[cfg(feature = "nss")]