diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 91eac6613db37..e14a725d7d773 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1754,6 +1754,21 @@ fn parse_jobs_all( }, }; + // If reproducible compilation is enabled we lower frontend parallelism to 1, + // hopefully we'll be able to do better in the future. + let reproducible = match parse_reproducible(matches.opt_str("reproducible").as_deref()) { + Ok(reproducible) => reproducible, + Err(()) => { + let msg = "`--reproducible`: expected of of `none`, `binaries`, `diagnostics`, \ + or `binaries,diagnostics`"; + early_dcx.early_fatal(msg); + } + }; + let frontend = match frontend { + Some(n) if !reproducible || n.get() == 1 => Some(n), // respect `sync` + _ => None, + }; + Jobs { frontend, backend, linker } } @@ -1788,6 +1803,16 @@ fn parse_jobs_one( (n > 1).then_some(NonZero::new(usize::from(n)).unwrap()) } +fn parse_reproducible(s: Option<&str>) -> Result { + match s { + None | Some("none") => Ok(false), + Some("binaries" | "diagnostics" | "binaries,diagnostics" | "diagnostics,binaries") => { + Ok(true) + } + _ => Err(()), + } +} + pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg { // First disallow some configuration given on the command line cfg::disallow_cfgs(sess, &user_cfg); @@ -2128,6 +2153,14 @@ pub fn rustc_optgroups() -> Vec { "Limit on the number of parallel jobs used by linker", "", ), + opt( + Unstable, + Opt, + "", + "reproducible", + "Try making some of the compiler outputs reproducible, possibly at cost of compilation speed", + "BOOL", + ), ]; options.extend(verbose_only.into_iter().map(|mut opt| { opt.is_verbose_help_only = true; diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 52dc58f65bb8b..643d64f881c83 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -531,6 +531,35 @@ then we cannot limit it. Currently only LLD supports controlling parallelism. + +## `--reproducible`: request reproducible output + +By reproducible or deterministic compilation here we'll understand obtaining identical binaries +or other compiler outputs when rustc is run multiple times on the same source code with the same +options and environment. + +The reproducibility may be needed in a number of scenarios: +- When compiler outputs are compared to some existing snapshots for testing + (e.g. rustc's own UI test suite). +- When compiler outputs are cached between multiple compilations using tools like sccache. + In this case different outputs will break the caching even if they are functionally equivalent. +- Other scenarios, including security-oriented. + +However, in many other scenarios the reproducibility is not an issue, for example: +- Building and running the project to check whether the tests pass, on CI or locally. +- Compiling the project with cargo check to see and fix the reported errors. + +This option accepts the following values - `none`, `binaries`, `diagnostics`, +`binaries,diagnostics`, or `diagnostics,binaries`. + +The default is `binaries,diagnostics`, unless rustc frontend parallelism is enabled with +`--jobs` or `--jobs-frontend`. + +Currently `--reproducible=value` for any value except `none` will just reduce the frontend +parallelism to 1, but we hope to do something better in the future. For example keeping at least +some parallelism in `--reproducible=binaries,diagnostics` cases, or enabling more optimizations for +`--reproducible=none`, even in single-threaded mode. + ## `@path`: load command-line flags from a path diff --git a/tests/ui/compile-flags/jobs/jobs-fail-conflict.h.stderr b/tests/ui/compile-flags/jobs/jobs-fail-conflict.h.stderr new file mode 100644 index 0000000000000..ec34e63a3d734 --- /dev/null +++ b/tests/ui/compile-flags/jobs/jobs-fail-conflict.h.stderr @@ -0,0 +1,2 @@ +error: Option 'reproducible' given more than once + diff --git a/tests/ui/compile-flags/jobs/jobs-fail-conflict.rs b/tests/ui/compile-flags/jobs/jobs-fail-conflict.rs index 0e37d0a41f32f..d989c774350b8 100644 --- a/tests/ui/compile-flags/jobs/jobs-fail-conflict.rs +++ b/tests/ui/compile-flags/jobs/jobs-fail-conflict.rs @@ -1,4 +1,4 @@ -//@ revisions: a b c d e f g +//@ revisions: a b c d e f g h //@ ignore-parallel-frontend option conflicts //@ compile-flags: -Z unstable-options @@ -21,4 +21,7 @@ //@[g] compile-flags: --jobs 1 --jobs 2 //[g]~? RAW Option 'jobs' given more than once +//@[h] compile-flags: --reproducible true --reproducible false +//[h]~? RAW Option 'reproducible' given more than once + fn main() {} diff --git a/tests/ui/compile-flags/jobs/jobs-fail-gate.g.stderr b/tests/ui/compile-flags/jobs/jobs-fail-gate.g.stderr new file mode 100644 index 0000000000000..e5531f54eb312 --- /dev/null +++ b/tests/ui/compile-flags/jobs/jobs-fail-gate.g.stderr @@ -0,0 +1,2 @@ +error: the `-Z unstable-options` flag must also be passed to enable the flag `reproducible` + diff --git a/tests/ui/compile-flags/jobs/jobs-fail-gate.rs b/tests/ui/compile-flags/jobs/jobs-fail-gate.rs index 2359165b2f167..7bd7941dbd9d2 100644 --- a/tests/ui/compile-flags/jobs/jobs-fail-gate.rs +++ b/tests/ui/compile-flags/jobs/jobs-fail-gate.rs @@ -1,4 +1,4 @@ -//@ revisions: a b c d e f +//@ revisions: a b c d e f g //@[a] compile-flags: -j 2 //@[b] compile-flags: --jobs 2 @@ -14,4 +14,7 @@ //@[f] compile-flags: --jobs-linker 2 //[f]~? RAW the `-Z unstable-options` flag must also be passed to enable the flag `jobs-linker` +//@[g] compile-flags: --reproducible=yes +//[g]~? RAW the `-Z unstable-options` flag must also be passed to enable the flag `reproducible` + fn main() {} diff --git a/tests/ui/compile-flags/jobs/jobs-fail-range.m.stderr b/tests/ui/compile-flags/jobs/jobs-fail-range.m.stderr new file mode 100644 index 0000000000000..fa0e67a945139 --- /dev/null +++ b/tests/ui/compile-flags/jobs/jobs-fail-range.m.stderr @@ -0,0 +1,5 @@ +error: Argument to option 'reproducible' missing + Usage: + --reproducible BOOL Try making some of the compiler outputs reproducible, + possibly at cost of compilation speed + diff --git a/tests/ui/compile-flags/jobs/jobs-fail-range.n.stderr b/tests/ui/compile-flags/jobs/jobs-fail-range.n.stderr new file mode 100644 index 0000000000000..3402732c344d2 --- /dev/null +++ b/tests/ui/compile-flags/jobs/jobs-fail-range.n.stderr @@ -0,0 +1,2 @@ +error: `--reproducible`: expected of of `none`, `binaries`, `diagnostics`, or `binaries,diagnostics` + diff --git a/tests/ui/compile-flags/jobs/jobs-fail-range.rs b/tests/ui/compile-flags/jobs/jobs-fail-range.rs index 139509a68c66c..c3a7b7b062677 100644 --- a/tests/ui/compile-flags/jobs/jobs-fail-range.rs +++ b/tests/ui/compile-flags/jobs/jobs-fail-range.rs @@ -1,4 +1,4 @@ -//@ revisions: a b c d e f g h i j k l +//@ revisions: a b c d e f g h i j k l m n //@ compile-flags: -Z unstable-options //@[a] compile-flags: -j 256 @@ -23,9 +23,13 @@ //@[j] compile-flags: --jobs //@[k] compile-flags: -Zthreads //@[l] compile-flags: -Zno-parallel-backend +//@[m] compile-flags: --reproducible +//@[n] compile-flags: --reproducible nonsense //[i]~? RAW Argument to option 'j' missing //[j]~? RAW Argument to option 'jobs' missing //[k]~? ERROR unstable option `threads` requires a string //[l]~? ERROR `-Zno-parallel-backend` is removed, use `--jobs-backend=1` instead +//[m]~? RAW Argument to option 'reproducible' missing +//[n]~? ERROR `--reproducible`: expected of of `none`, `binaries`, `diagnostics`, or `binaries,diagnostics` fn main() {} diff --git a/tests/ui/compile-flags/jobs/jobs-pass.rs b/tests/ui/compile-flags/jobs/jobs-pass.rs index 33a300f300051..73af113b6e80b 100644 --- a/tests/ui/compile-flags/jobs/jobs-pass.rs +++ b/tests/ui/compile-flags/jobs/jobs-pass.rs @@ -8,6 +8,7 @@ //@[c] compile-flags: -j 255 //@[d] compile-flags: --jobs 16 --jobs-frontend 8 --jobs-backend 4 --jobs-linker 2 //@[e] compile-flags: --jobs 16 -Zthreads=8 --jobs-backend 4 -//@[g] compile-flags: -Zthreads=1 -Zthreads=2 +//@[f] compile-flags: -Zthreads=1 -Zthreads=2 +//@[g] compile-flags: --jobs 16 --reproducible=binaries,diagnostics fn main() {}