|
1 | | -use std::path::PathBuf; |
| 1 | +use std::env; |
| 2 | +use std::fs; |
| 3 | +use std::path::{Path, PathBuf}; |
| 4 | +use std::process::Command; |
2 | 5 |
|
3 | 6 | fn main() { |
4 | | - // Tell cargo to look for shared libraries in the specified directory |
5 | | - println!("cargo:rustc-link-search=./"); |
| 7 | + let out_dir = env::var("OUT_DIR").unwrap(); |
| 8 | + let out_path = PathBuf::from(&out_dir); |
| 9 | + let libchdb_info = find_libchdb_or_download(&out_path); |
| 10 | + match libchdb_info { |
| 11 | + Ok((lib_dir, header_path)) => { |
| 12 | + setup_link_paths(&lib_dir); |
| 13 | + generate_bindings(&header_path, &out_path); |
| 14 | + } |
| 15 | + Err(e) => { |
| 16 | + eprintln!("Failed to find or download libchdb: {}", e); |
| 17 | + println!("cargo:warning=Failed to find libchdb. Please install manually using './update_libchdb.sh --local' or '--global'"); |
| 18 | + std::process::exit(1); |
| 19 | + } |
| 20 | + } |
| 21 | +} |
6 | 22 |
|
7 | | - // Tell cargo to tell rustc to link the system chdb library. |
8 | | - println!("cargo:rustc-link-lib=chdb"); |
| 23 | +fn find_libchdb_or_download(out_dir: &Path) -> Result<(PathBuf, PathBuf), Box<dyn std::error::Error>> { |
| 24 | + if let Some((lib_dir, header_path)) = find_existing_libchdb() { |
| 25 | + return Ok((lib_dir, header_path)); |
| 26 | + } |
| 27 | + |
| 28 | + println!("cargo:warning=libchdb not found locally, attempting to download..."); |
| 29 | + download_libchdb_to_out_dir(out_dir)?; |
| 30 | + let lib_dir = out_dir.to_path_buf(); |
| 31 | + let header_path = out_dir.join("chdb.h"); |
| 32 | + |
| 33 | + if !header_path.exists() { |
| 34 | + return Err("Header file not found after download".into()); |
| 35 | + } |
| 36 | + |
| 37 | + Ok((lib_dir, header_path)) |
| 38 | +} |
9 | 39 |
|
10 | | - // Tell cargo to invalidate the built crate whenever the wrapper changes. |
11 | | - println!("cargo:rerun-if-changed=chdb.h"); |
| 40 | +fn find_existing_libchdb() -> Option<(PathBuf, PathBuf)> { |
| 41 | + if Path::new("./libchdb.so").exists() && Path::new("./chdb.h").exists() { |
| 42 | + return Some((PathBuf::from("."), PathBuf::from("./chdb.h"))); |
| 43 | + } |
| 44 | + |
| 45 | + // Check system installation |
| 46 | + let system_lib_path = Path::new("/usr/local/lib"); |
| 47 | + let system_header_path = Path::new("/usr/local/include/chdb.h"); |
| 48 | + |
| 49 | + if system_header_path.exists() { |
| 50 | + if system_lib_path.join("libchdb.so").exists() || |
| 51 | + system_lib_path.join("libchdb.dylib").exists() { |
| 52 | + return Some((system_lib_path.to_path_buf(), system_header_path.to_path_buf())); |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + None |
| 57 | +} |
| 58 | + |
| 59 | +fn download_libchdb_to_out_dir(out_dir: &Path) -> Result<(), Box<dyn std::error::Error>> { |
| 60 | + let platform = get_platform_string()?; |
| 61 | + let version = "v2.1.1"; |
| 62 | + let url = format!( |
| 63 | + "https://github.com/chdb-io/chdb/releases/download/{}/{}", |
| 64 | + version, platform |
| 65 | + ); |
| 66 | + println!("cargo:warning=Downloading libchdb from: {}", url); |
| 67 | + let response = reqwest::blocking::get(&url)?; |
| 68 | + let content = response.bytes()?; |
| 69 | + let temp_archive = out_dir.join("libchdb.tar.gz"); |
| 70 | + fs::write(&temp_archive, content)?; |
| 71 | + let file = fs::File::open(&temp_archive)?; |
| 72 | + let mut archive = tar::Archive::new(flate2::read::GzDecoder::new(file)); |
| 73 | + archive.unpack(out_dir)?; |
| 74 | + fs::remove_file(&temp_archive)?; |
| 75 | + if cfg!(unix) { |
| 76 | + let lib_path = out_dir.join("libchdb.so"); |
| 77 | + if lib_path.exists() { |
| 78 | + let _ = Command::new("chmod") |
| 79 | + .args(&["+x", lib_path.to_str().unwrap()]) |
| 80 | + .output(); |
| 81 | + } |
| 82 | + } |
| 83 | + println!("cargo:warning=libchdb downloaded successfully to OUT_DIR"); |
| 84 | + Ok(()) |
| 85 | +} |
12 | 86 |
|
13 | | - // The bindgen::Builder is the main entry point |
14 | | - // to bindgen, and lets you build up options for |
15 | | - // the resulting bindings. |
| 87 | +fn get_platform_string() -> Result<String, &'static str> { |
| 88 | + let os = env::consts::OS; |
| 89 | + let arch = env::consts::ARCH; |
| 90 | + match (os, arch) { |
| 91 | + ("linux", "x86_64") => Ok("linux-x86_64-libchdb.tar.gz".to_string()), |
| 92 | + ("linux", "aarch64") => Ok("linux-aarch64-libchdb.tar.gz".to_string()), |
| 93 | + ("macos", "x86_64") => Ok("macos-x86_64-libchdb.tar.gz".to_string()), |
| 94 | + ("macos", "aarch64") => Ok("macos-arm64-libchdb.tar.gz".to_string()), |
| 95 | + _ => Err("Unsupported platform"), |
| 96 | + } |
| 97 | +} |
| 98 | + |
| 99 | +fn setup_link_paths(lib_dir: &Path) { |
| 100 | + println!("cargo:rustc-link-search={}", lib_dir.display()); |
| 101 | + println!("cargo:rustc-link-search=./"); |
| 102 | + println!("cargo:rustc-link-search=/usr/local/lib"); |
| 103 | + println!("cargo:rustc-link-lib=chdb"); |
| 104 | + println!("cargo:rerun-if-changed=wrapper.h"); |
| 105 | + println!("cargo:rerun-if-changed=build.rs"); |
| 106 | +} |
| 107 | + |
| 108 | +fn generate_bindings(header_path: &Path, out_dir: &Path) { |
| 109 | + let wrapper_content = format!("#include \"{}\"", header_path.display()); |
| 110 | + let temp_wrapper = out_dir.join("temp_wrapper.h"); |
| 111 | + fs::write(&temp_wrapper, wrapper_content).expect("Failed to write temp wrapper"); |
16 | 112 | let bindings = bindgen::Builder::default() |
17 | | - // The input header we would like to generate |
18 | | - // bindings for. |
19 | | - .header("wrapper.h") |
20 | | - // Tell cargo to invalidate the built crate whenever any of the |
21 | | - // included header files changed. |
| 113 | + .header(temp_wrapper.to_str().unwrap()) |
22 | 114 | .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) |
23 | | - // Finish the builder and generate the bindings. |
24 | 115 | .generate() |
25 | | - // Unwrap the Result and panic on failure. |
26 | 116 | .expect("Unable to generate bindings"); |
27 | | - |
28 | | - // Write the bindings to the $OUT_DIR/bindings.rs file. |
29 | | - let out_path = PathBuf::from("./src/"); |
| 117 | + let src_path = PathBuf::from("./src/"); |
| 118 | + bindings |
| 119 | + .write_to_file(src_path.join("bindings.rs")) |
| 120 | + .expect("Couldn't write bindings to src!"); |
30 | 121 | bindings |
31 | | - .write_to_file(out_path.join("bindings.rs")) |
32 | | - .expect("Couldn't write bindings!"); |
| 122 | + .write_to_file(out_dir.join("bindings.rs")) |
| 123 | + .expect("Couldn't write bindings to OUT_DIR!"); |
33 | 124 | } |
0 commit comments