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