183 lines
6.1 KiB
Rust
183 lines
6.1 KiB
Rust
use pkg_config;
|
|
use std::env;
|
|
use std::path::PathBuf;
|
|
use std::process::Command;
|
|
|
|
fn ninja_available() -> bool {
|
|
Command::new("ninja").arg("--version").spawn().is_ok()
|
|
}
|
|
|
|
fn msvc_cmake_tools_available() -> bool {
|
|
Command::new("cmake").arg("--version").spawn().is_ok() && ninja_available()
|
|
}
|
|
|
|
fn setup_env_msvc(compiler: &cc::Tool) {
|
|
// If PATH already contains what we need, skip this
|
|
if msvc_cmake_tools_available() {
|
|
return;
|
|
}
|
|
|
|
let target = env::var("TARGET").unwrap();
|
|
let devenv = cc::windows_registry::find_tool(target.as_str(), "devenv");
|
|
let tool_root: PathBuf = match devenv {
|
|
Some(devenv_tool) => devenv_tool.path().parent().unwrap().to_path_buf(),
|
|
None => {
|
|
// if devenv (i.e. Visual Studio) was not found, assume compiler is
|
|
// from standalone Build Tools and look there instead.
|
|
// this should be done properly in cc crate, but for now it's not.
|
|
let tools_name = std::ffi::OsStr::new("BuildTools");
|
|
let compiler_path = compiler.path().to_path_buf();
|
|
compiler_path
|
|
.iter()
|
|
.find(|x| *x == tools_name)
|
|
.expect("Failed to find devenv or Build Tools");
|
|
compiler_path
|
|
.iter()
|
|
.take_while(|x| *x != tools_name)
|
|
.collect::<PathBuf>()
|
|
.join(tools_name)
|
|
.join(r"Common7\IDE")
|
|
}
|
|
};
|
|
let cmake_pkg_dir = tool_root.join(r"CommonExtensions\Microsoft\CMake");
|
|
let cmake_path = cmake_pkg_dir.join(r"CMake\bin\cmake.exe");
|
|
let ninja_path = cmake_pkg_dir.join(r"Ninja\ninja.exe");
|
|
if !cmake_path.is_file() {
|
|
panic!("missing cmake");
|
|
}
|
|
if !ninja_path.is_file() {
|
|
panic!("missing ninja");
|
|
}
|
|
|
|
// append cmake and ninja location to PATH
|
|
if let Some(path) = env::var_os("PATH") {
|
|
let mut paths = env::split_paths(&path).collect::<Vec<_>>();
|
|
for tool_path in [cmake_path, ninja_path] {
|
|
paths.push(tool_path.parent().unwrap().to_path_buf());
|
|
}
|
|
let new_path = env::join_paths(paths).unwrap();
|
|
env::set_var("PATH", &new_path);
|
|
}
|
|
}
|
|
|
|
fn build_with_cmake() {
|
|
let uc_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
|
let compiler = cc::Build::new().get_compiler();
|
|
let has_ninja = if compiler.is_like_msvc() {
|
|
setup_env_msvc(&compiler);
|
|
// this is a BIG HACK that should be fixed in unicorn's cmake!!
|
|
// but for now, tell link.exe to ignore multiply defined symbol names
|
|
println!("cargo:rustc-link-arg=/FORCE:MULTIPLE");
|
|
true
|
|
} else {
|
|
ninja_available()
|
|
};
|
|
|
|
// cc crate (as of 1.0.73) misdetects clang as gnu on apple
|
|
if compiler.is_like_gnu() && env::consts::OS != "macos" {
|
|
// see comment on /FORCE:MULTIPLE
|
|
println!("cargo:rustc-link-arg=-Wl,-allow-multiple-definition");
|
|
}
|
|
|
|
let mut config = cmake::Config::new(&uc_dir);
|
|
if has_ninja {
|
|
config.generator("Ninja");
|
|
}
|
|
|
|
let mut archs = String::new();
|
|
|
|
if std::env::var("CARGO_FEATURE_ARCH_X86").is_ok() {
|
|
archs.push_str("x86;");
|
|
}
|
|
if std::env::var("CARGO_FEATURE_ARCH_ARM").is_ok() {
|
|
archs.push_str("arm;");
|
|
}
|
|
if std::env::var("CARGO_FEATURE_ARCH_AARCH64").is_ok() {
|
|
archs.push_str("aarch64;");
|
|
}
|
|
if std::env::var("CARGO_FEATURE_ARCH_RISCV").is_ok() {
|
|
archs.push_str("riscv;");
|
|
}
|
|
if std::env::var("CARGO_FEATURE_ARCH_MIPS").is_ok() {
|
|
archs.push_str("mips;");
|
|
}
|
|
if std::env::var("CARGO_FEATURE_ARCH_SPARC").is_ok() {
|
|
archs.push_str("sparc;");
|
|
}
|
|
if std::env::var("CARGO_FEATURE_ARCH_M68K").is_ok() {
|
|
archs.push_str("m68k;");
|
|
}
|
|
if std::env::var("CARGO_FEATURE_ARCH_PPC").is_ok() {
|
|
archs.push_str("ppc;");
|
|
}
|
|
if std::env::var("CARGO_FEATURE_ARCH_S390X").is_ok() {
|
|
archs.push_str("s390x;");
|
|
}
|
|
if std::env::var("CARGO_FEATURE_ARCH_TRICORE").is_ok() {
|
|
archs.push_str("tricore;");
|
|
}
|
|
|
|
if !archs.is_empty() {
|
|
archs.pop();
|
|
}
|
|
|
|
// need to clear build target and append "build" to the path because
|
|
// unicorn's CMakeLists.txt doesn't properly support 'install', so we use
|
|
// the build artifacts from the build directory, which cmake crate sets
|
|
// to "<out_dir>/build/"
|
|
let dst = config
|
|
.define("UNICORN_BUILD_TESTS", "OFF")
|
|
.define("UNICORN_INSTALL", "OFF")
|
|
.define("UNICORN_ARCH", archs)
|
|
.no_build_target(true)
|
|
.build();
|
|
println!(
|
|
"cargo:rustc-link-search=native={}",
|
|
dst.join("build").display()
|
|
);
|
|
|
|
// Lazymio(@wtdcode): Dynamic link may break. See: https://github.com/rust-lang/cargo/issues/5077
|
|
if cfg!(feature = "dynamic_linkage") {
|
|
if compiler.is_like_msvc() {
|
|
println!("cargo:rustc-link-lib=dylib=unicorn-import");
|
|
} else {
|
|
println!("cargo:rustc-link-lib=dylib=unicorn");
|
|
}
|
|
} else {
|
|
println!("cargo:rustc-link-lib=static=unicorn");
|
|
}
|
|
if !compiler.is_like_msvc() {
|
|
println!("cargo:rustc-link-lib=pthread");
|
|
println!("cargo:rustc-link-lib=m");
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
match pkg_config::Config::new()
|
|
.atleast_version("2")
|
|
.cargo_metadata(false)
|
|
.probe("unicorn")
|
|
{
|
|
Ok(lib) => {
|
|
for dir in lib.link_paths {
|
|
println!("cargo:rustc-link-search=native={}", dir.to_str().unwrap());
|
|
}
|
|
if cfg!(feature = "dynamic_linkage") {
|
|
if cc::Build::new().get_compiler().is_like_msvc() {
|
|
println!("cargo:rustc-link-lib=dylib=unicorn-import");
|
|
} else {
|
|
println!("cargo:rustc-link-lib=dylib=unicorn");
|
|
}
|
|
} else {
|
|
println!("cargo:rustc-link-arg=-Wl,-allow-multiple-definition");
|
|
println!("cargo:rustc-link-lib=static=unicorn");
|
|
println!("cargo:rustc-link-lib=pthread");
|
|
println!("cargo:rustc-link-lib=m");
|
|
}
|
|
}
|
|
Err(_) => {
|
|
build_with_cmake();
|
|
}
|
|
};
|
|
}
|