• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 pub use self::Mode::*;
2 
3 use std::ffi::OsString;
4 use std::fmt;
5 use std::iter;
6 use std::path::{Path, PathBuf};
7 use std::process::Command;
8 use std::str::FromStr;
9 
10 use crate::util::{add_dylib_path, PathBufExt};
11 use lazycell::AtomicLazyCell;
12 use serde::de::{Deserialize, Deserializer, Error as _};
13 use std::collections::{HashMap, HashSet};
14 use test::{ColorConfig, OutputFormat};
15 
16 macro_rules! string_enum {
17     ($(#[$meta:meta])* $vis:vis enum $name:ident { $($variant:ident => $repr:expr,)* }) => {
18         $(#[$meta])*
19         $vis enum $name {
20             $($variant,)*
21         }
22 
23         impl $name {
24             $vis const VARIANTS: &'static [Self] = &[$(Self::$variant,)*];
25             $vis const STR_VARIANTS: &'static [&'static str] = &[$(Self::$variant.to_str(),)*];
26 
27             $vis const fn to_str(&self) -> &'static str {
28                 match self {
29                     $(Self::$variant => $repr,)*
30                 }
31             }
32         }
33 
34         impl fmt::Display for $name {
35             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36                 fmt::Display::fmt(self.to_str(), f)
37             }
38         }
39 
40         impl FromStr for $name {
41             type Err = ();
42 
43             fn from_str(s: &str) -> Result<Self, ()> {
44                 match s {
45                     $($repr => Ok(Self::$variant),)*
46                     _ => Err(()),
47                 }
48             }
49         }
50     }
51 }
52 
53 string_enum! {
54     #[derive(Clone, Copy, PartialEq, Debug)]
55     pub enum Mode {
56         RunPassValgrind => "run-pass-valgrind",
57         Pretty => "pretty",
58         DebugInfo => "debuginfo",
59         Codegen => "codegen",
60         Rustdoc => "rustdoc",
61         RustdocJson => "rustdoc-json",
62         CodegenUnits => "codegen-units",
63         Incremental => "incremental",
64         RunMake => "run-make",
65         Ui => "ui",
66         JsDocTest => "js-doc-test",
67         MirOpt => "mir-opt",
68         Assembly => "assembly",
69         RunCoverage => "run-coverage",
70     }
71 }
72 
73 impl Default for Mode {
default() -> Self74     fn default() -> Self {
75         Mode::Ui
76     }
77 }
78 
79 impl Mode {
disambiguator(self) -> &'static str80     pub fn disambiguator(self) -> &'static str {
81         // Pretty-printing tests could run concurrently, and if they do,
82         // they need to keep their output segregated.
83         match self {
84             Pretty => ".pretty",
85             _ => "",
86         }
87     }
88 }
89 
90 string_enum! {
91     #[derive(Clone, Copy, PartialEq, Debug, Hash)]
92     pub enum PassMode {
93         Check => "check",
94         Build => "build",
95         Run => "run",
96     }
97 }
98 
99 #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
100 pub enum FailMode {
101     Check,
102     Build,
103     Run,
104 }
105 
106 string_enum! {
107     #[derive(Clone, Debug, PartialEq)]
108     pub enum CompareMode {
109         Polonius => "polonius",
110         NextSolver => "next-solver",
111         NextSolverCoherence => "next-solver-coherence",
112         SplitDwarf => "split-dwarf",
113         SplitDwarfSingle => "split-dwarf-single",
114     }
115 }
116 
117 string_enum! {
118     #[derive(Clone, Copy, Debug, PartialEq)]
119     pub enum Debugger {
120         Cdb => "cdb",
121         Gdb => "gdb",
122         Lldb => "lldb",
123     }
124 }
125 
126 #[derive(Clone, Copy, Debug, PartialEq, Default, serde::Deserialize)]
127 #[serde(rename_all = "kebab-case")]
128 pub enum PanicStrategy {
129     #[default]
130     Unwind,
131     Abort,
132 }
133 
134 impl PanicStrategy {
for_miropt_test_tools(&self) -> miropt_test_tools::PanicStrategy135     pub(crate) fn for_miropt_test_tools(&self) -> miropt_test_tools::PanicStrategy {
136         match self {
137             PanicStrategy::Unwind => miropt_test_tools::PanicStrategy::Unwind,
138             PanicStrategy::Abort => miropt_test_tools::PanicStrategy::Abort,
139         }
140     }
141 }
142 
143 /// Configuration for compiletest
144 #[derive(Debug, Default, Clone)]
145 pub struct Config {
146     /// `true` to overwrite stderr/stdout files instead of complaining about changes in output.
147     pub bless: bool,
148 
149     /// The library paths required for running the compiler.
150     pub compile_lib_path: PathBuf,
151 
152     /// The library paths required for running compiled programs.
153     pub run_lib_path: PathBuf,
154 
155     /// The rustc executable.
156     pub rustc_path: PathBuf,
157 
158     /// The rustdoc executable.
159     pub rustdoc_path: Option<PathBuf>,
160 
161     /// The rust-demangler executable.
162     pub rust_demangler_path: Option<PathBuf>,
163 
164     /// The Python executable to use for LLDB and htmldocck.
165     pub python: String,
166 
167     /// The jsondocck executable.
168     pub jsondocck_path: Option<String>,
169 
170     /// The jsondoclint executable.
171     pub jsondoclint_path: Option<String>,
172 
173     /// The LLVM `FileCheck` binary path.
174     pub llvm_filecheck: Option<PathBuf>,
175 
176     /// Path to LLVM's bin directory.
177     pub llvm_bin_dir: Option<PathBuf>,
178 
179     /// The valgrind path.
180     pub valgrind_path: Option<String>,
181 
182     /// Whether to fail if we can't run run-pass-valgrind tests under valgrind
183     /// (or, alternatively, to silently run them like regular run-pass tests).
184     pub force_valgrind: bool,
185 
186     /// The path to the Clang executable to run Clang-based tests with. If
187     /// `None` then these tests will be ignored.
188     pub run_clang_based_tests_with: Option<String>,
189 
190     /// The directory containing the tests to run
191     pub src_base: PathBuf,
192 
193     /// The directory where programs should be built
194     pub build_base: PathBuf,
195 
196     /// The directory containing the compiler sysroot
197     pub sysroot_base: PathBuf,
198 
199     /// The name of the stage being built (stage1, etc)
200     pub stage_id: String,
201 
202     /// The test mode, e.g. ui or debuginfo.
203     pub mode: Mode,
204 
205     /// The test suite (essentially which directory is running, but without the
206     /// directory prefix such as tests)
207     pub suite: String,
208 
209     /// The debugger to use in debuginfo mode. Unset otherwise.
210     pub debugger: Option<Debugger>,
211 
212     /// Run ignored tests
213     pub run_ignored: bool,
214 
215     /// Only run tests that match these filters
216     pub filters: Vec<String>,
217 
218     /// Skip tests tests matching these substrings. Corresponds to
219     /// `test::TestOpts::skip`. `filter_exact` does not apply to these flags.
220     pub skip: Vec<String>,
221 
222     /// Exactly match the filter, rather than a substring
223     pub filter_exact: bool,
224 
225     /// Force the pass mode of a check/build/run-pass test to this mode.
226     pub force_pass_mode: Option<PassMode>,
227 
228     /// Explicitly enable or disable running.
229     pub run: Option<bool>,
230 
231     /// Write out a parseable log of tests that were run
232     pub logfile: Option<PathBuf>,
233 
234     /// A command line to prefix program execution with,
235     /// for running under valgrind
236     pub runtool: Option<String>,
237 
238     /// Flags to pass to the compiler when building for the host
239     pub host_rustcflags: Vec<String>,
240 
241     /// Flags to pass to the compiler when building for the target
242     pub target_rustcflags: Vec<String>,
243 
244     /// Whether tests should be optimized by default. Individual test-suites and test files may
245     /// override this setting.
246     pub optimize_tests: bool,
247 
248     /// Target system to be tested
249     pub target: String,
250 
251     /// Host triple for the compiler being invoked
252     pub host: String,
253 
254     /// Path to / name of the Microsoft Console Debugger (CDB) executable
255     pub cdb: Option<OsString>,
256 
257     /// Version of CDB
258     pub cdb_version: Option<[u16; 4]>,
259 
260     /// Path to / name of the GDB executable
261     pub gdb: Option<String>,
262 
263     /// Version of GDB, encoded as ((major * 1000) + minor) * 1000 + patch
264     pub gdb_version: Option<u32>,
265 
266     /// Whether GDB has native rust support
267     pub gdb_native_rust: bool,
268 
269     /// Version of LLDB
270     pub lldb_version: Option<u32>,
271 
272     /// Whether LLDB has native rust support
273     pub lldb_native_rust: bool,
274 
275     /// Version of LLVM
276     pub llvm_version: Option<u32>,
277 
278     /// Is LLVM a system LLVM
279     pub system_llvm: bool,
280 
281     /// Path to the android tools
282     pub android_cross_path: PathBuf,
283 
284     /// Extra parameter to run adb on arm-linux-androideabi
285     pub adb_path: String,
286 
287     /// Extra parameter to run test suite on arm-linux-androideabi
288     pub adb_test_dir: String,
289 
290     /// status whether android device available or not
291     pub adb_device_status: bool,
292 
293     /// the path containing LLDB's Python module
294     pub lldb_python_dir: Option<String>,
295 
296     /// Explain what's going on
297     pub verbose: bool,
298 
299     /// Print one character per test instead of one line
300     pub format: OutputFormat,
301 
302     /// Whether to use colors in test.
303     pub color: ColorConfig,
304 
305     /// where to find the remote test client process, if we're using it
306     pub remote_test_client: Option<PathBuf>,
307 
308     /// mode describing what file the actual ui output will be compared to
309     pub compare_mode: Option<CompareMode>,
310 
311     /// If true, this will generate a coverage file with UI test files that run `MachineApplicable`
312     /// diagnostics but are missing `run-rustfix` annotations. The generated coverage file is
313     /// created in `/<build_base>/rustfix_missing_coverage.txt`
314     pub rustfix_coverage: bool,
315 
316     /// whether to run `tidy` when a rustdoc test fails
317     pub has_tidy: bool,
318 
319     /// The current Rust channel
320     pub channel: String,
321 
322     /// Whether adding git commit information such as the commit hash has been enabled for building
323     pub git_hash: bool,
324 
325     /// The default Rust edition
326     pub edition: Option<String>,
327 
328     // Configuration for various run-make tests frobbing things like C compilers
329     // or querying about various LLVM component information.
330     pub cc: String,
331     pub cxx: String,
332     pub cflags: String,
333     pub cxxflags: String,
334     pub ar: String,
335     pub target_linker: Option<String>,
336     pub host_linker: Option<String>,
337     pub llvm_components: String,
338 
339     /// Path to a NodeJS executable. Used for JS doctests, emscripten and WASM tests
340     pub nodejs: Option<String>,
341     /// Path to a npm executable. Used for rustdoc GUI tests
342     pub npm: Option<String>,
343 
344     /// Whether to rerun tests even if the inputs are unchanged.
345     pub force_rerun: bool,
346 
347     /// Only rerun the tests that result has been modified accoring to Git status
348     pub only_modified: bool,
349 
350     pub target_cfgs: AtomicLazyCell<TargetCfgs>,
351 
352     pub nocapture: bool,
353 }
354 
355 impl Config {
run_enabled(&self) -> bool356     pub fn run_enabled(&self) -> bool {
357         self.run.unwrap_or_else(|| {
358             // Auto-detect whether to run based on the platform.
359             !self.target.ends_with("-fuchsia")
360         })
361     }
362 
target_cfgs(&self) -> &TargetCfgs363     pub fn target_cfgs(&self) -> &TargetCfgs {
364         match self.target_cfgs.borrow() {
365             Some(cfgs) => cfgs,
366             None => {
367                 let _ = self.target_cfgs.fill(TargetCfgs::new(self));
368                 self.target_cfgs.borrow().unwrap()
369             }
370         }
371     }
372 
target_cfg(&self) -> &TargetCfg373     pub fn target_cfg(&self) -> &TargetCfg {
374         &self.target_cfgs().current
375     }
376 
matches_arch(&self, arch: &str) -> bool377     pub fn matches_arch(&self, arch: &str) -> bool {
378         self.target_cfg().arch == arch ||
379         // Shorthand for convenience. The arch for
380         // asmjs-unknown-emscripten is actually wasm32.
381         (arch == "asmjs" && self.target.starts_with("asmjs")) ||
382         // Matching all the thumb variants as one can be convenient.
383         // (thumbv6m, thumbv7em, thumbv7m, etc.)
384         (arch == "thumb" && self.target.starts_with("thumb"))
385     }
386 
matches_os(&self, os: &str) -> bool387     pub fn matches_os(&self, os: &str) -> bool {
388         self.target_cfg().os == os
389     }
390 
matches_env(&self, env: &str) -> bool391     pub fn matches_env(&self, env: &str) -> bool {
392         self.target_cfg().env == env
393     }
394 
matches_abi(&self, abi: &str) -> bool395     pub fn matches_abi(&self, abi: &str) -> bool {
396         self.target_cfg().abi == abi
397     }
398 
matches_family(&self, family: &str) -> bool399     pub fn matches_family(&self, family: &str) -> bool {
400         self.target_cfg().families.iter().any(|f| f == family)
401     }
402 
is_big_endian(&self) -> bool403     pub fn is_big_endian(&self) -> bool {
404         self.target_cfg().endian == Endian::Big
405     }
406 
get_pointer_width(&self) -> u32407     pub fn get_pointer_width(&self) -> u32 {
408         *&self.target_cfg().pointer_width
409     }
410 
can_unwind(&self) -> bool411     pub fn can_unwind(&self) -> bool {
412         self.target_cfg().panic == PanicStrategy::Unwind
413     }
414 
has_asm_support(&self) -> bool415     pub fn has_asm_support(&self) -> bool {
416         static ASM_SUPPORTED_ARCHS: &[&str] = &[
417             "x86", "x86_64", "arm", "aarch64", "riscv32",
418             "riscv64",
419             // These targets require an additional asm_experimental_arch feature.
420             // "nvptx64", "hexagon", "mips", "mips64", "spirv", "wasm32",
421         ];
422         ASM_SUPPORTED_ARCHS.contains(&self.target_cfg().arch.as_str())
423     }
424 }
425 
426 #[derive(Debug, Clone)]
427 pub struct TargetCfgs {
428     pub current: TargetCfg,
429     pub all_targets: HashSet<String>,
430     pub all_archs: HashSet<String>,
431     pub all_oses: HashSet<String>,
432     pub all_oses_and_envs: HashSet<String>,
433     pub all_envs: HashSet<String>,
434     pub all_abis: HashSet<String>,
435     pub all_families: HashSet<String>,
436     pub all_pointer_widths: HashSet<String>,
437 }
438 
439 impl TargetCfgs {
new(config: &Config) -> TargetCfgs440     fn new(config: &Config) -> TargetCfgs {
441         let mut targets: HashMap<String, TargetCfg> = serde_json::from_str(&rustc_output(
442             config,
443             &["--print=all-target-specs-json", "-Zunstable-options"],
444         ))
445         .unwrap();
446 
447         let mut all_targets = HashSet::new();
448         let mut all_archs = HashSet::new();
449         let mut all_oses = HashSet::new();
450         let mut all_oses_and_envs = HashSet::new();
451         let mut all_envs = HashSet::new();
452         let mut all_abis = HashSet::new();
453         let mut all_families = HashSet::new();
454         let mut all_pointer_widths = HashSet::new();
455 
456         // Handle custom target specs, which are not included in `--print=all-target-specs-json`.
457         if config.target.ends_with(".json") {
458             targets.insert(
459                 config.target.clone(),
460                 serde_json::from_str(&rustc_output(
461                     config,
462                     &["--print=target-spec-json", "-Zunstable-options", "--target", &config.target],
463                 ))
464                 .unwrap(),
465             );
466         }
467 
468         for (target, cfg) in targets.iter() {
469             all_archs.insert(cfg.arch.clone());
470             all_oses.insert(cfg.os.clone());
471             all_oses_and_envs.insert(cfg.os_and_env());
472             all_envs.insert(cfg.env.clone());
473             all_abis.insert(cfg.abi.clone());
474             for family in &cfg.families {
475                 all_families.insert(family.clone());
476             }
477             all_pointer_widths.insert(format!("{}bit", cfg.pointer_width));
478 
479             all_targets.insert(target.clone());
480         }
481 
482         Self {
483             current: Self::get_current_target_config(config, &targets),
484             all_targets,
485             all_archs,
486             all_oses,
487             all_oses_and_envs,
488             all_envs,
489             all_abis,
490             all_families,
491             all_pointer_widths,
492         }
493     }
494 
get_current_target_config( config: &Config, targets: &HashMap<String, TargetCfg>, ) -> TargetCfg495     fn get_current_target_config(
496         config: &Config,
497         targets: &HashMap<String, TargetCfg>,
498     ) -> TargetCfg {
499         let mut cfg = targets[&config.target].clone();
500 
501         // To get the target information for the current target, we take the target spec obtained
502         // from `--print=all-target-specs-json`, and then we enrich it with the information
503         // gathered from `--print=cfg --target=$target`.
504         //
505         // This is done because some parts of the target spec can be overridden with `-C` flags,
506         // which are respected for `--print=cfg` but not for `--print=all-target-specs-json`. The
507         // code below extracts them from `--print=cfg`: make sure to only override fields that can
508         // actually be changed with `-C` flags.
509         for config in
510             rustc_output(config, &["--print=cfg", "--target", &config.target]).trim().lines()
511         {
512             let (name, value) = config
513                 .split_once("=\"")
514                 .map(|(name, value)| {
515                     (
516                         name,
517                         Some(
518                             value
519                                 .strip_suffix("\"")
520                                 .expect("key-value pair should be properly quoted"),
521                         ),
522                     )
523                 })
524                 .unwrap_or_else(|| (config, None));
525 
526             match (name, value) {
527                 // Can be overridden with `-C panic=$strategy`.
528                 ("panic", Some("abort")) => cfg.panic = PanicStrategy::Abort,
529                 ("panic", Some("unwind")) => cfg.panic = PanicStrategy::Unwind,
530                 ("panic", other) => panic!("unexpected value for panic cfg: {other:?}"),
531                 _ => {}
532             }
533         }
534 
535         cfg
536     }
537 }
538 
539 #[derive(Clone, Debug, serde::Deserialize)]
540 #[serde(rename_all = "kebab-case")]
541 pub struct TargetCfg {
542     pub(crate) arch: String,
543     #[serde(default = "default_os")]
544     pub(crate) os: String,
545     #[serde(default)]
546     pub(crate) env: String,
547     #[serde(default)]
548     pub(crate) abi: String,
549     #[serde(rename = "target-family", default)]
550     pub(crate) families: Vec<String>,
551     #[serde(rename = "target-pointer-width", deserialize_with = "serde_parse_u32")]
552     pub(crate) pointer_width: u32,
553     #[serde(rename = "target-endian", default)]
554     endian: Endian,
555     #[serde(rename = "panic-strategy", default)]
556     pub(crate) panic: PanicStrategy,
557     #[serde(default)]
558     pub(crate) dynamic_linking: bool,
559 }
560 
561 impl TargetCfg {
os_and_env(&self) -> String562     pub(crate) fn os_and_env(&self) -> String {
563         format!("{}-{}", self.os, self.env)
564     }
565 }
566 
default_os() -> String567 fn default_os() -> String {
568     "none".into()
569 }
570 
571 #[derive(Eq, PartialEq, Clone, Debug, Default, serde::Deserialize)]
572 #[serde(rename_all = "kebab-case")]
573 pub enum Endian {
574     #[default]
575     Little,
576     Big,
577 }
578 
rustc_output(config: &Config, args: &[&str]) -> String579 fn rustc_output(config: &Config, args: &[&str]) -> String {
580     let mut command = Command::new(&config.rustc_path);
581     add_dylib_path(&mut command, iter::once(&config.compile_lib_path));
582     command.args(&config.target_rustcflags).args(args);
583     command.env("RUSTC_BOOTSTRAP", "1");
584 
585     let output = match command.output() {
586         Ok(output) => output,
587         Err(e) => panic!("error: failed to run {command:?}: {e}"),
588     };
589     if !output.status.success() {
590         panic!(
591             "error: failed to run {command:?}\n--- stdout\n{}\n--- stderr\n{}",
592             String::from_utf8(output.stdout).unwrap(),
593             String::from_utf8(output.stderr).unwrap(),
594         );
595     }
596     String::from_utf8(output.stdout).unwrap()
597 }
598 
serde_parse_u32<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u32, D::Error>599 fn serde_parse_u32<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u32, D::Error> {
600     let string = String::deserialize(deserializer)?;
601     string.parse().map_err(D::Error::custom)
602 }
603 
604 #[derive(Debug, Clone)]
605 pub struct TestPaths {
606     pub file: PathBuf,         // e.g., compile-test/foo/bar/baz.rs
607     pub relative_dir: PathBuf, // e.g., foo/bar
608 }
609 
610 /// Used by `ui` tests to generate things like `foo.stderr` from `foo.rs`.
expected_output_path( testpaths: &TestPaths, revision: Option<&str>, compare_mode: &Option<CompareMode>, kind: &str, ) -> PathBuf611 pub fn expected_output_path(
612     testpaths: &TestPaths,
613     revision: Option<&str>,
614     compare_mode: &Option<CompareMode>,
615     kind: &str,
616 ) -> PathBuf {
617     assert!(UI_EXTENSIONS.contains(&kind));
618     let mut parts = Vec::new();
619 
620     if let Some(x) = revision {
621         parts.push(x);
622     }
623     if let Some(ref x) = *compare_mode {
624         parts.push(x.to_str());
625     }
626     parts.push(kind);
627 
628     let extension = parts.join(".");
629     testpaths.file.with_extension(extension)
630 }
631 
632 pub const UI_EXTENSIONS: &[&str] = &[
633     UI_STDERR,
634     UI_STDOUT,
635     UI_FIXED,
636     UI_RUN_STDERR,
637     UI_RUN_STDOUT,
638     UI_STDERR_64,
639     UI_STDERR_32,
640     UI_STDERR_16,
641     UI_COVERAGE,
642 ];
643 pub const UI_STDERR: &str = "stderr";
644 pub const UI_STDOUT: &str = "stdout";
645 pub const UI_FIXED: &str = "fixed";
646 pub const UI_RUN_STDERR: &str = "run.stderr";
647 pub const UI_RUN_STDOUT: &str = "run.stdout";
648 pub const UI_STDERR_64: &str = "64bit.stderr";
649 pub const UI_STDERR_32: &str = "32bit.stderr";
650 pub const UI_STDERR_16: &str = "16bit.stderr";
651 pub const UI_COVERAGE: &str = "coverage";
652 
653 /// Absolute path to the directory where all output for all tests in the given
654 /// `relative_dir` group should reside. Example:
655 ///   /path/to/build/host-triple/test/ui/relative/
656 /// This is created early when tests are collected to avoid race conditions.
output_relative_path(config: &Config, relative_dir: &Path) -> PathBuf657 pub fn output_relative_path(config: &Config, relative_dir: &Path) -> PathBuf {
658     config.build_base.join(relative_dir)
659 }
660 
661 /// Generates a unique name for the test, such as `testname.revision.mode`.
output_testname_unique( config: &Config, testpaths: &TestPaths, revision: Option<&str>, ) -> PathBuf662 pub fn output_testname_unique(
663     config: &Config,
664     testpaths: &TestPaths,
665     revision: Option<&str>,
666 ) -> PathBuf {
667     let mode = config.compare_mode.as_ref().map_or("", |m| m.to_str());
668     let debugger = config.debugger.as_ref().map_or("", |m| m.to_str());
669     PathBuf::from(&testpaths.file.file_stem().unwrap())
670         .with_extra_extension(revision.unwrap_or(""))
671         .with_extra_extension(mode)
672         .with_extra_extension(debugger)
673 }
674 
675 /// Absolute path to the directory where all output for the given
676 /// test/revision should reside. Example:
677 ///   /path/to/build/host-triple/test/ui/relative/testname.revision.mode/
output_base_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf678 pub fn output_base_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
679     output_relative_path(config, &testpaths.relative_dir)
680         .join(output_testname_unique(config, testpaths, revision))
681 }
682 
683 /// Absolute path to the base filename used as output for the given
684 /// test/revision. Example:
685 ///   /path/to/build/host-triple/test/ui/relative/testname.revision.mode/testname
output_base_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf686 pub fn output_base_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
687     output_base_dir(config, testpaths, revision).join(testpaths.file.file_stem().unwrap())
688 }
689 
690 /// Absolute path to the directory to use for incremental compilation. Example:
691 ///   /path/to/build/host-triple/test/ui/relative/testname.mode/testname.inc
incremental_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf692 pub fn incremental_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
693     output_base_name(config, testpaths, revision).with_extension("inc")
694 }
695