axbuild 0.4.22

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

use anyhow::Context;
use ostool::board::{BoardRunRequest, RunBoardOptions};

use super::{
    ArgsTestBoard, StarryBoardTestGroup, board_assets::prepare_board_session_assets,
    discover_board_test_groups,
};
use crate::{
    context::{SnapshotPersistence, StarryCliArgs, arch_for_target_checked, board_run_request},
    starry::{Starry, board, build},
    test::{board as board_test, qemu as qemu_test},
};

pub(crate) fn collect_board_test_groups(
    _workspace_root: &Path,
    test_suite_dir: &Path,
) -> anyhow::Result<Vec<StarryBoardTestGroup>> {
    let mut groups = Vec::new();
    for info in board_test::discover_board_case_build_infos(test_suite_dir, "Starry")? {
        let board_file = board::load_board_file(&info.build_config_path).with_context(|| {
            format!(
                "failed to load Starry board build config `{}`",
                info.build_config_path.display()
            )
        })?;
        let arch = arch_for_target_checked(&board_file.target)?.to_string();
        let target = board_file.target;
        groups.push(StarryBoardTestGroup {
            name: info.name,
            board_name: info.board_name,
            arch,
            target,
            build_config_path: info.build_config_path,
            board_test_config_path: info.board_test_config_path,
        });
    }

    Ok(groups)
}

impl Starry {
    pub(super) async fn test_board(&mut self, args: ArgsTestBoard) -> anyhow::Result<()> {
        let groups = discover_board_test_groups(
            self.app.workspace_root(),
            args.test_case.as_deref(),
            args.board.as_deref(),
        )?;
        if args.list {
            let case_names = board_test::labeled_board_cases(groups);
            println!(
                "{}",
                qemu_test::render_labeled_case_forest("starry", [("board", case_names)])
            );
            return Ok(());
        }

        let mut run_state = board_test::BoardTestRunState::new("starry", groups.len());
        for (index, group) in groups.into_iter().enumerate() {
            let group_label = run_state.start_group(index, &group);
            let board_test_config = group.board_test_config_path.clone();
            let board_test_config_summary = board_test_config.display().to_string();
            if !board_test_config.exists() {
                run_state.fail_group(
                    group_label,
                    anyhow::anyhow!("missing board test config `{board_test_config_summary}`"),
                );
                continue;
            }

            let result = async {
                let request = self.prepare_request(
                    Self::test_board_build_args(&group),
                    None,
                    None,
                    SnapshotPersistence::Discard,
                )?;
                let cargo = build::load_cargo_config(&request)?;
                let (board_config, board_config_path) = self
                    .load_board_config(&cargo, Some(board_test_config.as_path()))
                    .await?;
                let options = RunBoardOptions {
                    board_type: args.board_type.clone(),
                    server: args.server.clone(),
                    port: args.port,
                };
                let case_dir = board_config_path.parent().with_context(|| {
                    format!(
                        "board configuration path `{}` has no parent directory",
                        board_config_path.display()
                    )
                })?;
                let session_assets = prepare_board_session_assets(
                    self.app.workspace_root(),
                    &group.arch,
                    &group.target,
                    &group.name,
                    case_dir,
                    &board_config_path,
                    &board_config.session_files,
                )
                .await?;
                let output = self.build_artifact(&request, cargo.clone()).await?;
                let board_request = match session_assets {
                    Some(assets) => {
                        println!(
                            "[axbuild] board session upload root: {}",
                            assets.root.display()
                        );
                        BoardRunRequest::new(board_config, options)
                            .with_session_files(&assets.root, &assets.relative_paths)?
                    }
                    None => board_run_request(&board_config_path, board_config, options)?,
                };
                self.app
                    .board_prepared_elf_with_request(
                        output.elf_path().to_path_buf(),
                        cargo.to_bin,
                        request.build_info_path.clone(),
                        board_request,
                    )
                    .await
                    .with_context(|| {
                        format!(
                            "starry board test failed for group `{}` (build_config={}, \
                             board_test_config={})",
                            group_label,
                            group.build_config_path.display(),
                            board_test_config_summary
                        )
                    })
            }
            .await;

            match result {
                Ok(()) => run_state.pass_group(&group_label),
                Err(err) => run_state.fail_group(group_label, err),
            }
        }
        run_state.finish()
    }

    pub(crate) fn test_build_args(target: &str, config: Option<PathBuf>) -> StarryCliArgs {
        StarryCliArgs {
            config,
            arch: None,
            target: Some(target.to_string()),
            smp: None,
            debug: false,
        }
    }

    fn test_board_build_args(group: &StarryBoardTestGroup) -> StarryCliArgs {
        StarryCliArgs {
            config: Some(group.build_config_path.clone()),
            arch: None,
            target: Some(group.target.clone()),
            smp: None,
            debug: false,
        }
    }
}