]> git.proxmox.com Git - cargo.git/blame - src/bin/cargo/main.rs
Finish implementing `Value`, use it in helpers
[cargo.git] / src / bin / cargo / main.rs
CommitLineData
92bf2c36 1#![warn(rust_2018_idioms)] // while we're getting used to 2018
9ed82b57
AC
2#![allow(clippy::too_many_arguments)] // large project
3#![allow(clippy::redundant_closure)] // there's a false positive
ef0b4776 4#![warn(clippy::needless_borrow)]
a91a12b4 5#![warn(clippy::redundant_clone)]
1ca6830e 6
dad9fe66 7use std::collections::BTreeSet;
ee5e24ff 8use std::env;
a6dad622 9use std::fs;
ef4c09f9 10use std::path::{Path, PathBuf};
ba273af5 11
901065f5 12use cargo::core::shell::Shell;
7d7fe679 13use cargo::util::{self, closest_msg, command_prelude, CargoResult, CliResult, Config};
37cffbe0 14use cargo::util::{CliError, ProcessError};
3a15f5b7 15
38f81e05 16mod cli;
66bb9dc3 17mod commands;
7acd343b 18
04ddd4d0 19use crate::command_prelude::*;
c3422cfc 20
3512d997 21fn main() {
aa8eff88 22 #[cfg(feature = "pretty-env-logger")]
782266aa 23 pretty_env_logger::init_custom_env("CARGO_LOG");
aa8eff88 24 #[cfg(not(feature = "pretty-env-logger"))]
782266aa 25 env_logger::init_from_env("CARGO_LOG");
75bb1906 26 cargo::core::maybe_allow_nightly_features();
3b93c575 27
89c09e20 28 let mut config = match Config::default() {
3b93c575
AK
29 Ok(cfg) => cfg,
30 Err(e) => {
f8fb0a02 31 let mut shell = Shell::new();
ef043577 32 cargo::exit_with_error(e.into(), &mut shell)
3b93c575
AK
33 }
34 };
35
b02ba377
AC
36 let result = match cargo::ops::fix_maybe_exec_rustc() {
37 Ok(true) => Ok(()),
38 Ok(false) => {
ed6f93a9 39 init_git_transports(&config);
b02ba377
AC
40 let _token = cargo::util::job::setup();
41 cli::main(&mut config)
42 }
43 Err(e) => Err(CliError::from(e)),
16bde4e0
AK
44 };
45
46 match result {
ef043577 47 Err(e) => cargo::exit_with_error(e, &mut *config.shell()),
ef4c09f9 48 Ok(()) => {}
3b93c575 49 }
3512d997 50}
3a15f5b7 51
23591fe5 52fn aliased_command(config: &Config, command: &str) -> CargoResult<Option<Vec<String>>> {
66739f1c 53 let alias_name = format!("alias.{}", command);
e8f37dae
EH
54 let user_alias = match config.get_string(&alias_name) {
55 Ok(Some(record)) => Some(
56 record
57 .val
58 .split_whitespace()
59 .map(|s| s.to_string())
60 .collect(),
61 ),
62 Ok(None) => None,
63 Err(_) => config
64 .get_list(&alias_name)?
65 .map(|record| record.val.iter().map(|s| s.0.to_string()).collect()),
66 };
67 let result = user_alias.or_else(|| match command {
68 "b" => Some(vec!["build".to_string()]),
69 "c" => Some(vec!["check".to_string()]),
70 "r" => Some(vec!["run".to_string()]),
71 "t" => Some(vec!["test".to_string()]),
72 _ => None,
73 });
74 Ok(result)
66739f1c
SBI
75}
76
e68c682a 77/// List all runnable commands
c3422cfc 78fn list_commands(config: &Config) -> BTreeSet<CommandInfo> {
e68c682a
AK
79 let prefix = "cargo-";
80 let suffix = env::consts::EXE_SUFFIX;
81 let mut commands = BTreeSet::new();
82 for dir in search_directories(config) {
83 let entries = match fs::read_dir(dir) {
84 Ok(entries) => entries,
85 _ => continue,
86 };
87 for entry in entries.filter_map(|e| e.ok()) {
88 let path = entry.path();
89 let filename = match path.file_name().and_then(|s| s.to_str()) {
90 Some(filename) => filename,
91 _ => continue,
92 };
93 if !filename.starts_with(prefix) || !filename.ends_with(suffix) {
94 continue;
95 }
96 if is_executable(entry.path()) {
97 let end = filename.len() - suffix.len();
c3422cfc
DW
98 commands.insert(CommandInfo::External {
99 name: filename[prefix.len()..end].to_string(),
100 path: path.clone(),
101 });
e68c682a
AK
102 }
103 }
104 }
105
106 for cmd in commands::builtin() {
c3422cfc
DW
107 commands.insert(CommandInfo::BuiltIn {
108 name: cmd.get_name().to_string(),
af2c3555 109 about: cmd.p.meta.about.map(|s| s.to_string()),
c3422cfc 110 });
e68c682a
AK
111 }
112
113 commands
114}
115
81ce3e1b 116fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&str]) -> CliResult {
20b768e6
AC
117 let command_exe = format!("cargo-{}{}", cmd, env::consts::EXE_SUFFIX);
118 let path = search_directories(config)
ef4c09f9 119 .iter()
120 .map(|dir| dir.join(&command_exe))
121 .find(|file| is_executable(file));
20b768e6 122 let command = match path {
7ed31fab 123 Some(command) => command,
12f5de8e 124 None => {
7d7fe679
EH
125 let cmds = list_commands(config);
126 let did_you_mean = closest_msg(cmd, cmds.iter(), |c| c.name());
127 let err = failure::format_err!("no such subcommand: `{}`{}", cmd, did_you_mean);
1e682848 128 return Err(CliError::new(err, 101));
12f5de8e 129 }
8cce8996 130 };
015a08a0
VK
131
132 let cargo_exe = config.cargo_exe()?;
133 let err = match util::process(&command)
134 .env(cargo::CARGO_ENV, cargo_exe)
deb1c1e1 135 .args(args)
1e682848
AC
136 .exec_replace()
137 {
fdc5b07c
AC
138 Ok(()) => return Ok(()),
139 Err(e) => e,
140 };
141
37cffbe0 142 if let Some(perr) = err.downcast_ref::<ProcessError>() {
e95044e3 143 if let Some(code) = perr.exit.as_ref().and_then(|c| c.code()) {
144 return Err(CliError::code(code));
145 }
fdc5b07c 146 }
48446574 147 Err(CliError::new(err, 101))
a3f6a404 148}
149
a6dad622 150#[cfg(unix)]
7ed31fab 151fn is_executable<P: AsRef<Path>>(path: P) -> bool {
a6dad622 152 use std::os::unix::prelude::*;
ef4c09f9 153 fs::metadata(path)
154 .map(|metadata| metadata.is_file() && metadata.permissions().mode() & 0o111 != 0)
155 .unwrap_or(false)
a6dad622
AC
156}
157#[cfg(windows)]
7ed31fab 158fn is_executable<P: AsRef<Path>>(path: P) -> bool {
1e682848
AC
159 fs::metadata(path)
160 .map(|metadata| metadata.is_file())
161 .unwrap_or(false)
a3f6a404 162}
163
20b768e6 164fn search_directories(config: &Config) -> Vec<PathBuf> {
8eac1d62 165 let mut dirs = vec![config.home().clone().into_path_unlocked().join("bin")];
1384050e 166 if let Some(val) = env::var_os("PATH") {
ee5e24ff
AC
167 dirs.extend(env::split_paths(&val));
168 }
a1b3d153 169 dirs
3a15f5b7 170}
b20b0f6b
AC
171
172fn init_git_transports(config: &Config) {
9f932e11
JG
173 // Only use a custom transport if any HTTP options are specified,
174 // such as proxies or custom certificate authorities. The custom
175 // transport, however, is not as well battle-tested.
176
177 match cargo::ops::needs_custom_http_transport(config) {
8eb28ad6 178 Ok(true) => {}
ef4c09f9 179 _ => return,
b20b0f6b
AC
180 }
181
182 let handle = match cargo::ops::http_handle(config) {
183 Ok(handle) => handle,
184 Err(..) => return,
185 };
186
187 // The unsafety of the registration function derives from two aspects:
188 //
189 // 1. This call must be synchronized with all other registration calls as
190 // well as construction of new transports.
191 // 2. The argument is leaked.
192 //
193 // We're clear on point (1) because this is only called at the start of this
194 // binary (we know what the state of the world looks like) and we're mostly
195 // clear on point (2) because we'd only free it after everything is done
196 // anyway
197 unsafe {
198 git2_curl::register(handle);
199 }
200}