mod bundled;
mod callback;
mod from_source;
#[cfg(any(feature = "bundled", feature = "from-source"))]
mod download;
extern crate bindgen;
use glob::glob;
use std::env;
use std::error::Error;
use std::path::PathBuf;
use crate::from_source::{compile_scip, download_scip_source, is_from_source_feature_enabled};
use bundled::*;
use callback::DeriveCastedConstant;
#[cfg(not(feature = "bundled"))]
pub fn is_bundled_feature_enabled() -> bool {
false
}
fn _build_from_scip_dir(path: &str) -> bindgen::Builder {
let lib_dir = PathBuf::from(&path).join("lib");
let lib_dir_path = lib_dir.to_str().unwrap();
if lib_dir.exists() {
println!("cargo:warning=Using SCIP from {}", lib_dir_path);
println!("cargo:rustc-link-search={}", lib_dir_path);
println!("cargo:libdir={}", lib_dir_path);
#[cfg(windows)]
let lib_dir_path = PathBuf::from(&path).join("bin");
#[cfg(windows)]
println!("cargo:rustc-link-search={}", lib_dir_path.to_str().unwrap());
} else {
panic!(
"{}",
format!(
"{}/lib does not exist, please check your SCIP installation",
path
)
);
}
println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir_path);
let include_dir = PathBuf::from(&path).join("include");
let include_dir_path = include_dir.to_str().unwrap();
let scip_header_file = PathBuf::from(&path)
.join("include")
.join("scip")
.join("scip.h")
.to_str()
.unwrap()
.to_owned();
let scipdefplugins_header_file = PathBuf::from(&path)
.join("include")
.join("scip")
.join("scipdefplugins.h")
.to_str()
.unwrap()
.to_owned();
let scipdef_file = PathBuf::from(&path)
.join("include")
.join("scip")
.join("def.h")
.to_str()
.unwrap()
.to_owned();
bindgen::Builder::default()
.header(scip_header_file)
.header(scipdefplugins_header_file)
.header(scipdef_file)
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.clang_arg(format!("-I{}", include_dir_path))
}
fn lib_scip_in_dir(path: &str) -> bool {
glob(&format!("{}/lib/libscip*", path)).unwrap().count() > 0
}
fn look_in_scipoptdir_and_conda_env() -> Option<bindgen::Builder> {
let env_vars = vec!["SCIPOPTDIR", "CONDA_PREFIX"];
for env_var_name in env_vars {
println!("cargo:rerun-if-env-changed={}", env_var_name);
let env_var = env::var(env_var_name);
if let Ok(scip_dir) = env_var {
println!("cargo:warning=Looking for SCIP in {}", scip_dir);
if lib_scip_in_dir(&scip_dir) {
return Some(_build_from_scip_dir(&scip_dir));
} else {
println!("cargo:warning=SCIP was not found in {}", scip_dir);
}
} else {
println!("cargo:warning={} is not set", env_var_name);
}
}
None
}
fn try_system_include_paths() -> Option<bindgen::Builder> {
println!("cargo:warning=Searching for SCIP in standard system directories");
let search_paths = vec![
"/usr/include",
"/usr/local/include",
"/opt/local/include", "/opt/homebrew/include", "/usr/local/opt/scip/include", ];
for base_path in search_paths {
let base = PathBuf::from(base_path);
let scip_h = base.join("scip").join("scip.h");
let scipdefplugins_h = base.join("scip").join("scipdefplugins.h");
let def_h = base.join("scip").join("def.h");
if scip_h.exists() && scipdefplugins_h.exists() && def_h.exists() {
println!("cargo:warning=Found SCIP headers in {}", base_path);
return Some(
bindgen::Builder::default()
.header(scip_h.to_str().unwrap())
.header(scipdefplugins_h.to_str().unwrap())
.header(def_h.to_str().unwrap())
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.clang_arg(format!("-I{}", base_path)),
);
}
}
println!("cargo:warning=Could not find SCIP headers in standard system directories");
None
}
fn main() -> Result<(), Box<dyn Error>> {
if env::var("DOCS_RS").is_ok() {
println!("cargo:warning=Building on docs.rs, using pre-generated bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
std::fs::copy("src/bindings_pregenerated.rs", out_path.join("bindings.rs"))?;
return Ok(());
}
let builder = if is_bundled_feature_enabled() {
download_scip();
let path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("scip_install");
_build_from_scip_dir(path.to_str().unwrap())
} else if is_from_source_feature_enabled() {
let source_path = download_scip_source();
let build_path = compile_scip(source_path);
_build_from_scip_dir(build_path.to_str().unwrap())
} else {
let builder = look_in_scipoptdir_and_conda_env();
if builder.is_some() {
builder.unwrap()
} else {
println!("cargo:warning=SCIP was not found in SCIPOPTDIR or in Conda environment");
println!("cargo:warning=Looking for SCIP in system libraries");
try_system_include_paths().unwrap_or_else(|| {
panic!(
"Could not find SCIP installation.\n\
Please either:\n\
- Set SCIPOPTDIR environment variable to point to your SCIP installation\n\
- Install SCIP system-wide (headers in /usr/include or /usr/local/include)\n\
- Use --features bundled to download and use a bundled version\n\
- Use --features from-source to build SCIP from source"
)
})
}
};
#[cfg(windows)]
{
println!("cargo:rustc-link-lib=libscip");
}
#[cfg(not(windows))]
{
println!("cargo:rustc-link-lib=scip");
}
#[cfg(feature = "from-source")]
{
let target = env::var("TARGET").unwrap();
let apple = target.contains("apple");
let linux = target.contains("linux");
let mingw = target.contains("pc-windows-gnu");
if apple {
println!("cargo:rustc-link-lib=dylib=c++");
} else if linux || mingw {
println!("cargo:rustc-link-lib=dylib=stdc++");
}
#[cfg(windows)]
{
println!("cargo:rustc-link-lib=libsoplex");
}
#[cfg(not(windows))]
{
println!("cargo:rustc-link-lib=soplex");
}
}
let derive_casted_constant = DeriveCastedConstant::new().target("SCIP_INVALID");
let builder = builder
.clang_arg("-DSCIP_DEPRECATED=")
.blocklist_item("FP_NAN")
.blocklist_item("FP_INFINITE")
.blocklist_item("FP_ZERO")
.blocklist_item("FP_SUBNORMAL")
.blocklist_item("FP_NORMAL")
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.parse_callbacks(Box::new(derive_casted_constant));
let bindings = builder.generate()?;
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings.write_to_file(out_path.join("bindings.rs"))?;
Ok(())
}