use std::{
env,
path::{Path, PathBuf},
};
const Z3_SRC_SOURCE_DIR: &str = "Z3_SRC_SOURCE_DIR";
pub struct Artifacts {
include_dir: PathBuf,
lib_dir: PathBuf,
}
impl Artifacts {
pub fn include_dir(&self) -> &Path {
&self.include_dir
}
pub fn lib_dir(&self) -> &Path {
&self.lib_dir
}
pub fn print_cargo_metadata(&self) {
println!("cargo:rustc-link-search=native={}", self.lib_dir.display());
if env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("msvc") {
if self.lib_dir.join("libz3-static.lib").exists() {
println!("cargo:rustc-link-lib=static=libz3-static");
} else {
println!("cargo:rustc-link-lib=static=libz3");
}
} else {
println!("cargo:rustc-link-lib=static=z3");
}
}
}
pub fn build() -> Artifacts {
println!("cargo:rerun-if-env-changed={Z3_SRC_SOURCE_DIR}");
let src_dir = resolve_source();
let dst = build_cmake(&src_dir);
let lib_dir = find_lib_dir(&dst);
let include_dir = dst.join("include");
Artifacts {
include_dir,
lib_dir,
}
}
fn resolve_source() -> PathBuf {
if let Ok(dir) = env::var(Z3_SRC_SOURCE_DIR) {
let path = PathBuf::from(dir);
assert!(
path.exists(),
"{Z3_SRC_SOURCE_DIR} points to non-existent path: {}",
path.display()
);
return path;
}
let submodule = Path::new(env!("CARGO_MANIFEST_DIR")).join("z3");
if submodule.exists() {
return submodule;
}
panic!("Z3 source not found — run `git submodule update --init` or set {Z3_SRC_SOURCE_DIR}");
}
fn build_cmake(src_dir: &Path) -> PathBuf {
let mut cfg = cmake::Config::new(src_dir);
cfg.define("Z3_BUILD_LIBZ3_SHARED", "false")
.define("Z3_BUILD_EXECUTABLE", "false")
.define("Z3_BUILD_TEST_EXECUTABLES", "false")
.define("Z3_ENABLE_EXAMPLE_TARGETS", "false");
for var in &["CMAKE_C_COMPILER_LAUNCHER", "CMAKE_CXX_COMPILER_LAUNCHER"] {
if let Ok(val) = env::var(var) {
cfg.define(var, val);
}
}
if env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") {
cfg.cxxflag("-DWIN32");
cfg.cxxflag("-D_WINDOWS");
if env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("msvc") {
if env::var("CMAKE_CXX_COMPILER_LAUNCHER").is_err() {
cfg.cxxflag("-MP");
cfg.build_arg("-m");
}
let target_features = env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or_default();
let runtime = if target_features.split(',').any(|f| f.trim() == "crt-static") {
"MultiThreaded"
} else {
"MultiThreadedDLL"
};
cfg.define("CMAKE_MSVC_RUNTIME_LIBRARY", runtime);
}
} else if env::var("TARGET").unwrap().starts_with("wasm") {
cfg.no_default_flags(true).cxxflag("-fexceptions");
}
cfg.build()
}
fn find_lib_dir(dst: &Path) -> PathBuf {
for candidate in &["lib", "lib64"] {
let dir = dst.join(candidate);
if dir.exists() {
if *candidate == "lib64" {
assert_eq!(
env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap(),
"64",
"lib64 only valid for 64-bit targets"
);
}
return dir;
}
}
panic!(
"Could not find lib directory in Z3 build output at {}",
dst.display()
);
}