Skip to content

Commit 6a12aa5

Browse files
committed
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
1 parent 2fb77a4 commit 6a12aa5

File tree

3 files changed

+133
-10
lines changed

3 files changed

+133
-10
lines changed

src/cargo/core/compiler/compilation.rs

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ use std::collections::{BTreeSet, HashMap, HashSet};
22
use std::env;
33
use std::ffi::OsStr;
44
use std::path::PathBuf;
5+
use std::str::FromStr;
56

67
use semver::Version;
7-
use lazycell::LazyCell;
88

99
use core::{Edition, Package, PackageId, Target, TargetKind};
10-
use util::{self, join_paths, process, CargoResult, Config, ProcessBuilder};
10+
use util::{self, join_paths, process, CargoResult, CfgExpr, Config, ProcessBuilder};
1111
use super::BuildContext;
1212

1313
pub struct Doctest {
@@ -77,7 +77,7 @@ pub struct Compilation<'cfg> {
7777
config: &'cfg Config,
7878
rustc_process: ProcessBuilder,
7979

80-
target_runner: LazyCell<Option<(PathBuf, Vec<String>)>>,
80+
target_runner: Option<(PathBuf, Vec<String>)>,
8181
}
8282

8383
impl<'cfg> Compilation<'cfg> {
@@ -124,7 +124,7 @@ impl<'cfg> Compilation<'cfg> {
124124
rustc_process: rustc,
125125
host: bcx.host_triple().to_string(),
126126
target: bcx.target_triple().to_string(),
127-
target_runner: LazyCell::new(),
127+
target_runner: target_runner(&bcx)?,
128128
})
129129
}
130130

@@ -156,11 +156,8 @@ impl<'cfg> Compilation<'cfg> {
156156
self.fill_env(process(cmd), pkg, true)
157157
}
158158

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-
})
159+
fn target_runner(&self) -> &Option<(PathBuf, Vec<String>)> {
160+
&self.target_runner
164161
}
165162

166163
/// See `process`.
@@ -169,7 +166,7 @@ impl<'cfg> Compilation<'cfg> {
169166
cmd: T,
170167
pkg: &Package,
171168
) -> CargoResult<ProcessBuilder> {
172-
let builder = if let Some((ref runner, ref args)) = *self.target_runner()? {
169+
let builder = if let Some((ref runner, ref args)) = *self.target_runner() {
173170
let mut builder = process(runner);
174171
builder.args(args);
175172
builder.arg(cmd);
@@ -263,3 +260,44 @@ fn pre_version_component(v: &Version) -> String {
263260

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

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)