axbuild 0.4.15

An OS build lib toolkit used by arceos
Documentation
pub(super) use std::{
    collections::HashMap,
    env,
    ffi::{OsStr, OsString},
    fs,
    path::{Path, PathBuf},
    sync::{LazyLock, Mutex},
};

use ostool::invocation::{Invocation, InvocationOptions};
pub(super) use tempfile::tempdir;

use super::*;

pub(super) fn test_app_context(root: &Path) -> AppContext {
    AppContext {
        invocation: test_invocation(root),
        build_config_path: None,
        root: root.to_path_buf(),
        member_dirs: HashMap::from([("axvisor".to_string(), root.join("os/axvisor"))]),
        original_path: env::var_os("PATH").unwrap_or_default(),
        debug: false,
    }
}

fn test_invocation(root: &Path) -> Invocation {
    let manifest_path = root.join("Cargo.toml");
    if !manifest_path.exists() {
        fs::create_dir_all(root.join("src")).unwrap();
        fs::write(root.join("src/lib.rs"), "").unwrap();
        fs::write(
            &manifest_path,
            r#"[package]
name = "test-workspace"
version = "0.1.0"
edition = "2021"

[workspace]
"#,
        )
        .unwrap();
    }
    Invocation::new(InvocationOptions::new(
        Some(manifest_path),
        None,
        None,
        false,
    ))
    .unwrap()
}

fn resolve_arceos_build_info_path(
    package: &str,
    target: &str,
    explicit_path: Option<PathBuf>,
) -> anyhow::Result<PathBuf> {
    crate::arceos::build::resolve_build_info_path(package, target, explicit_path)
}

pub(super) fn prepare_arceos_request(
    app: &AppContext,
    cli: BuildCliArgs,
    qemu_config: Option<PathBuf>,
    uboot_config: Option<PathBuf>,
) -> anyhow::Result<(ResolvedBuildRequest, ArceosCommandSnapshot)> {
    app.prepare_arceos_request(
        cli,
        qemu_config,
        uboot_config,
        resolve_arceos_build_info_path,
    )
}

fn resolve_starry_build_info_path(
    workspace_root: &Path,
    target: &str,
    explicit_path: Option<PathBuf>,
) -> anyhow::Result<PathBuf> {
    crate::starry::build::resolve_build_info_path(workspace_root, target, explicit_path)
}

pub(super) fn prepare_starry_request(
    app: &AppContext,
    cli: StarryCliArgs,
    qemu_config: Option<PathBuf>,
    uboot_config: Option<PathBuf>,
) -> anyhow::Result<(ResolvedStarryRequest, StarryCommandSnapshot)> {
    app.prepare_starry_request(
        cli,
        qemu_config,
        uboot_config,
        resolve_starry_build_info_path,
    )
}

pub(super) fn prepare_axvisor_request(
    app: &AppContext,
    cli: AxvisorCliArgs,
    qemu_config: Option<PathBuf>,
    uboot_config: Option<PathBuf>,
) -> anyhow::Result<(ResolvedAxvisorRequest, AxvisorCommandSnapshot)> {
    app.prepare_axvisor_request(
        cli,
        AxvisorRequestPaths {
            package: crate::axvisor::build::AXVISOR_PACKAGE.to_string(),
            axvisor_dir: app.root.join("os/axvisor"),
            load_config_target: crate::axvisor::build::load_target_from_build_config,
            resolve_build_info_path: crate::axvisor::build::resolve_build_info_path,
        },
        qemu_config,
        uboot_config,
    )
}

pub(super) static ENV_LOCK: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));

pub(super) struct TempEnvVar {
    key: &'static str,
    original: Option<OsString>,
}

impl TempEnvVar {
    pub(super) fn set(key: &'static str, value: impl AsRef<OsStr>) -> Self {
        let original = env::var_os(key);
        unsafe {
            env::set_var(key, value);
        }
        Self { key, original }
    }

    pub(super) fn unset(key: &'static str) -> Self {
        let original = env::var_os(key);
        unsafe {
            env::remove_var(key);
        }
        Self { key, original }
    }
}

impl Drop for TempEnvVar {
    fn drop(&mut self) {
        match self.original.as_ref() {
            Some(value) => unsafe {
                env::set_var(self.key, value);
            },
            None => unsafe {
                env::remove_var(self.key);
            },
        }
    }
}

fn write_minimal_workspace_package(path: &Path, name: &str) {
    let src_dir = path.parent().unwrap().join("src");
    fs::create_dir_all(&src_dir).unwrap();
    fs::write(src_dir.join("lib.rs"), "").unwrap();
    fs::write(
        path,
        format!("[package]\nname = \"{name}\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"),
    )
    .unwrap();
}

pub(super) fn prepare_starry_workspace(root: &Path) {
    let starry_dir = root.join("os/StarryOS/starryos");
    fs::create_dir_all(&starry_dir).unwrap();
    write_minimal_workspace_package(&starry_dir.join("Cargo.toml"), STARRY_PACKAGE);
    fs::write(
        root.join("Cargo.toml"),
        "[workspace]\nmembers = [\"os/StarryOS/starryos\"]\n",
    )
    .unwrap();
}

pub(super) fn snapshot_path(root: &Path, file_name: &str) -> PathBuf {
    axbuild_tmp_dir(root).join(file_name)
}

pub(super) fn write_snapshot_text(
    root: &Path,
    file_name: &str,
    content: &str,
) -> std::io::Result<()> {
    let path = snapshot_path(root, file_name);
    fs::create_dir_all(path.parent().unwrap())?;
    fs::write(path, content)
}