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