Skip to content

Commit

Permalink
Merge #81
Browse files Browse the repository at this point in the history
81: Add implementation to verify the MSRV r=foresterre a=foresterre

* Allows verifying a certain MSRV without installing other toolchains
* Not yet implemented: option to directly run cargo-msrv in determine-msrv mode when the verification check failed
* Requires MSRV to be defined in the Cargo.toml, specifically in the `package.metadata.msrv` key.

Co-authored-by: Martijn Gribnau <[email protected]>
  • Loading branch information
foresterre committed Jun 14, 2021
2 parents d71171d + 86a68e9 commit 1be5445
Show file tree
Hide file tree
Showing 12 changed files with 340 additions and 78 deletions.
29 changes: 29 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ license = "Apache-2.0/MIT"
edition = "2018"
repository = "https://github.com/foresterre/cargo-msrv"

[package.metadata]
msrv = "1.42.0"

[dependencies]
# Used for parsing cli arguments.
clap = "2.33.0"
Expand All @@ -18,6 +21,9 @@ indicatif = "0.16.2"
# json output
json = "0.12.4"

# read Cargo.toml
decent-toml-rs-alternative = "0.3.0"

# Get the available rust versions
[dependencies.rust-releases]
version = "0.15.4"
Expand Down
36 changes: 33 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ With Cargo from Github [latest development version]:
* `cargo msrv --path <dir>` to find the MSRV in the `<dir>` directory cargo project.
* `cargo msrv -- <command> ` to use `<command>` as the compatibility check which decides whether a Rust version is
compatible or not. This command should be runnable through `rustup run <toolchain> <command>`.
* `cargo msrv --verify` to verify the MSRV, if defined with the 'package.metadata.msrv' key in the 'Cargo.toml'.

