66use std:: path:: PathBuf ;
77use std:: { env, fs} ;
88
9- const DUCKDB_VERSION : & str = "v1 .3.1 " ;
9+ const DUCKDB_VERSION : & str = "1 .3.2 " ;
1010const DUCKDB_BASE_URL : & str = "https://github.com/duckdb/duckdb/releases/download" ;
1111
12- fn download_duckdb_archive ( ) -> Result < PathBuf , Box < dyn std:: error:: Error > > {
12+ fn download_duckdb_lib_archive ( ) -> Result < PathBuf , Box < dyn std:: error:: Error > > {
1313 let manifest_dir = PathBuf :: from ( env:: var ( "CARGO_MANIFEST_DIR" ) ?) ;
1414 let target_dir = manifest_dir. parent ( ) . unwrap ( ) . join ( "target" ) ;
15- let duckdb_dir = target_dir. join ( format ! ( "duckdb-{DUCKDB_VERSION}" ) ) ;
15+ let duckdb_dir = target_dir. join ( format ! ( "duckdb-lib-v {DUCKDB_VERSION}" ) ) ;
1616
1717 let target = env:: var ( "TARGET" ) ?;
1818 let ( platform, arch) = match target. as_str ( ) {
@@ -24,7 +24,7 @@ fn download_duckdb_archive() -> Result<PathBuf, Box<dyn std::error::Error>> {
2424 } ;
2525
2626 let archive_name = format ! ( "libduckdb-{platform}-{arch}.zip" ) ;
27- let url = format ! ( "{DUCKDB_BASE_URL}/{DUCKDB_VERSION}/{archive_name}" ) ;
27+ let url = format ! ( "{DUCKDB_BASE_URL}/v {DUCKDB_VERSION}/{archive_name}" ) ;
2828 let archive_path = duckdb_dir. join ( & archive_name) ;
2929
3030 // Create directory if it doesn't exist.
@@ -43,29 +43,95 @@ fn download_duckdb_archive() -> Result<PathBuf, Box<dyn std::error::Error>> {
4343}
4444
4545fn extract_duckdb_libraries ( archive_path : PathBuf ) -> Result < PathBuf , Box < dyn std:: error:: Error > > {
46- let duckdb_dir = archive_path
46+ let duckdb_lib_dir = archive_path
4747 . parent ( )
4848 . ok_or ( "Invalid archive path" ) ?
4949 . to_path_buf ( ) ;
5050
5151 // Check if already extracted. The archive for Linux only contains a .so library, macOS only .dylib.
52- if duckdb_dir. join ( "libduckdb.dylib" ) . exists ( ) || duckdb_dir. join ( "libduckdb.so" ) . exists ( ) {
52+ if duckdb_lib_dir. join ( "libduckdb.dylib" ) . exists ( )
53+ || duckdb_lib_dir. join ( "libduckdb.so" ) . exists ( )
54+ {
5355 println ! ( "DuckDB libraries already extracted, skipping extraction" ) ;
54- return Ok ( duckdb_dir ) ;
56+ return Ok ( duckdb_lib_dir ) ;
5557 }
5658
5759 let file = fs:: File :: open ( & archive_path) ?;
5860 let mut archive = zip:: ZipArchive :: new ( file) ?;
59- archive. extract ( & duckdb_dir) ?;
60- println ! ( "Extracting DuckDB libraries to {}" , duckdb_dir. display( ) ) ;
61+ archive. extract ( & duckdb_lib_dir) ?;
62+ println ! (
63+ "Extracting DuckDB libraries to {}" ,
64+ duckdb_lib_dir. display( )
65+ ) ;
6166
62- Ok ( duckdb_dir)
67+ Ok ( duckdb_lib_dir)
68+ }
69+
70+ fn download_duckdb_source_archive ( ) -> Result < PathBuf , Box < dyn std:: error:: Error > > {
71+ let manifest_dir = PathBuf :: from ( env:: var ( "CARGO_MANIFEST_DIR" ) ?) ;
72+ let target_dir = manifest_dir. parent ( ) . unwrap ( ) . join ( "target" ) ;
73+ let duckdb_source_dir = target_dir. join ( format ! ( "duckdb-source-v{DUCKDB_VERSION}" ) ) ;
74+ let archive_name = format ! ( "duckdb-source-v{DUCKDB_VERSION}.zip" ) ;
75+ let url = format ! ( "https://github.com/duckdb/duckdb/archive/refs/tags/v{DUCKDB_VERSION}.zip" ) ;
76+ let archive_path = duckdb_source_dir. join ( & archive_name) ;
77+
78+ // Create directory if it doesn't exist.
79+ fs:: create_dir_all ( & duckdb_source_dir) ?;
80+
81+ if !archive_path. exists ( ) {
82+ println ! ( "Downloading DuckDB source code from {url}" ) ;
83+ let response = reqwest:: blocking:: get ( & url) ?;
84+ fs:: write ( & archive_path, & response. bytes ( ) ?) ?;
85+ println ! ( "Downloaded to {}" , archive_path. display( ) ) ;
86+ } else {
87+ println ! ( "Source archive already exists, skipping download" ) ;
88+ }
89+
90+ Ok ( archive_path)
91+ }
92+
93+ fn extract_duckdb_source ( archive_path : PathBuf ) -> Result < PathBuf , Box < dyn std:: error:: Error > > {
94+ let duckdb_source_dir = archive_path
95+ . parent ( )
96+ . ok_or ( "Invalid archive path" ) ?
97+ . to_path_buf ( ) ;
98+
99+ // Check if the source is already extracted.
100+ if duckdb_source_dir
101+ . join ( format ! ( "duckdb-{DUCKDB_VERSION}/CMakeLists.txt" ) )
102+ . exists ( )
103+ {
104+ println ! ( "DuckDB source already extracted, skipping extraction" ) ;
105+ return Ok ( duckdb_source_dir) ;
106+ }
107+
108+ let file = fs:: File :: open ( & archive_path) ?;
109+ let mut archive = zip:: ZipArchive :: new ( file) ?;
110+ archive. extract ( & duckdb_source_dir) ?;
111+ println ! (
112+ "Extracting DuckDB source to {}" ,
113+ duckdb_source_dir. display( )
114+ ) ;
115+
116+ Ok ( duckdb_source_dir)
63117}
64118
65119fn main ( ) {
66120 let crate_dir = PathBuf :: from ( env:: var ( "CARGO_MANIFEST_DIR" ) . unwrap ( ) ) ;
67121 let duckdb_repo = crate_dir. join ( "duckdb" ) ;
68122
123+ // Download and extract prebuilt DuckDB libraries.
124+ let zip_lib_path = download_duckdb_lib_archive ( ) . unwrap ( ) ;
125+ let extraced_lib_path = extract_duckdb_libraries ( zip_lib_path) . unwrap ( ) ;
126+
127+ // Download, extract and symlink DuckDB source code.
128+ let zip_source_path = download_duckdb_source_archive ( ) . unwrap ( ) ;
129+ let extracted_source_path = extract_duckdb_source ( zip_source_path) . unwrap ( ) ;
130+ if duckdb_repo. exists ( ) {
131+ fs:: remove_file ( & duckdb_repo) . unwrap ( ) ;
132+ }
133+ std:: os:: unix:: fs:: symlink ( & extracted_source_path, & duckdb_repo) . unwrap ( ) ;
134+
69135 // Generate the _imported_ bindings from our C++ code.
70136 bindgen:: Builder :: default ( )
71137 . header ( "cpp/include/duckdb_vx.h" )
@@ -81,7 +147,10 @@ fn main() {
81147 //.generate_cstr(true) // This emits invalid syntax and breaks the Rust formatter
82148 . clang_arg ( format ! (
83149 "-I{}" ,
84- duckdb_repo. join( "src/include" ) . to_str( ) . unwrap( )
150+ duckdb_repo
151+ . join( format!( "duckdb-{DUCKDB_VERSION}/src/include" ) )
152+ . to_str( )
153+ . unwrap( )
85154 ) )
86155 . clang_arg ( format ! (
87156 "-I{}" ,
@@ -97,14 +166,16 @@ fn main() {
97166 . write_to_file ( crate_dir. join ( "src/cpp.rs" ) )
98167 . expect ( "Couldn't write bindings!" ) ;
99168
100- // Download and extract prebuilt DuckDB libraries.
101- let zip_path = download_duckdb_archive ( ) . unwrap ( ) ;
102- let lib_path = extract_duckdb_libraries ( zip_path) . unwrap ( ) ;
103-
104169 // Link against DuckDB dylib.
105- println ! ( "cargo:rustc-link-search=native={}" , lib_path. display( ) ) ;
170+ println ! (
171+ "cargo:rustc-link-search=native={}" ,
172+ extraced_lib_path. display( )
173+ ) ;
106174 println ! ( "cargo:rustc-link-lib=dylib=duckdb" ) ;
107- println ! ( "cargo:rustc-link-arg=-Wl,-rpath,{}" , lib_path. display( ) ) ;
175+ println ! (
176+ "cargo:rustc-link-arg=-Wl,-rpath,{}" ,
177+ extraced_lib_path. display( )
178+ ) ;
108179
109180 if env:: var ( "TARGET" ) . unwrap ( ) . contains ( "linux" ) {
110181 println ! ( "cargo:rustc-link-lib=stdc++" ) ;
@@ -119,7 +190,7 @@ fn main() {
119190 . flag ( "-Wno-c++20-designator" )
120191 . flag ( "-Wno-unused-parameter" )
121192 // We include DuckDB headers from the DuckDB extension submodule.
122- . include ( duckdb_repo. join ( " src/include") )
193+ . include ( duckdb_repo. join ( format ! ( "duckdb-{DUCKDB_VERSION}/ src/include") ) )
123194 . include ( "cpp/include" )
124195 . file ( "cpp/copy_function.cpp" )
125196 . file ( "cpp/data.cpp" )
0 commit comments