From e1a85a607c5599d4116ecd6394a33674875581b9 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Wed, 26 Apr 2017 01:16:17 +0100 Subject: [PATCH 1/4] Add support for custom target-specific runners When `target.$triple.runner` is specified, it will be used for any execution commands by cargo including `cargo run`, `cargo test` and `cargo bench`. The original file is passed to the runner executable as a first argument. This allows to run tests when cross-comping Rust projects. This is not a complete solution, and might be extended in future for better ergonomics to support passing extra arguments to the runner itself or overriding runner from command line, but it should already unlock major existing usecases. Fixes #1411 Resolves #3626 --- src/cargo/ops/cargo_compile.rs | 7 ++-- src/cargo/ops/cargo_rustc/compilation.rs | 21 +++++++++-- tests/tool-paths.rs | 45 ++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 1256787f4f8..26faf702e31 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -662,8 +662,11 @@ fn scrape_target_config(config: &Config, triple: &str) None => return Ok(ret), }; for (lib_name, value) in table { - if lib_name == "ar" || lib_name == "linker" || lib_name == "rustflags" { - continue + match lib_name.as_str() { + "ar" | "linker" | "runner" | "rustflags" => { + continue + }, + _ => {} } let mut output = BuildOutput { diff --git a/src/cargo/ops/cargo_rustc/compilation.rs b/src/cargo/ops/cargo_rustc/compilation.rs index b34850a7978..594f3cb195c 100644 --- a/src/cargo/ops/cargo_rustc/compilation.rs +++ b/src/cargo/ops/cargo_rustc/compilation.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use semver::Version; use core::{PackageId, Package, Target, TargetKind}; -use util::{self, CargoResult, Config, ProcessBuilder, process, join_paths}; +use util::{self, CargoResult, Config, LazyCell, ProcessBuilder, process, join_paths}; /// A structure returning the result of a compilation. pub struct Compilation<'cfg> { @@ -53,6 +53,8 @@ pub struct Compilation<'cfg> { pub target: String, config: &'cfg Config, + + target_runner: LazyCell>, } impl<'cfg> Compilation<'cfg> { @@ -72,6 +74,7 @@ impl<'cfg> Compilation<'cfg> { cfgs: HashMap::new(), config: config, target: String::new(), + target_runner: LazyCell::new(), } } @@ -91,10 +94,24 @@ impl<'cfg> Compilation<'cfg> { self.fill_env(process(cmd), pkg, true) } + fn target_runner(&self) -> CargoResult<&Option> { + self.target_runner.get_or_try_init(|| { + let key = format!("target.{}.runner", self.target); + Ok(self.config.get_path(&key)?.map(|v| v.val)) + }) + } + /// See `process`. pub fn target_process>(&self, cmd: T, pkg: &Package) -> CargoResult { - self.fill_env(process(cmd), pkg, false) + let builder = if let &Some(ref runner) = self.target_runner()? { + let mut builder = process(runner); + builder.arg(cmd); + builder + } else { + process(cmd) + }; + self.fill_env(builder, pkg, false) } /// Prepares a new process with an appropriate environment to run against diff --git a/tests/tool-paths.rs b/tests/tool-paths.rs index dcb9e636811..641ccba9a74 100644 --- a/tests/tool-paths.rs +++ b/tests/tool-paths.rs @@ -124,3 +124,48 @@ fn relative_tools() { [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", url = foo_url, ar = output.0, linker = output.1))) } + +#[test] +fn custom_runner() { + let target = rustc_host(); + + let foo = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + "#) + .file("src/main.rs", "fn main() {}") + .file("tests/test.rs", "") + .file("benches/bench.rs", "") + .file(".cargo/config", &format!(r#" + [target.{}] + runner = "nonexistent-runner" + "#, target)); + + foo.build(); + + assert_that(foo.cargo("run").args(&["--", "--param"]), + execs().with_stderr_contains(&format!("\ +[COMPILING] foo v0.0.1 ({url}) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `nonexistent-runner target[/]debug[/]foo[EXE] --param` +", url = foo.url()))); + + assert_that(foo.cargo("test").args(&["--test", "test", "--verbose", "--", "--param"]), + execs().with_stderr_contains(&format!("\ +[COMPILING] foo v0.0.1 ({url}) +[RUNNING] `rustc [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `nonexistent-runner [..][/]target[/]debug[/]deps[/]test-[..][EXE] --param` +", url = foo.url()))); + + assert_that(foo.cargo("bench").args(&["--bench", "bench", "--verbose", "--", "--param"]), + execs().with_stderr_contains(&format!("\ +[COMPILING] foo v0.0.1 ({url}) +[RUNNING] `rustc [..]` +[RUNNING] `rustc [..]` +[FINISHED] release [optimized] target(s) in [..] +[RUNNING] `nonexistent-runner [..][/]target[/]release[/]deps[/]bench-[..][EXE] --param --bench` +", url = foo.url()))); +} From 058e805906b4c8840884c95957b68386c83c5fbd Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Wed, 26 Apr 2017 01:32:20 +0100 Subject: [PATCH 2/4] Add documentation about target.$triple.runner --- src/doc/config.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/doc/config.md b/src/doc/config.md index 382b5c14529..679bc5f9bc9 100644 --- a/src/doc/config.md +++ b/src/doc/config.md @@ -57,15 +57,20 @@ email = "..." vcs = "none" # For the following sections, $triple refers to any valid target triple, not the -# literal string "$triple", and it will apply whenever that target triple is +# literal string "$triple", and it will apply whenever that target triple is # being compiled to. 'cfg(...)' refers to the Rust-like `#[cfg]` syntax for # conditional compilation. -[target.$triple] -# This is the linker which is passed to rustc (via `-C linker=`) when the `$triple` +[target.$triple] +# This is the linker which is passed to rustc (via `-C linker=`) when the `$triple` # is being compiled for. By default this flag is not passed to the compiler. -linker = ".." -# Same but for the library archiver which is passed to rustc via `-C ar=`. +linker = ".." +# Same but for the library archiver which is passed to rustc via `-C ar=`. ar = ".." +# If a runner is provided, compiled targets for the `$triple` will be executed +# by invoking the specified runner executable with actual target as first argument. +# This applies to `cargo run`, `cargo test` and `cargo bench` commands. +# By default compiled targets are executed directly. +runner = ".." # custom flags to pass to all compiler invocations that target $triple # this value overrides build.rustflags when both are present rustflags = ["..", ".."] From 851a28404d6ea4ab59fc86a7f757b66b5582d8ec Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Fri, 12 May 2017 23:07:42 +0100 Subject: [PATCH 3/4] Allow runner params --- src/cargo/ops/cargo_rustc/compilation.rs | 9 +++--- src/cargo/util/config.rs | 35 +++++++++++++++++------- tests/tool-paths.rs | 8 +++--- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/cargo/ops/cargo_rustc/compilation.rs b/src/cargo/ops/cargo_rustc/compilation.rs index 594f3cb195c..be3e16a0437 100644 --- a/src/cargo/ops/cargo_rustc/compilation.rs +++ b/src/cargo/ops/cargo_rustc/compilation.rs @@ -54,7 +54,7 @@ pub struct Compilation<'cfg> { config: &'cfg Config, - target_runner: LazyCell>, + target_runner: LazyCell)>>, } impl<'cfg> Compilation<'cfg> { @@ -94,18 +94,19 @@ impl<'cfg> Compilation<'cfg> { self.fill_env(process(cmd), pkg, true) } - fn target_runner(&self) -> CargoResult<&Option> { + fn target_runner(&self) -> CargoResult<&Option<(PathBuf, Vec)>> { self.target_runner.get_or_try_init(|| { let key = format!("target.{}.runner", self.target); - Ok(self.config.get_path(&key)?.map(|v| v.val)) + Ok(self.config.get_path_and_args(&key)?.map(|v| v.val)) }) } /// See `process`. pub fn target_process>(&self, cmd: T, pkg: &Package) -> CargoResult { - let builder = if let &Some(ref runner) = self.target_runner()? { + let builder = if let &Some((ref runner, ref args)) = self.target_runner()? { let mut builder = process(runner); + builder.args(args); builder.arg(cmd); builder } else { diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index aa27fa0436c..f16110d2498 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -215,25 +215,40 @@ impl Config { } } + fn string_to_path(&self, value: String, definition: &Definition) -> PathBuf { + let is_path = value.contains('/') || + (cfg!(windows) && value.contains('\\')); + if is_path { + definition.root(self).join(value) + } else { + // A pathless name + PathBuf::from(value) + } + } + pub fn get_path(&self, key: &str) -> CargoResult>> { if let Some(val) = self.get_string(key)? { - let is_path = val.val.contains('/') || - (cfg!(windows) && val.val.contains('\\')); - let path = if is_path { - val.definition.root(self).join(val.val) - } else { - // A pathless name - PathBuf::from(val.val) - }; Ok(Some(Value { - val: path, - definition: val.definition, + val: self.string_to_path(val.val, &val.definition), + definition: val.definition })) } else { Ok(None) } } + pub fn get_path_and_args(&self, key: &str) -> CargoResult)>>> { + if let Some(mut val) = self.get_list_or_split_string(key)? { + if !val.val.is_empty() { + return Ok(Some(Value { + val: (self.string_to_path(val.val.remove(0), &val.definition), val.val), + definition: val.definition + })); + } + } + Ok(None) + } + pub fn get_list(&self, key: &str) -> CargoResult>>> { match self.get(key)? { diff --git a/tests/tool-paths.rs b/tests/tool-paths.rs index 641ccba9a74..ef1a183d956 100644 --- a/tests/tool-paths.rs +++ b/tests/tool-paths.rs @@ -140,7 +140,7 @@ fn custom_runner() { .file("benches/bench.rs", "") .file(".cargo/config", &format!(r#" [target.{}] - runner = "nonexistent-runner" + runner = "nonexistent-runner -r" "#, target)); foo.build(); @@ -149,7 +149,7 @@ fn custom_runner() { execs().with_stderr_contains(&format!("\ [COMPILING] foo v0.0.1 ({url}) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] -[RUNNING] `nonexistent-runner target[/]debug[/]foo[EXE] --param` +[RUNNING] `nonexistent-runner -r target[/]debug[/]foo[EXE] --param` ", url = foo.url()))); assert_that(foo.cargo("test").args(&["--test", "test", "--verbose", "--", "--param"]), @@ -157,7 +157,7 @@ fn custom_runner() { [COMPILING] foo v0.0.1 ({url}) [RUNNING] `rustc [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] -[RUNNING] `nonexistent-runner [..][/]target[/]debug[/]deps[/]test-[..][EXE] --param` +[RUNNING] `nonexistent-runner -r [..][/]target[/]debug[/]deps[/]test-[..][EXE] --param` ", url = foo.url()))); assert_that(foo.cargo("bench").args(&["--bench", "bench", "--verbose", "--", "--param"]), @@ -166,6 +166,6 @@ fn custom_runner() { [RUNNING] `rustc [..]` [RUNNING] `rustc [..]` [FINISHED] release [optimized] target(s) in [..] -[RUNNING] `nonexistent-runner [..][/]target[/]release[/]deps[/]bench-[..][EXE] --param --bench` +[RUNNING] `nonexistent-runner -r [..][/]target[/]release[/]deps[/]bench-[..][EXE] --param --bench` ", url = foo.url()))); } From 0f1c687d3bc150d225a2bd75cc40b7a9ca0a18d0 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Sat, 13 May 2017 11:36:53 +0100 Subject: [PATCH 4/4] Fix tidy error --- src/cargo/util/config.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index f16110d2498..269384145b3 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -237,7 +237,8 @@ impl Config { } } - pub fn get_path_and_args(&self, key: &str) -> CargoResult)>>> { + pub fn get_path_and_args(&self, key: &str) + -> CargoResult)>>> { if let Some(mut val) = self.get_list_or_split_string(key)? { if !val.val.is_empty() { return Ok(Some(Value {