botan_src/
lib.rs

1use std::env;
2use std::path::PathBuf;
3use std::process::Command;
4
5const BUILD_ERROR_MSG: &str = "Unable to build botan.";
6const SRC_DIR_ERROR_MSG: &str = "Unable to find the source directory.";
7const SRC_DIR: &str = "botan";
8const INCLUDE_DIR: &str = "build/include/public";
9
10macro_rules! pathbuf_to_string {
11    ($s: ident) => {
12        $s.to_str().expect(BUILD_ERROR_MSG).to_string()
13    };
14}
15
16fn env_name_for(opt: &'static str) -> String {
17    assert!(opt[0..2] == *"--");
18    let to_var = opt[2..].to_uppercase().replace('-', "_");
19    format!("BOTAN_CONFIGURE_{to_var}")
20}
21
22fn configure(build_dir: &str) {
23    let mut configure = Command::new("python3");
24    configure.arg("configure.py");
25    configure.arg(format!("--with-build-dir={build_dir}"));
26    configure.arg("--build-targets=static");
27    configure.arg("--without-documentation");
28    configure.arg("--no-install-python-module");
29    configure.arg("--distribution-info=https://crates.io/crates/botan-src");
30
31    configure.arg(format!(
32        "--cpu={}",
33        env::var("CARGO_CFG_TARGET_ARCH").unwrap()
34    ));
35    configure.arg(format!("--os={}", env::var("CARGO_CFG_TARGET_OS").unwrap()));
36
37    #[cfg(debug_assertions)]
38    configure.arg("--with-debug-info");
39
40    // On Windows we require the amalgamation, to work around the fact that
41    // otherwise the linker command lines become too long for Windows
42    #[cfg(target_os = "windows")]
43    configure.arg("--amalgamation");
44
45    let args = [
46        "--compiler-cache",
47        "--cc",
48        "--cc-bin",
49        "--cc-abi-flags",
50        "--cxxflags",
51        "--extra-cxxflags",
52        "--ldflags",
53        "--ar-command",
54        "--ar-options",
55        "--msvc-runtime",
56        "--system-cert-bundle",
57        "--module-policy",
58        "--enable-modules",
59        "--disable-modules",
60    ];
61
62    let flags = [
63        "--optimize-for-size",
64        "--amalgamation",
65        "--with-commoncrypto",
66        "--with-sqlite3",
67    ];
68
69    for arg_name in &args {
70        let env_name = env_name_for(arg_name);
71        if let Ok(arg_val) = env::var(env_name) {
72            let arg = format!("{arg_name}={arg_val}");
73            configure.arg(arg);
74        }
75    }
76
77    for flag_name in &flags {
78        let env_name = env_name_for(flag_name);
79        if env::var(env_name).is_ok() {
80            configure.arg(flag_name);
81        }
82    }
83
84    let status = configure
85        .spawn()
86        .expect(BUILD_ERROR_MSG)
87        .wait()
88        .expect(BUILD_ERROR_MSG);
89    if !status.success() {
90        panic!("configure terminated unsuccessfully");
91    }
92}
93
94fn make(build_dir: &str) {
95    let mut cmd = Command::new("make");
96    // Set MAKEFLAGS to the content of CARGO_MAKEFLAGS
97    // to give jobserver (parallel builds) support to the
98    // spawned sub-make.
99    if let Ok(val) = env::var("CARGO_MAKEFLAGS") {
100        cmd.env("MAKEFLAGS", val);
101    } else {
102        eprintln!("Can't set MAKEFLAGS as CARGO_MAKEFLAGS couldn't be read");
103    }
104
105    let status = cmd
106        .arg("-f")
107        .arg(format!("{build_dir}/Makefile"))
108        .arg("libs")
109        .spawn()
110        .expect(BUILD_ERROR_MSG)
111        .wait()
112        .expect(BUILD_ERROR_MSG);
113    if !status.success() {
114        panic!("make terminated unsuccessfully");
115    }
116}
117
118pub fn build() -> (String, String) {
119    let src_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SRC_DIR);
120    let build_dir = env::var_os("OUT_DIR").map_or(src_dir.to_owned(), PathBuf::from);
121    let build_dir = build_dir.join("botan");
122    let include_dir = build_dir.join(INCLUDE_DIR);
123    let build_dir = pathbuf_to_string!(build_dir);
124    let orig_dir = env::current_dir().expect(SRC_DIR_ERROR_MSG);
125    env::set_current_dir(&src_dir).expect(SRC_DIR_ERROR_MSG);
126    configure(&build_dir);
127    make(&build_dir);
128    env::set_current_dir(&orig_dir).expect("Unable to restore cwd");
129    (build_dir, pathbuf_to_string!(include_dir))
130}