1use std::env;
2use std::fs;
3use std::path::{Path, PathBuf};
4use std::process::Command;
5
6pub struct Build {
7 out_dir: Option<PathBuf>,
8 target: Option<String>,
9 host: Option<String>,
10 lua52compat: bool,
11}
12
13pub struct Artifacts {
14 lib_dir: PathBuf,
15 libs: Vec<String>,
16}
17
18impl Default for Build {
19 fn default() -> Self {
20 Build {
21 out_dir: env::var_os("OUT_DIR").map(PathBuf::from),
22 target: env::var("TARGET").ok(),
23 host: env::var("HOST").ok(),
24 lua52compat: false,
25 }
26 }
27}
28
29impl Build {
30 pub fn new() -> Build {
31 Build::default()
32 }
33
34 pub fn out_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Build {
35 self.out_dir = Some(path.as_ref().to_path_buf());
36 self
37 }
38
39 pub fn target(&mut self, target: &str) -> &mut Build {
40 self.target = Some(target.to_string());
41 self
42 }
43
44 pub fn host(&mut self, host: &str) -> &mut Build {
45 self.host = Some(host.to_string());
46 self
47 }
48
49 pub fn lua52compat(&mut self, enabled: bool) -> &mut Build {
50 self.lua52compat = enabled;
51 self
52 }
53
54 fn cmd_make(&self) -> Command {
55 match &self.host.as_ref().expect("HOST is not set")[..] {
56 "x86_64-unknown-dragonfly" => Command::new("gmake"),
57 "x86_64-unknown-freebsd" => Command::new("gmake"),
58 _ => Command::new("make"),
59 }
60 }
61
62 pub fn build(&mut self) -> Artifacts {
63 let target = &self.target.as_ref().expect("TARGET is not set")[..];
64
65 if target.contains("msvc") {
66 return self.build_msvc();
67 }
68
69 self.build_unix()
70 }
71
72 fn build_unix(&mut self) -> Artifacts {
73 let target = &self.target.as_ref().expect("TARGET is not set")[..];
74 let host = &self.host.as_ref().expect("HOST is not set")[..];
75 let out_dir = self.out_dir.as_ref().expect("OUT_DIR is not set");
76 let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
77 let source_dir = manifest_dir.join("luajit2");
78 let build_dir = out_dir.join("luajit-build");
79
80 if build_dir.exists() {
82 fs::remove_dir_all(&build_dir).unwrap();
83 }
84 fs::create_dir_all(&build_dir)
85 .unwrap_or_else(|e| panic!("cannot create {}: {}", build_dir.display(), e));
86 cp_r(&source_dir, &build_dir);
87
88 #[rustfmt::skip]
90 fs::copy(manifest_dir.join("luajit_relver.txt"), build_dir.join(".relver")).unwrap();
91
92 let mut cc = cc::Build::new();
93 cc.warnings(false);
94 let compiler = cc.get_compiler();
95 let compiler_path = compiler.path().to_str().unwrap();
96
97 let mut make = self.cmd_make();
98 make.current_dir(build_dir.join("src"));
99 make.arg("-e");
100
101 match target {
102 "x86_64-apple-darwin" if env::var_os("MACOSX_DEPLOYMENT_TARGET").is_none() => {
103 make.env("MACOSX_DEPLOYMENT_TARGET", "10.11");
104 }
105 "aarch64-apple-darwin" if env::var_os("MACOSX_DEPLOYMENT_TARGET").is_none() => {
106 make.env("MACOSX_DEPLOYMENT_TARGET", "11.0");
107 }
108 _ if target.contains("linux") => {
109 make.env("TARGET_SYS", "Linux");
110 }
111 _ if target.contains("windows") => {
112 make.env("TARGET_SYS", "Windows");
113 }
114 _ => {}
115 }
116
117 let target_pointer_width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap();
118 if target_pointer_width == "32" && env::var_os("HOST_CC").is_none() {
119 let host_cc = cc::Build::new().target(host).get_compiler();
121 make.env("HOST_CC", format!("{} -m32", host_cc.path().display()));
122 }
123
124 let prefix = if compiler_path.ends_with("-gcc") {
128 &compiler_path[..compiler_path.len() - 3]
129 } else if compiler_path.ends_with("-clang") {
130 &compiler_path[..compiler_path.len() - 5]
131 } else {
132 ""
133 };
134
135 let compiler_path =
136 which::which(compiler_path).unwrap_or_else(|_| panic!("cannot find {compiler_path}"));
137 let bindir = compiler_path.parent().unwrap();
138 let compiler_path = compiler_path.to_str().unwrap();
139 let compiler_args = compiler.cflags_env();
140 let compiler_args = compiler_args.to_str().unwrap();
141 if env::var_os("STATIC_CC").is_none() {
142 make.env("STATIC_CC", format!("{compiler_path} {compiler_args}"));
143 }
144 if env::var_os("TARGET_LD").is_none() {
145 make.env("TARGET_LD", format!("{compiler_path} {compiler_args}"));
146 }
147
148 if env::var_os("TARGET_AR").is_none() {
150 let mut ar = if bindir.join(format!("{prefix}ar")).is_file() {
151 bindir.join(format!("{prefix}ar")).into_os_string()
152 } else if compiler.is_like_clang() && bindir.join("llvm-ar").is_file() {
153 bindir.join("llvm-ar").into_os_string()
154 } else if compiler.is_like_gnu() && bindir.join("ar").is_file() {
155 bindir.join("ar").into_os_string()
156 } else if let Ok(ar) = which::which(format!("{prefix}ar")) {
157 ar.into_os_string()
158 } else {
159 panic!("cannot find {prefix}ar")
160 };
161 ar.push(" rcus");
162 make.env("TARGET_AR", ar);
163 }
164
165 if env::var_os("TARGET_STRIP").is_none() {
167 let strip = if bindir.join(format!("{prefix}strip")).is_file() {
168 bindir.join(format!("{prefix}strip"))
169 } else if compiler.is_like_clang() && bindir.join("llvm-strip").is_file() {
170 bindir.join("llvm-strip")
171 } else if compiler.is_like_gnu() && bindir.join("strip").is_file() {
172 bindir.join("strip")
173 } else if let Ok(strip) = which::which(format!("{prefix}strip")) {
174 strip
175 } else {
176 panic!("cannot find {prefix}strip")
177 };
178 make.env("TARGET_STRIP", strip);
179 }
180
181 let mut xcflags = vec!["-fPIC"];
182 if self.lua52compat {
183 xcflags.push("-DLUAJIT_ENABLE_LUA52COMPAT");
184 }
185 if cfg!(debug_assertions) {
186 xcflags.push("-DLUA_USE_ASSERT");
187 xcflags.push("-DLUA_USE_APICHECK");
188 }
189
190 make.env("BUILDMODE", "static");
191 make.env("XCFLAGS", xcflags.join(" "));
192 self.run_command(make, "building LuaJIT");
193
194 Artifacts {
195 lib_dir: build_dir.join("src"),
196 libs: vec!["luajit".to_string()],
197 }
198 }
199
200 fn build_msvc(&mut self) -> Artifacts {
201 let target = &self.target.as_ref().expect("TARGET is not set")[..];
202 let out_dir = self.out_dir.as_ref().expect("OUT_DIR is not set");
203 let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
204 let source_dir = manifest_dir.join("luajit2");
205 let extras_dir = manifest_dir.join("extras");
206 let build_dir = out_dir.join("luajit-build");
207
208 if build_dir.exists() {
210 fs::remove_dir_all(&build_dir).unwrap();
211 }
212 fs::create_dir_all(&build_dir)
213 .unwrap_or_else(|e| panic!("cannot create {}: {}", build_dir.display(), e));
214 cp_r(&source_dir, &build_dir);
215
216 #[rustfmt::skip]
218 fs::copy(manifest_dir.join("luajit_relver.txt"), build_dir.join(".relver")).unwrap();
219
220 let mut msvcbuild = Command::new(build_dir.join("src").join("msvcbuild.bat"));
221 msvcbuild.current_dir(build_dir.join("src"));
222 if self.lua52compat {
223 cp_r(&extras_dir, &build_dir.join("src"));
224 msvcbuild.arg("lua52c");
225 }
226 msvcbuild.arg("static");
227
228 let cl = cc::windows_registry::find_tool(target, "cl.exe").expect("failed to find cl");
229 for (k, v) in cl.env() {
230 msvcbuild.env(k, v);
231 }
232
233 self.run_command(msvcbuild, "building LuaJIT");
234
235 Artifacts {
236 lib_dir: build_dir.join("src"),
237 libs: vec!["lua51".to_string()],
238 }
239 }
240
241 fn run_command(&self, mut command: Command, desc: &str) {
242 let status = command.status().unwrap();
243 if !status.success() {
244 panic!(
245 "
246Error {desc}:
247 Command: {command:?}
248 Exit status: {status}
249 "
250 );
251 }
252 }
253}
254
255fn cp_r(src: &Path, dst: &Path) {
256 for f in fs::read_dir(src).unwrap() {
257 let f = f.unwrap();
258 let path = f.path();
259 let name = path.file_name().unwrap();
260
261 if name.to_str() == Some(".git") {
263 continue;
264 }
265
266 let dst = dst.join(name);
267 if f.file_type().unwrap().is_dir() {
268 fs::create_dir_all(&dst).unwrap();
269 cp_r(&path, &dst);
270 } else {
271 let _ = fs::remove_file(&dst);
272 fs::copy(&path, &dst).unwrap();
273 }
274 }
275}
276
277impl Artifacts {
278 pub fn lib_dir(&self) -> &Path {
279 &self.lib_dir
280 }
281
282 pub fn libs(&self) -> &[String] {
283 &self.libs
284 }
285
286 pub fn print_cargo_metadata(&self) {
287 println!("cargo:rerun-if-env-changed=HOST_CC");
288 println!("cargo:rerun-if-env-changed=STATIC_CC");
289 println!("cargo:rerun-if-env-changed=TARGET_LD");
290 println!("cargo:rerun-if-env-changed=TARGET_AR");
291 println!("cargo:rerun-if-env-changed=TARGET_STRIP");
292 println!("cargo:rerun-if-env-changed=MACOSX_DEPLOYMENT_TARGET");
293
294 println!("cargo:rustc-link-search=native={}", self.lib_dir.display());
295 for lib in self.libs.iter() {
296 println!("cargo:rustc-link-lib=static={lib}");
297 }
298 }
299}