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 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"); 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") .flag_if_supported("-fno-common") .file(source_dir.join("lapi.c"))
160 .file(source_dir.join("lauxlib.c"))
161 .file(source_dir.join("lbaselib.c"))
162 .file(source_dir.join("lcode.c"))
164 .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 .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}