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 {
config: &'cfg Config,
rustc_process: ProcessBuilder,
- target_runner: LazyCell<Option<(PathBuf, Vec<String>)>>,
+ target_runner: Option<(PathBuf, Vec<String>)>,
}
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)?,
})
}
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`.
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);
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)
+}
",
).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();
+}