-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Port cargo to clap3 #10265
Port cargo to clap3 #10265
Conversation
- One parser change found by `cargo_config::includes` is that clap 2 would ignore any values after a `=` for flags. `cargo config --show-origin` is a flag but the test passed `--show-origin=yes` which happens to give the desired result for that test but is the same as `--show-origin=no` or `--show-origin=alien-invasion`. - The parser now panics when accessing an undefined attribute but clap takes advantage of that for sharing code across commands that have different subsets of arguments defined. I've extended clap so we can "look before you leap" and put the checks at the argument calls to start off with so its very clear what is tenuously shared. This allows us to go in either direction in the future, either addressing how we are sharing between commands or by moving this down into the extension methods and pretending this clap feature doesn't exist - On that topic, a test found clap-rs/clap#3263. For now, there is a hack in clap. Depending on how we fix that in clap for clap 4.0, we might need to re-address things in cargo. - `value_of_os` now requires setting `allow_invalid_utf8`, otherwise it asserts. To help catch this, I updated the argument definitions associated with lookups reported by: - `rg 'values?_os' src/` - `rg 'values?_of_os' src/` - clap now reports `2` for usage errors, so we had to bypass clap's `exit` call to keep the same exit code. BREAKING CHANGE: API now uses clap3
Note: `cargo vendor --sync` did not use `multi_opt` and so it has both multiple occurrences **and** multiple values. If we want to deprecate this, we'll need `unstable-grouped` to be stablized (or pin our clap version) and ensure each group has only 1 value.
(rust-highfive has picked a reviewer for you, use r? to override) |
Arg::new("tomls") | ||
.short('s') | ||
.long("sync") | ||
.help("Additional `Cargo.toml` to sync and vendor") | ||
.value_name("TOML") | ||
.multiple(true), | ||
.allow_invalid_utf8(true) | ||
.multiple_occurrences(true) | ||
.multiple_values(true), | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cargo vendor --sync did not use multi_opt and so it has both
multiple occurrences and multiple values. If we want to deprecate
this, we'll need unstable-grouped to be stablized (or pin our clap
version) and ensure each group has only 1 value.
let exit_code = if clap_err.use_stderr() { 1 } else { 0 }; | ||
let _ = clap_err.print(); | ||
std::process::exit(exit_code) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clap now reports 2 for usage errors, so we had to bypass clap's
exit call to keep the same exit code.
cargo_process("config get build.rustflags --show-origin -Zunstable-options -Zconfig-include") | ||
.cwd(&sub_folder.parent().unwrap()) | ||
.masquerade_as_nightly_cargo() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One parser change found by
cargo_config::includes
is that clap 2
would ignore any values after a=
for flags.
cargo config --show-origin
is a flag but the test passed--show-origin=yes
which
happens to give the desired result for that test but is the same as
--show-origin=no
or--show-origin=alien-invasion
.
fn _is_valid_arg(&self, name: &str) -> bool { | ||
self.is_valid_arg(name) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ArgMatches now panics when accessing an undefined argument but clap
takes advantage of that for sharing code across commands that have
different subsets of arguments defined. I've extended clap so we can
"look before you leap" and put the checks at the argument calls to
start off with so its very clear what is tenuously shared. This
allows us to go in either direction in the future, either addressing
how we are sharing between commands or by moving this down into the
extension methods and pretending this clap feature doesn't exist
|
||
let (new_cmd, _) = new_args.subcommand(); | ||
let new_cmd = new_args.subcommand_name().expect("subcommand is required"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clap2 returned ""
when no subcommand was present but now returns None
.
@@ -408,22 +408,24 @@ fn cli() -> App { | |||
"cargo [OPTIONS] [SUBCOMMAND]" | |||
}; | |||
App::new("cargo") | |||
.settings(&[ | |||
AppSettings::UnifiedHelpMessage, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UnifiedHelpMessage
is now built-in
.settings(&[ | ||
AppSettings::UnifiedHelpMessage, | ||
AppSettings::DeriveDisplayOrder, | ||
AppSettings::VersionlessSubcommands, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
VersionlessSubcommands
isn't needed, if a subcommand has no version set then it won't have a --version
.setting( | ||
AppSettings::DeriveDisplayOrder | ||
| AppSettings::AllowExternalSubcommands | ||
| AppSettings::NoAutoVersion, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like clap used to allow you to completely override the --version
flag but now you can override its --help
but you still get the built-in version behavior unless you opt-out with this flag.
#[test] | ||
fn verify_cli() { | ||
cli().debug_assert(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clap assumes errors are programmer mistakes and reports them through debug_asserrts. It does this lazily, only evaluating the subcommands that the user activates. This test will eagerly run all of the debug asserts, helping to catch problems sooner.
.arg( | ||
Arg::with_name("no-delete") | ||
Arg::new("path") | ||
.allow_invalid_utf8(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
value_of_os now requires setting allow_invalid_utf8, otherwise it
asserts. To help catch this, I updated the argument definitions
associated with lookups reported by:
I thought it interesting that most clap path arguments don't allow non-UTF-8 arguments but vendor
seems to be one of the main exceptions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's interesting. In general, any path argument we accept should allow non-UTF-8, but that can get fixed in a follow-up PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The follow up PR would be two parts
- Add
arg.allow_invalid_utf8(true)
- Call
value_of_os
rather thanvalue_of
pub fn optional_multi_opt( | ||
name: &'static str, | ||
value_name: &'static str, | ||
help: &'static str, | ||
) -> Arg<'static, 'static> { | ||
) -> Arg<'static> { | ||
opt(name, help) | ||
.value_name(value_name) | ||
.multiple(true) | ||
.multiple_occurrences(true) | ||
.multiple_values(true) | ||
.min_values(0) | ||
.number_of_values(1) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't dig too deeply into how this works, it might be possible to simplify it like I did multi_opt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
optional_multi_opt
is supposed to allow cargo cmd --theopt value1 --otheropt
and cargo cmd --theopt value1 --theopt value2 --otheropt
and cargo cmd --theopt --otheropt
, but not cargo cmd --theopt value1 value2 --otheropt
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So its like --color[=WHEN]
with multiple.
What confused me is I never realized these args work like this, Playing with some of them it now makes sense; at least in some of the cases, like cargo check --test
, its so cargo can list what values are supported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, as far as I can tell, because cargo accepts optional values passed to options, and doesn't require =
on those options, we've effectively ruled out the possibility of having positional parameters on various commands.
impl From<std::io::Error> for CliError { | ||
fn from(err: std::io::Error) -> CliError { | ||
CliError::new(err.into(), 1) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cli().print_help()?;
now reports IO errors rather than clap errors
@ehuss as the original porter to clap (IIRC) would you be up for reviewing this? For me to review this I feel like I'd have to build up a more-complete understanding of clap2, then clap3, then look at this diff, which at least for me would take a significant amount of time. |
I think that was matklad. I can review it, but it may take a while. |
Ah ok no that's my mistake. I'll try to get to this at some point. I can't commit to a particular point in time right now though. |
I pulled this down locally, reviewed it in detail, and built it to run various tests. Seems to work exactly as expected, and the code changes look good to me. I also appreciated the incremental approach here of first doing the minimal port, then migrating away from the deprecated functions one by one. @bors r+ |
📌 Commit 92fa72d has been approved by |
⌛ Testing commit 92fa72d with merge b81ca081d3e901d0cb01abd13af66c8ff5dc1bbf... |
💔 Test failed - checks-actions |
@bors retry
|
☀️ Test successful - checks-actions |
Update cargo 6 commits in 358e79fe56fe374649275ca7aebaafd57ade0e8d..06b9d31743210b788b130c8a484c2838afa6fc27 2022-01-04 18:39:45 +0000 to 2022-01-11 23:47:29 +0000 - Port cargo to clap3 (rust-lang/cargo#10265) - feat: support rustflags per profile (rust-lang/cargo#10217) - Make bors ignore the PR template so it doesn't end up in merge messages (rust-lang/cargo#10267) - Be resilient to most IO error and filesystem loop while walking dirs (rust-lang/cargo#10214) - Remove the option to disable pipelining (rust-lang/cargo#10258) - Always ask rustc for messages about artifacts, and always process them (rust-lang/cargo#10255)
Update cargo 6 commits in 358e79fe56fe374649275ca7aebaafd57ade0e8d..06b9d31743210b788b130c8a484c2838afa6fc27 2022-01-04 18:39:45 +0000 to 2022-01-11 23:47:29 +0000 - Port cargo to clap3 (rust-lang/cargo#10265) - feat: support rustflags per profile (rust-lang/cargo#10217) - Make bors ignore the PR template so it doesn't end up in merge messages (rust-lang/cargo#10267) - Be resilient to most IO error and filesystem loop while walking dirs (rust-lang/cargo#10214) - Remove the option to disable pipelining (rust-lang/cargo#10258) - Always ask rustc for messages about artifacts, and always process them (rust-lang/cargo#10255)
Update cargo 6 commits in 358e79fe56fe374649275ca7aebaafd57ade0e8d..06b9d31743210b788b130c8a484c2838afa6fc27 2022-01-04 18:39:45 +0000 to 2022-01-11 23:47:29 +0000 - Port cargo to clap3 (rust-lang/cargo#10265) - feat: support rustflags per profile (rust-lang/cargo#10217) - Make bors ignore the PR template so it doesn't end up in merge messages (rust-lang/cargo#10267) - Be resilient to most IO error and filesystem loop while walking dirs (rust-lang/cargo#10214) - Remove the option to disable pipelining (rust-lang/cargo#10258) - Always ask rustc for messages about artifacts, and always process them (rust-lang/cargo#10255)
Update cargo 16 commits in 358e79fe56fe374649275ca7aebaafd57ade0e8d..95bb3c92bf516017e812e7f1c14c2dea3845b30e 2022-01-04 18:39:45 +0000 to 2022-01-18 17:39:35 +0000 - Error when setting crate type of both dylib and cdylib in library (rust-lang/cargo#10243) - Include `help` in `--list` (rust-lang/cargo#10300) - Add report subcommand to bash completion. (rust-lang/cargo#10295) - Downgrade some log messages. (rust-lang/cargo#10296) - Enable shortcut for triage bot (rust-lang/cargo#10298) - Bump to 0.61.0, update changelog (rust-lang/cargo#10294) - use new cargo fmt option (rust-lang/cargo#10291) - Add `run-fail` to semver-check for docs (rust-lang/cargo#10287) - Use `is_symlink()` method (rust-lang/cargo#10290) - Stabilize namespaced and weak dependency features. (rust-lang/cargo#10269) - Port cargo to clap3 (rust-lang/cargo#10265) - feat: support rustflags per profile (rust-lang/cargo#10217) - Make bors ignore the PR template so it doesn't end up in merge messages (rust-lang/cargo#10267) - Be resilient to most IO error and filesystem loop while walking dirs (rust-lang/cargo#10214) - Remove the option to disable pipelining (rust-lang/cargo#10258) - Always ask rustc for messages about artifacts, and always process them (rust-lang/cargo#10255)
What does this PR try to resolve?
This moves cargo to the latest major version of clap.
This supersedes #10259 and #10262
How should we test and review this PR?
For testing, I mostly relied on existing tests. I did manually validate that
cargo run <non-escaped command args>
behaved the same between bothFor reviewing, I split out each deprecation resolution into a separate commit so its easy to focus on more mechanical changes (most of the deprecation fixes) from interesting ones (the port, the
Arg::multiple
deprecation)Additional information
cargo_config::includes
is that clap 2would ignore any values after a
=
for flags.cargo config --show-origin
is a flag but the test passed--show-origin=yes
whichhappens to give the desired result for that test but is the same as
--show-origin=no
or--show-origin=alien-invasion
.ArgMatches
now panics when accessing an undefined argument but claptakes advantage of that for sharing code across commands that have
different subsets of arguments defined. I've extended clap so we can
"look before you leap" and put the checks at the argument calls to
start off with so its very clear what is tenuously shared. This
allows us to go in either direction in the future, either addressing
how we are sharing between commands or by moving this down into the
extension methods and pretending this clap feature doesn't exist
hack in clap. Depending on how we fix that in clap for clap 4.0, we
might need to re-address things in cargo.
value_of_os
now requires settingallow_invalid_utf8
, otherwise itasserts. To help catch this, I updated the argument definitions
associated with lookups reported by:
rg 'values?_os' src/
rg 'values?_of_os' src/
2
for usage errors, so we had to bypass clap'sexit
call to keep the same exit code.cargo vendor --sync
did not usemulti_opt
and so it has bothmultiple occurrences and multiple values. If we want to deprecate
this, we'll need
unstable-grouped
to be stablized (or pin our clapversion) and ensure each group has only 1 value.