#![deny(missing_docs)]
extern crate gcc;
use std::env;
use std::ffi::{OsString, OsStr};
use std::fs;
use std::io::ErrorKind;
use std::path::{Path, PathBuf};
use std::process::Command;
pub struct Config {
path: PathBuf,
cflags: OsString,
defines: Vec<(OsString, OsString)>,
deps: Vec<String>,
}
pub fn build<P: AsRef<Path>>(path: P) -> PathBuf {
Config::new(path.as_ref()).build()
}
impl Config {
pub fn new<P: AsRef<Path>>(path: P) -> Config {
Config {
path: path.as_ref().to_path_buf(),
cflags: OsString::new(),
defines: Vec::new(),
deps: Vec::new(),
}
}
pub fn cflag<P: AsRef<OsStr>>(&mut self, flag: P) -> &mut Config {
self.cflags.push(" ");
self.cflags.push(flag.as_ref());
self
}
pub fn define<K, V>(&mut self, k: K, v: V) -> &mut Config
where K: AsRef<OsStr>, V: AsRef<OsStr>
{
self.defines.push((k.as_ref().to_owned(), v.as_ref().to_owned()));
self
}
pub fn register_dep(&mut self, dep: &str) -> &mut Config {
self.deps.push(dep.to_string());
self
}
pub fn build(&mut self) -> PathBuf {
let target = env::var("TARGET").unwrap();
let msvc = target.contains("msvc");
let dst = PathBuf::from(&env::var("OUT_DIR").unwrap());
let _ = fs::create_dir(&dst.join("build"));
let mut cflags = env::var_os("CFLAGS").unwrap_or(OsString::new());
cflags.push(" ");
cflags.push(&self.cflags);
if !msvc {
cflags.push(" -ffunction-sections");
cflags.push(" -fdata-sections");
if target.contains("i686") {
cflags.push(" -m32");
} else if target.contains("x86_64") {
cflags.push(" -m64");
}
if !target.contains("i686") {
cflags.push(" -fPIC");
}
}
let mut cmake_prefix_path = Vec::new();
for dep in &self.deps {
if let Some(root) = env::var_os(&format!("DEP_{}_ROOT", dep)) {
cmake_prefix_path.push(PathBuf::from(root));
}
}
let system_prefix = env::var_os("CMAKE_PREFIX_PATH")
.unwrap_or(OsString::new());
cmake_prefix_path.extend(env::split_paths(&system_prefix)
.map(|s| s.to_owned()));
let cmake_prefix_path = env::join_paths(&cmake_prefix_path).unwrap();
let mut cmd = Command::new("cmake");
cmd.arg(env::current_dir().unwrap().join(&self.path))
.current_dir(&dst.join("build"));
if target.contains("windows-gnu") {
cmd.arg("-G").arg("MSYS Makefiles");
} else if msvc {
cmd.arg("-G").arg(self.visual_studio_generator(&target));
}
let profile = match &env::var("PROFILE").unwrap()[..] {
"bench" | "release" => "Release",
_ if msvc => "Release", _ => "Debug",
};
for &(ref k, ref v) in &self.defines {
let mut os = OsString::from("-D");
os.push(k);
os.push("=");
os.push(v);
cmd.arg(os);
}
let mut dstflag = OsString::from("-DCMAKE_INSTALL_PREFIX=");
dstflag.push(&dst);
let mut cflagsflag = OsString::from("-DCMAKE_C_FLAGS=");
cflagsflag.push(&cflags);
run(cmd.arg(&format!("-DCMAKE_BUILD_TYPE={}", profile))
.arg(dstflag)
.arg(cflagsflag)
.env("CMAKE_PREFIX_PATH", cmake_prefix_path), "cmake");
run(Command::new("cmake")
.arg("--build").arg(".")
.arg("--target").arg("install")
.arg("--config").arg(profile)
.current_dir(&dst.join("build")), "cmake");
println!("cargo:root={}", dst.display());
return dst
}
fn visual_studio_generator(&self, target: &str) -> String {
let candidate = format!("{:?}", gcc::windows_registry::find(target,
"cl.exe"));
let base = if candidate.contains("12.0") {
"Visual Studio 12 2013"
} else if candidate.contains("14.0") {
"Visual Studio 14 2015"
} else {
panic!("couldn't determine visual studio generator")
};
if target.contains("i686") {
base.to_string()
} else if target.contains("x86_64") {
format!("{} Win64", base)
} else {
panic!("unsupported msvc target: {}", target);
}
}
}
fn run(cmd: &mut Command, program: &str) {
println!("running: {:?}", cmd);
let status = match cmd.status() {
Ok(status) => status,
Err(ref e) if e.kind() == ErrorKind::NotFound => {
fail(&format!("failed to execute command: {}\nis `{}` not installed?",
e, program));
}
Err(e) => fail(&format!("failed to execute command: {}", e)),
};
if !status.success() {
fail(&format!("command did not execute successfully, got: {}", status));
}
}
fn fail(s: &str) -> ! {
panic!("\n{}\n\nbuild script failed, must exit now", s)
}