Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions bootstrap.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,15 @@
#
#rust.parallel-frontend-threads = 1

# Baseline commit SHA for comparing semver breakages in the Rust standard library.
# The in-tree stdlib API will be evaluated for semver breakages against this commit.
# Used for the `./x test std-semver-check` command.
# If unset, the first upstream parent commit will be used.
#
# The SHA must point to a merge commit merged into the mainline rust-lang/rust `main` branch,
# because bootstrap will attempt to download the JSON docs data for this commit from its CI.
#rust.stdlib-semver-baseline = "<commit-sha>"

# =============================================================================
# Distribution options
#
Expand Down
84 changes: 68 additions & 16 deletions src/bootstrap/src/core/build_steps/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4617,7 +4617,7 @@ impl CommandLineStep for RemoteTestClientTests {
}

fn check_if_cargo_semver_checks_is_installed(builder: &Builder<'_>) -> bool {
command("cargo")
command(&builder.initial_cargo)
.allow_failure()
.arg("semver-checks")
.arg("--version")
Expand All @@ -4630,7 +4630,13 @@ fn check_if_cargo_semver_checks_is_installed(builder: &Builder<'_>) -> bool {
/// Run cargo-semver-checks on the standard library and compare its API
/// versus a previous baseline, using rustdoc JSON data.
///
/// The baseline commit can be configured using `rust.stdlib-semver-baseline`.
/// If unset, the first upstream parent commit will be used.
///
/// Fails if a semver-breaking change is detected.
///
/// If you want to allow a breaking change in a given PR, or if cargo-semver-checks has a false
/// positive, modify the `src/bootstrap/stdlib-semver-check-stamp` file.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StdSemverCheck {
build_compiler: Compiler,
Expand All @@ -4651,19 +4657,22 @@ impl CommandLineStep for StdSemverCheck {
panic!("cargo-semver-checks was not found, please install it");
}

let baseline_commit = match get_closest_upstream_commit(
Some(&run.builder.config.src),
&run.builder.config.git_config(),
run.builder.config.ci_env,
) {
Ok(Some(commit)) => commit,
Ok(None) => {
panic!("No baseline parent commit found for std-semver-check");
}
Err(error) => {
panic!("Cannot get baseline parent commit for std-semver-check: {error:?}");
}
};
let baseline_commit =
run.builder.config.stdlib_semver_baseline.clone().unwrap_or_else(|| {
match get_closest_upstream_commit(
Some(&run.builder.config.src),
&run.builder.config.git_config(),
run.builder.config.ci_env,
) {
Ok(Some(commit)) => commit,
Ok(None) => {
panic!("No baseline parent commit found for std-semver-check");
}
Err(error) => {
panic!("Cannot get baseline parent commit for std-semver-check: {error:?}");
}
}
});

run.builder.ensure(Self {
build_compiler: run.builder.compiler_for_std(run.builder.top_stage),
Expand All @@ -4673,6 +4682,15 @@ impl CommandLineStep for StdSemverCheck {
}

fn run(self, builder: &Builder<'_>) {
const STDLIB_SEMVER_CHECK_STAMP_PATH: &str = "src/bootstrap/stdlib-semver-check-stamp";

if builder.config.ci_env.is_running_in_ci()
&& builder.config.has_changes_from_upstream(&[STDLIB_SEMVER_CHECK_STAMP_PATH])
{
builder.info(&format!("Skipping stdlib semver check, because {STDLIB_SEMVER_CHECK_STAMP_PATH} was modified."));
return;
}

let Some(docs_dir) = builder.config.download_std_json_docs(self.target, &self.commit)
else {
return;
Expand All @@ -4687,7 +4705,7 @@ impl CommandLineStep for StdSemverCheck {

for library in ["core", "alloc", "std"] {
println!("Checking semver compatibility of {library}");
let mut cmd = command("cargo");
let mut cmd = command(&builder.initial_cargo);
cmd.arg("semver-checks")
.arg("-Z")
.arg("unstable-options")
Expand All @@ -4698,7 +4716,41 @@ impl CommandLineStep for StdSemverCheck {
.arg(directory.join(format!("{library}.json")))
.arg("--baseline-rustdoc")
.arg(baseline_dir.join(format!("{library}.json")));
cmd.run(builder);

// We use run_capture to get the exit status
let res = cmd.allow_failure().run_capture(builder);
match res.status() {
Some(status) if status.success() => {
println!("{}\n{}", res.stdout(), res.stderr());
}
// 101 marks that csc was unable to parse the JSON data, but it did not fail with a
// semver breakage.
Some(status) if status.code() == Some(101) => {
eprintln!(
"cargo-semver-checks was unable to process {library} (this is not a fatal error)\n{}\n{}",
res.stderr(),
res.stdout()
);
}
Comment thread
jieyouxu marked this conversation as resolved.
// 100 marks semver breakage
Some(status) if status.code() == Some(100) => {
let error = format!(
"cargo-semver-checks found semver breakage in {library}\n{}\n{}",
res.stderr(),
res.stdout()
);
if builder.fail_fast {
eprintln!("{error}",);
exit!(1);
} else {
builder.config.exec_ctx().add_to_delay_failure(error);
}
}
_ => {
eprintln!("cargo-semver-checks failed.\n{}\n{}", res.stderr(), res.stdout());
exit!(1);
}
}
}
}
}
4 changes: 4 additions & 0 deletions src/bootstrap/src/core/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ pub struct Config {
pub rustdoc_pgo: PgoConfig,
pub cargo_pgo: PgoConfig,

pub stdlib_semver_baseline: Option<String>,

pub llvm_libunwind_default: Option<LlvmLibunwind>,
pub enable_bolt_settings: bool,

Expand Down Expand Up @@ -610,6 +612,7 @@ impl Config {
std_features: rust_std_features,
break_on_ice: rust_break_on_ice,
rustflags: rust_rustflags,
stdlib_semver_baseline: rust_stdlib_semver_baseline,
} = toml_rust.unwrap_or_default();

let Llvm {
Expand Down Expand Up @@ -1594,6 +1597,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to
.or(rust_rustc_debug_assertions)
.unwrap_or(rust_debug == Some(true)),
stderr_is_tty: std::io::stderr().is_terminal(),
stdlib_semver_baseline: rust_stdlib_semver_baseline,
stdout_is_tty: std::io::stdout().is_terminal(),
submodules: build_submodules,
sysconfdir: install_sysconfdir.map(PathBuf::from),
Expand Down
2 changes: 2 additions & 0 deletions src/bootstrap/src/core/config/toml/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ define_config! {
std_features: Option<BTreeSet<String>> = "std-features",
break_on_ice: Option<bool> = "break-on-ice",
parallel_frontend_threads: Option<u32> = "parallel-frontend-threads",
stdlib_semver_baseline: Option<String> = "stdlib-semver-baseline",
}
}

Expand Down Expand Up @@ -384,6 +385,7 @@ pub fn check_incompatible_options_for_ci_rustc(
parallel_frontend_threads: _,
bootstrap_override_lld: _,
rustflags: _,
stdlib_semver_baseline: _,
} = ci_rust_config;

// There are two kinds of checks for CI rustc incompatible options:
Expand Down
5 changes: 5 additions & 0 deletions src/bootstrap/stdlib-semver-check-stamp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Change this file to explicitly acknowledge making a breaking change to the Rust standard library.
If this file is modified in the same PR as the breaking change, then CI will not fail due to the
breaking change being detected by cargo-semver-checks.

Last change is for: https://github.com/rust-lang/rust/pull/160253
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
FROM ubuntu:26.04

ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
make \
ninja-build \
file \
curl \
ca-certificates \
python3 \
git \
cmake \
sudo \
gdb \
libssl-dev \
pkg-config \
xz-utils \
mingw-w64 \
zlib1g-dev \
libzstd-dev \
&& rm -rf /var/lib/apt/lists/*

COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh

ENV RUST_CONFIGURE_ARGS="--build=x86_64-unknown-linux-gnu"
ENV RUSTC_WRAPPER=/usr/local/bin/sccache

COPY /scripts/std-semver-check.sh /tmp/std-semver-check.sh
ENV SCRIPT="bash /tmp/std-semver-check.sh"
24 changes: 24 additions & 0 deletions src/ci/docker/scripts/std-semver-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash

set -euo pipefail

BUILD_DIR=$(realpath ./build/x86_64-unknown-linux-gnu)

# Install the latest version of cargo-semver-checks, so that once the JSON doc format changes,
# we will eventually get a csc version that supports it
# Speed up compilation by reducing optimizations settings a bit
RUSTC="${BUILD_DIR}"/stage0/bin/rustc \
CARGO_PROFILE_RELEASE_LTO=false \
CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16 \
"${BUILD_DIR}"/stage0/bin/cargo install cargo-semver-checks --locked

# Provide path to cargo-semver-checks
export PATH=${PATH}:/cargo/bin

# Explicitly compute the baseline commit (the first git parent, which is the latest upstream main
# commit), so that it is shown in the commit log and so that the command can be easily reproduced
# locally.
PARENT=$(git rev-parse HEAD^1)

# Run the test
python3 ../x.py test std-semver-check --set rust.stdlib-semver-baseline=${PARENT}
4 changes: 4 additions & 0 deletions src/ci/github-actions/jobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,10 @@ auto:
- name: x86_64-gnu-miri
<<: *job-linux-4c

- name: x86_64-gnu-stdlib-semver-check
doc_url: https://rustc-dev-guide.rust-lang.org/tests/stdlib-semver-check.html
<<: *job-linux-4c
Comment thread
jieyouxu marked this conversation as resolved.
Comment thread
jieyouxu marked this conversation as resolved.
Comment thread
jieyouxu marked this conversation as resolved.

- name: optional-x86_64-gnu-autodiff
continue_on_error: true
doc_url: https://rustc-dev-guide.rust-lang.org/tests/autodiff-ci-job.html
Expand Down
1 change: 1 addition & 0 deletions src/doc/rustc-dev-guide/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
- [Performance testing](./tests/perf.md)
- [Autodiff CI job](./tests/autodiff-ci-job.md)
- [Pre-stabilization CI job for the next solver and polonius alpha](./tests/x86_64-gnu-next-trait-solver-polonius-ci-job.md)
- [Standard library semver breakage test](./tests/stdlib-semver-check.md)
- [Misc info](./tests/misc.md)
- [Debugging the compiler](./compiler-debugging.md)
- [Using the tracing/logging instrumentation](./tracing.md)
Expand Down
21 changes: 21 additions & 0 deletions src/doc/rustc-dev-guide/src/tests/stdlib-semver-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Standard library semantic versioning breakage check

The `x86_64-gnu-stdlib-semver-check` job runs the [`cargo-semver-checks`][csc] (c-s-c) tool on the standard library (`core`, `alloc` and `std`) in order to find potential unintended semantic versioning (semver) breakages. It does so by analyzing the rustdoc JSON output (from the `rust-docs-json` component) of the parent merge commit, and the current commit being merged. When it runs, one of five things can happen:

1. Everything proceeds correctly, c-s-c does not find any breakage.
2. The rustdoc JSON version was bumped recently, and c-s-c cannot handle it yet. This case will result in the test ending with a success, and printing a warning that c-s-c needs to be updated. Once c-s-c releases a version that supports the new JSON format, it should go to 1. again.
- We currently install the latest released version of c-s-c in this job, so its version does not need to be updated manually in the `rust-lang/rust` repository.
3. c-s-c detects a breakage, but it is a false positive. In this case, please report the false positive to [this][semver-topic] Zulip channel, and [bump the stamp file](#bumping-the-stdlib-semver-stamp-file).
4. c-s-c detects a real breakage, and it helped you find unintended beakage. Yay! In this case, please consider reporting the success to [this][semver-topic] Zulip channel.
5. c-s-c detects a real breakage, but you want to land it anyway (maybe it is an edge case that was FCPed). In that case, [bump the stamp file](#bumping-the-stdlib-semver-stamp-file).

## Bumping the stdlib semver stamp file

If you want to let CI pass on a PR where c-s-c detects breakage (whether it is real or not), you have to modify the `src/bootstrap/stdlib-semver-check-stamp` file. Please update the PR number in which you modify this file at the bottom of the file. This will ensure that the test will stay green, regardless of what c-s-c detects.

## Running the check manually

You can manually run the semver check locally using `./x test std-semver-check --set rust.stdlib-semver-baseline=${PARENT}`, where `PARENT` is a commit SHA against which you want to compare the in-tree stdlib. If you do not specify it, bootstrap will select the latest upstream commit that it finds in your local git history.

[semver-topic]: https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/Breakages.20detected.20by.20cargo-semver-checks/with/615570111
[csc]: https://github.com/obi1kenobi/cargo-semver-checks
Loading