|
3 | 3 |
|
4 | 4 | #![allow(clippy::unwrap_used)] |
5 | 5 | #![allow(clippy::expect_used)] |
6 | | -use std::path::PathBuf; |
| 6 | +use std::path::{Path, PathBuf}; |
7 | 7 | use std::{env, fs}; |
8 | 8 |
|
9 | 9 | use bindgen::Abi; |
@@ -149,20 +149,81 @@ fn http_client() -> Result<reqwest::blocking::Client, Box<dyn std::error::Error> |
149 | 149 | Ok(client) |
150 | 150 | } |
151 | 151 |
|
| 152 | +fn build_duckdb(duckdb_source_root: &Path) -> Result<PathBuf, Box<dyn std::error::Error>> { |
| 153 | + let duckdb_repo_dir = duckdb_source_root.join(format!("duckdb-{}", DUCKDB_VERSION.as_str())); |
| 154 | + let build_dir = duckdb_repo_dir.join("build").join("debug"); |
| 155 | + |
| 156 | + // Build the DuckDB library with ASAN in case `VX_DUCKDB_ASAN=1` is set. |
| 157 | + let asan_option = |
| 158 | + if env::var("VX_DUCKDB_ASAN").is_ok_and(|v| matches!(v.as_str(), "1" | "true")) { |
| 159 | + "0" |
| 160 | + } else { |
| 161 | + "1" |
| 162 | + }; |
| 163 | + |
| 164 | + let output = std::process::Command::new("make") |
| 165 | + .current_dir(&duckdb_repo_dir) |
| 166 | + .env("GEN", "ninja") |
| 167 | + // Run with `ASAN_OPTIONS=detect_container_overflow=0` to skip false positives. |
| 168 | + .env("DISABLE_SANITIZER", asan_option) |
| 169 | + .arg("debug") |
| 170 | + .output()?; |
| 171 | + |
| 172 | + if !output.status.success() { |
| 173 | + return Err(format!( |
| 174 | + "Failed to build DuckDB:\nstdout: {}\nstderr: {}", |
| 175 | + String::from_utf8_lossy(&output.stdout), |
| 176 | + String::from_utf8_lossy(&output.stderr) |
| 177 | + ) |
| 178 | + .into()); |
| 179 | + } |
| 180 | + |
| 181 | + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?); |
| 182 | + let target_dir = manifest_dir.parent().unwrap().join("target"); |
| 183 | + let duckdb_library_dir = target_dir.join("duckdb-lib"); |
| 184 | + |
| 185 | + let _ = fs::remove_dir_all(&duckdb_library_dir); |
| 186 | + fs::create_dir_all(&duckdb_library_dir)?; |
| 187 | + |
| 188 | + // Copy .dylib and .so files (macOS and Linux). |
| 189 | + for entry in fs::read_dir(build_dir.join("src"))? { |
| 190 | + let entry = entry?; |
| 191 | + let path = entry.path(); |
| 192 | + |
| 193 | + if path |
| 194 | + .extension() |
| 195 | + .map(|ext| ext == "dylib" || ext == "so") |
| 196 | + .unwrap_or(false) |
| 197 | + { |
| 198 | + let dest = duckdb_library_dir.join(entry.file_name()); |
| 199 | + fs::copy(&path, &dest)?; |
| 200 | + } |
| 201 | + } |
| 202 | + |
| 203 | + Ok(duckdb_library_dir) |
| 204 | +} |
| 205 | + |
152 | 206 | fn main() { |
153 | 207 | let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); |
154 | 208 | let duckdb_repo = crate_dir.join("duckdb"); |
155 | 209 |
|
156 | | - // Download and extract prebuilt DuckDB libraries. |
157 | | - let zip_lib_path = download_duckdb_lib_archive().unwrap(); |
158 | | - let extraced_lib_path = extract_duckdb_libraries(zip_lib_path).unwrap(); |
159 | | - |
160 | 210 | // Download, extract and symlink DuckDB source code. |
161 | 211 | let zip_source_path = download_duckdb_source_archive().unwrap(); |
162 | 212 | let extracted_source_path = extract_duckdb_source(zip_source_path).unwrap(); |
163 | 213 | let _ = fs::remove_dir_all(&duckdb_repo); |
164 | 214 | std::os::unix::fs::symlink(&extracted_source_path, &duckdb_repo).unwrap(); |
165 | 215 |
|
| 216 | + let library_path = |
| 217 | + // DuckDB debug build is linked in case of `VX_DUCKDB_DEBUG=1`. |
| 218 | + if env::var("VX_DUCKDB_DEBUG").is_ok_and(|v| matches!(v.as_str(), "1" | "true")) { |
| 219 | + // Build DuckDB from source. |
| 220 | + build_duckdb(&extracted_source_path).unwrap() |
| 221 | + } else { |
| 222 | + // Download and extract prebuilt DuckDB libraries. |
| 223 | + let zip_lib_path = download_duckdb_lib_archive().unwrap(); |
| 224 | + extract_duckdb_libraries(zip_lib_path).unwrap() |
| 225 | + }; |
| 226 | + |
166 | 227 | // Generate the _imported_ bindings from our C++ code. |
167 | 228 | bindgen::Builder::default() |
168 | 229 | .header("cpp/include/duckdb_vx.h") |
@@ -206,15 +267,11 @@ fn main() { |
206 | 267 | .expect("Couldn't write bindings!"); |
207 | 268 |
|
208 | 269 | // Link against DuckDB dylib. |
209 | | - println!( |
210 | | - "cargo:rustc-link-search=native={}", |
211 | | - extraced_lib_path.display() |
212 | | - ); |
| 270 | + println!("cargo:rerun-if-env-changed=VX_DUCKDB_DEBUG"); |
| 271 | + println!("cargo:rerun-if-env-changed=VX_DUCKDB_ASAN"); |
| 272 | + println!("cargo:rustc-link-search=native={}", library_path.display()); |
213 | 273 | println!("cargo:rustc-link-lib=dylib=duckdb"); |
214 | | - println!( |
215 | | - "cargo:rustc-link-arg=-Wl,-rpath,{}", |
216 | | - extraced_lib_path.display() |
217 | | - ); |
| 274 | + println!("cargo:rustc-link-arg=-Wl,-rpath,{}", library_path.display()); |
218 | 275 |
|
219 | 276 | // Compile our C++ code that exposes additional DuckDB functionality. |
220 | 277 | cc::Build::new() |
|
0 commit comments