Skip to main content

rbnx/cmd/
build.rs

1// SPDX-License-Identifier: MulanPSL-2.0
2// Build command: run the package's build.script
3
4use anyhow::{Context, Result};
5use robonix_cli::manifest;
6use robonix_cli::output;
7use std::fs;
8use std::path::{Path, PathBuf};
9use std::process::Command;
10
11const RBNX_BUILD_DIR: &str = "rbnx-build";
12const RBNX_BUILT_STAMP: &str = "rbnx-build/.rbnx-built";
13
14pub fn build_stamp_path(package_root: &Path) -> PathBuf {
15    package_root.join(RBNX_BUILT_STAMP)
16}
17
18fn run_build_shell(package_root: &Path, body: &str, clean: bool) -> Result<()> {
19    let mut cmd = Command::new("bash");
20    cmd.arg("-c").arg(body);
21    cmd.current_dir(package_root);
22    cmd.env("RBNX_PACKAGE_ROOT", package_root.as_os_str());
23    if clean {
24        cmd.env("RBNX_BUILD_CLEAN", "1");
25    }
26    let status = cmd.status().with_context(|| {
27        format!(
28            "Failed to run build shell body in {}",
29            package_root.display()
30        )
31    })?;
32    if !status.success() {
33        anyhow::bail!("Build exited with status {:?}", status.code());
34    }
35    Ok(())
36}
37
38fn build_local(package_root: &Path, manifest: &manifest::Manifest, clean: bool) -> Result<()> {
39    let _summary = manifest.validate_and_summarize()?;
40    let build_body = manifest.build.trim();
41    output::action(
42        "Building",
43        &format!("{} via manifest.build", manifest.package.name),
44    );
45    run_build_shell(package_root, build_body, clean)?;
46    fs::create_dir_all(package_root.join(RBNX_BUILD_DIR))?;
47    fs::write(build_stamp_path(package_root), "").with_context(|| {
48        format!(
49            "Failed to write {}",
50            build_stamp_path(package_root).display()
51        )
52    })?;
53    output::success(&format!(
54        "Package '{}' build finished",
55        manifest.package.name
56    ));
57    Ok(())
58}
59
60/// Build the package at `path`. `manifest_override` selects a non-default
61/// package manifest file (deploy `manifest:` field) so a per-target variant
62/// builds its own Dockerfile / native path; `None` uses package_manifest.yaml.
63pub fn build_local_package(
64    path: &Path,
65    clean: bool,
66    manifest_override: Option<&str>,
67) -> Result<()> {
68    let package_root = path
69        .canonicalize()
70        .with_context(|| format!("Failed to canonicalize package path {}", path.display()))?;
71    let detected = manifest::detect_and_load(&package_root, manifest_override)?;
72    build_local(&package_root, &detected.manifest, clean)
73}
74
75pub async fn execute_local(path: PathBuf, clean: bool) -> Result<()> {
76    let package_root = path
77        .canonicalize()
78        .with_context(|| format!("Failed to canonicalize package path {}", path.display()))?;
79    output::action(
80        "Building",
81        &format!("local package at {}", package_root.display()),
82    );
83    build_local_package(&package_root, clean, None)?;
84    Ok(())
85}