1 // For various reasons, some idioms are still allow'ed, but we would like to
2 // test and enforce them.
3 #![warn(rust_2018_idioms)]
4 #![cfg_attr(test, deny(warnings))]
5 // Due to some of the default clippy lints being somewhat subjective and not
6 // necessarily an improvement, we prefer to not use them at this time.
8 #![warn(clippy::needless_borrow)]
9 #![warn(clippy::redundant_clone)]
11 use crate::core
::shell
::Verbosity
::Verbose
;
12 use crate::core
::Shell
;
17 pub use crate::util
::errors
::{InternalError, VerboseError}
;
18 pub use crate::util
::{indented_lines, CargoResult, CliError, CliResult, Config}
;
20 pub const CARGO_ENV
: &str = "CARGO";
30 pub struct CommitInfo
{
31 pub short_commit_hash
: String
,
32 pub commit_hash
: String
,
33 pub commit_date
: String
,
37 // Information about the Git repository we may have been built from.
38 pub commit_info
: Option
<CommitInfo
>,
39 // The release channel we were built for.
40 pub release_channel
: String
,
43 pub struct VersionInfo
{
47 pub pre_release
: Option
<String
>,
48 // Information that's only available when we were built with
49 // configure/make, rather than Cargo itself.
50 pub cfg_info
: Option
<CfgInfo
>,
53 impl fmt
::Display
for VersionInfo
{
54 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
55 write
!(f
, "cargo {}.{}.{}", self.major
, self.minor
, self.patch
)?
;
56 if let Some(channel
) = self.cfg_info
.as_ref().map(|ci
| &ci
.release_channel
) {
57 if channel
!= "stable" {
58 write
!(f
, "-{}", channel
)?
;
59 let empty
= String
::new();
60 write
!(f
, "{}", self.pre_release
.as_ref().unwrap_or(&empty
))?
;
64 if let Some(ref cfg
) = self.cfg_info
{
65 if let Some(ref ci
) = cfg
.commit_info
{
66 write
!(f
, " ({} {})", ci
.short_commit_hash
, ci
.commit_date
)?
;
73 pub fn exit_with_error(err
: CliError
, shell
: &mut Shell
) -> ! {
74 debug
!("exit_with_error; err={:?}", err
);
75 if let Some(ref err
) = err
.error
{
76 if let Some(clap_err
) = err
.downcast_ref
::<clap
::Error
>() {
81 let CliError { error, exit_code }
= err
;
82 if let Some(error
) = error
{
83 display_error(&error
, shell
);
86 std
::process
::exit(exit_code
)
89 /// Displays an error, and all its causes, to stderr.
90 pub fn display_error(err
: &Error
, shell
: &mut Shell
) {
91 debug
!("display_error; err={:?}", err
);
92 let has_verbose
= _display_error(err
, shell
, true);
96 "\nTo learn more, run the command again with --verbose."
101 .any(|e
| e
.downcast_ref
::<InternalError
>().is_some())
103 drop(shell
.note("this is an unexpected cargo internal error"));
106 "we would appreciate a bug report: https://github.com/rust-lang/cargo/issues/",
109 drop(shell
.note(format
!("{}", version())));
110 // Once backtraces are stabilized, this should print out a backtrace
111 // if it is available.
115 /// Displays a warning, with an error object providing detailed information
117 pub fn display_warning_with_error(warning
: &str, err
: &Error
, shell
: &mut Shell
) {
118 drop(shell
.warn(warning
));
119 drop(writeln
!(shell
.err()));
120 _display_error(err
, shell
, false);
123 fn _display_error(err
: &Error
, shell
: &mut Shell
, as_err
: bool
) -> bool
{
124 let verbosity
= shell
.verbosity();
125 let is_verbose
= |e
: &(dyn std
::error
::Error
+ '
static)| -> bool
{
126 verbosity
!= Verbose
&& e
.downcast_ref
::<VerboseError
>().is_some()
128 // Generally the top error shouldn't be verbose, but check it anyways.
129 if is_verbose(err
.as_ref()) {
133 drop(shell
.error(&err
));
135 drop(writeln
!(shell
.err(), "{}", err
));
137 for cause
in err
.chain().skip(1) {
138 // If we're not in verbose mode then print remaining errors until one
139 // marked as `VerboseError` appears.
140 if is_verbose(cause
) {
143 drop(writeln
!(shell
.err(), "\nCaused by:"));
147 indented_lines(&cause
.to_string())
153 pub fn version() -> VersionInfo
{
154 macro_rules
! option_env_str
{
156 option_env
!($name
).map(|s
| s
.to_string())
160 // So this is pretty horrible...
161 // There are two versions at play here:
162 // - version of cargo-the-binary, which you see when you type `cargo --version`
163 // - version of cargo-the-library, which you download from crates.io for use
166 // We want to make the `binary` version the same as the corresponding Rust/rustc release.
167 // At the same time, we want to keep the library version at `0.x`, because Cargo as
168 // a library is (and probably will always be) unstable.
170 // Historically, Cargo used the same version number for both the binary and the library.
171 // Specifically, rustc 1.x.z was paired with cargo 0.x+1.w.
172 // We continue to use this scheme for the library, but transform it to 1.x.w for the purposes
173 // of `cargo --version`.
175 let minor
= env
!("CARGO_PKG_VERSION_MINOR").parse
::<u8>().unwrap() - 1;
176 let patch
= env
!("CARGO_PKG_VERSION_PATCH").parse
::<u8>().unwrap();
178 match option_env
!("CFG_RELEASE_CHANNEL") {
179 // We have environment variables set up from configure/make.
181 let commit_info
= option_env
!("CFG_COMMIT_HASH").map(|s
| CommitInfo
{
182 commit_hash
: s
.to_string(),
183 short_commit_hash
: option_env_str
!("CFG_SHORT_COMMIT_HASH").unwrap(),
184 commit_date
: option_env_str
!("CFG_COMMIT_DATE").unwrap(),
190 pre_release
: option_env_str
!("CARGO_PKG_VERSION_PRE"),
191 cfg_info
: Some(CfgInfo
{
192 release_channel
: option_env_str
!("CFG_RELEASE_CHANNEL").unwrap(),
197 // We are being compiled by Cargo itself.
198 None
=> VersionInfo
{
202 pre_release
: option_env_str
!("CARGO_PKG_VERSION_PRE"),