Skip to content

Commit a867d8e

Browse files
committed
allow build-std to be configured per-target in .cargo/config.toml
1 parent 2251525 commit a867d8e

File tree

8 files changed

+422
-126
lines changed

8 files changed

+422
-126
lines changed

src/cargo/core/compiler/build_context/target_info.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,24 @@ impl<'gctx> RustcTargetData<'gctx> {
10961096
}
10971097
}
10981098

1099+
/// Returns an iterator that contains all possible non-host targets for this session,
1100+
/// including those set by artifact dependencies or per-pkg-targets.
1101+
///
1102+
/// Should be called only after workspace resolution to ensure that all targets have
1103+
/// been collected.
1104+
pub fn all_kinds(&self) -> impl Iterator<Item = CompileKind> + '_ {
1105+
self.target_config.keys().copied().map(CompileKind::Target)
1106+
}
1107+
1108+
/// Returns whether `build_std` was enabled in any of the targets we have queried.
1109+
pub fn build_std(&self) -> bool {
1110+
self.host_config.build_std.is_some()
1111+
|| self
1112+
.target_config
1113+
.values()
1114+
.any(|config| config.build_std.is_some())
1115+
}
1116+
10991117
pub fn get_unsupported_std_targets(&self) -> Vec<&str> {
11001118
let mut unsupported = Vec::new();
11011119
for (target, target_info) in &self.target_info {

src/cargo/core/compiler/standard_lib.rs

Lines changed: 115 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -10,48 +10,78 @@ use crate::core::{PackageId, PackageSet, Resolve, Workspace};
1010
use crate::ops::{self, Packages};
1111
use crate::util::errors::CargoResult;
1212

13+
use std::borrow::Borrow;
1314
use std::collections::{HashMap, HashSet};
15+
use std::hash::Hash;
1416
use std::path::PathBuf;
1517

1618
use super::BuildConfig;
1719

18-
fn 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+
fn add_default_std_crates<T: Borrow<str> + From<&'static str> + Eq + Hash>(
21+
crates: &mut HashSet<T>,
22+
default: &'static str,
23+
include_libtest: bool,
24+
) {
2025
// This is a temporary hack until there is a more principled way to
2126
// declare dependencies in Cargo.toml.
2227
if crates.is_empty() {
23-
crates.insert(default);
28+
crates.insert(default.into());
2429
}
2530
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");
31+
crates.insert("core".into());
32+
crates.insert("alloc".into());
33+
crates.insert("proc_macro".into());
34+
crates.insert("panic_unwind".into());
35+
crates.insert("compiler_builtins".into());
36+
if include_libtest {
37+
crates.insert("test".into());
3838
}
3939
} else if crates.contains("core") {
40-
crates.insert("compiler_builtins");
40+
crates.insert("compiler_builtins".into());
4141
}
42+
}
43+
44+
fn std_crates(
45+
target_data: &RustcTargetData<'_>,
46+
cli_crates: Option<&[String]>,
47+
include_libtest: bool,
48+
) -> HashMap<CompileKind, HashSet<String>> {
49+
let mut map = HashMap::new();
50+
for kind in target_data.all_kinds().chain([CompileKind::Host]) {
51+
let requested_crates = if let Some(crates) = &target_data.target_config(kind).build_std {
52+
crates.val.as_slice()
53+
} else if let Some(cli_crates) = cli_crates {
54+
cli_crates
55+
} else {
56+
continue;
57+
};
4258

43-
crates
59+
let mut actual_crates = requested_crates
60+
.iter()
61+
.map(Clone::clone)
62+
.collect::<HashSet<_>>();
63+
add_default_std_crates(
64+
&mut actual_crates,
65+
if target_data.info(kind).maybe_support_std() {
66+
"std"
67+
} else {
68+
"core"
69+
},
70+
include_libtest,
71+
);
72+
map.insert(kind, actual_crates);
73+
}
74+
map
4475
}
4576

