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 #[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 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}