1use std::{
2 env,
3 path::{Path, PathBuf},
4};
5
6const Z3_SRC_SOURCE_DIR: &str = "Z3_SRC_SOURCE_DIR";
7
8pub struct Artifacts {
10 include_dir: PathBuf,
11 lib_dir: PathBuf,
12}
13
14impl Artifacts {
15 pub fn include_dir(&self) -> &Path {
17 &self.include_dir
18 }
19
20 pub fn lib_dir(&self) -> &Path {
22 &self.lib_dir
23 }
24
25 pub fn print_cargo_metadata(&self) {
27 println!("cargo:rustc-link-search=native={}", self.lib_dir.display());
28 if env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("msvc") {
34 if self.lib_dir.join("libz3-static.lib").exists() {
35 println!("cargo:rustc-link-lib=static=libz3-static");
36 } else {
37 println!("cargo:rustc-link-lib=static=libz3");
38 }
39 } else {
40 println!("cargo:rustc-link-lib=static=z3");
41 }
42 }
43}
44
45pub fn build() -> Artifacts {
54 println!("cargo:rerun-if-env-changed={Z3_SRC_SOURCE_DIR}");
55
56 let src_dir = resolve_source();
57
58 let dst = build_cmake(&src_dir);
59
60 let lib_dir = find_lib_dir(&dst);
61
62 let include_dir = dst.join("include");
63
64 Artifacts {
65 include_dir,
66 lib_dir,
67 }
68}
69
70fn resolve_source() -> PathBuf {
71 if let Ok(dir) = env::var(Z3_SRC_SOURCE_DIR) {
72 let path = PathBuf::from(dir);
73 assert!(
74 path.exists(),
75 "{Z3_SRC_SOURCE_DIR} points to non-existent path: {}",
76 path.display()
77 );
78 return path;
79 }
80
81 let submodule = Path::new(env!("CARGO_MANIFEST_DIR")).join("z3");
84 if submodule.exists() {
85 return submodule;
86 }
87
88 panic!("Z3 source not found — run `git submodule update --init` or set {Z3_SRC_SOURCE_DIR}");
95}
96
97fn build_cmake(src_dir: &Path) -> PathBuf {
98 let mut cfg = cmake::Config::new(src_dir);
99
100 cfg.define("Z3_BUILD_LIBZ3_SHARED", "false")
101 .define("Z3_BUILD_EXECUTABLE", "false")
102 .define("Z3_BUILD_TEST_EXECUTABLES", "false")
103 .define("Z3_ENABLE_EXAMPLE_TARGETS", "false");
104
105 for var in &["CMAKE_C_COMPILER_LAUNCHER", "CMAKE_CXX_COMPILER_LAUNCHER"] {
108 if let Ok(val) = env::var(var) {
109 cfg.define(var, val);
110 }
111 }
112
113 if env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") {
114 cfg.cxxflag("-DWIN32");
115 cfg.cxxflag("-D_WINDOWS");
116
117 if env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("msvc") {
121 if env::var("CMAKE_CXX_COMPILER_LAUNCHER").is_err() {
124 cfg.cxxflag("-MP");
125 cfg.build_arg("-m");
126 }
127
128 let target_features = env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or_default();
129 let runtime = if target_features.split(',').any(|f| f.trim() == "crt-static") {
130 "MultiThreaded"
131 } else {
132 "MultiThreadedDLL"
133 };
134 cfg.define("CMAKE_MSVC_RUNTIME_LIBRARY", runtime);
135 }
136 } else if env::var("TARGET").unwrap().starts_with("wasm") {
137 cfg.no_default_flags(true).cxxflag("-fexceptions");
139 }
140
141 cfg.build()
142}
143
144fn find_lib_dir(dst: &Path) -> PathBuf {
145 for candidate in &["lib", "lib64"] {
146 let dir = dst.join(candidate);
147 if dir.exists() {
148 if *candidate == "lib64" {
149 assert_eq!(
150 env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap(),
151 "64",
152 "lib64 only valid for 64-bit targets"
153 );
154 }
155 return dir;
156 }
157 }
158 panic!(
159 "Could not find lib directory in Z3 build output at {}",
160 dst.display()
161 );
162}