lua_src/
lib.rs

1use std::env;
2use std::fs;
3use std::path::{Path, PathBuf};
4
5#[derive(Debug, PartialEq, Eq)]
6pub enum Version {
7    Lua51,
8    Lua52,
9    Lua53,
10    Lua54,
11}
12pub use self::Version::*;
13
14pub struct Build {
15    out_dir: Option<PathBuf>,
16    target: Option<String>,
17    host: Option<String>,
18}
19
20pub struct Artifacts {
21    include_dir: PathBuf,
22    lib_dir: PathBuf,
23    libs: Vec<String>,
24}
25
26impl Build {
27    #[allow(clippy::new_without_default)]
28    pub fn new() -> Build {
29        Build {
30            out_dir: env::var_os("OUT_DIR").map(|s| PathBuf::from(s).join("lua-build")),
31            target: env::var("TARGET").ok(),
32            host: env::var("HOST").ok(),
33        }
34    }
35
36    pub fn out_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Build {
37        self.out_dir = Some(path.as_ref().to_path_buf());
38        self
39    }
40
41    pub fn target(&mut self, target: &str) -> &mut Build {
42        self.target = Some(target.to_string());
43        self
44    }
45
46    pub fn host(&mut self, host: &str) -> &mut Build {
47        self.host = Some(host.to_string());
48        self
49    }
50
51    pub fn build(&mut self, version: Version) -> Artifacts {
52        let target = &self.target.as_ref().expect("TARGET not set")[..];
53        let host = &self.host.as_ref().expect("HOST not set")[..];
54        let out_dir = self.out_dir.as_ref().expect("OUT_DIR not set");
55        let lib_dir = out_dir.join("lib");
56        let include_dir = out_dir.join("include");
57
58        let source_dir_base = Path::new(env!("CARGO_MANIFEST_DIR"));
59        let mut source_dir = match version {
60            Lua51 => source_dir_base.join("lua-5.1.5"),
61            Lua52 => source_dir_base.join("lua-5.2.4"),
62            Lua53 => source_dir_base.join("lua-5.3.6"),
63            Lua54 => source_dir_base.join("lua-5.4.7"),
64        };
65
66        if lib_dir.exists() {
67            fs::remove_dir_all(&lib_dir).unwrap();
68        }
69        fs::create_dir_all(&lib_dir).unwrap();
70
71        if include_dir.exists() {
72            fs::remove_dir_all(&include_dir).unwrap();
73        }
74        fs::create_dir_all(&include_dir).unwrap();
75
76        let mut config = cc::Build::new();
77        config
78            .target(target)
79            .host(host)
80            .warnings(false)
81            .opt_level(2)
82            .cargo_metadata(false);
83
84        match target {
85            _ if target.contains("linux") => {
86                config.define("LUA_USE_LINUX", None);
87            }
88            _ if target.ends_with("bsd") => {
89                config.define("LUA_USE_LINUX", None);
90            }
91            _ if target.contains("apple-darwin") => {
92                match version {
93                    Lua51 => config.define("LUA_USE_LINUX", None),
94                    _ => config.define("LUA_USE_MACOSX", None),
95                };
96            }
97            _ if target.contains("apple-ios") => {
98                match version {
99                    Lua54 => config.define("LUA_USE_IOS", None),
100                    _ => config.define("LUA_USE_POSIX", None),
101                };
102            }
103            _ if target.contains("windows") => {
104                // Defined in Lua >= 5.3
105                config.define("LUA_USE_WINDOWS", None);
106            }
107            _ if target.ends_with("emscripten") => {
108                config
109                    .define("LUA_USE_POSIX", None)
110                    .cpp(true)
111                    .flag("-fexceptions"); // Enable exceptions to be caught
112
113                let cpp_source_dir = out_dir.join("cpp_source");
114                if cpp_source_dir.exists() {
115                    fs::remove_dir_all(&cpp_source_dir).unwrap();
116                }
117                fs::create_dir_all(&cpp_source_dir).unwrap();
118
119                for file in fs::read_dir(&source_dir).unwrap() {
120                    let file = file.unwrap();
121                    let filename = file.file_name();
122                    let filename = filename.to_str().unwrap();
123                    let src_file = source_dir.join(file.file_name());
124                    let dst_file = cpp_source_dir.join(file.file_name());
125
126                    let mut content = fs::read(src_file).unwrap();
127                    if ["lauxlib.h", "lua.h", "lualib.h"].contains(&filename) {
128                        content.splice(0..0, b"extern \"C\" {\n".to_vec());
129                        content.extend(b"\n}".to_vec())
130                    }
131                    fs::write(dst_file, content).unwrap();
132                }
133                source_dir = cpp_source_dir
134            }
135            _ => panic!("don't know how to build Lua for {}", target),
136        };
137
138        if let Lua54 = version {
139            config.define("LUA_COMPAT_5_3", None);
140            #[cfg(feature = "ucid")]
141            config.define("LUA_UCID", None);
142        }
143
144        if cfg!(debug_assertions) {
145            config.define("LUA_USE_APICHECK", None);
146        }
147
148        let lib_name = match version {
149            Lua51 => "lua5.1",
150            Lua52 => "lua5.2",
151            Lua53 => "lua5.3",
152            Lua54 => "lua5.4",
153        };
154
155        config
156            .include(&source_dir)
157            .flag("-w") // Suppress all warnings
158            .flag_if_supported("-fno-common") // Compile common globals like normal definitions
159            .file(source_dir.join("lapi.c"))
160            .file(source_dir.join("lauxlib.c"))
161            .file(source_dir.join("lbaselib.c"))
162            // skipped: lbitlib.c (>= 5.2, <= 5.3)
163            .file(source_dir.join("lcode.c"))
164            // skipped: lcorolib.c (>= 5.2)
165            // skipped: lctype.c (>= 5.2)
166            .file(source_dir.join("ldblib.c"))
167            .file(source_dir.join("ldebug.c"))
168            .file(source_dir.join("ldo.c"))
169            .file(source_dir.join("ldump.c"))
170            .file(source_dir.join("lfunc.c"))
171            .file(source_dir.join("lgc.c"))
172            .file(source_dir.join("linit.c"))
173            .file(source_dir.join("liolib.c"))
174            .file(source_dir.join("llex.c"))
175            .file(source_dir.join("lmathlib.c"))
176            .file(source_dir.join("lmem.c"))
177            .file(source_dir.join("loadlib.c"))
178            .file(source_dir.join("lobject.c"))
179            .file(source_dir.join("lopcodes.c"))
180            .file(source_dir.join("loslib.c"))
181            .file(source_dir.join("lparser.c"))
182            .file(source_dir.join("lstate.c"))
183            .file(source_dir.join("lstring.c"))
184            .file(source_dir.join("lstrlib.c"))
185            .file(source_dir.join("ltable.c"))
186            .file(source_dir.join("ltablib.c"))
187            .file(source_dir.join("ltm.c"))
188            .file(source_dir.join("lundump.c"))
189            // skipped: lutf8lib.c (>= 5.3)
190            .file(source_dir.join("lvm.c"))
191            .file(source_dir.join("lzio.c"));
192
193        match version {
194            Lua51 => {}
195            Lua52 => {
196                config
197                    .file(source_dir.join("lbitlib.c"))
198                    .file(source_dir.join("lcorolib.c"))
199                    .file(source_dir.join("lctype.c"));
200            }
201            Lua53 => {
202                config
203                    .file(source_dir.join("lbitlib.c"))
204                    .file(source_dir.join("lcorolib.c"))
205                    .file(source_dir.join("lctype.c"))
206                    .file(source_dir.join("lutf8lib.c"));
207            }
208            Lua54 => {
209                config
210                    .file(source_dir.join("lcorolib.c"))
211                    .file(source_dir.join("lctype.c"))
212                    .file(source_dir.join("lutf8lib.c"));
213            }
214        }
215
216        config.out_dir(&lib_dir).compile(lib_name);
217
218        for f in &["lauxlib.h", "lua.h", "luaconf.h", "lualib.h"] {
219            fs::copy(source_dir.join(f), include_dir.join(f)).unwrap();
220        }
221
222        Artifacts {
223            lib_dir,
224            include_dir,
225            libs: vec![lib_name.to_string()],
226        }
227    }
228}
229
230impl Artifacts {
231    pub fn include_dir(&self) -> &Path {
232        &self.include_dir
233    }
234
235    pub fn lib_dir(&self) -> &Path {
236        &self.lib_dir
237    }
238
239    pub fn libs(&self) -> &[String] {
240        &self.libs
241    }
242
243    pub fn print_cargo_metadata(&self) {
244        println!("cargo:rustc-link-search=native={}", self.lib_dir.display());
245        for lib in self.libs.iter() {
246            println!("cargo:rustc-link-lib=static={}", lib);
247        }
248        println!("cargo:include={}", self.include_dir.display());
249        println!("cargo:lib={}", self.lib_dir.display());
250    }
251}