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