Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 27 additions & 16 deletions src/cargo/core/compiler/build_context/target_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,7 @@ pub struct RustcTargetData<'gctx> {
target_config: HashMap<CompileTarget, TargetConfig>,
/// Information about the target platform that we're building for.
target_info: HashMap<CompileTarget, TargetInfo>,
manifest_target_kinds: Vec<CompileKind>,
}

impl<'gctx> RustcTargetData<'gctx> {
Expand Down Expand Up @@ -1005,16 +1006,6 @@ impl<'gctx> RustcTargetData<'gctx> {
}
};

let mut res = RustcTargetData {
rustc,
gctx,
requested_kinds: requested_kinds.into(),
host_config,
host_info,
target_config,
target_info,
};

// Get all kinds we currently know about.
//
// For now, targets can only ever come from the root workspace
Expand All @@ -1029,16 +1020,32 @@ impl<'gctx> RustcTargetData<'gctx> {
.iter()
.filter_map(|d| d.artifact()?.target()?.to_compile_kind())
}
let all_kinds = requested_kinds
.iter()
.copied()
.chain(ws.members().flat_map(|p| {
let mut manifest_target_kinds = ws

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit unfortunate that the real target of a package is only figured out during unit generation. As is, this leads to std being resolved with more targets than required if a package has both default and forced targets set.

Right now it would be feasible to do the main resolve, generate its units, then resolve std for the right targets and attach those units. But with #16675, unit generation happens in one step and so will require both resolves ahead of time.

I think that's fine for now as std doesn't actually get built more times than required. per-pkg-target is unstable (as is build-std), and I'd prefer if #16675 is merged before looking for improvements here

.members()
.flat_map(|p| {
p.manifest()
.default_kind()
.into_iter()
.chain(p.manifest().forced_kind())
.chain(artifact_targets(p))
}));
})
.collect::<Vec<_>>();
manifest_target_kinds.sort();
manifest_target_kinds.dedup();
let mut res = RustcTargetData {
rustc,
gctx,
requested_kinds: requested_kinds.into(),
host_config,
host_info,
target_config,
target_info,
manifest_target_kinds: manifest_target_kinds.clone(),
};
let all_kinds = requested_kinds
.iter()
.copied()
.chain(manifest_target_kinds.iter().copied())
.chain(ws.members().flat_map(artifact_targets));
for kind in all_kinds {
res.merge_compile_kind(kind)?;
}
Expand Down Expand Up @@ -1130,4 +1137,8 @@ impl<'gctx> RustcTargetData<'gctx> {
pub fn requested_kinds(&self) -> &[CompileKind] {
&self.requested_kinds
}

pub fn manifest_target_kinds(&self) -> &[CompileKind] {
&self.manifest_target_kinds
}
}
51 changes: 47 additions & 4 deletions src/cargo/core/compiler/standard_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ use crate::util::errors::CargoResult;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;

use super::BuildConfig;

fn std_crates<'a>(crates: &'a [String], default: &'static str, units: &[Unit]) -> HashSet<&'a str> {
let mut crates = HashSet::from_iter(crates.iter().map(|s| s.as_str()));
// This is a temporary hack until there is a more principled way to
Expand Down Expand Up @@ -49,7 +47,6 @@ fn std_crates<'a>(crates: &'a [String], default: &'static str, units: &[Unit]) -
pub fn resolve_std<'gctx>(
ws: &Workspace<'gctx>,
target_data: &mut RustcTargetData<'gctx>,
build_config: &BuildConfig,
crates: &[String],
kinds: &[CompileKind],
) -> CargoResult<(PackageSet<'gctx>, Resolve, ResolvedFeatures)> {
Expand Down Expand Up @@ -93,7 +90,7 @@ pub fn resolve_std<'gctx>(
let mut resolve = ops::resolve_ws_with_opts(
&std_ws,
target_data,
&build_config.requested_kinds,
kinds,
&cli_features,
&specs,
HasDevUnits::No,
Expand All @@ -112,6 +109,52 @@ pub fn resolve_std<'gctx>(
))
}

