Skip to content

Commit c49c40b

Browse files
committed
Support per-pkg-target for -Zbuild-std, take two
This is a simplified minimal change, which changes the build-std to iterate over the packages to collect a list of targets std should be built for. Does the iterating over packages part have issues with artifact dependencies? I am not very familiar with artifact deps to know so any help would be appreciated. But it should be fine for now since artifact dependencies don't work with build-std yet. #10444
1 parent 3837689 commit c49c40b

File tree

7 files changed

+220
-46
lines changed

7 files changed

+220
-46
lines changed

src/cargo/core/compiler/standard_lib.rs

+44-21
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::core::{Dependency, PackageId, PackageSet, Resolve, SourceId, Workspac
1010
use crate::ops::{self, Packages};
1111
use crate::util::errors::CargoResult;
1212
use crate::Config;
13+
use std::collections::hash_map::Entry;
1314
use std::collections::{HashMap, HashSet};
1415
use std::path::PathBuf;
1516

@@ -168,7 +169,8 @@ pub fn generate_std_roots(
168169
crates: &[String],
169170
std_resolve: &Resolve,
170171
std_features: &ResolvedFeatures,
171-
kinds: &[CompileKind],
172+
requested_kinds: &[CompileKind],
173+
explicit_host_kind: CompileKind,
172174
package_set: &PackageSet<'_>,
173175
interner: &UnitInterner,
174176
profiles: &Profiles,
@@ -182,41 +184,62 @@ pub fn generate_std_roots(
182184
let std_pkgs = package_set.get_many(std_ids)?;
183185
// Generate a map of Units for each kind requested.
184186
let mut ret = HashMap::new();
185-
for pkg in std_pkgs {
186-
let lib = pkg
187-
.targets()
188-
.iter()
189-
.find(|t| t.is_lib())
190-
.expect("std has a lib");
191-
// I don't think we need to bother with Check here, the difference
192-
// in time is minimal, and the difference in caching is
193-
// significant.
194-
let mode = CompileMode::Build;
195-
let features = std_features.activated_features(pkg.package_id(), FeaturesFor::NormalOrDev);
196-
for kind in kinds {
197-
let list = ret.entry(*kind).or_insert_with(Vec::new);
198-
let unit_for = UnitFor::new_normal(*kind);
187+
188+
// Get information for the list of packages for standard library
189+
let std_pkg_infos: Vec<_> = std_pkgs
190+
.iter()
191+
.map(|pkg| {
192+
let lib = pkg
193+
.targets()
194+
.iter()
195+
.find(|t| t.is_lib())
196+
.expect("std has a lib");
197+
let features =
198+
std_features.activated_features(pkg.package_id(), FeaturesFor::NormalOrDev);
199+
let unit_for = UnitFor::new_normal(explicit_host_kind);
200+
(pkg, lib, unit_for, features)
201+
})
202+
.collect();
203+
204+
// Iterate over all packages over the package set to find all targets requested.
205+
for kind in package_set
206+
.packages()
207+
.flat_map(|pkg| pkg.explicit_kinds(requested_kinds, explicit_host_kind))
208+
{
209+
let e = match ret.entry(kind) {
210+
Entry::Vacant(e) => e,
211+
Entry::Occupied(_) => continue,
212+
};
213+
214+
let units = std_pkg_infos.iter().map(|(pkg, lib, unit_for, features)| {
215+
// I don't think we need to bother with Check here, the difference
216+
// in time is minimal, and the difference in caching is
217+
// significant.
218+
let mode = CompileMode::Build;
199219
let profile = profiles.get_profile(
200220
pkg.package_id(),
201221
/*is_member*/ false,
202222
/*is_local*/ false,
203-
unit_for,
204-
*kind,
223+
*unit_for,
224+
kind,
205225
);
206-
list.push(interner.intern(
226+
interner.intern(
207227
pkg,
208228
lib,
209229
profile,
210-
*kind,
230+
kind,
211231
mode,
212232
features.clone(),
213233
/*is_std*/ true,
214234
/*dep_hash*/ 0,
215235
IsArtifact::No,
216236
None,
217-
));
218-
}
237+
)
238+
});
239+
240+
e.insert(units.collect());
219241
}
242+
220243
Ok(ret)
221244
}
222245

src/cargo/core/package.rs

+22
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,28 @@ impl Package {
185185
self.targets().iter().any(|t| t.is_custom_build())
186186
}
187187

188+
/// Returns explicit kinds either forced by `forced-target` in `Cargo.toml`,
189+
/// fallback to `default-target`, or specified in cli parameters.
190+
pub fn explicit_kinds(
191+
&self,
192+
requested_kinds: &[CompileKind],
193+
explicit_host_kind: CompileKind,
194+
) -> Vec<CompileKind> {
195+
if let Some(k) = self.manifest().forced_kind() {
196+
vec![k]
197+
} else {
198+
requested_kinds
199+
.iter()
200+
.map(|kind| match kind {
201+
CompileKind::Host => {
202+
self.manifest().default_kind().unwrap_or(explicit_host_kind)
203+
}
204+
CompileKind::Target(t) => CompileKind::Target(*t),
205+
})
206+
.collect()
207+
}
208+
}
209+
188210
pub fn map_source(self, to_replace: SourceId, replace_with: SourceId) -> Package {
189211
Package {
190212
inner: Rc::new(PackageInner {

src/cargo/ops/cargo_compile/mod.rs

+2-9
Original file line numberDiff line numberDiff line change
@@ -341,14 +341,6 @@ pub fn create_bcx<'a, 'cfg>(
341341
// assuming `--target $HOST` was specified. See
342342
// `rebuild_unit_graph_shared` for more on why this is done.
343343
let explicit_host_kind = CompileKind::Target(CompileTarget::new(&target_data.rustc.host)?);
344-
let explicit_host_kinds: Vec<_> = build_config
345-
.requested_kinds
346-
.iter()
347-
.map(|kind| match kind {
348-
CompileKind::Host => explicit_host_kind,
349-
CompileKind::Target(t) => CompileKind::Target(*t),
350-
})
351-
.collect();
352344

353345
// Passing `build_config.requested_kinds` instead of
354346
// `explicit_host_kinds` here so that `generate_root_units` can do
@@ -393,7 +385,8 @@ pub fn create_bcx<'a, 'cfg>(
393385
&crates,
394386
std_resolve,
395387
std_features,
396-
&explicit_host_kinds,
388+
&build_config.requested_kinds,
389+
explicit_host_kind,
397390
&pkg_set,
398391
interner,
399392
&profiles,

src/cargo/ops/cargo_compile/unit_generator.rs

+1-14
Original file line numberDiff line numberDiff line change
@@ -111,20 +111,7 @@ impl<'a> UnitGenerator<'a, '_> {
111111
// why this is done. However, if the package has its own
112112
// `package.target` key, then this gets used instead of
113113
// `$HOST`
114-
let explicit_kinds = if let Some(k) = pkg.manifest().forced_kind() {
115-
vec![k]
116-
} else {
117-
self.requested_kinds
118-
.iter()
119-
.map(|kind| match kind {
120-
CompileKind::Host => pkg
121-
.manifest()
122-
.default_kind()
123-
.unwrap_or(self.explicit_host_kind),
124-
CompileKind::Target(t) => CompileKind::Target(*t),
125-
})
126-
.collect()
127-
};
114+
let explicit_kinds = pkg.explicit_kinds(self.requested_kinds, self.explicit_host_kind);
128115

129116
explicit_kinds
130117
.into_iter()

tests/build-std/main.rs

+47
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,50 @@ fn custom_test_framework() {
227227
.build_std_arg("core")
228228
.run();
229229
}
230+
231+
/// like cross-custom but uses per-package-target instead
232+
#[cargo_test(build_std_real)]
233+
fn per_package_target() {
234+
let p = project()
235+
.file(
236+
"Cargo.toml",
237+
r#"
238+
cargo-features = ["per-package-target"]
239+
[package]
240+
name = "foo"
241+
version = "0.1.0"
242+
edition = "2018"
243+
default-target = "custom-target.json"
244+
# [target.custom-target.dependencies] <-- not sure why this doesn't work
245+
[dependencies]
246+
dep = { path = "dep" }
247+
"#,
248+
)
249+
.file(
250+
"src/lib.rs",
251+
"#![no_std] pub fn f() -> u32 { dep::answer() }",
252+
)
253+
.file("dep/Cargo.toml", &basic_manifest("dep", "0.1.0"))
254+
.file("dep/src/lib.rs", "#![no_std] pub fn answer() -> u32 { 42 }")
255+
.file(
256+
"custom-target.json",
257+
r#"
258+
{
259+
"llvm-target": "x86_64-unknown-none-gnu",
260+
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
261+
"arch": "x86_64",
262+
"target-endian": "little",
263+
"target-pointer-width": "64",
264+
"target-c-int-width": "32",
265+
"os": "none",
266+
"linker-flavor": "ld.lld"
267+
}
268+
"#,
269+
)
270+
.build();
271+
272+
p.cargo("build -v")
273+
.build_std_arg("core")
274+
.target_host()
275+
.run();
276+
}

tests/testsuite/mock-std/library/proc_macro/src/lib.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
#![feature(staged_api)]
22
#![stable(since = "1.0.0", feature = "dummy")]
33

4+
45
extern crate proc_macro;
56

7+
// Don't re-export everything in the root so that the mock std can be distinguished from the real one.
68
#[stable(since = "1.0.0", feature = "dummy")]
7-
pub use proc_macro::*;
9+
pub mod exported {
10+
#[stable(since = "1.0.0", feature = "dummy")]
11+
pub use proc_macro::*;
12+
}
813

914
#[stable(since = "1.0.0", feature = "dummy")]
1015
pub fn custom_api() {

tests/testsuite/standard_lib.rs

+98-1
Original file line numberDiff line numberDiff line change
@@ -366,16 +366,113 @@ fn target_proc_macro() {
366366
"src/lib.rs",
367367
r#"
368368
extern crate proc_macro;
369-
pub fn f() {
369+
fn f() {
370370
let _ts = proc_macro::TokenStream::new();
371371
}
372372
"#,
373373
)
374+
.file(
375+
"Cargo.toml",
376+
r#"
377+
[package]
378+
name = "pm"
379+
version = "0.1.0"
380+
[lib]
381+
proc-macro = true
382+
"#,
383+
)
374384
.build();
375385

376386
p.cargo("build -v").build_std(&setup).target_host().run();
377387
}
378388

389+
// We already have `basic` which uses `proc_macro::custom_api()`. This case attempts to use
390+
// `TokenStream` which would error because we are using the sysroot version.
391+
#[cargo_test(build_std_mock)]
392+
fn non_proc_macro_crate_uses_non_sysroot_proc_macro() {
393+
let setup = setup();
394+
395+
let p = project()
396+
.file(
397+
"src/lib.rs",
398+
r#"
399+
extern crate proc_macro;
400+
fn f() {
401+
let _ts = proc_macro::TokenStream::new();
402+
}
403+
"#,
404+
)
405+
.file(
406+
"Cargo.toml",
407+
r#"
408+
[package]
409+
name = "pm"
410+
version = "0.1.0"
411+
"#,
412+
)
413+
.build();
414+
415+
p.cargo("build -v")
416+
.build_std(&setup)
417+
.target_host()
418+
.run_expect_error();
419+
}
420+
421+
#[cargo_test(build_std_mock)]
422+
fn intergrated_proc_macro() {
423+
let setup = setup();
424+
425+
let p = project()
426+
.file(
427+
"src/main.rs",
428+
r#"
429+
fn main() {
430+
println!("The answer is {}", pm::m!());
431+
}
432+
"#,
433+
)
434+
.file(
435+
"pm/src/lib.rs",
436+
r#"
437+
extern crate proc_macro;
438+
use proc_macro::TokenStream;
439+
#[proc_macro]
440+
pub fn m(_item: TokenStream) -> TokenStream {
441+
"42".parse().unwrap()
442+
}
443+
"#,
444+
)
445+
.file(
446+
"Cargo.toml",
447+
r#"
448+
[package]
449+
name = "foo"
450+
version = "0.1.0"
451+
[workspace]
452+
members = ["pm"]
453+
[dependencies]
454+
pm = { path = "./pm" }
455+
"#,
456+
)
457+
.file(
458+
"pm/Cargo.toml",
459+
r#"
460+
[package]
461+
name = "pm"
462+
version = "0.1.0"
463+
[lib]
464+
proc-macro = true
465+
"#,
466+
)
467+
.build();
468+
469+
p.cargo("run -v")
470+
.build_std(&setup)
471+
.with_stdout_contains("The answer is 42")
472+
.target_host()
473+
.run();
474+
}
475+
379476
#[cargo_test(build_std_mock)]
380477
fn bench() {
381478
let setup = setup();

0 commit comments

Comments
 (0)