Skip to main content

compiletest/
runtest.rs

1use std::borrow::Cow;
2use std::collections::{HashMap, HashSet};
3use std::ffi::OsString;
4use std::fs::{self, create_dir_all};
5use std::hash::{DefaultHasher, Hash, Hasher};
6use std::io::prelude::*;
7use std::process::{Child, Command, ExitStatus, Output, Stdio};
8use std::{env, fmt, io, iter, str};
9
10use build_helper::fs::remove_and_create_dir_all;
11use camino::{Utf8Path, Utf8PathBuf};
12use colored::{Color, Colorize};
13use regex::{Captures, Regex};
14use tracing::*;
15
16use crate::common::{
17    CompareMode, Config, Debugger, FailMode, PassMode, RunFailMode, RunResult, TestMode, TestPaths,
18    TestSuite, UI_EXTENSIONS, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT, UI_STDERR, UI_STDOUT, UI_SVG,
19    UI_WINDOWS_SVG, expected_output_path, incremental_dir, output_base_dir, output_base_name,
20};
21use crate::directives::{AuxCrate, TestProps};
22use crate::errors::{Error, ErrorKind, load_errors};
23use crate::output_capture::ConsoleOut;
24use crate::read2::{Truncated, read2_abbreviated};
25use crate::runtest::compute_diff::{DiffLine, make_diff, write_diff};
26use crate::util::{Utf8PathBufExt, add_dylib_path, static_regex};
27use crate::{json, stamp_file_path};
28
29// Helper modules that implement test running logic for each test suite.
30// tidy-alphabetical-start
31mod assembly;
32mod codegen;
33mod codegen_units;
34mod coverage;
35mod crashes;
36mod debuginfo;
37mod incremental;
38mod js_doc;
39mod mir_opt;
40mod pretty;
41mod run_make;
42mod rustdoc;
43mod rustdoc_json;
44mod ui;
45// tidy-alphabetical-end
46
47mod compute_diff;
48mod debugger;
49#[cfg(test)]
50mod tests;
51
52const FAKE_SRC_BASE: &str = "fake-test-src-base";
53
54#[cfg(windows)]
55fn disable_error_reporting<F: FnOnce() -> R, R>(f: F) -> R {
56    use std::sync::Mutex;
57
58    use windows::Win32::System::Diagnostics::Debug::{
59        SEM_FAILCRITICALERRORS, SEM_NOGPFAULTERRORBOX, SetErrorMode,
60    };
61
62    static LOCK: Mutex<()> = Mutex::new(());
63
64    // Error mode is a global variable, so lock it so only one thread will change it
65    let _lock = LOCK.lock().unwrap();
66
67    // Tell Windows to not show any UI on errors (such as terminating abnormally). This is important
68    // for running tests, since some of them use abnormal termination by design. This mode is
69    // inherited by all child processes.
70    //
71    // Note that `run-make` tests require `SEM_FAILCRITICALERRORS` in addition to suppress Windows
72    // Error Reporting (WER) error dialogues that come from "critical failures" such as missing
73    // DLLs.
74    //
75    // See <https://github.com/rust-lang/rust/issues/132092> and
76    // <https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-seterrormode?redirectedfrom=MSDN>.
77    unsafe {
78        // read inherited flags
79        let old_mode = SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS);
80        SetErrorMode(old_mode | SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS);
81        let r = f();
82        SetErrorMode(old_mode);
83        r
84    }
85}
86
87#[cfg(not(windows))]
88fn disable_error_reporting<F: FnOnce() -> R, R>(f: F) -> R {
89    f()
90}
91
92/// The platform-specific library name
93fn get_lib_name(name: &str, aux_type: AuxType) -> Option<String> {
94    match aux_type {
95        AuxType::Bin => None,
96        // In some cases (e.g. MUSL), we build a static
97        // library, rather than a dynamic library.
98        // In this case, the only path we can pass
99        // with '--extern-meta' is the '.rlib' file
100        AuxType::Lib => Some(format!("lib{name}.rlib")),
101        AuxType::Dylib | AuxType::ProcMacro => Some(dylib_name(name)),
102    }
103}
104
105fn dylib_name(name: &str) -> String {
106    format!("{}{name}.{}", std::env::consts::DLL_PREFIX, std::env::consts::DLL_EXTENSION)
107}
108
109pub(crate) fn run(
110    config: &Config,
111    stdout: &dyn ConsoleOut,
112    stderr: &dyn ConsoleOut,
113    testpaths: &TestPaths,
114    revision: Option<&str>,
115) {
116    match &*config.target {
117        "arm-linux-androideabi"
118        | "armv7-linux-androideabi"
119        | "thumbv7neon-linux-androideabi"
120        | "aarch64-linux-android" => {
121            if !config.adb_device_status {
122                panic!("android device not available");
123            }
124        }
125
126        _ => {
127            // FIXME: this logic seems strange as well.
128
129            // android has its own gdb handling
130            if config.debugger == Some(Debugger::Gdb) && config.gdb.is_none() {
131                panic!("gdb not available but debuginfo gdb debuginfo test requested");
132            }
133        }
134    }
135
136    if config.verbose {
137        // We're going to be dumping a lot of info. Start on a new line.
138        write!(stdout, "\n\n");
139    }
140    debug!("running {}", testpaths.file);
141    let mut props = TestProps::from_file(&testpaths.file, revision, &config);
142
143    // For non-incremental (i.e. regular UI) tests, the incremental directory
144    // takes into account the revision name, since the revisions are independent
145    // of each other and can race.
146    if props.incremental {
147        props.incremental_dir = Some(incremental_dir(&config, testpaths, revision));
148    }
149
150    let cx = TestCx { config: &config, stdout, stderr, props: &props, testpaths, revision };
151
152    if let Err(e) = create_dir_all(&cx.output_base_dir()) {
153        panic!("failed to create output base directory {}: {e}", cx.output_base_dir());
154    }
155
156    if props.incremental {
157        cx.init_incremental_test();
158    }
159
160    if config.mode == TestMode::Incremental {
161        // Incremental tests are special because they cannot be run in
162        // parallel.
163        assert!(!props.revisions.is_empty(), "Incremental tests require revisions.");
164        for revision in &props.revisions {
165            let mut revision_props = TestProps::from_file(&testpaths.file, Some(revision), &config);
166            revision_props.incremental_dir = props.incremental_dir.clone();
167            let rev_cx = TestCx {
168                config: &config,
169                stdout,
170                stderr,
171                props: &revision_props,
172                testpaths,
173                revision: Some(revision),
174            };
175            rev_cx.run_revision();
176        }
177    } else {
178        cx.run_revision();
179    }
180
181    cx.create_stamp();
182}
183
184pub(crate) fn compute_stamp_hash(config: &Config) -> String {
185    let mut hash = DefaultHasher::new();
186    config.stage_id.hash(&mut hash);
187    config.run.hash(&mut hash);
188    config.edition.hash(&mut hash);
189
190    match config.debugger {
191        Some(Debugger::Cdb) => {
192            config.cdb.hash(&mut hash);
193        }
194
195        Some(Debugger::Gdb) => {
196            config.gdb.hash(&mut hash);
197            env::var_os("PATH").hash(&mut hash);
198            env::var_os("PYTHONPATH").hash(&mut hash);
199        }
200
201        Some(Debugger::Lldb) => {
202            // LLDB debuginfo tests now use LLDB's embedded Python, with an
203            // explicit PYTHONPATH, so they don't depend on `--python` or
204            // the ambient PYTHONPATH.
205            config.lldb.hash(&mut hash);
206            env::var_os("PATH").hash(&mut hash);
207        }
208
209        None => {}
210    }
211
212    if config.mode == TestMode::Ui {
213        config.force_pass_mode.hash(&mut hash);
214    }
215
216    format!("{:x}", hash.finish())
217}
218
219#[derive(Copy, Clone, Debug)]
220struct TestCx<'test> {
221    config: &'test Config,
222    stdout: &'test dyn ConsoleOut,
223    stderr: &'test dyn ConsoleOut,
224    props: &'test TestProps,
225    testpaths: &'test TestPaths,
226    revision: Option<&'test str>,
227}
228
229enum ReadFrom {
230    Path,
231    Stdin(String),
232}
233
234enum TestOutput {
235    Compile,
236    Run,
237}
238
239/// Will this test be executed? Should we use `make_exe_name`?
240#[derive(Copy, Clone, PartialEq)]
241enum WillExecute {
242    Yes,
243    No,
244    Disabled,
245}
246
247/// What value should be passed to `--emit`?
248#[derive(Copy, Clone)]
249enum Emit {
250    None,
251    Metadata,
252    LlvmIr,
253    Mir,
254    Asm,
255    LinkArgsAsm,
256}
257
258/// Indicates whether we are using `rustc` or `rustdoc` to compile an input file.
259#[derive(Clone, Copy, Debug, PartialEq, Eq)]
260enum CompilerKind {
261    Rustc,
262    Rustdoc,
263}
264
265impl<'test> TestCx<'test> {
266    /// Code executed for each revision in turn (or, if there are no
267    /// revisions, exactly once, with revision == None).
268    fn run_revision(&self) {
269        if self.props.should_ice
270            && self.config.mode != TestMode::Incremental
271            && self.config.mode != TestMode::Crashes
272        {
273            self.fatal("cannot use should-ice in a test that is not cfail");
274        }
275        // Run the test multiple times if requested.
276        // This is useful for catching flaky tests under the parallel frontend.
277        for _ in 0..self.config.iteration_count {
278            match self.config.mode {
279                TestMode::Pretty => self.run_pretty_test(),
280                TestMode::DebugInfo => self.run_debuginfo_test(),
281                TestMode::Codegen => self.run_codegen_test(),
282                TestMode::RustdocHtml => self.run_rustdoc_html_test(),
283                TestMode::RustdocJson => self.run_rustdoc_json_test(),
284                TestMode::CodegenUnits => self.run_codegen_units_test(),
285                TestMode::Incremental => self.run_incremental_test(),
286                TestMode::RunMake => self.run_rmake_test(),
287                TestMode::Ui => self.run_ui_test(),
288                TestMode::MirOpt => self.run_mir_opt_test(),
289                TestMode::Assembly => self.run_assembly_test(),
290                TestMode::RustdocJs => self.run_rustdoc_js_test(),
291                TestMode::CoverageMap => self.run_coverage_map_test(), // see self::coverage
292                TestMode::CoverageRun => self.run_coverage_run_test(), // see self::coverage
293                TestMode::Crashes => self.run_crash_test(),
294            }
295        }
296    }
297
298    fn pass_mode(&self) -> Option<PassMode> {
299        self.props.pass_mode(self.config)
300    }
301
302    fn should_run(&self, pm: Option<PassMode>) -> WillExecute {
303        let test_should_run = match self.config.mode {
304            TestMode::Ui
305                if pm == Some(PassMode::Run)
306                    || matches!(self.props.fail_mode, Some(FailMode::Run(_))) =>
307            {
308                true
309            }
310            TestMode::MirOpt if pm == Some(PassMode::Run) => true,
311            TestMode::Ui | TestMode::MirOpt => false,
312            mode => panic!("unimplemented for mode {:?}", mode),
313        };
314        if test_should_run { self.run_if_enabled() } else { WillExecute::No }
315    }
316
317    fn run_if_enabled(&self) -> WillExecute {
318        if self.config.run_enabled() { WillExecute::Yes } else { WillExecute::Disabled }
319    }
320
321    fn should_run_successfully(&self, pm: Option<PassMode>) -> bool {
322        match self.config.mode {
323            TestMode::Ui | TestMode::MirOpt => pm == Some(PassMode::Run),
324            mode => panic!("unimplemented for mode {:?}", mode),
325        }
326    }
327
328    fn should_compile_successfully(&self, pm: Option<PassMode>) -> bool {
329        match self.config.mode {
330            TestMode::RustdocJs => true,
331            TestMode::Ui => pm.is_some() || self.props.fail_mode > Some(FailMode::Build),
332            TestMode::Crashes => false,
333            TestMode::Incremental => {
334                let revision =
335                    self.revision.expect("incremental tests require a list of revisions");
336                if revision.starts_with("cpass") || revision.starts_with("rpass") {
337                    true
338                } else if revision.starts_with("cfail") {
339                    pm.is_some()
340                } else {
341                    panic!("revision name must begin with `cfail`, `cpass`, or `rpass`");
342                }
343            }
344            mode => panic!("unimplemented for mode {:?}", mode),
345        }
346    }
347
348    fn check_if_test_should_compile(
349        &self,
350        fail_mode: Option<FailMode>,
351        pass_mode: Option<PassMode>,
352        proc_res: &ProcRes,
353    ) {
354        if self.should_compile_successfully(pass_mode) {
355            if !proc_res.status.success() {
356                match (fail_mode, pass_mode) {
357                    (Some(FailMode::Build), Some(PassMode::Check)) => {
358                        // A `build-fail` test needs to `check-pass`.
359                        self.fatal_proc_rec(
360                            "`build-fail` test is required to pass check build, but check build failed",
361                            proc_res,
362                        );
363                    }
364                    _ => {
365                        self.fatal_proc_rec(
366                            "test compilation failed although it shouldn't!",
367                            proc_res,
368                        );
369                    }
370                }
371            }
372        } else {
373            if proc_res.status.success() {
374                let err = &format!("{} test did not emit an error", self.config.mode);
375                let extra_note = (self.config.mode == crate::common::TestMode::Ui)
376                    .then_some("note: by default, ui tests are expected not to compile.\nhint: use check-pass, build-pass, or run-pass directive to change this behavior.");
377                self.fatal_proc_rec_general(err, extra_note, proc_res, || ());
378            }
379
380            if !self.props.dont_check_failure_status {
381                self.check_correct_failure_status(proc_res);
382            }
383        }
384    }
385
386    fn get_output(&self, proc_res: &ProcRes) -> String {
387        if self.props.check_stdout {
388            format!("{}{}", proc_res.stdout, proc_res.stderr)
389        } else {
390            proc_res.stderr.clone()
391        }
392    }
393
394    fn check_correct_failure_status(&self, proc_res: &ProcRes) {
395        let expected_status = Some(self.props.failure_status.unwrap_or(1));
396        let received_status = proc_res.status.code();
397
398        if expected_status != received_status {
399            self.fatal_proc_rec(
400                &format!(
401                    "Error: expected failure status ({:?}) but received status {:?}.",
402                    expected_status, received_status
403                ),
404                proc_res,
405            );
406        }
407    }
408
409    /// Runs a [`Command`] and waits for it to finish, then converts its exit
410    /// status and output streams into a [`ProcRes`].
411    ///
412    /// The command might have succeeded or failed; it is the caller's
413    /// responsibility to check the exit status and take appropriate action.
414    ///
415    /// # Panics
416    /// Panics if the command couldn't be executed at all
417    /// (e.g. because the executable could not be found).
418    #[must_use = "caller should check whether the command succeeded"]
419    fn run_command_to_procres(&self, cmd: &mut Command) -> ProcRes {
420        let output = cmd
421            .output()
422            .unwrap_or_else(|e| self.fatal(&format!("failed to exec `{cmd:?}` because: {e}")));
423
424        let proc_res = ProcRes {
425            status: output.status,
426            stdout: String::from_utf8(output.stdout).unwrap(),
427            stderr: String::from_utf8(output.stderr).unwrap(),
428            truncated: Truncated::No,
429            cmdline: format!("{cmd:?}"),
430        };
431        self.dump_output(
432            self.config.verbose || !proc_res.status.success(),
433            &cmd.get_program().to_string_lossy(),
434            &proc_res.stdout,
435            &proc_res.stderr,
436        );
437
438        proc_res
439    }
440
441    fn print_source(&self, read_from: ReadFrom, pretty_type: &str) -> ProcRes {
442        let aux_dir = self.aux_output_dir_name();
443        let input: &str = match read_from {
444            ReadFrom::Stdin(_) => "-",
445            ReadFrom::Path => self.testpaths.file.as_str(),
446        };
447
448        let mut rustc = Command::new(&self.config.rustc_path);
449
450        self.build_all_auxiliary(&self.aux_output_dir(), &mut rustc);
451
452        rustc
453            .arg(input)
454            .args(&["-Z", &format!("unpretty={}", pretty_type)])
455            .args(&["--target", &self.config.target])
456            .arg("-L")
457            .arg(&aux_dir)
458            .arg("-A")
459            .arg("internal_features")
460            .args(&self.props.compile_flags)
461            .envs(self.props.rustc_env.clone());
462        self.maybe_add_external_args(&mut rustc, &self.config.target_rustcflags);
463
464        let src = match read_from {
465            ReadFrom::Stdin(src) => Some(src),
466            ReadFrom::Path => None,
467        };
468
469        self.compose_and_run(
470            rustc,
471            self.config.host_compile_lib_path.as_path(),
472            Some(aux_dir.as_path()),
473            src,
474        )
475    }
476
477    fn compare_source(&self, expected: &str, actual: &str) {
478        if expected != actual {
479            self.fatal(&format!(
480                "pretty-printed source does not match expected source\n\
481                 expected:\n\
482                 ------------------------------------------\n\
483                 {}\n\
484                 ------------------------------------------\n\
485                 actual:\n\
486                 ------------------------------------------\n\
487                 {}\n\
488                 ------------------------------------------\n\
489                 diff:\n\
490                 ------------------------------------------\n\
491                 {}\n",
492                expected,
493                actual,
494                write_diff(expected, actual, 3),
495            ));
496        }
497    }
498
499    fn set_revision_flags(&self, cmd: &mut Command) {
500        // Normalize revisions to be lowercase and replace `-`s with `_`s.
501        // Otherwise the `--cfg` flag is not valid.
502        let normalize_revision = |revision: &str| revision.to_lowercase().replace("-", "_");
503
504        if let Some(revision) = self.revision {
505            let normalized_revision = normalize_revision(revision);
506            let cfg_arg = ["--cfg", &normalized_revision];
507            let arg = format!("--cfg={normalized_revision}");
508            // Handle if compile_flags is length 1
509            let contains_arg =
510                self.props.compile_flags.iter().any(|considered_arg| *considered_arg == arg);
511            let contains_cfg_arg = self.props.compile_flags.windows(2).any(|args| args == cfg_arg);
512            if contains_arg || contains_cfg_arg {
513                error!(
514                    "redundant cfg argument `{normalized_revision}` is already created by the \
515                    revision"
516                );
517                panic!("redundant cfg argument");
518            }
519            if self.config.builtin_cfg_names().contains(&normalized_revision) {
520                error!("revision `{normalized_revision}` collides with a built-in cfg");
521                panic!("revision collides with built-in cfg");
522            }
523            cmd.args(cfg_arg);
524        }
525
526        if !self.props.no_auto_check_cfg {
527            let mut check_cfg = String::with_capacity(25);
528
529            // Generate `cfg(FALSE, REV1, ..., REVN)` (for all possible revisions)
530            //
531            // For compatibility reason we consider the `FALSE` cfg to be expected
532            // since it is extensively used in the testsuite, as well as the `test`
533            // cfg since we have tests that uses it.
534            check_cfg.push_str("cfg(test,FALSE");
535            for revision in &self.props.revisions {
536                check_cfg.push(',');
537                check_cfg.push_str(&normalize_revision(revision));
538            }
539            check_cfg.push(')');
540
541            cmd.args(&["--check-cfg", &check_cfg]);
542        }
543    }
544
545    fn typecheck_source(&self, src: String) -> ProcRes {
546        let mut rustc = Command::new(&self.config.rustc_path);
547
548        let out_dir = self.output_base_name().with_extension("pretty-out");
549        remove_and_create_dir_all(&out_dir).unwrap_or_else(|e| {
550            panic!("failed to remove and recreate output directory `{out_dir}`: {e}")
551        });
552
553        let target = if self.props.force_host { &*self.config.host } else { &*self.config.target };
554
555        let aux_dir = self.aux_output_dir_name();
556
557        rustc
558            .arg("-")
559            .arg("-Zno-codegen")
560            .arg("--out-dir")
561            .arg(&out_dir)
562            .arg(&format!("--target={}", target))
563            .arg("-L")
564            // FIXME(jieyouxu): this search path seems questionable. Is this intended for
565            // `rust_test_helpers` in ui tests?
566            .arg(&self.config.build_test_suite_root)
567            .arg("-L")
568            .arg(aux_dir)
569            .arg("-A")
570            .arg("internal_features");
571        self.set_revision_flags(&mut rustc);
572        self.maybe_add_external_args(&mut rustc, &self.config.target_rustcflags);
573        rustc.args(&self.props.compile_flags);
574
575        self.compose_and_run_compiler(rustc, Some(src))
576    }
577
578    fn maybe_add_external_args(&self, cmd: &mut Command, args: &Vec<String>) {
579        // Filter out the arguments that should not be added by runtest here.
580        //
581        // Notable use-cases are: do not add our optimisation flag if
582        // `compile-flags: -Copt-level=x` and similar for debug-info level as well.
583        const OPT_FLAGS: &[&str] = &["-O", "-Copt-level=", /*-C<space>*/ "opt-level="];
584        const DEBUG_FLAGS: &[&str] = &["-g", "-Cdebuginfo=", /*-C<space>*/ "debuginfo="];
585
586        // FIXME: ideally we would "just" check the `cmd` itself, but it does not allow inspecting
587        // its arguments. They need to be collected separately. For now I cannot be bothered to
588        // implement this the "right" way.
589        let have_opt_flag =
590            self.props.compile_flags.iter().any(|arg| OPT_FLAGS.iter().any(|f| arg.starts_with(f)));
591        let have_debug_flag = self
592            .props
593            .compile_flags
594            .iter()
595            .any(|arg| DEBUG_FLAGS.iter().any(|f| arg.starts_with(f)));
596
597        for arg in args {
598            if OPT_FLAGS.iter().any(|f| arg.starts_with(f)) && have_opt_flag {
599                continue;
600            }
601            if DEBUG_FLAGS.iter().any(|f| arg.starts_with(f)) && have_debug_flag {
602                continue;
603            }
604            cmd.arg(arg);
605        }
606    }
607
608    /// Check `error-pattern` and `regex-error-pattern` directives.
609    fn check_all_error_patterns(&self, output_to_check: &str, proc_res: &ProcRes) {
610        let mut missing_patterns: Vec<String> = Vec::new();
611        self.check_error_patterns(output_to_check, &mut missing_patterns);
612        self.check_regex_error_patterns(output_to_check, proc_res, &mut missing_patterns);
613
614        if missing_patterns.is_empty() {
615            return;
616        }
617
618        if missing_patterns.len() == 1 {
619            self.fatal_proc_rec(
620                &format!("error pattern '{}' not found!", missing_patterns[0]),
621                proc_res,
622            );
623        } else {
624            for pattern in missing_patterns {
625                writeln!(
626                    self.stdout,
627                    "\n{prefix}: error pattern '{pattern}' not found!",
628                    prefix = self.error_prefix()
629                );
630            }
631            self.fatal_proc_rec("multiple error patterns not found", proc_res);
632        }
633    }
634
635    fn check_error_patterns(&self, output_to_check: &str, missing_patterns: &mut Vec<String>) {
636        debug!("check_error_patterns");
637        for pattern in &self.props.error_patterns {
638            if output_to_check.contains(pattern.trim()) {
639                debug!("found error pattern {}", pattern);
640            } else {
641                missing_patterns.push(pattern.to_string());
642            }
643        }
644    }
645
646    fn check_regex_error_patterns(
647        &self,
648        output_to_check: &str,
649        proc_res: &ProcRes,
650        missing_patterns: &mut Vec<String>,
651    ) {
652        debug!("check_regex_error_patterns");
653
654        for pattern in &self.props.regex_error_patterns {
655            let pattern = pattern.trim();
656            let re = match Regex::new(pattern) {
657                Ok(re) => re,
658                Err(err) => {
659                    self.fatal_proc_rec(
660                        &format!("invalid regex error pattern '{}': {:?}", pattern, err),
661                        proc_res,
662                    );
663                }
664            };
665            if re.is_match(output_to_check) {
666                debug!("found regex error pattern {}", pattern);
667            } else {
668                missing_patterns.push(pattern.to_string());
669            }
670        }
671    }
672
673    fn check_no_compiler_crash(&self, proc_res: &ProcRes, should_ice: bool) {
674        match proc_res.status.code() {
675            Some(101) if !should_ice => {
676                self.fatal_proc_rec("compiler encountered internal error", proc_res)
677            }
678            None => self.fatal_proc_rec("compiler terminated by signal", proc_res),
679            _ => (),
680        }
681    }
682
683    fn check_forbid_output(&self, output_to_check: &str, proc_res: &ProcRes) {
684        for pat in &self.props.forbid_output {
685            if output_to_check.contains(pat) {
686                self.fatal_proc_rec("forbidden pattern found in compiler output", proc_res);
687            }
688        }
689    }
690
691    /// Check `//~ KIND message` annotations.
692    fn check_expected_errors(&self, proc_res: &ProcRes) {
693        let expected_errors = load_errors(&self.testpaths.file, self.revision);
694        debug!(
695            "check_expected_errors: expected_errors={:?} proc_res.status={:?}",
696            expected_errors, proc_res.status
697        );
698        if proc_res.status.success() && expected_errors.iter().any(|x| x.kind == ErrorKind::Error) {
699            self.fatal_proc_rec("process did not return an error status", proc_res);
700        }
701
702        if self.props.known_bug {
703            if !expected_errors.is_empty() {
704                self.fatal_proc_rec(
705                    "`known_bug` tests should not have an expected error",
706                    proc_res,
707                );
708            }
709            return;
710        }
711
712        // On Windows, keep all '\' path separators to match the paths reported in the JSON output
713        // from the compiler
714        let diagnostic_file_name = if self.props.remap_src_base {
715            let mut p = Utf8PathBuf::from(FAKE_SRC_BASE);
716            p.push(&self.testpaths.relative_dir);
717            p.push(self.testpaths.file.file_name().unwrap());
718            p.to_string()
719        } else {
720            self.testpaths.file.to_string()
721        };
722
723        // Errors and warnings are always expected, other diagnostics are only expected
724        // if one of them actually occurs in the test.
725        let expected_kinds: HashSet<_> = [ErrorKind::Error, ErrorKind::Warning]
726            .into_iter()
727            .chain(expected_errors.iter().map(|e| e.kind))
728            .collect();
729
730        // Parse the JSON output from the compiler and extract out the messages.
731        let actual_errors = json::parse_output(&diagnostic_file_name, &self.get_output(proc_res))
732            .into_iter()
733            .map(|e| Error { msg: self.normalize_output(&e.msg, &[]), ..e });
734
735        let mut unexpected = Vec::new();
736        let mut unimportant = Vec::new();
737        let mut found = vec![false; expected_errors.len()];
738        for actual_error in actual_errors {
739            for pattern in &self.props.error_patterns {
740                let pattern = pattern.trim();
741                if actual_error.msg.contains(pattern) {
742                    let q = if actual_error.line_num.is_none() { "?" } else { "" };
743                    self.fatal(&format!(
744                        "error pattern '{pattern}' is found in structured \
745                         diagnostics, use `//~{q} {} {pattern}` instead",
746                        actual_error.kind,
747                    ));
748                }
749            }
750
751            let opt_index =
752                expected_errors.iter().enumerate().position(|(index, expected_error)| {
753                    !found[index]
754                        && actual_error.line_num == expected_error.line_num
755                        && actual_error.kind == expected_error.kind
756                        && actual_error.msg.contains(&expected_error.msg)
757                });
758
759            match opt_index {
760                Some(index) => {
761                    // found a match, everybody is happy
762                    assert!(!found[index]);
763                    found[index] = true;
764                }
765
766                None => {
767                    if actual_error.require_annotation
768                        && expected_kinds.contains(&actual_error.kind)
769                        && !self.props.dont_require_annotations.contains(&actual_error.kind)
770                    {
771                        unexpected.push(actual_error);
772                    } else {
773                        unimportant.push(actual_error);
774                    }
775                }
776            }
777        }
778
779        let mut not_found = Vec::new();
780        // anything not yet found is a problem
781        for (index, expected_error) in expected_errors.iter().enumerate() {
782            if !found[index] {
783                not_found.push(expected_error);
784            }
785        }
786
787        if !unexpected.is_empty() || !not_found.is_empty() {
788            // Emit locations in a format that is short (relative paths) but "clickable" in editors.
789            // Also normalize path separators to `/`.
790            let file_name = self
791                .testpaths
792                .file
793                .strip_prefix(self.config.src_root.as_str())
794                .unwrap_or(&self.testpaths.file)
795                .to_string()
796                .replace(r"\", "/");
797            let line_str = |e: &Error| {
798                let line_num = e.line_num.map_or("?".to_string(), |line_num| line_num.to_string());
799                // `file:?:NUM` may be confusing to editors and unclickable.
800                let opt_col_num = match e.column_num {
801                    Some(col_num) if line_num != "?" => format!(":{col_num}"),
802                    _ => "".to_string(),
803                };
804                format!("{file_name}:{line_num}{opt_col_num}")
805            };
806            let print_error =
807                |e| writeln!(self.stdout, "{}: {}: {}", line_str(e), e.kind, e.msg.cyan());
808            let push_suggestion =
809                |suggestions: &mut Vec<_>, e: &Error, kind, line, msg, color, rank| {
810                    let mut ret = String::new();
811                    if kind {
812                        ret += &format!("{} {}", "with different kind:".color(color), e.kind);
813                    }
814                    if line {
815                        if !ret.is_empty() {
816                            ret.push(' ');
817                        }
818                        ret += &format!("{} {}", "on different line:".color(color), line_str(e));
819                    }
820                    if msg {
821                        if !ret.is_empty() {
822                            ret.push(' ');
823                        }
824                        ret +=
825                            &format!("{} {}", "with different message:".color(color), e.msg.cyan());
826                    }
827                    suggestions.push((ret, rank));
828                };
829            let show_suggestions = |mut suggestions: Vec<_>, prefix: &str, color| {
830                // Only show suggestions with the highest rank.
831                suggestions.sort_by_key(|(_, rank)| *rank);
832                if let Some(&(_, top_rank)) = suggestions.first() {
833                    for (suggestion, rank) in suggestions {
834                        if rank == top_rank {
835                            writeln!(self.stdout, "  {} {suggestion}", prefix.color(color));
836                        }
837                    }
838                }
839            };
840
841            // Fuzzy matching quality:
842            // - message and line / message and kind - great, suggested
843            // - only message - good, suggested
844            // - known line and kind - ok, suggested
845            // - only known line - meh, but suggested
846            // - others are not worth suggesting
847            if !unexpected.is_empty() {
848                writeln!(
849                    self.stdout,
850                    "\n{prefix}: {n} diagnostics reported in JSON output but not expected in test file",
851                    prefix = self.error_prefix(),
852                    n = unexpected.len(),
853                );
854                for error in &unexpected {
855                    print_error(error);
856                    let mut suggestions = Vec::new();
857                    for candidate in &not_found {
858                        let kind_mismatch = candidate.kind != error.kind;
859                        let mut push_red_suggestion = |line, msg, rank| {
860                            push_suggestion(
861                                &mut suggestions,
862                                candidate,
863                                kind_mismatch,
864                                line,
865                                msg,
866                                Color::Red,
867                                rank,
868                            )
869                        };
870                        if error.msg.contains(&candidate.msg) {
871                            push_red_suggestion(candidate.line_num != error.line_num, false, 0);
872                        } else if candidate.line_num.is_some()
873                            && candidate.line_num == error.line_num
874                        {
875                            push_red_suggestion(false, true, if kind_mismatch { 2 } else { 1 });
876                        }
877                    }
878
879                    show_suggestions(suggestions, "expected", Color::Red);
880                }
881            }
882            if !not_found.is_empty() {
883                writeln!(
884                    self.stdout,
885                    "\n{prefix}: {n} diagnostics expected in test file but not reported in JSON output",
886                    prefix = self.error_prefix(),
887                    n = not_found.len(),
888                );
889
890                // FIXME: Ideally, we should check this at the place where we actually parse error annotations.
891                // it's better to use (negated) heuristic inside normalize_output if possible
892                if let Some(human_format) = self.props.compile_flags.iter().find(|flag| {
893                    // `human`, `human-unicode`, `short` will not generate JSON output
894                    flag.contains("error-format")
895                        && (flag.contains("short") || flag.contains("human"))
896                }) {
897                    let msg = format!(
898                        "tests with compile flag `{}` should not have error annotations such as `//~ ERROR`",
899                        human_format
900                    ).color(Color::Red);
901                    writeln!(self.stdout, "{}", msg);
902                }
903
904                for error in &not_found {
905                    print_error(error);
906                    let mut suggestions = Vec::new();
907                    for candidate in unexpected.iter().chain(&unimportant) {
908                        let kind_mismatch = candidate.kind != error.kind;
909                        let mut push_green_suggestion = |line, msg, rank| {
910                            push_suggestion(
911                                &mut suggestions,
912                                candidate,
913                                kind_mismatch,
914                                line,
915                                msg,
916                                Color::Green,
917                                rank,
918                            )
919                        };
920                        if candidate.msg.contains(&error.msg) {
921                            push_green_suggestion(candidate.line_num != error.line_num, false, 0);
922                        } else if candidate.line_num.is_some()
923                            && candidate.line_num == error.line_num
924                        {
925                            push_green_suggestion(false, true, if kind_mismatch { 2 } else { 1 });
926                        }
927                    }
928
929                    show_suggestions(suggestions, "reported", Color::Green);
930                }
931            }
932            panic!(
933                "errors differ from expected\nstatus: {}\ncommand: {}\n",
934                proc_res.status, proc_res.cmdline
935            );
936        }
937    }
938
939    fn should_emit_metadata(&self, pm: Option<PassMode>) -> Emit {
940        match (pm, self.props.fail_mode, self.config.mode) {
941            (Some(PassMode::Check), ..) | (_, Some(FailMode::Check), TestMode::Ui) => {
942                Emit::Metadata
943            }
944            _ => Emit::None,
945        }
946    }
947
948    fn compile_test(&self, will_execute: WillExecute, emit: Emit) -> ProcRes {
949        self.compile_test_general(will_execute, emit, self.props.local_pass_mode(), Vec::new())
950    }
951
952    fn compile_test_with_passes(
953        &self,
954        will_execute: WillExecute,
955        emit: Emit,
956        passes: Vec<String>,
957    ) -> ProcRes {
958        self.compile_test_general(will_execute, emit, self.props.local_pass_mode(), passes)
959    }
960
961    fn compile_test_general(
962        &self,
963        will_execute: WillExecute,
964        emit: Emit,
965        local_pm: Option<PassMode>,
966        passes: Vec<String>,
967    ) -> ProcRes {
968        let compiler_kind = self.compiler_kind_for_non_aux();
969
970        // Only use `make_exe_name` when the test ends up being executed.
971        let output_file = match will_execute {
972            WillExecute::Yes => TargetLocation::ThisFile(self.make_exe_name()),
973            WillExecute::No | WillExecute::Disabled => {
974                TargetLocation::ThisDirectory(self.output_base_dir())
975            }
976        };
977
978        let allow_unused = match self.config.mode {
979            TestMode::Ui => {
980                // UI tests tend to have tons of unused code as
981                // it's just testing various pieces of the compile, but we don't
982                // want to actually assert warnings about all this code. Instead
983                // let's just ignore unused code warnings by defaults and tests
984                // can turn it back on if needed.
985                if compiler_kind == CompilerKind::Rustc
986                    // Note that we use the local pass mode here as we don't want
987                    // to set unused to allow if we've overridden the pass mode
988                    // via command line flags.
989                    && local_pm != Some(PassMode::Run)
990                {
991                    AllowUnused::Yes
992                } else {
993                    AllowUnused::No
994                }
995            }
996            _ => AllowUnused::No,
997        };
998
999        let rustc = self.make_compile_args(
1000            compiler_kind,
1001            &self.testpaths.file,
1002            output_file,
1003            emit,
1004            allow_unused,
1005            LinkToAux::Yes,
1006            passes,
1007        );
1008
1009        self.compose_and_run_compiler(rustc, None)
1010    }
1011
1012    /// `root_out_dir` and `root_testpaths` refer to the parameters of the actual test being run.
1013    /// Auxiliaries, no matter how deep, have the same root_out_dir and root_testpaths.
1014    fn document(&self, root_out_dir: &Utf8Path, kind: DocKind) -> ProcRes {
1015        self.document_inner(&self.testpaths.file, root_out_dir, kind)
1016    }
1017
1018    /// Like `document`, but takes an explicit `file_to_doc` argument so that
1019    /// it can also be used for documenting auxiliaries, in addition to
1020    /// documenting the main test file.
1021    fn document_inner(
1022        &self,
1023        file_to_doc: &Utf8Path,
1024        root_out_dir: &Utf8Path,
1025        kind: DocKind,
1026    ) -> ProcRes {
1027        if self.props.build_aux_docs {
1028            assert_eq!(kind, DocKind::Html, "build-aux-docs only make sense for html output");
1029
1030            for rel_ab in &self.props.aux.builds {
1031                let aux_path = self.resolve_aux_path(rel_ab);
1032                let props_for_aux = self.props.from_aux_file(&aux_path, self.revision, self.config);
1033                let aux_cx = TestCx {
1034                    config: self.config,
1035                    stdout: self.stdout,
1036                    stderr: self.stderr,
1037                    props: &props_for_aux,
1038                    testpaths: self.testpaths,
1039                    revision: self.revision,
1040                };
1041                // Create the directory for the stdout/stderr files.
1042                create_dir_all(aux_cx.output_base_dir()).unwrap();
1043                let auxres = aux_cx.document_inner(&aux_path, &root_out_dir, kind);
1044                if !auxres.status.success() {
1045                    return auxres;
1046                }
1047            }
1048        }
1049
1050        let aux_dir = self.aux_output_dir_name();
1051
1052        let rustdoc_path = self.config.rustdoc_path.as_ref().expect("--rustdoc-path not passed");
1053
1054        // actual --out-dir given to the auxiliary or test, as opposed to the root out dir for the entire
1055        // test
1056        let out_dir: Cow<'_, Utf8Path> = if self.props.unique_doc_out_dir {
1057            let file_name = file_to_doc.file_stem().expect("file name should not be empty");
1058            let out_dir = Utf8PathBuf::from_iter([
1059                root_out_dir,
1060                Utf8Path::new("docs"),
1061                Utf8Path::new(file_name),
1062                Utf8Path::new("doc"),
1063            ]);
1064            create_dir_all(&out_dir).unwrap();
1065            Cow::Owned(out_dir)
1066        } else {
1067            Cow::Borrowed(root_out_dir)
1068        };
1069
1070        let mut rustdoc = Command::new(rustdoc_path);
1071        let current_dir = self.output_base_dir();
1072        rustdoc.current_dir(current_dir);
1073        rustdoc
1074            .arg("-L")
1075            .arg(self.config.target_run_lib_path.as_path())
1076            .arg("-L")
1077            .arg(aux_dir)
1078            .arg("-o")
1079            .arg(out_dir.as_ref())
1080            .arg("--deny")
1081            .arg("warnings")
1082            .arg(file_to_doc)
1083            .arg("-A")
1084            .arg("internal_features")
1085            .args(&self.props.compile_flags)
1086            .args(&self.props.doc_flags);
1087
1088        match kind {
1089            DocKind::Html => {}
1090            DocKind::Json => {
1091                rustdoc.arg("--output-format").arg("json").arg("-Zunstable-options");
1092            }
1093        }
1094
1095        if let Some(ref linker) = self.config.target_linker {
1096            rustdoc.arg(format!("-Clinker={}", linker));
1097        }
1098
1099        self.compose_and_run_compiler(rustdoc, None)
1100    }
1101
1102    fn exec_compiled_test(&self) -> ProcRes {
1103        self.exec_compiled_test_general(&[], true)
1104    }
1105
1106    fn exec_compiled_test_general(
1107        &self,
1108        env_extra: &[(&str, &str)],
1109        delete_after_success: bool,
1110    ) -> ProcRes {
1111        let prepare_env = |cmd: &mut Command| {
1112            for (key, val) in &self.props.exec_env {
1113                cmd.env(key, val);
1114            }
1115            for (key, val) in env_extra {
1116                cmd.env(key, val);
1117            }
1118
1119            for key in &self.props.unset_exec_env {
1120                cmd.env_remove(key);
1121            }
1122        };
1123
1124        let proc_res = match &*self.config.target {
1125            // This is pretty similar to below, we're transforming:
1126            //
1127            // ```text
1128            // program arg1 arg2
1129            // ```
1130            //
1131            // into
1132            //
1133            // ```text
1134            // remote-test-client run program 2 support-lib.so support-lib2.so arg1 arg2
1135            // ```
1136            //
1137            // The test-client program will upload `program` to the emulator along with all other
1138            // support libraries listed (in this case `support-lib.so` and `support-lib2.so`. It
1139            // will then execute the program on the emulator with the arguments specified (in the
1140            // environment we give the process) and then report back the same result.
1141            _ if self.config.remote_test_client.is_some() => {
1142                let aux_dir = self.aux_output_dir_name();
1143                let ProcArgs { prog, args } = self.make_run_args();
1144                let mut support_libs = Vec::new();
1145                if let Ok(entries) = aux_dir.read_dir() {
1146                    for entry in entries {
1147                        let entry = entry.unwrap();
1148                        if !entry.path().is_file() {
1149                            continue;
1150                        }
1151                        support_libs.push(entry.path());
1152                    }
1153                }
1154                let mut test_client =
1155                    Command::new(self.config.remote_test_client.as_ref().unwrap());
1156                test_client
1157                    .args(&["run", &support_libs.len().to_string()])
1158                    .arg(&prog)
1159                    .args(support_libs)
1160                    .args(args);
1161
1162                prepare_env(&mut test_client);
1163
1164                self.compose_and_run(
1165                    test_client,
1166                    self.config.target_run_lib_path.as_path(),
1167                    Some(aux_dir.as_path()),
1168                    None,
1169                )
1170            }
1171            _ if self.config.target.contains("vxworks") => {
1172                let aux_dir = self.aux_output_dir_name();
1173                let ProcArgs { prog, args } = self.make_run_args();
1174                let mut wr_run = Command::new("wr-run");
1175                wr_run.args(&[&prog]).args(args);
1176
1177                prepare_env(&mut wr_run);
1178
1179                self.compose_and_run(
1180                    wr_run,
1181                    self.config.target_run_lib_path.as_path(),
1182                    Some(aux_dir.as_path()),
1183                    None,
1184                )
1185            }
1186            _ => {
1187                let aux_dir = self.aux_output_dir_name();
1188                let ProcArgs { prog, args } = self.make_run_args();
1189                let mut program = Command::new(&prog);
1190                program.args(args).current_dir(&self.output_base_dir());
1191
1192                prepare_env(&mut program);
1193
1194                self.compose_and_run(
1195                    program,
1196                    self.config.target_run_lib_path.as_path(),
1197                    Some(aux_dir.as_path()),
1198                    None,
1199                )
1200            }
1201        };
1202
1203        if delete_after_success && proc_res.status.success() {
1204            // delete the executable after running it to save space.
1205            // it is ok if the deletion failed.
1206            let _ = fs::remove_file(self.make_exe_name());
1207        }
1208
1209        proc_res
1210    }
1211
1212    /// For each `aux-build: foo/bar` annotation, we check to find the file in an `auxiliary`
1213    /// directory relative to the test itself (not any intermediate auxiliaries).
1214    fn resolve_aux_path(&self, relative_aux_path: &str) -> Utf8PathBuf {
1215        let aux_path = self
1216            .testpaths
1217            .file
1218            .parent()
1219            .expect("test file path has no parent")
1220            .join("auxiliary")
1221            .join(relative_aux_path);
1222        if !aux_path.exists() {
1223            self.fatal(&format!(
1224                "auxiliary source file `{relative_aux_path}` not found at `{aux_path}`"
1225            ));
1226        }
1227
1228        aux_path
1229    }
1230
1231    fn is_vxworks_pure_static(&self) -> bool {
1232        if self.config.target.contains("vxworks") {
1233            match env::var("RUST_VXWORKS_TEST_DYLINK") {
1234                Ok(s) => s != "1",
1235                _ => true,
1236            }
1237        } else {
1238            false
1239        }
1240    }
1241
1242    fn is_vxworks_pure_dynamic(&self) -> bool {
1243        self.config.target.contains("vxworks") && !self.is_vxworks_pure_static()
1244    }
1245
1246    fn has_aux_dir(&self) -> bool {
1247        !self.props.aux.builds.is_empty()
1248            || !self.props.aux.crates.is_empty()
1249            || !self.props.aux.proc_macros.is_empty()
1250    }
1251
1252    fn aux_output_dir(&self) -> Utf8PathBuf {
1253        let aux_dir = self.aux_output_dir_name();
1254
1255        if !self.props.aux.builds.is_empty() {
1256            remove_and_create_dir_all(&aux_dir).unwrap_or_else(|e| {
1257                panic!("failed to remove and recreate output directory `{aux_dir}`: {e}")
1258            });
1259        }
1260
1261        if !self.props.aux.bins.is_empty() {
1262            let aux_bin_dir = self.aux_bin_output_dir_name();
1263            remove_and_create_dir_all(&aux_dir).unwrap_or_else(|e| {
1264                panic!("failed to remove and recreate output directory `{aux_dir}`: {e}")
1265            });
1266            remove_and_create_dir_all(&aux_bin_dir).unwrap_or_else(|e| {
1267                panic!("failed to remove and recreate output directory `{aux_bin_dir}`: {e}")
1268            });
1269        }
1270
1271        aux_dir
1272    }
1273
1274    fn build_all_auxiliary(&self, aux_dir: &Utf8Path, rustc: &mut Command) {
1275        for rel_ab in &self.props.aux.builds {
1276            self.build_auxiliary(rel_ab, &aux_dir, None);
1277        }
1278
1279        for rel_ab in &self.props.aux.bins {
1280            self.build_auxiliary(rel_ab, &aux_dir, Some(AuxType::Bin));
1281        }
1282
1283        let path_to_crate_name = |path: &str| -> String {
1284            path.rsplit_once('/')
1285                .map_or(path, |(_, tail)| tail)
1286                .trim_end_matches(".rs")
1287                .replace('-', "_")
1288        };
1289
1290        let add_extern = |rustc: &mut Command,
1291                          extern_modifiers: Option<&str>,
1292                          aux_name: &str,
1293                          aux_path: &str,
1294                          aux_type: AuxType| {
1295            let lib_name = get_lib_name(&path_to_crate_name(aux_path), aux_type);
1296            if let Some(lib_name) = lib_name {
1297                let modifiers_and_name = match extern_modifiers {
1298                    Some(modifiers) => format!("{modifiers}:{aux_name}"),
1299                    None => aux_name.to_string(),
1300                };
1301                rustc.arg("--extern").arg(format!("{modifiers_and_name}={aux_dir}/{lib_name}"));
1302            }
1303        };
1304
1305        for AuxCrate { extern_modifiers, name, path } in &self.props.aux.crates {
1306            let aux_type = self.build_auxiliary(&path, &aux_dir, None);
1307            add_extern(rustc, extern_modifiers.as_deref(), name, path, aux_type);
1308        }
1309
1310        for proc_macro in &self.props.aux.proc_macros {
1311            self.build_auxiliary(&proc_macro.path, &aux_dir, Some(AuxType::ProcMacro));
1312            let crate_name = path_to_crate_name(&proc_macro.path);
1313            add_extern(
1314                rustc,
1315                proc_macro.extern_modifiers.as_deref(),
1316                &crate_name,
1317                &proc_macro.path,
1318                AuxType::ProcMacro,
1319            );
1320        }
1321
1322        // Build any `//@ aux-codegen-backend`, and pass the resulting library
1323        // to `-Zcodegen-backend` when compiling the test file.
1324        if let Some(aux_file) = &self.props.aux.codegen_backend {
1325            let aux_type = self.build_auxiliary(aux_file, aux_dir, None);
1326            if let Some(lib_name) = get_lib_name(aux_file.trim_end_matches(".rs"), aux_type) {
1327                let lib_path = aux_dir.join(&lib_name);
1328                rustc.arg(format!("-Zcodegen-backend={}", lib_path));
1329            }
1330        }
1331    }
1332
1333    /// `root_testpaths` refers to the path of the original test. the auxiliary and the test with an
1334    /// aux-build have the same `root_testpaths`.
1335    fn compose_and_run_compiler(&self, mut rustc: Command, input: Option<String>) -> ProcRes {
1336        if self.props.add_minicore {
1337            let minicore_path = self.build_minicore();
1338            rustc.arg("--extern");
1339            rustc.arg(&format!("minicore={}", minicore_path));
1340        }
1341
1342        let aux_dir = self.aux_output_dir();
1343        self.build_all_auxiliary(&aux_dir, &mut rustc);
1344
1345        rustc.envs(self.props.rustc_env.clone());
1346        self.props.unset_rustc_env.iter().fold(&mut rustc, Command::env_remove);
1347        self.compose_and_run(
1348            rustc,
1349            self.config.host_compile_lib_path.as_path(),
1350            Some(aux_dir.as_path()),
1351            input,
1352        )
1353    }
1354
1355    /// Builds `minicore`. Returns the path to the minicore rlib within the base test output
1356    /// directory.
1357    fn build_minicore(&self) -> Utf8PathBuf {
1358        let output_file_path = self.output_base_dir().join("libminicore.rlib");
1359        let mut rustc = self.make_compile_args(
1360            CompilerKind::Rustc,
1361            &self.config.minicore_path,
1362            TargetLocation::ThisFile(output_file_path.clone()),
1363            Emit::None,
1364            AllowUnused::Yes,
1365            LinkToAux::No,
1366            vec![],
1367        );
1368
1369        rustc.args(&["--crate-type", "rlib"]);
1370        rustc.arg("-Cpanic=abort");
1371        rustc.args(self.props.minicore_compile_flags.clone());
1372
1373        let res =
1374            self.compose_and_run(rustc, self.config.host_compile_lib_path.as_path(), None, None);
1375        if !res.status.success() {
1376            self.fatal_proc_rec(
1377                &format!("auxiliary build of {} failed to compile: ", self.config.minicore_path),
1378                &res,
1379            );
1380        }
1381
1382        output_file_path
1383    }
1384
1385    /// Builds an aux dependency.
1386    ///
1387    /// If `aux_type` is `None`, then this will determine the aux-type automatically.
1388    fn build_auxiliary(
1389        &self,
1390        source_path: &str,
1391        aux_dir: &Utf8Path,
1392        aux_type: Option<AuxType>,
1393    ) -> AuxType {
1394        let aux_path = self.resolve_aux_path(source_path);
1395        let mut aux_props = self.props.from_aux_file(&aux_path, self.revision, self.config);
1396        if aux_type == Some(AuxType::ProcMacro) {
1397            aux_props.force_host = true;
1398        }
1399        let mut aux_dir = aux_dir.to_path_buf();
1400        if aux_type == Some(AuxType::Bin) {
1401            // On unix, the binary of `auxiliary/foo.rs` will be named
1402            // `auxiliary/foo` which clashes with the _dir_ `auxiliary/foo`, so
1403            // put bins in a `bin` subfolder.
1404            aux_dir.push("bin");
1405        }
1406        let aux_output = TargetLocation::ThisDirectory(aux_dir.clone());
1407        let aux_cx = TestCx {
1408            config: self.config,
1409            stdout: self.stdout,
1410            stderr: self.stderr,
1411            props: &aux_props,
1412            testpaths: self.testpaths,
1413            revision: self.revision,
1414        };
1415        // Create the directory for the stdout/stderr files.
1416        create_dir_all(aux_cx.output_base_dir()).unwrap();
1417        let mut aux_rustc = aux_cx.make_compile_args(
1418            // Always use `rustc` for aux crates, even in rustdoc tests.
1419            CompilerKind::Rustc,
1420            &aux_path,
1421            aux_output,
1422            Emit::None,
1423            AllowUnused::No,
1424            LinkToAux::No,
1425            Vec::new(),
1426        );
1427        aux_cx.build_all_auxiliary(&aux_dir, &mut aux_rustc);
1428
1429        aux_rustc.envs(aux_props.rustc_env.clone());
1430        for key in &aux_props.unset_rustc_env {
1431            aux_rustc.env_remove(key);
1432        }
1433
1434        let (aux_type, crate_type) = if aux_type == Some(AuxType::Bin) {
1435            (AuxType::Bin, Some("bin"))
1436        } else if aux_type == Some(AuxType::ProcMacro) {
1437            (AuxType::ProcMacro, Some("proc-macro"))
1438        } else if aux_type.is_some() {
1439            panic!("aux_type {aux_type:?} not expected");
1440        } else if aux_props.no_prefer_dynamic {
1441            (AuxType::Lib, None)
1442        } else if self.config.target.contains("emscripten")
1443            || (self.config.target.contains("musl")
1444                && !aux_props.force_host
1445                && !self.config.host.contains("musl"))
1446            || self.config.target.contains("wasm32")
1447            || self.config.target.contains("nvptx")
1448            || self.is_vxworks_pure_static()
1449            || self.config.target.contains("bpf")
1450            || !self.config.target_cfg().dynamic_linking
1451            || matches!(self.config.mode, TestMode::CoverageMap | TestMode::CoverageRun)
1452        {
1453            // We primarily compile all auxiliary libraries as dynamic libraries
1454            // to avoid code size bloat and large binaries as much as possible
1455            // for the test suite (otherwise including libstd statically in all
1456            // executables takes up quite a bit of space).
1457            //
1458            // For targets like MUSL or Emscripten, however, there is no support for
1459            // dynamic libraries so we just go back to building a normal library. Note,
1460            // however, that for MUSL if the library is built with `force_host` then
1461            // it's ok to be a dylib as the host should always support dylibs.
1462            //
1463            // Coverage tests want static linking by default so that coverage
1464            // mappings in auxiliary libraries can be merged into the final
1465            // executable.
1466            (AuxType::Lib, Some("lib"))
1467        } else {
1468            (AuxType::Dylib, Some("dylib"))
1469        };
1470
1471        if let Some(crate_type) = crate_type {
1472            aux_rustc.args(&["--crate-type", crate_type]);
1473        }
1474
1475        if aux_type == AuxType::ProcMacro {
1476            // For convenience, but this only works on 2018.
1477            aux_rustc.args(&["--extern", "proc_macro"]);
1478        }
1479
1480        aux_rustc.arg("-L").arg(&aux_dir);
1481
1482        if aux_props.add_minicore {
1483            let minicore_path = self.build_minicore();
1484            aux_rustc.arg("--extern");
1485            aux_rustc.arg(&format!("minicore={}", minicore_path));
1486        }
1487
1488        let auxres = aux_cx.compose_and_run(
1489            aux_rustc,
1490            aux_cx.config.host_compile_lib_path.as_path(),
1491            Some(aux_dir.as_path()),
1492            None,
1493        );
1494        if !auxres.status.success() {
1495            self.fatal_proc_rec(
1496                &format!("auxiliary build of {aux_path} failed to compile: "),
1497                &auxres,
1498            );
1499        }
1500        aux_type
1501    }
1502
1503    fn read2_abbreviated(&self, child: Child) -> (Output, Truncated) {
1504        let mut filter_paths_from_len = Vec::new();
1505        let mut add_path = |path: &Utf8Path| {
1506            let path = path.to_string();
1507            let windows = path.replace("\\", "\\\\");
1508            if windows != path {
1509                filter_paths_from_len.push(windows);
1510            }
1511            filter_paths_from_len.push(path);
1512        };
1513
1514        // List of paths that will not be measured when determining whether the output is larger
1515        // than the output truncation threshold.
1516        //
1517        // Note: avoid adding a subdirectory of an already filtered directory here, otherwise the
1518        // same slice of text will be double counted and the truncation might not happen.
1519        add_path(&self.config.src_test_suite_root);
1520        add_path(&self.config.build_test_suite_root);
1521
1522        read2_abbreviated(child, &filter_paths_from_len).expect("failed to read output")
1523    }
1524
1525    fn compose_and_run(
1526        &self,
1527        mut command: Command,
1528        lib_path: &Utf8Path,
1529        aux_path: Option<&Utf8Path>,
1530        input: Option<String>,
1531    ) -> ProcRes {
1532        let cmdline = {
1533            let cmdline = self.make_cmdline(&command, lib_path);
1534            self.logv(format_args!("executing {cmdline}"));
1535            cmdline
1536        };
1537
1538        command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::piped());
1539
1540        // Need to be sure to put both the lib_path and the aux path in the dylib
1541        // search path for the child.
1542        add_dylib_path(&mut command, iter::once(lib_path).chain(aux_path));
1543
1544        let mut child = disable_error_reporting(|| command.spawn())
1545            .unwrap_or_else(|e| panic!("failed to exec `{command:?}`: {e:?}"));
1546        if let Some(input) = input {
1547            child.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap();
1548        }
1549
1550        let (Output { status, stdout, stderr }, truncated) = self.read2_abbreviated(child);
1551
1552        let result = ProcRes {
1553            status,
1554            stdout: String::from_utf8_lossy(&stdout).into_owned(),
1555            stderr: String::from_utf8_lossy(&stderr).into_owned(),
1556            truncated,
1557            cmdline,
1558        };
1559
1560        self.dump_output(
1561            self.config.verbose || (!result.status.success() && self.config.mode != TestMode::Ui),
1562            &command.get_program().to_string_lossy(),
1563            &result.stdout,
1564            &result.stderr,
1565        );
1566
1567        result
1568    }
1569
1570    /// Choose a compiler kind (rustc or rustdoc) for compiling test files,
1571    /// based on the test suite being tested.
1572    fn compiler_kind_for_non_aux(&self) -> CompilerKind {
1573        match self.config.suite {
1574            TestSuite::RustdocJs | TestSuite::RustdocJson | TestSuite::RustdocUi => {
1575                CompilerKind::Rustdoc
1576            }
1577
1578            // Exhaustively match all other suites.
1579            // Note that some suites never actually use this method, so the
1580            // return value for those suites is not necessarily meaningful.
1581            TestSuite::AssemblyLlvm
1582            | TestSuite::BuildStd
1583            | TestSuite::CodegenLlvm
1584            | TestSuite::CodegenUnits
1585            | TestSuite::Coverage
1586            | TestSuite::CoverageRunRustdoc
1587            | TestSuite::Crashes
1588            | TestSuite::Debuginfo
1589            | TestSuite::Incremental
1590            | TestSuite::MirOpt
1591            | TestSuite::Pretty
1592            | TestSuite::RunMake
1593            | TestSuite::RunMakeCargo
1594            | TestSuite::RustdocGui
1595            | TestSuite::RustdocHtml
1596            | TestSuite::RustdocJsStd
1597            | TestSuite::Ui
1598            | TestSuite::UiFullDeps => CompilerKind::Rustc,
1599        }
1600    }
1601
1602    fn make_compile_args(
1603        &self,
1604        compiler_kind: CompilerKind,
1605        input_file: &Utf8Path,
1606        output_file: TargetLocation,
1607        emit: Emit,
1608        allow_unused: AllowUnused,
1609        link_to_aux: LinkToAux,
1610        passes: Vec<String>, // Vec of passes under mir-opt test to be dumped
1611    ) -> Command {
1612        // FIXME(Zalathar): We should have a cleaner distinction between
1613        // `rustc` flags, `rustdoc` flags, and flags shared by both.
1614        let mut compiler = match compiler_kind {
1615            CompilerKind::Rustc => Command::new(&self.config.rustc_path),
1616            CompilerKind::Rustdoc => {
1617                Command::new(&self.config.rustdoc_path.clone().expect("no rustdoc built yet"))
1618            }
1619        };
1620        compiler.arg(input_file);
1621
1622        // Use a single thread for efficiency and a deterministic error message order
1623        compiler.arg("-Zthreads=1");
1624
1625        // Hide libstd sources from ui tests to make sure we generate the stderr
1626        // output that users will see.
1627        // Without this, we may be producing good diagnostics in-tree but users
1628        // will not see half the information.
1629        //
1630        // This also has the benefit of more effectively normalizing output between different
1631        // compilers, so that we don't have to know the `/rustc/$sha` output to normalize after the
1632        // fact.
1633        compiler.arg("-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX");
1634        compiler.arg("-Ztranslate-remapped-path-to-local-path=no");
1635
1636        // Hide Cargo dependency sources from ui tests to make sure the error message doesn't
1637        // change depending on whether $CARGO_HOME is remapped or not. If this is not present,
1638        // when $CARGO_HOME is remapped the source won't be shown, and when it's not remapped the
1639        // source will be shown, causing a blessing hell.
1640        compiler.arg("-Z").arg(format!(
1641            "ignore-directory-in-diagnostics-source-blocks={}",
1642            home::cargo_home().expect("failed to find cargo home").to_str().unwrap()
1643        ));
1644        // Similarly, vendored sources shouldn't be shown when running from a dist tarball.
1645        compiler.arg("-Z").arg(format!(
1646            "ignore-directory-in-diagnostics-source-blocks={}",
1647            self.config.src_root.join("vendor"),
1648        ));
1649
1650        // Optionally prevent default --sysroot if specified in test compile-flags.
1651        //
1652        // FIXME: I feel like this logic is fairly sus.
1653        if !self.props.compile_flags.iter().any(|flag| flag.starts_with("--sysroot"))
1654            && !self.config.host_rustcflags.iter().any(|flag| flag == "--sysroot")
1655        {
1656            // In stage 0, make sure we use `stage0-sysroot` instead of the bootstrap sysroot.
1657            compiler.arg("--sysroot").arg(&self.config.sysroot_base);
1658        }
1659
1660        // If the provided codegen backend is not LLVM, we need to pass it.
1661        if let Some(ref backend) = self.config.override_codegen_backend {
1662            compiler.arg(format!("-Zcodegen-backend={}", backend));
1663        }
1664
1665        // Optionally prevent default --target if specified in test compile-flags.
1666        let custom_target = self.props.compile_flags.iter().any(|x| x.starts_with("--target"));
1667
1668        if !custom_target {
1669            let target =
1670                if self.props.force_host { &*self.config.host } else { &*self.config.target };
1671
1672            compiler.arg(&format!("--target={}", target));
1673            if target.ends_with(".json") {
1674                // `-Zunstable-options` is necessary when compiletest is running with custom targets
1675                // (such as synthetic targets used to bless mir-opt tests).
1676                compiler.arg("-Zunstable-options");
1677            }
1678        }
1679        self.set_revision_flags(&mut compiler);
1680
1681        if compiler_kind == CompilerKind::Rustc {
1682            if let Some(ref incremental_dir) = self.props.incremental_dir {
1683                compiler.args(&["-C", &format!("incremental={}", incremental_dir)]);
1684                compiler.args(&["-Z", "incremental-verify-ich"]);
1685            }
1686
1687            if self.config.mode == TestMode::CodegenUnits {
1688                compiler.args(&["-Z", "human_readable_cgu_names"]);
1689            }
1690        }
1691
1692        if self.config.optimize_tests && compiler_kind == CompilerKind::Rustc {
1693            match self.config.mode {
1694                TestMode::Ui => {
1695                    // If optimize-tests is true we still only want to optimize tests that actually get
1696                    // executed and that don't specify their own optimization levels.
1697                    // Note: aux libs don't have a pass-mode, so they won't get optimized
1698                    // unless compile-flags are set in the aux file.
1699                    if self.props.pass_mode(&self.config) == Some(PassMode::Run)
1700                        && !self
1701                            .props
1702                            .compile_flags
1703                            .iter()
1704                            .any(|arg| arg == "-O" || arg.contains("opt-level"))
1705                    {
1706                        compiler.arg("-O");
1707                    }
1708                }
1709                TestMode::DebugInfo => { /* debuginfo tests must be unoptimized */ }
1710                TestMode::CoverageMap | TestMode::CoverageRun => {
1711                    // Coverage mappings and coverage reports are affected by
1712                    // optimization level, so they ignore the optimize-tests
1713                    // setting and set an optimization level in their mode's
1714                    // compile flags (below) or in per-test `compile-flags`.
1715                }
1716                _ => {
1717                    compiler.arg("-O");
1718                }
1719            }
1720        }
1721
1722        let set_mir_dump_dir = |rustc: &mut Command| {
1723            let mir_dump_dir = self.output_base_dir();
1724            let mut dir_opt = "-Zdump-mir-dir=".to_string();
1725            dir_opt.push_str(mir_dump_dir.as_str());
1726            debug!("dir_opt: {:?}", dir_opt);
1727            rustc.arg(dir_opt);
1728        };
1729
1730        match self.config.mode {
1731            TestMode::Incremental => {
1732                // If we are extracting and matching errors in the new
1733                // fashion, then you want JSON mode. Old-skool error
1734                // patterns still match the raw compiler output.
1735                if self.props.error_patterns.is_empty()
1736                    && self.props.regex_error_patterns.is_empty()
1737                {
1738                    compiler.args(&["--error-format", "json"]);
1739                    compiler.args(&["--json", "future-incompat"]);
1740                }
1741                compiler.arg("-Zui-testing");
1742                compiler.arg("-Zdeduplicate-diagnostics=no");
1743            }
1744            TestMode::Ui => {
1745                if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) {
1746                    compiler.args(&["--error-format", "json"]);
1747                    compiler.args(&["--json", "future-incompat"]);
1748                }
1749                compiler.arg("-Ccodegen-units=1");
1750                // Hide line numbers to reduce churn
1751                compiler.arg("-Zui-testing");
1752                compiler.arg("-Zdeduplicate-diagnostics=no");
1753                compiler.arg("-Zwrite-long-types-to-disk=no");
1754                // FIXME: use this for other modes too, for perf?
1755                compiler.arg("-Cstrip=debuginfo");
1756
1757                if self.config.parallel_frontend_enabled() {
1758                    // Currently, we only use multiple threads for the UI test suite,
1759                    // because UI tests can effectively verify the parallel frontend and
1760                    // require minimal modification. The option will later be extended to
1761                    // other test suites.
1762                    compiler.arg(&format!("-Zthreads={}", self.config.parallel_frontend_threads));
1763                }
1764            }
1765            TestMode::MirOpt => {
1766                // We check passes under test to minimize the mir-opt test dump
1767                // if files_for_miropt_test parses the passes, we dump only those passes
1768                // otherwise we conservatively pass -Zdump-mir=all
1769                let zdump_arg = if !passes.is_empty() {
1770                    format!("-Zdump-mir={}", passes.join(" | "))
1771                } else {
1772                    "-Zdump-mir=all".to_string()
1773                };
1774
1775                compiler.args(&[
1776                    "-Copt-level=1",
1777                    &zdump_arg,
1778                    "-Zvalidate-mir",
1779                    "-Zlint-mir",
1780                    "-Zdump-mir-exclude-pass-number",
1781                    "-Zmir-include-spans=false", // remove span comments from NLL MIR dumps
1782                    "--crate-type=rlib",
1783                ]);
1784                if let Some(pass) = &self.props.mir_unit_test {
1785                    compiler
1786                        .args(&["-Zmir-opt-level=0", &format!("-Zmir-enable-passes=+{}", pass)]);
1787                } else {
1788                    compiler.args(&[
1789                        "-Zmir-opt-level=4",
1790                        "-Zmir-enable-passes=+ReorderBasicBlocks,+ReorderLocals",
1791                    ]);
1792                }
1793
1794                set_mir_dump_dir(&mut compiler);
1795            }
1796            TestMode::CoverageMap => {
1797                compiler.arg("-Cinstrument-coverage");
1798                // These tests only compile to LLVM IR, so they don't need the
1799                // profiler runtime to be present.
1800                compiler.arg("-Zno-profiler-runtime");
1801                // Coverage mappings are sensitive to MIR optimizations, and
1802                // the current snapshots assume `opt-level=2` unless overridden
1803                // by `compile-flags`.
1804                compiler.arg("-Copt-level=2");
1805            }
1806            TestMode::CoverageRun => {
1807                compiler.arg("-Cinstrument-coverage");
1808                // Coverage reports are sometimes sensitive to optimizations,
1809                // and the current snapshots assume `opt-level=2` unless
1810                // overridden by `compile-flags`.
1811                compiler.arg("-Copt-level=2");
1812            }
1813            TestMode::Assembly | TestMode::Codegen => {
1814                compiler.arg("-Cdebug-assertions=no");
1815                // For assembly and codegen tests, we want to use the same order
1816                // of the items of a codegen unit as the source order, so that
1817                // we can compare the output with the source code through filecheck.
1818                compiler.arg("-Zcodegen-source-order");
1819            }
1820            TestMode::Crashes => {
1821                set_mir_dump_dir(&mut compiler);
1822            }
1823            TestMode::CodegenUnits => {
1824                compiler.arg("-Zprint-mono-items");
1825            }
1826            TestMode::Pretty
1827            | TestMode::DebugInfo
1828            | TestMode::RustdocHtml
1829            | TestMode::RustdocJson
1830            | TestMode::RunMake
1831            | TestMode::RustdocJs => {
1832                // do not use JSON output
1833            }
1834        }
1835
1836        if self.props.remap_src_base {
1837            compiler.arg(format!(
1838                "--remap-path-prefix={}={}",
1839                self.config.src_test_suite_root, FAKE_SRC_BASE,
1840            ));
1841        }
1842
1843        if compiler_kind == CompilerKind::Rustc {
1844            match emit {
1845                Emit::None => {}
1846                Emit::Metadata => {
1847                    compiler.args(&["--emit", "metadata"]);
1848                }
1849                Emit::LlvmIr => {
1850                    compiler.args(&["--emit", "llvm-ir"]);
1851                }
1852                Emit::Mir => {
1853                    compiler.args(&["--emit", "mir"]);
1854                }
1855                Emit::Asm => {
1856                    compiler.args(&["--emit", "asm"]);
1857                }
1858                Emit::LinkArgsAsm => {
1859                    compiler.args(&["-Clink-args=--emit=asm"]);
1860                }
1861            }
1862        }
1863
1864        if compiler_kind == CompilerKind::Rustc {
1865            if self.config.target == "wasm32-unknown-unknown" || self.is_vxworks_pure_static() {
1866                // rustc.arg("-g"); // get any backtrace at all on errors
1867            } else if !self.props.no_prefer_dynamic {
1868                compiler.args(&["-C", "prefer-dynamic"]);
1869            }
1870        }
1871
1872        match output_file {
1873            // If the test's compile flags specify an output path with `-o`,
1874            // avoid a compiler warning about `--out-dir` being ignored.
1875            _ if self.props.compile_flags.iter().any(|flag| flag == "-o") => {}
1876            TargetLocation::ThisFile(path) => {
1877                compiler.arg("-o").arg(path);
1878            }
1879            TargetLocation::ThisDirectory(path) => match compiler_kind {
1880                CompilerKind::Rustdoc => {
1881                    // `rustdoc` uses `-o` for the output directory.
1882                    compiler.arg("-o").arg(path);
1883                }
1884                CompilerKind::Rustc => {
1885                    compiler.arg("--out-dir").arg(path);
1886                }
1887            },
1888        }
1889
1890        match self.config.compare_mode {
1891            Some(CompareMode::Polonius) => {
1892                compiler.args(&["-Zpolonius=next"]);
1893            }
1894            Some(CompareMode::NextSolver) => {
1895                compiler.args(&["-Znext-solver"]);
1896            }
1897            Some(CompareMode::NextSolverCoherence) => {
1898                compiler.args(&["-Znext-solver=coherence"]);
1899            }
1900            Some(CompareMode::SplitDwarf) if self.config.target.contains("windows") => {
1901                compiler.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]);
1902            }
1903            Some(CompareMode::SplitDwarf) => {
1904                compiler.args(&["-Csplit-debuginfo=unpacked"]);
1905            }
1906            Some(CompareMode::SplitDwarfSingle) => {
1907                compiler.args(&["-Csplit-debuginfo=packed"]);
1908            }
1909            None => {}
1910        }
1911
1912        // Add `-A unused` before `config` flags and in-test (`props`) flags, so that they can
1913        // overwrite this.
1914        // Don't allow `unused_attributes` since these are usually actual mistakes, rather than just unused code.
1915        if let AllowUnused::Yes = allow_unused {
1916            compiler.args(&["-A", "unused", "-W", "unused_attributes"]);
1917        }
1918
1919        // Allow tests to use internal and incomplete features.
1920        compiler.args(&["-A", "internal_features"]);
1921        compiler.args(&["-A", "incomplete_features"]);
1922
1923        // Allow tests to have unused parens and braces.
1924        // Add #![deny(unused_parens, unused_braces)] to the test file if you want to
1925        // test that these lints are working.
1926        compiler.args(&["-A", "unused_parens"]);
1927        compiler.args(&["-A", "unused_braces"]);
1928
1929        if self.props.force_host {
1930            self.maybe_add_external_args(&mut compiler, &self.config.host_rustcflags);
1931            if compiler_kind == CompilerKind::Rustc
1932                && let Some(ref linker) = self.config.host_linker
1933            {
1934                compiler.arg(format!("-Clinker={linker}"));
1935            }
1936        } else {
1937            self.maybe_add_external_args(&mut compiler, &self.config.target_rustcflags);
1938            if compiler_kind == CompilerKind::Rustc
1939                && let Some(ref linker) = self.config.target_linker
1940            {
1941                compiler.arg(format!("-Clinker={linker}"));
1942            }
1943        }
1944
1945        // Use dynamic musl for tests because static doesn't allow creating dylibs
1946        if self.config.host.contains("musl") || self.is_vxworks_pure_dynamic() {
1947            compiler.arg("-Ctarget-feature=-crt-static");
1948        }
1949
1950        if let LinkToAux::Yes = link_to_aux {
1951            // if we pass an `-L` argument to a directory that doesn't exist,
1952            // macOS ld emits warnings which disrupt the .stderr files
1953            if self.has_aux_dir() {
1954                compiler.arg("-L").arg(self.aux_output_dir_name());
1955            }
1956        }
1957
1958        // FIXME(jieyouxu): we should report a fatal error or warning if user wrote `-Cpanic=` with
1959        // something that's not `abort` and `-Cforce-unwind-tables` with a value that is not `yes`.
1960        //
1961        // We could apply these last and override any provided flags. That would ensure that the
1962        // build works, but some tests want to exercise that mixing panic modes in specific ways is
1963        // rejected. So we enable aborting panics and unwind tables before adding flags, just to
1964        // change the default.
1965        //
1966        // `minicore` requires `#![no_std]` and `#![no_core]`, which means no unwinding panics.
1967        if self.props.add_minicore {
1968            compiler.arg("-Cpanic=abort");
1969            compiler.arg("-Cforce-unwind-tables=yes");
1970        }
1971
1972        compiler.args(&self.props.compile_flags);
1973
1974        compiler
1975    }
1976
1977    fn make_exe_name(&self) -> Utf8PathBuf {
1978        // Using a single letter here to keep the path length down for
1979        // Windows.  Some test names get very long.  rustc creates `rcgu`
1980        // files with the module name appended to it which can more than
1981        // double the length.
1982        let mut f = self.output_base_dir().join("a");
1983        // FIXME: This is using the host architecture exe suffix, not target!
1984        if self.config.target.contains("emscripten") {
1985            f = f.with_extra_extension("js");
1986        } else if self.config.target.starts_with("wasm") {
1987            f = f.with_extra_extension("wasm");
1988        } else if self.config.target.contains("spirv") {
1989            f = f.with_extra_extension("spv");
1990        } else if !env::consts::EXE_SUFFIX.is_empty() {
1991            f = f.with_extra_extension(env::consts::EXE_SUFFIX);
1992        }
1993        f
1994    }
1995
1996    fn make_run_args(&self) -> ProcArgs {
1997        // If we've got another tool to run under (valgrind),
1998        // then split apart its command
1999        let mut args = self.split_maybe_args(&self.config.runner);
2000
2001        let exe_file = self.make_exe_name();
2002
2003        args.push(exe_file.into_os_string());
2004
2005        // Add the arguments in the run_flags directive
2006        args.extend(self.props.run_flags.iter().map(OsString::from));
2007
2008        let prog = args.remove(0);
2009        ProcArgs { prog, args }
2010    }
2011
2012    fn split_maybe_args(&self, argstr: &Option<String>) -> Vec<OsString> {
2013        match *argstr {
2014            Some(ref s) => s
2015                .split(' ')
2016                .filter_map(|s| {
2017                    if s.chars().all(|c| c.is_whitespace()) {
2018                        None
2019                    } else {
2020                        Some(OsString::from(s))
2021                    }
2022                })
2023                .collect(),
2024            None => Vec::new(),
2025        }
2026    }
2027
2028    fn make_cmdline(&self, command: &Command, libpath: &Utf8Path) -> String {
2029        use crate::util;
2030
2031        // Linux and mac don't require adjusting the library search path
2032        if cfg!(unix) {
2033            format!("{:?}", command)
2034        } else {
2035            // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
2036            // for diagnostic purposes
2037            fn lib_path_cmd_prefix(path: &str) -> String {
2038                format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path))
2039            }
2040
2041            format!("{} {:?}", lib_path_cmd_prefix(libpath.as_str()), command)
2042        }
2043    }
2044
2045    fn dump_output(&self, print_output: bool, proc_name: &str, out: &str, err: &str) {
2046        let revision = if let Some(r) = self.revision { format!("{}.", r) } else { String::new() };
2047
2048        self.dump_output_file(out, &format!("{}out", revision));
2049        self.dump_output_file(err, &format!("{}err", revision));
2050
2051        if !print_output {
2052            return;
2053        }
2054
2055        let path = Utf8Path::new(proc_name);
2056        let proc_name = if path.file_stem().is_some_and(|p| p == "rmake") {
2057            String::from_iter(
2058                path.parent()
2059                    .unwrap()
2060                    .file_name()
2061                    .into_iter()
2062                    .chain(Some("/"))
2063                    .chain(path.file_name()),
2064            )
2065        } else {
2066            path.file_name().unwrap().into()
2067        };
2068        writeln!(self.stdout, "------{proc_name} stdout------------------------------");
2069        writeln!(self.stdout, "{}", out);
2070        writeln!(self.stdout, "------{proc_name} stderr------------------------------");
2071        writeln!(self.stdout, "{}", err);
2072        writeln!(self.stdout, "------------------------------------------");
2073    }
2074
2075    fn dump_output_file(&self, out: &str, extension: &str) {
2076        let outfile = self.make_out_name(extension);
2077        fs::write(outfile.as_std_path(), out)
2078            .unwrap_or_else(|err| panic!("failed to write {outfile}: {err:?}"));
2079    }
2080
2081    /// Creates a filename for output with the given extension.
2082    /// E.g., `/.../testname.revision.mode/testname.extension`.
2083    fn make_out_name(&self, extension: &str) -> Utf8PathBuf {
2084        self.output_base_name().with_extension(extension)
2085    }
2086
2087    /// Gets the directory where auxiliary files are written.
2088    /// E.g., `/.../testname.revision.mode/auxiliary/`.
2089    fn aux_output_dir_name(&self) -> Utf8PathBuf {
2090        self.output_base_dir()
2091            .join("auxiliary")
2092            .with_extra_extension(self.config.mode.aux_dir_disambiguator())
2093    }
2094
2095    /// Gets the directory where auxiliary binaries are written.
2096    /// E.g., `/.../testname.revision.mode/auxiliary/bin`.
2097    fn aux_bin_output_dir_name(&self) -> Utf8PathBuf {
2098        self.aux_output_dir_name().join("bin")
2099    }
2100
2101    /// The revision, ignored for incremental compilation since it wants all revisions in
2102    /// the same directory.
2103    fn safe_revision(&self) -> Option<&str> {
2104        if self.config.mode == TestMode::Incremental { None } else { self.revision }
2105    }
2106
2107    /// Gets the absolute path to the directory where all output for the given
2108    /// test/revision should reside.
2109    /// E.g., `/path/to/build/host-tuple/test/ui/relative/testname.revision.mode/`.
2110    fn output_base_dir(&self) -> Utf8PathBuf {
2111        output_base_dir(self.config, self.testpaths, self.safe_revision())
2112    }
2113
2114    /// Gets the absolute path to the base filename used as output for the given
2115    /// test/revision.
2116    /// E.g., `/.../relative/testname.revision.mode/testname`.
2117    fn output_base_name(&self) -> Utf8PathBuf {
2118        output_base_name(self.config, self.testpaths, self.safe_revision())
2119    }
2120
2121    /// Prints a message to (captured) stdout if `config.verbose` is true.
2122    /// The message is also logged to `tracing::debug!` regardless of verbosity.
2123    ///
2124    /// Use `format_args!` as the argument to perform formatting if required.
2125    fn logv(&self, message: impl fmt::Display) {
2126        debug!("{message}");
2127        if self.config.verbose {
2128            // Note: `./x test ... --verbose --no-capture` is needed to see this print.
2129            writeln!(self.stdout, "{message}");
2130        }
2131    }
2132
2133    /// Prefix to print before error messages. Normally just `error`, but also
2134    /// includes the revision name for tests that use revisions.
2135    #[must_use]
2136    fn error_prefix(&self) -> String {
2137        match self.revision {
2138            Some(rev) => format!("error in revision `{rev}`"),
2139            None => format!("error"),
2140        }
2141    }
2142
2143    #[track_caller]
2144    fn fatal(&self, err: &str) -> ! {
2145        writeln!(self.stdout, "\n{prefix}: {err}", prefix = self.error_prefix());
2146        error!("fatal error, panic: {:?}", err);
2147        panic!("fatal error");
2148    }
2149
2150    fn fatal_proc_rec(&self, err: &str, proc_res: &ProcRes) -> ! {
2151        self.fatal_proc_rec_general(err, None, proc_res, || ());
2152    }
2153
2154    /// Underlying implementation of [`Self::fatal_proc_rec`], providing some
2155    /// extra capabilities not needed by most callers.
2156    fn fatal_proc_rec_general(
2157        &self,
2158        err: &str,
2159        extra_note: Option<&str>,
2160        proc_res: &ProcRes,
2161        callback_before_unwind: impl FnOnce(),
2162    ) -> ! {
2163        writeln!(self.stdout, "\n{prefix}: {err}", prefix = self.error_prefix());
2164
2165        // Some callers want to print additional notes after the main error message.
2166        if let Some(note) = extra_note {
2167            writeln!(self.stdout, "{note}");
2168        }
2169
2170        // Print the details and output of the subprocess that caused this test to fail.
2171        writeln!(self.stdout, "{}", proc_res.format_info());
2172
2173        // Some callers want print more context or show a custom diff before the unwind occurs.
2174        callback_before_unwind();
2175
2176        // Use resume_unwind instead of panic!() to prevent a panic message + backtrace from
2177        // compiletest, which is unnecessary noise.
2178        std::panic::resume_unwind(Box::new(()));
2179    }
2180
2181    // codegen tests (using FileCheck)
2182
2183    fn compile_test_and_save_ir(&self) -> (ProcRes, Utf8PathBuf) {
2184        let output_path = self.output_base_name().with_extension("ll");
2185        let input_file = &self.testpaths.file;
2186        let rustc = self.make_compile_args(
2187            CompilerKind::Rustc,
2188            input_file,
2189            TargetLocation::ThisFile(output_path.clone()),
2190            Emit::LlvmIr,
2191            AllowUnused::No,
2192            LinkToAux::Yes,
2193            Vec::new(),
2194        );
2195
2196        let proc_res = self.compose_and_run_compiler(rustc, None);
2197        (proc_res, output_path)
2198    }
2199
2200    fn verify_with_filecheck(&self, output: &Utf8Path) -> ProcRes {
2201        let mut filecheck = Command::new(self.config.llvm_filecheck.as_ref().unwrap());
2202        filecheck.arg("--input-file").arg(output).arg(&self.testpaths.file);
2203
2204        // Because we use custom prefixes, we also have to register the default prefix.
2205        filecheck.arg("--check-prefix=CHECK");
2206
2207        // FIXME(#134510): auto-registering revision names as check prefix is a bit sketchy, and
2208        // that having to pass `--allow-unused-prefix` is an unfortunate side-effect of not knowing
2209        // whether the test author actually wanted revision-specific check prefixes or not.
2210        //
2211        // TL;DR We may not want to conflate `compiletest` revisions and `FileCheck` prefixes.
2212
2213        // HACK: tests are allowed to use a revision name as a check prefix.
2214        if let Some(rev) = self.revision {
2215            filecheck.arg("--check-prefix").arg(rev);
2216        }
2217
2218        // HACK: the filecheck tool normally fails if a prefix is defined but not used. However,
2219        // sometimes revisions are used to specify *compiletest* directives which are not FileCheck
2220        // concerns.
2221        filecheck.arg("--allow-unused-prefixes");
2222
2223        // Provide more context on failures.
2224        filecheck.args(&["--dump-input-context", "100"]);
2225
2226        // Add custom flags supplied by the `filecheck-flags:` test directive.
2227        filecheck.args(&self.props.filecheck_flags);
2228
2229        // FIXME(jieyouxu): don't pass an empty Path
2230        self.compose_and_run(filecheck, Utf8Path::new(""), None, None)
2231    }
2232
2233    fn charset() -> &'static str {
2234        // FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset
2235        if cfg!(target_os = "freebsd") { "ISO-8859-1" } else { "UTF-8" }
2236    }
2237
2238    fn get_lines(&self, path: &Utf8Path, mut other_files: Option<&mut Vec<String>>) -> Vec<usize> {
2239        let content = fs::read_to_string(path.as_std_path()).unwrap();
2240        let mut ignore = false;
2241        content
2242            .lines()
2243            .enumerate()
2244            .filter_map(|(line_nb, line)| {
2245                if (line.trim_start().starts_with("pub mod ")
2246                    || line.trim_start().starts_with("mod "))
2247                    && line.ends_with(';')
2248                {
2249                    if let Some(ref mut other_files) = other_files {
2250                        other_files.push(line.rsplit("mod ").next().unwrap().replace(';', ""));
2251                    }
2252                    None
2253                } else {
2254                    let sline = line.rsplit("///").next().unwrap();
2255                    let line = sline.trim_start();
2256                    if line.starts_with("```") {
2257                        if ignore {
2258                            ignore = false;
2259                            None
2260                        } else {
2261                            ignore = true;
2262                            Some(line_nb + 1)
2263                        }
2264                    } else {
2265                        None
2266                    }
2267                }
2268            })
2269            .collect()
2270    }
2271
2272    /// This method is used for `//@ check-test-line-numbers-match`.
2273    ///
2274    /// It checks that doctests line in the displayed doctest "name" matches where they are
2275    /// defined in source code.
2276    fn check_rustdoc_test_option(&self, res: ProcRes) {
2277        let mut other_files = Vec::new();
2278        let mut files: HashMap<String, Vec<usize>> = HashMap::new();
2279        let normalized = fs::canonicalize(&self.testpaths.file).expect("failed to canonicalize");
2280        let normalized = normalized.to_str().unwrap().replace('\\', "/");
2281        files.insert(normalized, self.get_lines(&self.testpaths.file, Some(&mut other_files)));
2282        for other_file in other_files {
2283            let mut path = self.testpaths.file.clone();
2284            path.set_file_name(&format!("{}.rs", other_file));
2285            let path = path.canonicalize_utf8().expect("failed to canonicalize");
2286            let normalized = path.as_str().replace('\\', "/");
2287            files.insert(normalized, self.get_lines(&path, None));
2288        }
2289
2290        let mut tested = 0;
2291        for _ in res.stdout.split('\n').filter(|s| s.starts_with("test ")).inspect(|s| {
2292            if let Some((left, right)) = s.split_once(" - ") {
2293                let path = left.rsplit("test ").next().unwrap();
2294                let path = fs::canonicalize(&path).expect("failed to canonicalize");
2295                let path = path.to_str().unwrap().replace('\\', "/");
2296                if let Some(ref mut v) = files.get_mut(&path) {
2297                    tested += 1;
2298                    let mut iter = right.split("(line ");
2299                    iter.next();
2300                    let line = iter
2301                        .next()
2302                        .unwrap_or(")")
2303                        .split(')')
2304                        .next()
2305                        .unwrap_or("0")
2306                        .parse()
2307                        .unwrap_or(0);
2308                    if let Ok(pos) = v.binary_search(&line) {
2309                        v.remove(pos);
2310                    } else {
2311                        self.fatal_proc_rec(
2312                            &format!("Not found doc test: \"{}\" in \"{}\":{:?}", s, path, v),
2313                            &res,
2314                        );
2315                    }
2316                }
2317            }
2318        }) {}
2319        if tested == 0 {
2320            self.fatal_proc_rec(&format!("No test has been found... {:?}", files), &res);
2321        } else {
2322            for (entry, v) in &files {
2323                if !v.is_empty() {
2324                    self.fatal_proc_rec(
2325                        &format!(
2326                            "Not found test at line{} \"{}\":{:?}",
2327                            if v.len() > 1 { "s" } else { "" },
2328                            entry,
2329                            v
2330                        ),
2331                        &res,
2332                    );
2333                }
2334            }
2335        }
2336    }
2337
2338    fn force_color_svg(&self) -> bool {
2339        self.props.compile_flags.iter().any(|s| s.contains("--color=always"))
2340    }
2341
2342    fn load_compare_outputs(
2343        &self,
2344        proc_res: &ProcRes,
2345        output_kind: TestOutput,
2346        explicit_format: bool,
2347    ) -> usize {
2348        let stderr_bits = format!("{}bit.stderr", self.config.get_pointer_width());
2349        let (stderr_kind, stdout_kind) = match output_kind {
2350            TestOutput::Compile => (
2351                if self.force_color_svg() {
2352                    if self.config.target.contains("windows") {
2353                        // We single out Windows here because some of the CLI coloring is
2354                        // specifically changed for Windows.
2355                        UI_WINDOWS_SVG
2356                    } else {
2357                        UI_SVG
2358                    }
2359                } else if self.props.stderr_per_bitwidth {
2360                    &stderr_bits
2361                } else {
2362                    UI_STDERR
2363                },
2364                UI_STDOUT,
2365            ),
2366            TestOutput::Run => (UI_RUN_STDERR, UI_RUN_STDOUT),
2367        };
2368
2369        let expected_stderr = self.load_expected_output(stderr_kind);
2370        let expected_stdout = self.load_expected_output(stdout_kind);
2371
2372        let mut normalized_stdout =
2373            self.normalize_output(&proc_res.stdout, &self.props.normalize_stdout);
2374        match output_kind {
2375            TestOutput::Run if self.config.remote_test_client.is_some() => {
2376                // When tests are run using the remote-test-client, the string
2377                // 'uploaded "$TEST_BUILD_DIR/<test_executable>, waiting for result"'
2378                // is printed to stdout by the client and then captured in the ProcRes,
2379                // so it needs to be removed when comparing the run-pass test execution output.
2380                normalized_stdout = static_regex!(
2381                    "^uploaded \"\\$TEST_BUILD_DIR(/[[:alnum:]_\\-.]+)+\", waiting for result\n"
2382                )
2383                .replace(&normalized_stdout, "")
2384                .to_string();
2385                // When there is a panic, the remote-test-client also prints "died due to signal";
2386                // that needs to be removed as well.
2387                normalized_stdout = static_regex!("^died due to signal [0-9]+\n")
2388                    .replace(&normalized_stdout, "")
2389                    .to_string();
2390                // FIXME: it would be much nicer if we could just tell the remote-test-client to not
2391                // print these things.
2392            }
2393            _ => {}
2394        };
2395
2396        let stderr;
2397        let normalized_stderr;
2398
2399        if self.force_color_svg() {
2400            let normalized = self.normalize_output(&proc_res.stderr, &self.props.normalize_stderr);
2401            stderr = anstyle_svg::Term::new().render_svg(&normalized);
2402            normalized_stderr = stderr.clone();
2403        } else {
2404            stderr = if explicit_format {
2405                proc_res.stderr.clone()
2406            } else {
2407                json::extract_rendered(&proc_res.stderr)
2408            };
2409            normalized_stderr = self.normalize_output(&stderr, &self.props.normalize_stderr);
2410        }
2411
2412        let mut errors = 0;
2413        match output_kind {
2414            TestOutput::Compile => {
2415                if !self.props.dont_check_compiler_stdout {
2416                    if self
2417                        .compare_output(
2418                            stdout_kind,
2419                            &normalized_stdout,
2420                            &proc_res.stdout,
2421                            &expected_stdout,
2422                        )
2423                        .should_error()
2424                    {
2425                        errors += 1;
2426                    }
2427                }
2428                if !self.props.dont_check_compiler_stderr {
2429                    if self
2430                        .compare_output(stderr_kind, &normalized_stderr, &stderr, &expected_stderr)
2431                        .should_error()
2432                    {
2433                        errors += 1;
2434                    }
2435                }
2436            }
2437            TestOutput::Run => {
2438                if self
2439                    .compare_output(
2440                        stdout_kind,
2441                        &normalized_stdout,
2442                        &proc_res.stdout,
2443                        &expected_stdout,
2444                    )
2445                    .should_error()
2446                {
2447                    errors += 1;
2448                }
2449
2450                if self
2451                    .compare_output(stderr_kind, &normalized_stderr, &stderr, &expected_stderr)
2452                    .should_error()
2453                {
2454                    errors += 1;
2455                }
2456            }
2457        }
2458        errors
2459    }
2460
2461    fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> String {
2462        // Crude heuristic to detect when the output should have JSON-specific
2463        // normalization steps applied.
2464        let rflags = self.props.run_flags.join(" ");
2465        let cflags = self.props.compile_flags.join(" ");
2466        let json = rflags.contains("--format json")
2467            || rflags.contains("--format=json")
2468            || cflags.contains("--error-format json")
2469            || cflags.contains("--error-format pretty-json")
2470            || cflags.contains("--error-format=json")
2471            || cflags.contains("--error-format=pretty-json")
2472            || cflags.contains("--output-format json")
2473            || cflags.contains("--output-format=json");
2474
2475        let mut normalized = output.to_string();
2476
2477        let mut normalize_path = |from: &Utf8Path, to: &str| {
2478            let from = if json { &from.as_str().replace("\\", "\\\\") } else { from.as_str() };
2479
2480            normalized = normalized.replace(from, to);
2481        };
2482
2483        let parent_dir = self.testpaths.file.parent().unwrap();
2484        normalize_path(parent_dir, "$DIR");
2485
2486        if self.props.remap_src_base {
2487            let mut remapped_parent_dir = Utf8PathBuf::from(FAKE_SRC_BASE);
2488            if self.testpaths.relative_dir != Utf8Path::new("") {
2489                remapped_parent_dir.push(&self.testpaths.relative_dir);
2490            }
2491            normalize_path(&remapped_parent_dir, "$DIR");
2492        }
2493
2494        let base_dir = Utf8Path::new("/rustc/FAKE_PREFIX");
2495        // Fake paths into the libstd/libcore
2496        normalize_path(&base_dir.join("library"), "$SRC_DIR");
2497        // `ui-fulldeps` tests can show paths to the compiler source when testing macros from
2498        // `rustc_macros`
2499        // eg. /home/user/rust/compiler
2500        normalize_path(&base_dir.join("compiler"), "$COMPILER_DIR");
2501
2502        // Real paths into the libstd/libcore
2503        let rust_src_dir = &self.config.sysroot_base.join("lib/rustlib/src/rust");
2504        rust_src_dir.try_exists().expect(&*format!("{} should exists", rust_src_dir));
2505        let rust_src_dir =
2506            rust_src_dir.read_link_utf8().unwrap_or_else(|_| rust_src_dir.to_path_buf());
2507        normalize_path(&rust_src_dir.join("library"), "$SRC_DIR_REAL");
2508
2509        // Real paths into the compiler
2510        let rustc_src_dir = &self.config.sysroot_base.join("lib/rustlib/rustc-src/rust");
2511        rustc_src_dir.try_exists().expect(&*format!("{} should exists", rustc_src_dir));
2512        let rustc_src_dir = rustc_src_dir.read_link_utf8().unwrap_or(rustc_src_dir.to_path_buf());
2513        normalize_path(&rustc_src_dir.join("compiler"), "$COMPILER_DIR_REAL");
2514
2515        // eg.
2516        // /home/user/rust/build/x86_64-unknown-linux-gnu/test/ui/<test_dir>/$name.$revision.$mode/
2517        normalize_path(&self.output_base_dir(), "$TEST_BUILD_DIR");
2518        // Same as above, but with a canonicalized path.
2519        // This is required because some tests print canonical paths inside test build directory,
2520        // so if the build directory is a symlink, normalization doesn't help.
2521        //
2522        // NOTE: There are also tests which print the non-canonical name, so we need both this and
2523        // the above normalizations.
2524        normalize_path(&self.output_base_dir().canonicalize_utf8().unwrap(), "$TEST_BUILD_DIR");
2525        // eg. /home/user/rust/build
2526        normalize_path(&self.config.build_root, "$BUILD_DIR");
2527
2528        if json {
2529            // escaped newlines in json strings should be readable
2530            // in the stderr files. There's no point in being correct,
2531            // since only humans process the stderr files.
2532            // Thus we just turn escaped newlines back into newlines.
2533            normalized = normalized.replace("\\n", "\n");
2534        }
2535
2536        // If there are `$SRC_DIR` normalizations with line and column numbers, then replace them
2537        // with placeholders as we do not want tests needing updated when compiler source code
2538        // changes.
2539        // eg. $SRC_DIR/libcore/mem.rs:323:14 becomes $SRC_DIR/libcore/mem.rs:LL:COL
2540        normalized = static_regex!("SRC_DIR(.+):\\d+:\\d+(: \\d+:\\d+)?")
2541            .replace_all(&normalized, "SRC_DIR$1:LL:COL")
2542            .into_owned();
2543
2544        normalized = Self::normalize_platform_differences(&normalized);
2545
2546        // Normalize long type name hash.
2547        normalized =
2548            static_regex!(r"\$TEST_BUILD_DIR/(?P<filename>[^\.]+).long-type-(?P<hash>\d+).txt")
2549                .replace_all(&normalized, |caps: &Captures<'_>| {
2550                    format!(
2551                        "$TEST_BUILD_DIR/{filename}.long-type-$LONG_TYPE_HASH.txt",
2552                        filename = &caps["filename"]
2553                    )
2554                })
2555                .into_owned();
2556
2557        // Normalize thread IDs in panic messages
2558        normalized = static_regex!(r"thread '(?P<name>.*?)' \((rtid )?\d+\) panicked")
2559            .replace_all(&normalized, "thread '$name' ($$TID) panicked")
2560            .into_owned();
2561
2562        normalized = normalized.replace("\t", "\\t"); // makes tabs visible
2563
2564        // Remove test annotations like `//~ ERROR text` from the output,
2565        // since they duplicate actual errors and make the output hard to read.
2566        // This mirrors the regex in src/tools/tidy/src/style.rs, please update
2567        // both if either are changed.
2568        normalized =
2569            static_regex!("\\s*//(\\[.*\\])?~.*").replace_all(&normalized, "").into_owned();
2570
2571        // This code normalizes various hashes in v0 symbol mangling that is
2572        // emitted in the ui and mir-opt tests.
2573        let v0_crate_hash_prefix_re = static_regex!(r"_R.*?Cs[0-9a-zA-Z]+_");
2574        let v0_crate_hash_re = static_regex!(r"Cs[0-9a-zA-Z]+_");
2575
2576        const V0_CRATE_HASH_PLACEHOLDER: &str = r"CsCRATE_HASH_";
2577        if v0_crate_hash_prefix_re.is_match(&normalized) {
2578            // Normalize crate hash
2579            normalized =
2580                v0_crate_hash_re.replace_all(&normalized, V0_CRATE_HASH_PLACEHOLDER).into_owned();
2581        }
2582
2583        let v0_back_ref_prefix_re = static_regex!(r"\(_R.*?B[0-9a-zA-Z]_");
2584        let v0_back_ref_re = static_regex!(r"B[0-9a-zA-Z]_");
2585
2586        const V0_BACK_REF_PLACEHOLDER: &str = r"B<REF>_";
2587        if v0_back_ref_prefix_re.is_match(&normalized) {
2588            // Normalize back references (see RFC 2603)
2589            normalized =
2590                v0_back_ref_re.replace_all(&normalized, V0_BACK_REF_PLACEHOLDER).into_owned();
2591        }
2592
2593        // AllocId are numbered globally in a compilation session. This can lead to changes
2594        // depending on the exact compilation flags and host architecture. Meanwhile, we want
2595        // to keep them numbered, to see if the same id appears multiple times.
2596        // So we remap to deterministic numbers that only depend on the subset of allocations
2597        // that actually appear in the output.
2598        // We use uppercase ALLOC to distinguish from the non-normalized version.
2599        {
2600            let mut seen_allocs = indexmap::IndexSet::new();
2601
2602            // The alloc-id appears in pretty-printed allocations.
2603            normalized = static_regex!(
2604                r"╾─*a(lloc)?([0-9]+)(\+0x[0-9a-f]+)?(<imm>)?( \([0-9]+ ptr bytes\))?─*╼"
2605            )
2606            .replace_all(&normalized, |caps: &Captures<'_>| {
2607                // Renumber the captured index.
2608                let index = caps.get(2).unwrap().as_str().to_string();
2609                let (index, _) = seen_allocs.insert_full(index);
2610                let offset = caps.get(3).map_or("", |c| c.as_str());
2611                let imm = caps.get(4).map_or("", |c| c.as_str());
2612                // Do not bother keeping it pretty, just make it deterministic.
2613                format!("╾ALLOC{index}{offset}{imm}╼")
2614            })
2615            .into_owned();
2616
2617            // The alloc-id appears in a sentence.
2618            normalized = static_regex!(r"\balloc([0-9]+)\b")
2619                .replace_all(&normalized, |caps: &Captures<'_>| {
2620                    let index = caps.get(1).unwrap().as_str().to_string();
2621                    let (index, _) = seen_allocs.insert_full(index);
2622                    format!("ALLOC{index}")
2623                })
2624                .into_owned();
2625        }
2626
2627        // Custom normalization rules
2628        for rule in custom_rules {
2629            let re = Regex::new(&rule.0).expect("bad regex in custom normalization rule");
2630            normalized = re.replace_all(&normalized, &rule.1[..]).into_owned();
2631        }
2632        normalized
2633    }
2634
2635    /// Normalize output differences across platforms. Generally changes Windows output to be more
2636    /// Unix-like.
2637    ///
2638    /// Replaces backslashes in paths with forward slashes, and replaces CRLF line endings
2639    /// with LF.
2640    fn normalize_platform_differences(output: &str) -> String {
2641        let output = output.replace(r"\\", r"\");
2642
2643        // Used to find Windows paths.
2644        //
2645        // It's not possible to detect paths in the error messages generally, but this is a
2646        // decent enough heuristic.
2647        let re = static_regex!(
2648            r#"(?x)
2649                (?:
2650                  # Match paths that don't include spaces.
2651                  (?:\\[\pL\pN\.\-_']+)+\.\pL+
2652                |
2653                  # If the path starts with a well-known root, then allow spaces and no file extension.
2654                  \$(?:DIR|SRC_DIR|TEST_BUILD_DIR|BUILD_DIR|LIB_DIR)(?:\\[\pL\pN\.\-_'\ ]+)+
2655                )"#
2656        );
2657        re.replace_all(&output, |caps: &Captures<'_>| caps[0].replace(r"\", "/"))
2658            .replace("\r\n", "\n")
2659    }
2660
2661    fn expected_output_path(&self, kind: &str) -> Utf8PathBuf {
2662        let mut path =
2663            expected_output_path(&self.testpaths, self.revision, &self.config.compare_mode, kind);
2664
2665        if !path.exists() {
2666            if let Some(CompareMode::Polonius) = self.config.compare_mode {
2667                path = expected_output_path(&self.testpaths, self.revision, &None, kind);
2668            }
2669        }
2670
2671        if !path.exists() {
2672            path = expected_output_path(&self.testpaths, self.revision, &None, kind);
2673        }
2674
2675        path
2676    }
2677
2678    fn load_expected_output(&self, kind: &str) -> String {
2679        let path = self.expected_output_path(kind);
2680        if path.exists() {
2681            match self.load_expected_output_from_path(&path) {
2682                Ok(x) => x,
2683                Err(x) => self.fatal(&x),
2684            }
2685        } else {
2686            String::new()
2687        }
2688    }
2689
2690    fn load_expected_output_from_path(&self, path: &Utf8Path) -> Result<String, String> {
2691        fs::read_to_string(path)
2692            .map_err(|err| format!("failed to load expected output from `{}`: {}", path, err))
2693    }
2694
2695    /// Attempts to delete a file, succeeding if the file does not exist.
2696    fn delete_file(&self, file: &Utf8Path) {
2697        if let Err(e) = fs::remove_file(file.as_std_path())
2698            && e.kind() != io::ErrorKind::NotFound
2699        {
2700            self.fatal(&format!("failed to delete `{}`: {}", file, e,));
2701        }
2702    }
2703
2704    fn compare_output(
2705        &self,
2706        stream: &str,
2707        actual: &str,
2708        actual_unnormalized: &str,
2709        expected: &str,
2710    ) -> CompareOutcome {
2711        let expected_path =
2712            expected_output_path(self.testpaths, self.revision, &self.config.compare_mode, stream);
2713
2714        if self.config.bless && actual.is_empty() && expected_path.exists() {
2715            self.delete_file(&expected_path);
2716        }
2717
2718        let are_different = match (self.force_color_svg(), expected.find('\n'), actual.find('\n')) {
2719            // FIXME: We ignore the first line of SVG files
2720            // because the width parameter is non-deterministic.
2721            (true, Some(nl_e), Some(nl_a)) => expected[nl_e..] != actual[nl_a..],
2722            _ => expected != actual,
2723        };
2724        if !are_different {
2725            return CompareOutcome::Same;
2726        }
2727
2728        // Wrapper tools set by `runner` might provide extra output on failure,
2729        // for example a WebAssembly runtime might print the stack trace of an
2730        // `unreachable` instruction by default.
2731        let compare_output_by_lines_subset = self.config.runner.is_some();
2732
2733        // Also, some tests like `ui/parallel-rustc` have non-deterministic
2734        // orders of output, so we need to compare by lines.
2735        let compare_output_by_lines = self.props.compare_output_by_lines;
2736
2737        let tmp;
2738        let (expected, actual): (&str, &str) = if compare_output_by_lines_subset {
2739            let actual_lines: HashSet<_> = actual.lines().collect();
2740            let expected_lines: Vec<_> = expected.lines().collect();
2741            let mut used = expected_lines.clone();
2742            used.retain(|line| actual_lines.contains(line));
2743
2744            // check if `expected` contains a subset of the lines of `actual`
2745            if used.len() == expected_lines.len() && (expected.is_empty() == actual.is_empty()) {
2746                return CompareOutcome::Same;
2747            }
2748            if expected_lines.is_empty() {
2749                // if we have no lines to check, force a full overwrite
2750                ("", actual)
2751            } else {
2752                // this prints/blesses the subset, not the actual
2753                tmp = (expected_lines.join("\n"), used.join("\n"));
2754                (&tmp.0, &tmp.1)
2755            }
2756        } else if compare_output_by_lines {
2757            let mut actual_lines: Vec<&str> = actual.lines().collect();
2758            let mut expected_lines: Vec<&str> = expected.lines().collect();
2759            actual_lines.sort_unstable();
2760            expected_lines.sort_unstable();
2761            if actual_lines == expected_lines {
2762                return CompareOutcome::Same;
2763            } else {
2764                (expected, actual)
2765            }
2766        } else {
2767            (expected, actual)
2768        };
2769
2770        // Write the actual output to a file in build directory.
2771        let actual_path = self
2772            .output_base_name()
2773            .with_extra_extension(self.revision.unwrap_or(""))
2774            .with_extra_extension(
2775                self.config.compare_mode.as_ref().map(|cm| cm.to_str()).unwrap_or(""),
2776            )
2777            .with_extra_extension(stream);
2778
2779        if let Err(err) = fs::write(&actual_path, &actual) {
2780            self.fatal(&format!("failed to write {stream} to `{actual_path}`: {err}",));
2781        }
2782        writeln!(self.stdout, "Saved the actual {stream} to `{actual_path}`");
2783
2784        if !self.config.bless {
2785            if expected.is_empty() {
2786                writeln!(self.stdout, "normalized {}:\n{}\n", stream, actual);
2787            } else {
2788                self.show_diff(
2789                    stream,
2790                    &expected_path,
2791                    &actual_path,
2792                    expected,
2793                    actual,
2794                    actual_unnormalized,
2795                );
2796            }
2797        } else {
2798            // Delete non-revision .stderr/.stdout file if revisions are used.
2799            // Without this, we'd just generate the new files and leave the old files around.
2800            if self.revision.is_some() {
2801                let old =
2802                    expected_output_path(self.testpaths, None, &self.config.compare_mode, stream);
2803                self.delete_file(&old);
2804            }
2805
2806            if !actual.is_empty() {
2807                if let Err(err) = fs::write(&expected_path, &actual) {
2808                    self.fatal(&format!("failed to write {stream} to `{expected_path}`: {err}"));
2809                }
2810                writeln!(
2811                    self.stdout,
2812                    "Blessing the {stream} of `{test_name}` as `{expected_path}`",
2813                    test_name = self.testpaths.file
2814                );
2815            }
2816        }
2817
2818        writeln!(self.stdout, "\nThe actual {stream} differed from the expected {stream}");
2819
2820        if self.config.bless { CompareOutcome::Blessed } else { CompareOutcome::Differed }
2821    }
2822
2823    /// Returns whether to show the full stderr/stdout.
2824    fn show_diff(
2825        &self,
2826        stream: &str,
2827        expected_path: &Utf8Path,
2828        actual_path: &Utf8Path,
2829        expected: &str,
2830        actual: &str,
2831        actual_unnormalized: &str,
2832    ) {
2833        writeln!(self.stderr, "diff of {stream}:\n");
2834        if let Some(diff_command) = self.config.diff_command.as_deref() {
2835            let mut args = diff_command.split_whitespace();
2836            let name = args.next().unwrap();
2837            match Command::new(name).args(args).args([expected_path, actual_path]).output() {
2838                Err(err) => {
2839                    self.fatal(&format!(
2840                        "failed to call custom diff command `{diff_command}`: {err}"
2841                    ));
2842                }
2843                Ok(output) => {
2844                    let output = String::from_utf8_lossy(&output.stdout);
2845                    write!(self.stderr, "{output}");
2846                }
2847            }
2848        } else {
2849            write!(self.stderr, "{}", write_diff(expected, actual, 3));
2850        }
2851
2852        // NOTE: argument order is important, we need `actual` to be on the left so the line number match up when we compare it to `actual_unnormalized` below.
2853        let diff_results = make_diff(actual, expected, 0);
2854
2855        let (mut mismatches_normalized, mut mismatch_line_nos) = (String::new(), vec![]);
2856        for hunk in diff_results {
2857            let mut line_no = hunk.line_number;
2858            for line in hunk.lines {
2859                // NOTE: `Expected` is actually correct here, the argument order is reversed so our line numbers match up
2860                if let DiffLine::Expected(normalized) = line {
2861                    mismatches_normalized += &normalized;
2862                    mismatches_normalized += "\n";
2863                    mismatch_line_nos.push(line_no);
2864                    line_no += 1;
2865                }
2866            }
2867        }
2868        let mut mismatches_unnormalized = String::new();
2869        let diff_normalized = make_diff(actual, actual_unnormalized, 0);
2870        for hunk in diff_normalized {
2871            if mismatch_line_nos.contains(&hunk.line_number) {
2872                for line in hunk.lines {
2873                    if let DiffLine::Resulting(unnormalized) = line {
2874                        mismatches_unnormalized += &unnormalized;
2875                        mismatches_unnormalized += "\n";
2876                    }
2877                }
2878            }
2879        }
2880
2881        let normalized_diff = make_diff(&mismatches_normalized, &mismatches_unnormalized, 0);
2882        // HACK: instead of checking if each hunk is empty, this only checks if the whole input is empty. we should be smarter about this so we don't treat added or removed output as normalized.
2883        if !normalized_diff.is_empty()
2884            && !mismatches_unnormalized.is_empty()
2885            && !mismatches_normalized.is_empty()
2886        {
2887            writeln!(
2888                self.stderr,
2889                "Note: some mismatched output was normalized before being compared"
2890            );
2891            // FIXME: respect diff_command
2892            write!(
2893                self.stderr,
2894                "{}",
2895                write_diff(&mismatches_unnormalized, &mismatches_normalized, 0)
2896            );
2897        }
2898    }
2899
2900    fn check_and_prune_duplicate_outputs(
2901        &self,
2902        proc_res: &ProcRes,
2903        modes: &[CompareMode],
2904        require_same_modes: &[CompareMode],
2905    ) {
2906        for kind in UI_EXTENSIONS {
2907            let canon_comparison_path =
2908                expected_output_path(&self.testpaths, self.revision, &None, kind);
2909
2910            let canon = match self.load_expected_output_from_path(&canon_comparison_path) {
2911                Ok(canon) => canon,
2912                _ => continue,
2913            };
2914            let bless = self.config.bless;
2915            let check_and_prune_duplicate_outputs = |mode: &CompareMode, require_same: bool| {
2916                let examined_path =
2917                    expected_output_path(&self.testpaths, self.revision, &Some(mode.clone()), kind);
2918
2919                // If there is no output, there is nothing to do
2920                let examined_content = match self.load_expected_output_from_path(&examined_path) {
2921                    Ok(content) => content,
2922                    _ => return,
2923                };
2924
2925                let is_duplicate = canon == examined_content;
2926
2927                match (bless, require_same, is_duplicate) {
2928                    // If we're blessing and the output is the same, then delete the file.
2929                    (true, _, true) => {
2930                        self.delete_file(&examined_path);
2931                    }
2932                    // If we want them to be the same, but they are different, then error.
2933                    // We do this whether we bless or not
2934                    (_, true, false) => {
2935                        self.fatal_proc_rec(
2936                            &format!("`{}` should not have different output from base test!", kind),
2937                            proc_res,
2938                        );
2939                    }
2940                    _ => {}
2941                }
2942            };
2943            for mode in modes {
2944                check_and_prune_duplicate_outputs(mode, false);
2945            }
2946            for mode in require_same_modes {
2947                check_and_prune_duplicate_outputs(mode, true);
2948            }
2949        }
2950    }
2951
2952    fn create_stamp(&self) {
2953        let stamp_file_path = stamp_file_path(&self.config, self.testpaths, self.revision);
2954        fs::write(&stamp_file_path, compute_stamp_hash(&self.config)).unwrap();
2955    }
2956
2957    fn init_incremental_test(&self) {
2958        // (See `run_incremental_test` for an overview of how incremental tests work.)
2959
2960        // Before any of the revisions have executed, create the
2961        // incremental workproduct directory.  Delete any old
2962        // incremental work products that may be there from prior
2963        // runs.
2964        let incremental_dir = self.props.incremental_dir.as_ref().unwrap();
2965        if incremental_dir.exists() {
2966            // Canonicalizing the path will convert it to the //?/ format
2967            // on Windows, which enables paths longer than 260 character
2968            let canonicalized = incremental_dir.canonicalize().unwrap();
2969            fs::remove_dir_all(canonicalized).unwrap();
2970        }
2971        fs::create_dir_all(&incremental_dir).unwrap();
2972
2973        if self.config.verbose {
2974            writeln!(self.stdout, "init_incremental_test: incremental_dir={incremental_dir}");
2975        }
2976    }
2977}
2978
2979struct ProcArgs {
2980    prog: OsString,
2981    args: Vec<OsString>,
2982}
2983
2984#[derive(Debug)]
2985pub(crate) struct ProcRes {
2986    status: ExitStatus,
2987    stdout: String,
2988    stderr: String,
2989    truncated: Truncated,
2990    cmdline: String,
2991}
2992
2993impl ProcRes {
2994    #[must_use]
2995    pub(crate) fn format_info(&self) -> String {
2996        fn render(name: &str, contents: &str) -> String {
2997            let contents = json::extract_rendered(contents);
2998            let contents = contents.trim_end();
2999            if contents.is_empty() {
3000                format!("{name}: none")
3001            } else {
3002                format!(
3003                    "\
3004                     --- {name} -------------------------------\n\
3005                     {contents}\n\
3006                     ------------------------------------------",
3007                )
3008            }
3009        }
3010
3011        format!(
3012            "status: {}\ncommand: {}\n{}\n{}\n",
3013            self.status,
3014            self.cmdline,
3015            render("stdout", &self.stdout),
3016            render("stderr", &self.stderr),
3017        )
3018    }
3019}
3020
3021#[derive(Debug)]
3022enum TargetLocation {
3023    ThisFile(Utf8PathBuf),
3024    ThisDirectory(Utf8PathBuf),
3025}
3026
3027enum AllowUnused {
3028    Yes,
3029    No,
3030}
3031
3032enum LinkToAux {
3033    Yes,
3034    No,
3035}
3036
3037#[derive(Debug, PartialEq)]
3038enum AuxType {
3039    Bin,
3040    Lib,
3041    Dylib,
3042    ProcMacro,
3043}
3044
3045/// Outcome of comparing a stream to a blessed file,
3046/// e.g. `.stderr` and `.fixed`.
3047#[derive(Copy, Clone, Debug, PartialEq, Eq)]
3048enum CompareOutcome {
3049    /// Expected and actual outputs are the same
3050    Same,
3051    /// Outputs differed but were blessed
3052    Blessed,
3053    /// Outputs differed and an error should be emitted
3054    Differed,
3055}
3056
3057#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3058enum DocKind {
3059    Html,
3060    Json,
3061}
3062
3063impl CompareOutcome {
3064    fn should_error(&self) -> bool {
3065        matches!(self, CompareOutcome::Differed)
3066    }
3067}