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
Comment thread
Kobzol marked this conversation as resolved.
Comment thread
jieyouxu marked this conversation as resolved.
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 @@ -4613,3 +4615,90 @@ impl CommandLineStep 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 CommandLineStep 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
source: src/bootstrap/src/core/builder/cli_paths/tests.rs
expression: test std-semver-check
---
[Test] test::StdSemverCheck
targets: [aarch64-unknown-linux-gnu]
- Set({test::std-semver-check})
1 change: 1 addition & 0 deletions src/bootstrap/src/core/builder/cli_paths/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ declare_tests!(
(x_test_librustdoc_rustdoc_html, "test librustdoc rustdoc-html"),
(x_test_rustdoc, "test rustdoc"),
(x_test_rustdoc_html, "test rustdoc-html"),
(x_test_semver_check, "test std-semver-check"),
(x_test_skip_coverage, "test --skip=coverage"),
(x_test_skip_coverage_map, "test --skip=coverage-map"),
(x_test_skip_coverage_run, "test --skip=coverage-run"),
Expand Down
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 @@ -974,6 +974,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 @@ -785,11 +809,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 @@ -834,8 +858,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 @@ -848,8 +871,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 @@ -872,7 +894,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 @@ -916,7 +938,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 @@ -979,6 +1001,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