use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
const _THEMIS_MAKEFILE: &[u8] = include_bytes!("../themis/Makefile");
#[derive(Default)]
pub struct Build {
out_dir: Option<PathBuf>,
}
pub struct Library {
prefix: PathBuf,
}
pub fn make() {
Build::new().build().set_pkg_config_path();
}
fn check_dependencies() {
fn fails_to_run(terms: &[&str]) -> bool {
Command::new(&terms[0])
.args(&terms[1..])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.is_err()
}
if fails_to_run(&["make", "--version"]) {
panic!(
"
It seems your system does not have GNU make installed. Make is required
to build Themis from source.
Please install \"make\" or \"build-essential\" package and try again.
"
);
}
if fails_to_run(&["cc", "--version"]) {
panic!(
"
It seems your system does not have a C compiler installed. C compiler
is required to build Themis from source.
Please install \"clang\" (or \"gcc\" and \"g++\") package and try again.
"
);
}
}
impl Build {
pub fn new() -> Build {
Build {
out_dir: env::var_os("OUT_DIR").map(|s| PathBuf::from(s).join("themis")),
}
}
pub fn out_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.out_dir = Some(path.as_ref().to_path_buf());
self
}
pub fn build(&self) -> Library {
check_dependencies();
let out_dir = self.out_dir.as_ref().expect("OUT_DIR not set");
let themis_src_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("themis");
let themis_build_dir = out_dir.join("build");
let themis_install_dir = out_dir.join("install");
if !out_dir.exists() {
fs::create_dir(&out_dir).expect("mkdir themis");
}
if themis_build_dir.exists() {
fs::remove_dir_all(&themis_build_dir).expect("rm -r themis/build");
}
if themis_install_dir.exists() {
fs::remove_dir_all(&themis_install_dir).expect("rm -r themis/install");
}
fs::create_dir(&themis_build_dir).expect("mkdir themis/build");
fs::create_dir(&themis_install_dir).expect("mkdir themis/install");
let mut themis_build_and_install = make_cmd::make();
themis_build_and_install
.current_dir(&themis_src_dir)
.stdout(Stdio::null())
.env("BUILD_PATH", &themis_build_dir)
.env("PREFIX", &themis_install_dir)
.arg("install");
if cfg!(debug) {
themis_build_and_install.env("DEBUG", "1");
} else {
themis_build_and_install.env_remove("DEBUG");
}
let status = themis_build_and_install
.status()
.expect("failed to run Themis build");
if !status.success() {
panic!("Themis build failed: {}", status);
}
Library {
prefix: themis_install_dir,
}
}
}
impl Library {
pub fn prefix(&self) -> &Path {
&self.prefix
}
pub fn set_pkg_config_path(&self) {
let mut paths = env::var_os("PKG_CONFIG_PATH").unwrap_or_default();
if !paths.is_empty() {
paths.push(":");
}
paths.push(self.prefix.join("lib/pkgconfig"));
env::set_var("PKG_CONFIG_PATH", paths);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
use std::ffi::OsStr;
#[test]
fn build_and_install() {
let temp_dir = tempfile::tempdir().expect("temporary directory");
let library = Build::new().out_dir(&temp_dir).build();
assert!(library.prefix().join("include/themis/themis.h").exists());
assert!(library.prefix().join("lib/pkgconfig/libthemis.pc").exists());
assert!(library.prefix().join("lib").read_dir().unwrap().count() > 0);
}
#[test]
#[allow(non_snake_case)]
fn build_and_install_to_OUT_DIR() {
let temp_dir = tempfile::tempdir().expect("temporary directory");
let library = with_env_var("OUT_DIR", temp_dir.path(), || Build::new().build());
assert!(library.prefix().join("include/themis/themis.h").exists());
assert!(library.prefix().join("lib/pkgconfig/libthemis.pc").exists());
assert!(library.prefix().join("lib").read_dir().unwrap().count() > 0);
}
#[test]
fn pkg_config_setting() {
let temp_dir = tempfile::tempdir().expect("temporary directory");
let library = Build::new().out_dir(&temp_dir).build();
with_env_var("PKG_CONFIG_PATH", "", || {
library.set_pkg_config_path();
let pkg_path = env::var("PKG_CONFIG_PATH").expect("PKG_CONFIG_PATH");
let prefix = library.prefix().to_str().expect("prefix").to_owned();
assert!(pkg_path.contains(&prefix));
});
}
fn with_env_var<K, V, F, T>(key: K, value: V, f: F) -> T
where
K: AsRef<OsStr>,
V: AsRef<OsStr>,
F: FnOnce() -> T,
{
let old_value = env::var_os(&key);
env::set_var(&key, value);
let result = f();
match old_value {
Some(old_value) => env::set_var(&key, old_value),
None => env::remove_var(&key),
}
result
}
}