Skip to content

Commit

Permalink
unify rust build.rs for win/nix and fix it on windows
Browse files Browse the repository at this point in the history
  • Loading branch information
shuffle2 committed Apr 13, 2022
1 parent 9620514 commit d0cad65
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 156 deletions.
9 changes: 3 additions & 6 deletions bindings/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,11 @@ bitflags = "1.3"
libc = "0.2"

[build-dependencies]
reqwest = { optional = true, version = "0.11", features = ["blocking"] }
flate2 = { optional = true, version = "1.0" }
tar = { optional = true, version = "0.4" }
bytes = { optional = true, version = "1" }
cc = { optional = true, version = "1.0.73" }
cmake = { optional = true, version = "0.1" }
pkg-config = { optional = true, version = "0.3" }

[features]
default = ["build_unicorn_cmake"]

build_unicorn_cmake = ["cc", "cmake"]
use_system_unicorn = ["pkg-config"]
build_unicorn_cmake = ["reqwest", "flate2", "tar", "bytes"]
198 changes: 48 additions & 150 deletions bindings/rust/build.rs
Original file line number Diff line number Diff line change
@@ -1,174 +1,72 @@
#[cfg(feature = "build_unicorn_cmake")]
use bytes::Buf;
#[cfg(feature = "build_unicorn_cmake")]
use flate2::read::GzDecoder;
#[cfg(feature = "use_system_unicorn")]
use pkg_config;
#[cfg(feature = "build_unicorn_cmake")]
use reqwest::header::USER_AGENT;
#[cfg(feature = "build_unicorn_cmake")]
use std::path::{Path, PathBuf};
#[cfg(feature = "build_unicorn_cmake")]
use std::{env, process::Command};
use std::env;
#[cfg(feature = "build_unicorn_cmake")]
use tar::Archive;

