#
cargo/core/compiler/
standard_lib.rs

    1//! Code for building the standard library.
    2
    3use crate::core::compiler::UnitInterner;
    4use crate::core::compiler::unit_dependencies::IsArtifact;
    5use crate::core::compiler::{CompileKind, CompileMode, RustcTargetData, Unit};
    6use crate::core::profiles::{Profiles, UnitFor};
    7use crate::core::resolver::HasDevUnits;
    8use crate::core::resolver::features::{CliFeatures, FeaturesFor, ResolvedFeatures};
    9use crate::core::{PackageId, PackageSet, Resolve, Workspace};
    10use crate::ops::{self, Packages};
    11use crate::util::errors::CargoResult;
    12
    13use std::collections::{HashMap, HashSet};
    14use std::path::PathBuf;
    15
    16use super::BuildConfig;
    17
    18fn std_crates<'a>(crates: &'a [String], default: &'static str, units: &[Unit]) -> HashSet<&'a str> {
    19    let mut crates = HashSet::from_iter(crates.iter().map(|s| s.as_str()));
    20    // This is a temporary hack until there is a more principled way to
    21    // declare dependencies in Cargo.toml.
    22    if crates.is_empty() {
    23        crates.insert(default);
    24    }
    25    if crates.contains("std") {
    26        crates.insert("core");
    27        crates.insert("alloc");
    28        crates.insert("proc_macro");
    29        crates.insert("panic_unwind");
    30        crates.insert("compiler_builtins");
    31        // Only build libtest if it looks like it is needed (libtest depends on libstd)
    32        // If we know what units we're building, we can filter for libtest depending on the jobs.
    33        if units
    34            .iter()
    35            .any(|unit| unit.mode.is_rustc_test() && unit.target.harness())
    36        {
    37            crates.insert("test");
    38        }
    39    } else if crates.contains("core") {
    40        crates.insert("compiler_builtins");
    41    }
    42
    43    crates
    44}
    45
    46/// Resolve the standard library dependencies.
    47///
    48/// * `crates` is the arg value from `-Zbuild-std`.
    49pub fn resolve_std<'gctx>(
    50    ws: &Workspace<'gctx>,
    51    target_data: &mut RustcTargetData<'gctx>,
    52    build_config: &BuildConfig,
    53    crates: &[String],
    54    kinds: &[CompileKind],
    55) -> CargoResult<(PackageSet<'gctx>, Resolve, ResolvedFeatures)> {
    56    if build_config.build_plan {
    57        ws.gctx()
    58            .shell()
    59            .warn("-Zbuild-std does not currently fully support --build-plan")?;
    60    }
    61
    62    let src_path = detect_sysroot_src_path(target_data)?;
    63    let std_ws_manifest_path = src_path.join("Cargo.toml");
    64    let gctx = ws.gctx();
    65    // TODO: Consider doing something to enforce --locked? Or to prevent the
    66    // lock file from being written, such as setting ephemeral.
    67    let mut std_ws = Workspace::new(&std_ws_manifest_path, gctx)?;
    68    // Don't require optional dependencies in this workspace, aka std's own
    69    // `[dev-dependencies]`. No need for us to generate a `Resolve` which has
    70    // those included because we'll never use them anyway.
    71    std_ws.set_require_optional_deps(false);
    72    let specs = {
    73        // If there is anything looks like needing std, resolve with it.
    74        // If not, we assume only `core` maye be needed, as `core the most fundamental crate.
    75        //
    76        // This may need a UI overhaul if `build-std` wants to fully support multi-targets.
    77        let maybe_std = kinds
    78            .iter()
    79            .any(|kind| target_data.info(*kind).maybe_support_std());
    80        let mut crates = std_crates(crates, if maybe_std { "std" } else { "core" }, &[]);
    81        // `sysroot` is not in the default set because it is optional, but it needs
    82        // to be part of the resolve in case we do need it or `libtest`.
    83        crates.insert("sysroot");
    84        let specs = Packages::Packages(crates.into_iter().map(Into::into).collect());
    85        specs.to_package_id_specs(&std_ws)?
    86    };
    87    let features = match &gctx.cli_unstable().build_std_features {
    88        Some(list) => list.clone(),
    89        None => vec![
    90            "panic-unwind".to_string(),
    91            "backtrace".to_string(),
    92            "default".to_string(),
    93        ],
    94    };
    95    let cli_features = CliFeatures::from_command_line(
    96        &features, /*all_features*/ false, /*uses_default_features*/ false,
    97    )?;
    98    let dry_run = false;
    99    let mut resolve = ops::resolve_ws_with_opts(
    100        &std_ws,
    101        target_data,
    102        &build_config.requested_kinds,
    103        &cli_features,
    104        &specs,
    105        HasDevUnits::No,
    106        crate::core::resolver::features::ForceAllTargets::No,
    107        dry_run,
    108    )?;
    109    debug_assert_eq!(resolve.specs_and_features.len(), 1);
    110    Ok((
    111        resolve.pkg_set,
    112        resolve.targeted_resolve,
    113        resolve
    114            .specs_and_features
    115            .pop()
    116            .expect("resolve should have a single spec with resolved features")
    117            .resolved_features,
    118    ))
    119}
    120
    121/// Generates a map of root units for the standard library for each kind requested.
    122///
    123/// * `crates` is the arg value from `-Zbuild-std`.
    124/// * `units` is the root units of the build.
    125pub fn generate_std_roots(
    126    crates: &[String],
    127    units: &[Unit],
    128    std_resolve: &Resolve,
    129    std_features: &ResolvedFeatures,
    130    kinds: &[CompileKind],
    131    package_set: &PackageSet<'_>,
    132    interner: &UnitInterner,
    133    profiles: &Profiles,
    134    target_data: &RustcTargetData<'_>,
    135) -> CargoResult<HashMap<CompileKind, Vec<Unit>>> {
    136    // Generate a map of Units for each kind requested.
    137    let mut ret = HashMap::new();
    138    let (maybe_std, maybe_core): (Vec<&CompileKind>, Vec<_>) = kinds
    139        .iter()
    140        .partition(|kind| target_data.info(**kind).maybe_support_std());
    141    for (default_crate, kinds) in [("core", maybe_core), ("std", maybe_std)] {
    142        if kinds.is_empty() {
    143            continue;
    144        }
    145        generate_roots(
    146            &mut ret,
    147            default_crate,
    148            crates,
    149            units,
    150            std_resolve,
    151            std_features,
    152            &kinds,
    153            package_set,
    154            interner,
    155            profiles,
    156            target_data,
    157        )?;
    158    }
    159
    160    Ok(ret)
    161}
    162
    163fn generate_roots(
    164    ret: &mut HashMap<CompileKind, Vec<Unit>>,
    165    default: &'static str,
    166    crates: &[String],
    167    units: &[Unit],
    168    std_resolve: &Resolve,
    169    std_features: &ResolvedFeatures,
    170    kinds: &[&CompileKind],
    171    package_set: &PackageSet<'_>,
    172    interner: &UnitInterner,
    173    profiles: &Profiles,
    174    target_data: &RustcTargetData<'_>,
    175) -> CargoResult<()> {
    176    let std_ids = std_crates(crates, default, units)
    177        .iter()
    178        .map(|crate_name| std_resolve.query(crate_name))
    179        .collect::<CargoResult<Vec<PackageId>>>()?;
    180    let std_pkgs = package_set.get_many(std_ids)?;
    181
    182    for pkg in std_pkgs {
    183        let lib = pkg
    184            .targets()
    185            .iter()
    186            .find(|t| t.is_lib())
    187            .expect("std has a lib");
    188        // I don't think we need to bother with Check here, the difference
    189        // in time is minimal, and the difference in caching is
    190        // significant.
    191        let mode = CompileMode::Build;
    192        let features = std_features.activated_features(pkg.package_id(), FeaturesFor::NormalOrDev);
    193        for kind in kinds {
    194            let kind = **kind;
    195            let list = ret.entry(kind).or_insert_with(Vec::new);
    196            let unit_for = UnitFor::new_normal(kind);
    197            let profile = profiles.get_profile(
    198                pkg.package_id(),
    199                /*is_member*/ false,
    200                /*is_local*/ false,
    201                unit_for,
    202                kind,
    203            );
    204            list.push(interner.intern(
    205                pkg,
    206                lib,
    207                profile,
    208                kind,
    209                mode,
    210                features.clone(),
    211                target_data.info(kind).rustflags.clone(),
    212                target_data.info(kind).rustdocflags.clone(),
    213                target_data.target_config(kind).links_overrides.clone(),
    214                /*is_std*/ true,
    215                /*dep_hash*/ 0,
    216                IsArtifact::No,
    217                None,
    218                false,
    219            ));
    220        }
    221    }
    222    Ok(())
    223}
    224
    225fn detect_sysroot_src_path(target_data: &RustcTargetData<'_>) -> CargoResult<PathBuf> {
    226    if let Some(s) = target_data.gctx.get_env_os("__CARGO_TESTS_ONLY_SRC_ROOT") {
    227        return Ok(s.into());
    228    }
    229
    230    // NOTE: This is temporary until we figure out how to acquire the source.
    231    let src_path = target_data
    232        .info(CompileKind::Host)
    233        .sysroot
    234        .join("lib")
    235        .join("rustlib")
    236        .join("src")
    237        .join("rust")
    238        .join("library");
    239    let lock = src_path.join("Cargo.lock");
    240    if !lock.exists() {
    241        let msg = format!(
    242            "{:?} does not exist, unable to build with the standard \
    243             library, try:\n        rustup component add rust-src",
    244            lock
    245        );
    246        match target_data.gctx.get_env("RUSTUP_TOOLCHAIN") {
    247            Ok(rustup_toolchain) => {
    248                anyhow::bail!("{} --toolchain {}", msg, rustup_toolchain);
    249            }
    250            Err(_) => {
    251                anyhow::bail!(msg);
    252            }
    253        }
    254    }
    255    Ok(src_path)
    256}