1 //! Code for building the standard library.
2 
3 use crate::core::compiler::UnitInterner;
4 use crate::core::compiler::{CompileKind, CompileMode, RustcTargetData, Unit};
5 use crate::core::profiles::{Profiles, UnitFor};
6 use crate::core::resolver::features::{CliFeatures, FeaturesFor, ResolvedFeatures};
7 use crate::core::resolver::HasDevUnits;
8 use crate::core::{Dependency, PackageId, PackageSet, Resolve, SourceId, Workspace};
9 use crate::ops::{self, Packages};
10 use crate::util::errors::CargoResult;
11 use std::collections::{HashMap, HashSet};
12 use std::env;
13 use std::path::PathBuf;
14 
15 /// Parse the `-Zbuild-std` flag.
parse_unstable_flag(value: Option<&str>) -> Vec<String>16 pub fn parse_unstable_flag(value: Option<&str>) -> Vec<String> {
17     // This is a temporary hack until there is a more principled way to
18     // declare dependencies in Cargo.toml.
19     let value = value.unwrap_or("std");
20     let mut crates: HashSet<&str> = value.split(',').collect();
21     if crates.contains("std") {
22         crates.insert("core");
23         crates.insert("alloc");
24         crates.insert("proc_macro");
25         crates.insert("panic_unwind");
26         crates.insert("compiler_builtins");
27     } else if crates.contains("core") {
28         crates.insert("compiler_builtins");
29     }
30     crates.into_iter().map(|s| s.to_string()).collect()
31 }
32 
33 /// Resolve the standard library dependencies.
resolve_std<'cfg>( ws: &Workspace<'cfg>, target_data: &RustcTargetData<'cfg>, requested_targets: &[CompileKind], crates: &[String], ) -> CargoResult<(PackageSet<'cfg>, Resolve, ResolvedFeatures)>34 pub fn resolve_std<'cfg>(
35     ws: &Workspace<'cfg>,
36     target_data: &RustcTargetData<'cfg>,
37     requested_targets: &[CompileKind],
38     crates: &[String],
39 ) -> CargoResult<(PackageSet<'cfg>, Resolve, ResolvedFeatures)> {
40     let src_path = detect_sysroot_src_path(target_data)?;
41     let to_patch = [
42         "rustc-std-workspace-core",
43         "rustc-std-workspace-alloc",
44         "rustc-std-workspace-std",
45     ];
46     let patches = to_patch
47         .iter()
48         .map(|&name| {
49             let source_path = SourceId::for_path(&src_path.join("library").join(name))?;
50             let dep = Dependency::parse(name, None, source_path)?;
51             Ok(dep)
52         })
53         .collect::<CargoResult<Vec<_>>>()?;
54     let crates_io_url = crate::sources::CRATES_IO_INDEX.parse().unwrap();
55     let mut patch = HashMap::new();
56     patch.insert(crates_io_url, patches);
57     let members = vec![
58         String::from("library/std"),
59         String::from("library/core"),
60         String::from("library/alloc"),
61         String::from("library/test"),
62     ];
63     let ws_config = crate::core::WorkspaceConfig::Root(crate::core::WorkspaceRootConfig::new(
64         &src_path,
65         &Some(members),
66         /*default_members*/ &None,
67         /*exclude*/ &None,
68         /*custom_metadata*/ &None,
69     ));
70     let virtual_manifest = crate::core::VirtualManifest::new(
71         /*replace*/ Vec::new(),
72         patch,
73         ws_config,
74         /*profiles*/ None,
75         crate::core::Features::default(),
76         None,
77     );
78 
79     let config = ws.config();
80     // This is a delicate hack. In order for features to resolve correctly,
81     // the resolver needs to run a specific "current" member of the workspace.
82     // Thus, in order to set the features for `std`, we need to set `libtest`
83     // to be the "current" member. `libtest` is the root, and all other
84     // standard library crates are dependencies from there. Since none of the
85     // other crates need to alter their features, this should be fine, for
86     // now. Perhaps in the future features will be decoupled from the resolver
87     // and it will be easier to control feature selection.
88     let current_manifest = src_path.join("library/test/Cargo.toml");
89     // TODO: Consider doing something to enforce --locked? Or to prevent the
90     // lock file from being written, such as setting ephemeral.
91     let mut std_ws = Workspace::new_virtual(src_path, current_manifest, virtual_manifest, config)?;
92     // Don't require optional dependencies in this workspace, aka std's own
93     // `[dev-dependencies]`. No need for us to generate a `Resolve` which has
94     // those included because we'll never use them anyway.
95     std_ws.set_require_optional_deps(false);
96     // `test` is not in the default set because it is optional, but it needs
97     // to be part of the resolve in case we do need it.
98     let mut spec_pkgs = Vec::from(crates);
99     spec_pkgs.push("test".to_string());
100     let spec = Packages::Packages(spec_pkgs);
101     let specs = spec.to_package_id_specs(&std_ws)?;
102     let features = match &config.cli_unstable().build_std_features {
103         Some(list) => list.clone(),
104         None => vec![
105             "panic-unwind".to_string(),
106             "backtrace".to_string(),
107             "default".to_string(),
108         ],
109     };
110     let cli_features = CliFeatures::from_command_line(
111         &features, /*all_features*/ false, /*uses_default_features*/ false,
112     )?;
113     let resolve = ops::resolve_ws_with_opts(
114         &std_ws,
115         target_data,
116         requested_targets,
117         &cli_features,
118         &specs,
119         HasDevUnits::No,
120         crate::core::resolver::features::ForceAllTargets::No,
121     )?;
122     Ok((
123         resolve.pkg_set,
124         resolve.targeted_resolve,
125         resolve.resolved_features,
126     ))
127 }
128 
129 /// Generate a list of root `Unit`s for the standard library.
130 ///
131 /// The given slice of crate names is the root set.
generate_std_roots( crates: &[String], std_resolve: &Resolve, std_features: &ResolvedFeatures, kinds: &[CompileKind], package_set: &PackageSet<'_>, interner: &UnitInterner, profiles: &Profiles, ) -> CargoResult<HashMap<CompileKind, Vec<Unit>>>132 pub fn generate_std_roots(
133     crates: &[String],
134     std_resolve: &Resolve,
135     std_features: &ResolvedFeatures,
136     kinds: &[CompileKind],
137     package_set: &PackageSet<'_>,
138     interner: &UnitInterner,
139     profiles: &Profiles,
140 ) -> CargoResult<HashMap<CompileKind, Vec<Unit>>> {
141     // Generate the root Units for the standard library.
142     let std_ids = crates
143         .iter()
144         .map(|crate_name| std_resolve.query(crate_name))
145         .collect::<CargoResult<Vec<PackageId>>>()?;
146     // Convert PackageId to Package.
147     let std_pkgs = package_set.get_many(std_ids)?;
148     // Generate a map of Units for each kind requested.
149     let mut ret = HashMap::new();
150     for pkg in std_pkgs {
151         let lib = pkg
152             .targets()
153             .iter()
154             .find(|t| t.is_lib())
155             .expect("std has a lib");
156         let unit_for = UnitFor::new_normal();
157         // I don't think we need to bother with Check here, the difference
158         // in time is minimal, and the difference in caching is
159         // significant.
160         let mode = CompileMode::Build;
161         let features = std_features.activated_features(pkg.package_id(), FeaturesFor::NormalOrDev);
162 
163         for kind in kinds {
164             let list = ret.entry(*kind).or_insert_with(Vec::new);
165             let profile = profiles.get_profile(
166                 pkg.package_id(),
167                 /*is_member*/ false,
168                 /*is_local*/ false,
169                 unit_for,
170                 mode,
171                 *kind,
172             );
173             list.push(interner.intern(
174                 pkg,
175                 lib,
176                 profile,
177                 *kind,
178                 mode,
179                 features.clone(),
180                 /*is_std*/ true,
181                 /*dep_hash*/ 0,
182             ));
183         }
184     }
185     Ok(ret)
186 }
187 
detect_sysroot_src_path(target_data: &RustcTargetData<'_>) -> CargoResult<PathBuf>188 fn detect_sysroot_src_path(target_data: &RustcTargetData<'_>) -> CargoResult<PathBuf> {
189     if let Some(s) = env::var_os("__CARGO_TESTS_ONLY_SRC_ROOT") {
190         return Ok(s.into());
191     }
192 
193     // NOTE: This is temporary until we figure out how to acquire the source.
194     let src_path = target_data
195         .info(CompileKind::Host)
196         .sysroot
197         .join("lib")
198         .join("rustlib")
199         .join("src")
200         .join("rust");
201     let lock = src_path.join("Cargo.lock");
202     if !lock.exists() {
203         anyhow::bail!(
204             "{:?} does not exist, unable to build with the standard \
205              library, try:\n        rustup component add rust-src",
206             lock
207         );
208     }
209     Ok(src_path)
210 }
211