1use std::env;
48use std::fs;
49use std::path::{Path, PathBuf};
50use std::process::{Command, Stdio};
51
52const _THEMIS_MAKEFILE: &[u8] = include_bytes!("../themis/Makefile");
54
55#[derive(Default)]
59pub struct Build {
60 out_dir: Option<PathBuf>,
61}
62
63pub struct Library {
67 prefix: PathBuf,
68}
69
70pub fn make() {
72 Build::new().build().set_pkg_config_path();
73}
74
75fn check_dependencies() {
77 fn fails_to_run(terms: &[&str]) -> bool {
78 Command::new(&terms[0])
79 .args(&terms[1..])
80 .stdout(Stdio::null())
81 .stderr(Stdio::null())
82 .status()
83 .is_err()
84 }
85
86 if fails_to_run(&["make", "--version"]) {
87 panic!(
88 "
89
90It seems your system does not have GNU make installed. Make is required
91to build Themis from source.
92
93Please install \"make\" or \"build-essential\" package and try again.
94
95 "
96 );
97 }
98
99 if fails_to_run(&["cc", "--version"]) {
100 panic!(
101 "
102
103It seems your system does not have a C compiler installed. C compiler
104is required to build Themis from source.
105
106Please install \"clang\" (or \"gcc\" and \"g++\") package and try again.
107
108 "
109 );
110 }
111
112 }
114
115impl Build {
116 pub fn new() -> Build {
118 Build {
119 out_dir: env::var_os("OUT_DIR").map(|s| PathBuf::from(s).join("themis")),
120 }
121 }
122
123 pub fn out_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
126 self.out_dir = Some(path.as_ref().to_path_buf());
127 self
128 }
129
130 pub fn build(&self) -> Library {
132 check_dependencies();
133
134 let out_dir = self.out_dir.as_ref().expect("OUT_DIR not set");
135 let themis_src_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("themis");
136 let themis_build_dir = out_dir.join("build");
137 let themis_install_dir = out_dir.join("install");
138
139 if !out_dir.exists() {
143 fs::create_dir(&out_dir).expect("mkdir themis");
144 }
145 if themis_build_dir.exists() {
146 fs::remove_dir_all(&themis_build_dir).expect("rm -r themis/build");
147 }
148 if themis_install_dir.exists() {
149 fs::remove_dir_all(&themis_install_dir).expect("rm -r themis/install");
150 }
151
152 fs::create_dir(&themis_build_dir).expect("mkdir themis/build");
153 fs::create_dir(&themis_install_dir).expect("mkdir themis/install");
154
155 let mut themis_build_and_install = make_cmd::make();
157 themis_build_and_install
158 .current_dir(&themis_src_dir)
159 .stdout(Stdio::null())
160 .env("BUILD_PATH", &themis_build_dir)
161 .env("PREFIX", &themis_install_dir)
162 .arg("install");
163
164 if cfg!(debug) {
167 themis_build_and_install.env("DEBUG", "1");
168 } else {
169 themis_build_and_install.env_remove("DEBUG");
170 }
171
172 let status = themis_build_and_install
173 .status()
174 .expect("failed to run Themis build");
175
176 if !status.success() {
177 panic!("Themis build failed: {}", status);
178 }
179
180 Library {
181 prefix: themis_install_dir,
182 }
183 }
184}
185
186impl Library {
187 pub fn prefix(&self) -> &Path {
189 &self.prefix
190 }
191
192 pub fn set_pkg_config_path(&self) {
194 let mut paths = env::var_os("PKG_CONFIG_PATH").unwrap_or_default();
195 if !paths.is_empty() {
196 paths.push(":");
197 }
198 paths.push(self.prefix.join("lib/pkgconfig"));
199
200 env::set_var("PKG_CONFIG_PATH", paths);
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 use std::env;
209 use std::ffi::OsStr;
210
211 #[test]
212 fn build_and_install() {
213 let temp_dir = tempfile::tempdir().expect("temporary directory");
214 let library = Build::new().out_dir(&temp_dir).build();
215
216 assert!(library.prefix().join("include/themis/themis.h").exists());
217 assert!(library.prefix().join("lib/pkgconfig/libthemis.pc").exists());
218 assert!(library.prefix().join("lib").read_dir().unwrap().count() > 0);
219 }
220
221 #[test]
222 #[allow(non_snake_case)]
223 fn build_and_install_to_OUT_DIR() {
224 let temp_dir = tempfile::tempdir().expect("temporary directory");
225 let library = with_env_var("OUT_DIR", temp_dir.path(), || Build::new().build());
226
227 assert!(library.prefix().join("include/themis/themis.h").exists());
228 assert!(library.prefix().join("lib/pkgconfig/libthemis.pc").exists());
229 assert!(library.prefix().join("lib").read_dir().unwrap().count() > 0);
230 }
231
232 #[test]
233 fn pkg_config_setting() {
234 let temp_dir = tempfile::tempdir().expect("temporary directory");
235 let library = Build::new().out_dir(&temp_dir).build();
236
237 with_env_var("PKG_CONFIG_PATH", "", || {
238 library.set_pkg_config_path();
239
240 let pkg_path = env::var("PKG_CONFIG_PATH").expect("PKG_CONFIG_PATH");
241 let prefix = library.prefix().to_str().expect("prefix").to_owned();
242 assert!(pkg_path.contains(&prefix));
243 });
244 }
245
246 fn with_env_var<K, V, F, T>(key: K, value: V, f: F) -> T
247 where
248 K: AsRef<OsStr>,
249 V: AsRef<OsStr>,
250 F: FnOnce() -> T,
251 {
252 let old_value = env::var_os(&key);
253 env::set_var(&key, value);
254 let result = f();
255 match old_value {
256 Some(old_value) => env::set_var(&key, old_value),
257 None => env::remove_var(&key),
258 }
259 result
260 }
261}