zoi_cli/cmd/package/
build.rs1use std::path::PathBuf;
7
8use anyhow::Result;
9use clap::{Parser, ValueEnum};
10use colored::Colorize;
11
12#[derive(ValueEnum, Clone, Copy, Debug)]
14pub enum SignModeArg {
15 Embed,
17 File
19}
20
21impl From<SignModeArg> for zoi_core::types::SignMode {
22 fn from(mode: SignModeArg) -> Self {
23 match mode {
24 SignModeArg::Embed => Self::Embed,
25 SignModeArg::File => Self::File
26 }
27 }
28}
29
30#[derive(Parser, Debug)]
32pub struct BuildCommand {
33 #[arg(required = true)]
35 pub package_file: PathBuf,
36
37 #[arg(long)]
39 pub r#type: Option<String>,
40
41 #[arg(long, short, num_args = 1.., default_values_t = vec!["current".to_string()])]
44 pub platform: Vec<String>,
45
46 #[arg(long, num_args = 1..)]
48 pub sub: Option<Vec<String>>,
49
50 #[arg(long)]
52 pub sign: Option<String>,
53
54 #[arg(long, value_enum, default_value_t = SignModeArg::Embed)]
57 pub sign_mode: SignModeArg,
58
59 #[arg(long)]
61 pub test: bool,
62
63 #[arg(long, short = 'o')]
65 pub output_dir: Option<PathBuf>,
66
67 #[arg(long)]
69 pub install_deps: bool,
70
71 #[arg(long)]
73 pub version_override: Option<String>,
74
75 #[arg(long, default_value = "native")]
77 pub method: String,
78
79 #[arg(long)]
81 pub image: Option<String>,
82
83 #[arg(long)]
85 pub fakeroot: bool,
86
87 #[arg(long)]
91 pub pure: bool,
92
93 #[arg(long, default_value = "@core/base:dev")]
96 pub root_package: String,
97
98 #[arg(long)]
101 pub no_zsa: bool
102}
103
104pub fn run(mut args: BuildCommand) -> Result<()> {
111 let mut _temp_root = None;
112
113 if args.pure {
114 if args.method == "docker" {
115 return Err(anyhow::anyhow!(
116 "--pure is not compatible with --method docker"
117 ));
118 }
119 args.method = "bwrap".to_string();
120
121 let temp = tempfile::Builder::new().prefix("zoi-pure-").tempdir()?;
122 println!(
123 "{} Initializing pure build environment in {}...",
124 "::".bold().blue(),
125 temp.path().display()
126 );
127
128 let etc_dir = temp.path().join("etc");
132 std::fs::create_dir_all(&etc_dir)?;
133 std::fs::write(
134 etc_dir.join("os-release"),
135 "ID=zoios\nID_LIKE=zoios\n"
136 )?;
137
138 crate::pkg::sysroot::set_sysroot(temp.path().to_path_buf());
139 _temp_root = Some(temp);
140
141 let root_dep = crate::pkg::dependencies::parse_dependency_string(
143 &args.root_package
144 )?;
145 crate::pkg::install::dep_install::install_dependency(
146 &root_dep,
147 "pure-root",
148 crate::pkg::types::Scope::System,
149 true,
150 true,
151 &std::sync::Mutex::new(std::collections::HashSet::new()),
152 &mut Vec::new(),
153 None
154 )?;
155 }
156
157 if args.install_deps {
158 install_dependencies_for_build(&args, args.test)?;
159 }
160
161 if args.test {
162 println!("Running tests before building...");
163 crate::pkg::package::test::run(&args)?;
164 println!("Tests passed, proceeding with build...");
165 }
166
167 let mut _temp_zsa_bundle = None;
172 if !args.no_zsa && args.package_file.to_string_lossy().ends_with(".pkg.lua")
173 {
174 let temp = tempfile::Builder::new()
175 .prefix("zoi-build-zsa-")
176 .tempdir()?;
177 println!("{} Bundling source archive...", "::".bold().blue());
178 let zsa_path = crate::pkg::package::bundle::run(
179 &args.package_file,
180 Some(temp.path()),
181 None,
182 args.sign_mode.into(),
183 args.version_override.as_deref(),
184 args.r#type.as_deref()
185 )?;
186
187 if args.output_dir.is_none() {
191 args.output_dir = args.package_file.parent().map(PathBuf::from);
192 }
193
194 println!("{} Building from source bundle...", "::".bold().blue());
195 args.package_file = zsa_path;
196 _temp_zsa_bundle = Some(temp);
197 }
198
199 crate::pkg::package::build::run(
200 &args.package_file,
201 args.r#type.as_deref(),
202 &args.platform,
203 args.sign,
204 args.sign_mode.into(),
205 args.output_dir.as_deref(),
206 args.version_override.as_deref(),
207 args.sub,
208 false,
209 &args.method,
210 args.image.as_deref(),
211 args.fakeroot,
212 args.install_deps,
213 args.test
214 )
215}
216
217pub fn install_dependencies_for_build(
224 args: &BuildCommand,
225 include_test: bool
226) -> Result<()> {
227 for platform in &args.platform {
228 let current_platform = if platform == "current" {
229 crate::pkg::utils::get_platform()?
230 } else {
231 platform.clone()
232 };
233
234 if let Some(mut dep_strings) =
235 crate::pkg::package::build::get_build_dependencies(
236 &args.package_file,
237 args.r#type.as_deref(),
238 ¤t_platform,
239 args.version_override.as_deref(),
240 false
241 )?
242 {
243 if include_test
244 && let Some(test_deps) =
245 crate::pkg::package::build::get_test_dependencies(
246 &args.package_file,
247 ¤t_platform,
248 args.version_override.as_deref(),
249 false
250 )?
251 {
252 dep_strings.extend(test_deps);
253 }
254
255 if !dep_strings.is_empty() {
256 println!(
257 "{} Installing build/test dependencies...",
258 "::".bold().blue()
259 );
260 let processed =
261 std::sync::Mutex::new(std::collections::HashSet::new());
262 let mut installed = Vec::new();
263 for dep_str in dep_strings {
264 let dep =
265 crate::pkg::dependencies::parse_dependency_string(
266 &dep_str
267 )?;
268 crate::pkg::install::dep_install::install_dependency(
269 &dep,
270 "build",
271 if crate::pkg::sysroot::get_sysroot().is_some() {
272 crate::pkg::types::Scope::System
273 } else {
274 crate::pkg::types::Scope::User
275 },
276 true,
277 true,
278 &processed,
279 &mut installed,
280 None
281 )?;
282 }
283 }
284 }
285 }
286 Ok(())
287}