lux_lib/build/
rust_mlua.rs

1use super::external_dependency::ExternalDependencyInfo;
2use super::utils::c_dylib_extension;
3use crate::config::LuaVersionUnset;
4use crate::lua_rockspec::BuildInfo;
5use crate::progress::{Progress, ProgressBar};
6use crate::tree::Tree;
7use crate::{
8    config::{Config, LuaVersion},
9    lua_installation::LuaInstallation,
10    lua_rockspec::{Build, RustMluaBuildSpec},
11    tree::RockLayout,
12};
13use itertools::Itertools;
14use std::collections::HashMap;
15use std::path::{Path, PathBuf};
16use std::process::ExitStatus;
17use std::{fs, io};
18use thiserror::Error;
19use tokio::process::Command;
20
21#[derive(Error, Debug)]
22pub enum RustError {
23    #[error("`cargo build` failed.\nstatus: {status}\nstdout: {stdout}\nstderr: {stderr}")]
24    CargoBuild {
25        status: ExitStatus,
26        stdout: String,
27        stderr: String,
28    },
29    #[error("failed to run `cargo build`: {0}")]
30    RustBuild(#[from] io::Error),
31    #[error(transparent)]
32    LuaVersionUnset(#[from] LuaVersionUnset),
33}
34
35impl Build for RustMluaBuildSpec {
36    type Err = RustError;
37
38    async fn run(
39        self,
40        output_paths: &RockLayout,
41        _no_install: bool,
42        _lua: &LuaInstallation,
43        _external_dependencies: &HashMap<String, ExternalDependencyInfo>,
44        config: &Config,
45        _tree: &Tree,
46        build_dir: &Path,
47        progress: &Progress<ProgressBar>,
48    ) -> Result<BuildInfo, Self::Err> {
49        let lua_version = LuaVersion::from(config)?;
50        let lua_feature = match lua_version {
51            LuaVersion::Lua51 => "lua51",
52            LuaVersion::Lua52 => "lua52",
53            LuaVersion::Lua53 => "lua53",
54            LuaVersion::Lua54 => "lua54",
55            LuaVersion::LuaJIT => "luajit",
56            LuaVersion::LuaJIT52 => "luajit",
57        };
58        let features = self
59            .features
60            .into_iter()
61            .chain(std::iter::once(lua_feature.into()))
62            .join(",");
63        let target_dir_arg = format!("--target-dir={}", self.target_path.display());
64        let mut build_args = vec!["build", "--release", &target_dir_arg];
65        if !self.default_features {
66            build_args.push("--no-default-features");
67        }
68        build_args.push("--features");
69        build_args.push(&features);
70        match Command::new("cargo")
71            .current_dir(build_dir)
72            .args(build_args)
73            .output()
74            .await
75        {
76            Ok(output) if output.status.success() => {}
77            Ok(output) => {
78                return Err(RustError::CargoBuild {
79                    status: output.status,
80                    stdout: String::from_utf8_lossy(&output.stdout).into(),
81                    stderr: String::from_utf8_lossy(&output.stderr).into(),
82                });
83            }
84            Err(err) => return Err(RustError::RustBuild(err)),
85        }
86        fs::create_dir_all(&output_paths.lib)?;
87        if let Err(err) =
88            install_rust_libs(self.modules, &self.target_path, build_dir, output_paths)
89        {
90            cleanup(output_paths, progress).await?;
91            return Err(err.into());
92        }
93        fs::create_dir_all(&output_paths.src)?;
94        if let Err(err) = install_lua_libs(self.include, build_dir, output_paths) {
95            cleanup(output_paths, progress).await?;
96            return Err(err.into());
97        }
98        Ok(BuildInfo::default())
99    }
100}
101
102fn install_rust_libs(
103    modules: HashMap<String, PathBuf>,
104    target_path: &Path,
105    build_dir: &Path,
106    output_paths: &RockLayout,
107) -> io::Result<()> {
108    for (module, rust_lib) in modules {
109        let src = build_dir.join(target_path).join("release").join(rust_lib);
110        let mut dst: PathBuf = output_paths.lib.join(module);
111        dst.set_extension(c_dylib_extension());
112        fs::copy(src, dst)?;
113    }
114    Ok(())
115}
116
117fn install_lua_libs(
118    include: HashMap<PathBuf, PathBuf>,
119    build_dir: &Path,
120    output_paths: &RockLayout,
121) -> io::Result<()> {
122    for (from, to) in include {
123        let src = build_dir.join(from);
124        let dst = output_paths.src.join(to);
125        fs::copy(src, dst)?;
126    }
127    Ok(())
128}
129
130async fn cleanup(output_paths: &RockLayout, progress: &Progress<ProgressBar>) -> io::Result<()> {
131    let root_dir = &output_paths.rock_path;
132
133    progress.map(|p| p.set_message(format!("🗑️ Cleaning up {}", root_dir.display())));
134
135    match std::fs::remove_dir_all(root_dir) {
136        Ok(_) => (),
137        Err(err) => {
138            progress
139                .map(|p| p.println(format!("Error cleaning up {}: {}", root_dir.display(), err)));
140        }
141    };
142
143    Ok(())
144}