use std::path::Path;
#[cfg(feature = "build_unicorn_cmake")]
fn find_unicorn(unicorn_dir: &Path) -> Option<PathBuf> {
for entry in std::fs::read_dir(unicorn_dir).ok()? {
let entry = entry.unwrap();
let path = entry.path();

if path.is_dir() && path.file_name()?.to_str()?.contains("unicorn") {
return Some(path);
}
use std::process::Command;

#[cfg(all(feature = "build_unicorn_cmake"))]
fn setup_env_msvc() {
let devenv_path = cc::windows_registry::find_tool(
env::var("TARGET").unwrap().as_str(), "devenv").unwrap();
let devenv_dir = devenv_path.path().parent().unwrap();

let cmake_path = devenv_dir.join(r"CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe");
let ninja_path = devenv_dir.join(r"CommonExtensions\Microsoft\CMake\Ninja\ninja.exe");
if !cmake_path.is_file() {
panic!("missing cmake");
}

None
}

#[cfg(feature = "build_unicorn_cmake")]
fn out_dir() -> PathBuf {
let out_dir = env::var("OUT_DIR").unwrap();
Path::new(&out_dir).to_path_buf()
}

#[cfg(feature = "build_unicorn_cmake")]
fn download_unicorn() -> PathBuf {
// https://docs.github.com/en/rest/reference/repos#download-a-repository-archive-tar
let pkg_version;
if let Ok(unicorn_version) = env::var("UNICORN_VERSION") {
pkg_version = unicorn_version;
} else {
pkg_version = env::var("CARGO_PKG_VERSION").unwrap();
if !ninja_path.is_file() {
panic!("missing ninja");
}
let out_dir = out_dir();
let client = reqwest::blocking::Client::new();
let resp = client
.get(format!(
"https://api.github.com/repos/unicorn-engine/unicorn/tarball/{}",
pkg_version
))
.header(USER_AGENT, "unicorn-engine-rust-bindings")
.send()
.unwrap()
.bytes()
.unwrap();
let tar = GzDecoder::new(resp.reader());

let mut archive = Archive::new(tar);
archive.unpack(&out_dir).unwrap();

find_unicorn(&out_dir).unwrap()
// 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);
}
}

#[cfg(feature = "build_unicorn_cmake")]
#[allow(clippy::branches_sharing_code)]
fn build_with_cmake() {
let profile = env::var("PROFILE").unwrap();

if let Some(unicorn_dir) = find_unicorn(&out_dir()) {
let rust_build_path = unicorn_dir.join("build_rust");
println!(
"cargo:rustc-link-search={}",
rust_build_path.to_str().unwrap()
);
println!(
"cargo:rustc-link-search={}",
rust_build_path.join("Debug").to_str().unwrap()
);
println!(
"cargo:rustc-link-search={}",
rust_build_path.join("Release").to_str().unwrap()
);
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let manifest_path = Path::new(&manifest_dir).to_path_buf();
let src_dir = manifest_path.parent().unwrap().parent().unwrap();

let compiler = cc::Build::new().get_compiler();
let has_ninja = if compiler.is_like_msvc() {
setup_env_msvc();
true
} else {
let unicorn_dir = if let Result::Ok(_) = env::var("UNICORN_LOCAL") {
Path::new("..").join("..")
} else {
println!("cargo:warning=Unicorn not found. Downloading...");
download_unicorn()
};

let rust_build_path = unicorn_dir.join("build_rust");

let mut cmd = Command::new("cmake");

// We don't use TARGET since we can't cross-build.
if env::consts::OS == "windows" {
// Windows
cmd.current_dir(&unicorn_dir)
.arg("-B")
.arg("build_rust")
.arg("-DBUILD_SHARED_LIBS=OFF")
.arg("-G")
.arg("Visual Studio 16 2019");

if profile == "debug" {
cmd.arg("-DCMAKE_BUILD_TYPE=Debug");
} else {
cmd.arg("-DCMAKE_BUILD_TYPE=Release");
}

cmd.output()
.expect("Fail to create build directory on Windows.");
Command::new("ninja").arg("--version").spawn().is_ok()
};

let mut platform = "x64";
let mut conf = "Release";
if std::mem::size_of::<usize>() == 4 {
platform = "Win32";
}
if profile == "debug" {
conf = "Debug";
}

Command::new("msbuild")
.current_dir(&rust_build_path)
.arg("unicorn.sln")
.arg("-m")
.arg("-p:Platform=".to_owned() + platform)
.arg("-p:Configuration=".to_owned() + conf)
.output()
.expect("Fail to build unicorn on Win32.");
println!(
"cargo:rustc-link-search={}",
rust_build_path.to_str().unwrap()
);
} else {
// Most Unix-like systems
let mut cmd = Command::new("cmake");
cmd.current_dir(&unicorn_dir)
.arg("-B")
.arg("build_rust")
.arg("-DBUILD_SHARED_LIBS=OFF");

if profile == "debug" {
cmd.arg("-DCMAKE_BUILD_TYPE=Debug");
} else {
cmd.arg("-DCMAKE_BUILD_TYPE=Release");
}

cmd.output()
.expect("Fail to create build directory on *nix.");

Command::new("make")
.current_dir(&rust_build_path)
.arg("-j6")
.output()
.expect("Fail to build unicorn on *nix.");

println!(
"cargo:rustc-link-search={}",
rust_build_path.to_str().unwrap()
);
}
let mut config = cmake::Config::new(src_dir);
if has_ninja {
config.generator("Ninja");
}
// 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("BUILD_SHARED_LIBS", "OFF")
.no_build_target(true)
.build();
println!("cargo:rustc-link-search=native={}", dst.join("build").display());

// Lazymio(@wtdcode): Why do I stick to static link? See: https://github.com/rust-lang/cargo/issues/5077
println!("cargo:rustc-link-lib=unicorn");
if env::consts::OS != "windows" {
if !compiler.is_like_msvc() {
println!("cargo:rustc-link-lib=pthread");
println!("cargo:rustc-link-lib=m");
}

println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src");
}
Expand Down

0 comments on commit d0cad65

Please sign in to comment.