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::() .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::>(); 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 "/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(); } }; }