use ::cc::Build as CcBuild;
use ::std::{
fs::read_dir,
io::Error as IoError,
path::Path,
};
pub use ::cc::Error as CcError;
mod lua_conf;
pub use lua_conf::*;
pub mod platforms;
use platforms::{
Platform, from_current_triple, CURRENT_TRIPLE,
};
#[repr(transparent)]
pub struct Build {
cc: CcBuild,
}
impl Build {
pub fn for_current() -> Self {
let Some(platform) = from_current_triple() else {
panic!("couldn't determine platform for current target triple {CURRENT_TRIPLE:?}");
};
Self::new(platform)
}
}
impl Build {
pub fn new<P: Platform>(p: P) -> Self {
match Self::try_new(p) {
Ok(b) => b,
Err(e) => panic!("{e}"),
}
}
pub fn try_new<P: Platform>(p: P) -> Result<Self, CcError> {
let mut cc = CcBuild::new();
{
let tool = cc.try_get_compiler()?;
let stds = p.standards();
let mut set_std = |std: Option<&str>| if let Some(std) = std { cc.std(std); };
if tool.is_like_gnu() {
set_std(stds.gnu)
} else if tool.is_like_clang() {
set_std(stds.clang)
} else if tool.is_like_msvc() {
set_std(stds.msvc)
} else if tool.is_like_clang_cl() {
set_std(stds.clang_cl)
}
}
cc.warnings(true).extra_warnings(true);
for define in p.defines() {
cc.define(define, None);
}
Ok(Self {
cc,
})
}
pub fn compile(&self, output: &str) {
if let Err(e) = self.try_compile(output) {
panic!("{e}");
}
}
pub fn try_compile(&self, output: &str) -> Result<(), CcError> {
self.cc.try_compile(output)
}
pub fn host(&mut self, host: &str) -> &mut Self {
self.cc.host(host);
self
}
pub fn out_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.cc.out_dir(path);
self
}
fn define_flag(&mut self, flag: &str) -> &mut Self {
self.cc.define(flag, None);
self
}
fn define_lit(&mut self, ident: &str, data: &str) -> &mut Self {
self.cc.define(ident, Some(data));
self
}
fn define_str(&mut self, ident: &str, data: &str) -> &mut Self {
let data = format!("\"{}\"", data.replace('"', "\\\"").replace('\\', "\\\\"));
self.define_lit(ident, &data)
}
pub fn add_lunka_src(&mut self) -> &mut Self {
match self.try_add_lunka_src() {
Ok(s) => s,
Err(e) => panic!("{e}"),
}
}
pub fn try_add_lunka_src(&mut self) -> Result<&mut Self, IoError> {
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("lua-5.4.8");
self.include(root.join("include"));
let src = {
let mut b = root;
b.push("src");
b
};
for result in read_dir(src)? {
let item = result?;
if !item.file_type()?.is_file() {
continue
}
self.cc.file(item.path());
}
Ok(self)
}
pub fn add_lua_src<P: AsRef<Path>>(&mut self, root: P) -> &mut Self {
match self.try_add_lua_src(root) {
Ok(s) => s,
Err(e) => panic!("{e}"),
}
}
pub fn try_add_lua_src<P: AsRef<Path>>(&mut self, root: P) -> Result<&mut Self, IoError> {
const BINARIES: [&str; 2] = ["lua.c", "luac.c"];
for result in read_dir(root)? {
let item = result?;
if !item.file_type()?.is_file() {
continue
}
let file_name = item.file_name();
let Some(file_name) = file_name.to_str() else {
continue
};
if !file_name.ends_with(".c") || BINARIES.contains(&file_name) {
continue
}
self.cc.file(item.path());
}
Ok(self)
}
pub fn include<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.cc.include(path);
self
}
pub fn includes<P>(&mut self, paths: P) -> &mut Self
where
P: IntoIterator,
P::Item: AsRef<Path>,
{
self.cc.includes(paths);
self
}
pub fn debug_info(&mut self, emit_debug_info: bool) -> &mut Self {
self.cc.debug(emit_debug_info);
self
}
pub fn opt_level(&mut self, opt_level: u32) -> &mut Self {
self.cc.opt_level(opt_level);
self
}
pub fn compat_lua_5_3(&mut self) -> &mut Self {
self.define_flag("LUA_COMPAT_5_3")
}
pub fn compat_math_lib(&mut self) -> &mut Self {
self.define_flag("LUA_COMPAT_MATH_LIB")
}
pub fn compat_lt_le(&mut self) -> &mut Self {
self.define_flag("LUA_COMPAT_LT_LE")
}
pub fn api_checks(&mut self) -> &mut Self {
self.define_flag("LUA_USE_APICHECK")
}
pub fn lua_lib_path(&mut self, path: &str) -> &mut Self {
self.define_str("LUA_PATH_DEFAULT", path)
}
pub fn lua_c_lib_path(&mut self, path: &str) -> &mut Self {
self.define_str("LUA_CPATH_DEFAULT", path)
}
pub fn dir_separator(&mut self, sep: &str) -> &mut Self {
self.define_str("LUA_DIRSEP", sep)
}
pub fn unicode_identifiers(&mut self) -> &mut Self {
self.define_flag("LUA_UCID")
}
pub fn lua_conf<S: AsRef<str>>(&mut self, lua_conf: &LuaConf<S>) -> &mut Self {
if lua_conf.no_number_to_string {
self.define_flag("LUNKA_NOCVTN2S");
}
if lua_conf.no_string_to_number {
self.define_flag("LUNKA_NOCVTS2N");
}
if let Some(extra_space) = lua_conf.extra_space.as_ref().map(move |s| s.as_ref()) {
self.define_lit("LUNKA_EXTRASPACE", extra_space);
}
if let Some(id_size) = lua_conf.id_size.as_ref().map(move |s| s.as_ref()) {
self.define_lit("LUNKA_IDSIZE", id_size);
}
self
}
pub fn use_32_bits(&mut self) -> &mut Self {
self.define_flag("LUNKA_32BITS")
}
}