]> git.proxmox.com Git - cargo.git/blob - src/cargo/lib.rs
Auto merge of #9467 - ehuss:install-metadata, r=alexcrichton
[cargo.git] / src / cargo / lib.rs
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.
7 #![allow(clippy::all)]
8 #![warn(clippy::needless_borrow)]
9 #![warn(clippy::redundant_clone)]
10
11 use crate::core::shell::Verbosity::Verbose;
12 use crate::core::Shell;
13 use anyhow::Error;
14 use log::debug;
15 use std::fmt;
16
17 pub use crate::util::errors::{InternalError, VerboseError};
18 pub use crate::util::{indented_lines, CargoResult, CliError, CliResult, Config};
19
20 pub const CARGO_ENV: &str = "CARGO";
21
22 #[macro_use]
23 mod macros;
24
25 pub mod core;
26 pub mod ops;
27 pub mod sources;
28 pub mod util;
29
30 pub struct CommitInfo {
31 pub short_commit_hash: String,
32 pub commit_hash: String,
33 pub commit_date: String,
34 }
35
36 pub struct CfgInfo {
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,
41 }
42
43 pub struct VersionInfo {
44 pub major: u8,
45 pub minor: u8,
46 pub patch: u8,
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>,
51 }
52
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))?;
61 }
62 };
63
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)?;
67 }
68 };
69 Ok(())
70 }
71 }
72
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>() {
77 clap_err.exit()
78 }
79 }
80
81 let CliError { error, exit_code } = err;
82 if let Some(error) = error {
83 display_error(&error, shell);
84 }
85
86 std::process::exit(exit_code)
87 }
88
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);
93 if has_verbose {
94 drop(writeln!(
95 shell.err(),
96 "\nTo learn more, run the command again with --verbose."
97 ));
98 }
99 if err
100 .chain()
101 .any(|e| e.downcast_ref::<InternalError>().is_some())
102 {
103 drop(shell.note("this is an unexpected cargo internal error"));
104 drop(
105 shell.note(
106 "we would appreciate a bug report: https://github.com/rust-lang/cargo/issues/",
107 ),
108 );
109 drop(shell.note(format!("{}", version())));
110 // Once backtraces are stabilized, this should print out a backtrace
111 // if it is available.
112 }
113 }
114
115 /// Displays a warning, with an error object providing detailed information
116 /// and context.
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);
121 }
122
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()
127 };
128 // Generally the top error shouldn't be verbose, but check it anyways.
129 if is_verbose(err.as_ref()) {
130 return true;
131 }
132 if as_err {
133 drop(shell.error(&err));
134 } else {
135 drop(writeln!(shell.err(), "{}", err));
136 }
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) {
141 return true;
142 }
143 drop(writeln!(shell.err(), "\nCaused by:"));
144 drop(write!(
145 shell.err(),
146 "{}",
147 indented_lines(&cause.to_string())
148 ));
149 }
150 false
151 }
152
153 pub fn version() -> VersionInfo {
154 macro_rules! option_env_str {
155 ($name:expr) => {
156 option_env!($name).map(|s| s.to_string())
157 };
158 }
159
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
164 // in your packages.
165 //
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.
169 //
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`.
174 let major = 1;
175 let minor = env!("CARGO_PKG_VERSION_MINOR").parse::<u8>().unwrap() - 1;
176 let patch = env!("CARGO_PKG_VERSION_PATCH").parse::<u8>().unwrap();
177
178 match option_env!("CFG_RELEASE_CHANNEL") {
179 // We have environment variables set up from configure/make.
180 Some(_) => {
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(),
185 });
186 VersionInfo {
187 major,
188 minor,
189 patch,
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(),
193 commit_info,
194 }),
195 }
196 }
197 // We are being compiled by Cargo itself.
198 None => VersionInfo {
199 major,
200 minor,
201 patch,
202 pre_release: option_env_str!("CARGO_PKG_VERSION_PRE"),
203 cfg_info: None,
204 },
205 }
206 }