diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index 01f424adc62..2c73f4ea0df 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -3,7 +3,7 @@ use crate::core::compiler::UnitInterner; use crate::core::compiler::unit_dependencies::IsArtifact; use crate::core::compiler::{CompileKind, CompileMode, RustcTargetData, Unit}; -use crate::core::profiles::{Profiles, UnitFor}; +use crate::core::profiles::{PanicStrategy, Profiles, UnitFor}; use crate::core::resolver::HasDevUnits; use crate::core::resolver::features::{CliFeatures, FeaturesFor, ResolvedFeatures}; use crate::core::{PackageId, PackageSet, Resolve, Workspace}; @@ -15,7 +15,12 @@ use std::path::PathBuf; use super::BuildConfig; -fn std_crates<'a>(crates: &'a [String], default: &'static str, units: &[Unit]) -> HashSet<&'a str> { +fn std_crates<'a>( + crates: &'a [String], + default: &'static str, + units: &[Unit], + profiles: &Profiles, +) -> 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 // declare dependencies in Cargo.toml. @@ -26,7 +31,7 @@ fn std_crates<'a>(crates: &'a [String], default: &'static str, units: &[Unit]) - crates.insert("core"); crates.insert("alloc"); crates.insert("proc_macro"); - crates.insert("panic_unwind"); + crates.insert("panic_abort"); crates.insert("compiler_builtins"); // Only build libtest if it looks like it is needed (libtest depends on libstd) // If we know what units we're building, we can filter for libtest depending on the jobs. @@ -36,6 +41,13 @@ fn std_crates<'a>(crates: &'a [String], default: &'static str, units: &[Unit]) - { crates.insert("test"); } + // Conditionally build `panic_unwind` based on the user's profile settings. + // NOTE: Base profile is enough here since `panic` profile cannot be + // overridden. See `validate_profile_override`. + let profile = profiles.base_profile(); + if profile.panic == PanicStrategy::Unwind { + crates.insert("panic_unwind"); + } } else if crates.contains("core") { crates.insert("compiler_builtins"); } @@ -63,29 +75,37 @@ pub fn resolve_std<'gctx>( // `[dev-dependencies]`. No need for us to generate a `Resolve` which has // those included because we'll never use them anyway. std_ws.set_require_optional_deps(false); - let specs = { + let profiles = Profiles::new(ws, build_config.requested_profile)?; + let (specs, build_panic_unwind) = { // If there is anything looks like needing std, resolve with it. - // If not, we assume only `core` maye be needed, as `core the most fundamental crate. + // If not, we assume only `core` may be needed, as `core` is the most fundamental crate. // // This may need a UI overhaul if `build-std` wants to fully support multi-targets. let maybe_std = kinds .iter() .any(|kind| target_data.info(*kind).maybe_support_std()); - let mut crates = std_crates(crates, if maybe_std { "std" } else { "core" }, &[]); + let mut crates = std_crates( + crates, + if maybe_std { "std" } else { "core" }, + &[], + &profiles, + ); + let build_panic_unwind = crates.contains("panic_unwind"); // `sysroot` is not in the default set because it is optional, but it needs // to be part of the resolve in case we do need it or `libtest`. crates.insert("sysroot"); let specs = Packages::Packages(crates.into_iter().map(Into::into).collect()); - specs.to_package_id_specs(&std_ws)? + (specs.to_package_id_specs(&std_ws)?, build_panic_unwind) }; - let features = match &gctx.cli_unstable().build_std_features { + let mut features = match &gctx.cli_unstable().build_std_features { Some(list) => list.clone(), - None => vec![ - "panic-unwind".to_string(), - "backtrace".to_string(), - "default".to_string(), - ], + None => vec!["backtrace".to_string(), "default".to_string()], }; + + if build_panic_unwind { + features.push("panic-unwind".to_string()); + } + let cli_features = CliFeatures::from_command_line( &features, /*all_features*/ false, /*uses_default_features*/ false, )?; @@ -167,7 +187,7 @@ fn generate_roots( profiles: &Profiles, target_data: &RustcTargetData<'_>, ) -> CargoResult<()> { - let std_ids = std_crates(crates, default, units) + let std_ids = std_crates(crates, default, units, profiles) .iter() .map(|crate_name| std_resolve.query(crate_name)) .collect::>>()?; diff --git a/tests/build-std/main.rs b/tests/build-std/main.rs index 4d46bfa4322..62e7cc2da8b 100644 --- a/tests/build-std/main.rs +++ b/tests/build-std/main.rs @@ -505,6 +505,28 @@ fn default_features_still_included_with_extra_build_std_features() { .run(); } +#[cargo_test(build_std_real)] +fn duplicate_lang_item_with_panic_abort() { + // This is a regression test to ensure that rustc doesn't load `panic_abort` + // from the sysroot. See rust-lang/cargo#7359 + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + edition = "2021" + + [profile.dev] + panic = 'abort' + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check").build_std_arg("std").run(); +} + pub trait CargoProjectExt { /// Creates a `ProcessBuilder` to run cargo. /// diff --git a/tests/testsuite/mock-std/library/Cargo.toml b/tests/testsuite/mock-std/library/Cargo.toml index f44d7bef23a..ab040518e49 100644 --- a/tests/testsuite/mock-std/library/Cargo.toml +++ b/tests/testsuite/mock-std/library/Cargo.toml @@ -1,3 +1,5 @@ +cargo-features = ["profile-rustflags"] + [workspace] resolver = "1" members = [ @@ -5,6 +7,12 @@ members = [ "sysroot", ] +[profile.dev.package.panic_abort] +rustflags = ["-Cpanic=abort"] + +[profile.release.package.panic_abort] +rustflags = ["-Cpanic=abort"] + [patch.crates-io] rustc-std-workspace-core = { path = 'rustc-std-workspace-core' } rustc-std-workspace-alloc = { path = 'rustc-std-workspace-alloc' } diff --git a/tests/testsuite/mock-std/library/panic_abort/Cargo.toml b/tests/testsuite/mock-std/library/panic_abort/Cargo.toml new file mode 100644 index 00000000000..9c94210270b --- /dev/null +++ b/tests/testsuite/mock-std/library/panic_abort/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "panic_abort" +version = "0.1.0" +edition = "2018" + +[dependencies] diff --git a/tests/testsuite/mock-std/library/panic_abort/src/lib.rs b/tests/testsuite/mock-std/library/panic_abort/src/lib.rs new file mode 100644 index 00000000000..f4550042c52 --- /dev/null +++ b/tests/testsuite/mock-std/library/panic_abort/src/lib.rs @@ -0,0 +1,5 @@ +#![feature(panic_abort, panic_runtime)] +#![panic_runtime] +#![no_std] + +extern crate panic_abort; diff --git a/tests/testsuite/mock-std/library/std/Cargo.toml b/tests/testsuite/mock-std/library/std/Cargo.toml index 2a31b514709..1088bc26d09 100644 --- a/tests/testsuite/mock-std/library/std/Cargo.toml +++ b/tests/testsuite/mock-std/library/std/Cargo.toml @@ -6,7 +6,11 @@ edition = "2018" [dependencies] registry-dep-using-alloc = { version = "*", features = ['mockbuild'] } +panic_unwind = { path = "../panic_unwind", optional = true } +panic_abort = { path = "../panic_abort" } dep_test = { path = "../../dep_test" } [features] +default = [] +panic-unwind = ["dep:panic_unwind"] feature1 = [] diff --git a/tests/testsuite/mock-std/library/sysroot/Cargo.toml b/tests/testsuite/mock-std/library/sysroot/Cargo.toml index 615c894dbf8..d3d2e490020 100644 --- a/tests/testsuite/mock-std/library/sysroot/Cargo.toml +++ b/tests/testsuite/mock-std/library/sysroot/Cargo.toml @@ -9,7 +9,7 @@ std = { path = "../std" } test = { path = "../test" } [features] -panic-unwind = [] +panic-unwind = ["std/panic-unwind"] backtrace = [] feature1 = ["std/feature1"] default = [] diff --git a/tests/testsuite/standard_lib.rs b/tests/testsuite/standard_lib.rs index 1c8a9b2e4f0..90d48f03512 100644 --- a/tests/testsuite/standard_lib.rs +++ b/tests/testsuite/standard_lib.rs @@ -450,6 +450,7 @@ fn build_std_with_no_arg_for_core_only_target() { [COMPILING] dep_test v0.1.0 ([..]/dep_test) [COMPILING] compiler_builtins v0.1.0 ([..]/library/compiler_builtins) [COMPILING] proc_macro v0.1.0 ([..]/library/proc_macro) +[COMPILING] panic_abort v0.1.0 ([..]/library/panic_abort) [COMPILING] panic_unwind v0.1.0 ([..]/library/panic_unwind) [COMPILING] rustc-std-workspace-core v1.9.0 ([..]/library/rustc-std-workspace-core) [COMPILING] foo v0.0.1 ([ROOT]/foo) @@ -467,6 +468,7 @@ fn build_std_with_no_arg_for_core_only_target() { [RUNNING] `[..]rustc --crate-name core [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name dep_test [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name proc_macro [..]--target [HOST_TARGET][..]` +[RUNNING] `[..]rustc --crate-name panic_abort [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name panic_unwind [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name compiler_builtins [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name rustc_std_workspace_core [..]--target [HOST_TARGET][..]` @@ -890,3 +892,187 @@ fn std_build_script_metadata_propagate_to_user() { p.cargo("check").build_std(&setup).target_host().run(); } + +#[cargo_test(build_std_mock)] +fn panic_unwind_no_std_build() { + // If panic is set to unwind and Cargo is not building std + // 1) neither of the panic runtimes is built + // 2) rustc throw an error (for targets that aren't rlibs) + let setup = setup(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + edition = "2024" + [lib] + crate-type = ["dylib"] + [profile.dev] + panic = 'unwind' + "#, + ) + .file("src/lib.rs", "#![no_std]") + .build(); + + p.cargo("check") + .build_std_arg(&setup, "core") + .target_host() + .with_stderr_does_not_contain("[COMPILING] panic_abort [..]") + .with_stderr_does_not_contain("[COMPILING] panic_unwind [..]") + .with_stderr_contains("[ERROR] unwinding panics are not supported without std") + .with_status(101) + .run(); +} + +#[cargo_test(build_std_mock)] +fn panic_not_set_build_std() { + // If Cargo is building std and panic is not specified in the profile + // 1) enable the `panic-unwind` feature to build `panic_unwind` + // 2) build `panic_abort` + let setup = setup(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + edition = "2024" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -v") + .build_std_arg(&setup, "std") + .with_stderr_contains("[COMPILING] panic_abort [..]") + .with_stderr_contains("[COMPILING] panic_unwind [..]") + .with_stderr_contains(r#"[RUNNING] `[..]rustc --crate-name std [..]panic-unwind[..]`"#) + .with_status(0) + .run(); +} + +#[cargo_test(build_std_mock)] +fn panic_unwind_build_std() { + // If Cargo is building std and panic is set to unwind + // 1) enable the `panic-unwind` feature to build `panic_unwind` + // 2) build `panic_abort` + let setup = setup(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + edition = "2024" + [profile.dev] + panic = 'unwind' + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -v") + .build_std_arg(&setup, "std") + .with_stderr_contains("[COMPILING] panic_abort [..]") + .with_stderr_contains("[COMPILING] panic_unwind [..]") + .with_stderr_contains(r#"[RUNNING] `[..]rustc --crate-name std [..]panic-unwind[..]`"#) + .run(); +} + +#[cargo_test(build_std_mock)] +fn panic_abort_build_std() { + // If Cargo is building std and panic is set to abort + // 1) build `panic_abort` only + // 2) pass `-Cpanic=abort` + let setup = setup(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + edition = "2024" + [profile.dev] + panic = 'abort' + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -v") + .build_std_arg(&setup, "std") + .target_host() + .with_stderr_contains("[COMPILING] panic_abort [..]") + .with_stderr_does_not_contain("[COMPILING] panic_unwind [..]") + .with_stderr_contains("[RUNNING] `[..]rustc --crate-name foo [..] -C panic=abort [..]") + .run(); +} + +#[cargo_test(build_std_mock)] +fn panic_immediate_abort_build_std() { + // If Cargo is building std and panic is set to immediate-abort + // 1) build `panic_abort` only + // 2) pass `-Cpanic=immediate-abort` + let setup = setup(); + + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["panic-immediate-abort"] + [package] + name = "foo" + edition = "2024" + [profile.dev] + panic = 'immediate-abort' + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -v") + .build_std_arg(&setup, "std") + .target_host() + .with_stderr_contains("[COMPILING] panic_abort [..]") + .with_stderr_does_not_contain("[COMPILING] panic_unwind [..]") + .with_stderr_contains( + "[RUNNING] `[..]rustc --crate-name foo [..] -C panic=immediate-abort [..]", + ) + .run(); +} + +#[cargo_test(build_std_mock)] +fn panic_abort_test_bench() { + let setup = setup(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + edition = "2024" + [profile.test] + panic = 'abort' + [profile.bench] + panic = "abort" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .build_std_arg(&setup, "std") + .target_host() + .with_stderr_contains("[COMPILING] panic_unwind [..]") + .with_stderr_contains( + "[WARNING] Cargo.toml: `panic` setting is ignored for `bench` profile", + ) + .with_stderr_contains("[WARNING] Cargo.toml: `panic` setting is ignored for `test` profile") + .run(); +}