lux_lib/build/compile/
c_files.rs1use std::{
2 collections::HashMap,
3 path::{Path, PathBuf},
4};
5
6use miette::Diagnostic;
7use target_lexicon::Triple;
8use thiserror::Error;
9
10use crate::{
11 build::external_dependency::ExternalDependencyInfo, config::Config, fs,
12 lua_installation::LuaInstallation, lua_rockspec::LuaModule,
13};
14
15use super::{c_link, CCExt};
16
17#[derive(Error, Debug, Diagnostic)]
18#[non_exhaustive]
19pub enum Error {
20 #[error(transparent)]
21 #[diagnostic(transparent)]
22 Fs(#[from] fs::Error),
23 #[error("failed to compile intermediates from C files")]
24 #[diagnostic(help(
25 r#"check the compiler output above for missing headers or syntax errors.
26ensure the C compiler has access to the required include paths."#
27 ))]
28 CompileIntermediates { source: cc::Error },
29 #[error("error compiling C files (compilation failed):")]
30 #[diagnostic(help(
31 r#"check the compiler output above for details.
32if no C compiler was found, run `lx debug toolchains` to verify your build tools."#
33 ))]
34 Compilation(#[from] cc::Error),
35 #[error("error compiling C files (linking failed)")]
36 #[diagnostic(forward(0))]
37 Link(#[from] c_link::Error),
38}
39
40#[tracing::instrument(
44 name = "Compiling C files",
45 level = "info",
46 skip_all,
47 fields(profile = config.build_profile().to_string()),
48)]
49pub(crate) async fn compile(
50 files: &Vec<PathBuf>,
51 target_module: &LuaModule,
52 target_dir: &Path,
53 lua: &LuaInstallation,
54 external_dependencies: &HashMap<String, ExternalDependencyInfo>,
55 config: &Config,
56) -> Result<(), Error> {
57 let target = target_dir.join(target_module.to_lib_path());
58 let target_parent_dir = target.parent().unwrap_or_else(|| {
59 unreachable!(
60 "couldn't determine build target parent directory:\n{}",
61 target.display()
62 )
63 });
64 let target_file_name = target
65 .file_name()
66 .unwrap_or_default()
67 .to_string_lossy()
68 .to_string();
69
70 fs::sync::create_dir_all(target_parent_dir)?;
71
72 let host = Triple::host();
73
74 let mut build = cc::Build::new();
77 let intermediate_dir = fs::tempfile::tempdir()?;
78 let build = build
79 .cargo_output(config.verbose())
80 .cargo_debug(config.verbose())
81 .cargo_metadata(config.verbose())
82 .cargo_warnings(config.verbose())
83 .warnings(config.verbose())
84 .files(files)
85 .host(&host.to_string())
86 .target(&host.to_string())
87 .includes(lua.includes())
88 .includes(
89 external_dependencies
90 .values()
91 .filter_map(|dep| dep.include_dir.as_ref()),
92 )
93 .opt_level(config.build_profile().opt_level())
94 .out_dir(intermediate_dir);
95
96 let compiler = build.try_get_compiler()?;
97 if compiler.is_like_msvc() {
99 build.flag("-W0");
100 } else {
101 build.flag("-w");
102 }
103 for arg in lua.define_flags() {
104 build.flag(&arg);
105 }
106
107 let objects = build
108 .try_compile_objects(config)
109 .map_err(|source| Error::CompileIntermediates { source })?;
110
111 c_link::artifacts(
112 build,
113 lua,
114 external_dependencies,
115 &target_file_name,
116 Vec::new(),
117 Vec::new(),
118 target_module,
119 target_parent_dir,
120 objects,
121 config,
122 )
123 .await?;
124 Ok(())
125}