1use ::cc::Build as CcBuild;
2use ::std::{
3 fs::read_dir,
4 io::Error as IoError,
5 path::Path,
6};
7
8pub use ::cc::Error as CcError;
9
10mod lua_conf;
11pub use lua_conf::*;
12pub mod platforms;
13
14use platforms::{
15 Platform, from_current_triple, CURRENT_TRIPLE,
16};
17
18#[repr(transparent)]
20pub struct Build {
21 cc: CcBuild,
22}
23
24impl Build {
25 pub fn for_current() -> Self {
28 let Some(platform) = from_current_triple() else {
29 panic!("couldn't determine platform for current target triple {CURRENT_TRIPLE:?}");
30 };
31 Self::new(platform)
32 }
33}
34
35impl Build {
36 pub fn new<P: Platform>(p: P) -> Self {
41 match Self::try_new(p) {
42 Ok(b) => b,
43 Err(e) => panic!("{e}"),
44 }
45 }
46
47 pub fn try_new<P: Platform>(p: P) -> Result<Self, CcError> {
49 let mut cc = CcBuild::new();
50
51 {
52 let tool = cc.try_get_compiler()?;
53
54 let stds = p.standards();
55 let mut set_std = |std: Option<&str>| if let Some(std) = std { cc.std(std); };
56 if tool.is_like_gnu() {
57 set_std(stds.gnu)
58 } else if tool.is_like_clang() {
59 set_std(stds.clang)
60 } else if tool.is_like_msvc() {
61 set_std(stds.msvc)
62 } else if tool.is_like_clang_cl() {
63 set_std(stds.clang_cl)
64 }
65 }
66
67 cc.warnings(true).extra_warnings(true);
68 for define in p.defines() {
69 cc.define(define, None);
70 }
71
72 Ok(Self {
73 cc,
74 })
75 }
76
77 pub fn compile(&self, output: &str) {
82 if let Err(e) = self.try_compile(output) {
83 panic!("{e}");
84 }
85 }
86
87 pub fn try_compile(&self, output: &str) -> Result<(), CcError> {
89 self.cc.try_compile(output)
90 }
91
92 pub fn host(&mut self, host: &str) -> &mut Self {
94 self.cc.host(host);
95 self
96 }
97
98 pub fn out_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
100 self.cc.out_dir(path);
101 self
102 }
103
104 fn define_flag(&mut self, flag: &str) -> &mut Self {
105 self.cc.define(flag, None);
106 self
107 }
108
109 fn define_lit(&mut self, ident: &str, data: &str) -> &mut Self {
110 self.cc.define(ident, Some(data));
111 self
112 }
113
114 fn define_str(&mut self, ident: &str, data: &str) -> &mut Self {
115 let data = format!("\"{}\"", data.replace('"', "\\\"").replace('\\', "\\\\"));
116 self.define_lit(ident, &data)
117 }
118
119 pub fn add_lunka_src(&mut self) -> &mut Self {
123 match self.try_add_lunka_src() {
124 Ok(s) => s,
125 Err(e) => panic!("{e}"),
126 }
127 }
128
129 pub fn try_add_lunka_src(&mut self) -> Result<&mut Self, IoError> {
132 let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("lua-5.4.8");
133 self.include(root.join("include"));
134 let src = {
135 let mut b = root;
136 b.push("src");
137 b
138 };
139 for result in read_dir(src)? {
140 let item = result?;
141 if !item.file_type()?.is_file() {
142 continue
143 }
144 self.cc.file(item.path());
145 }
146 Ok(self)
147 }
148
149 pub fn add_lua_src<P: AsRef<Path>>(&mut self, root: P) -> &mut Self {
160 match self.try_add_lua_src(root) {
161 Ok(s) => s,
162 Err(e) => panic!("{e}"),
163 }
164 }
165
166 pub fn try_add_lua_src<P: AsRef<Path>>(&mut self, root: P) -> Result<&mut Self, IoError> {
174 const BINARIES: [&str; 2] = ["lua.c", "luac.c"];
175 for result in read_dir(root)? {
176 let item = result?;
177 if !item.file_type()?.is_file() {
178 continue
179 }
180
181 let file_name = item.file_name();
182 let Some(file_name) = file_name.to_str() else {
183 continue
184 };
185 if !file_name.ends_with(".c") || BINARIES.contains(&file_name) {
186 continue
187 }
188
189 self.cc.file(item.path());
190 }
191 Ok(self)
192 }
193
194 pub fn include<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
196 self.cc.include(path);
197 self
198 }
199
200 pub fn includes<P>(&mut self, paths: P) -> &mut Self
202 where
203 P: IntoIterator,
204 P::Item: AsRef<Path>,
205 {
206 self.cc.includes(paths);
207 self
208 }
209
210 pub fn debug_info(&mut self, emit_debug_info: bool) -> &mut Self {
212 self.cc.debug(emit_debug_info);
213 self
214 }
215
216 pub fn opt_level(&mut self, opt_level: u32) -> &mut Self {
218 self.cc.opt_level(opt_level);
219 self
220 }
221
222 pub fn compat_lua_5_3(&mut self) -> &mut Self {
224 self.define_flag("LUA_COMPAT_5_3")
225 }
226
227 pub fn compat_math_lib(&mut self) -> &mut Self {
229 self.define_flag("LUA_COMPAT_MATH_LIB")
230 }
231
232 pub fn compat_lt_le(&mut self) -> &mut Self {
234 self.define_flag("LUA_COMPAT_LT_LE")
235 }
236
237 pub fn api_checks(&mut self) -> &mut Self {
239 self.define_flag("LUA_USE_APICHECK")
240 }
241
242 pub fn lua_lib_path(&mut self, path: &str) -> &mut Self {
244 self.define_str("LUA_PATH_DEFAULT", path)
245 }
246
247 pub fn lua_c_lib_path(&mut self, path: &str) -> &mut Self {
249 self.define_str("LUA_CPATH_DEFAULT", path)
250 }
251
252 pub fn dir_separator(&mut self, sep: &str) -> &mut Self {
254 self.define_str("LUA_DIRSEP", sep)
255 }
256
257 pub fn unicode_identifiers(&mut self) -> &mut Self {
262 self.define_flag("LUA_UCID")
263 }
264
265 pub fn lua_conf<S: AsRef<str>>(&mut self, lua_conf: &LuaConf<S>) -> &mut Self {
267 if lua_conf.no_number_to_string {
268 self.define_flag("LUNKA_NOCVTN2S");
269 }
270 if lua_conf.no_string_to_number {
271 self.define_flag("LUNKA_NOCVTS2N");
272 }
273 if let Some(extra_space) = lua_conf.extra_space.as_ref().map(move |s| s.as_ref()) {
274 self.define_lit("LUNKA_EXTRASPACE", extra_space);
275 }
276 if let Some(id_size) = lua_conf.id_size.as_ref().map(move |s| s.as_ref()) {
277 self.define_lit("LUNKA_IDSIZE", id_size);
278 }
279 self
280 }
281
282 pub fn use_32_bits(&mut self) -> &mut Self {
284 self.define_flag("LUNKA_32BITS")
285 }
286}