Working commit
This commit is contained in:
@@ -3,22 +3,13 @@ name = "luajit"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[features]
|
||||
default = ["enable-jit", "enable-ffi"]
|
||||
runtime = []
|
||||
enable-jit = []
|
||||
enable-ffi = []
|
||||
runtime = ["luajit-sys/runtime"]
|
||||
unwind = ["luajit-sys/unwind"]
|
||||
|
||||
[dependencies]
|
||||
bitflags = { version = "2.9.1", features = ["std"] }
|
||||
bstr = "1.12.0"
|
||||
luaffi = { version = "0.1.0", path = "../luaffi" }
|
||||
luajit-sys = { version = "0.1.0", path = "../luajit-sys" }
|
||||
thiserror = "2.0.12"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.71.1"
|
||||
cc = "1.2.26"
|
||||
which = "8.0.0"
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
use std::{
|
||||
env,
|
||||
fs::{copy, create_dir_all, read_dir, write},
|
||||
io, mem,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
use which::which;
|
||||
|
||||
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),+))) };
|
||||
}
|
||||
|
||||
macro_rules! env_name {
|
||||
($($arg:expr),+) => { format!($($arg),+).replace("-", "_").to_uppercase() };
|
||||
}
|
||||
|
||||
macro_rules! env {
|
||||
($($arg:expr),+) => { panic_err!(env::var(env_name!($($arg),+))) };
|
||||
}
|
||||
|
||||
macro_rules! cfg {
|
||||
($($arg:expr),+) => { env!("CARGO_CFG_{}", format_args!($($arg),+)) };
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
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 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 {
|
||||
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 mut make = find_make();
|
||||
let opt_level = env!("OPT_LEVEL");
|
||||
let debug = opt_level == "0";
|
||||
|
||||
let host = env!("HOST");
|
||||
let host_cc = find_cc(&host).to_str().unwrap().to_owned();
|
||||
let host_ptr_width = 8 * mem::size_of::<usize>();
|
||||
|
||||
let target = env!("TARGET");
|
||||
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();
|
||||
|
||||
if target == host {
|
||||
println!("--- begin compile for {target}:");
|
||||
} else {
|
||||
println!("--- begin cross-compile on {host} for {target}:");
|
||||
}
|
||||
|
||||
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{opt_level} -fomit-frame-pointer")];
|
||||
|
||||
let mut xcflags = vec![
|
||||
"-DLUAJIT_ENABLE_LUA52COMPAT", // lua 5.2 compatibility
|
||||
"-DLUAJIT_USE_SYSMALLOC", // disable bundled allocator
|
||||
"-DLUAJIT_UNWIND_EXTERNAL -funwind-tables", // use external frame unwinding
|
||||
];
|
||||
|
||||
if debug {
|
||||
make.env("CCDEBUG", "-g"); // generate debug information
|
||||
xcflags.push("-DLUAJIT_USE_GDBJIT"); // gdb support
|
||||
xcflags.push("-DLUA_USE_APICHECK -DLUA_USE_ASSERT"); // enable assertions
|
||||
}
|
||||
|
||||
if !feature!("enable-jit") {
|
||||
xcflags.push("-DLUAJIT_DISABLE_JIT");
|
||||
}
|
||||
|
||||
if !feature!("enable-ffi") {
|
||||
xcflags.push("-DLUAJIT_DISABLE_FFI");
|
||||
}
|
||||
|
||||
match (host_ptr_width, target_ptr_width) {
|
||||
(64, 64) | (32, 32) => make.env("HOST_CC", &host_cc),
|
||||
(64, 32) => make.env("HOST_CC", format!("{} -m32", host_cc)),
|
||||
(n, m) if n != m => panic!("cannot cross-compile on {n}-bit host for {m}-bit target"),
|
||||
(n, _) => panic!("unsupported {n}-bit architecture"),
|
||||
};
|
||||
|
||||
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"),
|
||||
};
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
if let Some(path) = env::var("RUSTC_LINKER").ok() {
|
||||
make.env("TARGET_LD", panic_err!(which(path), "failed to find ld"));
|
||||
}
|
||||
|
||||
make.env("CCOPT", ccopt.join(" "))
|
||||
.env("XCFLAGS", xcflags.join(" "));
|
||||
|
||||
let status = panic_err!(make.status(), "failed to execute make");
|
||||
(!status.success()).then(|| panic!("failed to compile luajit: {status}: {make:?}"));
|
||||
|
||||
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 bindgen = bindgen::builder()
|
||||
.clang_arg(format!("--target={}", env!("TARGET")))
|
||||
.allowlist_item(r"^(lua|LUA).*_.+$")
|
||||
.formatter(bindgen::Formatter::None)
|
||||
.default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
|
||||
.generate_cstr(true);
|
||||
|
||||
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=LJ_BINDINGS={}", path.display());
|
||||
}
|
||||
Submodule crates/luajit/src deleted from f9140a622a
@@ -2,6 +2,7 @@
|
||||
use bitflags::bitflags;
|
||||
use bstr::{BStr, BString, ByteSlice};
|
||||
use luaffi::future::lua_pollable;
|
||||
use luajit_sys::*;
|
||||
use std::{
|
||||
alloc::{Layout, alloc, dealloc, realloc},
|
||||
ffi::{CString, NulError},
|
||||
@@ -16,131 +17,79 @@ use std::{
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
include!(env!("LJ_BINDINGS"));
|
||||
|
||||
// constants not exposed by lua.h
|
||||
pub const LUA_TPROTO: c_int = LUA_TTHREAD + 1;
|
||||
pub const LUA_TCDATA: c_int = LUA_TTHREAD + 2;
|
||||
|
||||
// macros not translated by bindgen
|
||||
pub unsafe fn lua_upvalueindex(i: c_int) -> c_int {
|
||||
LUA_GLOBALSINDEX - i
|
||||
}
|
||||
|
||||
pub unsafe fn lua_pop(L: *mut lua_State, n: c_int) {
|
||||
unsafe { lua_settop(L, -n - 1) }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_newtable(L: *mut lua_State) {
|
||||
unsafe { lua_createtable(L, 0, 0) }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_register(L: *mut lua_State, n: *const c_char, f: lua_CFunction) {
|
||||
unsafe { (lua_pushcfunction(L, f), lua_setglobal(L, n)) };
|
||||
}
|
||||
|
||||
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
|
||||
unsafe { lua_pushcclosure(L, f, 0) }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_strlen(L: *mut lua_State, i: c_int) -> usize {
|
||||
unsafe { lua_objlen(L, i) }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
|
||||
unsafe { (lua_type(L, n) == LUA_TFUNCTION) as c_int }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_istable(L: *mut lua_State, n: c_int) -> c_int {
|
||||
unsafe { (lua_type(L, n) == LUA_TTABLE) as c_int }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_islightuserdata(L: *mut lua_State, n: c_int) -> c_int {
|
||||
unsafe { (lua_type(L, n) == LUA_TLIGHTUSERDATA) as c_int }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_isnil(L: *mut lua_State, n: c_int) -> c_int {
|
||||
unsafe { (lua_type(L, n) == LUA_TNIL) as c_int }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int {
|
||||
unsafe { (lua_type(L, n) == LUA_TBOOLEAN) as c_int }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
|
||||
unsafe { (lua_type(L, n) == LUA_TTHREAD) as c_int }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
|
||||
unsafe { (lua_type(L, n) == LUA_TNONE) as c_int }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
|
||||
unsafe { (lua_type(L, n) <= LUA_TNIL) as c_int }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: impl AsRef<[u8]>) {
|
||||
unsafe { lua_pushlstring(L, s.as_ref().as_ptr().cast(), s.as_ref().len()) }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_setglobal(L: *mut lua_State, s: *const c_char) {
|
||||
unsafe { lua_setfield(L, LUA_GLOBALSINDEX, s) }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_getglobal(L: *mut lua_State, s: *const c_char) {
|
||||
unsafe { lua_getfield(L, LUA_GLOBALSINDEX, s) }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
unsafe { lua_tolstring(L, i, ptr::null_mut()) }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_open() -> *mut lua_State {
|
||||
unsafe { luaL_newstate() }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_getregistry(L: *mut lua_State) {
|
||||
unsafe { lua_pushvalue(L, LUA_REGISTRYINDEX) }
|
||||
}
|
||||
|
||||
pub unsafe fn lua_getgccount(L: *mut lua_State) -> c_int {
|
||||
unsafe { lua_gc(L, LUA_GCCOUNT, 0) }
|
||||
}
|
||||
|
||||
/// LuaJIT error.
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// Out of memory error.
|
||||
#[error("out of memory")]
|
||||
OutOfMemory,
|
||||
/// Lua syntax error returned by [`Stack::load`].
|
||||
#[error("{msg}")]
|
||||
Syntax { chunk: BString, msg: BString },
|
||||
Syntax {
|
||||
/// Content of the chunk which had errors.
|
||||
chunk: BString,
|
||||
/// Lua error message.
|
||||
msg: BString,
|
||||
},
|
||||
/// Lua chunk name error returned by [`Stack::load`].
|
||||
#[error("bad chunk name: {0}")]
|
||||
BadChunkName(NulError),
|
||||
/// Lua error returned by [`Stack::call`].
|
||||
#[error("{msg}")]
|
||||
Call { msg: BString },
|
||||
Call {
|
||||
/// Lua error message.
|
||||
msg: BString,
|
||||
},
|
||||
/// Lua error returned by [`Stack::resume`].
|
||||
#[error("{msg}")]
|
||||
Resume { msg: BString, trace: BString },
|
||||
Resume {
|
||||
/// Lua error message.
|
||||
msg: BString,
|
||||
/// Lua stack trace.
|
||||
trace: BString,
|
||||
},
|
||||
/// Type mismatch type error.
|
||||
#[error("{0} expected, got {1}")]
|
||||
InvalidType(&'static str, &'static str),
|
||||
InvalidType(
|
||||
/// The expected type.
|
||||
&'static str,
|
||||
/// The actual type.
|
||||
&'static str,
|
||||
),
|
||||
/// Invalid UTF-8 string error.
|
||||
#[error("{0}")]
|
||||
InvalidUtf8(#[from] Utf8Error),
|
||||
}
|
||||
|
||||
/// Lua type.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
pub enum Type {
|
||||
/// `nil` type.
|
||||
#[default]
|
||||
Nil,
|
||||
/// `boolean` type.
|
||||
Boolean,
|
||||
/// `lightuserdata` type.
|
||||
Lightuserdata,
|
||||
/// `number` type.
|
||||
Number,
|
||||
/// `string` type.
|
||||
String,
|
||||
/// `table` type.
|
||||
Table,
|
||||
/// `function` type.
|
||||
Function,
|
||||
/// `userdata` type.
|
||||
Userdata,
|
||||
/// `thread` type.
|
||||
Thread,
|
||||
/// `cdata` type.
|
||||
Cdata,
|
||||
}
|
||||
|
||||
impl Type {
|
||||
/// Converts a raw Lua type code to [`Type`], returning [`None`] if the value is invalid.
|
||||
pub fn from_code(code: c_int) -> Option<Self> {
|
||||
Some(match code {
|
||||
LUA_TNIL => Self::Nil,
|
||||
@@ -157,6 +106,7 @@ impl Type {
|
||||
})
|
||||
}
|
||||
|
||||
/// Name of this type, like `"nil"` or `"string"`.
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Nil => "nil",
|
||||
@@ -179,15 +129,20 @@ impl fmt::Display for Type {
|
||||
}
|
||||
}
|
||||
|
||||
/// Lua thread status.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
pub enum Status {
|
||||
/// Thread is alive (currently running or can run a function).
|
||||
#[default]
|
||||
Normal,
|
||||
/// Thread terminated with an error and cannot be resumed.
|
||||
Dead,
|
||||
/// Thread suspended with `coroutine.yield(...)` and is awaiting resume.
|
||||
Suspended,
|
||||
}
|
||||
|
||||
impl Status {
|
||||
/// Converts a raw Lua status code to [`Status`], returning [`None`] if the value is invalid.
|
||||
pub fn from_code(code: c_int) -> Option<Self> {
|
||||
Some(match code {
|
||||
LUA_OK => Self::Normal,
|
||||
@@ -197,6 +152,7 @@ impl Status {
|
||||
})
|
||||
}
|
||||
|
||||
/// Name of this status, like `"normal"` or `"dead"`.
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Status::Normal => "normal",
|
||||
@@ -212,14 +168,18 @@ impl fmt::Display for Status {
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of [`Stack::resume`].
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum ResumeStatus {
|
||||
/// Thread returned successfully and can run another function.
|
||||
#[default]
|
||||
Ok,
|
||||
/// Thread suspended with `coroutine.yield(...)` and is awaiting resume.
|
||||
Suspended,
|
||||
}
|
||||
|
||||
impl ResumeStatus {
|
||||
/// Name of this status, like `"ok"` or `"suspended"`.
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
ResumeStatus::Ok => "ok",
|
||||
@@ -722,7 +682,10 @@ impl Stack {
|
||||
let value = self.index(1);
|
||||
let ptr = value.cdata::<lua_pollable>().cast_mut();
|
||||
if ptr.is_null() {
|
||||
return Err(Error::InvalidType("cdata", value.type_of().name()));
|
||||
return Err(Error::InvalidType(
|
||||
"cdata<struct lua_pollable>",
|
||||
value.type_of().name(),
|
||||
));
|
||||
} else {
|
||||
unsafe { (&mut *ptr).await }
|
||||
narg = 1;
|
||||
Reference in New Issue
Block a user