pub fn resolve_std_kinds<'gctx>(
target_data: &mut RustcTargetData<'gctx>,
requested_kinds: &[CompileKind],
resolve: &Resolve,
) -> CargoResult<Vec<CompileKind>> {
let host_kind_requested = requested_kinds.iter().any(CompileKind::is_host);
let mut kinds = Vec::new();

// With no explicit `--target`, Cargo represents the root package as
// `CompileKind::Host` but internally also loads target data for the host
// triple. Avoid returning both, otherwise build-std would resolve and
// generate duplicate std roots for the same platform.
let mut add_kind =
|target_data: &mut RustcTargetData<'gctx>, kind: CompileKind| -> CargoResult<()> {
if host_kind_requested
&& matches!(kind, CompileKind::Target(_))
&& target_data.rustc.host == target_data.short_name(&kind)
{
return Ok(());
}

target_data.merge_compile_kind(kind)?;
kinds.push(kind);
Ok(())
};

for kind in requested_kinds.iter().copied() {
add_kind(target_data, kind)?;
}
for kind in target_data.manifest_target_kinds().to_vec() {
add_kind(target_data, kind)?;
}
for kind in resolve.iter().flat_map(|pkg_id| {
resolve
.deps(pkg_id)
.flat_map(|(_, deps)| deps.iter())
.filter_map(|dep| dep.artifact()?.target()?.to_compile_kind())
}) {
add_kind(target_data, kind)?;
}

kinds.sort();
kinds.dedup();
Ok(kinds)
}

/// Generates a map of root units for the standard library for each kind requested.
///
/// * `crates` is the arg value from `-Zbuild-std`.
Expand Down
40 changes: 26 additions & 14 deletions src/cargo/ops/cargo_compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,19 +339,20 @@ pub fn create_bcx<'a, 'gctx>(
logger.log(LogMessage::ResolutionFinished { elapsed });
}

let std_resolve_features = if let Some(crates) = &gctx.cli_unstable().build_std {
let (std_package_set, std_resolve, std_features) = standard_lib::resolve_std(
ws,
&mut target_data,
&build_config,
crates,
&build_config.requested_kinds,
)?;
pkg_set.add_set(std_package_set);
Some((std_resolve, std_features))
} else {
None
};
let (std_resolve_features, build_std_kinds) =
if let Some(crates) = &gctx.cli_unstable().build_std {
let std_kinds = standard_lib::resolve_std_kinds(
&mut target_data,
&build_config.requested_kinds,
&resolve,
)?;
let (std_package_set, std_resolve, std_features) =
standard_lib::resolve_std(ws, &mut target_data, crates, &std_kinds)?;
pkg_set.add_set(std_package_set);
(Some((std_resolve, std_features)), Some(std_kinds))
} else {
(None, None)
};