4677
/// Resolve the standard library dependencies.
4778
///
48-
/// * `crates` is the arg value from `-Zbuild-std`.
79+
/// * `cli_crates` is the arg value from `-Zbuild-std`.
4980
pub fn resolve_std<'gctx>(
5081
ws: &Workspace<'gctx>,
5182
target_data: &mut RustcTargetData<'gctx>,
5283
build_config: &BuildConfig,
53-
crates: &[String],
54-
kinds: &[CompileKind],
84+
cli_crates: Option<&[String]>,
5585
) -> CargoResult<(PackageSet<'gctx>, Resolve, ResolvedFeatures)> {
5686
if build_config.build_plan {
5787
ws.gctx()
@@ -69,31 +99,32 @@ pub fn resolve_std<'gctx>(
6999
// `[dev-dependencies]`. No need for us to generate a `Resolve` which has
70100
// those included because we'll never use them anyway.
71101
std_ws.set_require_optional_deps(false);
102+
let mut build_std_features = HashSet::new();
72103
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" }, &[]);
104+
let mut set = HashSet::new();
105+
for (compile_kind, new_set) in std_crates(target_data, cli_crates, false) {
106+
set.extend(new_set);
107+
if let Some(features) = &target_data.target_config(compile_kind).build_std_features {
108+
build_std_features.extend(features.val.as_slice().iter().map(String::clone));
109+
} else if let Some(features) = &gctx.cli_unstable().build_std_features {
110+
build_std_features.extend(features.iter().map(String::clone));
111+
} else {
112+
build_std_features
113+
.extend(["panic-unwind", "backtrace", "default"].map(String::from));
114+
}
115+
}
81116
// `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());
117+
// to be part of the resolve in case we do need it for `libtest`.
118+
set.insert("sysroot".into());
119+
let specs = Packages::Packages(set.into_iter().collect());
85120
specs.to_package_id_specs(&std_ws)?
86121
};
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-
};
122+
123+
let build_std_features = build_std_features.into_iter().collect::<Vec<_>>();
95124
let cli_features = CliFeatures::from_command_line(
96-
&features, /*all_features*/ false, /*uses_default_features*/ false,
125+
&build_std_features,
126+
/*all_features*/ false,
127+
/*uses_default_features*/ false,
97128
)?;
98129
let dry_run = false;
99130
let resolve = ops::resolve_ws_with_opts(
@@ -118,76 +149,62 @@ pub fn resolve_std<'gctx>(
118149
/// * `crates` is the arg value from `-Zbuild-std`.
119150
/// * `units` is the root units of the build.
120151
pub fn generate_std_roots(
121-
crates: &[String],
152+
cli_crates: Option<&[String]>,
122153
units: &[Unit],
123154
std_resolve: &Resolve,
124155
std_features: &ResolvedFeatures,
125-
kinds: &[CompileKind],
126156
package_set: &PackageSet<'_>,
127157
interner: &UnitInterner,
128158
profiles: &Profiles,
129159
target_data: &RustcTargetData<'_>,
130160
) -> CargoResult<HashMap<CompileKind, Vec<Unit>>> {
131161
// Generate a map of Units for each kind requested.
132-
let mut ret = HashMap::new();
133-
let (maybe_std, maybe_core): (Vec<&CompileKind>, Vec<_>) = kinds
162+
let mut ret: HashMap<CompileKind, Vec<Unit>> = HashMap::new();
163+
// Only build libtest if it looks like it is needed (libtest depends on libstd)
164+
// If we know what units we're building, we can filter for libtest depending on the jobs.
165+
let include_libtest = units
134166
.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-
}
167+
.any(|unit| unit.mode.is_rustc_test() && unit.target.harness());
168+
let crates = std_crates(target_data, cli_crates, include_libtest);
154169

155-
Ok(ret)
156-
}
170+
let all_crates = crates
171+
.values()
172+
.flat_map(|set| set)
173+
.map(|s| s.as_str())
174+
.collect::<HashSet<_>>();
175+
// collect as `Vec` for stable order
176+
let all_crates = all_crates.into_iter().collect::<Vec<_>>();
177+
let std_ids = all_crates
178+
.iter()
179+
.map(|crate_name| {
180+
std_resolve
181+
.query(crate_name)
182+
.map(|pkg_id| (pkg_id, *crate_name))
183+
})
184+
.collect::<CargoResult<HashMap<PackageId, &str>>>()?;
185+
let std_pkgs = package_set.get_many(std_ids.keys().copied())?;
157186

