lux_lib/operations/
build_workspace.rs1use crate::{
2 build::BuildBehaviour,
3 config::Config,
4 drivers::sync::{Sync, SyncError, SyncMode, TargetSet},
5 lockfile::LockedPackage,
6 package::PackageName,
7 workspace::Workspace,
8};
9use bon::Builder;
10use thiserror::Error;
11use tracing::{info_span, Instrument};
12
13#[derive(Debug, Error, miette::Diagnostic)]
14#[non_exhaustive]
15pub enum BuildWorkspaceError {
16 #[error(transparent)]
17 #[diagnostic(transparent)]
18 Sync(Box<SyncError>),
19}
20
21impl From<SyncError> for BuildWorkspaceError {
22 fn from(source: SyncError) -> Self {
23 Self::Sync(Box::new(source))
24 }
25}
26
27#[derive(Builder)]
28#[builder(start_fn = new, finish_fn(name = _build, vis = ""))]
29pub struct BuildWorkspace<'a> {
30 #[builder(start_fn)]
31 workspace: &'a Workspace,
32
33 #[builder(start_fn)]
34 config: &'a Config,
35
36 package: Option<PackageName>,
38
39 no_lock: bool,
41
42 only_deps: bool,
44
45 #[builder(default = BuildBehaviour::Force)]
46 behaviour: BuildBehaviour,
47}
48
49impl<State: build_workspace_builder::State + build_workspace_builder::IsComplete>
50 BuildWorkspaceBuilder<'_, State>
51{
52 pub async fn build(self) -> Result<Vec<LockedPackage>, BuildWorkspaceError> {
53 let build = self._build();
54 let span = match &build.package {
55 Some(package) => info_span!("Building workspace", package = package.to_string()),
56 None => info_span!("Building workspace"),
57 };
58 async move {
59 let report = Sync::new(build.workspace, build.config)
60 .mode(SyncMode::Open)
61 .targets(match build.package {
62 Some(package) => TargetSet::member(package, false),
63 None => TargetSet::all(build.workspace, false),
64 })
65 .behaviour(build.behaviour)
66 .only_deps(build.only_deps)
67 .no_lock(build.no_lock)
68 .sync()
69 .await?;
70 Ok(report.added().to_vec())
71 }
72 .instrument(span)
73 .await
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 use crate::{
82 config::ConfigBuilder, lua_installation::detect_installed_lua_version,
83 lua_version::LuaVersion,
84 };
85 #[cfg(feature = "impure_tests")]
86 use crate::{fs, tree::InstallTree};
87 use assert_fs::prelude::PathCopy;
88 use std::path::PathBuf;
89
90 #[cfg(feature = "impure_tests")]
91 #[tokio::test]
92 async fn builtin_build_autodetect_bin_scripts() {
94 let project_root =
95 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/sample-projects/init/");
96 let data_dir: PathBuf = assert_fs::TempDir::new().unwrap().path().into();
97 let temp_dir = assert_fs::TempDir::new().unwrap();
98 temp_dir.copy_from(&project_root, &["**"]).unwrap();
99 let project_root = temp_dir.path();
100 let foo_bin_dir = project_root.join("src").join("bin");
101 fs::tokio::create_dir_all(&foo_bin_dir).await.unwrap();
102 let foo_bin_file = foo_bin_dir.join("foo");
103 fs::tokio::write(&foo_bin_file, "print('hello')")
104 .await
105 .unwrap();
106 let bar_bin_dir = project_root.join("bin");
107 fs::tokio::create_dir_all(&bar_bin_dir).await.unwrap();
108 let bar_bin_file = bar_bin_dir.join("bar");
109 fs::tokio::write(&bar_bin_file, "print('hello')")
110 .await
111 .unwrap();
112 let lua_version = detect_installed_lua_version().or(Some(LuaVersion::Lua51));
113 let config = ConfigBuilder::new()
114 .unwrap()
115 .data_dir(Some(data_dir))
116 .lua_version(lua_version)
117 .build()
118 .unwrap();
119 let workspace = Workspace::from_exact(project_root).unwrap().unwrap();
120 let tree = workspace.tree(&config).unwrap();
121 BuildWorkspace::new(&workspace, &config)
122 .no_lock(false)
123 .only_deps(false)
124 .build()
125 .await
126 .unwrap();
127 let bin_dir = tree.bin();
128 assert!(bin_dir.join("foo").is_file());
129 assert!(bin_dir.join("bar").is_file());
130 }
131
132 #[cfg(feature = "impure_tests")]
133 #[tokio::test]
134 async fn builtin_build_support_src_init_lua() {
136 let data_dir: PathBuf = assert_fs::TempDir::new().unwrap().path().into();
137 let project_root =
138 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/sample-projects/init/");
139 let temp_dir = assert_fs::TempDir::new().unwrap();
140 temp_dir.copy_from(&project_root, &["**"]).unwrap();
141 let project_root = temp_dir.path();
142 let src_dir = project_root.join("src");
143 fs::tokio::create_dir_all(&src_dir).await.unwrap();
144 let init_lua_file = src_dir.join("init.lua");
145 fs::tokio::write(&init_lua_file, "print('hello')")
146 .await
147 .unwrap();
148 let lua_version = detect_installed_lua_version().or(Some(LuaVersion::Lua51));
149 let config = ConfigBuilder::new()
150 .unwrap()
151 .data_dir(Some(data_dir))
152 .lua_version(lua_version)
153 .build()
154 .unwrap();
155 let workspace = Workspace::from_exact(project_root).unwrap().unwrap();
156 let package = BuildWorkspace::new(&workspace, &config)
157 .no_lock(false)
158 .only_deps(false)
159 .build()
160 .await
161 .unwrap();
162 let package = package.first().unwrap();
163 let tree = workspace.tree(&config).unwrap();
164 let src_dir = tree.layout_for(&package.spec).src;
165 assert!(src_dir.join("init.lua").is_file());
166 }
167
168 #[tokio::test]
169 async fn build_install_post_build_artifact() {
171 let data_dir: PathBuf = assert_fs::TempDir::new().unwrap().path().into();
172 let project_root =
173 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/sample-projects/c-src/");
174 let temp_dir = assert_fs::TempDir::new().unwrap();
175 temp_dir.copy_from(&project_root, &["**"]).unwrap();
176 let project_root = temp_dir.path();
177 let lua_version = detect_installed_lua_version().or(Some(LuaVersion::Lua51));
178 let config = ConfigBuilder::new()
179 .unwrap()
180 .data_dir(Some(data_dir))
181 .lua_version(lua_version)
182 .build()
183 .unwrap();
184 let workspace = Workspace::from_exact(project_root).unwrap().unwrap();
185 BuildWorkspace::new(&workspace, &config)
187 .no_lock(false)
188 .only_deps(false)
189 .build()
190 .await
191 .unwrap();
192 }
193}