use std::env;
use std::path::PathBuf;
use std::fs::File;
use std::str::FromStr;
use std::io::prelude::*;
use std::process::Command;
fn get_platform_name(majorver : i32,minorver : i32) -> (String,String) {
if cfg!(target_os = "windows") {
if cfg!(target_arch = "x86_64") {
("win64x86".to_string(), format!("mosek64_{}_{}",majorver,minorver))
}
else {
panic!("Unsupported architecture")
}
}
else if cfg!(target_os = "linux") {
if cfg!(target_arch = "x86_64") {
("linux64x86".to_string(),"mosek64".to_string())
}
else if cfg!(target_arch = "aarch64") {
("linuxaarch64".to_string(),"mosek64".to_string())
}
else {
panic!("Unsupported architecture")
}
}
else if cfg!(target_os = "macos") {
if cfg!(target_arch = "aarch64") {
("osxaarch64".to_string(), "mosek64".to_string())
}
else if cfg!(target_arch = "x86_64") {
("osx64x86".to_string(), "mosek64".to_string())
}
else {
panic!("Unsupported architecture")
}
}
else {
panic!("Unsupported operating system")
}
}
fn find_mosek_installation(pfname : &String, majorver : i32, minorver : i32) -> Option<String> {
let bindirvar = format!("MOSEK_BINDIR_{}{}",majorver,minorver);
let mut bindir_b = PathBuf::new();
match env::var_os(bindirvar.as_str()) {
Some(p) => bindir_b.push(p),
None => {
let inst_base =
if let Some(p) = env::var_os("MOSEK_INST_BASE") { p }
else if let Some(p) = env::var_os("HOME") { p }
else if let (Some(homed),Some(homep)) = (env::var_os("HOMEDRIVE"),env::var_os("HOMEPATH")) {
let mut r = homed;
r.push(homep);
r
}
else { return None; };
bindir_b.push(inst_base);
bindir_b.push("mosek");
bindir_b.push(format!("{}.{}",majorver,minorver));
bindir_b.push("tools");
bindir_b.push("platform");
bindir_b.push(pfname);
bindir_b.push("bin");
},
}
if ! bindir_b.as_path().is_dir() {
return None
}
let mut mosekbin = bindir_b.clone(); mosekbin.push("mosek");
let res = Command::new(mosekbin).arg("-v").output().expect("Failed to check mosek version");
let text : String = String::from_utf8_lossy(res.stdout.as_ref()).to_string();
if let Some(text) = text.strip_prefix("MOSEK version ") {
if let Some(p) = text.find('\n') {
let mut ver = text[0..p].split('.');
let vmajor = ver.next();
let vminor = ver.next();
if let (Some(vmajor),Some(vminor)) = (vmajor,vminor) {
let vmajor : Option<i32> = FromStr::from_str(vmajor).ok();
let vminor : Option<i32> = FromStr::from_str(vminor).ok();
if let (Some(vmajor),Some(vminor)) = (vmajor,vminor) {
if vmajor == majorver && vminor == minorver {
return Some(bindir_b.as_path().to_str().unwrap().to_string())
}
}
}
}
}
None
}
fn getmosek(pfname : &String,majorver : i32, minorver : i32) -> String {
let mut outdir = PathBuf::new();
outdir.push(env::var_os("OUT_DIR").unwrap());
let targetdir = outdir.as_path();
let (archname,iszip) = match pfname.as_str() {
"linux64x86" => ("mosektoolslinux64x86.tar.bz2",false),
"linuxaarch64" => ("mosektoolslinuxaarch64.tar.bz2",false),
"osx64x86" => ("mosektoolsosx64x86.tar.bz2",false),
"osxaarch64" => ("mosektoolsosxaarch64.tar.bz2",false),
"win32x86" => ("mosektoolswin32x86.zip",true),
"win64x86" => ("mosektoolswin64x86.zip",true),
_ => panic!("Invalid platform")
};
let mut archfile = PathBuf::new();
archfile.push(targetdir);
archfile.push(archname);
if ! archfile.exists() {
let res = Command::new("curl")
.arg("--silent")
.arg(format!("https://download.mosek.com/stable/{}.{}/version",majorver,minorver).as_str())
.output()
.expect("Failed to get latest version");
let verstr = match String::from_utf8_lossy(res.stdout.as_ref()) {
std::borrow::Cow::Owned(s) => s,
std::borrow::Cow::Borrowed(s) => s.to_string()
};
let ver = verstr.trim();
Command::new("curl")
.arg("-o").arg(archfile.as_path())
.arg("--silent")
.arg(format!("https://download.mosek.com/stable/{}/{}",ver,archname).as_str())
.status()
.expect("Failed to get distro file");
if iszip {
panic!("Not implemented: Unzipping distro on Windows");
}
else {
Command::new("tar")
.arg("xjf").arg(archfile)
.arg("-C").arg(outdir.as_path())
.status()
.expect("Failed to unpack distro");
}
}
let mut res = PathBuf::new();
res.push(outdir.as_path());
res.push("mosek");
res.push(format!("{}.{}",majorver,minorver).as_str());
res.push("tools");
res.push("platform");
res.push(pfname.as_str());
res.push("bin");
res.as_path().to_str().unwrap().to_string()
}
fn extract_version(text : &String) -> Option<(i32,i32)> {
let mosekverstr = text.trim();
match mosekverstr.find('.') {
None => None,
Some(p) => {
let vmajor : i32 = FromStr::from_str(&mosekverstr[0..p]).unwrap();
let vminor : i32 = FromStr::from_str(&mosekverstr[p+1..mosekverstr.len()]).unwrap();
Some((vmajor,vminor))
}
}
}
fn readversion(filename : &str) -> (i32,i32) {
let mut mosekverstr = String::new();
match File::open(filename) {
Err(_) => panic!("Failed to open version file '{}'",filename),
Ok(mut f) => { let _ = f.read_to_string(& mut mosekverstr).unwrap(); }
}
match extract_version(&mosekverstr) {
None => panic!("Invalid version file '{}'",filename),
Some(v) => { v }
}
}
fn main() {
let (mskvermajor,mskverminor) = readversion("MOSEKVERSION");
let mosekrs_force_download =
if let Some(s) = env::var_os("MOSEKRS_FORCE_DOWNLOAD") {
if let Some(s) = s.to_str() {
s.eq("YES") || s.eq("ON") || s.eq("TRUE")
}
else {
false
}
}
else {
false
};
let (pfname, libname) = get_platform_name(mskvermajor,mskverminor);
let libdir =
if ! mosekrs_force_download {
if let Some(p) = find_mosek_installation(&pfname,mskvermajor,mskverminor) { p }
else { getmosek(&pfname, mskvermajor, mskverminor) }
}
else { getmosek(&pfname, mskvermajor, mskverminor) };
println!("cargo:rustc-link-search={}",libdir);
println!("cargo:rustc-flags=-L {} -l {}",libdir,libname);
if cfg!(target_os = "linux") {
println!("cargo:rustc-link-arg=-Wl,-rpath={}",libdir);
}
else if cfg!(target_os = "macos") {
println!("cargo:rustc-link-arg=-Wl,-rpath,{}",libdir);
}
}