1 #![warn(rust_2018_idioms)] // while we're getting used to 2018
4 use cargo
::util
::toml
::StringOrVec
;
5 use cargo
::util
::CliError
;
6 use cargo
::util
::{self, closest_msg, command_prelude, CargoResult, CliResult, Config}
;
7 use cargo_util
::{ProcessBuilder, ProcessError}
;
8 use std
::collections
::BTreeMap
;
11 use std
::path
::{Path, PathBuf}
;
16 use crate::command_prelude
::*;
19 #[cfg(feature = "pretty-env-logger")]
20 pretty_env_logger
::init_custom_env("CARGO_LOG");
21 #[cfg(not(feature = "pretty-env-logger"))]
22 env_logger
::init_from_env("CARGO_LOG");
24 let mut config
= cli
::LazyConfig
::new();
26 let result
= if let Some(lock_addr
) = cargo
::ops
::fix_get_proxy_lock_addr() {
27 cargo
::ops
::fix_exec_rustc(config
.get(), &lock_addr
).map_err(|e
| CliError
::from(e
))
29 let _token
= cargo
::util
::job
::setup();
30 cli
::main(&mut config
)
34 Err(e
) => cargo
::exit_with_error(e
, &mut config
.get_mut().shell()),
39 /// Table for defining the aliases which come builtin in `Cargo`.
40 /// The contents are structured as: `(alias, aliased_command, description)`.
41 const BUILTIN_ALIASES
: [(&str, &str, &str); 5] = [
42 ("b", "build", "alias: build"),
43 ("c", "check", "alias: check"),
44 ("d", "doc", "alias: doc"),
45 ("r", "run", "alias: run"),
46 ("t", "test", "alias: test"),
49 /// Function which contains the list of all of the builtin aliases and it's
50 /// corresponding execs represented as &str.
51 fn builtin_aliases_execs(cmd
: &str) -> Option
<&(&str, &str, &str)> {
52 BUILTIN_ALIASES
.iter().find(|alias
| alias
.0 == cmd
)
55 fn aliased_command(config
: &Config
, command
: &str) -> CargoResult
<Option
<Vec
<String
>>> {
56 let alias_name
= format
!("alias.{}", command
);
57 let user_alias
= match config
.get_string(&alias_name
) {
58 Ok(Some(record
)) => Some(
62 .map(|s
| s
.to_string())
66 Err(_
) => config
.get
::<Option
<Vec
<String
>>>(&alias_name
)?
,
69 let result
= user_alias
.or_else(|| {
70 builtin_aliases_execs(command
).map(|command_str
| vec
![command_str
.1.to_string()])
75 /// List all runnable commands
76 fn list_commands(config
: &Config
) -> BTreeMap
<String
, CommandInfo
> {
77 let prefix
= "cargo-";
78 let suffix
= env
::consts
::EXE_SUFFIX
;
79 let mut commands
= BTreeMap
::new();
80 for dir
in search_directories(config
) {
81 let entries
= match fs
::read_dir(dir
) {
82 Ok(entries
) => entries
,
85 for entry
in entries
.filter_map(|e
| e
.ok()) {
86 let path
= entry
.path();
87 let filename
= match path
.file_name().and_then(|s
| s
.to_str()) {
88 Some(filename
) => filename
,
91 if !filename
.starts_with(prefix
) || !filename
.ends_with(suffix
) {
94 if is_executable(entry
.path()) {
95 let end
= filename
.len() - suffix
.len();
97 filename
[prefix
.len()..end
].to_string(),
98 CommandInfo
::External { path: path.clone() }
,
104 for cmd
in commands
::builtin() {
106 cmd
.get_name().to_string(),
107 CommandInfo
::BuiltIn
{
108 about
: cmd
.get_about().map(|s
| s
.to_string()),
113 // Add the builtin_aliases and them descriptions to the
114 // `commands` `BTreeMap`.
115 for command
in &BUILTIN_ALIASES
{
117 command
.0.to_string(),
118 CommandInfo
::BuiltIn
{
119 about
: Some(command
.2.to_string()),
124 // Add the user-defined aliases
125 if let Ok(aliases
) = config
.get
::<BTreeMap
<String
, StringOrVec
>>("alias") {
126 for (name
, target
) in aliases
.iter() {
130 target
: target
.clone(),
136 // `help` is special, so it needs to be inserted separately.
139 CommandInfo
::BuiltIn
{
140 about
: Some("Displays help for a cargo subcommand".to_string()),
147 fn find_external_subcommand(config
: &Config
, cmd
: &str) -> Option
<PathBuf
> {
148 let command_exe
= format
!("cargo-{}{}", cmd
, env
::consts
::EXE_SUFFIX
);
149 search_directories(config
)
151 .map(|dir
| dir
.join(&command_exe
))
152 .find(|file
| is_executable(file
))
155 fn execute_external_subcommand(config
: &Config
, cmd
: &str, args
: &[&str]) -> CliResult
{
156 let path
= find_external_subcommand(config
, cmd
);
157 let command
= match path
{
158 Some(command
) => command
,
160 let err
= if cmd
.starts_with('
+'
) {
162 "no such subcommand: `{}`\n\n\t\
163 Cargo does not handle `+toolchain` directives.\n\t\
164 Did you mean to invoke `cargo` through `rustup` instead?",
168 let suggestions
= list_commands(config
);
169 let did_you_mean
= closest_msg(cmd
, suggestions
.keys(), |c
| c
);
172 "no such subcommand: `{}`{}\n\n\t\
173 View all installed commands with `cargo --list`",
179 return Err(CliError
::new(err
, 101));
183 let cargo_exe
= config
.cargo_exe()?
;
184 let mut cmd
= ProcessBuilder
::new(&command
);
185 cmd
.env(cargo
::CARGO_ENV
, cargo_exe
).args(args
);
186 if let Some(client
) = config
.jobserver_from_env() {
187 cmd
.inherit_jobserver(client
);
189 let err
= match cmd
.exec_replace() {
190 Ok(()) => return Ok(()),
194 if let Some(perr
) = err
.downcast_ref
::<ProcessError
>() {
195 if let Some(code
) = perr
.code
{
196 return Err(CliError
::code(code
));
199 Err(CliError
::new(err
, 101))
203 fn is_executable
<P
: AsRef
<Path
>>(path
: P
) -> bool
{
204 use std
::os
::unix
::prelude
::*;
206 .map(|metadata
| metadata
.is_file() && metadata
.permissions().mode() & 0o111 != 0)
210 fn is_executable
<P
: AsRef
<Path
>>(path
: P
) -> bool
{
211 path
.as_ref().is_file()
214 fn search_directories(config
: &Config
) -> Vec
<PathBuf
> {
215 let mut dirs
= vec
![config
.home().clone().into_path_unlocked().join("bin")];
216 if let Some(val
) = env
::var_os("PATH") {
217 dirs
.extend(env
::split_paths(&val
));
222 fn init_git_transports(config
: &Config
) {
223 // Only use a custom transport if any HTTP options are specified,
224 // such as proxies or custom certificate authorities. The custom
225 // transport, however, is not as well battle-tested.
227 match cargo
::ops
::needs_custom_http_transport(config
) {
232 let handle
= match cargo
::ops
::http_handle(config
) {
233 Ok(handle
) => handle
,
237 // The unsafety of the registration function derives from two aspects:
239 // 1. This call must be synchronized with all other registration calls as
240 // well as construction of new transports.
241 // 2. The argument is leaked.
243 // We're clear on point (1) because this is only called at the start of this
244 // binary (we know what the state of the world looks like) and we're mostly
245 // clear on point (2) because we'd only free it after everything is done
248 git2_curl
::register(handle
);
251 // Disabling the owner validation in git can, in theory, lead to code execution
252 // vulnerabilities. However, libgit2 does not launch executables, which is the foundation of
253 // the original security issue. Meanwhile, issues with refusing to load git repos in
254 // `CARGO_HOME` for example will likely be very frustrating for users. So, we disable the
257 // For further discussion of Cargo's current interactions with git, see
259 // https://github.com/rust-lang/rfcs/pull/3279
261 // and in particular the subsection on "Git support".
263 // Note that we only disable this when Cargo is run as a binary. If Cargo is used as a library,
264 // this code won't be invoked. Instead, developers will need to explicitly disable the
265 // validation in their code. This is inconvenient, but won't accidentally open consuming
266 // applications up to security issues if they use git2 to open repositories elsewhere in their
269 if git2
::opts
::set_verify_owner_validation(false).is_err() {