254 lines
8.5 KiB
Rust
254 lines
8.5 KiB
Rust
use std::{
|
|
env,
|
|
fs::{copy, create_dir_all, read_dir, write},
|
|
io, mem,
|
|
path::{Path, PathBuf},
|
|
process::Command,
|
|
};
|
|
use which::which;
|
|
|
|
/// Unwraps a Result and panics in a way that prints a nicer error message to the user.
|
|
macro_rules! panic_err {
|
|
($value:expr) => {{ $value.unwrap_or_else(|err| panic!("{err}")) }};
|
|
($value:expr, $($arg:expr)+) => {{ $value.unwrap_or_else(|err| panic!("{}: {err}", format_args!($($arg),+))) }};
|
|
}
|
|
|
|
/// Formats arguments in a way that is intended to address environment variables.
|
|
macro_rules! env_name {
|
|
($($arg:expr),+) => {{ format!($($arg),+).replace("-", "_").to_uppercase() }};
|
|
}
|
|
|
|
/// Expands to the value of an environment variable.
|
|
macro_rules! env {
|
|
($($arg:expr),+) => { panic_err!(env::var(env_name!($($arg),+))) };
|
|
}
|
|
|
|
/// Expands to the value of a cargo configuration environment variable.
|
|
macro_rules! cfg {
|
|
($($arg:expr),+) => { env!("CARGO_CFG_{}", format_args!($($arg),+)) };
|
|
}
|
|
|
|
/// Whether a cargo feature is enabled.
|
|
macro_rules! feature {
|
|
($($arg:expr),+) => { env::var_os(env_name!("CARGO_FEATURE_{}", format_args!($($arg),+))).is_some() };
|
|
}
|
|
|
|
fn main() {
|
|
let src_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src");
|
|
let out_path = PathBuf::from(env!("OUT_DIR"));
|
|
|
|
panic_err!(
|
|
copy_recursive(&src_path, &out_path),
|
|
"failed to copy luajit source (is the Git submodule initialised?)"
|
|
);
|
|
|
|
panic_err!(write(out_path.join(".relver"), get_relver()));
|
|
println!("cargo::rerun-if-changed={}", src_path.display()); // rerun if luajit source changed
|
|
|
|
// NOTE: we cannot build bindings without building luajit first (luajit generates some headers)
|
|
build_runtime(&out_path.join("src"));
|
|
build_bindings(&out_path.join("src"));
|
|
}
|
|
|
|
fn get_relver() -> String {
|
|
let out = panic_err!(
|
|
Command::new("git")
|
|
.args(["show", "-s", "--format=%ct"])
|
|
.output(),
|
|
"failed to obtain luajit release version (is the Git command available?)"
|
|
);
|
|
|
|
String::from_utf8_lossy(&out.stdout).into()
|
|
}
|
|
|
|
fn copy_recursive(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
|
create_dir_all(dst.as_ref())?;
|
|
|
|
for entry in read_dir(src)? {
|
|
let entry = entry?;
|
|
if entry.file_name().to_str() == Some(".git") {
|
|
continue;
|
|
}
|
|
|
|
let path = dst.as_ref().join(entry.file_name());
|
|
if entry.file_type()?.is_dir() {
|
|
copy_recursive(entry.path(), path)?;
|
|
} else {
|
|
copy(entry.path(), path)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn find_make() -> Command {
|
|
// always use gnu make on bsds
|
|
let name = match parse_target(&env!("HOST")) {
|
|
(_, _, "freebsd" | "openbsd" | "netbsd" | "dragonfly", _) => "gmake",
|
|
_ => "make",
|
|
};
|
|
|
|
Command::new(panic_err!(which(name), "failed to find make"))
|
|
}
|
|
|
|
fn find_cc(target: impl AsRef<str>) -> PathBuf {
|
|
let cc = cc::Build::new().target(target.as_ref()).get_compiler();
|
|
let path = panic_err!(which(cc.path()), "failed to find cc");
|
|
path.to_str().unwrap().into()
|
|
}
|
|
|
|
fn parse_target(target: &str) -> (&str, &str, &str, Option<&str>) {
|
|
let mut i = target.splitn(4, "-");
|
|
let arch = i.next().unwrap();
|
|
let vendor = i.next().unwrap();
|
|
let os = i.next().unwrap();
|
|
let env = i.next();
|
|
(arch, vendor, os, env)
|
|
}
|
|
|
|
fn build_runtime(src_path: &Path) {
|
|
let host = env!("HOST"); // host triplet
|
|
let host_cc = find_cc(&host).to_str().unwrap().to_owned();
|
|
let host_ptr_width = 8 * mem::size_of::<usize>();
|
|
|
|
let target = env!("TARGET"); // target triplet
|
|
let target_cc = find_cc(&target).to_str().unwrap().to_owned();
|
|
// let target_features: HashSet<&str> = HashSet::from_iter(cfg!("target_feature").split(","));
|
|
let target_ptr_width: usize = cfg!("target_pointer_width").parse().unwrap();
|
|
|
|
let mut make = find_make();
|
|
|
|
make.current_dir(src_path)
|
|
.env_clear()
|
|
.arg("-e")
|
|
.env("PATH", env!("PATH"))
|
|
.env("MAKEFLAGS", env!("CARGO_MAKEFLAGS"))
|
|
.env("BUILDMODE", "static");
|
|
|
|
let ccopt = vec![format!("-O{} -fomit-frame-pointer", env!("OPT_LEVEL"))]; // propagate opt_level
|
|
let mut ccdebug = vec![];
|
|
let mut xcflags = vec![];
|
|
|
|
if env!("OPT_LEVEL") == "0" {
|
|
ccdebug.push("-g"); // generate debug information
|
|
xcflags.push("-DLUAJIT_USE_GDBJIT"); // gdb support
|
|
xcflags.push("-DLUA_USE_APICHECK -DLUA_USE_ASSERT"); // enable assertions
|
|
}
|
|
|
|
xcflags.push(if feature!("unwind") {
|
|
"-DLUAJIT_UNWIND_EXTERNAL -funwind-tables" // external frame unwinding (C++ exceptions)
|
|
} else {
|
|
"-DLUAJIT_UNWIND_INTERNAL" // internal frame unwinding (setjmp/longjmp)
|
|
});
|
|
|
|
if !feature!("jit") {
|
|
xcflags.push("-DLUAJIT_DISABLE_JIT");
|
|
}
|
|
|
|
if !feature!("ffi") {
|
|
xcflags.push("-DLUAJIT_DISABLE_FFI");
|
|
}
|
|
|
|
if !feature!("bundled-alloc") {
|
|
xcflags.push("-DLUAJIT_USE_SYSMALLOC"); // using system malloc disables the bundled allocator
|
|
}
|
|
|
|
if feature!("lua52") {
|
|
xcflags.push("-DLUAJIT_ENABLE_LUA52COMPAT");
|
|
}
|
|
|
|
// host toolchain config
|
|
match (host_ptr_width, target_ptr_width) {
|
|
(64, 64) | (32, 32) => make.env("HOST_CC", &host_cc),
|
|
(64, 32) => make.env("HOST_CC", format!("{host_cc} -m32")),
|
|
(n, m) if n != m => panic!("cannot cross-compile on {n}-bit host for {m}-bit target"),
|
|
(n, _) => panic!("unsupported {n}-bit architecture"),
|
|
};
|
|
|
|
// target system config
|
|
match cfg!("target_os").as_str() {
|
|
"linux" | "android" => make.env("TARGET_SYS", "Linux"),
|
|
"windows" => make.env("TARGET_SYS", "Windows"),
|
|
"macos" => make.env("TARGET_SYS", "Darwin").env(
|
|
"MACOSX_DEPLOYMENT_TARGET",
|
|
env::var("MACOSX_DEPLOYMENT_TARGET").as_deref().unwrap_or(
|
|
match cfg!("target_arch").as_str() {
|
|
"x86_64" | "x86_64h" => "10.12",
|
|
"aarch64" | "arm64" | "arm64e" => "11.0",
|
|
arch => panic!("unsupported MacOS target architecture '{arch}'"),
|
|
},
|
|
),
|
|
),
|
|
"ios" => make.env("TARGET_SYS", "iOS"),
|
|
"solaris" => make.env("TARGET_SYS", "SunOS"),
|
|
_ => make.env("TARGET_SYS", "Other"),
|
|
};
|
|
|
|
// target toolchain config
|
|
if let Some(cross) = target_cc.strip_suffix("gcc") {
|
|
make.env("CC", "gcc").env("CROSS", cross);
|
|
} else if let Some(cross) = target_cc.strip_suffix("clang") {
|
|
make.env("CC", "clang").env("CROSS", cross);
|
|
} else {
|
|
let path = Path::new(&target_cc);
|
|
make.env("CC", path.file_name().unwrap())
|
|
.env("CROSS", format!("{}/", path.parent().unwrap().display()));
|
|
}
|
|
|
|
// propagate linker config
|
|
if let Ok(path) = env::var("RUSTC_LINKER") {
|
|
make.env("TARGET_LD", panic_err!(which(path), "failed to find ld"));
|
|
}
|
|
|
|
make.env("CCOPT", ccopt.join(" "))
|
|
.env("CCDEBUG", ccdebug.join(" "))
|
|
.env("XCFLAGS", xcflags.join(" "));
|
|
|
|
if target == host {
|
|
println!("--- begin compile for {target}:");
|
|
} else {
|
|
println!("--- begin cross-compile on {host} for {target}:");
|
|
}
|
|
|
|
println!("{make:?}");
|
|
let status = panic_err!(make.status(), "failed to execute make");
|
|
(!status.success()).then(|| panic!("failed to compile luajit: {status}: {make:?}"));
|
|
|
|
println!(
|
|
"cargo::rustc-env=LUAJIT_SYS_JITLIB={}",
|
|
src_path.join("jit").display(),
|
|
);
|
|
|
|
if feature!("runtime") {
|
|
println!("cargo::rustc-link-search=native={}", src_path.display());
|
|
println!("cargo::rustc-link-lib=static=luajit");
|
|
}
|
|
}
|
|
|
|
fn build_bindings(src_path: &Path) {
|
|
let mut clang_args = vec![format!("--target={}", env!("TARGET"))];
|
|
let abi = if feature!("unwind") {
|
|
clang_args.push("-fexceptions".into());
|
|
bindgen::Abi::CUnwind
|
|
} else {
|
|
clang_args.push("-fno-exceptions".into());
|
|
bindgen::Abi::C
|
|
};
|
|
|
|
let mut bindgen = bindgen::builder()
|
|
.clang_args(clang_args)
|
|
.allowlist_item(r"^(lua|LUA).*_.+$")
|
|
.formatter(bindgen::Formatter::None)
|
|
.default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
|
|
.generate_cstr(true)
|
|
.override_abi(abi, ".*");
|
|
|
|
for header in ["lua.h", "lualib.h", "lauxlib.h", "luaconf.h", "luajit.h"] {
|
|
bindgen = bindgen.header(src_path.join(header).to_str().unwrap());
|
|
}
|
|
|
|
let path = src_path.join("bindgen.rs");
|
|
panic_err!(panic_err!(bindgen.generate(), "failed to generate bindings").write_to_file(&path));
|
|
println!("cargo::rustc-env=LUAJIT_SYS_BINDGEN={}", path.display());
|
|
}
|