Skip to main content

cargo/compiler/
standard_lib.rs

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