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