use std::env;
use std::path::{Path, PathBuf};
fn link_mode() -> &'static str {
if env::var("LBUG_SHARED").is_ok() {
"dylib"
} else {
"static"
}
}
fn get_target() -> String {
env::var("PROFILE").unwrap()
}
fn link_libraries() {
if !cfg!(windows) && link_mode() == "static" {
println!("cargo:rustc-link-arg=-rdynamic");
}
if cfg!(windows) && link_mode() == "dylib" {
println!("cargo:rustc-link-lib=dylib=lbug_shared");
} else if link_mode() == "dylib" {
println!("cargo:rustc-link-lib={}=lbug", link_mode());
} else if rustversion::cfg!(since(1.82)) {
println!("cargo:rustc-link-lib=static:+whole-archive=lbug");
} else {
println!("cargo:rustc-link-lib=static=lbug");
}
if link_mode() == "static" {
if cfg!(windows) {
println!("cargo:rustc-link-lib=dylib=msvcrt");
println!("cargo:rustc-link-lib=dylib=shell32");
println!("cargo:rustc-link-lib=dylib=ole32");
} else if cfg!(target_os = "macos") {
println!("cargo:rustc-link-lib=dylib=c++");
} else {
println!("cargo:rustc-link-lib=dylib=stdc++");
}
for lib in [
"utf8proc",
"antlr4_cypher",
"antlr4_runtime",
"re2",
"fastpfor",
"parquet",
"thrift",
"snappy",
"zstd",
"miniz",
"mbedtls",
"brotlidec",
"brotlicommon",
"lz4",
"roaring_bitmap",
"simsimd",
"yyjson",
] {
if rustversion::cfg!(since(1.82)) {
println!("cargo:rustc-link-lib=static:+whole-archive={lib}");
} else {
println!("cargo:rustc-link-lib=static={lib}");
}
}
}
}
fn get_lbug_root() -> PathBuf {
let manifest_dir_str = std::env::var("CARGO_MANIFEST_DIR").unwrap();
let manifest_dir = Path::new(&manifest_dir_str);
let root = manifest_dir.join("lbug-src");
if root.is_symlink() || root.is_dir() {
return root;
}
if cfg!(windows) {
return manifest_dir.join("../..");
}
let lbug_dir = manifest_dir.join("lbug-src");
if !lbug_dir.exists() {
let version = std::env::var("LBUG_VERSION").unwrap_or_else(|_| "main".to_string());
println!("Downloading ladybug source version {version}...");
let url = if version.starts_with('v') {
format!(
"https://github.com/LadybugDB/ladybug/archive/refs/tags/{}.tar.gz",
version
)
} else if version == "main" {
"https://github.com/LadybugDB/ladybug/archive/refs/heads/main.tar.gz".to_string()
} else {
format!(
"https://github.com/LadybugDB/ladybug/archive/refs/tags/v{}.tar.gz",
version
)
};
let output = std::process::Command::new("curl")
.args(["-sL", &url])
.arg("-o")
.arg("ladybug.tar.gz")
.current_dir(manifest_dir)
.output()
.expect("Failed to download ladybug source");
if !output.status.success() {
panic!("Failed to download ladybug source from {}", url);
}
std::fs::create_dir_all(&lbug_dir).expect("Failed to create lbug-src directory");
std::process::Command::new("tar")
.args([
"-xzf",
"ladybug.tar.gz",
"--strip-components=1",
"-C",
"lbug-src",
])
.current_dir(manifest_dir)
.status()
.expect("Failed to extract ladybug source");
std::fs::remove_file(manifest_dir.join("ladybug.tar.gz")).ok();
}
lbug_dir
}
fn build_bundled_cmake() -> Vec<PathBuf> {
let lbug_root = get_lbug_root();
let mut build = cmake::Config::new(&lbug_root);
build
.no_build_target(true)
.define("BUILD_SHELL", "OFF")
.define("BUILD_SINGLE_FILE_HEADER", "OFF")
.define("AUTO_UPDATE_GRAMMAR", "OFF");
if cfg!(windows) {
build.generator("Ninja");
build.cxxflag("/EHsc");
build.define("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreadedDLL");
build.define("CMAKE_POLICY_DEFAULT_CMP0091", "NEW");
}
if let Ok(jobs) = std::env::var("NUM_JOBS") {
std::env::set_var("CMAKE_BUILD_PARALLEL_LEVEL", jobs);
}
let build_dir = build.build();
let lbug_lib_path = build_dir.join("build").join("src");
println!("cargo:rustc-link-search=native={}", lbug_lib_path.display());
for dir in [
"utf8proc",
"antlr4_cypher",
"antlr4_runtime",
"re2",
"brotli",
"alp",
"fastpfor",
"parquet",
"thrift",
"snappy",
"zstd",
"miniz",
"mbedtls",
"lz4",
"roaring_bitmap",
"simsimd",
"yyjson",
] {
let lib_path = build_dir
.join("build")
.join("third_party")
.join(dir)
.canonicalize()
.unwrap_or_else(|_| {
panic!(
"Could not find {}/build/third_party/{}",
build_dir.display(),
dir
)
});
println!("cargo:rustc-link-search=native={}", lib_path.display());
}
vec![
lbug_root.join("src/include"),
build_dir.join("build/src"),
build_dir.join("build/src/include"),
lbug_root.join("third_party/nlohmann_json"),
lbug_root.join("third_party/fastpfor"),
lbug_root.join("third_party/alp/include"),
]
}
fn build_ffi(
bridge_file: &str,
out_name: &str,
source_file: &str,
bundled: bool,
include_paths: &Vec<PathBuf>,
) {
let mut build = cxx_build::bridge(bridge_file);
build.file(source_file);
if bundled {
build.define("LBUG_BUNDLED", None);
}
if get_target() == "debug" || get_target() == "relwithdebinfo" {
build.define("ENABLE_RUNTIME_CHECKS", "1");
}
if link_mode() == "static" {
build.define("LBUG_STATIC_DEFINE", None);
}
build.includes(include_paths);
println!("cargo:rerun-if-env-changed=LBUG_SHARED");
println!("cargo:rerun-if-changed=include/lbug_rs.h");
println!("cargo:rerun-if-changed=src/lbug_rs.cpp");
println!("cargo:rerun-if-changed=lbug-src/src");
println!("cargo:rerun-if-changed=lbug-src/cmake");
println!("cargo:rerun-if-changed=lbug-src/third_party");
println!("cargo:rerun-if-changed=lbug-src/CMakeLists.txt");
println!("cargo:rerun-if-changed=lbug-src/tools/CMakeLists.txt");
if cfg!(windows) {
build.flag("/std:c++20");
build.flag("/MD");
} else {
build.flag("-std=c++2a");
}
build.compile(out_name);
}
fn main() {
if env::var("DOCS_RS").is_ok() {
return;
}
let mut bundled = false;
let mut include_paths =
vec![Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("include")];
if let (Ok(lbug_lib_dir), Ok(lbug_include)) =
(env::var("LBUG_LIBRARY_DIR"), env::var("LBUG_INCLUDE_DIR"))
{
println!("cargo:rustc-link-search=native={lbug_lib_dir}");
println!("cargo:rustc-link-arg=-Wl,-rpath,{lbug_lib_dir}");
include_paths.push(Path::new(&lbug_include).to_path_buf());
} else {
include_paths.extend(build_bundled_cmake());
bundled = true;
}
if link_mode() == "static" {
link_libraries();
}
build_ffi(
"src/ffi.rs",
"lbug_rs",
"src/lbug_rs.cpp",
bundled,
&include_paths,
);
if cfg!(feature = "arrow") {
build_ffi(
"src/ffi/arrow.rs",
"lbug_arrow_rs",
"src/lbug_arrow.cpp",
bundled,
&include_paths,
);
}
if link_mode() == "dylib" {
link_libraries();
}
}