axbuild 0.4.14

An OS build lib toolkit used by arceos
Documentation
use std::collections::BTreeSet;

use cargo_metadata::Package;
use serde_json::Value;

use super::{AX_HAL_PACKAGE, DOCS_METADATA, DOCS_RS_METADATA, RS_METADATA, TARGETS_METADATA};

const CLIPPY_TARGET_ALIASES: &[(&str, &str)] = &[
    (
        "aarch64-unknown-linux-gnu",
        "aarch64-unknown-none-softfloat",
    ),
    ("aarch64-unknown-none", "aarch64-unknown-none-softfloat"),
    (
        "loongarch64-unknown-none",
        "loongarch64-unknown-none-softfloat",
    ),
];

const AX_HAL_PLATFORM_FEATURE_TARGET_ARCHES: &[(&str, &[&str])] = &[
    ("plat-dyn", &["aarch64", "loongarch64", "riscv64", "x86_64"]),
    ("loongarch64-qemu-virt", &["loongarch64"]),
    ("riscv64-sg2002", &["riscv64"]),
    ("riscv64-visionfive2", &["riscv64"]),
];

pub(super) fn docs_rs_targets(package: &Package) -> Vec<String> {
    let Some(docs_rs) = package
        .metadata
        .get(DOCS_RS_METADATA)
        .and_then(Value::as_object)
        .or_else(|| {
            package
                .metadata
                .get(DOCS_METADATA)
                .and_then(Value::as_object)
                .and_then(|docs| docs.get(RS_METADATA))
                .and_then(Value::as_object)
        })
    else {
        return Vec::new();
    };

    let Some(targets) = docs_rs.get(TARGETS_METADATA).and_then(Value::as_array) else {
        return Vec::new();
    };

    let mut unique_targets = BTreeSet::new();
    for target in targets.iter().filter_map(Value::as_str) {
        unique_targets.insert(normalize_clippy_target(target).to_string());
    }

    unique_targets.into_iter().collect()
}

fn normalize_clippy_target(target: &str) -> &str {
    CLIPPY_TARGET_ALIASES
        .iter()
        .find_map(|(source, normalized)| (*source == target).then_some(*normalized))
        .unwrap_or(target)
}

fn clippy_target_arch(target: &str) -> Option<&'static str> {
    if target.starts_with("aarch64-") {
        Some("aarch64")
    } else if target.starts_with("loongarch64-") {
        Some("loongarch64")
    } else if target.starts_with("riscv64") {
        Some("riscv64")
    } else if target.starts_with("x86_64-") {
        Some("x86_64")
    } else {
        None
    }
}

fn ax_hal_platform_target_arches(feature: &str) -> Option<&'static [&'static str]> {
    AX_HAL_PLATFORM_FEATURE_TARGET_ARCHES
        .iter()
        .find_map(|(platform_feature, target_arches)| {
            (*platform_feature == feature).then_some(*target_arches)
        })
}

fn ax_hal_feature_dependency(feature_dependency: &str) -> Option<&str> {
    feature_dependency
        .strip_prefix("ax-hal/")
        .or_else(|| feature_dependency.strip_prefix("ax-hal?/"))
}

fn ax_hal_platform_constraints<'a>(
    package: &'a Package,
    feature: &'a str,
) -> Vec<&'static [&'static str]> {
    let mut constraints = Vec::new();
    if package.name == AX_HAL_PACKAGE
        && let Some(target_arches) = ax_hal_platform_target_arches(feature)
    {
        constraints.push(target_arches);
    }

    if let Some(feature_dependencies) = package.features.get(feature) {
        constraints.extend(
            feature_dependencies
                .iter()
                .filter_map(|feature_dependency| ax_hal_feature_dependency(feature_dependency))
                .filter_map(ax_hal_platform_target_arches),
        );
    }

    constraints
}

pub(super) fn feature_supported_on_clippy_target(
    package: &Package,
    feature: &str,
    target: Option<&str>,
) -> bool {
    let constraints = ax_hal_platform_constraints(package, feature);
    if constraints.is_empty() {
        return true;
    }
    let Some(target) = target else {
        return false;
    };
    clippy_target_arch(target).is_some_and(|arch| {
        constraints
            .iter()
            .all(|target_arches| target_arches.contains(&arch))
    })
}