axbuild 0.4.21

An OS build lib toolkit used by arceos
use std::{
    fs,
    path::{Path, PathBuf},
    process::Command,
};

use anyhow::Context;

use super::{super::rootfs, types::StarryAppCase};
use crate::{rootfs::inject, support::process::ProcessExt};

pub(super) async fn prepare_qemu_app_rootfs(
    workspace_root: &Path,
    app: &StarryAppCase,
    arch: &str,
    target: &str,
    configured_rootfs: Option<&Path>,
) -> anyhow::Result<PathBuf> {
    let rootfs_path = match configured_rootfs {
        Some(path) => path.to_path_buf(),
        None => crate::image::storage::default_rootfs_path(workspace_root, arch)?,
    };
    if app.prebuild_path.is_none() {
        if let Some(configured) = configured_rootfs {
            crate::image::storage::ensure_optional_managed_rootfs(
                workspace_root,
                arch,
                Some(configured),
            )
            .await?;
            rootfs::ensure_apk_region_in_rootfs(configured)?;
            return Ok(configured.to_path_buf());
        }
        return rootfs::ensure_rootfs_in_tmp_dir(workspace_root, arch, target).await;
    }

    let default_rootfs = rootfs::ensure_rootfs_in_tmp_dir(workspace_root, arch, target).await?;
    if let Some(parent) = rootfs_path.parent() {
        fs::create_dir_all(parent)
            .with_context(|| format!("failed to create {}", parent.display()))?;
    }
    if !rootfs_path.exists() {
        fs::copy(&default_rootfs, &rootfs_path).with_context(|| {
            format!(
                "failed to copy default rootfs {} to {}",
                default_rootfs.display(),
                rootfs_path.display()
            )
        })?;
    }

    let layout_root = workspace_root
        .join("tmp/axbuild/starry-app")
        .join(&app.name);
    let staging_root = layout_root.join("staging-root");
    let overlay_dir = layout_root.join("overlay");

    let prepare_result = (|| -> anyhow::Result<()> {
        reset_dir(&staging_root)?;
        reset_dir(&overlay_dir)?;

        if let Some(prebuild_path) = app.prebuild_path.as_deref() {
            let mut command = Command::new("bash");
            command
                .arg(prebuild_path)
                .current_dir(&app.case_dir)
                .env("STARRY_APP_NAME", &app.name)
                .env("STARRY_APP_DIR", &app.case_dir)
                .env("STARRY_WORKSPACE", workspace_root)
                .env("STARRY_ARCH", arch)
                .env("STARRY_ROOTFS", &rootfs_path)
                .env("STARRY_STAGING_ROOT", &staging_root)
                .env("STARRY_OVERLAY_DIR", &overlay_dir);
            command
                .exec()
                .with_context(|| format!("failed to run {}", prebuild_path.display()))?;
        }

        inject::inject_overlay(&rootfs_path, &overlay_dir)
    })();
    prepare_result?;
    Ok(rootfs_path)
}

fn reset_dir(path: &Path) -> anyhow::Result<()> {
    if path.exists() {
        fs::remove_dir_all(path).with_context(|| format!("failed to remove {}", path.display()))?;
    }
    fs::create_dir_all(path).with_context(|| format!("failed to create {}", path.display()))
}