nss_build_common/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5//! This shouldn't exist, but does because if something isn't going to link
6//! against `nss` but has an `nss`-enabled `sqlcipher` turned on (for example,
7//! by a `cargo` feature activated by something else in the workspace).
8//! it might need to issue link commands for NSS.
9
10use std::{
11    env,
12    ffi::OsString,
13    path::{Path, PathBuf},
14};
15
16#[derive(Clone, Copy, PartialEq, Eq, Debug)]
17pub enum LinkingKind {
18    Dynamic { folded_libs: bool },
19    Static,
20}
21
22#[derive(Debug, PartialEq, Eq, Clone)]
23pub struct NoNssDir;
24
25pub fn link_nss() -> Result<(), NoNssDir> {
26    let is_gecko = env::var_os("MOZ_TOPOBJDIR").is_some();
27    if !is_gecko {
28        let (lib_dir, include_dir) = get_nss()?;
29        println!(
30            "cargo:rustc-link-search=native={}",
31            lib_dir.to_string_lossy()
32        );
33        println!("cargo:include={}", include_dir.to_string_lossy());
34        let kind = determine_kind();
35        link_nss_libs(kind);
36    } else {
37        let libs = match env::var("CARGO_CFG_TARGET_OS")
38            .as_ref()
39            .map(std::string::String::as_str)
40        {
41            Ok("android") | Ok("macos") => vec!["nss3"],
42            _ => vec!["nssutil3", "nss3", "plds4", "plc4", "nspr4"],
43        };
44        for lib in &libs {
45            println!("cargo:rustc-link-lib=dylib={}", lib);
46        }
47    }
48    Ok(())
49}
50
51fn get_nss() -> Result<(PathBuf, PathBuf), NoNssDir> {
52    let nss_dir = env("NSS_DIR").ok_or(NoNssDir)?;
53    let nss_dir = Path::new(&nss_dir);
54    if !nss_dir.exists() {
55        println!(
56            "NSS_DIR path (obtained via `env`) does not exist: {}",
57            nss_dir.display()
58        );
59        panic!("It looks like NSS is not built. Please run `libs/verify-[platform]-environment.sh` first!");
60    }
61    let lib_dir = nss_dir.join("lib");
62    let include_dir = nss_dir.join("include");
63    Ok((lib_dir, include_dir))
64}
65
66fn determine_kind() -> LinkingKind {
67    if env_flag("NSS_STATIC") {
68        LinkingKind::Static
69    } else {
70        let folded_libs = env_flag("NSS_USE_FOLDED_LIBS");
71        LinkingKind::Dynamic { folded_libs }
72    }
73}
74
75fn link_nss_libs(kind: LinkingKind) {
76    let libs = get_nss_libs(kind);
77    // Emit -L flags
78    let kind_str = match kind {
79        LinkingKind::Dynamic { .. } => "dylib",
80        LinkingKind::Static => "static",
81    };
82    for lib in libs {
83        println!("cargo:rustc-link-lib={}={}", kind_str, lib);
84    }
85    // Link against C++ stdlib (for mozpkix)
86    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
87    if target_os == "android" || target_os == "linux" {
88        println!("cargo:rustc-link-lib=stdc++");
89    } else {
90        println!("cargo:rustc-link-lib=c++");
91    }
92    let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
93    if target_arch == "x86_64" && target_os == "android" {
94        let android_home = env::var("ANDROID_HOME").expect("ANDROID_HOME not set");
95        const ANDROID_NDK_VERSION: &str = "28.1.13356709";
96        // One of these will exist, depending on the host platform.
97        const DARWIN_X86_64_LIB_DIR: &str =
98            "/toolchains/llvm/prebuilt/darwin-x86_64/lib/clang/19/lib/linux/";
99        println!("cargo:rustc-link-search={android_home}/ndk/{ANDROID_NDK_VERSION}/{DARWIN_X86_64_LIB_DIR}");
100        const LINUX_X86_64_LIB_DIR: &str =
101            "/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/19/lib/linux/";
102        println!("cargo:rustc-link-search={android_home}/ndk/{ANDROID_NDK_VERSION}/{LINUX_X86_64_LIB_DIR}");
103        println!("cargo:rustc-link-lib=static=clang_rt.builtins-x86_64-android");
104    }
105}
106
107fn get_nss_libs(kind: LinkingKind) -> Vec<&'static str> {
108    match kind {
109        LinkingKind::Static => {
110            let mut static_libs = vec![
111                "certdb",
112                "certhi",
113                "cryptohi",
114                "freebl_static",
115                "mozpkix",
116                "nspr4",
117                "nss_static",
118                "nssb",
119                "nssdev",
120                "nsspki",
121                "nssutil",
122                "pk11wrap_static",
123                "plc4",
124                "plds4",
125                "softokn_static",
126            ];
127            // Hardware specific libs.
128            let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
129            let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
130            // https://searchfox.org/nss/rev/0d5696b3edce5124353f03159d2aa15549db8306/lib/freebl/freebl.gyp#508-542
131            if target_arch == "arm" || target_arch == "aarch64" {
132                static_libs.push("armv8_c_lib");
133            }
134            if target_arch == "x86_64" || target_arch == "x86" {
135                static_libs.push("gcm-aes-x86_c_lib");
136                static_libs.push("sha-x86_c_lib");
137            }
138            if target_arch == "arm" {
139                static_libs.push("gcm-aes-arm32-neon_c_lib")
140            }
141            if target_arch == "aarch64" {
142                static_libs.push("gcm-aes-aarch64_c_lib");
143            }
144            if target_arch == "x86_64" {
145                static_libs.push("hw-acc-crypto-avx");
146                static_libs.push("hw-acc-crypto-avx2");
147            }
148            // https://searchfox.org/nss/rev/08c4d05078d00089f8d7540651b0717a9d66f87e/lib/freebl/freebl.gyp#315-324
149            if ((target_os == "android" || target_os == "linux") && target_arch == "x86_64")
150                || target_os == "windows"
151            {
152                static_libs.push("intel-gcm-wrap_c_lib");
153                // https://searchfox.org/nss/rev/08c4d05078d00089f8d7540651b0717a9d66f87e/lib/freebl/freebl.gyp#43-47
154                if (target_os == "android" || target_os == "linux") && target_arch == "x86_64" {
155                    static_libs.push("intel-gcm-s_lib");
156                }
157            }
158            static_libs
159        }
160        LinkingKind::Dynamic { folded_libs } => {
161            let mut dylibs = vec!["freebl3", "nss3", "nssckbi", "softokn3"];
162            if !folded_libs {
163                dylibs.append(&mut vec!["nspr4", "nssutil3", "plc4", "plds4"]);
164            }
165            dylibs
166        }
167    }
168}
169
170pub fn env(name: &str) -> Option<OsString> {
171    println!("cargo:rerun-if-env-changed={}", name);
172    env::var_os(name)
173}
174
175pub fn env_str(name: &str) -> Option<String> {
176    println!("cargo:rerun-if-env-changed={}", name);
177    env::var(name).ok()
178}
179
180pub fn env_flag(name: &str) -> bool {
181    match env_str(name).as_ref().map(String::as_ref) {
182        Some("1") => true,
183        Some("0") => false,
184        Some(s) => {
185            println!(
186                "cargo:warning=unknown value for environment var {:?}: {:?}. Ignoring",
187                name, s
188            );
189            false
190        }
191        None => false,
192    }
193}