axbuild 0.4.0

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

use anyhow::anyhow;

use super::{
    ARCEOS_SNAPSHOT_FILE, AppContext, ArceosCommandSnapshot, ArceosQemuSnapshot,
    ArceosUbootSnapshot, AxvisorCliArgs, AxvisorCommandSnapshot, AxvisorQemuSnapshot,
    AxvisorUbootSnapshot, BuildCliArgs, DEFAULT_ARCEOS_TARGET, ResolvedAxvisorRequest,
    ResolvedBuildRequest, ResolvedStarryRequest, STARRY_PACKAGE, StarryCliArgs,
    StarryCommandSnapshot, StarryQemuSnapshot, StarryUbootSnapshot,
    resolve_axvisor_arch_and_target, resolve_starry_arch_and_target,
};

#[derive(Debug, Clone, Default, PartialEq, Eq)]
struct ResolvedCommandPaths {
    qemu_config: Option<PathBuf>,
    uboot_config: Option<PathBuf>,
}

impl AppContext {
    pub fn prepare_arceos_request(
        &self,
        cli: BuildCliArgs,
        qemu_config: Option<PathBuf>,
        uboot_config: Option<PathBuf>,
    ) -> anyhow::Result<(ResolvedBuildRequest, ArceosCommandSnapshot)> {
        let snapshot = ArceosCommandSnapshot::load(&self.root)?;

        let package = cli
            .package
            .clone()
            .or_else(|| snapshot.package.clone())
            .ok_or_else(|| {
                anyhow!(
                    "missing ArceOS package; pass `--package` or set `package` in {}",
                    ARCEOS_SNAPSHOT_FILE
                )
            })?;
        let target = cli
            .target
            .clone()
            .or_else(|| snapshot.target.clone())
            .unwrap_or_else(|| DEFAULT_ARCEOS_TARGET.to_string());
        let plat_dyn = cli.plat_dyn.or(snapshot.plat_dyn);
        let runtime_paths = self.resolve_runtime_paths(
            qemu_config,
            snapshot.qemu.qemu_config.as_ref(),
            uboot_config,
            snapshot.uboot.uboot_config.as_ref(),
        );
        let build_info_path =
            crate::arceos::build::resolve_build_info_path(&package, &target, cli.config.clone())?;

        let request = ResolvedBuildRequest {
            package: package.clone(),
            target: target.clone(),
            plat_dyn,
            build_info_path,
            qemu_config: runtime_paths.qemu_config.clone(),
            uboot_config: runtime_paths.uboot_config.clone(),
        };

        let snapshot = ArceosCommandSnapshot {
            package: Some(package),
            target: Some(target),
            plat_dyn,
            qemu: ArceosQemuSnapshot {
                qemu_config: runtime_paths
                    .qemu_config
                    .as_ref()
                    .map(|path| snapshot_path_value(&self.root, path)),
            },
            uboot: ArceosUbootSnapshot {
                uboot_config: runtime_paths
                    .uboot_config
                    .as_ref()
                    .map(|path| snapshot_path_value(&self.root, path)),
            },
        };

        Ok((request, snapshot))
    }

    pub fn prepare_and_store_arceos_request(
        &self,
        cli: BuildCliArgs,
        qemu_config: Option<PathBuf>,
        uboot_config: Option<PathBuf>,
    ) -> anyhow::Result<ResolvedBuildRequest> {
        let (request, snapshot) = self.prepare_arceos_request(cli, qemu_config, uboot_config)?;
        self.store_arceos_snapshot(&snapshot)?;
        Ok(request)
    }

    pub fn store_arceos_snapshot(
        &self,
        snapshot: &ArceosCommandSnapshot,
    ) -> anyhow::Result<PathBuf> {
        snapshot.store(&self.root)
    }

