1use std::{
2 env,
3 io::{self, Cursor},
4 path::Path,
5 process::{ExitStatus, Stdio},
6};
7
8use crate::{
9 build::utils::{self, CCExt},
10 config::Config,
11 hash::HasIntegrity,
12 lua_version::LuaVersion,
13 operations::{self, UnpackError},
14};
15use bon::Builder;
16use git2::{build::RepoBuilder, FetchOptions};
17use miette::Diagnostic;
18use path_slash::PathExt;
19use ssri::Integrity;
20use target_lexicon::Triple;
21use tempfile::tempdir;
22use thiserror::Error;
23use tokio::{fs, process::Command};
24use tracing::span;
25use url::Url;
26
27const LUA51_VERSION: &str = "5.1.5";
28const LUA51_HASH: &str = "sha256-JkD8VqeV8p0o7xXhPDSkfiI5YLAkDoywqC2bBzhpUzM=";
29const LUA52_VERSION: &str = "5.2.4";
30const LUA52_HASH: &str = "sha256-ueLkqtZ4mztjoFbUQveznw7Pyjrg8fwK5OlhRAG2n0s=";
31const LUA53_VERSION: &str = "5.3.6";
32const LUA53_HASH: &str = "sha256-/F/Wm7hzYyPwJmcrG3I12mE9cXfnJViJOgvc0yBGbWA=";
33const LUA54_VERSION: &str = "5.4.8";
34const LUA54_HASH: &str = "sha256-TxjdrhVOeT5G7qtyfFnvHAwMK3ROe5QhlxDXb1MGKa4=";
35const LUA55_VERSION: &str = "5.5.0";
36const LUA55_HASH: &str = "sha256-V8zDK7vQBcq3W8xSREBSU1r2kXiduiuQFtXFBkDWiz0=";
37const LUAJIT_MM_VERSION: &str = "2.1";
40
41#[derive(Builder)]
42#[builder(start_fn = new, finish_fn(name = _build, vis = ""))]
43pub struct BuildLua<'a> {
44 lua_version: &'a LuaVersion,
45 install_dir: &'a Path,
46 config: &'a Config,
47}
48
49#[derive(Debug, Error, Diagnostic)]
50pub enum BuildLuaError {
51 #[error(transparent)]
52 Request(#[from] reqwest::Error),
53 #[error(transparent)]
54 Io(#[from] io::Error),
55 #[error(transparent)]
56 #[diagnostic(transparent)]
57 Unpack(#[from] UnpackError),
58 #[error(transparent)]
59 Git(#[from] git2::Error),
60 #[error(transparent)]
61 CC(#[from] cc::Error),
62 #[error("failed to find cl.exe")]
63 ClNotFound,
64 #[error("failed to find LINK.exe")]
65 LinkNotFound,
66 #[error(
67 r#"source integrity mismatch.
68- source: {src}
69- expected: {expected}
70- got: {actual}"#
71 )]
72 #[diagnostic(help(
73 r#"the source may have been modified or a tag may have been moved.
74check the source, then rerun the command with `lx --no-lock` to update the hash."#
75 ))]
76 SourceIntegrityMismatch {
77 src: String,
78 expected: Integrity,
79 actual: Integrity,
80 },
81 #[error("{name} failed.\n\n{status}\n\nstdout:\n{stdout}\n\nstderr:\n{stderr}")]
82 CommandFailure {
83 name: String,
84 status: ExitStatus,
85 stdout: String,
86 stderr: String,
87 },
88}
89
90impl<State: build_lua_builder::State + build_lua_builder::IsComplete> BuildLuaBuilder<'_, State> {
91 pub async fn build(self) -> Result<(), BuildLuaError> {
92 let args = self._build();
93 let lua_version = args.lua_version;
94 match lua_version {
95 LuaVersion::Lua51
96 | LuaVersion::Lua52
97 | LuaVersion::Lua53
98 | LuaVersion::Lua54
99 | LuaVersion::Lua55 => do_build_lua(args).await,
100 LuaVersion::LuaJIT | LuaVersion::LuaJIT52 => do_build_luajit(args).await,
101 }
102 }
103}
104
105async fn do_build_luajit(args: BuildLua<'_>) -> Result<(), BuildLuaError> {
106 let span = span!(
107 tracing::Level::INFO,
108 "Building LuaJIT",
109 version = LUAJIT_MM_VERSION,
110 );
111 let _enter = span.enter();
112
113 let build_dir = tempdir().map_err(|err| {
114 io::Error::other(format!(
115 "failed to create lua_installation temp directory:\n{}",
116 err
117 ))
118 })?;
119 let luajit_url = "https://github.com/LuaJIT/LuaJIT.git";
122
123 {
124 let span = span!(
125 tracing::Level::INFO,
126 "Cloning LuaJIT sources",
127 url = luajit_url,
128 );
129 let _enter = span.enter();
130
131 let mut fetch_options = FetchOptions::new();
133 fetch_options.update_fetchhead(false);
134 let mut repo_builder = RepoBuilder::new();
135 repo_builder.fetch_options(fetch_options);
136 let repo = repo_builder.clone(luajit_url, build_dir.path())?;
137 let (object, _) = repo.revparse_ext(&format!("v{LUAJIT_MM_VERSION}"))?;
138 repo.checkout_tree(&object, None)?;
139 }
140 if cfg!(target_env = "msvc") {
141 do_build_luajit_msvc(args, build_dir.path()).await
142 } else {
143 do_build_luajit_unix(args, build_dir.path()).await
144 }
145}
146
147#[tracing::instrument(name = "Compiling LuaJIT", skip_all)]
148async fn do_build_luajit_unix(args: BuildLua<'_>, build_dir: &Path) -> Result<(), BuildLuaError> {
149 let lua_version = args.lua_version;
150 let config = args.config;
151 let install_dir = args.install_dir;
152
153 let host = Triple::host();
154
155 let mut cc = cc::Build::new();
156 cc.cargo_output(false)
157 .cargo_metadata(false)
158 .cargo_warnings(false)
159 .warnings(config.verbose())
160 .opt_level(3)
161 .host(&host.to_string())
162 .target(&host.to_string());
163 let compiler = cc.try_get_compiler()?;
164 let compiler_path = compiler.path().to_slash_lossy().to_string();
165 let mut make_cmd = Command::new(config.make_cmd());
166 make_cmd.current_dir(build_dir.join("src"));
167 make_cmd.arg("-e");
168 make_cmd.stdout(Stdio::piped());
169 make_cmd.stderr(Stdio::piped());
170 let target = host.to_string();
171 match target.as_str() {
172 "x86_64-apple-darwin" if env::var_os("MACOSX_DEPLOYMENT_TARGET").is_none() => {
173 make_cmd.env("MACOSX_DEPLOYMENT_TARGET", "10.11");
174 }
175 "aarch64-apple-darwin" if env::var_os("MACOSX_DEPLOYMENT_TARGET").is_none() => {
176 make_cmd.env("MACOSX_DEPLOYMENT_TARGET", "11.0");
177 }
178 _ if target.contains("linux") => {
179 make_cmd.env("TARGET_SYS", "Linux");
180 }
181 _ => {}
182 }
183 let compiler_path = which::which(&compiler_path)
184 .map_err(|err| io::Error::other(format!("cannot find {}:\n{}", compiler_path, err)))?;
185 let compiler_path = compiler_path.to_slash_lossy().to_string();
186 let compiler_args = compiler.cflags_env();
187 let compiler_args = compiler_args.to_string_lossy();
188 if env::var_os("STATIC_CC").is_none() {
189 make_cmd.env("STATIC_CC", format!("{compiler_path} {compiler_args}"));
190 }
191 if env::var_os("TARGET_LD").is_none() {
192 make_cmd.env("TARGET_LD", format!("{compiler_path} {compiler_args}"));
193 }
194 let mut xcflags = vec!["-fPIC"];
195 if lua_version == &LuaVersion::LuaJIT52 {
196 xcflags.push("-DLUAJIT_ENABLE_LUA52COMPAT");
197 }
198 if cfg!(debug_assertions) {
199 xcflags.push("-DLUA_USE_ASSERT");
200 xcflags.push("-DLUA_USE_APICHECK");
201 }
202 make_cmd.env("BUILDMODE", "static");
203 make_cmd.env("XCFLAGS", xcflags.join(" "));
204
205 match make_cmd.output().await {
206 Ok(output) if output.status.success() => utils::trace_command_output(&output),
207 Ok(output) => {
208 return Err(BuildLuaError::CommandFailure {
209 name: "build".into(),
210 status: output.status,
211 stdout: String::from_utf8_lossy(&output.stdout).into(),
212 stderr: String::from_utf8_lossy(&output.stderr).into(),
213 });
214 }
215 Err(err) => {
216 return Err(BuildLuaError::Io(io::Error::other(format!(
217 "Failed to run `{} build`:\n{}",
218 config.make_cmd(),
219 err
220 ))));
221 }
222 };
223
224 match Command::new(config.make_cmd())
225 .current_dir(build_dir)
226 .stdout(Stdio::piped())
227 .stderr(Stdio::piped())
228 .arg("install")
229 .arg(format!(r#"PREFIX="{}""#, install_dir.display()))
230 .output()
231 .await
232 {
233 Ok(output) if output.status.success() => utils::trace_command_output(&output),
234 Ok(output) => {
235 return Err(BuildLuaError::CommandFailure {
236 name: "install".into(),
237 status: output.status,
238 stdout: String::from_utf8_lossy(&output.stdout).into(),
239 stderr: String::from_utf8_lossy(&output.stderr).into(),
240 });
241 }
242 Err(err) => {
243 return Err(BuildLuaError::Io(io::Error::other(format!(
244 "Failed to run `{} install`:\n{}",
245 config.make_cmd(),
246 err
247 ))));
248 }
249 };
250 move_luajit_includes(install_dir).await?;
251 Ok(())
252}
253
254async fn move_luajit_includes(install_dir: &Path) -> io::Result<()> {
257 let include_dir = install_dir.join("include");
258 let include_subdir = include_dir.join(format!("luajit-{LUAJIT_MM_VERSION}"));
259 if !include_subdir.is_dir() {
260 return Ok(());
261 }
262 let mut dir = fs::read_dir(&include_subdir).await.map_err(|err| {
263 io::Error::other(format!(
264 "Failed to read {}:\n{}",
265 include_subdir.display(),
266 err
267 ))
268 })?;
269 while let Some(entry) = dir.next_entry().await? {
270 let file_name = entry.file_name();
271 let src_path = entry.path();
272 let dest_path = include_dir.join(&file_name);
273 fs::copy(&src_path, &dest_path).await.map_err(|err| {
274 io::Error::other(format!(
275 "error copying {} to {}:\n{}",
276 src_path.display(),
277 dest_path.display(),
278 err
279 ))
280 })?;
281 }
282 fs::remove_dir_all(&include_subdir).await.map_err(|err| {
283 io::Error::other(format!(
284 "Failed to remove {}:\n{}",
285 include_subdir.display(),
286 err
287 ))
288 })?;
289 Ok(())
290}
291
292#[tracing::instrument(name = "Compiling LuaJIT", skip_all)]
293async fn do_build_luajit_msvc(args: BuildLua<'_>, build_dir: &Path) -> Result<(), BuildLuaError> {
294 let lua_version = args.lua_version;
295 let install_dir = args.install_dir;
296 let lib_dir = install_dir.join("lib");
297 fs::create_dir_all(&lib_dir).await.map_err(|err| {
298 io::Error::other(format!(
299 "Failed to create directory {}:\n{}",
300 lib_dir.display(),
301 err
302 ))
303 })?;
304 let include_dir = install_dir.join("include");
305 fs::create_dir_all(&include_dir).await.map_err(|err| {
306 io::Error::other(format!(
307 "Failed to create directory {}:\n{}",
308 include_dir.display(),
309 err
310 ))
311 })?;
312 let bin_dir = install_dir.join("bin");
313 fs::create_dir_all(&bin_dir).await.map_err(|err| {
314 io::Error::other(format!(
315 "Failed to create directory {}:\n{}",
316 bin_dir.display(),
317 err
318 ))
319 })?;
320
321 let src_dir = build_dir.join("src");
322 let mut msvcbuild = Command::new(src_dir.join("msvcbuild.bat"));
323 msvcbuild.current_dir(&src_dir);
324 if lua_version == &LuaVersion::LuaJIT52 {
325 msvcbuild.arg("lua52compat");
326 }
327 msvcbuild.arg("static");
328 let host = Triple::host();
329 let target = host.to_string();
330 let cl = cc::windows_registry::find_tool(&target, "cl.exe").ok_or(BuildLuaError::ClNotFound)?;
331 for (k, v) in cl.env() {
332 msvcbuild.env(k, v);
333 }
334 fs::create_dir_all(&install_dir).await.map_err(|err| {
335 io::Error::other(format!(
336 "Failed to create directory {}:\n{}",
337 install_dir.display(),
338 err
339 ))
340 })?;
341 match msvcbuild.output().await {
342 Ok(output) if output.status.success() => utils::trace_command_output(&output),
343 Ok(output) => {
344 return Err(BuildLuaError::CommandFailure {
345 name: "build".into(),
346 status: output.status,
347 stdout: String::from_utf8_lossy(&output.stdout).into(),
348 stderr: String::from_utf8_lossy(&output.stderr).into(),
349 });
350 }
351 Err(err) => {
352 return Err(BuildLuaError::Io(io::Error::other(format!(
353 "Failed to run msvcbuild.bat:\n{}",
354 err
355 ))))
356 }
357 };
358
359 copy_includes(&src_dir, &include_dir).await?;
360 fs::copy(src_dir.join("lua51.lib"), lib_dir.join("luajit.lib"))
361 .await
362 .map_err(|err| {
363 io::Error::other(format!(
364 "Failed to rename lua51.lib to luajit.lib:\n{}",
365 err
366 ))
367 })?;
368 fs::copy(src_dir.join("luajit.exe"), bin_dir.join("luajit.exe"))
369 .await
370 .map_err(|err| {
371 io::Error::other(format!(
372 "Failed to install luajit.exe to {}:\n{}",
373 bin_dir.display(),
374 err
375 ))
376 })?;
377 Ok(())
378}
379
380async fn do_build_lua(args: BuildLua<'_>) -> Result<(), BuildLuaError> {
381 let lua_version = args.lua_version;
382 let span = span!(
383 tracing::Level::INFO,
384 "Building Lua",
385 version = lua_version.to_string(),
386 );
387 let _enter = span.enter();
388
389 let build_dir = tempdir().map_err(|err| {
390 io::Error::other(format!(
391 "failed to create lua_installation temp directory:\n{}",
392 err
393 ))
394 })?;
395
396 let (source_integrity, pkg_version): (Integrity, &str) = unsafe {
397 match lua_version {
398 LuaVersion::Lua51 => (LUA51_HASH.parse().unwrap_unchecked(), LUA51_VERSION),
399 LuaVersion::Lua52 => (LUA52_HASH.parse().unwrap_unchecked(), LUA52_VERSION),
400 LuaVersion::Lua53 => (LUA53_HASH.parse().unwrap_unchecked(), LUA53_VERSION),
401 LuaVersion::Lua54 => (LUA54_HASH.parse().unwrap_unchecked(), LUA54_VERSION),
402 LuaVersion::Lua55 => (LUA55_HASH.parse().unwrap_unchecked(), LUA55_VERSION),
403 LuaVersion::LuaJIT | LuaVersion::LuaJIT52 => unreachable!(),
404 }
405 };
406
407 let file_name = format!("lua-{pkg_version}.tar.gz");
408
409 let source_url: Url = unsafe {
410 format!("https://www.lua.org/ftp/{file_name}")
411 .parse()
412 .unwrap_unchecked()
413 };
414
415 let response = {
416 let span = span!(
417 tracing::Level::INFO,
418 "Downloading Lua",
419 url = source_url.to_string(),
420 );
421 let _enter = span.enter();
422
423 crate::reqwest::new_https_client(args.config)?
424 .get(source_url.clone())
425 .send()
426 .await?
427 .error_for_status()?
428 .bytes()
429 .await?
430 };
431
432 let hash = response.hash()?;
433
434 if hash.matches(&source_integrity).is_none() {
435 return Err(BuildLuaError::SourceIntegrityMismatch {
436 src: source_url.to_string(),
437 expected: source_integrity,
438 actual: hash,
439 });
440 }
441
442 let cursor = Cursor::new(response);
443 let mime_type = infer::get(cursor.get_ref()).map(|file_type| file_type.mime_type());
444 operations::unpack::unpack(mime_type, cursor, true, file_name, build_dir.path()).await?;
445
446 if cfg!(target_env = "msvc") {
447 do_build_lua_msvc(args, build_dir.path(), lua_version, pkg_version).await
448 } else {
449 do_build_lua_unix(args, build_dir.path(), lua_version, pkg_version).await
450 }
451}
452
453#[tracing::instrument(name = "Compiling Lua", skip_all)]
454async fn do_build_lua_unix(
455 args: BuildLua<'_>,
456 build_dir: &Path,
457 lua_version: &LuaVersion,
458 _pkg_version: &str,
459) -> Result<(), BuildLuaError> {
460 tracing::debug!(message = "Compiling Lua (Unix)...");
461 let config = args.config;
462 let install_dir = args.install_dir;
463
464 let build_target = if cfg!(target_os = "linux") {
465 if matches!(&lua_version, LuaVersion::Lua54) {
467 "linux-readline"
468 } else {
469 "linux"
470 }
471 } else if cfg!(target_os = "macos") {
472 "macosx"
473 } else if cfg!(target_os = "freebsd") {
474 "freebsd"
475 } else {
476 "generic"
477 };
478 match Command::new(config.make_cmd())
479 .current_dir(build_dir)
480 .stdout(Stdio::piped())
481 .stderr(Stdio::piped())
482 .arg(build_target)
483 .output()
484 .await
485 {
486 Ok(output) if !output.status.success() && build_target != "generic" => {
490 let fallback_output = Command::new(config.make_cmd())
491 .current_dir(build_dir)
492 .stdout(Stdio::piped())
493 .stderr(Stdio::piped())
494 .arg("generic")
495 .output()
496 .await;
497 guard_success(fallback_output, config, "build (generic)")?;
498 }
499 output => guard_success(output, config, &format!("build ({build_target})"))?,
500 };
501
502 match Command::new(config.make_cmd())
503 .current_dir(build_dir)
504 .stdout(Stdio::piped())
505 .stderr(Stdio::piped())
506 .arg("install")
507 .arg(format!(r#"INSTALL_TOP="{}""#, install_dir.display()))
508 .output()
509 .await
510 {
511 Ok(output) if output.status.success() => utils::trace_command_output(&output),
512 Ok(output) => {
513 return Err(BuildLuaError::CommandFailure {
514 name: "install".into(),
515 status: output.status,
516 stdout: String::from_utf8_lossy(&output.stdout).into(),
517 stderr: String::from_utf8_lossy(&output.stderr).into(),
518 });
519 }
520 Err(err) => {
521 return Err(BuildLuaError::Io(io::Error::other(format!(
522 "Failed to run `{} install`:\n{}",
523 config.make_cmd(),
524 err
525 ))))
526 }
527 };
528
529 Ok(())
530}
531
532#[tracing::instrument(name = "Compiling Lua", skip_all)]
533async fn do_build_lua_msvc(
534 args: BuildLua<'_>,
535 build_dir: &Path,
536 lua_version: &LuaVersion,
537 _pkg_version: &str,
538) -> Result<(), BuildLuaError> {
539 tracing::debug!(message = "Compiling Lua (MSVC)...");
540 let config = args.config;
541 let install_dir = args.install_dir;
542
543 let lib_dir = install_dir.join("lib");
544 fs::create_dir_all(&lib_dir).await.map_err(|err| {
545 io::Error::other(format!(
546 "Failed to create directory {}:\n{}",
547 lib_dir.display(),
548 err
549 ))
550 })?;
551 let include_dir = install_dir.join("include");
552 fs::create_dir_all(&include_dir).await.map_err(|err| {
553 io::Error::other(format!(
554 "Failed to create directory {}:\n{}",
555 include_dir.display(),
556 err
557 ))
558 })?;
559 let bin_dir = install_dir.join("bin");
560 fs::create_dir_all(&bin_dir).await.map_err(|err| {
561 io::Error::other(format!(
562 "Failed to create directory {}:\n{}",
563 bin_dir.display(),
564 err
565 ))
566 })?;
567
568 let src_dir = build_dir.join("src");
569
570 let lua_bin_name = "lua";
571 let lua_c_bin_name = "luac";
572
573 let dll_name = match lua_version {
574 LuaVersion::Lua51 => "lua51",
575 LuaVersion::Lua52 => "lua52",
576 LuaVersion::Lua53 => "lua53",
577 LuaVersion::Lua54 => "lua54",
578 LuaVersion::Lua55 => "lua55",
579 LuaVersion::LuaJIT | LuaVersion::LuaJIT52 => unreachable!(),
580 };
581
582 let lib_name = match lua_version {
583 LuaVersion::Lua51 => "lua5.1",
584 LuaVersion::Lua52 => "lua5.2",
585 LuaVersion::Lua53 => "lua5.3",
586 LuaVersion::Lua54 => "lua5.4",
587 LuaVersion::Lua55 => "lua5.5",
588 LuaVersion::LuaJIT | LuaVersion::LuaJIT52 => unreachable!(),
589 };
590
591 let host = Triple::host();
592 let mut cc = cc::Build::new();
593 cc.cargo_output(false)
594 .cargo_metadata(false)
595 .cargo_warnings(false)
596 .warnings(config.verbose())
597 .opt_level(3)
598 .host(&host.to_string())
599 .target(&host.to_string());
600
601 cc.define("LUA_USE_WINDOWS", None);
602 cc.define("LUA_BUILD_AS_DLL", None);
603
604 let mut lib_c_files = Vec::new();
605 let mut read_dir = fs::read_dir(&src_dir).await.map_err(|err| {
606 io::Error::other(format!(
607 "Failed to read directory {}:\n{}",
608 src_dir.display(),
609 err
610 ))
611 })?;
612 while let Some(entry) = read_dir.next_entry().await? {
613 let path = entry.path();
614 if path.extension().is_some_and(|ext| ext == "c")
615 && path
616 .with_extension("")
617 .file_name()
618 .is_some_and(|name| name != "lua" && name != "luac")
619 {
620 lib_c_files.push(path);
621 }
622 }
623
624 let lib_objects = cc
625 .include(&src_dir)
626 .files(lib_c_files)
627 .out_dir(&lib_dir)
628 .try_compile_objects(config)?;
629
630 let bin_objects = cc
631 .include(&src_dir)
632 .file(src_dir.join(format!("{lua_bin_name}.c")))
633 .file(src_dir.join(format!("{lua_c_bin_name}.c")))
634 .out_dir(&src_dir)
635 .try_compile_objects(config)?;
636
637 let lua_bin_objects = bin_objects.iter().filter(|file| {
638 file.file_stem().is_some_and(|fname| {
639 fname
640 .to_string_lossy()
641 .ends_with(&format!("-{lua_bin_name}"))
642 })
643 });
644
645 let lua_c_bin_objects = bin_objects.iter().filter(|file| {
646 file.file_stem().is_some_and(|fname| {
647 fname
648 .to_string_lossy()
649 .ends_with(&format!("-{lua_c_bin_name}"))
650 })
651 });
652
653 let target = host.to_string();
654 let link =
655 cc::windows_registry::find_tool(&target, "link.exe").ok_or(BuildLuaError::LinkNotFound)?;
656
657 let dll_path = bin_dir.join(format!("{dll_name}.dll"));
658 let lua_bin_path = bin_dir.join(format!("{lua_bin_name}.exe"));
659 let lua_c_bin_path = bin_dir.join(format!("{lua_c_bin_name}.exe"));
660
661 let implib_path = lib_dir.join(format!("{lib_name}.lib"));
662
663 guard_success(
665 Command::new(link.path())
666 .arg("/DLL")
667 .arg(format!("/OUT:{}", dll_path.display()))
668 .arg(format!("/IMPLIB:{}", implib_path.display()))
669 .args(&lib_objects)
670 .output()
671 .await,
672 config,
673 &format!("link {dll_name}.dll"),
674 )?;
675
676 guard_success(
678 Command::new(link.path())
679 .arg(format!("/OUT:{}", lua_bin_path.display()))
680 .arg(&implib_path)
681 .args(lua_bin_objects)
682 .output()
683 .await,
684 config,
685 &format!("link {}", lua_bin_path.display()),
686 )?;
687
688 guard_success(
690 Command::new(link.path())
691 .arg(format!("/OUT:{}", lua_c_bin_path.display()))
692 .args(&lib_objects)
693 .args(lua_c_bin_objects)
694 .output()
695 .await,
696 config,
697 &format!("link {}", lua_c_bin_path.display()),
698 )?;
699
700 copy_includes(&src_dir, &include_dir).await?;
701
702 Ok(())
703}
704
705fn guard_success(
706 output: io::Result<std::process::Output>,
707 config: &Config,
708 cmd_name: &str,
709) -> Result<(), BuildLuaError> {
710 match output {
711 Ok(output) if output.status.success() => {
712 utils::trace_command_output(&output);
713 Ok(())
714 }
715 Ok(output) => Err(BuildLuaError::CommandFailure {
716 name: cmd_name.to_string(),
717 status: output.status,
718 stdout: String::from_utf8_lossy(&output.stdout).into(),
719 stderr: String::from_utf8_lossy(&output.stderr).into(),
720 }),
721 Err(err) => Err(BuildLuaError::Io(io::Error::other(format!(
722 "Failed to run `{} build`:\n{}",
723 config.make_cmd(),
724 err
725 )))),
726 }
727}
728
729async fn copy_includes(src_dir: &Path, include_dir: &Path) -> Result<(), io::Error> {
730 for f in &[
731 "lauxlib.h",
732 "lua.h",
733 "luaconf.h",
734 "luajit.h",
735 "lualib.h",
736 "lua.hpp",
737 ] {
738 let src_file = src_dir.join(f);
739 if src_file.is_file() {
740 fs::copy(&src_file, include_dir.join(f))
741 .await
742 .map_err(|err| {
743 io::Error::other(format!(
744 "Failed to copy {} to {}:\n{}",
745 src_file.display(),
746 include_dir.display(),
747 err
748 ))
749 })?;
750 }
751 }
752 Ok(())
753}