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}
18
19pub struct Artifacts {
20 lib_dir: PathBuf,
21 libs: Vec<String>,
22}
23
24impl Default for Build {
25 fn default() -> Build {
26 Build {
27 out_dir: env::var_os("OUT_DIR").map(PathBuf::from),
28 target: env::var("TARGET").ok(),
29 }
30 }
31}
32
33impl Build {
34 pub fn new() -> Build {
35 Build::default()
36 }
37
38 pub fn out_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Build {
39 self.out_dir = Some(path.as_ref().to_path_buf());
40 self
41 }
42
43 pub fn target(&mut self, target: &str) -> &mut Build {
44 self.target = Some(target.to_string());
45 self
46 }
47
48 pub fn build(&mut self, version: Version) -> Artifacts {
49 let target = &self.target.as_ref().expect("TARGET is not set")[..];
50 let out_dir = self.out_dir.as_ref().expect("OUT_DIR is not set");
51 let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
52 let mut source_dir = manifest_dir.join(version.source_dir());
53 let build_dir = out_dir.join("lua-build");
54
55 let mut config = cc::Build::new();
56 config.warnings(false).cargo_metadata(false);
57
58 match target {
59 _ if target.contains("linux") => {
60 config.define("LUA_USE_LINUX", None);
61 }
62 _ if target.ends_with("bsd") => {
63 config.define("LUA_USE_LINUX", None);
64 }
65 _ if target.contains("apple-darwin") => {
66 match version {
67 Lua51 => config.define("LUA_USE_LINUX", None),
68 _ => config.define("LUA_USE_MACOSX", None),
69 };
70 }
71 _ if target.contains("apple-ios") => {
72 match version {
73 Lua54 => config.define("LUA_USE_IOS", None),
74 _ => config.define("LUA_USE_POSIX", None),
75 };
76 }
77 _ if target.contains("windows") => {
78 config.define("LUA_USE_WINDOWS", None);
80 }
81 _ if target.ends_with("emscripten") => {
82 config
83 .define("LUA_USE_POSIX", None)
84 .cpp(true)
85 .flag("-fexceptions"); let cpp_source_dir = out_dir.join("cpp_source");
88 if cpp_source_dir.exists() {
89 fs::remove_dir_all(&cpp_source_dir).unwrap();
90 }
91 fs::create_dir_all(&cpp_source_dir).unwrap();
92
93 for file in fs::read_dir(&source_dir).unwrap() {
94 let file = file.unwrap();
95 let filename = file.file_name();
96 let filename = filename.to_str().unwrap();
97 let src_file = source_dir.join(file.file_name());
98 let dst_file = cpp_source_dir.join(file.file_name());
99
100 let mut content = fs::read(src_file).unwrap();
101 if ["lauxlib.h", "lua.h", "lualib.h"].contains(&filename) {
102 content.splice(0..0, b"extern \"C\" {\n".to_vec());
103 content.extend(b"\n}".to_vec())
104 }
105 fs::write(dst_file, content).unwrap();
106 }
107 source_dir = cpp_source_dir
108 }
109 _ => panic!("don't know how to build Lua for {target}"),
110 }
111
112 if let Lua54 = version {
113 config.define("LUA_COMPAT_5_3", None);
114 #[cfg(feature = "ucid")]
115 config.define("LUA_UCID", None);
116 }
117
118 if cfg!(debug_assertions) {
119 config.define("LUA_USE_APICHECK", None);
120 }
121
122 config
123 .include(&source_dir)
124 .flag("-w") .flag_if_supported("-fno-common") .add_files_by_ext(&source_dir, "c")
127 .out_dir(&build_dir)
128 .compile(version.lib_name());
129
130 Artifacts {
131 lib_dir: build_dir,
132 libs: vec![version.lib_name().to_string()],
133 }
134 }
135}
136
137impl Version {
138 fn source_dir(&self) -> &str {
139 match self {
140 Lua51 => "lua-5.1.5",
141 Lua52 => "lua-5.2.4",
142 Lua53 => "lua-5.3.6",
143 Lua54 => "lua-5.4.7",
144 }
145 }
146
147 fn lib_name(&self) -> &str {
148 match self {
149 Lua51 => "lua5.1",
150 Lua52 => "lua5.2",
151 Lua53 => "lua5.3",
152 Lua54 => "lua5.4",
153 }
154 }
155}
156
157impl Artifacts {
158 pub fn lib_dir(&self) -> &Path {
159 &self.lib_dir
160 }
161
162 pub fn libs(&self) -> &[String] {
163 &self.libs
164 }
165
166 pub fn print_cargo_metadata(&self) {
167 println!("cargo:rustc-link-search=native={}", self.lib_dir.display());
168 for lib in self.libs.iter() {
169 println!("cargo:rustc-link-lib=static={lib}");
170 }
171 }
172}
173
174trait AddFilesByExt {
175 fn add_files_by_ext(&mut self, dir: &Path, ext: &str) -> &mut Self;
176}
177
178impl AddFilesByExt for cc::Build {
179 fn add_files_by_ext(&mut self, dir: &Path, ext: &str) -> &mut Self {
180 for entry in fs::read_dir(dir)
181 .unwrap()
182 .filter_map(|e| e.ok())
183 .filter(|e| e.path().extension() == Some(ext.as_ref()))
184 {
185 self.file(entry.path());
186 }
187 self
188 }
189}