    pub fn prepare_starry_request(
        &self,
        cli: StarryCliArgs,
        qemu_config: Option<PathBuf>,
        uboot_config: Option<PathBuf>,
    ) -> anyhow::Result<(ResolvedStarryRequest, StarryCommandSnapshot)> {
        let snapshot = StarryCommandSnapshot::load(&self.root)?;
        let effective_arch = cli.arch.clone().or_else(|| {
            if cli.target.is_some() {
                None
            } else {
                snapshot.arch.clone()
            }
        });
        let effective_target = cli.target.clone().or_else(|| {
            if cli.arch.is_some() {
                None
            } else {
                snapshot.target.clone()
            }
        });
        let (arch, target) = resolve_starry_arch_and_target(effective_arch, effective_target)?;
        let plat_dyn = cli.plat_dyn.or(snapshot.plat_dyn);
        let runtime_paths = self.resolve_runtime_paths(
            qemu_config,
            snapshot.qemu.qemu_config.as_ref(),
            uboot_config,
            snapshot.uboot.uboot_config.as_ref(),
        );
        let build_info_path =
            crate::starry::build::resolve_build_info_path(&self.root, &target, cli.config)?;

        let request = ResolvedStarryRequest {
            package: STARRY_PACKAGE.to_string(),
            arch: arch.clone(),
            target: target.clone(),
            plat_dyn,
            build_info_path,
            qemu_config: runtime_paths.qemu_config.clone(),
            uboot_config: runtime_paths.uboot_config.clone(),
        };

        let snapshot = StarryCommandSnapshot {
            arch: Some(arch),
            target: Some(target),
            plat_dyn,
            qemu: StarryQemuSnapshot {
                qemu_config: runtime_paths
                    .qemu_config
                    .as_ref()
                    .map(|path| snapshot_path_value(&self.root, path)),
            },
            uboot: StarryUbootSnapshot {
                uboot_config: runtime_paths
                    .uboot_config
                    .as_ref()
                    .map(|path| snapshot_path_value(&self.root, path)),
            },
        };

        Ok((request, snapshot))
    }

    pub fn prepare_and_store_starry_request(
        &self,
        cli: StarryCliArgs,
        qemu_config: Option<PathBuf>,
        uboot_config: Option<PathBuf>,
    ) -> anyhow::Result<ResolvedStarryRequest> {
        let (request, snapshot) = self.prepare_starry_request(cli, qemu_config, uboot_config)?;
        self.store_starry_snapshot(&snapshot)?;
        Ok(request)
    }

    pub fn store_starry_snapshot(
        &self,
        snapshot: &StarryCommandSnapshot,
    ) -> anyhow::Result<PathBuf> {
        snapshot.store(&self.root)
    }

    pub fn prepare_axvisor_request(
        &mut self,
        cli: AxvisorCliArgs,
        qemu_config: Option<PathBuf>,
        uboot_config: Option<PathBuf>,
    ) -> anyhow::Result<(ResolvedAxvisorRequest, AxvisorCommandSnapshot)> {
        let axvisor_dir = self.axvisor_dir()?.to_path_buf();
        let snapshot = AxvisorCommandSnapshot::load(&self.root)?;
        let explicit_config =
            self.resolve_command_path(cli.config.clone(), snapshot.config.as_ref());
        let config_target = explicit_config
            .as_ref()
            .filter(|path| path.exists())
            .map(|path| crate::axvisor::build::load_target_from_build_config(path))
            .transpose()?
            .flatten();

        let effective_arch = cli.arch.clone().or_else(|| {
            if cli.target.is_some() || config_target.is_some() {
                None
            } else {
                snapshot.arch.clone()
            }
        });
        let effective_target = cli.target.clone().or(config_target.clone()).or_else(|| {
            if cli.arch.is_some() {
                None
            } else {
                snapshot.target.clone()
            }
        });
        let (arch, target) = resolve_axvisor_arch_and_target(effective_arch, effective_target)?;
        let plat_dyn = cli.plat_dyn.or(snapshot.plat_dyn);
        let build_info_path =
            crate::axvisor::build::resolve_build_info_path(&axvisor_dir, &target, explicit_config)?;
        let runtime_paths = self.resolve_runtime_paths(
            qemu_config,
            snapshot.qemu.qemu_config.as_ref(),
            uboot_config,
            snapshot.uboot.uboot_config.as_ref(),
        );
        let vmconfigs = if cli.vmconfigs.is_empty() {
            self.resolve_workspace_paths(snapshot.vmconfigs.iter())
        } else {
            self.resolve_workspace_paths(cli.vmconfigs.iter())
        };

        let request = ResolvedAxvisorRequest {
            package: crate::axvisor::build::AXVISOR_PACKAGE.to_string(),
            axvisor_dir,
            arch: arch.clone(),
            target: target.clone(),
            plat_dyn,
            build_info_path: build_info_path.clone(),
            qemu_config: runtime_paths.qemu_config.clone(),
            uboot_config: runtime_paths.uboot_config.clone(),
            vmconfigs: vmconfigs.clone(),
        };

        let snapshot = AxvisorCommandSnapshot {
            arch: Some(arch),
            target: Some(target),
            plat_dyn,
            config: Some(snapshot_path_value(&self.root, &build_info_path)),
            vmconfigs: vmconfigs
                .iter()
                .map(|path| snapshot_path_value(&self.root, path))
                .collect(),
            qemu: AxvisorQemuSnapshot {
                qemu_config: runtime_paths
                    .qemu_config
                    .as_ref()
                    .map(|path| snapshot_path_value(&self.root, path)),
            },
            uboot: AxvisorUbootSnapshot {
                uboot_config: runtime_paths
                    .uboot_config
                    .as_ref()
                    .map(|path| snapshot_path_value(&self.root, path)),
            },
        };

        Ok((request, snapshot))
    }

