Skip to content

Commit 41f98f3

Browse files
committed
Auto merge of #5959 - japaric:cfg-runner, r=alexcrichton
add support for `target.'cfg(..)'.runner` `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 --- Does this sound like a reasonable feature / implementation?
2 parents 2fb77a4 + 33a6d99 commit 41f98f3

File tree

5 files changed

+142
-21
lines changed

5 files changed

+142
-21
lines changed

src/cargo/core/compiler/build_context/mod.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::collections::HashMap;
22
use std::env;
33
use std::path::{Path, PathBuf};
4-
use std::str::{self, FromStr};
4+
use std::str;
55

66
use core::profiles::Profiles;
77
use core::{Dependency, Workspace};
@@ -393,16 +393,9 @@ fn env_args(
393393
// ...including target.'cfg(...)'.rustflags
394394
if let Some(target_cfg) = target_cfg {
395395
if let Some(table) = config.get_table("target")? {
396-
let cfgs = table.val.keys().filter_map(|t| {
397-
if t.starts_with("cfg(") && t.ends_with(')') {
398-
let cfg = &t[4..t.len() - 1];
399-
CfgExpr::from_str(cfg).ok().and_then(|c| {
400-
if c.matches(target_cfg) {
401-
Some(t)
402-
} else {
403-
None
404-
}
405-
})
396+
let cfgs = table.val.keys().filter_map(|key| {
397+
if CfgExpr::matches_key(key, target_cfg) {
398+
Some(key)
406399
} else {
407400
None
408401
}

src/cargo/core/compiler/compilation.rs

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@ use std::ffi::OsStr;
44
use std::path::PathBuf;
55

66
use semver::Version;
7-
use lazycell::LazyCell;
87

98
use core::{Edition, Package, PackageId, Target, TargetKind};
10-
use util::{self, join_paths, process, CargoResult, Config, ProcessBuilder};
9+
use util::{self, join_paths, process, CargoResult, CfgExpr, Config, ProcessBuilder};
1110
use super::BuildContext;
1211

1312
pub struct Doctest {
@@ -77,7 +76,7 @@ pub struct Compilation<'cfg> {
7776
config: &'cfg Config,
7877
rustc_process: ProcessBuilder,
7978

80-
target_runner: LazyCell<Option<(PathBuf, Vec<String>)>>,
79+
target_runner: Option<(PathBuf, Vec<String>)>,
8180
}
8281

8382
impl<'cfg> Compilation<'cfg> {
@@ -124,7 +123,7 @@ impl<'cfg> Compilation<'cfg> {
124123
rustc_process: rustc,
125124
host: bcx.host_triple().to_string(),
126125
target: bcx.target_triple().to_string(),
127-
target_runner: LazyCell::new(),
126+
target_runner: target_runner(&bcx)?,
128127
})
129128
}
130129

@@ -156,11 +155,8 @@ impl<'cfg> Compilation<'cfg> {
156155
self.fill_env(process(cmd), pkg, true)
157156
}
158157

159-
fn target_runner(&self) -> CargoResult<&Option<(PathBuf, Vec<String>)>> {
160-
self.target_runner.try_borrow_with(|| {
161-
let key = format!("target.{}.runner", self.target);
162-
Ok(self.config.get_path_and_args(&key)?.map(|v| v.val))
163-
})
158+
fn target_runner(&self) -> &Option<(PathBuf, Vec<String>)> {
159+
&self.target_runner
164160
}
165161

166162
/// See `process`.
@@ -169,7 +165,7 @@ impl<'cfg> Compilation<'cfg> {
169165
cmd: T,
170166
pkg: &Package,
171167
) -> CargoResult<ProcessBuilder> {
172-
let builder = if let Some((ref runner, ref args)) = *self.target_runner()? {
168+
let builder = if let Some((ref runner, ref args)) = *self.target_runner() {
173169
let mut builder = process(runner);
174170
builder.args(args);
175171
builder.arg(cmd);
@@ -263,3 +259,39 @@ fn pre_version_component(v: &Version) -> String {
263259

264260
ret
265261
}
262+
263+
fn target_runner(bcx: &BuildContext) -> CargoResult<Option<(PathBuf, Vec<String>)>> {
264+
let target = bcx.target_triple();
265+
266+
// try target.{}.runner
267+
let key = format!("target.{}.runner", target);
268+
if let Some(v) = bcx.config.get_path_and_args(&key)? {
269+
return Ok(Some(v.val));
270+
}
271+
272+
// try target.'cfg(...)'.runner
273+
if let Some(target_cfg) = bcx.target_info.cfg() {
274+
if let Some(table) = bcx.config.get_table("target")? {
275+
let mut matching_runner = None;
276+
277+
for key in table.val.keys() {
278+
if CfgExpr::matches_key(key, target_cfg) {
279+
let key = format!("target.{}.runner", key);
280+
if let Some(runner) = bcx.config.get_path_and_args(&key)? {
281+
// more than one match, error out
282+
if matching_runner.is_some() {
283+
bail!("several matching instances of `target.'cfg(..)'.runner` \
284+
in `.cargo/config`")
285+
}
286+
287+
matching_runner = Some(runner.val);
288+
}
289+
}
290+
}
291+
292+
return Ok(matching_runner);
293+
}
294+
}
295+
296+
Ok(None)
297+
}

src/cargo/util/cfg.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ impl fmt::Display for Cfg {
6060
}
6161

6262
impl CfgExpr {
63+
/// Utility function to check if the key, "cfg(..)" matches the `target_cfg`
64+
pub fn matches_key(key: &str, target_cfg: &[Cfg]) -> bool {
65+
if key.starts_with("cfg(") && key.ends_with(')') {
66+
let cfg = &key[4..key.len() - 1 ];
67+
68+
CfgExpr::from_str(cfg).ok().map(|ce| ce.matches(target_cfg)).unwrap_or(false)
69+
} else {
70+
false
71+
}
72+
}
73+
6374
pub fn matches(&self, cfg: &[Cfg]) -> bool {
6475
match *self {
6576
CfgExpr::Not(ref e) => !e.matches(cfg),

src/doc/src/reference/config.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ rustflags = ["..", ".."]
8383
# are concatenated. The `cfg` syntax only applies to rustflags, and not to
8484
# linker.
8585
rustflags = ["..", ".."]
86+
# Similar for the $triple configuration, but using the `cfg` syntax.
87+
# If one or more `cfg`s, and a $triple target are candidates, then the $triple
88+
# will be used
89+
# If several `cfg` are candidates, then the build will error
90+
runner = ".."
8691

8792
# Configuration keys related to the registry
8893
[registry]

tests/testsuite/tool_paths.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,83 @@ fn custom_runner() {
167167
",
168168
).run();
169169
}
170+
171+
// can set a custom runner via `target.'cfg(..)'.runner`
172+
#[test]
173+
fn custom_runner_cfg() {
174+
let p = project()
175+
.file("src/main.rs", "fn main() {}")
176+
.file(
177+
".cargo/config",
178+
r#"
179+
[target.'cfg(not(target_os = "none"))']
180+
runner = "nonexistent-runner -r"
181+
"#,
182+
).build();
183+
184+
p.cargo("run -- --param")
185+
.with_status(101)
186+
.with_stderr_contains(&format!(
187+
"\
188+
[COMPILING] foo v0.0.1 (CWD)
189+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
190+
[RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param`
191+
",
192+
)).run();
193+
}
194+
195+
// custom runner set via `target.$triple.runner` have precende over `target.'cfg(..)'.runner`
196+
#[test]
197+
fn custom_runner_cfg_precedence() {
198+
let target = rustc_host();
199+
200+
let p = project()
201+
.file("src/main.rs", "fn main() {}")
202+
.file(
203+
".cargo/config",
204+
&format!(
205+
r#"
206+
[target.'cfg(not(target_os = "none"))']
207+
runner = "ignored-runner"
208+
209+
[target.{}]
210+
runner = "nonexistent-runner -r"
211+
"#,
212+
target
213+
),
214+
).build();
215+
216+
p.cargo("run -- --param")
217+
.with_status(101)
218+
.with_stderr_contains(&format!(
219+
"\
220+
[COMPILING] foo v0.0.1 (CWD)
221+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
222+
[RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param`
223+
",
224+
)).run();
225+
}
226+
227+
#[test]
228+
fn custom_runner_cfg_collision() {
229+
let p = project()
230+
.file("src/main.rs", "fn main() {}")
231+
.file(
232+
".cargo/config",
233+
r#"
234+
[target.'cfg(not(target_arch = "avr"))']
235+
runner = "true"
236+
237+
[target.'cfg(not(target_os = "none"))']
238+
runner = "false"
239+
"#,
240+
).build();
241+
242+
p.cargo("run -- --param")
243+
.with_status(101)
244+
.with_stderr_contains(&format!(
245+
"\
246+
[ERROR] several matching instances of `target.'cfg(..)'.runner` in `.cargo/config`
247+
",
248+
)).run();
249+
}

0 commit comments

Comments
 (0)