use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
fn get_compiler_include_paths(compiler_path: &Path) -> Result<Vec<String>, std::io::Error> {
println!(
"cargo:info=Sysroot 自动检测失败,回退至解析编译器 include 路径模式。"
);
let null_device = if cfg!(windows) { "NUL" } else { "/dev/null" };
let output = Command::new(compiler_path)
.arg("-E")
.arg("-Wp,-v")
.arg("-xc")
.arg(null_device)
.output()?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
println!(
"cargo:warning=从编译器获取 include 路径失败。 Stderr:\n{}",
stderr
);
return Ok(Vec::new());
}
let stderr = String::from_utf8_lossy(&output.stderr);
let mut clang_args = Vec::new();
let mut in_search_list = false;
for line in stderr.lines() {
if line.starts_with("#include <...> search starts here:") {
in_search_list = true;
continue;
}
if line.starts_with("End of search list.") {
break;
}
if in_search_list {
clang_args.push(format!("-I{}", line.trim()));
}
}
if clang_args.is_empty() {
println!(
"cargo:warning=无法自动确定编译器的 include 路径。 Stderr:\n{}",
stderr
);
} else {
println!(
"cargo:info=成功解析到编译器 include 路径: {:?}",
clang_args
);
}
Ok(clang_args)
}
fn main() {
let target = env::var("TARGET").unwrap();
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let mut build = cc::Build::new();
let mut bindings = bindgen::Builder::default();
let compiler = build.get_compiler();
let compiler_path = compiler.path();
let sysroot_output = Command::new(compiler_path).arg("-print-sysroot").output();
let mut found_headers = false;
match sysroot_output {
Ok(output) if output.status.success() => {
let sysroot_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !sysroot_path.is_empty() {
println!(
"cargo:info=为目标 {} 自动检测到 sysroot: {}",
target, sysroot_path
);
let sysroot_arg = format!("--sysroot={}", sysroot_path);
build.flag(&sysroot_arg);
bindings = bindings.clang_arg(&sysroot_arg);
found_headers = true;
}
}
_ => { }
}
if !found_headers {
match get_compiler_include_paths(compiler_path) {
Ok(include_args) => {
for arg in include_args {
build.flag(arg.as_str());
bindings = bindings.clang_arg(arg);
}
}
Err(e) => {
println!(
"cargo:warning=执行编译器以获取 include 路径失败。错误: {}",
e
);
}
}
}
let mut srcs = vec![
"flashdb/fdb.c",
"flashdb/fdb_kvdb.c",
"flashdb/fdb_tsdb.c",
"flashdb/fdb_utils.c",
];
let use_64bit_timestamp = cfg!(feature = "time64");
let use_kvdb = cfg!(feature = "kvdb");
let use_tsdb = cfg!(feature = "tsdb");
let use_log = cfg!(feature = "log");
let debug_enabled = cfg!(debug_assertions);
{
let linker = match target.as_str() {
"xtensa-esp32-espidf" => Some("xtensa-esp32-elf-gcc"),
"xtensa-esp32s2-espidf" => Some("xtensa-esp32s2-elf-gcc"),
"xtensa-esp32s3-espidf" => Some("xtensa-esp32s3-elf-gcc"),
"riscv32imc-esp-espidf" |
"riscv32imac-esp-espidf" | "riscv32imafc-esp-espidf" => Some("riscv32-esp-elf-gcc"),
_ => None
};
if let Some(linker) = linker {
build.flag_if_supported("-mlongcalls");
build.compiler(linker);
}
}
if use_log {
srcs.push("flashdb/shim.c");
build.define("FDB_PRINT(...)", "fdb_log_printf(__VA_ARGS__)");
} else {
build.define("FDB_PRINT(...)", "((void)0)");
}
build
.flag("-std=c99")
.files(&srcs)
.include("flashdb/inc")
.cargo_warnings(false);
build.define("FDB_USING_CUSTOM_MODE", "1");
if use_64bit_timestamp {
build.define("FDB_USING_TIMESTAMP_64BIT", "1");
}
if use_kvdb {
build.define("FDB_USING_KVDB", "1");
}
if use_tsdb {
build.define("FDB_USING_TSDB", "1");
}
if debug_enabled {
build.define("FDB_DEBUG_ENABLE", "1");
}
build.compile("flashdb");
println!("cargo:rerun-if-changed=flashdb/inc/flashdb.h");
bindings = bindings
.use_core()
.header("flashdb/inc/flashdb.h")
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.allowlist_type("fdb_.*")
.allowlist_function("fdb_.*")
.allowlist_var("FDB_.*")
.clang_arg("-std=c99")
.derive_default(true)
.derive_debug(true);
bindings = bindings.clang_arg("-DFDB_USING_CUSTOM_MODE=1");
if use_64bit_timestamp {
bindings = bindings.clang_arg("-DFDB_USING_TIMESTAMP_64BIT=1");
}
if use_kvdb {
bindings = bindings.clang_arg("-DFDB_USING_KVDB=1");
}
if use_tsdb {
bindings = bindings.clang_arg("-DFDB_USING_TSDB=1");
}
if debug_enabled {
bindings = bindings.clang_arg("-DFDB_DEBUG_ENABLE=1");
}
if !use_log {
bindings = bindings.clang_arg("-DFDB_PRINT(...)=");
}
let bindings = bindings.generate().expect("Unable to generate bindings");
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
}