158-
fn 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)
187+
// a map of the requested std crate and its actual package.
188+
let std_pkgs = std_pkgs
172189
.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)?;
190+
.map(|pkg| (*std_ids.get(&pkg.package_id()).unwrap(), *pkg))
191+
.collect::<HashMap<_, _>>();
176192

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);
193+
for (&kind, crates) in &crates {
194+
let list = ret.entry(kind).or_default();
195+
for krate in crates {
196+
let pkg = std_pkgs.get(krate.as_str()).unwrap();
197+
let lib = pkg
198+
.targets()
199+
.iter()
200+
.find(|t| t.is_lib())
201+
.expect("std has a lib");
202+
// I don't think we need to bother with Check here, the difference
203+
// in time is minimal, and the difference in caching is
204+
// significant.
205+
let mode = CompileMode::Build;
206+
let features =
207+
std_features.activated_features(pkg.package_id(), FeaturesFor::NormalOrDev);
191208
let unit_for = UnitFor::new_normal(kind);
192209
let profile = profiles.get_profile(
193210
pkg.package_id(),
@@ -213,7 +230,8 @@ fn generate_roots(
213230
));
214231
}
215232
}
216-
Ok(())
233+
234+
Ok(ret)
217235
}
218236

219237
fn detect_sysroot_src_path(target_data: &RustcTargetData<'_>) -> CargoResult<PathBuf> {

src/cargo/core/compiler/unit_dependencies.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -177,16 +177,19 @@ fn attach_std_deps(
177177
let mut found = false;
178178
for (unit, deps) in state.unit_dependencies.iter_mut() {
179179
if !unit.kind.is_host() && !unit.mode.is_run_custom_build() {
180-
deps.extend(std_roots[&unit.kind].iter().map(|unit| UnitDep {
181-
unit: unit.clone(),
182-
unit_for: UnitFor::new_normal(unit.kind),
183-
extern_crate_name: unit.pkg.name(),
184-
dep_name: None,
185-
// TODO: Does this `public` make sense?
186-
public: true,
187-
noprelude: true,
188-
}));
189-
found = true;
180+
// only attach roots for which the user has requested `build-std` for.
181+
if let Some(roots) = std_roots.get(&unit.kind) {
182+
deps.extend(roots.iter().map(|unit| UnitDep {
183+
unit: unit.clone(),
184+
unit_for: UnitFor::new_normal(unit.kind),
185+
extern_crate_name: unit.pkg.name(),
186+
dep_name: None,
187+
// TODO: Does this `public` make sense?
188+
public: true,
189+
noprelude: true,
190+
}));
191+
found = true;
192+
}
190193
}
191194
}
192195
// And also include the dependencies of the standard library itself. Don't

src/cargo/ops/cargo_compile/mod.rs

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -287,13 +287,13 @@ pub fn create_bcx<'a, 'gctx>(
287287
resolved_features,
288288
} = resolve;
289289

290-
let std_resolve_features = if let Some(crates) = &gctx.cli_unstable().build_std {
290+
let build_std = gctx.cli_unstable().build_std.is_some() || target_data.build_std();
291+
let std_resolve_features = if build_std {
291292
let (std_package_set, std_resolve, std_features) = standard_lib::resolve_std(
292293
ws,
293294
&mut target_data,
294295
&build_config,
295-
crates,
296-
&build_config.requested_kinds,
296+
gctx.cli_unstable().build_std.as_deref(),
297297
)?;
298298
pkg_set.add_set(std_package_set);
299299
Some((std_resolve, std_features))
@@ -354,14 +354,6 @@ pub fn create_bcx<'a, 'gctx>(
354354
// assuming `--target $HOST` was specified. See
355355
// `rebuild_unit_graph_shared` for more on why this is done.
356356
let explicit_host_kind = CompileKind::Target(CompileTarget::new(&target_data.rustc.host)?);
357-
let explicit_host_kinds: Vec<_> = build_config
358-
.requested_kinds
359-
.iter()
360-
.map(|kind| match kind {
361-
CompileKind::Host => explicit_host_kind,
362-
CompileKind::Target(t) => CompileKind::Target(*t),
363-
})
364-
.collect();
365357

366358
// Passing `build_config.requested_kinds` instead of
367359
// `explicit_host_kinds` here so that `generate_root_units` can do
@@ -398,14 +390,13 @@ pub fn create_bcx<'a, 'gctx>(
398390
Vec::new()
399391
};
400392

401-
let std_roots = if let Some(crates) = gctx.cli_unstable().build_std.as_ref() {
393+
let std_roots = if build_std {
402394
let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
403395
standard_lib::generate_std_roots(
404-
&crates,
396+
gctx.cli_unstable().build_std.as_deref(),
405397
&units,
406398
std_resolve,
407399
std_features,
408-
&explicit_host_kinds,
409400
&pkg_set,
410401
interner,
411402
&profiles,

0 commit comments

Comments
 (0)