Skip to content

Commit

Permalink
Auto merge of #10413 - ehuss:beta-fix-rustflags-gate, r=alexcrichton
Browse files Browse the repository at this point in the history
[beta] Add common profile validation.

Beta backport of:
* #10411 — This is intended to limit any potential misuse of rustflags in profiles.
* #10396 — Fix CI due to clap deprecation
  • Loading branch information
bors committed Feb 22, 2022
2 parents ea2a21c + 0ebfe2e commit 1e5cac7
Show file tree
Hide file tree
Showing 13 changed files with 107 additions and 49 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ toml_edit = { version = "0.13.4", features = ["serde", "easy"] }
unicode-xid = "0.2.0"
url = "2.2.2"
walkdir = "2.2"
clap = "3.0.13"
clap = "3.1.0"
unicode-width = "0.1.5"
openssl = { version = '0.10.11', optional = true }
im-rc = "15.0.0"
Expand Down
30 changes: 18 additions & 12 deletions src/bin/cargo/cli.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use anyhow::anyhow;
use cargo::core::{features, CliUnstable};
use cargo::{self, drop_print, drop_println, CliResult, Config};
use clap::{AppSettings, Arg, ArgMatches};
use clap::{
error::{ContextKind, ContextValue},
AppSettings, Arg, ArgMatches,
};
use itertools::Itertools;
use std::collections::HashMap;
use std::fmt::Write;
Expand Down Expand Up @@ -33,9 +36,17 @@ pub fn main(config: &mut Config) -> CliResult {
let args = match cli().try_get_matches() {
Ok(args) => args,
Err(e) => {
if e.kind == clap::ErrorKind::UnrecognizedSubcommand {
if e.kind() == clap::ErrorKind::UnrecognizedSubcommand {
// An unrecognized subcommand might be an external subcommand.
let cmd = e.info[0].clone();
let cmd = e
.context()
.find_map(|c| match c {
(ContextKind::InvalidSubcommand, &ContextValue::String(ref cmd)) => {
Some(cmd)
}
_ => None,
})
.expect("UnrecognizedSubcommand implies the presence of InvalidSubcommand");
return super::execute_external_subcommand(config, &cmd, &[&cmd, "--help"])
.map_err(|_| e.into());
} else {
Expand Down Expand Up @@ -286,9 +297,7 @@ For more information, see issue #10049 <https://github.com/rust-lang/cargo/issue
// Note that an alias to an external command will not receive
// these arguments. That may be confusing, but such is life.
let global_args = GlobalArgs::new(args);
let new_args = cli()
.setting(AppSettings::NoBinaryName)
.try_get_matches_from(alias)?;
let new_args = cli().no_binary_name(true).try_get_matches_from(alias)?;

let new_cmd = new_args.subcommand_name().expect("subcommand is required");
already_expanded.push(cmd.to_string());
Expand Down Expand Up @@ -406,14 +415,11 @@ fn cli() -> App {
"cargo [OPTIONS] [SUBCOMMAND]"
};
App::new("cargo")
.setting(
AppSettings::DeriveDisplayOrder
| AppSettings::AllowExternalSubcommands
| AppSettings::NoAutoVersion,
)
.allow_external_subcommands(true)
.setting(AppSettings::DeriveDisplayOrder | AppSettings::NoAutoVersion)
// Doesn't mix well with our list of common cargo commands. See clap-rs/clap#3108 for
// opening clap up to allow us to style our help template
.global_setting(AppSettings::DisableColoredHelp)
.disable_colored_help(true)
.override_usage(usage)
.help_template(
"\
Expand Down
2 changes: 1 addition & 1 deletion src/bin/cargo/commands/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use cargo::ops::{self, TestOptions};

pub fn cli() -> App {
subcommand("bench")
.setting(AppSettings::TrailingVarArg)
.trailing_var_arg(true)
.about("Execute all benchmarks of a local package")
.arg_quiet()
.arg(
Expand Down
3 changes: 2 additions & 1 deletion src/bin/cargo/commands/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ pub fn cli() -> App {
subcommand("config")
.about("Inspect configuration values")
.after_help("Run `cargo help config` for more detailed information.\n")
.setting(clap::AppSettings::SubcommandRequiredElseHelp)
.subcommand_required(true)
.arg_required_else_help(true)
.subcommand(
subcommand("get")
.arg(Arg::new("key").help("The config key to display"))
Expand Down
2 changes: 1 addition & 1 deletion src/bin/cargo/commands/git_checkout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const REMOVED: &str = "The `git-checkout` subcommand has been removed.";
pub fn cli() -> App {
subcommand("git-checkout")
.about("This subcommand has been removed")
.setting(AppSettings::Hidden)
.hide(true)
.override_help(REMOVED)
}

Expand Down
3 changes: 2 additions & 1 deletion src/bin/cargo/commands/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ pub fn cli() -> App {
subcommand("report")
.about("Generate and display various kinds of reports")
.after_help("Run `cargo help report` for more detailed information.\n")
.setting(clap::AppSettings::SubcommandRequiredElseHelp)
.subcommand_required(true)
.arg_required_else_help(true)
.subcommand(
subcommand("future-incompatibilities")
.alias("future-incompat")
Expand Down
2 changes: 1 addition & 1 deletion src/bin/cargo/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub fn cli() -> App {
subcommand("run")
// subcommand aliases are handled in aliased_command()
// .alias("r")
.setting(AppSettings::TrailingVarArg)
.trailing_var_arg(true)
.about("Run a binary or example of the local package")
.arg_quiet()
.arg(
Expand Down
2 changes: 1 addition & 1 deletion src/bin/cargo/commands/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const CRATE_TYPE_ARG_NAME: &str = "crate-type";

pub fn cli() -> App {
subcommand("rustc")
.setting(AppSettings::TrailingVarArg)
.trailing_var_arg(true)
.about("Compile a package, and pass extra options to the compiler")
.arg_quiet()
.arg(Arg::new("args").multiple_values(true).help("Rustc flags"))
Expand Down
2 changes: 1 addition & 1 deletion src/bin/cargo/commands/rustdoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::command_prelude::*;

pub fn cli() -> App {
subcommand("rustdoc")
.setting(AppSettings::TrailingVarArg)
.trailing_var_arg(true)
.about("Build a package's documentation, using specified custom flags.")
.arg_quiet()
.arg(Arg::new("args").multiple_values(true))
Expand Down
2 changes: 1 addition & 1 deletion src/bin/cargo/commands/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub fn cli() -> App {
subcommand("test")
// Subcommand aliases are handled in `aliased_command()`.
// .alias("t")
.setting(AppSettings::TrailingVarArg)
.trailing_var_arg(true)
.about("Execute all unit and integration tests and build examples of a local package")
.arg(
Arg::new("TESTNAME")
Expand Down
6 changes: 4 additions & 2 deletions src/cargo/util/command_prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub use crate::core::compiler::CompileMode;
pub use crate::{CliError, CliResult, Config};
pub use clap::{AppSettings, Arg, ArgMatches};

pub type App = clap::App<'static>;
pub type App = clap::Command<'static>;

pub trait AppExt: Sized {
fn _arg(self, arg: Arg<'static>) -> Self;
Expand Down Expand Up @@ -281,7 +281,9 @@ pub fn multi_opt(name: &'static str, value_name: &'static str, help: &'static st
}

pub fn subcommand(name: &'static str) -> App {
App::new(name).setting(AppSettings::DeriveDisplayOrder | AppSettings::DontCollapseArgsInUsage)
App::new(name)
.dont_collapse_args_in_usage(true)
.setting(AppSettings::DeriveDisplayOrder)
}

/// Determines whether or not to gate `--profile` as unstable when resolving it.
Expand Down
64 changes: 38 additions & 26 deletions src/cargo/util/toml/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,10 +448,7 @@ impl ser::Serialize for ProfilePackageSpec {
where
S: ser::Serializer,
{
match *self {
ProfilePackageSpec::Spec(ref spec) => spec.serialize(s),
ProfilePackageSpec::All => "*".serialize(s),
}
self.to_string().serialize(s)
}
}

Expand All @@ -471,21 +468,33 @@ impl<'de> de::Deserialize<'de> for ProfilePackageSpec {
}
}

impl fmt::Display for ProfilePackageSpec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ProfilePackageSpec::Spec(spec) => spec.fmt(f),
ProfilePackageSpec::All => f.write_str("*"),
}
}
}

impl TomlProfile {
pub fn validate(
&self,
name: &str,
features: &Features,
warnings: &mut Vec<String>,
) -> CargoResult<()> {
self.validate_profile(name, features)?;
if let Some(ref profile) = self.build_override {
features.require(Feature::profile_overrides())?;
profile.validate_override("build-override", features)?;
profile.validate_override("build-override")?;
profile.validate_profile(&format!("{name}.build-override"), features)?;
}
if let Some(ref packages) = self.package {
features.require(Feature::profile_overrides())?;
for profile in packages.values() {
profile.validate_override("package", features)?;
for (override_name, profile) in packages {
profile.validate_override("package")?;
profile.validate_profile(&format!("{name}.package.{override_name}"), features)?;
}
}

Expand Down Expand Up @@ -548,21 +557,6 @@ impl TomlProfile {
}
}

if self.rustflags.is_some() {
features.require(Feature::profile_rustflags())?;
}

if let Some(codegen_backend) = &self.codegen_backend {
features.require(Feature::codegen_backend())?;
if codegen_backend.contains(|c: char| !c.is_ascii_alphanumeric() && c != '_') {
bail!(
"`profile.{}.codegen-backend` setting of `{}` is not a valid backend name.",
name,
codegen_backend,
);
}
}

Ok(())
}

Expand Down Expand Up @@ -645,7 +639,28 @@ impl TomlProfile {
Ok(())
}

fn validate_override(&self, which: &str, features: &Features) -> CargoResult<()> {
/// Validates a profile.
///
/// This is a shallow check, which is reused for the profile itself and any overrides.
fn validate_profile(&self, name: &str, features: &Features) -> CargoResult<()> {
if let Some(codegen_backend) = &self.codegen_backend {
features.require(Feature::codegen_backend())?;
if codegen_backend.contains(|c: char| !c.is_ascii_alphanumeric() && c != '_') {
bail!(
"`profile.{}.codegen-backend` setting of `{}` is not a valid backend name.",
name,
codegen_backend,
);
}
}
if self.rustflags.is_some() {
features.require(Feature::profile_rustflags())?;
}
Ok(())
}

/// Validation that is specific to an override.
fn validate_override(&self, which: &str) -> CargoResult<()> {
if self.package.is_some() {
bail!("package-specific profiles cannot be nested");
}
Expand All @@ -661,9 +676,6 @@ impl TomlProfile {
if self.rpath.is_some() {
bail!("`rpath` may not be specified in a `{}` profile", which)
}
if self.codegen_backend.is_some() {
features.require(Feature::codegen_backend())?;
}
Ok(())
}

Expand Down
36 changes: 36 additions & 0 deletions tests/testsuite/profiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::env;

use cargo_test_support::project;
use cargo_test_support::registry::Package;

#[cargo_test]
fn profile_overrides() {
Expand Down Expand Up @@ -660,6 +661,41 @@ fn rustflags_requires_cargo_feature() {
"\
[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
Caused by:
feature `profile-rustflags` is required
The package requires the Cargo feature called `profile-rustflags`, but that feature is \
not stabilized in this version of Cargo (1.[..]).
Consider adding `cargo-features = [\"profile-rustflags\"]` to the top of Cargo.toml \
(above the [package] table) to tell Cargo you are opting in to use this unstable feature.
See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-rustflags-option \
for more information about the status of this feature.
",
)
.run();

Package::new("bar", "1.0.0").publish();
p.change_file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
[dependencies]
bar = "1.0"
[profile.dev.package.bar]
rustflags = ["-C", "link-dead-code=yes"]
"#,
);
p.cargo("check")
.masquerade_as_nightly_cargo()
.with_status(101)
.with_stderr(
"\
error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
Caused by:
feature `profile-rustflags` is required
Expand Down

0 comments on commit 1e5cac7

Please sign in to comment.