diff --git a/src/bin/cargo.rs b/src/bin/cargo.rs index 2c267623d2b..cdd110f3cab 100644 --- a/src/bin/cargo.rs +++ b/src/bin/cargo.rs @@ -7,13 +7,14 @@ extern crate toml; #[macro_use] extern crate log; use std::collections::BTreeSet; +use std::collections::HashMap; use std::env; use std::fs; use std::path::{Path,PathBuf}; use cargo::core::shell::Verbosity; use cargo::execute_main_without_stdin; -use cargo::util::{self, CliResult, lev_distance, Config, human}; +use cargo::util::{self, CliResult, lev_distance, Config, human, CargoResult}; use cargo::util::CliError; use cargo::util::process_builder::process; @@ -138,7 +139,7 @@ fn execute(flags: Flags, config: &Config) -> CliResult> { return Ok(None) } - let args = match &flags.arg_command[..] { + let mut args = match &flags.arg_command[..] { // For the commands `cargo` and `cargo help`, re-execute ourselves as // `cargo -h` so we can go through the normal process of printing the // help message. @@ -166,9 +167,30 @@ fn execute(flags: Flags, config: &Config) -> CliResult> { // For all other invocations, we're of the form `cargo foo args...`. We // use the exact environment arguments to preserve tokens like `--` for // example. - _ => env::args().collect(), + _ => { + let mut default_alias = HashMap::new(); + default_alias.insert("b", "build".to_string()); + default_alias.insert("t", "test".to_string()); + default_alias.insert("r", "run".to_string()); + let mut args: Vec = env::args().collect(); + if let Some(new_command) = default_alias.get(&args[1][..]){ + args[1] = new_command.clone(); + } + args + } }; + let alias_list = try!(aliased_command(&config, &args[1])); + if let Some(alias_command) = alias_list { + // Replace old command with new command and flags + let chain = args.iter().take(1) + .chain(alias_command.iter()) + .chain(args.iter().skip(2)) + .map(|s| s.to_string()) + .collect(); + args = chain; + } + macro_rules! cmd{ ($name:ident) => (if args[1] == stringify!($name).replace("_", "-") { config.shell().set_verbosity(Verbosity::Verbose); @@ -186,6 +208,30 @@ fn execute(flags: Flags, config: &Config) -> CliResult> { Ok(None) } +fn aliased_command(config: &Config, command: &String) -> CargoResult>> { + let alias_name = format!("alias.{}", command); + let mut result = Ok(None); + match config.get_string(&alias_name) { + Ok(value) => { + if let Some(record) = value { + let alias_commands = record.val.split_whitespace() + .map(|s| s.to_string()) + .collect(); + result = Ok(Some(alias_commands)); + } + }, + Err(_) => { + let value = try!(config.get_list(&alias_name)); + if let Some(record) = value { + let alias_commands: Vec = record.val.iter() + .map(|s| s.0.to_string()).collect(); + result = Ok(Some(alias_commands)); + } + } + } + result +} + fn find_closest(config: &Config, cmd: &str) -> Option { let cmds = list_commands(config); // Only consider candidates with a lev_distance of 3 or less so we don't diff --git a/src/doc/config.md b/src/doc/config.md index 551e534eb20..d091633b1d1 100644 --- a/src/doc/config.md +++ b/src/doc/config.md @@ -93,6 +93,15 @@ color = 'auto' # whether cargo colorizes output # Network configuration [net] retry = 2 # number of times a network call will automatically retried + +# Alias cargo commands. The first 3 aliases are built in. If your +command requires grouped whitespace use the list format. +[alias] +b = "build" +t = "test" +r = "run" +rr = "run --release" +space_example = ["run", "--release", "--", "\"command list\""] ``` # Environment variables diff --git a/tests/cargo_alias_config.rs b/tests/cargo_alias_config.rs new file mode 100644 index 00000000000..52453a1deb4 --- /dev/null +++ b/tests/cargo_alias_config.rs @@ -0,0 +1,103 @@ +extern crate cargotest; +extern crate hamcrest; +use cargotest::support::{project, execs, basic_bin_manifest}; +use hamcrest::{assert_that}; + +#[test] +fn alias_incorrect_config_type() { + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", r#" + fn main() { + }"#) + .file(".cargo/config",r#" + [alias] + b-cargo-test = 5 + "#);; + + assert_that(p.cargo_process("b-cargo-test").arg("-v"), + execs().with_status(101). + with_stderr_contains("[ERROR] invalid configuration \ +for key `alias.b-cargo-test` +expected a list, but found a integer in [..]")); +} + + +#[test] +fn alias_default_config_overrides_config() { + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", r#" + fn main() { + }"#) + .file(".cargo/config",r#" + [alias] + b = "not_build" + "#);; + + assert_that(p.cargo_process("b").arg("-v"), + execs().with_status(0). + with_stderr_contains("[COMPILING] foo v0.5.0 [..]")); +} + +#[test] +fn alias_config() { + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", r#" + fn main() { + }"#) + .file(".cargo/config",r#" + [alias] + b-cargo-test = "build" + "#);; + + assert_that(p.cargo_process("b-cargo-test").arg("-v"), + execs().with_status(0). + with_stderr_contains("[COMPILING] foo v0.5.0 [..] +[RUNNING] `rustc [..] --crate-name foo --crate-type \ +bin -g --out-dir [..] --emit=dep-info,link -L dependency=[..]\ +-L dependency=[..]")); +} + +#[test] +fn alias_list_test() { + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", r#" + fn main() { + }"#) + .file(".cargo/config",r#" + [alias] + b-cargo-test = ["build", "--release"] + "#);; + + assert_that(p.cargo_process("b-cargo-test").arg("-v"), + execs().with_status(0). + with_stderr_contains("[COMPILING] foo v0.5.0 [..]"). + with_stderr_contains("[RUNNING] `rustc [..] --crate-name foo \ + --crate-type bin -C opt-level=3 --out-dir [..]\ + --emit=dep-info,link -L dependency=[..]") + ); +} + +#[test] +fn alias_with_flags_config() { + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", r#" + fn main() { + }"#) + .file(".cargo/config",r#" + [alias] + b-cargo-test = "build --release" + "#);; + + assert_that(p.cargo_process("b-cargo-test").arg("-v"), + execs().with_status(0). + with_stderr_contains("[COMPILING] foo v0.5.0 [..]"). + with_stderr_contains("[RUNNING] `rustc [..] --crate-name foo \ + --crate-type bin -C opt-level=3 --out-dir [..]\ + --emit=dep-info,link -L dependency=[..]") + ); +}