]> git.proxmox.com Git - cargo.git/commitdiff
add support for `target.'cfg(..)'.runner`
authorJorge Aparicio <jorge@japaric.io>
Sat, 1 Sep 2018 16:01:47 +0000 (18:01 +0200)
committerJorge Aparicio <jorge@japaric.io>
Sat, 8 Sep 2018 12:25:45 +0000 (14:25 +0200)
`cfg` can be used to reduce the number of `runner`s one needs to type in
`.cargo/config`. So instead of writing this:

``` toml
[target.thumbv6m-none-eabi]
runner = "arm-none-eabi-gdb"

[target.thumbv7m-none-eabi]
runner = "arm-none-eabi-gdb"

[target.thumbv7em-none-eabi]
runner = "arm-none-eabi-gdb"

[target.thumbv7em-none-eabihf]
runner = "arm-none-eabi-gdb"
```

one can write:

``` toml
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "arm-none-eabi-gdb"
```

Handling of edge cases:

- When `target.$triple.runner` matches it will be chosen regardless of the
  existence of others `target.'cfg(..)'.runner`s.

- If more than one `target.'cfg(..)'.runner` matches the target the command will
  fail.

closes #5946

src/cargo/core/compiler/compilation.rs
src/doc/src/reference/config.md
tests/testsuite/tool_paths.rs

index 9e0cbc7842bad75f35b2f0b83653d525c8f6309b..ea65df0ac775342e6720870c9f79f62479a9a446 100644 (file)
@@ -2,12 +2,12 @@ use std::collections::{BTreeSet, HashMap, HashSet};
 use std::env;
 use std::ffi::OsStr;
 use std::path::PathBuf;
+use std::str::FromStr;
 
 use semver::Version;
-use lazycell::LazyCell;
 
 use core::{Edition, Package, PackageId, Target, TargetKind};
-use util::{self, join_paths, process, CargoResult, Config, ProcessBuilder};
+use util::{self, join_paths, process, CargoResult, CfgExpr, Config, ProcessBuilder};
 use super::BuildContext;
 
 pub struct Doctest {
@@ -77,7 +77,7 @@ pub struct Compilation<'cfg> {
     config: &'cfg Config,
     rustc_process: ProcessBuilder,
 
-    target_runner: LazyCell<Option<(PathBuf, Vec<String>)>>,
+    target_runner: Option<(PathBuf, Vec<String>)>,
 }
 
 impl<'cfg> Compilation<'cfg> {
@@ -124,7 +124,7 @@ impl<'cfg> Compilation<'cfg> {
             rustc_process: rustc,
             host: bcx.host_triple().to_string(),
             target: bcx.target_triple().to_string(),
-            target_runner: LazyCell::new(),
+            target_runner: target_runner(&bcx)?,
         })
     }
 
@@ -156,11 +156,8 @@ impl<'cfg> Compilation<'cfg> {
         self.fill_env(process(cmd), pkg, true)
     }
 
