extern crate dirs;
use std::env;
use std::ffi::OsString;
use std::fs::{self, File};
use std::io::{Result as IoResult, Write};
use std::path::{Path, PathBuf};
use std::process::Command;
const GMPMEE_DIR: &str = "verificatum-gmpmee-2.1.0-c";
#[derive(Clone, Copy, PartialEq)]
enum Target {
Mingw,
Msvc,
Other,
}
struct Environment {
out_dir: PathBuf,
source_dir: PathBuf,
lib_dir: PathBuf,
include_dir: PathBuf,
jobs: OsString,
target: Target,
make_check: bool,
cc: String,
cflags: String,
}
fn main() {
if cfg!(target_os = "windows") {
println!("cargo:rustc-link-search=native=./gmpmee_lib_windows");
println!("cargo:rustc-link-lib=static=gmpmee");
return;
}
let src_dir = PathBuf::from(cargo_env("CARGO_MANIFEST_DIR"));
let out_dir = PathBuf::from(cargo_env("OUT_DIR"));
let target = cargo_env("TARGET");
let target = target
.into_string()
.expect("cannot convert environment variable TARGET into a `String`");
let target = if target.contains("-windows-msvc") {
panic!("MSVC is not supported");
} else if target.contains("-windows-gnu") {
panic!("Windows-gnu is not working now");
} else {
Target::Other
};
let make_check = there_is_env("CARGO_FEATURE_CTEST")
|| (!there_is_env("CARGO_FEATURE_CNOTEST") && cargo_env("PROFILE") == *"release");
let compiler = cc::Build::new().get_compiler();
let cc = match &compiler.cc_env() {
cc if cc.is_empty() => compiler
.path()
.to_str()
.expect("Unprintable CC")
.to_string(),
cc => cc.to_str().expect("Unprintable CC").to_string(),
};
let cflags = compiler
.cflags_env()
.to_str()
.expect("Unprintable CFLAGS")
.to_string();
let host = String::from_utf8(
compiler
.to_command()
.arg("-dumpmachine")
.output()
.expect("Failed to get HOST")
.stdout,
)
.expect("Unprintable HOST")
.trim_end()
.to_string();
if host.is_empty() {
panic!("Failed to get HOST")
}
let env = Environment {
out_dir: out_dir.clone(),
source_dir: out_dir.join(GMPMEE_DIR),
lib_dir: out_dir.join("lib"),
include_dir: out_dir.join("include"),
jobs: cargo_env("NUM_JOBS"),
target,
make_check,
cc,
cflags,
};
create_dir_or_panic(&env.lib_dir);
create_dir_or_panic(&env.include_dir);
let gmpmee_ah = (
env.lib_dir.join("libgmpmee.a"),
env.include_dir.join("gmpmee.h"),
);
check_for_msvc(&env);
check_for_windows_gnu(&env);
remove_dir_or_panic(&env.source_dir);
create_dir_or_panic(&env.source_dir);
copy_dir_or_panic(&src_dir.join(GMPMEE_DIR), &env.source_dir);
build_gmpmee(&env, &gmpmee_ah.0, &gmpmee_ah.1);
if !there_is_env("CARGO_FEATURE_CNODELETE") {
remove_dir_or_panic(&env.source_dir);
}
write_link_info(&env);
}
fn build_gmpmee(env: &Environment, lib: &Path, header: &Path) {
println!("$ cd {:?}", &env.source_dir);
println!("$ export CC={:?}", &env.cc);
println!("$ export CFLAGS={:?}", &env.cflags);
println!("$ cd {:?}", env.source_dir);
let test_cmd = Command::new("env");
execute(test_cmd);
let mut create_conf = Command::new("sh");
create_conf
.current_dir(&env.source_dir)
.arg("-c")
.arg("make -f Makefile.build")
.env("CC", &env.cc)
.env("CFLAGS", &env.cflags);
execute(create_conf);
let conf = "./configure \
--disable-shared \
--with-pic \
"
.to_string();
let mut configure = Command::new("sh");
configure
.current_dir(&env.source_dir)
.arg("-c")
.arg(conf)
.env("CC", &env.cc)
.env("CFLAGS", &env.cflags);
execute(configure);
make_and_check(env, &env.source_dir);
let build_lib = env.source_dir.join(".libs").join("libgmpmee.a");
copy_file_or_panic(&build_lib, lib);
let build_header = env.source_dir.join("gmpmee.h");
copy_file_or_panic(&build_header, header);
}
fn write_link_info(env: &Environment) {
let out_str = env.out_dir.to_str().unwrap_or_else(|| {
panic!(
"Path contains unsupported characters, can only make {}",
env.out_dir.display()
)
});
let lib_str = env.lib_dir.to_str().unwrap_or_else(|| {
panic!(
"Path contains unsupported characters, can only make {}",
env.lib_dir.display()
)
});
let include_str = env.include_dir.to_str().unwrap_or_else(|| {
panic!(
"Path contains unsupported characters, can only make {}",
env.include_dir.display()
)
});
println!("cargo:out_dir={}", out_str);
println!("cargo:lib_dir={}", lib_str);
println!("cargo:include_dir={}", include_str);
println!("cargo:rustc-link-search=native={}", lib_str);
println!("cargo:rustc-link-lib=static=gmpmee");
}
fn cargo_env(name: &str) -> OsString {
env::var_os(name)
.unwrap_or_else(|| panic!("environment variable not found: {}, please use cargo", name))
}
fn there_is_env(name: &str) -> bool {
env::var_os(name).is_some()
}
fn check_for_msvc(env: &Environment) {
if env.target == Target::Msvc {
panic!("Windows MSVC target is not supported (linking would fail)");
}
}
fn check_for_windows_gnu(env: &Environment) {
if env.target == Target::Mingw {
panic!("Windows Mingw target is not supported (linking would fail)");
}
}
fn remove_dir(dir: &Path) -> IoResult<()> {
if !dir.exists() {
return Ok(());
}
assert!(dir.is_dir(), "Not a directory: {:?}", dir);
println!("$ rm -r {:?}", dir);
fs::remove_dir_all(dir)
}
fn remove_dir_or_panic(dir: &Path) {
remove_dir(dir).unwrap_or_else(|_| panic!("Unable to remove directory: {:?}", dir));
}
fn create_dir(dir: &Path) -> IoResult<()> {
println!("$ mkdir -p {:?}", dir);
fs::create_dir_all(dir)
}
fn create_dir_or_panic(dir: &Path) {
create_dir(dir).unwrap_or_else(|_| panic!("Unable to create directory: {:?}", dir));
}
#[allow(dead_code)]
fn create_file_or_panic(filename: &Path, contents: &str) {
println!("$ printf '%s' {:?}... > {:?}", &contents[0..10], filename);
let mut file =
File::create(filename).unwrap_or_else(|_| panic!("Unable to create file: {:?}", filename));
file.write_all(contents.as_bytes())
.unwrap_or_else(|_| panic!("Unable to write to file: {:?}", filename));
}
fn copy_file(src: &Path, dst: &Path) -> IoResult<u64> {
println!("$ cp {:?} {:?}", src, dst);
fs::copy(src, dst)
}
fn copy_file_or_panic(src: &Path, dst: &Path) {
copy_file(src, dst).unwrap_or_else(|_| {
panic!("Unable to copy {:?} -> {:?}", src, dst);
});
}
fn copy_dir(src: &Path, dst: &Path) -> fs_extra::error::Result<u64> {
println!("$ cp -r {:?} {:?}", src, dst);
let copy_options = fs_extra::dir::CopyOptions::new().overwrite(true);
fs_extra::dir::copy(src, dst.parent().unwrap(), ©_options)
}
fn copy_dir_or_panic(src: &Path, dst: &Path) {
copy_dir(src, dst).unwrap_or_else(|e| {
panic!("Unable to copy {:?} -> {:?}: {}", src, dst, e);
});
}
fn make_and_check(env: &Environment, build_dir: &Path) {
let mut make = Command::new("make");
make.current_dir(build_dir).arg("-j").arg(&env.jobs);
execute(make);
if env.make_check {
let mut make_check = Command::new("make");
make_check
.current_dir(build_dir)
.arg("-j")
.arg(&env.jobs)
.arg("check");
execute(make_check);
}
}
fn execute(mut command: Command) {
println!("$ {:?}", command);
let status = command
.status()
.unwrap_or_else(|_| panic!("Unable to execute: {:?}", command));
if !status.success() {
if let Some(code) = status.code() {
panic!("Program failed with code {}: {:?}", code, command);
} else {
panic!("Program failed: {:?}", command);
}
}
}