    pub fn prepare_and_store_axvisor_request(
        &mut self,
        cli: AxvisorCliArgs,
        qemu_config: Option<PathBuf>,
        uboot_config: Option<PathBuf>,
    ) -> anyhow::Result<ResolvedAxvisorRequest> {
        let (request, snapshot) = self.prepare_axvisor_request(cli, qemu_config, uboot_config)?;
        self.store_axvisor_snapshot(&snapshot)?;
        Ok(request)
    }

    pub fn store_axvisor_snapshot(
        &self,
        snapshot: &AxvisorCommandSnapshot,
    ) -> anyhow::Result<PathBuf> {
        snapshot.store(&self.root)
    }

    fn resolve_runtime_paths(
        &self,
        qemu_config: Option<PathBuf>,
        snapshot_qemu: Option<&PathBuf>,
        uboot_config: Option<PathBuf>,
        snapshot_uboot: Option<&PathBuf>,
    ) -> ResolvedCommandPaths {
        ResolvedCommandPaths {
            qemu_config: self.resolve_command_path(qemu_config, snapshot_qemu),
            uboot_config: self.resolve_command_path(uboot_config, snapshot_uboot),
        }
    }

    fn resolve_command_path(
        &self,
        explicit_path: Option<PathBuf>,
        snapshot_path: Option<&PathBuf>,
    ) -> Option<PathBuf> {
        explicit_path.or_else(|| resolve_snapshot_path(&self.root, snapshot_path))
    }

    fn resolve_workspace_paths<'a>(
        &self,
        paths: impl IntoIterator<Item = &'a PathBuf>,
    ) -> Vec<PathBuf> {
        paths
            .into_iter()
            .map(|path| self.resolve_workspace_path(path))
            .collect()
    }

    fn resolve_workspace_path(&self, path: &Path) -> PathBuf {
        if path.is_absolute() {
            path.to_path_buf()
        } else {
            self.root.join(path)
        }
    }
}

pub(crate) fn resolve_snapshot_path(root: &Path, path: Option<&PathBuf>) -> Option<PathBuf> {
    path.map(|path| {
        if path.is_relative() {
            root.join(path)
        } else {
            path.clone()
        }
    })
}

pub(crate) fn snapshot_path_value(root: &Path, path: &Path) -> PathBuf {
    if path.is_absolute() {
        path.strip_prefix(root)
            .map(PathBuf::from)
            .unwrap_or_else(|_| path.to_path_buf())
    } else {
        path.to_path_buf()
    }
}