// Find the packages in the resolver that the user wants to build (those
// passed in with `-p` or the defaults from the workspace), and convert
Expand Down Expand Up @@ -479,12 +480,23 @@ pub fn create_bcx<'a, 'gctx>(

let std_roots = if let Some(crates) = gctx.cli_unstable().build_std.as_ref() {
let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
let mut std_kinds = explicit_host_kinds.clone();
std_kinds.extend(
build_std_kinds
.as_ref()
.unwrap()
.iter()
.copied()
.filter(|kind| !kind.is_host()),
);
std_kinds.sort();
std_kinds.dedup();
standard_lib::generate_std_roots(
&crates,
&targeted_root_units,
std_resolve,
std_features,
&explicit_host_kinds,
&std_kinds,
&pkg_set,
interner,
&profiles,
Expand Down
10 changes: 3 additions & 7 deletions src/cargo/ops/cargo_fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,9 @@ pub fn fetch<'a>(

// If -Zbuild-std was passed, download dependencies for the standard library.
if let Some(crates) = &gctx.cli_unstable().build_std {
let (std_package_set, _, _) = standard_lib::resolve_std(
ws,
&mut data,
&build_config,
crates,
&build_config.requested_kinds,
)?;
let std_kinds =
standard_lib::resolve_std_kinds(&mut data, &build_config.requested_kinds, &resolve)?;
let (std_package_set, _, _) = standard_lib::resolve_std(ws, &mut data, crates, &std_kinds)?;
packages.add_set(std_package_set);
}

Expand Down
155 changes: 155 additions & 0 deletions tests/testsuite/standard_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use std::path::{Path, PathBuf};

use crate::prelude::*;
use crate::utils::cross_compile::disabled as cross_compile_disabled;
use cargo_test_support::ProjectBuilder;
use cargo_test_support::cross_compile;
use cargo_test_support::registry::{Dependency, Package};
Expand Down Expand Up @@ -518,6 +519,160 @@ fn depend_same_as_std() {
p.cargo("build -v").build_std(&setup).target_host().run();
}

#[cargo_test(build_std_mock)]
fn artifact_dep_target_builds_std_for_unrequested_target() {

@epage epage Jun 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see https://epage.github.io/dev/pr-style/#c-test (note our contrib guide says this as policy but that goes into more detail)

View changes since the review

if cross_compile_disabled() {
return;
}
let target = cross_compile::alternate();
let setup = setup();

let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.0.1"
edition = "2021"
resolver = "2"

[dependencies]
bindep = {{ path = "bindep", artifact = "bin", target = "{target}" }}
"#,
),
)
.file(
"src/lib.rs",
r#"
pub fn foo() {
std::custom_api();
let _bin = include_bytes!(env!("CARGO_BIN_FILE_BINDEP"));
}
"#,
)
.file(
"bindep/Cargo.toml",
r#"
[package]
name = "bindep"
version = "0.0.1"
edition = "2021"
"#,
)
.file(
"bindep/src/main.rs",
r#"
fn main() {
std::custom_api();
}
"#,
)
.build();

p.cargo("check -v -Z bindeps")
.masquerade_as_nightly_cargo(&["bindeps"])
.build_std(&setup)
.target_host()
.with_stderr_data(str![[r#"
...
[RUNNING] `[..]rustc --crate-name std [..]--target [ALT_TARGET][..]`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add an extra check that the host std is also built?

...
[RUNNING] `[..]rustc --crate-name bindep [..]--target [ALT_TARGET][..]`
...
"#]])
.run();
}

#[cargo_test(build_std_mock)]
fn inactive_artifact_dep_target_does_not_build_std() {
if cross_compile_disabled() {
return;
}
let target = cross_compile::alternate();
let setup = setup();

let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.0.1"
edition = "2021"
resolver = "2"

[dependencies]
bindep = {{ path = "bindep", artifact = "bin", target = "{target}", optional = true }}
"#,
),
)
.file("src/lib.rs", "pub fn foo() { std::custom_api(); }")
.file(
"bindep/Cargo.toml",
r#"
[package]
name = "bindep"
version = "0.0.1"
edition = "2021"
"#,
)
.file("bindep/src/main.rs", "fn main() { std::custom_api(); }")
.build();

p.cargo("check -v -Z bindeps")
.masquerade_as_nightly_cargo(&["bindeps"])
.build_std(&setup)
.target_host()
.with_stderr_does_not_contain(str![[r#"
[RUNNING] `[..]rustc --crate-name std [..]--target [ALT_TARGET][..]`
"#]])
.run();
}

#[cargo_test(build_std_mock)]
fn per_package_target_builds_std_for_manifest_target() {
if cross_compile_disabled() {
return;
}
let target = cross_compile::alternate();
let setup = setup();

let p = project()
.file(
"Cargo.toml",
&format!(
r#"
cargo-features = ["per-package-target"]

[package]
name = "foo"
version = "0.0.1"
edition = "2021"
default-target = "{target}"
"#,
),
)
.file("src/lib.rs", "pub fn foo() { std::custom_api(); }")
.build();

let mut cargo = p.cargo("check -v");
enable_build_std(&mut cargo, &setup);
cargo
.arg("-Zbuild-std")
.masquerade_as_nightly_cargo(&["build-std", "per-package-target"])
.with_stderr_data(str![[r#"
...
[RUNNING] `[..]rustc --crate-name std [..]--target [ALT_TARGET][..]`
...
[RUNNING] `[..]rustc --crate-name foo [..]--target [ALT_TARGET][..]`
...
"#]])
.run();
}

#[cargo_test(build_std_mock)]
fn test() {
let setup = setup();
Expand Down