use std::env;
use std::path::{Path, PathBuf};
fn target_has_feature(feature: &str) -> bool {
env::var("CARGO_CFG_TARGET_FEATURE")
.unwrap_or_default()
.split(',')
.any(|enabled_feature| enabled_feature == feature)
}
#[derive(Debug)]
pub struct CustomCargoCallbacks {
exclude_dir: Option<PathBuf>,
}
impl bindgen::callbacks::ParseCallbacks for CustomCargoCallbacks {
fn header_file(&self, filename: &str) {
if matches!(&self.exclude_dir, Some(exclude_dir) if Path::new(filename).starts_with(exclude_dir))
{
return;
}
println!("cargo:rerun-if-changed={filename}");
}
fn include_file(&self, filename: &str) {
if matches!(&self.exclude_dir, Some(exclude_dir) if Path::new(filename).starts_with(exclude_dir))
{
return;
}
println!("cargo:rerun-if-changed={filename}");
}
fn read_env_var(&self, key: &str) {
println!("cargo:rerun-if-env-changed={key}");
}
}
fn generate_bindings<'a>(
exclude_dir: Option<PathBuf>,
include_paths: impl IntoIterator<Item = &'a Path>,
) {
let target = env::var("TARGET").unwrap();
let emscripten = target.contains("emscripten");
let builder = include_paths
.into_iter()
.fold(bindgen::Builder::default(), |builder, path| {
builder.clang_arg(format!("-I{}", path.to_string_lossy()))
});
let builder = if emscripten {
println!("cargo:rerun-if-env-changed=HOST");
let host = env::var("HOST")
.expect("TARGET is emscripten but HOST is not set; needed for bindgen host parsing");
builder
.clang_arg(format!("--target={host}"))
.layout_tests(false)
} else {
builder
};
let c_bindings = builder
.header("wrapper.h")
.parse_callbacks(Box::new(CustomCargoCallbacks { exclude_dir }))
.generate()
.expect("Unable to generate bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
c_bindings
.write_to_file(out_path.join("c_bindings.rs"))
.expect("Couldn't write bindings!");
}
#[cfg(feature = "build")]
fn build() -> bool {
use cmake::Config;
let target = env::var("TARGET").unwrap();
let emscripten = target.contains("emscripten");
let mut dst = Config::new("HiGHS");
let crt_static = target_has_feature("crt-static");
if cfg!(feature = "ninja") {
dst.generator("Ninja");
}
dst.define("BUILD_CXX_EXE", "OFF");
dst.define("BUILD_EXAMPLES", "OFF");
if emscripten {
dst.no_default_flags(true);
dst.cxxflag("-fexceptions");
}
if cfg!(feature = "highs_release") {
dst.profile("Release");
}
let dst = dst
.define("FAST_BUILD", "ON")
.define("BUILD_SHARED_LIBS", "OFF")
.define(
"CMAKE_MSVC_RUNTIME_LIBRARY",
if crt_static {
"MultiThreaded"
} else {
"MultiThreadedDLL"
},
)
.define("CMAKE_INTERPROCEDURAL_OPTIMIZATION", "FALSE")
.define("ZLIB", if cfg!(feature = "libz") { "ON" } else { "OFF" })
.build();
let include_path = dst.join("include").join("highs");
generate_bindings(Some(dst.clone()), [include_path.as_path()]);
println!("cargo:rustc-link-search=native={}/lib", dst.display());
println!("cargo:rustc-link-search=native={}/lib64", dst.display());
println!("cargo:rustc-link-lib=static=highs");
if cfg!(feature = "libz") {
println!("cargo:rustc-link-lib=z");
}
let apple = target.contains("apple");
let linux = target.contains("linux");
let mingw = target.contains("pc-windows-gnu");
if apple || emscripten {
println!("cargo:rustc-link-lib=c++");
} else if linux || mingw {
println!("cargo:rustc-link-lib=stdc++");
}
println!("cargo:rerun-if-changed=HiGHS/highs/interfaces/highs_c_api.h");
true
}
#[cfg(not(feature = "build"))]
fn build() -> bool {
false
}
#[cfg(feature = "discover")]
fn discover() -> bool {
let lib = match pkg_config::Config::new()
.atleast_version("1.5.0")
.probe("highs")
{
Ok(lib) => lib,
Err(_e) => return false,
};
generate_bindings(None, lib.include_paths.iter().map(|p| p.as_path()));
true
}
#[cfg(not(feature = "discover"))]
fn discover() -> bool {
false
}
fn main() {
println!("cargo:rerun-if-env-changed=CARGO_CFG_TARGET_FEATURE");
let crt_static = target_has_feature("crt-static");
if cfg!(all(
any(
feature = "highs_release",
feature = "libz",
feature = "ninja"
),
not(feature = "build")
)) {
panic!(
"You have enabled features that control how HiGHS is built, but have not enabled the 'build' feature.
\
Thus, your features will never have any effect. Please enable the 'build' feature on highs-sys if you want to build HiGHS or disable the 'libz', 'ninja' and 'highs_release' features."
);
}
if cfg!(feature = "discover") && crt_static {
println!(
"cargo::warning=You have enabled Rust's 'crt-static' target feature, but also enabled the 'discover' feature. Discovering a system-installed HiGHS bypasses the bundled build, so highs-sys cannot ensure that HiGHS uses the same MSVC runtime. Please disable 'discover' when using '-C target-feature=+crt-static'."
);
}
if !discover() && !build() {
panic!("Could neither discover nor build HiGHS");
}
}