]>
Commit | Line | Data |
---|---|---|
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 | 7 | use std::collections::BTreeSet; |
ee5e24ff | 8 | use std::env; |
a6dad622 | 9 | use std::fs; |
ef4c09f9 | 10 | use std::path::{Path, PathBuf}; |
ba273af5 | 11 | |
901065f5 | 12 | use cargo::core::shell::Shell; |
7d7fe679 | 13 | use cargo::util::{self, closest_msg, command_prelude, CargoResult, CliResult, Config}; |
37cffbe0 | 14 | use cargo::util::{CliError, ProcessError}; |
3a15f5b7 | 15 | |
38f81e05 | 16 | mod cli; |
66bb9dc3 | 17 | mod commands; |
7acd343b | 18 | |
04ddd4d0 | 19 | use crate::command_prelude::*; |
c3422cfc | 20 | |
3512d997 | 21 | fn 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 | 52 | fn 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 | 78 | fn 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 | 116 | fn 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 | 151 | fn 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 | 158 | fn 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 | 164 | fn 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 | |
172 | fn 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 | } |