]> git.proxmox.com Git - cargo.git/blame - src/bin/cargo.rs
Auto merge of #5150 - Eh2406:more_interning, r=alexcrichton
[cargo.git] / src / bin / cargo.rs
CommitLineData
55321111 1extern crate cargo;
98854f6f 2extern crate env_logger;
37cffbe0
AC
3#[macro_use]
4extern crate failure;
964e72ff 5extern crate git2_curl;
a6dad622 6extern crate toml;
ef4c09f9 7#[macro_use]
8extern crate log;
a5a298f1
AC
9#[macro_use]
10extern crate serde_derive;
11extern crate serde_json;
3a15f5b7 12
8ab8b58b 13use std::collections::BTreeSet;
66739f1c 14use std::collections::HashMap;
ee5e24ff 15use std::env;
a6dad622 16use std::fs;
ef4c09f9 17use std::path::{Path, PathBuf};
ba273af5 18
f8fb0a02 19use cargo::core::shell::{Shell, Verbosity};
37cffbe0
AC
20use cargo::util::{self, CliResult, lev_distance, Config, CargoResult};
21use cargo::util::{CliError, ProcessError};
3a15f5b7 22
10373f40 23#[derive(Deserialize)]
dd342960 24pub struct Flags {
3512d997 25 flag_list: bool,
3fb50545 26 flag_version: bool,
805dfe4e 27 flag_verbose: u32,
be48d5b1 28 flag_quiet: Option<bool>,
6f2b241b 29 flag_color: Option<String>,
8dad57e8 30 flag_explain: Option<String>,
3512d997
AC
31 arg_command: String,
32 arg_args: Vec<String>,
a504f480
AC
33 flag_locked: bool,
34 flag_frozen: bool,
f26fc37d
AC
35 #[serde(rename = "flag_Z")]
36 flag_z: Vec<String>,
3a15f5b7
YK
37}
38
3512d997 39const USAGE: &'static str = "
ba273af5
AC
40Rust's package manager
41
42Usage:
43 cargo <command> [<args>...]
fff5de37 44 cargo [options]
ba273af5
AC
45
46Options:
6f2b241b
MB
47 -h, --help Display this message
48 -V, --version Print version info and exit
49 --list List installed commands
8dad57e8 50 --explain CODE Run `rustc --explain CODE`
d6d69a91 51 -v, --verbose ... Use verbose output (-vv very verbose/build.rs output)
6f2b241b
MB
52 -q, --quiet No output printed to stdout
53 --color WHEN Coloring: auto, always, never
a504f480
AC
54 --frozen Require Cargo.lock and cache are up to date
55 --locked Require Cargo.lock is up to date
f26fc37d 56 -Z FLAG ... Unstable (nightly-only) flags to Cargo
ba273af5 57
350223e9 58Some common cargo commands are (see all commands with --list):
ba273af5 59 build Compile the current project
4b82fdc0 60 check Analyze the current project and report errors, but don't build object files
ba273af5
AC
61 clean Remove the target directory
62 doc Build this project's and its dependencies' documentation
63 new Create a new cargo project
81d1ab24 64 init Create a new cargo project in an existing directory
ba273af5
AC
65 run Build and execute src/main.rs
66 test Run the tests
92e40c43 67 bench Run the benchmarks
620b2fe1 68 update Update dependencies listed in Cargo.lock
ae783c6c 69 search Search registry for crates
9672e3bd 70 publish Package and upload this project to the registry
5a427b47 71 install Install a Rust binary
ad2a1f48 72 uninstall Uninstall a Rust binary
ba273af5
AC
73
74See 'cargo help <command>' for more information on a specific command.
3512d997
AC
75";
76
77fn main() {
a540a39b 78 env_logger::init();
3b93c575 79
89c09e20 80 let mut config = match Config::default() {
3b93c575
AK
81 Ok(cfg) => cfg,
82 Err(e) => {
f8fb0a02 83 let mut shell = Shell::new();
ef043577 84 cargo::exit_with_error(e.into(), &mut shell)
3b93c575
AK
85 }
86 };
87
88 let result = (|| {
ef4c09f9 89 let args: Vec<_> = try!(env::args_os()
90 .map(|s| {
2f3955a2 91 s.into_string().map_err(|s| {
37cffbe0 92 format_err!("invalid unicode in argument: {:?}", s)
2f3955a2 93 })
3b93c575 94 })
ef4c09f9 95 .collect());
3b93c575 96 let rest = &args;
89c09e20 97 cargo::call_main_without_stdin(execute, &mut config, USAGE, rest, true)
3b93c575
AK
98 })();
99
100 match result {
ef043577 101 Err(e) => cargo::exit_with_error(e, &mut *config.shell()),
ef4c09f9 102 Ok(()) => {}
3b93c575 103 }
3512d997 104}
3a15f5b7 105
20b768e6 106macro_rules! each_subcommand{
d521a9a0 107 ($mac:ident) => {
20b768e6
AC
108 $mac!(bench);
109 $mac!(build);
4b82fdc0 110 $mac!(check);
20b768e6
AC
111 $mac!(clean);
112 $mac!(doc);
113 $mac!(fetch);
114 $mac!(generate_lockfile);
115 $mac!(git_checkout);
116 $mac!(help);
800172fb 117 $mac!(init);
20b768e6
AC
118 $mac!(install);
119 $mac!(locate_project);
120 $mac!(login);
fec3ce92 121 $mac!(metadata);
20b768e6
AC
122 $mac!(new);
123 $mac!(owner);
124 $mac!(package);
125 $mac!(pkgid);
126 $mac!(publish);
127 $mac!(read_manifest);
128 $mac!(run);
129 $mac!(rustc);
130 $mac!(rustdoc);
131 $mac!(search);
132 $mac!(test);
133 $mac!(uninstall);
134 $mac!(update);
135 $mac!(verify_project);
136 $mac!(version);
137 $mac!(yank);
d521a9a0
JS
138 }
139}
140
bd0e5123
JS
141macro_rules! declare_mod {
142 ($name:ident) => ( pub mod $name; )
20b768e6 143}
bd0e5123 144each_subcommand!(declare_mod);
8cce8996 145
739b1309
YK
146/**
147 The top-level `cargo` command handles configuration and project location
148 because they are fundamental (and intertwined). Other commands can rely
149 on this top-level information.
150*/
89c09e20 151fn execute(flags: Flags, config: &mut Config) -> CliResult {
82655b46 152 config.configure(flags.flag_verbose,
ef4c09f9 153 flags.flag_quiet,
154 &flags.flag_color,
155 flags.flag_frozen,
f26fc37d
AC
156 flags.flag_locked,
157 &flags.flag_z)?;
8cce8996 158
b20b0f6b 159 init_git_transports(config);
5ede71e5 160 let _token = cargo::util::job::setup();
b20b0f6b 161
3fb50545 162 if flags.flag_version {
50e1c1a5
NF
163 let version = cargo::version();
164 println!("{}", version);
ef4c09f9 165 if flags.flag_verbose > 0 {
50e1c1a5 166 println!("release: {}.{}.{}",
ef4c09f9 167 version.major,
168 version.minor,
169 version.patch);
50e1c1a5
NF
170 if let Some(ref cfg) = version.cfg_info {
171 if let Some(ref ci) = cfg.commit_info {
172 println!("commit-hash: {}", ci.commit_hash);
173 println!("commit-date: {}", ci.commit_date);
174 }
175 }
176 }
ef4c09f9 177 return Ok(());
3fb50545
AL
178 }
179
a3f6a404 180 if flags.flag_list {
181 println!("Installed Commands:");
20b768e6 182 for command in list_commands(config) {
f76db9c7
PW
183 let (command, path) = command;
184 if flags.flag_verbose > 0 {
185 match path {
186 Some(p) => println!(" {:<20} {}", command, p),
187 None => println!(" {:<20}", command),
188 }
189 } else {
190 println!(" {}", command);
191 }
ef4c09f9 192 }
193 return Ok(());
a3f6a404 194 }
8cce8996 195
8dad57e8 196 if let Some(ref code) = flags.flag_explain {
82655b46 197 let mut procss = config.rustc()?.process();
c7de4859 198 procss.arg("--explain").arg(code).exec()?;
ef4c09f9 199 return Ok(());
8dad57e8
AC
200 }
201
e1eb81ea 202 let args = match &flags.arg_command[..] {
22e7ede6
AC
203 // For the commands `cargo` and `cargo help`, re-execute ourselves as
204 // `cargo -h` so we can go through the normal process of printing the
205 // help message.
3ce79da0 206 "" | "help" if flags.arg_args.is_empty() => {
b40e1db7 207 config.shell().set_verbosity(Verbosity::Verbose);
22e7ede6 208 let args = &["cargo".to_string(), "-h".to_string()];
ef043577 209 return cargo::call_main_without_stdin(execute, config, USAGE, args, false);
a3f6a404 210 }
22e7ede6
AC
211
212 // For `cargo help -h` and `cargo help --help`, print out the help
213 // message for `cargo help`
ef4c09f9 214 "help" if flags.arg_args[0] == "-h" || flags.arg_args[0] == "--help" => {
2ff5f53b 215 vec!["cargo".to_string(), "help".to_string(), "-h".to_string()]
22e7ede6
AC
216 }
217
218 // For `cargo help foo`, print out the usage message for the specified
219 // subcommand by executing the command with the `-h` flag.
ef4c09f9 220 "help" => vec!["cargo".to_string(), flags.arg_args[0].clone(), "-h".to_string()],
22e7ede6
AC
221
222 // For all other invocations, we're of the form `cargo foo args...`. We
223 // use the exact environment arguments to preserve tokens like `--` for
224 // example.
66739f1c
SBI
225 _ => {
226 let mut default_alias = HashMap::new();
227 default_alias.insert("b", "build".to_string());
228 default_alias.insert("t", "test".to_string());
229 default_alias.insert("r", "run".to_string());
230 let mut args: Vec<String> = env::args().collect();
ef4c09f9 231 if let Some(new_command) = default_alias.get(&args[1][..]) {
66739f1c
SBI
232 args[1] = new_command.clone();
233 }
234 args
235 }
8cce8996 236 };
8cce8996 237
23591fe5 238 if let Some(r) = try_execute_builtin_command(config, &args) {
ef043577 239 return r;
66739f1c
SBI
240 }
241
23591fe5 242 let alias_list = aliased_command(config, &args[1])?;
e1eb81ea
AC
243 let args = match alias_list {
244 Some(alias_command) => {
ef4c09f9 245 let chain = args.iter()
246 .take(1)
e1eb81ea
AC
247 .chain(alias_command.iter())
248 .chain(args.iter().skip(2))
249 .map(|s| s.to_string())
250 .collect::<Vec<_>>();
23591fe5 251 if let Some(r) = try_execute_builtin_command(config, &chain) {
ef043577 252 return r;
e1eb81ea
AC
253 } else {
254 chain
255 }
256 }
257 None => args,
258 };
ef043577
AK
259
260 execute_external_subcommand(config, &args[1], &args)
e1eb81ea
AC
261}
262
89c09e20 263fn try_execute_builtin_command(config: &mut Config, args: &[String]) -> Option<CliResult> {
e1eb81ea 264 macro_rules! cmd {
20b768e6 265 ($name:ident) => (if args[1] == stringify!($name).replace("_", "-") {
b40e1db7 266 config.shell().set_verbosity(Verbosity::Verbose);
89c09e20
RD
267 let r = cargo::call_main_without_stdin($name::execute,
268 config,
bd0e5123 269 $name::USAGE,
25e537aa 270 &args,
8cce8996 271 false);
ef043577 272 return Some(r);
20b768e6
AC
273 })
274 }
157d639a 275 each_subcommand!(cmd);
8cce8996 276
ef043577 277 None
a3f6a404 278}
279
23591fe5 280fn aliased_command(config: &Config, command: &str) -> CargoResult<Option<Vec<String>>> {
66739f1c
SBI
281 let alias_name = format!("alias.{}", command);
282 let mut result = Ok(None);
283 match config.get_string(&alias_name) {
284 Ok(value) => {
285 if let Some(record) = value {
ef4c09f9 286 let alias_commands = record.val
287 .split_whitespace()
288 .map(|s| s.to_string())
289 .collect();
66739f1c
SBI
290 result = Ok(Some(alias_commands));
291 }
ef4c09f9 292 }
66739f1c 293 Err(_) => {
82655b46 294 let value = config.get_list(&alias_name)?;
66739f1c 295 if let Some(record) = value {
ef4c09f9 296 let alias_commands: Vec<String> = record.val
297 .iter()
298 .map(|s| s.0.to_string())
299 .collect();
66739f1c
SBI
300 result = Ok(Some(alias_commands));
301 }
302 }
303 }
304 result
305}
306
20b768e6
AC
307fn find_closest(config: &Config, cmd: &str) -> Option<String> {
308 let cmds = list_commands(config);
80fe0e6d
AC
309 // Only consider candidates with a lev_distance of 3 or less so we don't
310 // suggest out-of-the-blue options.
ef4c09f9 311 let mut filtered = cmds.iter()
f76db9c7 312 .map(|&(ref c, _)| (lev_distance(c, cmd), c))
ef4c09f9 313 .filter(|&(d, _)| d < 4)
314 .collect::<Vec<_>>();
80fe0e6d 315 filtered.sort_by(|a, b| a.0.cmp(&b.0));
a1b3d153 316 filtered.get(0).map(|slot| slot.1.clone())
12f5de8e
PW
317}
318
ef4c09f9 319fn execute_external_subcommand(config: &Config, cmd: &str, args: &[String]) -> CliResult {
20b768e6
AC
320 let command_exe = format!("cargo-{}{}", cmd, env::consts::EXE_SUFFIX);
321 let path = search_directories(config)
ef4c09f9 322 .iter()
323 .map(|dir| dir.join(&command_exe))
324 .find(|file| is_executable(file));
20b768e6 325 let command = match path {
7ed31fab 326 Some(command) => command,
12f5de8e 327 None => {
37cffbe0
AC
328 let err = match find_closest(config, cmd) {
329 Some(closest) => {
330 format_err!("no such subcommand: `{}`\n\n\tDid you mean `{}`?\n",
ef4c09f9 331 cmd,
332 closest)
37cffbe0
AC
333 }
334 None => format_err!("no such subcommand: `{}`", cmd),
335 };
336 return Err(CliError::new(err, 101))
12f5de8e 337 }
8cce8996 338 };
015a08a0
VK
339
340 let cargo_exe = config.cargo_exe()?;
341 let err = match util::process(&command)
342 .env(cargo::CARGO_ENV, cargo_exe)
343 .args(&args[1..])
ca0fc8dc 344 .exec_replace() {
fdc5b07c
AC
345 Ok(()) => return Ok(()),
346 Err(e) => e,
347 };
348
37cffbe0 349 if let Some(perr) = err.downcast_ref::<ProcessError>() {
e95044e3 350 if let Some(code) = perr.exit.as_ref().and_then(|c| c.code()) {
351 return Err(CliError::code(code));
352 }
fdc5b07c 353 }
48446574 354 Err(CliError::new(err, 101))
a3f6a404 355}
356
84045231 357/// List all runnable commands
f76db9c7 358fn list_commands(config: &Config) -> BTreeSet<(String, Option<String>)> {
20b768e6
AC
359 let prefix = "cargo-";
360 let suffix = env::consts::EXE_SUFFIX;
8ab8b58b 361 let mut commands = BTreeSet::new();
20b768e6 362 for dir in search_directories(config) {
a6dad622 363 let entries = match fs::read_dir(dir) {
a3f6a404 364 Ok(entries) => entries,
ef4c09f9 365 _ => continue,
a3f6a404 366 };
20b768e6
AC
367 for entry in entries.filter_map(|e| e.ok()) {
368 let path = entry.path();
369 let filename = match path.file_name().and_then(|s| s.to_str()) {
a3f6a404 370 Some(filename) => filename,
ef4c09f9 371 _ => continue,
a3f6a404 372 };
20b768e6 373 if !filename.starts_with(prefix) || !filename.ends_with(suffix) {
ef4c09f9 374 continue;
20b768e6 375 }
7ed31fab
JB
376 if is_executable(entry.path()) {
377 let end = filename.len() - suffix.len();
f76db9c7
PW
378 commands.insert(
379 (filename[prefix.len()..end].to_string(),
380 Some(path.display().to_string()))
381 );
64ff29ff 382 }
6a809efb 383 }
92b449b6 384 }
8cce8996 385
20b768e6 386 macro_rules! add_cmd {
f76db9c7 387 ($cmd:ident) => ({ commands.insert((stringify!($cmd).replace("_", "-"), None)); })
20b768e6 388 }
8cce8996 389 each_subcommand!(add_cmd);
a3f6a404 390 commands
391}
392
a6dad622 393#[cfg(unix)]
7ed31fab 394fn is_executable<P: AsRef<Path>>(path: P) -> bool {
a6dad622 395 use std::os::unix::prelude::*;
ef4c09f9 396 fs::metadata(path)
397 .map(|metadata| metadata.is_file() && metadata.permissions().mode() & 0o111 != 0)
398 .unwrap_or(false)
a6dad622
AC
399}
400#[cfg(windows)]
7ed31fab
JB
401fn is_executable<P: AsRef<Path>>(path: P) -> bool {
402 fs::metadata(path).map(|metadata| metadata.is_file()).unwrap_or(false)
a3f6a404 403}
404
20b768e6 405fn search_directories(config: &Config) -> Vec<PathBuf> {
8eac1d62 406 let mut dirs = vec![config.home().clone().into_path_unlocked().join("bin")];
1384050e 407 if let Some(val) = env::var_os("PATH") {
ee5e24ff
AC
408 dirs.extend(env::split_paths(&val));
409 }
a1b3d153 410 dirs
3a15f5b7 411}
b20b0f6b
AC
412
413fn init_git_transports(config: &Config) {
9f932e11
JG
414 // Only use a custom transport if any HTTP options are specified,
415 // such as proxies or custom certificate authorities. The custom
416 // transport, however, is not as well battle-tested.
417
418 match cargo::ops::needs_custom_http_transport(config) {
8eb28ad6 419 Ok(true) => {}
ef4c09f9 420 _ => return,
b20b0f6b
AC
421 }
422
423 let handle = match cargo::ops::http_handle(config) {
424 Ok(handle) => handle,
425 Err(..) => return,
426 };
427
428 // The unsafety of the registration function derives from two aspects:
429 //
430 // 1. This call must be synchronized with all other registration calls as
431 // well as construction of new transports.
432 // 2. The argument is leaked.
433 //
434 // We're clear on point (1) because this is only called at the start of this
435 // binary (we know what the state of the world looks like) and we're mostly
436 // clear on point (2) because we'd only free it after everything is done
437 // anyway
438 unsafe {
439 git2_curl::register(handle);
440 }
441}