Skip to content
Open
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
89 changes: 89 additions & 0 deletions src/bootstrap/src/core/build_steps/test.rs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remark: I think this will merge conflict with #159638, merge after that PR

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: could we add a step resolution snapshot test for ./x test std-semver-check?

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, fs, iter};

use build_helper::git::get_closest_upstream_commit;

use crate::core::build_steps::compile::{ArtifactKeepMode, Std, run_cargo};
use crate::core::build_steps::doc::{DocumentationFormat, prepare_doc_compiler};
use crate::core::build_steps::gcc::{Gcc, GccTargetPair, add_cg_gcc_cargo_flags};
Expand Down Expand Up @@ -4622,3 +4624,90 @@ impl Step for RemoteTestClientTests {
);
}
}

fn check_if_cargo_semver_checks_is_installed(builder: &Builder<'_>) -> bool {
command("cargo")
.allow_failure()
.arg("semver-checks")
.arg("--version")
// Cache the output to avoid running this command more than once (per builder).
.cached()
.run_capture_stdout(builder)
.is_success()
}

/// Run cargo-semver-checks on the standard library and compare its API
/// versus a previous baseline, using rustdoc JSON data.
///
/// Fails if a semver-breaking change is detected.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StdSemverCheck {
build_compiler: Compiler,
target: TargetSelection,
/// The baseline commit that we are comparing the local stdlib API against.
commit: String,
}

impl Step for StdSemverCheck {
type Output = ();

fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.alias("std-semver-check")
}

fn make_run(run: RunConfig<'_>) {
if !check_if_cargo_semver_checks_is_installed(run.builder) {
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:?}");
}
};

run.builder.ensure(Self {
build_compiler: run.builder.compiler_for_std(run.builder.top_stage),
target: run.target,
commit: baseline_commit,
});
}

fn run(self, builder: &Builder<'_>) {
let Some(docs_dir) = builder.config.download_std_json_docs(self.target, &self.commit)
else {
return;
};

let directory = builder.ensure(crate::core::build_steps::doc::Std::from_build_compiler(
self.build_compiler,
self.target,
DocumentationFormat::Json,
));
let baseline_dir = docs_dir.join("share").join("doc").join("rust").join("json");

for library in ["core", "alloc", "std"] {
println!("Checking semver compatibility of {library}");
let mut cmd = command("cargo");
cmd.arg("semver-checks")
.arg("-Z")
.arg("unstable-options")
.arg("--stability-aware")
.arg("--release-type")
.arg("minor")
.arg("--current-rustdoc")
.arg(directory.join(format!("{library}.json")))
.arg("--baseline-rustdoc")
.arg(baseline_dir.join(format!("{library}.json")));
cmd.run(builder);
}
}
}
1 change: 1 addition & 0 deletions src/bootstrap/src/core/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,7 @@ impl<'a> Builder<'a> {
test::RunMake,
test::RunMakeCargo,
test::BuildStd,
test::StdSemverCheck,
test::IntrinsicTest,
),
Kind::Miri => describe!(test::Crate),
Expand Down
39 changes: 31 additions & 8 deletions src/bootstrap/src/core/download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,30 @@ impl Config {
);
}

pub(crate) fn download_std_json_docs(
&self,
target: TargetSelection,
commit: &str,
) -> Option<PathBuf> {
if self.dry_run() {
return None;
}

self.do_if_verbose(|| println!("using downloaded std json docs from CI (commit {commit})"));

let version = self.artifact_version_part(commit);
download_component(
DownloadContext::from(self),
&self.out,
DownloadSource::CI,
format!("rust-docs-json-{version}-{target}.tar.xz"),
"rust-docs-json-preview",
// When using DownloadSource::CI, the key is assumed to end with -llvm-assertions
&format!("{commit}-{}", self.llvm_assertions),
"ci-docs-json",
)
}

fn download_toolchain(
&self,
version: &str,
Expand Down Expand Up @@ -784,11 +808,11 @@ fn download_component<'a>(
prefix: &str,
key: &str,
destination: &str,
) {
) -> Option<PathBuf> {
let dwn_ctx = dwn_ctx.as_ref();

if dwn_ctx.exec_ctx.dry_run() {
return;
return None;
}

let cache_dst =
Expand Down Expand Up @@ -833,8 +857,7 @@ fn download_component<'a>(
let sha256 = dwn_ctx.stage0_metadata.checksums_sha256.get(&url).expect(&error);
if tarball.exists() {
if verify(dwn_ctx.exec_ctx, &tarball, sha256) {
unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix);
return;
return Some(unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix));
} else {
dwn_ctx.exec_ctx.do_if_verbose(|| {
println!(
Expand All @@ -847,8 +870,7 @@ fn download_component<'a>(
}
Some(sha256)
} else if tarball.exists() {
unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix);
return;
return Some(unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix));
} else {
None
};
Expand All @@ -871,7 +893,7 @@ download-rustc = false
panic!("failed to verify {}", tarball.display());
}

unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix);
Some(unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix))
}

pub(crate) fn verify(exec_ctx: &ExecutionContext, path: &Path, expected: &str) -> bool {
Expand Down Expand Up @@ -915,7 +937,7 @@ pub(crate) fn verify(exec_ctx: &ExecutionContext, path: &Path, expected: &str) -
verified
}

fn unpack(exec_ctx: &ExecutionContext, tarball: &Path, dst: &Path, pattern: &str) {
fn unpack(exec_ctx: &ExecutionContext, tarball: &Path, dst: &Path, pattern: &str) -> PathBuf {
eprintln!("extracting {} to {}", tarball.display(), dst.display());
if !dst.exists() {
t!(fs::create_dir_all(dst));
Expand Down Expand Up @@ -978,6 +1000,7 @@ fn unpack(exec_ctx: &ExecutionContext, tarball: &Path, dst: &Path, pattern: &str
if dst_dir.exists() {
t!(fs::remove_dir_all(&dst_dir), format!("failed to remove {}", dst_dir.display()));
}
dst.to_path_buf()
}

fn download_file<'a>(
Expand Down
Loading