1use eyre::Context;
2use itertools::Itertools;
3use std::sync::Arc;
4
5use clap::Args;
6use eyre::Result;
7use lux_lib::{
8 build::{self, BuildBehaviour},
9 config::Config,
10 luarocks::luarocks_installation::LuaRocksInstallation,
11 operations::{Install, PackageInstallSpec, Sync},
12 progress::MultiProgress,
13 project::Project,
14 rockspec::Rockspec,
15 tree,
16};
17
18#[derive(Args, Default)]
19pub struct Build {
20 #[arg(long)]
22 no_lock: bool,
23
24 #[arg(long)]
26 only_deps: bool,
27}
28
29pub async fn build(data: Build, config: Config) -> Result<()> {
30 let project = Project::current_or_err()?;
31 let progress_arc = MultiProgress::new_arc();
32 let progress = Arc::clone(&progress_arc);
33
34 let project_toml = project.toml().into_local()?;
35 let project_tree = project.tree(&config)?;
36
37 let dependencies = project_toml
38 .dependencies()
39 .current_platform()
40 .iter()
41 .cloned()
42 .collect_vec();
43
44 let build_dependencies = project_toml
45 .build_dependencies()
46 .current_platform()
47 .iter()
48 .cloned()
49 .collect_vec();
50
51 let build_tree = project.build_tree(&config)?;
52 let luarocks = LuaRocksInstallation::new(&config, build_tree.clone())?;
53
54 if data.no_lock {
55 let dependencies_to_install = dependencies
56 .into_iter()
57 .filter(|dep| {
58 project_tree
59 .match_rocks(dep.package_req())
60 .is_ok_and(|rock_match| !rock_match.is_found())
61 })
62 .map(|dep| {
63 PackageInstallSpec::new(dep.clone().into_package_req(), tree::EntryType::Entrypoint)
64 .pin(*dep.pin())
65 .opt(*dep.opt())
66 .maybe_source(dep.source().clone())
67 .build()
68 })
69 .collect();
70
71 Install::new(&config)
72 .packages(dependencies_to_install)
73 .project(&project)?
74 .progress(progress.clone())
75 .install()
76 .await?;
77
78 let build_dependencies_to_install = build_dependencies
79 .into_iter()
80 .filter(|dep| {
81 project_tree
82 .match_rocks(dep.package_req())
83 .is_ok_and(|rock_match| !rock_match.is_found())
84 })
85 .map(|dep| {
86 PackageInstallSpec::new(dep.clone().into_package_req(), tree::EntryType::Entrypoint)
87 .pin(*dep.pin())
88 .opt(*dep.opt())
89 .maybe_source(dep.source().clone())
90 .build()
91 })
92 .collect_vec();
93
94 if !build_dependencies_to_install.is_empty() {
95 let bar = progress.map(|p| p.new_bar());
96 luarocks.ensure_installed(&bar).await?;
97 Install::new(&config)
98 .packages(build_dependencies_to_install)
99 .tree(build_tree)
100 .progress(progress.clone())
101 .install()
102 .await?;
103 }
104 } else {
105 Sync::new(&project, &config)
106 .progress(progress.clone())
107 .sync_dependencies()
108 .await
109 .wrap_err(
110 "
111syncing dependencies with the project lockfile failed.
112Use --no-lock to force a new build.
113",
114 )?;
115
116 Sync::new(&project, &config)
117 .progress(progress.clone())
118 .sync_build_dependencies()
119 .await
120 .wrap_err(
121 "
122syncing build dependencies with the project lockfile failed.
123Use --no-lock to force a new build.
124",
125 )?;
126 }
127
128 if !data.only_deps {
129 let package = build::Build::new(
130 &project_toml,
131 &project_tree,
132 tree::EntryType::Entrypoint,
133 &config,
134 &progress.map(|p| p.new_bar()),
135 )
136 .behaviour(BuildBehaviour::Force)
137 .build()
138 .await?;
139
140 let lockfile = project_tree.lockfile()?;
141 let dependencies = lockfile
142 .rocks()
143 .iter()
144 .filter_map(|(pkg_id, value)| {
145 if lockfile.is_entrypoint(pkg_id) {
146 Some(value)
147 } else {
148 None
149 }
150 })
151 .cloned()
152 .collect_vec();
153 let mut lockfile = lockfile.write_guard();
154 lockfile.add_entrypoint(&package);
155 for dep in dependencies {
156 lockfile.add_dependency(&package, &dep);
157 lockfile.remove_entrypoint(&dep);
158 }
159 }
160
161 Ok(())
162}