// 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,
}
// 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. // On macOS, clang-sys prefers the highest-versioned libclang it can find, which may be a // Homebrew LLVM that doesn't have the correct macOS SDK include paths, resulting in broken // bindings. Force use of Xcode's libclang instead.
fn setup_clang() {
println!("cargo:rerun-if-env-changed=LIBCLANG_PATH");
println!("cargo:rerun-if-env-changed=CI"); // In CI, the environment is already configured correctly. if env::var("CI").is_ok() { return;
} if env::var("LIBCLANG_PATH").is_ok() { return;
} if env::consts::OS == "macos" { iflet Ok(output) = Command::new("xcode-select").arg("--print-path").output() { if output.status.success() { let xcode_path = String::from_utf8_lossy(&output.stdout).trim().to_string(); let candidates = [
PathBuf::from(&xcode_path).join("Toolchains/XcodeDefault.xctoolchain/usr/lib"),
PathBuf::from(&xcode_path).join("usr/lib"),
]; iflet Some(libclang_dir) = candidates.iter().find(|p| p.is_dir()) { unsafe {
env::set_var("LIBCLANG_PATH", libclang_dir.to_str().unwrap());
}
} else {
println!( "cargo:warning=Xcode toolchain libclang not found at {}; set LIBCLANG_PATH if build fails",
candidates[0].display()
);
}
} else {
println!( "cargo:warning=xcode-select returned an error; set LIBCLANG_PATH if build fails"
);
}
} else {
println!("cargo:warning=xcode-select not found; set LIBCLANG_PATH if build fails");
}
} elseif env::consts::OS == "windows" {
println!("cargo:rerun-if-env-changed=MOZBUILD_STATE_PATH"); let mozbuild_root = iflet Ok(dir) = env::var("MOZBUILD_STATE_PATH") {
PathBuf::from(dir.trim())
} else {
println!("cargo:warning=Building without a gecko setup is not likely to work.");
println!("cargo:warning=A working libclang is needed to build nss-rs.");
println!("cargo:warning=Either LIBCLANG_PATH or MOZBUILD_STATE_PATH needs to be set.");
println!( "cargo:warning=We recommend checking out https://github.com/mozilla/gecko-dev"
);
println!("cargo:warning=Then run `./mach bootstrap` which will retrieve clang.");
println!("cargo:warning=Make sure to export MOZBUILD_STATE_PATH when building."); return;
}; let libclang_dir = mozbuild_root.join("clang").join("lib"); if libclang_dir.is_dir() { unsafe {
env::set_var("LIBCLANG_PATH", libclang_dir.to_str().unwrap());
}
} else {
println!( "cargo:warning=LIBCLANG_PATH isn't set; maybe run ./mach bootstrap with gecko"
);
}
}
}
fn nss_dir() -> String { let dir = env::var("NSS_DIR").map_or_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
},
|dir| { let path = PathBuf::from(dir.trim());
assert!(
!path.is_relative(), "The NSS_DIR environment variable is expected to be an absolute path."
);
path
},
);
assert!(dir.is_dir(), "NSS_DIR {} doesn't exist", dir.display()); // Note that this returns a relative path because UNC // paths on windows cause certain tools to explode.
dir.to_string_lossy().to_string()
}
fn get_bash() -> PathBuf { // If BASH is set, use that. iflet Ok(bash) = env::var("BASH") { return PathBuf::from(bash);
}
// When running under MOZILLABUILD, we need to make sure not to invoke // another instance of bash that might be sitting around (like WSL).
env::var("MOZILLABUILD").map_or_else(
|_| PathBuf::from("bash"),
|d| PathBuf::from(d).join("msys").join("bin").join("bash.exe"),
)
}
fn build_nss(dir: PathBuf) { letmut build_nss = vec![
String::from("./build.sh"),
String::from("-Ddisable_tests=1"),
String::from("-Ddisable_dbm=1"),
String::from("-Ddisable_libpkix=1"),
String::from("-Ddisable_ckbi=1"),
String::from("-Ddisable_fips=1"),
String::from("--opt"), // Generate static libraries in addition to shared libraries.
String::from("--static"),
]; if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "aarch64" {
build_nss.push(String::from("--target=arm64"));
} 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 target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); let dynamic_libs = if target_os == "windows" {
[ "nssutil3.dll", "nss3.dll", "ssl3.dll", "libplds4.dll", "libplc4.dll", "libnspr4.dll",
]
} else {
["nssutil3", "nss3", "ssl3", "plds4", "plc4", "nspr4"]
};
for lib in dynamic_libs {
println!("cargo:rustc-link-lib=dylib={lib}");
}
maybe_link_freebl3();
}
fn maybe_link_freebl3() { if env::var("CARGO_FEATURE_BLAPI").is_ok() {
println!("cargo:rustc-link-lib=dylib=freebl3");
}
}
/// Emit the library link and search-path for freebl3 in a Gecko build. #[cfg(feature = "gecko")]
fn maybe_link_freebl3_gecko(topobjdir: &Path, fold_libs: bool) { if env::var("CARGO_FEATURE_BLAPI").is_err() { return;
}
println!("cargo:rustc-link-lib=dylib=freebl3"); let search = if env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") {
topobjdir
.join("security")
.join("nss")
.join("lib")
.join("freebl")
.join("freebl_freebl3")
} elseif fold_libs {
topobjdir.join("dist").join("bin")
} else { return;
};
println!("cargo:rustc-link-search=native={}", search.display());
}
fn static_link() { let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); letmut static_libs = vec![ "certdb", "certhi", "cryptohi", "freebl_static", if target_os == "windows" { "libnspr4"
} else { "nspr4"
}, "gcm", "nss_static", "nssb", "nssdev", "nsspki", "nssutil", "pk11wrap_static", if target_os == "windows" { "libplc4"
} else { "plc4"
}, if target_os == "windows" { "libplds4"
} else { "plds4"
}, "softokn_static", "ssl",
]; // macOS always dynamically links against the system sqlite library. // See https://github.com/nss-dev/nss/blob/a8c22d8fc0458db3e261acc5e19b436ab573a961/coreconf/Darwin.mk#L130-L135 if target_os == "macos" {
println!("cargo:rustc-link-lib=dylib=sqlite3");
} else {
static_libs.push("sqlite");
} // Hardware specific libs. let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); if target_arch == "arm" || target_arch == "aarch64" {
static_libs.push("armv8_c_lib");
} if target_arch == "arm" {
static_libs.push("ghash-aes-arm32-neon_c_lib");
} if target_arch == "aarch64" {
static_libs.push("ghash-aes-aarch64_c_lib");
} if target_arch == "x86_64" || target_arch == "x86" {
static_libs.push("ghash-aes-x86_c_lib");
static_libs.push("sha-x86_c_lib");
} if target_arch == "x86_64" {
static_libs.push("hw-acc-crypto-avx");
static_libs.push("hw-acc-crypto-avx2");
static_libs.push("intel-gcm-wrap_c_lib");
}
for lib in static_libs {
println!("cargo:rustc-link-lib=static={lib}");
}
}
fn get_includes(nsstarget: &Path, nssdist: &Path) -> Vec<PathBuf> { let nsprinclude = nsstarget.join("include").join("nspr"); let nssinclude = nssdist.join("public").join("nss");
vec![nsprinclude, nssinclude]
}
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");
// 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");
}
// The NSS version number does not follow semver numbering, because it omits the patch version // when that's 0. Deal with that. let modversion_for_cmp = if modversion.chars().filter(|c| *c == '.').count() == 1 {
modversion.to_owned() + ".0"
} else {
modversion.to_owned()
};
let modversion_for_cmp = Version::parse(&modversion_for_cmp)?;
let version_req = VersionReq::parse(&format!(">={}", MINIMUM_NSS_VERSION.trim()))?;
assert!(
version_req.matches(&modversion_for_cmp), "nss-rs has NSS version requirement {version_req}, found {modversion}",
);
let cfg = Command::new("pkg-config")
.args(["--cflags", "--libs", "nss"])
.output()?
.stdout;
for f in cfg_str.split_whitespace() { if f.starts_with("-I") {
flags.push(String::from(f));
} elseiflet Some(path) = f.strip_prefix("-L") {
println!("cargo:rustc-link-search=native={path}");
lib_dirs.push(PathBuf::from(path));
} elseiflet Some(lib) = f.strip_prefix("-l") {
println!("cargo:rustc-link-lib=dylib={lib}");
} else {
println!("cargo:warning=Unknown flag from pkg-config: {f}");
}
}
if env::var("CARGO_FEATURE_BLAPI").is_ok() { // pkg-config omits -L for default system library paths (e.g., /usr/lib64 on // RHEL/Fedora), so also include the libdir from the .pc file. iflet Ok(output) = Command::new("pkg-config")
.args(["--variable=libdir", "nss"])
.output()
&& output.status.success()
&& let Ok(s) = String::from_utf8(output.stdout)
{ let trimmed = s.trim(); if !trimmed.is_empty() { let dir = PathBuf::from(trimmed); if !lib_dirs.contains(&dir) {
println!("cargo:rustc-link-search=native={}", dir.display());
lib_dirs.push(dir);
}
}
}
}
maybe_link_freebl3();
// $NSS_DIR/../dist/ let nssdist = nss.parent().unwrap().join("dist"); let nsstarget = "Release";
// If NSS_PREBUILT is set to a non-zero value, we assume that the NSS libraries are already // built. if !env::var("NSS_PREBUILT").is_ok_and(|v| v != "0") {
build_nss(nss);
}
let nsstarget = nssdist.join(nsstarget); let includes = get_includes(&nsstarget, &nssdist);
fn process_config(config: &mut HashMap<String, Bindings>) { letmut 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![]
} else {
vec![&b.types, &b.functions, &b.variables]
}
.into_iter()
.flat_map(|v| v.iter())
.cloned()
})
.collect::<HashSet<String>>(),
);
}
for (header, excludes) in excludes {
config
.get_mut(&header)
.expect("key disappeared from config?")
.exclude
.extend(excludes);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.