**Options:**
```
Expand Down Expand Up @@ -67,6 +68,10 @@ OPTIONS:
-V, --version
Prints version information
--verify
Verify the MSRV, if defined with the 'package.metadata.msrv' key in the 'Cargo.toml'. When this flag is
present, cargo-msrv will not attempt to determine the true MSRV. It will only attempt to verify specified
MSRV, the Rust build passes similarly to regular cargo-msrv runs.
ARGS:
<COMMAND>...
Expand All @@ -82,11 +87,31 @@ need to provide the <COMMAND...> part.

### JSON format

There are 4 types of status messages, each type is indicated
There are 6 types of status messages, each type is indicated
by the `reason` key.

#### Report mode

Reports the mode which will be used by `cargo-msrv`. There are currently two modes:
`determine-msrv` and `verify-msrv`, which respectively

```jsonc
{
"reason": "mode",
// The mode in which cargo-msrv will operate
"mode": "determine-msrv" /* OR */ "action": "verify-msrv",
// The toolchain that will be used
"toolchain":"x86_64-unknown-linux-gnu",
// command used to check a version
"check_cmd":"cargo check --all"}
}
```

#### Installing and Checking

Reported when a toolchain will be installed, or when a toolchain will
be run to check whether the version of the toolchain is compatible.

```jsonc
{
"reason": "installing", /* OR */ "reason": "checking",
Expand All @@ -105,6 +130,9 @@ by the `reason` key.

#### Check complete

Reported when a check, which determines whether the toolchain version under test
is compatible, completes.

```jsonc
{
"reason": "check-complete",
Expand All @@ -123,11 +151,13 @@ by the `reason` key.
}
```

#### MSRV complete
#### MSRV completed

Reported when all actions for a mode have been run to completion.

```jsonc
{
"reason": "msrv-complete",
"reason": "msrv-complete" /* OR */ "reason": "verify-complete",
// true if a msrv was found
"success": true,
// the msrv if found. The key will be absent if msrv wasn't found
Expand Down
14 changes: 12 additions & 2 deletions src/bin/cargo-msrv.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
use cargo_msrv::run_cargo_msrv;
use cargo_msrv::config::Config;
use cargo_msrv::errors::TResult;
use cargo_msrv::{cli, run_app};
use std::convert::TryFrom;

fn main() {
if let Err(err) = run_cargo_msrv() {
if let Err(err) = init_and_run() {
eprintln!("{}", err);
}
}

fn init_and_run() -> TResult<()> {
let matches = cli::cli().get_matches();
let config = Config::try_from(&matches)?;

run_app(&config)
}
3 changes: 2 additions & 1 deletion src/check.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::command::command;
use crate::config::Config;
use crate::crate_root_folder;
use crate::errors::{CargoMSRVError, TResult};
use crate::lockfile::{LockfileHandler, CARGO_LOCK};
use crate::{crate_root_folder, Output, ProgressAction};
use crate::reporter::{Output, ProgressAction};
use rust_releases::semver;
use std::path::Path;

Expand Down
12 changes: 11 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub mod id {
pub const ARG_TOOLCHAIN_FILE: &str = "toolchain_file";
pub const ARG_IGNORE_LOCKFILE: &str = "lockfile";
pub const ARG_OUTPUT_FORMAT: &str = "output_format";
pub const ARG_VERIFY: &str = "verify_msrv";
}

pub fn cli() -> App<'static, 'static> {
Expand Down Expand Up @@ -108,7 +109,16 @@ so: `rustup run <toolchain> <COMMAND...>`. You'll only need to provide the <COMM
.takes_value(true)
.possible_values(&["json"])
.long_help("Output status messages in machine-readable format. \
Machine-readable status updates will be printed in the requested format to stdout."))
Machine-readable status updates will be printed in the requested format to stdout.")
)
.arg(Arg::with_name(id::ARG_VERIFY)
.long("verify")
.help("Verify the MSRV, if defined with the 'package.metadata.msrv' key in the Cargo.toml")
.long_help("Verify the MSRV, if defined with the 'package.metadata.msrv' key in the 'Cargo.toml'. \
When this flag is present, cargo-msrv will not attempt to determine the true MSRV. \
It will only attempt to verify specified MSRV, the Rust build passes similarly to regular cargo-msrv runs. ")
.takes_value(false)
)
.arg(
Arg::with_name(id::ARG_CUSTOM_CHECK)
.value_name("COMMAND")
Expand Down
47 changes: 41 additions & 6 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,26 @@ pub fn test_config_from_matches<'a>(matches: &'a ArgMatches<'a>) -> TResult<Conf
Ok(config)
}

#[derive(Debug, Clone, Copy)]
pub enum ModeIntent {
// Determines the MSRV for a project
DetermineMSRV,
// Verifies the given MSRV
VerifyMSRV,
}

impl From<ModeIntent> for &'static str {
fn from(action: ModeIntent) -> Self {
match action {
ModeIntent::DetermineMSRV => "determine-msrv",
ModeIntent::VerifyMSRV => "verify-msrv",
}
}
}

#[derive(Debug, Clone)]
pub struct Config<'a> {
mode_intent: ModeIntent,
target: String,
check_command: Vec<&'a str>,
crate_path: Option<PathBuf>,
Expand All @@ -38,8 +56,9 @@ pub struct Config<'a> {
}

impl<'a> Config<'a> {
pub fn new(target: String) -> Self {
pub fn new(mode_intent: ModeIntent, target: String) -> Self {
Self {
mode_intent,
target,
check_command: vec!["cargo", "check", "--all"],
crate_path: None,
Expand All @@ -53,6 +72,10 @@ impl<'a> Config<'a> {
}
}

pub fn action_intent(&self) -> ModeIntent {
self.mode_intent
}

pub fn target(&self) -> &String {
&self.target
}
Expand Down Expand Up @@ -100,12 +123,17 @@ pub struct ConfigBuilder<'a> {
}

impl<'a> ConfigBuilder<'a> {
pub fn new(default_target: &str) -> Self {
pub fn new(action_intent: ModeIntent, default_target: &str) -> Self {
Self {
inner: Config::new(default_target.to_string()),
inner: Config::new(action_intent, default_target.to_string()),
}
}

pub fn mode_intent(mut self, mode_intent: ModeIntent) -> Self {
self.inner.mode_intent = mode_intent;
self
}

pub fn target(mut self, target: &str) -> Self {
self.inner.target = target.to_string();
self
Expand Down Expand Up @@ -168,13 +196,20 @@ impl<'config> TryFrom<&'config ArgMatches<'config>> for Config<'config> {
use crate::cli::id;
use crate::fetch::default_target;

let target = default_target()?;

let arg_matches = matches
.subcommand_matches(id::SUB_COMMAND_MSRV)
.ok_or(CargoMSRVError::UnableToParseCliArgs)?;

let mut builder = ConfigBuilder::new(&target);
let action_intent = if arg_matches.is_present(id::ARG_VERIFY) {
ModeIntent::VerifyMSRV
} else {
ModeIntent::DetermineMSRV
};

// FIXME: if set, we don't need to do this; in case we can't find it, it may fail here, but atm can't be manually supplied at all
let target = default_target()?;

let mut builder = ConfigBuilder::new(action_intent, &target);

// set the command which will be used to check if a project can build
let check_cmd = arg_matches.values_of(id::ARG_CUSTOM_CHECK);
Expand Down
11 changes: 11 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::error::Error;
use std::fmt;
use std::fmt::Formatter;
use std::io;
use std::path::PathBuf;
use std::string::FromUtf8Error;

pub type TResult<T> = Result<T, CargoMSRVError>;
Expand All @@ -16,6 +17,8 @@ pub enum CargoMSRVError {
Io(io::Error),
InvalidRustVersionNumber(std::num::ParseIntError),
InvalidUTF8(FromUtf8Error),
NoMSRVKeyInCargoToml(PathBuf),
ParseToml(decent_toml_rs_alternative::TomlError),
RustReleasesSource(rust_releases::RustChangelogError),
RustupInstallFailed(ToolchainSpecifier),
RustupRunWithCommandFailed,
Expand All @@ -39,6 +42,8 @@ impl fmt::Display for CargoMSRVError {
CargoMSRVError::Io(err) => err.fmt(f),
CargoMSRVError::InvalidRustVersionNumber(err) => err.fmt(f),
CargoMSRVError::InvalidUTF8(err) => err.fmt(f),
CargoMSRVError::NoMSRVKeyInCargoToml(path) => write!(f, "Unable to find key 'package.metadata.msrv' in '{}'", path.display()),
CargoMSRVError::ParseToml(err) => f.write_fmt(format_args!("Unable to parse Cargo.toml {:?}", err)),
CargoMSRVError::RustReleasesSource(err) => err.fmt(f),
CargoMSRVError::RustupInstallFailed(toolchain) => f.write_fmt(format_args!("Unable to install toolchain with `rustup install {}`.", toolchain)),
CargoMSRVError::RustupRunWithCommandFailed => write!(f, "Check toolchain (with `rustup run <toolchain> <command>`) failed."),
Expand Down Expand Up @@ -94,6 +99,12 @@ impl From<std::num::ParseIntError> for CargoMSRVError {
}
}

impl From<decent_toml_rs_alternative::TomlError> for CargoMSRVError {
fn from(err: decent_toml_rs_alternative::TomlError) -> Self {
CargoMSRVError::ParseToml(err)
}
}

impl From<rust_releases::semver::SemVerError> for CargoMSRVError {
fn from(err: rust_releases::semver::SemVerError) -> Self {
CargoMSRVError::SemverError(err)
Expand Down
Loading

0 comments on commit 1be5445

Please sign in to comment.