axbuild 0.5.0

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

use anyhow::Context;
use cargo_metadata::{Metadata, Package};

use super::{
    AXSTD_STD_DEFAULT_FEATURE, AXSTD_STD_PACKAGE, DEFAULT_FEATURE,
    check::{ClippyCheck, ClippyCheckKind},
    configurations::package_clippy_configurations,
    env::{clippy_env, feature_clippy_env},
    targets::{docs_rs_targets, feature_requires_host_target, feature_supported_on_clippy_target},
};

pub(super) fn expand_clippy_checks(
    packages: &[Package],
    metadata: &Metadata,
) -> anyhow::Result<Vec<ClippyCheck>> {
    let mut checks = Vec::new();
    for package in packages {
        let mut features: BTreeSet<_> = package
            .features
            .keys()
            .filter(|feature| feature.as_str() != DEFAULT_FEATURE)
            .cloned()
            .collect();
        if package.name == AXSTD_STD_PACKAGE {
            features.insert(AXSTD_STD_DEFAULT_FEATURE.to_string());
        }
        let host_only_features = features
            .iter()
            .filter(|feature| feature_requires_host_target(package, feature))
            .cloned()
            .collect::<BTreeSet<_>>();
        features.retain(|feature| !host_only_features.contains(feature));
        let targets = docs_rs_targets(package);
        let target_iter = if targets.is_empty() {
            vec![None]
        } else {
            targets.into_iter().map(Some).collect()
        };
        let env = clippy_env(package);

        for target in target_iter {
            checks.push(ClippyCheck {
                package: package.name.to_string(),
                kind: ClippyCheckKind::Base,
                target: target.clone(),
                env: env.clone(),
            });

            for feature in &features {
                if !feature_supported_on_clippy_target(package, feature, target.as_deref()) {
                    continue;
                }
                let feature_env = feature_clippy_env(package, feature, env.clone(), metadata)
                    .with_context(|| {
                        format!(
                            "failed to prepare clippy env for `{}` feature `{feature}`",
                            package.name
                        )
                    })?;
                checks.push(ClippyCheck {
                    package: package.name.to_string(),
                    kind: ClippyCheckKind::Feature(feature.clone()),
                    target: target.clone(),
                    env: feature_env,
                });
            }
        }

        for feature in host_only_features {
            let feature_env = feature_clippy_env(package, &feature, env.clone(), metadata)
                .with_context(|| {
                    format!(
                        "failed to prepare clippy env for `{}` feature `{feature}`",
                        package.name
                    )
                })?;
            checks.push(ClippyCheck {
                package: package.name.to_string(),
                kind: ClippyCheckKind::Feature(feature),
                target: None,
                env: feature_env,
            });
        }
        for configuration in package_clippy_configurations(package)? {
            checks.push(ClippyCheck {
                package: package.name.to_string(),
                kind: ClippyCheckKind::Configuration {
                    name: configuration.name,
                    features: configuration.features,
                },
                target: Some(configuration.target),
                env: configuration.env,
            });
        }
    }

    Ok(checks)
}