Skip to main content

compiletest/
cli.rs

1//! Isolates the APIs used by `bin/main.rs`, to help minimize the surface area
2//! of public exports from the compiletest library crate.
3
4use std::env;
5use std::io::IsTerminal;
6use std::sync::{Arc, OnceLock};
7
8use camino::{Utf8Path, Utf8PathBuf};
9use clap::Parser;
10
11use crate::common::{
12    CodegenBackend, CompareMode, Config, Debugger, ForcePassMode, TestMode, TestSuite,
13};
14use crate::edition::Edition;
15use crate::{debuggers, directives, early_config_check, run_tests};
16
17pub fn main() {
18    tracing_subscriber::fmt::init();
19
20    // colored checks stdout by default, but for some reason only stderr is a terminal.
21    // compiletest *does* print many things to stdout, but it doesn't really matter.
22    if std::io::stderr().is_terminal()
23        && matches!(std::env::var("NO_COLOR").as_deref(), Err(_) | Ok("0"))
24    {
25        colored::control::set_override(true);
26    }
27
28    let config = Arc::new(parse_config(env::args().collect()));
29
30    early_config_check(&config);
31
32    run_tests(config);
33}
34
35/// Compiletest command-line arguments.
36#[derive(clap::Parser)]
37struct Args {
38    // Required options
39    /// Path to host shared libraries.
40    #[arg(long)]
41    compile_lib_path: Utf8PathBuf,
42    /// Path to target shared libraries.
43    #[arg(long)]
44    run_lib_path: Utf8PathBuf,
45    /// Path to rustc to use for compiling.
46    #[arg(long)]
47    rustc_path: Utf8PathBuf,
48    /// Path to python to use for doc tests.
49    #[arg(long)]
50    python: String,
51    /// Directory containing sources.
52    #[arg(long)]
53    src_root: Utf8PathBuf,
54    /// Directory containing test suite sources.
55    #[arg(long)]
56    src_test_suite_root: Utf8PathBuf,
57    /// Path to root build directory.
58    #[arg(long)]
59    build_root: Utf8PathBuf,
60    /// Path to test suite specific build directory.
61    #[arg(long)]
62    build_test_suite_root: Utf8PathBuf,
63    /// Directory containing the compiler sysroot.
64    #[arg(long)]
65    sysroot_base: Utf8PathBuf,
66    /// Stage number under test.
67    #[arg(long)]
68    stage: u32,
69    /// The target-stage identifier.
70    #[arg(long)]
71    stage_id: String,
72    /// Which sort of compile tests to run.
73    #[arg(long)]
74    mode: TestMode,
75    /// Which suite of compile tests to run.
76    #[arg(long)]
77    suite: TestSuite,
78    /// Path to a C compiler.
79    #[arg(long)]
80    cc: String,
81    /// Path to a C++ compiler.
82    #[arg(long)]
83    cxx: String,
84    /// Flags for the C compiler.
85    #[arg(long, allow_hyphen_values = true)]
86    cflags: String,
87    /// Flags for the CXX compiler.
88    #[arg(long, allow_hyphen_values = true)]
89    cxxflags: String,
90    /// List of LLVM components built in.
91    #[arg(long)]
92    llvm_components: String,
93    /// Current Rust channel.
94    #[arg(long)]
95    channel: String,
96    /// Name of the git branch for nightly.
97    #[arg(long)]
98    nightly_branch: String,
99    /// Email address used for finding merge commits.
100    #[arg(long)]
101    git_merge_commit_email: String,
102    /// Path to minicore aux library.
103    #[arg(long)]
104    minicore_path: Utf8PathBuf,
105    /// Number of parallel jobs bootstrap was configured with.
106    #[arg(long)]
107    jobs: u32,
108    /// The host to build for.
109    #[arg(long)]
110    host: String,
111    /// The target to build for.
112    #[arg(long)]
113    target: String,
114
115    // Optional options
116    /// Path to cargo to use for compiling.
117    #[arg(long)]
118    cargo_path: Option<Utf8PathBuf>,
119    /// Path to rustc to use for compiling run-make recipes.
120    #[arg(long)]
121    stage0_rustc_path: Option<Utf8PathBuf>,
122    /// Path to rustc to use for querying target information.
123    #[arg(long)]
124    query_rustc_path: Option<Utf8PathBuf>,
125    /// Path to rustdoc to use for compiling.
126    #[arg(long)]
127    rustdoc_path: Option<Utf8PathBuf>,
128    /// Path to coverage-dump to use in tests.
129    #[arg(long)]
130    coverage_dump_path: Option<Utf8PathBuf>,
131    /// Path to jsondocck to use for doc tests.
132    #[arg(long)]
133    jsondocck_path: Option<Utf8PathBuf>,
134    /// Path to jsondoclint to use for doc tests.
135    #[arg(long)]
136    jsondoclint_path: Option<Utf8PathBuf>,
137    /// Path to Clang executable.
138    #[arg(long)]
139    run_clang_based_tests_with: Option<Utf8PathBuf>,
140    /// Path to LLVM's FileCheck binary.
141    #[arg(long)]
142    llvm_filecheck: Option<Utf8PathBuf>,
143    /// Path to LLVM's bin directory.
144    #[arg(long)]
145    llvm_bin_dir: Option<Utf8PathBuf>,
146    /// The name of nodejs.
147    #[arg(long)]
148    nodejs: Option<Utf8PathBuf>,
149    /// The name of npm.
150    #[arg(long)]
151    npm: Option<Utf8PathBuf>,
152    /// Path to the remote test client.
153    #[arg(long)]
154    remote_test_client: Option<Utf8PathBuf>,
155    /// Path to CDB to use for CDB debuginfo tests.
156    #[arg(long)]
157    cdb: Option<Utf8PathBuf>,
158    /// Path to GDB to use for GDB debuginfo tests.
159    #[arg(long)]
160    gdb: Option<Utf8PathBuf>,
161    /// Path to LLDB to use for LLDB debuginfo tests.
162    #[arg(long)]
163    lldb: Option<Utf8PathBuf>,
164    /// The version of LLDB used.
165    #[arg(long)]
166    lldb_version: Option<String>,
167    /// The version of LLVM used.
168    #[arg(long)]
169    llvm_version: Option<String>,
170    /// Android NDK standalone path.
171    #[arg(long)]
172    android_cross_path: Option<Utf8PathBuf>,
173    /// Path to the android debugger.
174    #[arg(long)]
175    adb_path: Option<Utf8PathBuf>,
176    /// Path to tests for the android debugger.
177    #[arg(long)]
178    adb_test_dir: Option<Utf8PathBuf>,
179    /// Path to an archiver.
180    #[arg(long, default_value = "ar")]
181    ar: String,
182    /// Path to a linker for the target.
183    #[arg(long)]
184    target_linker: Option<String>,
185    /// Path to a linker for the host.
186    #[arg(long)]
187    host_linker: Option<String>,
188    /// Force {check,build,run}-pass tests to this mode.
189    #[arg(long)]
190    pass: Option<ForcePassMode>,
191    /// Whether to execute run-* tests.
192    #[arg(long)]
193    run: Option<String>,
194    /// Supervisor program to run tests under (eg. emulator, valgrind).
195    #[arg(long)]
196    runner: Option<String>,
197    /// Mode describing what file the actual ui output will be compared to.
198    #[arg(long)]
199    compare_mode: Option<CompareMode>,
200    /// Default Rust edition.
201    #[arg(long)]
202    edition: Option<Edition>,
203    /// Only test a specific debugger in debuginfo tests.
204    #[arg(long)]
205    debugger: Option<String>,
206    /// The codegen backend currently used.
207    #[arg(long)]
208    default_codegen_backend: Option<CodegenBackend>,
209    /// The codegen backend to use instead of the default one.
210    #[arg(long)]
211    override_codegen_backend: Option<String>,
212    /// Custom diff tool to use for displaying compiletest tests.
213    #[arg(long)]
214    compiletest_diff_tool: Option<String>,
215    /// Number of parallel threads to use for the frontend when building test artifacts
216    #[arg(long)]
217    parallel_frontend_threads: Option<u32>,
218    /// Number of times to execute each test.
219    #[arg(long)]
220    iteration_count: Option<u32>,
221
222    // Flags
223    /// Overwrite stderr/stdout files instead of complaining about a mismatch.
224    #[arg(long)]
225    bless: bool,
226    /// Stop as soon as possible after any test fails.
227    #[arg(long)]
228    fail_fast: bool,
229    /// Run tests marked as ignored.
230    #[arg(long)]
231    ignored: bool,
232    /// Run tests that require enzyme.
233    #[arg(long)]
234    has_enzyme: bool,
235    /// Run tests that require offload.
236    #[arg(long)]
237    has_offload: bool,
238    /// Whether rustc was built with debug assertions.
239    #[arg(long)]
240    with_rustc_debug_assertions: bool,
241    /// Whether std was built with debug assertions.
242    #[arg(long)]
243    with_std_debug_assertions: bool,
244    /// Whether std was built with remapping.
245    #[arg(long)]
246    with_std_remap_debuginfo: bool,
247    /// Filters match exactly.
248    #[arg(long)]
249    exact: bool,
250    /// Set this when rustc/stdlib were compiled with randomized layouts.
251    #[arg(long)]
252    rust_randomized_layout: bool,
253    /// Run tests with optimizations enabled.
254    #[arg(long)]
255    optimize_tests: bool,
256    /// Run tests verbosely, showing all output.
257    #[arg(long)]
258    verbose: bool,
259    /// Show verbose subprocess output for successful run-make tests.
260    #[arg(long)]
261    verbose_run_make_subprocess_output: bool,
262    /// Is LLVM the system LLVM.
263    #[arg(long)]
264    system_llvm: bool,
265    /// Rerun tests even if the inputs are unchanged.
266    #[arg(long)]
267    force_rerun: bool,
268    /// Only run tests that result been modified.
269    #[arg(long)]
270    only_modified: bool,
271    // Backcompat option
272    #[arg(long, hide = true)]
273    nocapture: bool,
274    /// Don't capture stdout/stderr of tests.
275    #[arg(long)]
276    no_capture: bool,
277    /// Is the profiler runtime enabled for this target.
278    #[arg(long)]
279    profiler_runtime: bool,
280    /// Run tests which rely on commit version being compiled into the binaries.
281    #[arg(long)]
282    git_hash: bool,
283    /// Enable this to generate a Rustfix coverage file.
284    #[arg(long)]
285    rustfix_coverage: bool,
286    /// Ignore `//@ ignore-backends` directives.
287    #[arg(long)]
288    bypass_ignore_backends: bool,
289
290    // These values can be entered multiple times, for example:
291    // --skip foo --skip bar
292    /// Skip tests matching SUBSTRING.
293    #[arg(long)]
294    skip: Vec<String>,
295    /// Flags to pass to rustc for host.
296    #[arg(long, allow_hyphen_values = true)]
297    host_rustcflags: Vec<String>,
298    /// Flags to pass to rustc for target.
299    #[arg(long, allow_hyphen_values = true)]
300    target_rustcflags: Vec<String>,
301
302    // Positional arguments
303    /// Test name filters.
304    /// All leftover arguments will be stored in this list.
305    filters: Vec<String>,
306}
307
308pub(crate) fn parse_config(args: Vec<String>) -> Config {
309    let args = Args::parse_from(args);
310
311    fn make_absolute(path: Utf8PathBuf) -> Utf8PathBuf {
312        if path.is_relative() {
313            Utf8PathBuf::try_from(env::current_dir().unwrap()).unwrap().join(path)
314        } else {
315            path
316        }
317    }
318
319    if args.nocapture {
320        panic!("`--nocapture` is deprecated; please use `--no-capture`");
321    }
322
323    let adb_device_status = args.target.contains("android") && args.adb_test_dir.is_some();
324
325    // FIXME: `cdb_version` is *derived* from cdb, but it's *not* technically a config!
326    let cdb_version = args.cdb.as_deref().and_then(debuggers::query_cdb_version);
327    // FIXME: `gdb_version` is *derived* from gdb, but it's *not* technically a config!
328    let gdb_version = args.gdb.as_deref().and_then(debuggers::query_gdb_version);
329    // FIXME: `lldb_version` is *derived* from lldb, but it's *not* technically a config!
330    let lldb_version = args.lldb_version.as_deref().and_then(debuggers::extract_lldb_version);
331    // FIXME: this is very questionable, we really should be obtaining LLVM version info from
332    // `bootstrap`, and not trying to be figuring out that in `compiletest` by running the
333    // `FileCheck` binary.
334    let llvm_version =
335        args.llvm_version.as_deref().map(directives::extract_llvm_version).or_else(|| {
336            directives::extract_llvm_version_from_binary(args.llvm_filecheck.as_ref()?.as_str())
337        });
338
339    let default_codegen_backend = args.default_codegen_backend.unwrap_or(CodegenBackend::Llvm);
340
341    let mode = args.mode;
342    let filters = if mode == TestMode::RunMake {
343        args.filters
344            .iter()
345            .map(|f| {
346                // Here `f` is relative to `./tests/run-make`. So if you run
347                //
348                //   ./x test tests/run-make/crate-loading
349                //
350                //  then `f` is "crate-loading".
351                let path = Utf8Path::new(f);
352                let mut iter = path.iter().skip(1);
353
354                if iter.next().is_some_and(|s| s == "rmake.rs") && iter.next().is_none() {
355                    // Strip the "rmake.rs" suffix. For example, if `f` is
356                    // "crate-loading/rmake.rs" then this gives us "crate-loading".
357                    path.parent().unwrap().to_string()
358                } else {
359                    f.to_string()
360                }
361            })
362            .collect::<Vec<_>>()
363    } else {
364        // Note that the filters are relative to the root dir of the different test
365        // suites. For example, with:
366        //
367        //   ./x test tests/ui/lint/unused
368        //
369        // the filter is "lint/unused".
370        args.filters.clone()
371    };
372
373    let compare_mode = args.compare_mode;
374
375    let src_root = args.src_root;
376    let src_test_suite_root = args.src_test_suite_root;
377    assert!(
378        src_test_suite_root.starts_with(&src_root),
379        "`src-root` must be a parent of `src-test-suite-root`: `src-root`=`{}`, `src-test-suite-root` = `{}`",
380        src_root,
381        src_test_suite_root
382    );
383
384    let build_root = args.build_root;
385    let build_test_suite_root = args.build_test_suite_root;
386    assert!(build_test_suite_root.starts_with(&build_root));
387
388    let parallel_frontend_threads =
389        args.parallel_frontend_threads.unwrap_or(Config::DEFAULT_PARALLEL_FRONTEND_THREADS);
390    let iteration_count = args.iteration_count.unwrap_or(Config::DEFAULT_ITERATION_COUNT);
391    assert!(iteration_count > 0, "`--iteration-count` must be a positive integer");
392
393    Config {
394        bless: args.bless,
395        fail_fast: args.fail_fast || env::var_os("RUSTC_TEST_FAIL_FAST").is_some(),
396
397        host_compile_lib_path: make_absolute(args.compile_lib_path),
398        target_run_lib_path: make_absolute(args.run_lib_path),
399        rustc_path: args.rustc_path,
400        cargo_path: args.cargo_path,
401        stage0_rustc_path: args.stage0_rustc_path,
402        query_rustc_path: args.query_rustc_path,
403        rustdoc_path: args.rustdoc_path,
404        coverage_dump_path: args.coverage_dump_path,
405        python: args.python,
406        jsondocck_path: args.jsondocck_path,
407        jsondoclint_path: args.jsondoclint_path,
408        run_clang_based_tests_with: args.run_clang_based_tests_with,
409        llvm_filecheck: args.llvm_filecheck,
410        llvm_bin_dir: args.llvm_bin_dir,
411
412        src_root,
413        src_test_suite_root,
414
415        build_root,
416        build_test_suite_root,
417
418        sysroot_base: args.sysroot_base,
419
420        stage: args.stage,
421        stage_id: args.stage_id,
422
423        mode,
424        suite: args.suite,
425        debugger: args.debugger.map(|debugger| {
426            debugger
427                .parse::<Debugger>()
428                .unwrap_or_else(|_| panic!("unknown `--debugger` option `{debugger}` given"))
429        }),
430        run_ignored: args.ignored,
431        with_rustc_debug_assertions: args.with_rustc_debug_assertions,
432        with_std_debug_assertions: args.with_std_debug_assertions,
433        with_std_remap_debuginfo: args.with_std_remap_debuginfo,
434        filters,
435        skip: args.skip,
436        filter_exact: args.exact,
437        force_pass_mode: args.pass,
438        // FIXME: this run scheme is... confusing.
439        run: args.run.and_then(|mode| match mode.as_str() {
440            "auto" => None,
441            "always" => Some(true),
442            "never" => Some(false),
443            _ => panic!("unknown `--run` option `{}` given", mode),
444        }),
445        runner: args.runner,
446        host_rustcflags: args.host_rustcflags,
447        target_rustcflags: args.target_rustcflags,
448        optimize_tests: args.optimize_tests,
449        rust_randomized_layout: args.rust_randomized_layout,
450        target: args.target,
451        host: args.host,
452        cdb: args.cdb,
453        cdb_version,
454        gdb: args.gdb,
455        gdb_version,
456        lldb: args.lldb,
457        lldb_version,
458        llvm_version,
459        system_llvm: args.system_llvm,
460        android_cross_path: args.android_cross_path,
461        adb_path: args.adb_path,
462        adb_test_dir: args.adb_test_dir,
463        adb_device_status,
464        verbose: args.verbose,
465        verbose_run_make_subprocess_output: args.verbose_run_make_subprocess_output,
466        only_modified: args.only_modified,
467        remote_test_client: args.remote_test_client,
468        compare_mode,
469        rustfix_coverage: args.rustfix_coverage,
470        has_enzyme: args.has_enzyme,
471        has_offload: args.has_offload,
472        channel: args.channel,
473        git_hash: args.git_hash,
474        edition: args.edition,
475
476        cc: args.cc,
477        cxx: args.cxx,
478        cflags: args.cflags,
479        cxxflags: args.cxxflags,
480        ar: args.ar,
481        target_linker: args.target_linker,
482        host_linker: args.host_linker,
483        llvm_components: args.llvm_components,
484        nodejs: args.nodejs,
485
486        force_rerun: args.force_rerun,
487
488        target_cfgs: OnceLock::new(),
489        builtin_cfg_names: OnceLock::new(),
490        supported_crate_types: OnceLock::new(),
491
492        capture: !args.no_capture,
493
494        nightly_branch: args.nightly_branch,
495        git_merge_commit_email: args.git_merge_commit_email,
496
497        profiler_runtime: args.profiler_runtime,
498
499        diff_command: args.compiletest_diff_tool,
500
501        minicore_path: args.minicore_path,
502
503        default_codegen_backend,
504        override_codegen_backend: args.override_codegen_backend,
505        bypass_ignore_backends: args.bypass_ignore_backends,
506
507        jobs: args.jobs,
508
509        parallel_frontend_threads,
510        iteration_count,
511    }
512}