From 261ae46fc3febaabc9ccb5ed0a08e65d0e7cdcc0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 13 Feb 2017 19:02:27 +0300 Subject: [PATCH] cargo run supports --package argument closes #3529 --- src/bin/run.rs | 41 ++++++++++-------- src/cargo/ops/cargo_compile.rs | 4 +- src/cargo/ops/cargo_run.rs | 23 +++++++--- tests/run.rs | 77 ++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 24 deletions(-) diff --git a/src/bin/run.rs b/src/bin/run.rs index 79a0a326f1a..cd242860c4d 100644 --- a/src/bin/run.rs +++ b/src/bin/run.rs @@ -1,3 +1,5 @@ +use std::iter::FromIterator; + use cargo::core::Workspace; use cargo::ops::{self, MessageFormat, Packages}; use cargo::util::{CliResult, CliError, Config, Human}; @@ -7,6 +9,7 @@ use cargo::util::important_paths::{find_root_manifest_for_wd}; pub struct Options { flag_bin: Option, flag_example: Option, + flag_package: Option, flag_jobs: Option, flag_features: Vec, flag_all_features: bool, @@ -30,22 +33,23 @@ Usage: cargo run [options] [--] [...] Options: - -h, --help Print this message - --bin NAME Name of the bin target to run - --example NAME Name of the example target to run - -j N, --jobs N Number of parallel jobs, defaults to # of CPUs - --release Build artifacts in release mode, with optimizations - --features FEATURES Space-separated list of features to also build - --all-features Build all available features - --no-default-features Do not build the `default` feature - --target TRIPLE Build for the target triple - --manifest-path PATH Path to the manifest to execute - -v, --verbose ... Use verbose output (-vv very verbose/build.rs output) - -q, --quiet No output printed to stdout - --color WHEN Coloring: auto, always, never - --message-format FMT Error format: human, json [default: human] - --frozen Require Cargo.lock and cache are up to date - --locked Require Cargo.lock is up to date + -h, --help Print this message + --bin NAME Name of the bin target to run + --example NAME Name of the example target to run + -p SPEC, --package SPEC Package with the target to run + -j N, --jobs N Number of parallel jobs, defaults to # of CPUs + --release Build artifacts in release mode, with optimizations + --features FEATURES Space-separated list of features to also build + --all-features Build all available features + --no-default-features Do not build the `default` feature + --target TRIPLE Build for the target triple + --manifest-path PATH Path to the manifest to execute + -v, --verbose ... Use verbose output (-vv very verbose/build.rs output) + -q, --quiet No output printed to stdout + --color WHEN Coloring: auto, always, never + --message-format FMT Error format: human, json [default: human] + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date If neither `--bin` nor `--example` are given, then if the project only has one bin target it will be run. Otherwise `--bin` specifies the bin target to run, @@ -74,6 +78,9 @@ pub fn execute(options: Options, config: &Config) -> CliResult { examples.push(s); } + let packages = Vec::from_iter(options.flag_package.iter().cloned()); + let spec = Packages::Packages(&packages); + let compile_opts = ops::CompileOptions { config: config, jobs: options.flag_jobs, @@ -81,7 +88,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult { features: &options.flag_features, all_features: options.flag_all_features, no_default_features: options.flag_no_default_features, - spec: Packages::Packages(&[]), + spec: spec, release: options.flag_release, mode: ops::CompileMode::Build, filter: if examples.is_empty() && bins.is_empty() { diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 1c6e6cbac5a..ee882b7b6f4 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -101,7 +101,7 @@ pub enum MessageFormat { Json } -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum Packages<'a> { All, Packages(&'a [String]), @@ -141,7 +141,7 @@ pub fn compile<'a>(ws: &Workspace<'a>, options: &CompileOptions<'a>) } pub fn compile_with_exec<'a>(ws: &Workspace<'a>, - options: &CompileOptions<'a>, + options: &CompileOptions<'a>, exec: Arc) -> CargoResult> { for member in ws.members() { diff --git a/src/cargo/ops/cargo_run.rs b/src/cargo/ops/cargo_run.rs index 1c4c4a485c1..170e038852b 100644 --- a/src/cargo/ops/cargo_run.rs +++ b/src/cargo/ops/cargo_run.rs @@ -1,16 +1,28 @@ use std::path::Path; -use ops::{self, CompileFilter}; -use util::{self, CargoResult, ProcessError}; +use ops::{self, CompileFilter, Packages}; +use util::{self, human, CargoResult, ProcessError}; use core::Workspace; pub fn run(ws: &Workspace, options: &ops::CompileOptions, args: &[String]) -> CargoResult> { let config = ws.config(); - let root = ws.current()?; - let mut bins = root.manifest().targets().iter().filter(|a| { + let pkg = match options.spec { + Packages::All => unreachable!("cargo run supports single package only"), + Packages::Packages(xs) => match xs.len() { + 0 => ws.current()?, + 1 => ws.members() + .find(|pkg| pkg.name() == xs[0]) + .ok_or_else(|| human( + format!("package `{}` is not a member of the workspace", xs[0]) + ))?, + _ => unreachable!("cargo run supports single package only"), + } + }; + + let mut bins = pkg.manifest().targets().iter().filter(|a| { !a.is_lib() && !a.is_custom_build() && match options.filter { CompileFilter::Everything => a.is_bin(), CompileFilter::Only { .. } => options.filter.matches(a), @@ -41,6 +53,7 @@ pub fn run(ws: &Workspace, } let compile = ops::compile(ws, options)?; + assert_eq!(compile.binaries.len(), 1); let exe = &compile.binaries[0]; let exe = match util::without_prefix(&exe, config.cwd()) { Some(path) if path.file_name() == Some(path.as_os_str()) @@ -48,7 +61,7 @@ pub fn run(ws: &Workspace, Some(path) => path.to_path_buf(), None => exe.to_path_buf(), }; - let mut process = compile.target_process(exe, &root)?; + let mut process = compile.target_process(exe, &pkg)?; process.args(args).cwd(config.cwd()); config.shell().status("Running", process.to_string())?; diff --git a/tests/run.rs b/tests/run.rs index 36d2b5a28ac..a95dcf01e75 100644 --- a/tests/run.rs +++ b/tests/run.rs @@ -652,3 +652,80 @@ fn fail_no_extra_verbose() { .with_stdout("") .with_stderr("")); } + +#[test] +fn run_multiple_packages() { + let p = project("foo") + .file("foo/Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [workspace] + + [dependencies] + d1 = { path = "d1" } + d2 = { path = "d2" } + d3 = { path = "../d3" } # outside of the workspace + + [[bin]] + name = "foo" + "#) + .file("foo/src/foo.rs", "fn main() { println!(\"foo\"); }") + .file("foo/d1/Cargo.toml", r#" + [package] + name = "d1" + version = "0.0.1" + authors = [] + + [[bin]] + name = "d1" + "#) + .file("foo/d1/src/lib.rs", "") + .file("foo/d1/src/main.rs", "fn main() { println!(\"d1\"); }") + .file("foo/d2/Cargo.toml", r#" + [package] + name = "d2" + version = "0.0.1" + authors = [] + + [[bin]] + name = "d2" + "#) + .file("foo/d2/src/main.rs", "fn main() { println!(\"d2\"); }") + .file("d3/Cargo.toml", r#" + [package] + name = "d3" + version = "0.0.1" + authors = [] + "#) + .file("d3/src/main.rs", "fn main() { println!(\"d2\"); }"); + + let p = p.build(); + + let cargo = || { + let mut process_builder = p.cargo("run"); + process_builder.cwd(p.root().join("foo")); + process_builder + }; + + assert_that(cargo().arg("-p").arg("d1"), + execs().with_status(0).with_stdout("d1")); + + assert_that(cargo().arg("-p").arg("d2").arg("--bin").arg("d2"), + execs().with_status(0).with_stdout("d2")); + + assert_that(cargo(), + execs().with_status(0).with_stdout("foo")); + + assert_that(cargo().arg("-p").arg("d1").arg("-p").arg("d2"), + execs() + .with_status(1) + .with_stderr_contains("[ERROR] Invalid arguments.")); + + assert_that(cargo().arg("-p").arg("d3"), + execs() + .with_status(101) + .with_stderr_contains("[ERROR] package `d3` is not a member of the workspace")); +}