diff --git a/Cargo.lock b/Cargo.lock index 48a510250f2..cc1a32fff4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -553,7 +553,7 @@ dependencies = [ [[package]] name = "cargo-util" -version = "0.2.30" +version = "0.3.0" dependencies = [ "anyhow", "core-foundation", diff --git a/Cargo.toml b/Cargo.toml index 1af21572fb6..ad5fae82370 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ cargo-credential-wincred = { version = "0.4.23", path = "credential/cargo-creden cargo-platform = { path = "crates/cargo-platform", version = "0.3.3" } cargo-test-macro = { version = "0.4.12", path = "crates/cargo-test-macro" } cargo-test-support = { version = "0.11.3", path = "crates/cargo-test-support" } -cargo-util = { version = "0.2.30", path = "crates/cargo-util" } +cargo-util = { version = "0.3.0", path = "crates/cargo-util" } cargo-util-schemas = { version = "0.14.2", path = "crates/cargo-util-schemas" } cargo-util-terminal = { version = "0.1.0", path = "crates/cargo-util-terminal" } cargo_metadata = "0.23.1" diff --git a/crates/cargo-util/Cargo.toml b/crates/cargo-util/Cargo.toml index c39c9c91749..eeaec961614 100644 --- a/crates/cargo-util/Cargo.toml +++ b/crates/cargo-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-util" -version = "0.2.30" +version = "0.3.0" rust-version = "1.95" # MSRV:1 edition.workspace = true license.workspace = true diff --git a/crates/cargo-util/src/process_builder.rs b/crates/cargo-util/src/process_builder.rs index 2b9a735d865..cf2da2c2112 100644 --- a/crates/cargo-util/src/process_builder.rs +++ b/crates/cargo-util/src/process_builder.rs @@ -108,9 +108,12 @@ impl ProcessBuilder { } /// (chainable) Adds multiple `args` to the args list. - pub fn args>(&mut self, args: &[T]) -> &mut ProcessBuilder { - self.args - .extend(args.iter().map(|t| t.as_ref().to_os_string())); + pub fn args(&mut self, args: I) -> &mut ProcessBuilder + where + I: IntoIterator, + T: Into, + { + self.args.extend(args.into_iter().map(Into::into)); self } diff --git a/src/cargo/core/compiler/build_runner/mod.rs b/src/cargo/core/compiler/build_runner/mod.rs index b30fd1ec29a..e6acd36b4a3 100644 --- a/src/cargo/core/compiler/build_runner/mod.rs +++ b/src/cargo/core/compiler/build_runner/mod.rs @@ -244,7 +244,15 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { let mut unstable_opts = false; let mut args = compiler::extern_args(&self, unit, &mut unstable_opts)?; args.extend(compiler::lib_search_paths(&self, unit)?); + args.extend(compiler::opt_level_args(unit).map(Into::into)); + args.extend(compiler::panic_strategy_args(unit).map(Into::into)); args.extend(compiler::lto_args(&self, unit)); + args.extend(compiler::codegen_units_args(unit).map(Into::into)); + args.extend(compiler::debuginfo_args(&self, unit).map(Into::into)); + args.extend(compiler::debug_assertions_overflow_checks_args(unit).map(Into::into)); + args.extend(compiler::rpath_args(unit).map(Into::into)); + args.extend(compiler::add_codegen_linker(&self, unit)?); + args.extend(compiler::strip_args(unit).map(Into::into)); args.extend(compiler::features_args(unit)); args.extend(compiler::check_cfg_args(unit)); diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index dfe4802ceed..55a18c94909 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -808,7 +808,7 @@ fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult if let Some(args) = build_runner.bcx.extra_args_for(unit) { base.args(args); } - base.args(&unit.rustflags); + base.args(unit.rustflags.as_ref()); if gctx.cli_unstable().binary_dep_depinfo { base.arg("-Z").arg("binary-dep-depinfo"); } @@ -970,7 +970,7 @@ fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResu if let Some(args) = build_runner.bcx.extra_args_for(unit) { rustdoc.args(args); } - rustdoc.args(&unit.rustdocflags); + rustdoc.args(unit.rustdocflags.as_ref()); if !crate_version_flag_already_present(&rustdoc) { append_crate_version_flag(unit, &mut rustdoc); @@ -1231,17 +1231,9 @@ fn build_base_args( let bcx = build_runner.bcx; let Profile { - ref opt_level, codegen_backend, - codegen_units, - debuginfo, - debug_assertions, - split_debuginfo, - overflow_checks, - rpath, ref panic, incremental, - strip, rustflags: profile_rustflags, trim_paths, hint_mostly_unused: profile_hint_mostly_unused, @@ -1317,16 +1309,9 @@ fn build_base_args( cmd.arg("-C").arg("prefer-dynamic"); } - if opt_level.as_str() != "0" { - cmd.arg("-C").arg(&format!("opt-level={}", opt_level)); - } + cmd.args(opt_level_args(unit)); - if *panic != PanicStrategy::Unwind { - cmd.arg("-C").arg(format!("panic={}", panic)); - } - if *panic == PanicStrategy::ImmediateAbort { - cmd.arg("-Z").arg("unstable-options"); - } + cmd.args(panic_strategy_args(unit)); cmd.args(<o_args(build_runner, unit)); @@ -1334,31 +1319,9 @@ fn build_base_args( cmd.arg("-Z").arg(&format!("codegen-backend={}", backend)); } - if let Some(n) = codegen_units { - cmd.arg("-C").arg(&format!("codegen-units={}", n)); - } + cmd.args(codegen_units_args(unit)); - let debuginfo = debuginfo.into_inner(); - // Shorten the number of arguments if possible. - if debuginfo != TomlDebugInfo::None { - cmd.arg("-C").arg(format!("debuginfo={debuginfo}")); - // This is generally just an optimization on build time so if we don't - // pass it then it's ok. The values for the flag (off, packed, unpacked) - // may be supported or not depending on the platform, so availability is - // checked per-value. For example, at the time of writing this code, on - // Windows the only stable valid value for split-debuginfo is "packed", - // while on Linux "unpacked" is also stable. - if let Some(split) = split_debuginfo { - if build_runner - .bcx - .target_data - .info(unit.kind) - .supports_debuginfo_split(split) - { - cmd.arg("-C").arg(format!("split-debuginfo={split}")); - } - } - } + cmd.args(debuginfo_args(build_runner, unit)); if let Some(trim_paths) = trim_paths { trim_paths_args(cmd, build_runner, unit, &trim_paths)?; @@ -1367,26 +1330,7 @@ fn build_base_args( cmd.args(unit.pkg.manifest().lint_rustflags()); cmd.args(&profile_rustflags); - // `-C overflow-checks` is implied by the setting of `-C debug-assertions`, - // so we only need to provide `-C overflow-checks` if it differs from - // the value of `-C debug-assertions` we would provide. - if opt_level.as_str() != "0" { - if debug_assertions { - cmd.args(&["-C", "debug-assertions=on"]); - if !overflow_checks { - cmd.args(&["-C", "overflow-checks=off"]); - } - } else if overflow_checks { - cmd.args(&["-C", "overflow-checks=on"]); - } - } else if !debug_assertions { - cmd.args(&["-C", "debug-assertions=off"]); - if overflow_checks { - cmd.args(&["-C", "overflow-checks=on"]); - } - } else if !overflow_checks { - cmd.args(&["-C", "overflow-checks=off"]); - } + cmd.args(debug_assertions_overflow_checks_args(unit)); if test && unit.target.harness() { cmd.arg("--test"); @@ -1408,27 +1352,19 @@ fn build_base_args( cmd.args(&features_args(unit)); cmd.args(&check_cfg_args(unit)); - let meta = build_runner.files().metadata(unit); - cmd.arg("-C") - .arg(&format!("metadata={}", meta.c_metadata())); - if let Some(c_extra_filename) = meta.c_extra_filename() { - cmd.arg("-C") - .arg(&format!("extra-filename=-{c_extra_filename}")); - } + cmd.args(metadata_args(build_runner, unit)); - if rpath { - cmd.arg("-C").arg("rpath"); - } + cmd.args(rpath_args(unit)); cmd.arg("--out-dir") .arg(&build_runner.files().output_dir(unit)); unit.kind.add_target_arg(cmd); - add_codegen_linker(cmd, build_runner, unit, bcx.gctx.target_applies_to_host()?); + cmd.args(add_codegen_linker(build_runner, unit)?); if incremental { - add_codegen_incremental(cmd, build_runner, unit) + cmd.args(add_codegen_incremental(build_runner, unit)); } let pkg_hint_mostly_unused = match hints.mostly_unused { @@ -1462,10 +1398,7 @@ fn build_base_args( } } - let strip = strip.into_inner(); - if strip != StripInner::None { - cmd.arg("-C").arg(format!("strip={}", strip)); - } + cmd.args(strip_args(unit)); if unit.is_std { // -Zforce-unstable-if-unmarked prevents the accidental use of @@ -1481,6 +1414,138 @@ fn build_base_args( Ok(()) } +/// Returns `-C opt-level=` if `opt-level` isn't `"0"` +fn opt_level_args(unit: &Unit) -> impl Iterator { + let opt_level = unit.profile.opt_level; + + (opt_level.as_str() != "0") + .then(|| ["-C".into(), format!("opt-level={}", opt_level)]) + .into_iter() + .flatten() +} + +/// Returns `-C panic=` if `` isn't `Unwind` +/// +/// Also returns `-Z unstable-options` as needed +fn panic_strategy_args(unit: &Unit) -> impl Iterator { + let panic = unit.profile.panic; + let mut args = Vec::new(); + + if panic != PanicStrategy::Unwind { + args.push("-C".into()); + args.push(format!("panic={}", panic)); + } + if panic == PanicStrategy::ImmediateAbort { + args.push("-Z".into()); + args.push("unstable-options".into()); + } + args.into_iter() +} + +/// Returns `-C codegen-units=` if a number has been set +fn codegen_units_args(unit: &Unit) -> impl Iterator { + let codegen_units = unit.profile.codegen_units; + + codegen_units + .map(|n| ["-C".into(), format!("codegen-units={}", n)]) + .into_iter() + .flatten() +} + +/// Returns `-C debuginfo=` and `-C split-debuginfo=` if provided +fn debuginfo_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> impl Iterator { + let debuginfo = unit.profile.debuginfo.into_inner(); + let split_debuginfo = unit.profile.split_debuginfo; + let mut args = Vec::new(); + + // Shorten the number of arguments if possible. + if debuginfo != TomlDebugInfo::None { + args.push("-C".into()); + args.push(format!("debuginfo={debuginfo}")); + // This is generally just an optimization on build time so if we don't + // pass it then it's ok. The values for the flag (off, packed, unpacked) + // may be supported or not depending on the platform, so availability is + // checked per-value. For example, at the time of writing this code, on + // Windows the only stable valid value for split-debuginfo is "packed", + // while on Linux "unpacked" is also stable. + if let Some(split) = split_debuginfo { + if build_runner + .bcx + .target_data + .info(unit.kind) + .supports_debuginfo_split(split) + { + args.push("-C".into()); + args.push(format!("split-debuginfo={split}")); + } + } + } + args.into_iter() +} + +/// Returns `-C debug-assertions=` and `-C overflow-checks=` if the settings differs from the default +fn debug_assertions_overflow_checks_args(unit: &Unit) -> impl Iterator { + let opt_level = unit.profile.opt_level; + let debug_assertions = unit.profile.debug_assertions; + let overflow_checks = unit.profile.overflow_checks; + let mut args = Vec::new(); + + // `-C overflow-checks` is implied by the setting of `-C debug-assertions`, + // so we only need to provide `-C overflow-checks` if it differs from + // the value of `-C debug-assertions` we would provide. + if opt_level.as_str() != "0" { + if debug_assertions { + args.extend(["-C", "debug-assertions=on"]); + if !overflow_checks { + args.extend(["-C", "overflow-checks=off"]); + } + } else if overflow_checks { + args.extend(["-C", "overflow-checks=on"]); + } + } else if !debug_assertions { + args.extend(["-C", "debug-assertions=off"]); + if overflow_checks { + args.extend(["-C", "overflow-checks=on"]); + } + } else if !overflow_checks { + args.extend(["-C", "overflow-checks=off"]); + } + args.into_iter() +} + +/// Returns `-C metadata=` and `-C extra-filename=` if relevant +fn metadata_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> impl Iterator { + let meta = build_runner.files().metadata(unit); + ["-C".into(), format!("metadata={}", meta.c_metadata())] + .into_iter() + .chain( + meta.c_extra_filename() + .map(|c_extra_filename| { + ["-C".into(), format!("extra-filename=-{c_extra_filename}")] + }) + .into_iter() + .flatten(), + ) +} + +/// Returns `-C rpath` if rpath is enabled +fn rpath_args(unit: &Unit) -> impl Iterator { + unit.profile + .rpath + .then(|| ["-C", "rpath"]) + .into_iter() + .flatten() +} + +/// Returns `-C strip=` if `strip` is not `None` +fn strip_args(unit: &Unit) -> impl Iterator { + let strip = unit.profile.strip.into_inner(); + (strip != StripInner::None) + .then(|| ["-C".into(), format!("strip={}", strip)]) + .into_iter() + .flatten() +} + /// All active features for the unit passed as `--cfg features=`. fn features_args(unit: &Unit) -> Vec { let mut args = Vec::with_capacity(unit.features.len() * 2); @@ -1976,11 +2041,10 @@ pub fn extern_args( /// Adds `-C linker=` if specified. fn add_codegen_linker( - cmd: &mut ProcessBuilder, build_runner: &BuildRunner<'_, '_>, unit: &Unit, - target_applies_to_host: bool, -) { +) -> CargoResult> { + let target_applies_to_host = build_runner.bcx.gctx.target_applies_to_host()?; let linker = if unit.target.for_host() && !target_applies_to_host { build_runner .compilation @@ -1993,23 +2057,25 @@ fn add_codegen_linker( .map(|s| s.as_os_str()) }; - if let Some(linker) = linker { - let mut arg = OsString::from("linker="); - arg.push(linker); - cmd.arg("-C").arg(arg); - } + Ok(linker + .map(|linker| { + let mut arg = OsString::from("linker="); + arg.push(linker); + ["-C".into(), arg] + }) + .into_iter() + .flatten()) } /// Adds `-C incremental=`. fn add_codegen_incremental( - cmd: &mut ProcessBuilder, build_runner: &BuildRunner<'_, '_>, unit: &Unit, -) { +) -> impl Iterator { let dir = build_runner.files().incremental_dir(&unit); let mut arg = OsString::from("incremental="); - arg.push(dir.as_os_str()); - cmd.arg("-C").arg(arg); + arg.push(dir); + ["-C".into(), arg].into_iter() } fn envify(s: &str) -> String { diff --git a/src/cargo/ops/cargo_compile/mod.rs b/src/cargo/ops/cargo_compile/mod.rs index aae55186a37..e8731366593 100644 --- a/src/cargo/ops/cargo_compile/mod.rs +++ b/src/cargo/ops/cargo_compile/mod.rs @@ -223,7 +223,7 @@ pub fn print<'a>( let target_info = TargetInfo::new(gctx, &build_config.requested_kinds, &rustc, *kind)?; let mut process = rustc.process(); apply_env_config(gctx, &mut process)?; - process.args(&target_info.rustflags); + process.args(target_info.rustflags.as_ref()); if let Some(args) = target_rustc_args { process.args(args); } diff --git a/src/cargo/ops/cargo_test.rs b/src/cargo/ops/cargo_test.rs index 42cb5b0aaa5..951857b8795 100644 --- a/src/cargo/ops/cargo_test.rs +++ b/src/cargo/ops/cargo_test.rs @@ -1,5 +1,4 @@ use crate::core::compiler::{Compilation, Doctest, Unit, UnitHash, UnitOutput}; -use crate::core::profiles::PanicStrategy; use crate::core::{TargetKind, Workspace}; use crate::ops; use crate::util::errors::CargoResult; @@ -185,9 +184,9 @@ fn run_doc_tests( args, unstable_opts, unit, - linker, script_metas, env, + .. } = doctest_info; gctx.shell().status("Doc-tests", unit.target.name())?; @@ -218,15 +217,6 @@ fn run_doc_tests( p.arg("--test-runtool-arg").arg(arg); } } - if let Some(linker) = linker { - let mut joined = OsString::from("linker="); - joined.push(linker); - p.arg("-C").arg(joined); - } - - if unit.profile.panic != PanicStrategy::Unwind { - p.arg("-C").arg(format!("panic={}", unit.profile.panic)); - } for native_dep in compilation.native_dirs.iter() { p.arg("-L").arg(native_dep); diff --git a/tests/testsuite/test.rs b/tests/testsuite/test.rs index cc8ed3d22a3..e3f7e929c61 100644 --- a/tests/testsuite/test.rs +++ b/tests/testsuite/test.rs @@ -5653,3 +5653,270 @@ fn cargo_test_set_out_dir_env_var() { p.cargo("test --package foo --test case -- tests::test_add --exact --no-capture") .run(); } + +#[cargo_test] +fn doc_opt_level() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + + [profile.test] + opt-level = "s" + "#, + ) + .file( + "src/lib.rs", + r#" + //! ``` + //! // empty + //! ``` + "#, + ) + .build(); + + // Release flag sets opt-level=3 + p.cargo("test --doc --release -v") + .with_stderr_data(str![[r#" +... +[RUNNING] `rustdoc[..] -C opt-level=3[..]` +... +"#]]) + .run(); + + // Uses `test` profile settings by default + p.cargo("test --doc -v") + .with_stderr_data(str![[r#" +... +[RUNNING] `rustdoc[..] -C opt-level=s[..]` +... +"#]]) + .run(); +} + +#[cargo_test] +fn doc_codegen_units() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + + [profile.test] + codegen-units = 1 + "#, + ) + .file( + "src/lib.rs", + r#" + //! ``` + //! // empty + //! ``` + "#, + ) + .build(); + + p.cargo("test --doc -v") + .with_stderr_data(str![[r#" +... +[RUNNING] `rustdoc[..] -C codegen-units=1[..]` +... +"#]]) + .run(); +} + +#[cargo_test] +fn doc_debuginfo() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + + [profile.test] + debug = 1 + "#, + ) + .file( + "src/lib.rs", + r#" + //! ``` + //! // empty + //! ``` + "#, + ) + .build(); + + p.cargo("test --doc -v") + .with_stderr_data(str![[r#" +... +[RUNNING] `rustdoc[..] -C debuginfo=1[..]` +... +"#]]) + .run(); +} + +#[cargo_test(ignore_windows = "Only `packed` is supported on Windows, and it's the default")] +fn doc_split_debuginfo() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + + [profile.test] + debug = "full" + split-debuginfo = "packed" + "#, + ) + .file( + "src/lib.rs", + r#" + //! ``` + //! // empty + //! ``` + "#, + ) + .build(); + + p.cargo("test --doc -v") + .with_stderr_data(str![[r#" +... +[RUNNING] `rustdoc[..] -C split-debuginfo=packed[..]` +... +"#]]) + .run(); +} + +#[cargo_test] +fn doc_debug_assert() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + + [profile.test] + debug-assertions = false + "#, + ) + .file( + "src/lib.rs", + r#" + //! ``` + //! debug_assert!(false) + //! ``` + "#, + ) + .build(); + + p.cargo("test --doc -v") + .with_stderr_data(str![[r#" +... +[RUNNING] `rustdoc[..] -C debug-assertions=off[..]` +... +"#]]) + .run(); +} + +#[cargo_test] +fn doc_overflow_checks() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + + [profile.test] + overflow-checks = false + "#, + ) + .file( + "src/lib.rs", + r#" + //! ``` + //! // empty + //! ``` + "#, + ) + .build(); + + p.cargo("test --doc -v") + .with_stderr_data(str![[r#" +... +[RUNNING] `rustdoc[..] -C overflow-checks=off[..]` +... +"#]]) + .run(); +} + +#[cargo_test] +fn doc_rpath() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + + [profile.test] + rpath = true + "#, + ) + .file( + "src/lib.rs", + r#" + //! ``` + //! // empty + //! ``` + "#, + ) + .build(); + + p.cargo("test --doc -v") + .with_stderr_data(str![[r#" +... +[RUNNING] `rustdoc[..] -C rpath[..]` +... +"#]]) + .run(); +} + +#[cargo_test] +fn doc_strip_symbols() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + + [profile.test] + strip = true + "#, + ) + .file( + "src/lib.rs", + r#" + //! ``` + //! // empty + //! ``` + "#, + ) + .build(); + + p.cargo("test --doc -v") + .with_stderr_data(str![[r#" +... +[RUNNING] `rustdoc[..] -C strip=symbols[..]` +... +"#]]) + .run(); +}