use std::env;
use std::error::Error;
use std::fs;
use std::io::ErrorKind;
use std::path::{Path, PathBuf};
use std::process::Command;
use tempfile::TempDir;
fn env(name: &str) -> Option<String> {
let prefix = env::var("TARGET").unwrap().to_uppercase().replace('-', "_");
let prefixed = format!("{}_{}", prefix, name);
println!("cargo:rerun-if-env-changed={}", prefixed);
if let Ok(var) = env::var(&prefixed) {
return Some(var);
}
println!("cargo:rerun-if-env-changed={}", name);
env::var(name).ok()
}
fn main() {
if env("_CI_ONLY__DISABLE_DESMUME_SYS_BUILD_SCRIPT").is_some() {
return;
}
let target = env::var("TARGET").unwrap();
let src = env::current_dir().unwrap();
let build_dir = TempDir::new().unwrap();
let build_dir = build_dir.path();
let mut cmd = Command::new("cp");
cmd.current_dir(build_dir)
.arg("-a")
.arg(src.join("desmume/desmume/src"))
.arg(build_dir);
run(&mut cmd, "cp");
if target.contains("windows") {
let mut cmd = Command::new("MSBuild.exe");
let config = "Release Fastbuild";
let (arch_dirname, arch_targetname) = if cfg!(target_pointer_width = "64") {
("x64", "x64")
} else {
("x86", "Win32")
};
let props_path = build_dir.join("src/frontend/interface/windows/desmume.props");
let props = fs::read_to_string(&props_path).unwrap();
let mprops = props.replace(";WIN_EXPORT", "");
fs::write(props_path, mprops).unwrap();
cmd.arg("DeSmuME_Interface.vcxproj")
.arg(format!("/p:configuration={}", config))
.arg(format!("/p:Platform={}", arch_targetname))
.arg("-property:ConfigurationType=StaticLibrary")
.current_dir(build_dir.join("src/frontend/interface/windows"));
run(&mut cmd, "MSBuild");
let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
windows_move_libs(
build_dir,
"src/frontend/interface/windows/__bins/*.lib",
&dst,
)
.unwrap();
windows_move_libs(
build_dir,
"src/frontend/interface/windows/.libs/*.lib",
&dst,
)
.unwrap();
windows_move_libs(
build_dir,
format!("src/frontend/interface/windows/.libs/{arch_targetname}/*.lib"),
&dst,
)
.unwrap();
windows_move_libs(
build_dir,
format!("src/frontend/interface/windows/SDL/lib/{arch_dirname}/*.lib"),
&dst,
)
.unwrap();
windows_move_libs(build_dir, "src/frontend/windows/zlib128/*.lib", &dst).unwrap();
windows_move_libs(build_dir, "src/frontend/windows/agg/*.lib", &dst).unwrap();
println!(
"cargo:rustc-link-search={}",
dst.as_os_str().to_str().unwrap()
);
println!("cargo:rustc-link-lib=dylib=vfw32");
println!("cargo:rustc-link-lib=dylib=opengl32");
println!("cargo:rustc-link-lib=dylib=glu32");
println!("cargo:rustc-link-lib=dylib=ws2_32");
println!("cargo:rustc-link-lib=dylib=user32");
println!("cargo:rustc-link-lib=dylib=gdi32");
println!("cargo:rustc-link-lib=dylib=shell32");
println!("cargo:rustc-link-lib=dylib=comdlg32");
println!("cargo:rustc-link-lib=dylib=shlwapi");
println!("cargo:rustc-link-lib=dylib=comctl32");
println!("cargo:rustc-link-lib=dylib=winmm");
println!("cargo:rustc-link-lib=static=SDL2");
if arch_targetname == "x64" {
println!("cargo:rustc-link-lib=static=agg-2.5-x64");
println!("cargo:rustc-link-lib=static=zlib-vc8-x64");
} else {
println!("cargo:rustc-link-lib=static=agg-2.5");
println!("cargo:rustc-link-lib=static=zlib-vc8-Win32");
}
let desmume_lib_name = glob::glob(dst.join("DeSmuME*").to_str().unwrap())
.unwrap()
.next()
.unwrap()
.unwrap();
fs::rename(desmume_lib_name, dst.join("desmume.lib")).unwrap();
println!("cargo:rustc-link-lib=static=desmume");
if arch_targetname != "x64" {
println!("cargo:rustc-link-arg=/SAFESEH:NO");
}
} else {
let mut cmd = Command::new("meson");
cmd.arg("build")
.arg("--default-library=static")
.arg("-Dbuildtype=release")
.current_dir(build_dir.join("src/frontend/interface"));
run(&mut cmd, "meson");
let mut cmd = Command::new("ninja");
cmd.arg("-C")
.arg("build")
.current_dir(build_dir.join("src/frontend/interface"));
run(&mut cmd, "ninja");
let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let mut cmd = Command::new("cp");
cmd.current_dir(build_dir)
.arg("-r")
.arg(build_dir.join("src/frontend/interface/build/libdesmume.a"))
.arg(build_dir.join("src/frontend/interface/build/libdesmume.a.p"))
.arg(&dst);
run(&mut cmd, "cp");
let cfg = pkg_config::Config::new();
cfg.probe("glib-2.0").unwrap();
cfg.probe("sdl2").unwrap();
if cfg.probe("libpcap").is_err() {
if target.contains("darwin") {
println!("cargo:rustc-link-lib=pcap");
} else {
panic!("Could not find libpcap. Is it installed?");
}
}
cfg.probe("zlib").unwrap();
cfg.probe("soundtouch").ok();
cfg.probe("openal").ok();
if cfg.probe("opengl").is_err() && target.contains("darwin") {
println!("cargo:rustc-link-lib=framework=OpenGL");
}
cfg.probe("alsa").ok();
if target.contains("darwin") {
println!("cargo:rustc-link-lib=c++");
} else {
println!("cargo:rustc-link-lib=stdc++");
}
println!("cargo:rustc-link-search={}", dst.display());
}
}
fn run(cmd: &mut Command, program: &str) {
println!("running: {:?}", cmd);
let status = match cmd.status() {
Ok(status) => status,
Err(ref e) if e.kind() == ErrorKind::NotFound => {
fail(&format!(
"failed to execute command: {}\nis `{}` not installed?",
e, program
));
}
Err(e) => fail(&format!("failed to execute command: {}", e)),
};
if !status.success() {
fail(&format!(
"command did not execute successfully, got: {}",
status
));
}
}
fn fail(s: &str) -> ! {
panic!("\n{}\n\nbuild script failed, must exit now", s)
}
fn windows_move_libs<P: AsRef<Path>>(
build_dir: &Path,
glob_pattern: P,
dst: &Path,
) -> Result<(), Box<dyn Error>> {
let gl = glob::glob(build_dir.join(glob_pattern).to_str().unwrap())?;
for pathr in gl {
let path = pathr?;
fs::copy(&path, dst.join(path.file_name().unwrap()))?;
}
Ok(())
}