diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index a0d5595..d984f48 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -22,7 +22,14 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - - name: Test bundled + - name: Install requirements on ubuntu + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y libblas-dev liblapack-dev zlib1g-dev libmetis-dev + + - name: Install requirements on macos + if: matrix.os == 'macos-latest' || matrix.os == 'macos-14' run: | cargo b --features bundled --release cargo t --features bundled --release create @@ -45,6 +52,24 @@ jobs: # cargo b --features bundled # cargo t --features bundled create # cargo t --features bundled --examples + brew install gcc + brew info gcc + + - name: Test bundled (if not mac) + if: matrix.os == 'ubuntu-latest' || matrix.os == 'windows-latest' + run: | + cargo b --features bundled,static + cargo t --features bundled,static create + cargo t --features bundled,static --examples + + - name: Test bundled (if mac) + if: matrix.os == 'macos-latest' || matrix.os == 'macos-14' + run: | + export LIBRARY_PATH=$LIBRARY_PATH:/opt/homebrew/Cellar/gcc/14.2.0/lib/gcc/14 + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/homebrew/Cellar/gcc/14.2.0/lib/gcc/14 + cargo b --features bundled,static --no-default-features + cargo t --features bundled,static create + cargo t --features bundled,static --examples from-source-test: strategy: diff --git a/Cargo.toml b/Cargo.toml index 3585f68..85e44bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,8 @@ links = "scip" [features] bundled = ["ureq", "zip", "tempfile", "zip-extract"] -from-source = ["ureq", "zip", "tempfile", "zip-extract", "cmake"] +from-source = ["ureq", "zip", "tempfile", "zip-extract", "cmake", "flate2", "tar"] +static = ["libz-sys/static"] [build-dependencies] bindgen = "0.72.0" @@ -19,6 +20,8 @@ zip = { version = "0.5", optional = true } tempfile = { version = "3.2", optional = true } zip-extract = { version = "0.1.3", optional = true } cmake = { version = "0.1.50", optional = true } +flate2 = { version = "1.0", optional = true } +tar = { version = "0.4", optional = true } [dependencies] -cmake = "0.1.50" +libz-sys = { version="1.1.16", features = ["static"], optional = true } diff --git a/build.rs b/build.rs index 3d4bf55..f47de7f 100644 --- a/build.rs +++ b/build.rs @@ -42,7 +42,7 @@ fn _build_from_scip_dir(path: &str) -> bindgen::Builder { ); } - println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir_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(); @@ -64,7 +64,7 @@ fn _build_from_scip_dir(path: &str) -> bindgen::Builder { bindgen::Builder::default() .header(scip_header_file) .header(scipdefplugins_header_file) - .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .clang_arg(format!("-I{}", include_dir_path)) } @@ -128,7 +128,7 @@ fn main() -> Result<(), Box> { bindgen::Builder::default() .header(scip_header_file) .header(scipdefplugins_header_file) - .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .clang_arg(format!("-I{}", headers_dir_path)) } }; @@ -163,6 +163,44 @@ fn main() -> Result<(), Box> { println!("cargo:rustc-link-lib=soplex"); } } + + #[cfg(feature = "static")] { + #[cfg(windows)]{ + println!("cargo:rustc-link-lib=static=libscip"); + println!("cargo:rustc-link-lib=static=libsoplex"); + println!("cargo:rustc-link-lib=static=ipopt"); + } + #[cfg(not(windows))] + { + println!("cargo:rustc-link-lib=static=ipopt"); + println!("cargo:rustc-link-lib=static=soplex"); + println!("cargo:rustc-link-lib=static=z"); + println!("cargo:rustc-link-lib=static=scip"); + println!("cargo:rustc-link-lib=lapack"); + println!("cargo:rustc-link-lib=blas"); + println!("cargo:rustc-link-lib=coinmumps"); + println!("cargo:rustc-link-lib=gfortran"); + println!("cargo:rustc-link-lib=metis"); + } + + println!("cargo:rustc-link-arg=-no-pie"); + + 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(not(feature = "static"))] { + println!("cargo:rustc-link-lib=dylib=scip"); + } + let builder = builder .blocklist_item("FP_NAN") @@ -170,7 +208,7 @@ fn main() -> Result<(), Box> { .blocklist_item("FP_ZERO") .blocklist_item("FP_SUBNORMAL") .blocklist_item("FP_NORMAL") - .parse_callbacks(Box::new(bindgen::CargoCallbacks)); + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())); let bindings = builder.generate()?; let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); diff --git a/download.rs b/download.rs index 9e8ee02..aadbc67 100644 --- a/download.rs +++ b/download.rs @@ -9,6 +9,11 @@ use std::time::Duration; use tempfile::tempdir; use zip_extract::extract; +#[cfg(feature = "from-source")] +use flate2::read::GzDecoder; +#[cfg(feature = "from-source")] +use tar::Archive; + pub fn download_and_extract_zip(url: &str, extract_path: &Path) -> Result<(), Box> { // Download the ZIP file println!("cargo:warning=Downloading from {}", url); @@ -18,7 +23,7 @@ pub fn download_and_extract_zip(url: &str, extract_path: &Path) -> Result<(), Bo // Create a temporary file to store the ZIP let dir = tempdir()?; - let zip_path = dir.path().join("libscip.zip"); + let zip_path = dir.path().join("libscip.tgz"); let mut temp_file = File::create(&zip_path)?; temp_file.write_all(&content)?; @@ -53,3 +58,23 @@ pub fn download_and_extract_zip(url: &str, extract_path: &Path) -> Result<(), Bo } Ok(()) } + +#[cfg(feature = "from-source")] +pub fn download_and_extract_tar_gz(url: &str, extract_path: &Path) -> Result<(), Box> { + // Download the tar.gz file + println!("cargo:warning=Downloading from {}", url); + let resp = ureq::get(url).timeout(Duration::from_secs(300)).call()?; + let mut content: Vec = Vec::new(); + resp.into_reader().read_to_end(&mut content)?; + + println!("cargo:warning=Extracting to {:?}", extract_path); + + // Create GzDecoder and Archive + let tar_gz = GzDecoder::new(Cursor::new(content)); + let mut archive = Archive::new(tar_gz); + + // Extract the archive + archive.unpack(extract_path)?; + + Ok(()) +} diff --git a/from_source.rs b/from_source.rs index 606dd19..35c23bf 100644 --- a/from_source.rs +++ b/from_source.rs @@ -1,5 +1,5 @@ #[cfg(feature = "from-source")] -use crate::download::download_and_extract_zip; +use crate::download::download_and_extract_tar_gz; #[cfg(feature = "from-source")] use std::env; use std::path::PathBuf; @@ -21,14 +21,14 @@ pub fn download_scip_source() -> PathBuf { #[cfg(feature = "from-source")] pub fn download_scip_source() -> PathBuf { - let scip_version = "9.1.1"; - let url = format!("https://github.com/scipopt/scip-sys/releases/download/v0.1.9/scipoptsuite-{scip_version}.zip"); + let scip_version = "9.2.3"; + let url = format!("https://github.com/scipopt/scip/releases/download/v923/scipoptsuite-{scip_version}.tgz"); let target = env::var("OUT_DIR").unwrap(); let target = std::path::Path::new(&target); if target.join(format!("scipoptsuite-{scip_version}")).exists() { println!("cargo:warning=SCIP was previously downloaded, skipping download"); } else { - download_and_extract_zip(&url, &*target).expect("Failed to download SCIP"); + download_and_extract_tar_gz(&url, &*target).expect("Failed to download SCIP"); } target.join(format!("scipoptsuite-{scip_version}")) } @@ -46,7 +46,8 @@ pub fn compile_scip(source_path: PathBuf) -> PathBuf { use cmake::Config; let mut dst = Config::new(source_path); - dst.define("IPOPT", "OFF") + dst.define("CMAKE_BUILD_TYPE", "Release") + .define("IPOPT", "OFF") .define("ZIMPL", "OFF") .define("GMP", "OFF") .define("READLINE", "OFF") @@ -56,6 +57,11 @@ pub fn compile_scip(source_path: PathBuf) -> PathBuf { .define("SYM", "snauty") .define("ZLIB", "OFF") .define("SHARED", "OFF") + .define("GCG", "OFF") + .define("SANITIZE_ADDRESS", "OFF") + .define("SANITIZE_MEMORY", "OFF") + .define("SANITIZE_UNDEFINED", "OFF") + .define("SANITIZE_THREAD", "OFF") .build() }