use std::path::PathBuf;
use std::process;
use wolfram_app_discovery::{SystemID, WolframApp, WolframVersion};
const WOLFRAM_VERSION: WolframVersion = WolframVersion::new(13, 0, 1);
fn main() {
env_logger::init();
wolfram_app_discovery::config::set_print_cargo_build_script_directives(true);
if std::env::var("DOCS_RS").is_ok() {
let bindings_path = make_bindings_path(&WOLFRAM_VERSION, SystemID::MacOSX_x86_64);
println!(
"cargo:rustc-env=CRATE_WSTP_SYS_BINDINGS={}",
bindings_path.display()
);
return;
}
let host = std::env::var("HOST").expect("expected 'HOST' env var to be set");
let target = std::env::var("TARGET").expect("expected 'TARGET' env var to be set");
if host != target {
panic!(
"error: crate wstp-sys does not support cross compilation. (host: {}, target: {})",
host,
target
);
}
let app: Option<WolframApp> = WolframApp::try_default().ok();
let target_system_id: SystemID =
SystemID::try_from_rust_target(&std::env::var("TARGET").unwrap())
.expect("unable to get System ID for target system");
link_to_wstp(app.as_ref());
let wolfram_version = match target_system_id {
SystemID::Linux_ARM64 => WolframVersion::new(13, 2, 0),
_ => WOLFRAM_VERSION,
};
let bindings_path = use_pregenerated_bindings(wolfram_version, target_system_id);
println!(
"cargo:rustc-env=CRATE_WSTP_SYS_BINDINGS={}",
bindings_path.display()
);
}
#[allow(dead_code)]
fn use_pregenerated_bindings(
wolfram_version: WolframVersion,
target_system_id: SystemID,
) -> PathBuf {
let bindings_path = make_bindings_path(&wolfram_version, target_system_id);
println!("cargo:rerun-if-changed={}", bindings_path.display());
if !bindings_path.is_file() {
println!(
"
==== ERROR: wstp-sys =====
Rust bindings for Wolfram WSTP for target configuration:
WolframVersion: {}
SystemID: {}
have not been pre-generated.
See wstp-sys/generated/ for a listing of currently available targets.
=========================================
",
wolfram_version, target_system_id
);
panic!("<See printed error>");
}
println!(
"cargo:warning=info: using pre-generated bindings for WSTP ({wolfram_version}, {target_system_id}): {}",
bindings_path.display()
);
bindings_path
}
fn make_bindings_path(wolfram_version: &WolframVersion, system_id: SystemID) -> PathBuf {
let bindings_path = PathBuf::from("generated")
.join(&wolfram_version.to_string())
.join(system_id.as_str())
.join("WSTP_bindings.rs");
let absolute_bindings_path =
PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join(&bindings_path);
absolute_bindings_path
}
fn link_to_wstp(app: Option<&WolframApp>) {
let static_lib = wolfram_app_discovery::build_scripts::wstp_static_library_path(app)
.expect("unable to get WSTP static library path")
.into_path_buf();
println!(
"cargo:warning=info: linking to WSTP static lib from: {}",
static_lib.display()
);
link_wstp_statically(&static_lib);
if cfg!(target_os = "macos") {
println!("cargo:rustc-link-lib=framework=Foundation");
}
if cfg!(target_os = "windows") {
println!("cargo:rustc-link-lib=dylib=kernel32");
println!("cargo:rustc-link-lib=dylib=user32");
println!("cargo:rustc-link-lib=dylib=advapi32");
println!("cargo:rustc-link-lib=dylib=comdlg32");
println!("cargo:rustc-link-lib=dylib=ws2_32");
println!("cargo:rustc-link-lib=dylib=wsock32");
println!("cargo:rustc-link-lib=dylib=rpcrt4");
}
if cfg!(target_os = "linux") {
println!("cargo:rustc-link-lib=uuid")
}
}
fn link_wstp_statically(lib: &PathBuf) {
let mut lib = lib.clone();
if cfg!(all(target_os = "macos", target_arch = "x86_64")) {
lib = lipo_native_library(&lib, "x86_64");
} else if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
lib = lipo_native_library(&lib, "arm64");
}
link_library_file(lib);
}
fn lipo_native_library(wstp_lib: &PathBuf, lipo_arch: &str) -> PathBuf {
let wstp_lib = wstp_lib
.to_str()
.expect("could not convert WSTP archive path to str");
let is_universal_binary = {
let stdout = process::Command::new("file")
.args(&[wstp_lib])
.output()
.expect("failed to run `file` system utility")
.stdout;
let stdout = String::from_utf8(stdout).unwrap();
stdout.contains("Mach-O universal binary")
};
if !is_universal_binary {
return PathBuf::from(wstp_lib);
}
let output_lib = std::env::temp_dir().join("libWSTP-thin.a");
let output_lib = output_lib
.to_str()
.expect("could not convert WSTP archive path to str");
let output = process::Command::new("lipo")
.args(&[wstp_lib, "-thin", lipo_arch, "-output", output_lib])
.output()
.expect("failed to invoke macOS `lipo` command");
if !output.status.success() {
panic!("unable to lipo WSTP library: {:#?}", output);
}
PathBuf::from(output_lib)
}
fn link_library_file(libfile: PathBuf) {
let search_dir = libfile.parent().unwrap().display().to_string();
let libname = libfile
.file_stem()
.unwrap()
.to_str()
.unwrap()
.trim_start_matches("lib");
println!("cargo:rustc-link-search={}", search_dir);
println!("cargo:rustc-link-lib=static={}", libname);
}