diff --git a/Cargo.toml b/Cargo.toml index 29c3e91..5c7d61e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,8 @@ readme = "README.md" keywords = ["lua", "luajit"] categories = ["api-bindings"] license = "MIT" +links = "lua" +build = "build/main.rs" description = """ High level bindings to Lua 5.1/5.2/5.3 (including LuaJIT) with support of writing native lua modules in Rust. @@ -23,11 +25,13 @@ members = [ ] [features] -default = ["lua53"] +default = ["lua53", "lua-vendored"] lua53 = [] lua52 = [] lua51 = [] luajit = [] +lua-vendored = ["lua-src"] +luajit-vendored = ["luajit", "luajit-src"] [dependencies] num-traits = { version = "0.2.6" } @@ -36,6 +40,8 @@ bstr = { version = "0.2", features = ["std"], default_features = false } [build-dependencies] cc = { version = "1.0" } pkg-config = { version = "0.3.11" } +lua-src = { version = "535.0.1", optional = true } +luajit-src = { version = "210.0.0", optional = true } [dev-dependencies] rustyline = "5.0" diff --git a/build.rs b/build.rs deleted file mode 100644 index e8ad65e..0000000 --- a/build.rs +++ /dev/null @@ -1,189 +0,0 @@ -use std::env; -use std::fs::File; -use std::io::{BufRead, BufReader, Error, ErrorKind, Result}; -use std::ops::Bound; -use std::path::{Path, PathBuf}; -use std::process::Command; - -trait CommandExt { - fn execute(&mut self) -> Result<()>; -} - -impl CommandExt for Command { - /// Execute the command and return an error if it exited with a failure status. - fn execute(&mut self) -> Result<()> { - self.status() - .and_then(|status| { - if status.success() { - Ok(()) - } else { - Err(Error::new(ErrorKind::Other, "non-zero exit code")) - } - }) - .map_err(|_| { - Error::new( - ErrorKind::Other, - format!("The command {:?} did not run successfully.", self), - ) - }) - } -} - -fn use_custom_lua>(include_dir: &S, lib_dir: &S, lua_lib: &S) -> Result { - let mut version_found = String::new(); - - // Find LUA_VERSION_NUM - let mut lua_h_path = PathBuf::from(include_dir.as_ref()); - lua_h_path.push("lua.h"); - let f = File::open(lua_h_path)?; - let reader = BufReader::new(f); - for line in reader.lines() { - let line = line?; - let parts = line.split_whitespace().collect::>(); - if parts.len() == 3 && parts[1] == "LUA_VERSION_NUM" { - version_found = parts[2].to_string(); - } - } - - let mut static_link = ""; - if env::var("LUA_LINK").unwrap_or(String::new()) == "static" { - static_link = "static="; - } - - println!("cargo:rustc-link-search=native={}", lib_dir.as_ref()); - println!("cargo:rustc-link-lib={}{}", static_link, lua_lib.as_ref()); - - Ok(version_found) -} - -fn build_glue + std::fmt::Debug>(include_paths: &[P]) { - let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); - - // Ensure the presence of glue.rs - // if build_dir.join("glue.rs").exists() { - // return; - // } - - let mut config = cc::Build::new(); - - for path in include_paths { - config.include(path); - } - - // Compile and run glue.c - let glue = build_dir.join("glue"); - - config - .get_compiler() - .to_command() - .arg("src/ffi/glue/glue.c") - .arg("-o") - .arg(&glue) - .execute() - .unwrap(); - - Command::new(glue) - .arg(build_dir.join("glue.rs")) - .execute() - .unwrap(); -} - -fn main() { - let include_dir = env::var("LUA_INC").unwrap_or(String::new()); - let lib_dir = env::var("LUA_LIB").unwrap_or(String::new()); - let lua_lib = env::var("LUA_LIB_NAME").unwrap_or(String::new()); - - println!("cargo:rerun-if-env-changed=LUA_INC"); - println!("cargo:rerun-if-env-changed=LUA_LIB"); - println!("cargo:rerun-if-env-changed=LUA_LIB_NAME"); - println!("cargo:rerun-if-env-changed=LUA_LINK"); - println!("cargo:rerun-if-changed=src/ffi/glue/glue.c"); - - if include_dir != "" && lib_dir != "" && lua_lib != "" { - let _version = use_custom_lua(&include_dir, &lib_dir, &lua_lib).unwrap(); - build_glue(&[include_dir]); - return; - } - - // Find lua via pkg-config - - #[cfg(not(any( - feature = "lua53", - feature = "lua52", - feature = "lua51", - feature = "luajit" - )))] - panic!("You must enable one of the features: lua53, lua52, lua51, luajit"); - - #[cfg(all( - feature = "lua53", - any(feature = "lua52", feature = "lua51", feature = "luajit") - ))] - panic!("You can enable only one of the features: lua53, lua52, lua51, luajit"); - - #[cfg(all(feature = "lua52", any(feature = "lua51", feature = "luajit")))] - panic!("You can enable only one of the features: lua53, lua52, lua51, luajit"); - - #[cfg(all(feature = "lua51", feature = "luajit"))] - panic!("You can enable only one of the features: lua53, lua52, lua51, luajit"); - - #[cfg(feature = "lua53")] - { - let mut lua = pkg_config::Config::new() - .range_version((Bound::Included("5.3"), Bound::Excluded("5.4"))) - .probe("lua"); - - if lua.is_err() { - lua = pkg_config::Config::new().probe("lua5.3"); - } - - match lua { - Ok(lua) => build_glue(&lua.include_paths), - Err(err) => panic!(err), - }; - } - - #[cfg(feature = "lua52")] - { - let mut lua = pkg_config::Config::new() - .range_version((Bound::Included("5.2"), Bound::Excluded("5.3"))) - .probe("lua"); - - if lua.is_err() { - lua = pkg_config::Config::new().probe("lua5.2"); - } - - match lua { - Ok(lua) => build_glue(&lua.include_paths), - Err(_) => panic!("Lua 5.2 not found"), - }; - } - - #[cfg(feature = "lua51")] - { - let mut lua = pkg_config::Config::new() - .range_version((Bound::Included("5.1"), Bound::Excluded("5.2"))) - .probe("lua"); - - if lua.is_err() { - lua = pkg_config::Config::new().probe("lua5.1"); - } - - match lua { - Ok(lua) => build_glue(&lua.include_paths), - Err(err) => panic!(err), - }; - } - - #[cfg(feature = "luajit")] - { - let lua = pkg_config::Config::new() - .range_version((Bound::Included("2.0.5"), Bound::Unbounded)) - .probe("luajit"); - - match lua { - Ok(lua) => build_glue(&lua.include_paths), - Err(err) => panic!(err), - }; - } -} diff --git a/build/find_normal.rs b/build/find_normal.rs new file mode 100644 index 0000000..8604d40 --- /dev/null +++ b/build/find_normal.rs @@ -0,0 +1,106 @@ +use std::env; +use std::ffi::OsString; +use std::fs::File; +use std::io::{BufRead, BufReader, Result}; +use std::ops::Bound; +use std::path::{Path, PathBuf}; + +pub fn probe_lua() -> PathBuf { + let include_dir = env::var_os("LUA_INC").unwrap_or(OsString::new()); + let lib_dir = env::var_os("LUA_LIB").unwrap_or(OsString::new()); + let lua_lib = env::var_os("LUA_LIB_NAME").unwrap_or(OsString::new()); + + println!("cargo:rerun-if-env-changed=LUA_INC"); + println!("cargo:rerun-if-env-changed=LUA_LIB"); + println!("cargo:rerun-if-env-changed=LUA_LIB_NAME"); + println!("cargo:rerun-if-env-changed=LUA_LINK"); + + if include_dir != "" && lib_dir != "" && lua_lib != "" { + let _version = use_custom_lua(&include_dir, &lib_dir, &lua_lib).unwrap(); + return PathBuf::from(include_dir); + } + + // Find using via pkg-config + + #[cfg(feature = "lua53")] + { + let mut lua = pkg_config::Config::new() + .range_version((Bound::Included("5.3"), Bound::Excluded("5.4"))) + .probe("lua"); + + if lua.is_err() { + lua = pkg_config::Config::new().probe("lua5.3"); + } + + return lua.unwrap().include_paths[0].clone(); + } + + #[cfg(feature = "lua52")] + { + let mut lua = pkg_config::Config::new() + .range_version((Bound::Included("5.2"), Bound::Excluded("5.3"))) + .probe("lua"); + + if lua.is_err() { + lua = pkg_config::Config::new().probe("lua5.2"); + } + + return lua.unwrap().include_paths[0].clone(); + } + + #[cfg(feature = "lua51")] + { + let mut lua = pkg_config::Config::new() + .range_version((Bound::Included("5.1"), Bound::Excluded("5.2"))) + .probe("lua"); + + if lua.is_err() { + lua = pkg_config::Config::new().probe("lua5.1"); + } + + return lua.unwrap().include_paths[0].clone(); + } + + #[cfg(feature = "luajit")] + { + let lua = pkg_config::Config::new() + .range_version((Bound::Included("2.1.0"), Bound::Unbounded)) + .probe("luajit"); + + return lua.unwrap().include_paths[0].clone(); + } +} + +fn use_custom_lua>(include_dir: &S, lib_dir: &S, lua_lib: &S) -> Result { + let mut version_found = String::new(); + + // Find LUA_VERSION_NUM + let mut lua_h_path = include_dir.as_ref().to_owned(); + lua_h_path.push("lua.h"); + let f = File::open(lua_h_path)?; + let reader = BufReader::new(f); + for line in reader.lines() { + let line = line?; + let parts = line.split_whitespace().collect::>(); + if parts.len() == 3 && parts[1] == "LUA_VERSION_NUM" { + version_found = parts[2].to_string(); + } + } + + let mut link_lib = String::new(); + if env::var("LUA_LINK").unwrap_or(String::new()) == "static" { + link_lib = "static=".to_string(); + } + + println!( + "cargo:rustc-link-search=native={}", + lib_dir.as_ref().display() + ); + println!( + "cargo:rustc-link-lib={}{}", + link_lib, + lua_lib.as_ref().display() + ); + + Ok(version_found) +} diff --git a/build/find_vendored.rs b/build/find_vendored.rs new file mode 100644 index 0000000..a1bb76a --- /dev/null +++ b/build/find_vendored.rs @@ -0,0 +1,23 @@ +use std::path::PathBuf; + +#[cfg(feature = "lua-vendored")] +use lua_src; +#[cfg(feature = "luajit-vendored")] +use luajit_src; + +pub fn probe_lua() -> PathBuf { + #[cfg(all(feature = "lua53", feature = "lua-vendored"))] + let artifacts = lua_src::Build::new().build(lua_src::Lua53); + #[cfg(all(feature = "lua52", feature = "lua-vendored"))] + let artifacts = lua_src::Build::new().build(lua_src::Lua52); + #[cfg(all(feature = "lua51", feature = "lua-vendored"))] + let artifacts = lua_src::Build::new().build(lua_src::Lua51); + #[cfg(feature = "luajit-vendored")] + let artifacts = luajit_src::Build::new().build(); + + #[cfg(all(feature = "luajit", feature = "lua-vendored"))] + let artifacts = lua_src::Build::new().build(lua_src::Lua51); // Invalid case! Workaround to get panic + + artifacts.print_cargo_metadata(); + artifacts.include_dir().to_owned() +} diff --git a/build/main.rs b/build/main.rs new file mode 100644 index 0000000..851063e --- /dev/null +++ b/build/main.rs @@ -0,0 +1,95 @@ +#![allow(unreachable_code)] + +use std::env; +use std::io::{Error, ErrorKind, Result}; +use std::path::{Path, PathBuf}; +use std::process::Command; + +#[cfg_attr( + any(feature = "lua-vendored", feature = "luajit-vendored"), + path = "find_vendored.rs" +)] +#[cfg_attr( + not(any(feature = "lua-vendored", feature = "luajit-vendored")), + path = "find_normal.rs" +)] +mod find; + +trait CommandExt { + fn execute(&mut self) -> Result<()>; +} + +impl CommandExt for Command { + /// Execute the command and return an error if it exited with a failure status. + fn execute(&mut self) -> Result<()> { + self.status() + .and_then(|status| { + if status.success() { + Ok(()) + } else { + Err(Error::new(ErrorKind::Other, "non-zero exit code")) + } + }) + .map_err(|_| { + Error::new( + ErrorKind::Other, + format!("The command {:?} did not run successfully.", self), + ) + }) + } +} + +fn build_glue + std::fmt::Debug>(include_path: &P) { + let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + + let mut config = cc::Build::new(); + config.include(include_path); + + // Compile and run glue.c + let glue = build_dir.join("glue"); + + config + .get_compiler() + .to_command() + .arg("src/ffi/glue/glue.c") + .arg("-o") + .arg(&glue) + .execute() + .unwrap(); + + Command::new(glue) + .arg(build_dir.join("glue.rs")) + .execute() + .unwrap(); +} + +fn main() { + #[cfg(not(any( + feature = "lua53", + feature = "lua52", + feature = "lua51", + feature = "luajit" + )))] + panic!("You must enable one of the features: lua53, lua52, lua51, luajit"); + + #[cfg(all( + feature = "lua53", + any(feature = "lua52", feature = "lua51", feature = "luajit") + ))] + panic!("You can enable only one of the features: lua53, lua52, lua51, luajit"); + + #[cfg(all(feature = "lua52", any(feature = "lua51", feature = "luajit")))] + panic!("You can enable only one of the features: lua53, lua52, lua51, luajit"); + + #[cfg(all(feature = "lua51", feature = "luajit"))] + panic!("You can enable only one of the features: lua53, lua52, lua51, luajit"); + + #[cfg(all(feature = "lua51", feature = "luajit"))] + panic!("You can enable only one of the features: lua53, lua52, lua51, luajit"); + + #[cfg(all(feature = "lua-vendored", feature = "luajit"))] + panic!("You cannot mix lua-vendored and luajit features"); + + let include_dir = find::probe_lua(); + build_glue(&include_dir); +} diff --git a/src/userdata.rs b/src/userdata.rs index 32817b6..ed5db3d 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -77,11 +77,6 @@ pub enum MetaMethod { /// /// This is not an operator, but it will be called by the built-in `pairs` function. Pairs, - #[cfg(any(feature = "lua53", feature = "lua52"))] - /// The `__ipairs` metamethod. - /// - /// This is not an operator, but it will be called by the built-in `ipairs` function. - IPairs, } impl MetaMethod { @@ -119,8 +114,6 @@ impl MetaMethod { MetaMethod::ToString => b"__tostring", #[cfg(any(feature = "lua53", feature = "lua52"))] MetaMethod::Pairs => b"__pairs", - #[cfg(any(feature = "lua53", feature = "lua52"))] - MetaMethod::IPairs => b"__ipairs", } } } diff --git a/tests/byte_string.rs b/tests/byte_string.rs index ecafd50..3b05ee5 100644 --- a/tests/byte_string.rs +++ b/tests/byte_string.rs @@ -1,14 +1,3 @@ -#![cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - feature(link_args) -)] - -#[cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - link_args = "-pagezero_size 10000 -image_base 100000000" -)] -extern "system" {} - use bstr::{BStr, BString}; use mlua::{Lua, Result}; diff --git a/tests/function.rs b/tests/function.rs index 3744866..cc1c9ab 100644 --- a/tests/function.rs +++ b/tests/function.rs @@ -1,14 +1,3 @@ -#![cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - feature(link_args) -)] - -#[cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - link_args = "-pagezero_size 10000 -image_base 100000000" -)] -extern "system" {} - use mlua::{Function, Lua, Result, String}; #[test] diff --git a/tests/memory.rs b/tests/memory.rs index 53c08bd..cf67c18 100644 --- a/tests/memory.rs +++ b/tests/memory.rs @@ -1,14 +1,3 @@ -#![cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - feature(link_args) -)] - -#[cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - link_args = "-pagezero_size 10000 -image_base 100000000" -)] -extern "system" {} - use std::sync::Arc; use mlua::{Lua, Result, UserData}; diff --git a/tests/scope.rs b/tests/scope.rs index 22f28de..54840d6 100644 --- a/tests/scope.rs +++ b/tests/scope.rs @@ -1,14 +1,3 @@ -#![cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - feature(link_args) -)] - -#[cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - link_args = "-pagezero_size 10000 -image_base 100000000" -)] -extern "system" {} - use std::cell::Cell; use std::rc::Rc; diff --git a/tests/string.rs b/tests/string.rs index ce61f3c..abe8aaa 100644 --- a/tests/string.rs +++ b/tests/string.rs @@ -1,14 +1,3 @@ -#![cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - feature(link_args) -)] - -#[cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - link_args = "-pagezero_size 10000 -image_base 100000000" -)] -extern "system" {} - use std::borrow::Cow; use mlua::{Lua, Result, String}; diff --git a/tests/table.rs b/tests/table.rs index 26d9516..5b8974e 100644 --- a/tests/table.rs +++ b/tests/table.rs @@ -1,14 +1,3 @@ -#![cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - feature(link_args) -)] - -#[cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - link_args = "-pagezero_size 10000 -image_base 100000000" -)] -extern "system" {} - use mlua::{Lua, Nil, Result, Table, Value}; #[test] diff --git a/tests/tests.rs b/tests/tests.rs index 8ef9d48..2784da9 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,14 +1,3 @@ -#![cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - feature(link_args) -)] - -#[cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - link_args = "-pagezero_size 10000 -image_base 100000000" -)] -extern "system" {} - use std::iter::FromIterator; use std::panic::catch_unwind; use std::sync::Arc; diff --git a/tests/thread.rs b/tests/thread.rs index d3266ba..666a7e6 100644 --- a/tests/thread.rs +++ b/tests/thread.rs @@ -1,14 +1,3 @@ -#![cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - feature(link_args) -)] - -#[cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - link_args = "-pagezero_size 10000 -image_base 100000000" -)] -extern "system" {} - use std::panic::catch_unwind; use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus}; diff --git a/tests/types.rs b/tests/types.rs index c5c6c21..72f9484 100644 --- a/tests/types.rs +++ b/tests/types.rs @@ -1,14 +1,3 @@ -#![cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - feature(link_args) -)] - -#[cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - link_args = "-pagezero_size 10000 -image_base 100000000" -)] -extern "system" {} - use std::os::raw::c_void; use mlua::{Function, LightUserData, Lua, Result}; diff --git a/tests/userdata.rs b/tests/userdata.rs index 34aa8b4..64919cf 100644 --- a/tests/userdata.rs +++ b/tests/userdata.rs @@ -1,14 +1,3 @@ -#![cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - feature(link_args) -)] - -#[cfg_attr( - all(feature = "luajit", target_os = "macos", target_arch = "x86_64"), - link_args = "-pagezero_size 10000 -image_base 100000000" -)] -extern "system" {} - use std::sync::Arc; use mlua::{ @@ -107,7 +96,7 @@ fn test_metamethods() -> Result<()> { } }); #[cfg(any(feature = "lua53", feature = "lua52"))] - methods.add_meta_method(MetaMethod::IPairs, |lua, data, ()| { + methods.add_meta_method(MetaMethod::Pairs, |lua, data, ()| { use std::iter::FromIterator; let stateless_iter = lua.create_function(|_, (data, i): (MyUserData, i64)| { let i = i + 1; @@ -132,12 +121,12 @@ fn test_metamethods() -> Result<()> { ); #[cfg(any(feature = "lua53", feature = "lua52"))] - let ipairs_it = { + let pairs_it = { lua.load( r#" - function ipairs_it() + function pairs_it() local r = 0 - for i, v in ipairs(userdata1) do + for i, v in pairs(userdata1) do r = r + v end return r @@ -145,14 +134,14 @@ fn test_metamethods() -> Result<()> { "#, ) .exec()?; - globals.get::<_, Function>("ipairs_it")? + globals.get::<_, Function>("pairs_it")? }; assert_eq!(lua.load("userdata1 - userdata2").eval::()?.0, 4); assert_eq!(lua.load("userdata1:get()").eval::()?, 7); assert_eq!(lua.load("userdata2.inner").eval::()?, 3); #[cfg(any(feature = "lua53", feature = "lua52"))] - assert_eq!(ipairs_it.call::<_, i64>(())?, 28); + assert_eq!(pairs_it.call::<_, i64>(())?, 28); assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err()); let userdata2: Value = globals.get("userdata2")?;