/* * MIT License * * Copyright (c) 2019-2020 Frank Fischer * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ use regex::Regex; use std::env; use std::error::Error; use std::fs::File; use std::io::Read; use std::path::Path; fn main() -> Result<(), Box> { let out_dir = env::var("OUT_DIR")?; let out_dir = Path::new(&out_dir); let gurobi_include = match env::var("GUROBI_HOME") { Ok(grb) => { println!("cargo:rustc-link-search={}/lib", grb); Path::new(&grb).join("include") } Err(_) => { if let Some(dir) = ["/usr/include", "/usr/local/include"].iter().find(|dir| { let f = Path::new(dir).join("gurobi_c.h"); f.exists() }) { Path::new(dir).to_path_buf() } else { panic!("Can't find include file `gurobi_c.h` (maybe set GUROBI_HOME environment variable)"); } } }; let mut text = String::new(); File::open(gurobi_include.join("gurobi_c.h")) .expect("Can't open header file 'gurobi_c.h'") .read_to_string(&mut text)?; let major: usize = Regex::new(r"#define\s+GRB_VERSION_MAJOR\s+(\d+)")? .captures(&text) .expect("Cannot find GRB_VERSION_MAJOR in gurobi_c.h")[1] .parse()?; let minor: usize = Regex::new(r"#define\s+GRB_VERSION_MINOR\s+(\d+)")? .captures(&text) .expect("Cannot find GRB_VERSION_MINOR in gurobi_c.h")[1] .parse()?; println!("cargo:rustc-link-lib=gurobi{}{}", major, minor); let bindings = bindgen::Builder::default() .header(gurobi_include.join("gurobi_c.h").to_str().unwrap()) .allowlist_function("GRB.*") .allowlist_type("GRB.*") .allowlist_var("GRB.*") .blocklist_type("FILE") .blocklist_type("_.*") .opaque_type("GRBmodel") .opaque_type("GRBenv") .opaque_type("GRBsvec") .opaque_type("GRBbatch") .generate() .map_err(|_| "Can't generate bindings".to_string())?; // Replace string constants with `*const c_char` let result = bindings.to_string(); let result = Regex::new(r#"pub const (.*) &(?:'static )?\[u8;.*?\](.*");"#)?.replace_all( &result, "pub const $1 *const ::std::os::raw::c_char $2 as *const u8 as *const ::std::os::raw::c_char;", ); // Replace usual integer constants with `c_int` let result = Regex::new(r#": [ui]32 = (.*);"#)?.replace_all(&result, ": ::std::os::raw::c_int = $1;"); // Replace character constants with `c_char` let result = Regex::new(r#": [ui]8 = (.*)([ui]8)?;"#)? .replace_all(&result, ": ::std::os::raw::c_char = $1 as ::std::os::raw::c_char;"); std::fs::write(out_dir.join("gurobi-extern.rs"), result.into_owned())?; Ok(()) }