Skip to content

Commit 51b9afa

Browse files
authored
feat: allow for linking DuckDB debug build (#4705)
Signed-off-by: Alexander Droste <[email protected]>
1 parent a8df862 commit 51b9afa

File tree

2 files changed

+124
-16
lines changed

2 files changed

+124
-16
lines changed

vortex-duckdb/README.md

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,55 @@
1-
# Vortex DuckDB Extension
1+
# Vortex DuckDB
22

3-
This is an experimental DuckDB extension for Vortex (vs our current production one in vortex-duckdb) written in Rust
4-
with custom C bindings to DuckDB. These bindings expose more functionality than DuckDB's own C API.
3+
Rust bindings for DuckDB. Supports DuckDB precompiled libraries for fast builds and from source builds for debugging.
4+
5+
## Prerequisites
6+
7+
- **Ninja**: `brew install ninja` (macOS) | `apt-get install ninja-build` (Ubuntu)
8+
- **CMake**: `brew install cmake` (macOS) | `apt-get install cmake` (Ubuntu)
9+
- **C++17 compatible compiler**: GCC or Clang
10+
11+
## Build Modes
12+
13+
### Default (Release)
14+
15+
Link against the precompiled DuckDB release build.
16+
17+
```bash
18+
cargo build -p vortex-duckdb
19+
```
20+
21+
### Debug Build
22+
23+
Opt into DuckDB debug build: `VX_DUCKDB_DEBUG=1`.
24+
25+
```bash
26+
VX_DUCKDB_DEBUG=1 cargo build -p vortex-duckdb
27+
```
28+
29+
### AddressSanitizer
30+
31+
Enable ASAN: `VX_DUCKDB_ASAN=1`.
32+
33+
```bash
34+
VX_DUCKDB_DEBUG=1 VX_DUCKDB_ASAN=1 cargo build -p vortex-duckdb
35+
```
36+
37+
## Environment Variables
38+
39+
| Variable | Effect |
40+
| ----------------- | ------------------------------- |
41+
| `VX_DUCKDB_DEBUG` | Build from source in debug mode |
42+
| `VX_DUCKDB_ASAN` | Enable AddressSanitizer |
43+
44+
## Running Tests
45+
46+
```bash
47+
# By default, link against the precompiled DuckDB release build.
48+
cargo test -p vortex-duckdb
49+
50+
# Link against the DuckDB debug build from source.
51+
VX_DUCKDB_DEBUG=1 cargo test -p vortex-duckdb
52+
53+
# Link against the DuckDB debug build from source with ASAN.
54+
ASAN_OPTIONS=detect_container_overflow=0 VX_DUCKDB_DEBUG=1 VX_DUCKDB_ASAN=1 cargo test -p vortex-duckdb
55+
```

vortex-duckdb/build.rs

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
#![allow(clippy::unwrap_used)]
55
#![allow(clippy::expect_used)]
6-
use std::path::PathBuf;
6+
use std::path::{Path, PathBuf};
77
use std::{env, fs};
88

99
use bindgen::Abi;
@@ -149,20 +149,81 @@ fn http_client() -> Result<reqwest::blocking::Client, Box<dyn std::error::Error>
149149
Ok(client)
150150
}
151151

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+
152206
fn main() {
153207
let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
154208
let duckdb_repo = crate_dir.join("duckdb");
155209

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-
160210
// Download, extract and symlink DuckDB source code.
161211
let zip_source_path = download_duckdb_source_archive().unwrap();
162212
let extracted_source_path = extract_duckdb_source(zip_source_path).unwrap();
163213
let _ = fs::remove_dir_all(&duckdb_repo);
164214
std::os::unix::fs::symlink(&extracted_source_path, &duckdb_repo).unwrap();
165215

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+
166227
// Generate the _imported_ bindings from our C++ code.
167228
bindgen::Builder::default()
168229
.header("cpp/include/duckdb_vx.h")
@@ -206,15 +267,11 @@ fn main() {
206267
.expect("Couldn't write bindings!");
207268

208269
// 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());
213273
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());
218275

219276
// Compile our C++ code that exposes additional DuckDB functionality.
220277
cc::Build::new()

0 commit comments

Comments
 (0)