diff options
Diffstat (limited to 'third_party/rust/nss-gk-api/build.rs')
-rw-r--r-- | third_party/rust/nss-gk-api/build.rs | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/third_party/rust/nss-gk-api/build.rs b/third_party/rust/nss-gk-api/build.rs new file mode 100644 index 0000000000..ab1892936b --- /dev/null +++ b/third_party/rust/nss-gk-api/build.rs @@ -0,0 +1,445 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(clippy::pedantic)] + +use bindgen::Builder; +use serde_derive::Deserialize; +use std::collections::HashMap; +use std::collections::HashSet; +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; + +const BINDINGS_DIR: &str = "bindings"; +const BINDINGS_CONFIG: &str = "bindings.toml"; + +// This is the format of a single section of the configuration file. +#[derive(Deserialize)] +struct Bindings { + /// types that are explicitly included + #[serde(default)] + types: Vec<String>, + /// functions that are explicitly included + #[serde(default)] + functions: Vec<String>, + /// variables (and `#define`s) that are explicitly included + #[serde(default)] + variables: Vec<String>, + /// types that should be explicitly marked as opaque + #[serde(default)] + opaque: Vec<String>, + /// enumerations that are turned into a module (without this, the enum is + /// mapped using the default, which means that the individual values are + /// formed with an underscore as <enum_type>_<enum_value_name>). + #[serde(default)] + enums: Vec<String>, + + /// Any item that is specifically excluded; if none of the types, functions, + /// or variables fields are specified, everything defined will be mapped, + /// so this can be used to limit that. + #[serde(default)] + exclude: Vec<String>, + + /// Whether the file is to be interpreted as C++ + #[serde(default)] + cplusplus: bool, +} + +fn is_debug() -> bool { + env::var("DEBUG") + .map(|d| d.parse::<bool>().unwrap_or(false)) + .unwrap_or(false) +} + +// bindgen needs access to libclang. +// On windows, this doesn't just work, you have to set LIBCLANG_PATH. +// Rather than download the 400Mb+ files, like gecko does, let's just reuse their work. +fn setup_clang() { + if env::consts::OS != "windows" { + return; + } + println!("rerun-if-env-changed=LIBCLANG_PATH"); + println!("rerun-if-env-changed=MOZBUILD_STATE_PATH"); + if env::var("LIBCLANG_PATH").is_ok() { + return; + } + let mozbuild_root = if let Ok(dir) = env::var("MOZBUILD_STATE_PATH") { + PathBuf::from(dir.trim()) + } else { + eprintln!("warning: Building without a gecko setup is not likely to work."); + eprintln!(" A working libclang is needed to build nss-sys."); + eprintln!(" Either LIBCLANG_PATH or MOZBUILD_STATE_PATH needs to be set."); + eprintln!(); + eprintln!(" We recommend checking out https://github.com/mozilla/gecko-dev"); + eprintln!(" Then run `./mach bootstrap` which will retrieve clang."); + eprintln!(" Make sure to export MOZBUILD_STATE_PATH when building."); + return; + }; + let libclang_dir = mozbuild_root.join("clang").join("lib"); + if libclang_dir.is_dir() { + env::set_var("LIBCLANG_PATH", libclang_dir.to_str().unwrap()); + println!("rustc-env:LIBCLANG_PATH={}", libclang_dir.to_str().unwrap()); + } else { + println!("warning: LIBCLANG_PATH isn't set; maybe run ./mach bootstrap with gecko"); + } +} + +fn nss_dir() -> PathBuf { + let dir = if let Ok(dir) = env::var("NSS_DIR") { + let path = PathBuf::from(dir.trim()); + assert!( + !path.is_relative(), + "The NSS_DIR environment variable is expected to be an absolute path." + ); + path + } else { + let out_dir = env::var("OUT_DIR").unwrap(); + let dir = Path::new(&out_dir).join("nss"); + if !dir.exists() { + Command::new("hg") + .args(&[ + "clone", + "https://hg.mozilla.org/projects/nss", + dir.to_str().unwrap(), + ]) + .status() + .expect("can't clone nss"); + } + let nspr_dir = Path::new(&out_dir).join("nspr"); + if !nspr_dir.exists() { + Command::new("hg") + .args(&[ + "clone", + "https://hg.mozilla.org/projects/nspr", + nspr_dir.to_str().unwrap(), + ]) + .status() + .expect("can't clone nspr"); + } + dir + }; + assert!(dir.is_dir(), "NSS_DIR {:?} doesn't exist", dir); + // Note that this returns a relative path because UNC + // paths on windows cause certain tools to explode. + dir +} + +fn get_bash() -> PathBuf { + // When running under MOZILLABUILD, we need to make sure not to invoke + // another instance of bash that might be sitting around (like WSL). + match env::var("MOZILLABUILD") { + Ok(d) => PathBuf::from(d).join("msys").join("bin").join("bash.exe"), + Err(_) => PathBuf::from("bash"), + } +} + +fn build_nss(dir: PathBuf) { + let mut build_nss = vec![ + String::from("./build.sh"), + String::from("-Ddisable_tests=1"), + ]; + if is_debug() { + build_nss.push(String::from("--static")); + } else { + build_nss.push(String::from("-o")); + } + if let Ok(d) = env::var("NSS_JOBS") { + build_nss.push(String::from("-j")); + build_nss.push(d); + } + let status = Command::new(get_bash()) + .args(build_nss) + .current_dir(dir) + .status() + .expect("couldn't start NSS build"); + assert!(status.success(), "NSS build failed"); +} + +fn dynamic_link() { + let libs = if env::consts::OS == "windows" { + &["nssutil3.dll", "nss3.dll", "ssl3.dll"] + } else { + &["nssutil3", "nss3", "ssl3"] + }; + dynamic_link_both(libs); +} + +fn dynamic_link_both(extra_libs: &[&str]) { + let nspr_libs = if env::consts::OS == "windows" { + &["libplds4", "libplc4", "libnspr4"] + } else { + &["plds4", "plc4", "nspr4"] + }; + for lib in nspr_libs.iter().chain(extra_libs) { + println!("cargo:rustc-link-lib=dylib={}", lib); + } +} + +fn static_link() { + let mut static_libs = vec![ + "certdb", + "certhi", + "cryptohi", + "freebl", + "nss_static", + "nssb", + "nssdev", + "nsspki", + "nssutil", + "pk11wrap", + "pkcs12", + "pkcs7", + "smime", + "softokn_static", + "ssl", + ]; + if env::consts::OS != "macos" { + static_libs.push("sqlite"); + } + for lib in static_libs { + println!("cargo:rustc-link-lib=static={}", lib); + } + + // Dynamic libs that aren't transitively included by NSS libs. + let mut other_libs = Vec::new(); + if env::consts::OS != "windows" { + other_libs.extend_from_slice(&["pthread", "dl", "c", "z"]); + } + if env::consts::OS == "macos" { + other_libs.push("sqlite3"); + } + dynamic_link_both(&other_libs); +} + +fn get_includes(nsstarget: &Path, nssdist: &Path) -> Vec<PathBuf> { + let nsprinclude = nsstarget.join("include").join("nspr"); + let nssinclude = nssdist.join("public").join("nss"); + let includes = vec![nsprinclude, nssinclude]; + for i in &includes { + println!("cargo:include={}", i.to_str().unwrap()); + } + includes +} + +fn build_bindings(base: &str, bindings: &Bindings, flags: &[String], gecko: bool) { + let suffix = if bindings.cplusplus { ".hpp" } else { ".h" }; + let header_path = PathBuf::from(BINDINGS_DIR).join(String::from(base) + suffix); + let header = header_path.to_str().unwrap(); + let out = PathBuf::from(env::var("OUT_DIR").unwrap()).join(String::from(base) + ".rs"); + + println!("cargo:rerun-if-changed={}", header); + + let mut builder = Builder::default().header(header); + builder = builder.generate_comments(false); + builder = builder.size_t_is_usize(true); + + builder = builder.clang_arg("-v"); + + if !gecko { + builder = builder.clang_arg("-DNO_NSPR_10_SUPPORT"); + if env::consts::OS == "windows" { + builder = builder.clang_arg("-DWIN"); + } else if env::consts::OS == "macos" { + builder = builder.clang_arg("-DDARWIN"); + } else if env::consts::OS == "linux" { + builder = builder.clang_arg("-DLINUX"); + } else if env::consts::OS == "android" { + builder = builder.clang_arg("-DLINUX"); + builder = builder.clang_arg("-DANDROID"); + } + if bindings.cplusplus { + builder = builder.clang_args(&["-x", "c++", "-std=c++11"]); + } + } + + builder = builder.clang_args(flags); + + // Apply the configuration. + for v in &bindings.types { + builder = builder.allowlist_type(v); + } + for v in &bindings.functions { + builder = builder.allowlist_function(v); + } + for v in &bindings.variables { + builder = builder.allowlist_var(v); + } + for v in &bindings.exclude { + builder = builder.blocklist_item(v); + } + for v in &bindings.opaque { + builder = builder.opaque_type(v); + } + for v in &bindings.enums { + builder = builder.constified_enum_module(v); + } + + let bindings = builder.generate().expect("unable to generate bindings"); + bindings + .write_to_file(out) + .expect("couldn't write bindings"); +} + +fn setup_standalone() -> Vec<String> { + setup_clang(); + + let nss = nss_dir(); + println!("cargo:rerun-if-env-changed={}", nss.display()); + + build_nss(nss.clone()); + + // $NSS_DIR/../dist/ + let nssdist = nss.parent().unwrap().join("dist"); + println!("cargo:rerun-if-env-changed={}", nssdist.display()); + + let nsstarget = env::var("NSS_TARGET") + .unwrap_or_else(|_| fs::read_to_string(nssdist.join("latest")).unwrap()); + let nsstarget = nssdist.join(nsstarget.trim()); + + let includes = get_includes(&nsstarget, &nssdist); + + let nsslibdir = nsstarget.join("lib"); + println!( + "cargo:rustc-link-search=native={}", + nsslibdir.to_str().unwrap() + ); + if is_debug() { + static_link(); + } else { + dynamic_link(); + } + + let mut flags: Vec<String> = Vec::new(); + for i in includes { + flags.push(String::from("-I") + i.to_str().unwrap()); + } + + flags +} + +#[cfg(feature = "gecko")] +fn setup_for_gecko() -> Vec<String> { + use mozbuild::TOPOBJDIR; + + let fold_libs = mozbuild::config::MOZ_FOLD_LIBS; + let libs = if fold_libs { + vec!["nss3"] + } else { + vec!["nssutil3", "nss3", "ssl3", "plds4", "plc4", "nspr4"] + }; + + for lib in &libs { + println!("cargo:rustc-link-lib=dylib={}", lib); + } + + if fold_libs { + println!( + "cargo:rustc-link-search=native={}", + TOPOBJDIR.join("security").to_str().unwrap() + ); + } else { + println!( + "cargo:rustc-link-search=native={}", + TOPOBJDIR.join("dist").join("bin").to_str().unwrap() + ); + let nsslib_path = TOPOBJDIR.join("security").join("nss").join("lib"); + println!( + "cargo:rustc-link-search=native={}", + nsslib_path.join("nss").join("nss_nss3").to_str().unwrap() + ); + println!( + "cargo:rustc-link-search=native={}", + nsslib_path.join("ssl").join("ssl_ssl3").to_str().unwrap() + ); + println!( + "cargo:rustc-link-search=native={}", + TOPOBJDIR + .join("config") + .join("external") + .join("nspr") + .join("pr") + .to_str() + .unwrap() + ); + } + + let flags_path = TOPOBJDIR.join("netwerk/socket/neqo/extra-bindgen-flags"); + + println!("cargo:rerun-if-changed={}", flags_path.to_str().unwrap()); + let mut flags = fs::read_to_string(flags_path) + .expect("Failed to read extra-bindgen-flags file") + .split_whitespace() + .map(std::borrow::ToOwned::to_owned) + .collect::<Vec<String>>(); + + flags.push(String::from("-include")); + flags.push( + TOPOBJDIR + .join("dist") + .join("include") + .join("mozilla-config.h") + .to_str() + .unwrap() + .to_string(), + ); + flags +} + +#[cfg(not(feature = "gecko"))] +fn setup_for_gecko() -> Vec<String> { + unreachable!() +} + +fn process_config(config: &mut HashMap<String, Bindings>) { + let mut excludes = HashMap::new(); + for header in config.keys().cloned() { + // Collect the list of types, functions, and variables configured + // for generation in any other configured header, and add it to the list + // of items excluded from generation in this header. This ensures that + // each item only appears in one bindings module, which prevents some + // type conflicts. (However, it does mean that appropriate `use` + // declarations must be added for the generated modules.) + excludes.insert( + header.clone(), + config.iter().flat_map(|(h, b)| { + if *h != header { + vec![ + &b.types, + &b.functions, + &b.variables, + ] + } else { + vec![] + }.into_iter().flat_map(|v| v.iter()).cloned() + }).collect::<HashSet<String>>() + ); + } + + for (header, excludes) in excludes.into_iter() { + config.get_mut(&header).expect("key disappeared from config?").exclude.extend(excludes.into_iter()); + } +} + +fn main() { + let flags = if cfg!(feature = "gecko") { + setup_for_gecko() + } else { + setup_standalone() + }; + + let config_file = PathBuf::from(BINDINGS_DIR).join(BINDINGS_CONFIG); + println!("cargo:rerun-if-changed={}", config_file.to_str().unwrap()); + let config = fs::read_to_string(config_file).expect("unable to read binding configuration"); + let mut config: HashMap<String, Bindings> = ::toml::from_str(&config).unwrap(); + process_config(&mut config); + + for (k, v) in &config { + build_bindings(k, v, &flags[..], cfg!(feature = "gecko")); + } +} |