Maintain/Build/
Process.rs1use std::{
66 env,
67 fs,
68 path::PathBuf,
69 process::{Command as ProcessCommand, Stdio},
70};
71
72use log::info;
73use toml;
74
75use crate::Build::Error::Error as BuildError;
93use crate::Build::{
94 Constant::{CargoFile, IdDelimiter, JsonFile, JsonfiveFile, NameDelimiter},
95 Definition::{Argument, Guard, Manifest},
96 GetTauriTargetTriple::GetTauriTargetTriple,
97 JsonEdit::JsonEdit,
98 Pascalize::Pascalize,
99 TomlEdit::TomlEdit,
100 WordsFromPascal::WordsFromPascal,
101};
102
103pub fn Process(Argument:&Argument) -> Result<(), BuildError> {
178 info!(target: "Build", "Starting build orchestration...");
179
180 log::debug!(target: "Build", "Argument: {:?}", Argument);
181
182 let ProjectDir = PathBuf::from(&Argument.Directory);
183
184 if !ProjectDir.is_dir() {
185 return Err(BuildError::Missing(ProjectDir));
186 }
187
188 let CargoPath = ProjectDir.join(CargoFile);
189
190 let ConfigPath = {
191 let Jsonfive = ProjectDir.join(JsonfiveFile);
192
193 if Jsonfive.exists() { Jsonfive } else { ProjectDir.join(JsonFile) }
194 };
195
196 if !ConfigPath.exists() {
197 return Err(BuildError::Config);
198 }
199
200 let mut CargoGuard = Guard::New(CargoPath.clone(), "Cargo.toml".to_string())?;
202
203 let mut ConfigGuard = Guard::New(ConfigPath.clone(), "Tauri config".to_string())?;
204
205 let mut NamePartsForProductName = Vec::new();
206
207 let mut NamePartsForId = Vec::new();
208
209 if let Some(NodeValue) = &Argument.Environment {
211 if !NodeValue.is_empty() {
212 let PascalEnv = Pascalize(NodeValue);
213
214 if !PascalEnv.is_empty() {
215 NamePartsForProductName.push(format!("{}NodeEnvironment", PascalEnv));
216
217 NamePartsForId.extend(WordsFromPascal(&PascalEnv));
218
219 NamePartsForId.push("node".to_string());
220
221 NamePartsForId.push("environment".to_string());
222 }
223 }
224 }
225
226 if let Some(DependencyValue) = &Argument.Dependency {
228 if !DependencyValue.is_empty() {
229 let (PascalDepBase, IdDepWords) = if DependencyValue.eq_ignore_ascii_case("true") {
230 ("Generic".to_string(), vec!["generic".to_string()])
231 } else if let Some((Org, Repo)) = DependencyValue.split_once('/') {
232 (format!("{}{}", Pascalize(Org), Pascalize(Repo)), {
233 let mut w = WordsFromPascal(&Pascalize(Org));
234
235 w.extend(WordsFromPascal(&Pascalize(Repo)));
236
237 w
238 })
239 } else {
240 (Pascalize(DependencyValue), WordsFromPascal(&Pascalize(DependencyValue)))
241 };
242
243 if !PascalDepBase.is_empty() {
244 NamePartsForProductName.push(format!("{}Dependency", PascalDepBase));
245
246 NamePartsForId.extend(IdDepWords);
247
248 NamePartsForId.push("dependency".to_string());
249 }
250 }
251 }
252
253 if let Some(Version) = &Argument.NodeVersion {
255 if !Version.is_empty() {
256 let PascalVersion = format!("{}NodeVersion", Version);
257
258 NamePartsForProductName.push(PascalVersion.clone());
259
260 NamePartsForId.push("node".to_string());
261
262 NamePartsForId.push(Version.to_string());
263 }
264 }
265
266 if Argument.Bundle.as_ref().map_or(false, |v| v == "true") {
268 NamePartsForProductName.push("Bundle".to_string());
269
270 NamePartsForId.push("bundle".to_string());
271 }
272
273 if Argument.Clean.as_ref().map_or(false, |v| v == "true") {
274 NamePartsForProductName.push("Clean".to_string());
275
276 NamePartsForId.push("clean".to_string());
277 }
278
279 if Argument.Browser.as_ref().map_or(false, |v| v == "true") {
280 NamePartsForProductName.push("Browser".to_string());
281
282 NamePartsForId.push("browser".to_string());
283 }
284
285 if Argument.Compile.as_ref().map_or(false, |v| v == "true") {
286 NamePartsForProductName.push("Compile".to_string());
287
288 NamePartsForId.push("compile".to_string());
289 }
290
291 if Argument.Debug.as_ref().map_or(false, |v| v == "true")
292 || Argument.Command.iter().any(|arg| arg.contains("--debug"))
293 {
294 NamePartsForProductName.push("Debug".to_string());
295
296 NamePartsForId.push("debug".to_string());
297 }
298
299 let ProductNamePrefix = NamePartsForProductName.join(NameDelimiter);
301
302 let FinalName = if !ProductNamePrefix.is_empty() {
303 format!("{}{}{}", ProductNamePrefix, NameDelimiter, Argument.Name)
304 } else {
305 Argument.Name.clone()
306 };
307
308 info!(target: "Build", "Final generated product name: '{}'", FinalName);
309
310 NamePartsForId.extend(WordsFromPascal(&Argument.Name));
312
313 let IdSuffix = NamePartsForId
314 .into_iter()
315 .filter(|s| !s.is_empty())
316 .collect::<Vec<String>>()
317 .join(IdDelimiter);
318
319 let FinalId = format!("{}{}{}", Argument.Prefix, IdDelimiter, IdSuffix);
320
321 info!(target: "Build", "Generated bundle identifier: '{}'", FinalId);
322
323 if FinalName != Argument.Name {
325 TomlEdit(&CargoPath, &Argument.Name, &FinalName)?;
326 }
327
328 let AppVersion = toml::from_str::<Manifest>(&fs::read_to_string(&CargoPath)?)?
330 .get_version()
331 .to_string();
332
333 JsonEdit(
335 &ConfigPath,
336 &FinalName,
337 &FinalId,
338 &AppVersion,
339 (if let Some(version) = &Argument.NodeVersion {
340 info!(target: "Build", "Selected Node.js version: {}", version);
341
342 let Triple = GetTauriTargetTriple();
343
344 let Executable = if cfg!(target_os = "windows") {
346 PathBuf::from(format!("./Element/SideCar/{}/NODE/{}/node.exe", Triple, version))
347 } else {
348 PathBuf::from(format!("./Element/SideCar/{}/NODE/{}/bin/node", Triple, version))
349 };
350
351 let DirectorySideCarTemporary = ProjectDir.join("Binary");
353
354 fs::create_dir_all(&DirectorySideCarTemporary)?;
355
356 let PathExecutableDestination = if cfg!(target_os = "windows") {
358 DirectorySideCarTemporary.join(format!("node-{}.exe", Triple))
359 } else {
360 DirectorySideCarTemporary.join(format!("node-{}", Triple))
361 };
362
363 info!(
364 target: "Build",
365 "Staging sidecar from {} to {}",
366 Executable.display(),
367 PathExecutableDestination.display()
368 );
369
370 fs::copy(&Executable, &PathExecutableDestination)?;
372
373 #[cfg(not(target_os = "windows"))]
375 {
376 use std::os::unix::fs::PermissionsExt;
377
378 let mut Permission = fs::metadata(&PathExecutableDestination)?.permissions();
379
380 Permission.set_mode(0o755);
382
383 fs::set_permissions(&PathExecutableDestination, Permission)?;
384 }
385
386 Some("Binary/node".to_string())
387 } else {
388 info!(target: "Build", "No Node.js flavour selected for bundling.");
389
390 None
391 })
392 .as_deref(),
393 )?;
394
395 if Argument.Command.is_empty() {
397 return Err(BuildError::NoCommand);
398 }
399
400 let mut ShellCommand = if cfg!(target_os = "windows") {
401 let mut Command = ProcessCommand::new("cmd");
402
403 Command.arg("/C").args(&Argument.Command);
404
405 Command
406 } else {
407 let mut Command = ProcessCommand::new(&Argument.Command[0]);
408
409 Command.args(&Argument.Command[1..]);
410
411 Command
412 };
413
414 info!(target: "Build::Exec", "Executing final build command: {:?}", ShellCommand);
415
416 let Status = ShellCommand
417 .current_dir(env::current_dir()?)
418 .stdout(Stdio::inherit())
419 .stderr(Stdio::inherit())
420 .status()?;
421
422 if !Status.success() {
424 let temp_sidecar_dir = ProjectDir.join("bin");
425
426 if temp_sidecar_dir.exists() {
427 let _ = fs::remove_dir_all(&temp_sidecar_dir);
428 }
429
430 return Err(BuildError::Shell(Status));
431 }
432
433 let DirectorySideCarTemporary = ProjectDir.join("bin");
435
436 if DirectorySideCarTemporary.exists() {
437 fs::remove_dir_all(&DirectorySideCarTemporary)?;
438
439 info!(target: "Build", "Cleaned up temporary sidecar directory.");
440 }
441
442 drop(CargoGuard);
447 drop(ConfigGuard);
448
449 info!(target: "Build", "Build orchestration completed successfully.");
450
451 Ok(())
452}