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