-    fn target_runner(&self) -> CargoResult<&Option<(PathBuf, Vec<String>)>> {
-        self.target_runner.try_borrow_with(|| {
-            let key = format!("target.{}.runner", self.target);
-            Ok(self.config.get_path_and_args(&key)?.map(|v| v.val))
-        })
+    fn target_runner(&self) -> &Option<(PathBuf, Vec<String>)> {
+        &self.target_runner
     }
 
     /// See `process`.
@@ -169,7 +166,7 @@ impl<'cfg> Compilation<'cfg> {
         cmd: T,
         pkg: &Package,
     ) -> CargoResult<ProcessBuilder> {
-        let builder = if let Some((ref runner, ref args)) = *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);
@@ -263,3 +260,44 @@ fn pre_version_component(v: &Version) -> String {
 
     ret
 }
+
+fn target_runner(bcx: &BuildContext) -> CargoResult<Option<(PathBuf, Vec<String>)>> {
+    let target = bcx.target_triple();
+
+    // try target.{}.runner
+    let key = format!("target.{}.runner", target);
+    if let Some(v) = bcx.config.get_path_and_args(&key)? {
+        return Ok(Some(v.val));
+    }
+
+    // try target.'cfg(...)'.runner
+    if let Some(target_cfg) = bcx.target_info.cfg() {
+        if let Some(table) = bcx.config.get_table("target")? {
+            let mut matching_runner = None;
+
+            for t in table.val.keys() {
+                if t.starts_with("cfg(") && t.ends_with(')') {
+                    let cfg = &t[4..t.len() - 1];
+                    if let Ok(c) = CfgExpr::from_str(cfg) {
+                        if c.matches(target_cfg) {
+                            let key = format!("target.{}.runner", t);
+                            if let Some(runner) = bcx.config.get_path_and_args(&key)? {
+                                // more than one match, error out
+                                if matching_runner.is_some() {
+                                    bail!("several matching instances of `target.'cfg(..)'.runner` \
+                                           in `.cargo/config`")
+                                }
+
+                                matching_runner = Some(runner.val);
+                            }
+                        }
+                    }
+                }
+            }
+
+            return Ok(matching_runner);
+        }
+    }
+
+    Ok(None)
+}
index cc2590377304eb99089789c5415271d014e36546..91425164b0c41a4f5e4d0886d02b05ad6252a14f 100644 (file)
@@ -83,6 +83,11 @@ rustflags = ["..", ".."]
 # are concatenated. The `cfg` syntax only applies to rustflags, and not to
 # linker.
 rustflags = ["..", ".."]
+# Similar for the $triple configuration, but using the `cfg` syntax.
+# If one or more `cfg`s, and a $triple target are candidates, then the $triple
+# will be used
+# If several `cfg` are candidates, then the build will error
+runner = ".."
 
 # Configuration keys related to the registry
 [registry]
index 50f3b0267a6b1eded6be01fe4e1f7c69b8af9f37..ca22692ce41938f01b18555efee680ac567b3c11 100644 (file)
@@ -167,3 +167,83 @@ fn custom_runner() {
 ",
         ).run();
 }
+
+// can set a custom runner via `target.'cfg(..)'.runner`
+#[test]
+fn custom_runner_cfg() {
+    let p = project()
+        .file("src/main.rs", "fn main() {}")
+        .file(
+            ".cargo/config",
+            r#"
+            [target.'cfg(not(target_os = "none"))']
+            runner = "nonexistent-runner -r"
+            "#,
+        ).build();
+
+    p.cargo("run -- --param")
+        .with_status(101)
+        .with_stderr_contains(&format!(
+            "\
+[COMPILING] foo v0.0.1 (CWD)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param`
+",
+        )).run();
+}
+
+// custom runner set via `target.$triple.runner` have precende over `target.'cfg(..)'.runner`
+#[test]
+fn custom_runner_cfg_precedence() {
+    let target = rustc_host();
+
+    let p = project()
+        .file("src/main.rs", "fn main() {}")
+        .file(
+            ".cargo/config",
+            &format!(
+                r#"
+            [target.'cfg(not(target_os = "none"))']
+            runner = "ignored-runner"
+
+            [target.{}]
+            runner = "nonexistent-runner -r"
+        "#,
+                target
+            ),
+        ).build();
+
+    p.cargo("run -- --param")
+        .with_status(101)
+        .with_stderr_contains(&format!(
+            "\
+            [COMPILING] foo v0.0.1 (CWD)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param`
+",
+        )).run();
+}
+
+#[test]
+fn custom_runner_cfg_collision() {
+    let p = project()
+        .file("src/main.rs", "fn main() {}")
+        .file(
+            ".cargo/config",
+            r#"
+            [target.'cfg(not(target_arch = "avr"))']
+            runner = "true"
+
+            [target.'cfg(not(target_os = "none"))']
+            runner = "false"
+            "#,
+        ).build();
+
+    p.cargo("run -- --param")
+        .with_status(101)
+        .with_stderr_contains(&format!(
+            "\
+[ERROR] several matching instances of `target.'cfg(..)'.runner` in `.cargo/config`
+",
+        )).run();
+}