axbuild 0.4.21

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

use anyhow::{Context, ensure};

use super::build;
use crate::context::ResolvedBuildRequest;

mod compile;
mod features;
mod flags;
mod libc;
mod link;
#[cfg(test)]
mod tests;
mod types;

use compile::{archive_static_lib, compile_dir_c_sources};
use features::{c_compiler_features, dynamic_pie_for_c_app, map_c_app_features};
use flags::{CFlagsInput, cflags, write_pthread_mutex_header};
pub(crate) use libc::AX_LIBC_PACKAGE;
use libc::build_axlibc_staticlib;
use link::{find_link_scripts, libgcc, link_c_app, platform_name};
use types::sanitize_name;
pub(crate) use types::{ArceosCBuildInput, ArceosCBuildOutput};

pub(crate) type ArceosCArtifactPaths = types::ArceosCArtifactPaths;

pub(crate) fn default_c_app_artifact_paths(
    workspace_root: &Path,
    app_name: &str,
) -> ArceosCArtifactPaths {
    types::default_c_app_artifact_paths(workspace_root, app_name)
}

pub(crate) fn build_c_app(
    workspace_root: &Path,
    request: &ResolvedBuildRequest,
    input: &ArceosCBuildInput,
) -> anyhow::Result<ArceosCBuildOutput> {
    let mut cargo = build::load_c_app_cargo_config(request)?;
    cargo.package = AX_LIBC_PACKAGE.to_string();
    cargo.target = request.target.clone();
    cargo.to_bin = false;
    cargo.features = map_c_app_features(&input.features, &cargo.features)?;
    let c_features = c_compiler_features(&cargo.features, &input.features);
    let dynamic_pie = dynamic_pie_for_c_app(&cargo.features);

    let mode = if request.debug { "debug" } else { "release" };
    let arch = request.arch.as_str();
    let arceos_dir = workspace_root.join("os/arceos");
    let axlibc_dir = arceos_dir.join("ulib/axlibc");
    let c_source_dir = axlibc_dir.join("c");
    let include_dir = axlibc_dir.join("include");
    let obj_root = input
        .target_dir
        .join("arceos-c")
        .join(sanitize_name(&input.app_name))
        .join(arch);
    let generated_include_dir = obj_root.join("include");
    let axlibc_obj_dir = obj_root.join("axlibc");
    let app_obj_dir = obj_root.join("app");
    fs::create_dir_all(&axlibc_obj_dir)
        .with_context(|| format!("failed to create {}", axlibc_obj_dir.display()))?;
    fs::create_dir_all(&app_obj_dir)
        .with_context(|| format!("failed to create {}", app_obj_dir.display()))?;
    fs::create_dir_all(&input.out_dir)
        .with_context(|| format!("failed to create {}", input.out_dir.display()))?;

    build_axlibc_staticlib(
        workspace_root,
        &cargo,
        &input.target_dir,
        request.debug,
        dynamic_pie,
    )?;
    write_pthread_mutex_header(&generated_include_dir, &cargo.features)?;
    let rust_lib = input
        .target_dir
        .join(&request.target)
        .join(mode)
        .join("libax_libc.a");
    ensure!(
        rust_lib.is_file(),
        "expected ax-libc static library at {}",
        rust_lib.display()
    );

    let platform = platform_name(&cargo.env);
    let link_scripts = find_link_scripts(
        &input.target_dir,
        &request.target,
        mode,
        &platform,
        &cargo.features,
    )
    .context("failed to locate ArceOS C app linker scripts")?;

    let cflags = cflags(CFlagsInput {
        workspace_root,
        arch,
        mode,
        generated_include_dir: &generated_include_dir,
        include_dir: &include_dir,
        features: &c_features,
        log: cargo.log,
        dynamic_pie,
    });
    let lib_objects =
        compile_dir_c_sources(&c_source_dir, &axlibc_obj_dir, &cflags, None, "axlibc")?;
    let app_objects = compile_dir_c_sources(&input.app_dir, &app_obj_dir, &cflags, None, "app")?;
    let libc = axlibc_obj_dir.join("libc.a");
    archive_static_lib(arch, &libc, &lib_objects)?;

    let elf_path = input
        .out_dir
        .join(format!("{}_{}.unstripped", input.app_name, platform));
    link_c_app(
        arch,
        &link_scripts,
        &elf_path,
        &rust_lib,
        &libc,
        &app_objects,
        libgcc(arch, &cargo.features)?,
    )?;

    Ok(ArceosCBuildOutput { elf_path })
}