use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
#[derive(Debug)]
struct LibPaths {
dynamic_lib: Option<PathBuf>,
static_lib: Option<PathBuf>,
header: Option<PathBuf>,
}
fn merge_headers(lib_name: &str, headers: Vec<&str>) -> PathBuf {
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let wrapper = lib_name.to_string() + "_wrapper.h";
let wrapper_path = out_path.join(wrapper);
let mut wrapper = File::create(&wrapper_path).unwrap();
for header in headers {
wrapper
.write_all(format!("#include \"{}\"\n", header).as_bytes())
.unwrap();
}
wrapper_path
}
fn get_system_lib_paths(lib_name: &str, headers: Vec<&str>) -> LibPaths {
let mut possible_lib_paths: Vec<PathBuf> =
vec![PathBuf::from("/usr/local/lib"), PathBuf::from("/usr/lib")];
let header_name = merge_headers(lib_name, headers);
let env_vars = ["LD_LIBRARY_PATH"];
for env_var in env_vars {
if let Some(dirs) = env::var_os(env_var) {
for dir in dirs.to_string_lossy().split(":") {
let absolute_path_from_env =
PathBuf::from(&dir).canonicalize().unwrap_or_else(|e| {
panic!("Path in {env_var}={:?} does not exists. Error: {e}", dir);
});
possible_lib_paths.insert(0, absolute_path_from_env.clone());
}
};
}
let dyn_lib: String = format!("lib{}.so", lib_name);
let static_lib: String = format!("lib{}.a", lib_name);
let possible_dynamic_libs: Vec<PathBuf> = possible_lib_paths
.clone()
.into_iter()
.map(|path| path.join(&dyn_lib))
.collect();
let possible_static_libs: Vec<PathBuf> = possible_lib_paths
.into_iter()
.map(|path| path.join(&static_lib))
.collect();
let dynamic_lib = possible_dynamic_libs.into_iter().find(|path| path.exists());
let static_lib = possible_static_libs.into_iter().find(|path| path.exists());
LibPaths {
dynamic_lib: dynamic_lib,
static_lib: static_lib,
header: Some(header_name),
}
}
fn generate_bindings(
lib_name: &str,
headers: Vec<&str>,
feature_static: bool,
bindings_file: &str,
) {
let lib_paths = get_system_lib_paths(lib_name, headers);
if (feature_static && lib_paths.static_lib.is_none())
|| (!feature_static && lib_paths.dynamic_lib.is_none())
{
panic!(
r#"Couldn't find system library {lib_name} installed.
Please, do one of the following:
- Install the libftd2xx library in "/usr/local/lib".
- Set the "LD_LIBRARY_PATH" environment variable to the path where the library is installed.
See the crate documentation for details.
"#
);
}
let lib_dir = match feature_static {
true => lib_paths.static_lib.as_ref().unwrap().parent().unwrap(),
false => lib_paths.dynamic_lib.as_ref().unwrap().parent().unwrap(),
}
.to_string_lossy();
println!("cargo:rustc-link-search={}", lib_dir);
println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir);
if feature_static {
println!("cargo:rustc-link-lib=static={}", lib_name);
} else {
println!("cargo:rustc-link-lib=dylib={}", lib_name);
}
let bindings = bindgen::Builder::default()
.header(lib_paths.header.unwrap().to_string_lossy())
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.expect("Unable to generate bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join(bindings_file))
.expect("Couldn't write bindings!");
}
fn main() {
let feature_static = env::var_os("CARGO_FEATURE_STATIC").is_some();
let feature_mpsse = env::var_os("CARGO_FEATURE_MPSSE").is_some();
let d2xx_lib_name: &str = "ftd2xx";
let d2xx_headers: Vec<&str> = vec!["ftd2xx.h"];
let d2xx_bindings: &str = "d2xx_bindings.rs";
let mpsse_i2c_lib_name: &str = "mpsse";
let mpsse_i2c_headers: Vec<&str> = vec!["ftd2xx.h", "libmpsse_i2c.h"];
let mpsse_i2c_bindings: &str = "mpsse_i2c_bindings.rs";
let mpsse_spi_lib_name: &str = "mpsse";
let mpsse_spi_headers: Vec<&str> = vec!["ftd2xx.h", "libmpsse_spi.h"];
let mpsse_spi_bindings: &str = "mpsse_spi_bindings.rs";
println!("cargo:rerun-if-env-changed=LD_LIBRARY_PATH");
generate_bindings(d2xx_lib_name, d2xx_headers, feature_static, d2xx_bindings);
if feature_mpsse {
generate_bindings(
mpsse_i2c_lib_name,
mpsse_i2c_headers,
feature_static,
mpsse_i2c_bindings,
);
generate_bindings(
mpsse_spi_lib_name,
mpsse_spi_headers,
feature_static,
mpsse_spi_bindings,
);
}
}