#![allow(dead_code, unused)]
use glob::glob;
use std::{
env,
fs::{self, File},
io::Write,
path::Path,
process::Command,
str,
};
fn main() {
#[cfg(feature = "ipasir")]
{
#[cfg(feature = "cadical")]
println!("cargo:warning=Feature `cadical` (potentially) conflicts with feature `ipasir`");
}
build_cadical(
"https://github.com/chrjabs/cadical.git",
"master",
"e7433d7b68c2f173b9ea4dc968c4784c8e7a4887",
);
build_kissat(
"https://github.com/arminbiere/kissat.git",
"master",
"97917ddf2b12adc6f63c7b2a5a403a1ee7d81836",
);
build_glucose4(
"https://github.com/chrjabs/glucose4",
"main",
"e06dbf6d9b17b68794767617cf453b31e72a3226",
);
build_minisat(
"https://github.com/chrjabs/minisat",
"master",
"7767e6587e28d8c4f844dc018ffcbfdf49937514",
);
let out_dir = env::var("OUT_DIR").unwrap();
println!("cargo:rustc-link-search={}", out_dir);
println!("cargo:rustc-link-search={}/lib", out_dir);
#[cfg(any(
feature = "kissat",
feature = "cadical",
feature = "glucose4",
feature = "minisat",
feature = "ipasir"
))]
println!("cargo:rustc-cfg=solver");
#[cfg(any(
feature = "cadical",
feature = "glucose4",
feature = "minisat",
feature = "ipasir"
))]
println!("cargo:rustc-cfg=incsolver");
}
fn build_cadical(repo: &str, branch: &str, commit: &str) -> bool {
#[cfg(feature = "cadical")]
{
let out_dir = env::var("OUT_DIR").unwrap();
let mut cadical_dir_str = out_dir.clone();
cadical_dir_str.push_str("/cadical");
let cadical_dir = Path::new(&cadical_dir_str);
if update_repo(cadical_dir, repo, branch, commit)
|| !Path::new(&out_dir).join("libcadical.a").exists()
{
let src_files = glob(&format!("{}/src/*.cpp", cadical_dir_str))
.unwrap()
.filter_map(|res| {
if let Ok(p) = res {
if let Some(name) = p.file_name() {
if name == "cadical.cpp" || name == "mobical.cpp" {
return None; }
};
Some(p)
} else {
None
}
});
let mut cadical_build = cc::Build::new();
cadical_build.cpp(true);
if env::var("PROFILE").unwrap() == "debug" {
cadical_build
.opt_level(0)
.define("DEBUG", None)
.warnings(true)
.debug(true);
} else {
cadical_build
.opt_level(3)
.define("NDEBUG", None)
.warnings(false);
}
let mut build_header = File::create(cadical_dir.join("src").join("build.hpp"))
.expect("Could not create kissat CaDiCaL header");
let mut cadical_version = fs::read_to_string(cadical_dir.join("VERSION"))
.expect("Cannot read CaDiCaL version");
cadical_version.retain(|c| c != '\n');
let (compiler_desc, compiler_flags) =
get_compiler_description(cadical_build.get_compiler());
write!(
build_header,
"#define VERSION \"{}\"\n#define IDENTIFIER \"{}\"\n#define COMPILER \"{}\"\n#define FLAGS \"{}\"\n#define DATE \"{}\"",
cadical_version, commit, compiler_desc, compiler_flags, chrono::Utc::now()
).expect("Failed to write CaDiCaL build.hpp");
cadical_build
.include(cadical_dir.join("src"))
.warnings(false)
.files(src_files)
.compile("cadical");
};
println!("cargo:rustc-link-lib=static=cadical");
#[cfg(target_os = "macos")]
println!("cargo:rustc-flags=-l dylib=c++");
#[cfg(not(target_os = "macos"))]
println!("cargo:rustc-flags=-l dylib=stdc++");
return true;
}
false
}
fn build_kissat(repo: &str, branch: &str, commit: &str) -> bool {
#[cfg(feature = "kissat")]
{
let out_dir = env::var("OUT_DIR").unwrap();
let mut kissat_dir_str = out_dir.clone();
kissat_dir_str.push_str("/kissat");
let kissat_dir = Path::new(&kissat_dir_str);
if update_repo(kissat_dir, repo, branch, commit)
|| !Path::new(&out_dir).join("libkissat.a").exists()
{
let src_files = glob(&format!("{}/src/*.c", kissat_dir_str))
.unwrap()
.filter_map(|res| {
if let Ok(p) = res {
if let Some(name) = p.file_name() {
if name == "main.c"
|| name == "application.c"
|| name == "handle.c"
|| name == "parse.c"
|| name == "witness.c"
{
return None; }
};
Some(p)
} else {
None
}
});
let mut kissat_build = cc::Build::new();
if env::var("PROFILE").unwrap() == "debug" {
kissat_build
.opt_level(0)
.define("DEBUG", None)
.warnings(true)
.debug(true);
} else {
kissat_build
.opt_level(3)
.define("NDEBUG", None)
.warnings(false);
}
let mut build_header = File::create(kissat_dir.join("src").join("build.h"))
.expect("Could not create kissat build header");
let mut kissat_version =
fs::read_to_string(kissat_dir.join("VERSION")).expect("Cannot read kissat version");
kissat_version.retain(|c| c != '\n');
let (compiler_desc, compiler_flags) =
get_compiler_description(kissat_build.get_compiler());
write!(
build_header,
"#define VERSION \"{}\"\n#define COMPILER \"{} {}\"\n#define ID \"{}\"\n#define BUILD \"{}\"\n#define DIR \"{}\"",
kissat_version, compiler_desc, compiler_flags, commit, chrono::Utc::now(), kissat_dir.as_os_str().to_str().unwrap()
).expect("Failed to write kissat build.h");
kissat_build
.include(kissat_dir.join("src"))
.warnings(false)
.files(src_files)
.compile("kissat");
};
println!("cargo:rustc-link-lib=static=kissat");
return true;
}
false
}
fn build_glucose4(repo: &str, branch: &str, commit: &str) -> bool {
#[cfg(feature = "glucose4")]
{
let out_dir = env::var("OUT_DIR").unwrap();
let mut glucose4_dir_str = out_dir.clone();
glucose4_dir_str.push_str("/glucose4");
let glucose4_dir = Path::new(&glucose4_dir_str);
if update_repo(glucose4_dir, repo, branch, commit)
|| !Path::new(&out_dir)
.join("lib")
.join("libglucose4.a")
.exists()
{
cmake::build(glucose4_dir);
};
println!("cargo:rustc-link-lib=static=glucose4");
return true;
}
false
}
fn build_minisat(repo: &str, branch: &str, commit: &str) -> bool {
#[cfg(feature = "minisat")]
{
let out_dir = env::var("OUT_DIR").unwrap();
let mut minisat_dir_str = out_dir.clone();
minisat_dir_str.push_str("/minisat");
let minisat_dir = Path::new(&minisat_dir_str);
if update_repo(minisat_dir, repo, branch, commit)
|| !Path::new(&out_dir)
.join("lib")
.join("libminisat.a")
.exists()
{
cmake::build(minisat_dir);
};
println!("cargo:rustc-link-lib=static=minisat");
return true;
}
false
}
fn update_repo(path: &Path, url: &str, branch: &str, commit: &str) -> bool {
let mut changed = false;
let target_oid = git2::Oid::from_str(commit)
.unwrap_or_else(|e| panic!("Invalid commit hash {}: {}", commit, e));
let repo = match git2::Repository::open(path) {
Ok(repo) => {
if let Some(oid) = repo.head().unwrap().target_peel() {
if oid == target_oid {
return changed;
}
};
if repo.find_commit(target_oid).is_err() {
let mut remote = repo.find_remote("origin").unwrap_or_else(|e| {
panic!("Expected remote \"origin\" in git repo {:?}: {}", path, e)
});
remote.fetch(&[branch], None, None).unwrap_or_else(|e| {
panic!(
"Could not fetch \"origin/{}\" for git repo {:?}: {}",
branch, path, e
)
});
drop(remote);
}
repo
}
Err(_) => {
if path.exists() {
fs::remove_dir_all(path).unwrap_or_else(|e| {
panic!("Could not delete directory {}: e", path.to_str().unwrap())
});
};
changed = true;
git2::Repository::clone(url, path)
.unwrap_or_else(|e| panic!("Could not clone repository {}: {}", url, e))
}
};
let target_commit = repo
.find_commit(target_oid)
.unwrap_or_else(|e| panic!("Could not find commit {}: {}", commit, e));
repo.checkout_tree(target_commit.as_object(), None)
.unwrap_or_else(|e| panic!("Could not checkout commit {}: {}", commit, e));
repo.set_head_detached(target_oid)
.unwrap_or_else(|e| panic!("Could not detach head at {}: {}", commit, e));
changed
}
fn get_compiler_description(compiler: cc::Tool) -> (String, String) {
let compiler_command = compiler.to_command();
let mut first_line = true;
let compiler_version = match Command::new(compiler_command.get_program())
.arg("--version")
.output()
{
Ok(output) => {
let mut version = String::from_utf8(output.stdout).unwrap();
version.retain(|c| {
if first_line && c == '\n' {
first_line = false;
false
} else {
first_line
}
});
version
}
Err(_) => String::from(compiler_command.get_program().to_str().unwrap()),
};
let compiler_flags = compiler.cflags_env();
(
compiler_version,
String::from(compiler_flags.to_str().unwrap()),
)
}