Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 492a9d4

Browse files
add relnotes-api-list tool
1 parent 8e5b8dc commit 492a9d4

File tree

17 files changed

+1386
-3
lines changed

17 files changed

+1386
-3
lines changed

‎Cargo.lock

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3185,6 +3185,17 @@ version = "0.8.5"
31853185
source = "registry+https://github.com/rust-lang/crates.io-index"
31863186
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
31873187

3188+
[[package]]
3189+
name = "relnotes-api-list"
3190+
version = "0.1.0"
3191+
dependencies = [
3192+
"anyhow",
3193+
"rustdoc-json-types",
3194+
"serde",
3195+
"serde_json",
3196+
"tempfile",
3197+
]
3198+
31883199
[[package]]
31893200
name = "remote-test-client"
31903201
version = "0.1.0"

‎Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ members = [
3030
"src/tools/miri/cargo-miri",
3131
"src/tools/miropt-test-tools",
3232
"src/tools/opt-dist",
33+
"src/tools/relnotes-api-list",
3334
"src/tools/remote-test-client",
3435
"src/tools/remote-test-server",
3536
"src/tools/replace-version-placeholder",

‎src/bootstrap/src/core/build_steps/dist.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2636,3 +2636,62 @@ impl Step for Gcc {
26362636
tarball.generate()
26372637
}
26382638
}
2639+
2640+
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
2641+
pub struct RelnotesApiList {
2642+
pub host: TargetSelection,
2643+
}
2644+
2645+
impl Step for RelnotesApiList {
2646+
type Output = ();
2647+
const DEFAULT: bool = true;
2648+
2649+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2650+
let default = run.builder.config.docs;
2651+
run.alias("relnotes-api-list").default_condition(default)
2652+
}
2653+
2654+
fn make_run(run: RunConfig<'_>) {
2655+
run.builder.ensure(RelnotesApiList { host: run.target });
2656+
}
2657+
2658+
fn run(self, builder: &Builder<'_>) -> Self::Output {
2659+
let host = self.host;
2660+
let dest = builder.out.join("dist").join(format!("relnotes-api-list-{host}.json"));
2661+
builder.create_dir(dest.parent().unwrap());
2662+
2663+
// The HTML documentation for the standard library is needed to check all links generated by
2664+
// the tool are not broken.
2665+
builder.ensure(crate::core::build_steps::doc::Std::new(
2666+
builder.top_stage,
2667+
host,
2668+
DocumentationFormat::Html,
2669+
));
2670+
2671+
if std::env::var_os("EMILY_SKIP_DOC").is_none() {
2672+
// TODO: remove the condition
2673+
builder.ensure(
2674+
crate::core::build_steps::doc::Std::new(
2675+
builder.top_stage,
2676+
host,
2677+
DocumentationFormat::Json,
2678+
)
2679+
// Crates containing symbols exported by any std crate:
2680+
.add_extra_crate("rustc-literal-escaper")
2681+
.add_extra_crate("std_detect"),
2682+
);
2683+
}
2684+
2685+
let linkchecker = builder.tool_exe(Tool::Linkchecker);
2686+
2687+
builder.info("Generating the API list for the release notes");
2688+
builder
2689+
.tool_cmd(Tool::RelnotesApiList)
2690+
.arg(builder.json_doc_out(host))
2691+
.arg(&dest)
2692+
.env("LINKCHECKER_PATH", linkchecker)
2693+
.env("STD_HTML_DOCS", builder.doc_out(self.host))
2694+
.run(builder);
2695+
builder.info(&format!("API list for the release notes available at {}", dest.display()));
2696+
}
2697+
}

‎src/bootstrap/src/core/build_steps/doc.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,11 +560,17 @@ pub struct Std {
560560
pub target: TargetSelection,
561561
pub format: DocumentationFormat,
562562
crates: Vec<String>,
563+
extra_crates: Vec<String>,
563564
}
564565

565566
impl Std {
566567
pub(crate) fn new(stage: u32, target: TargetSelection, format: DocumentationFormat) -> Self {
567-
Std { stage, target, format, crates: vec![] }
568+
Std { stage, target, format, crates: vec![], extra_crates: vec![] }
569+
}
570+
571+
pub(crate) fn add_extra_crate(mut self, krate: &str) -> Self {
572+
self.extra_crates.push(krate.to_string());
573+
self
568574
}
569575
}
570576

@@ -592,6 +598,7 @@ impl Step for Std {
592598
DocumentationFormat::Html
593599
},
594600
crates,
601+
extra_crates: vec![],
595602
});
596603
}
597604

@@ -602,7 +609,7 @@ impl Step for Std {
602609
fn run(self, builder: &Builder<'_>) {
603610
let stage = self.stage;
604611
let target = self.target;
605-
let crates = if self.crates.is_empty() {
612+
let mutcrates = if self.crates.is_empty() {
606613
builder
607614
.in_tree_crates("sysroot", Some(target))
608615
.iter()
@@ -611,6 +618,7 @@ impl Step for Std {
611618
} else {
612619
self.crates
613620
};
621+
crates.extend(self.extra_crates.iter().cloned());
614622

615623
let out = match self.format {
616624
DocumentationFormat::Html => builder.doc_out(target),

‎src/bootstrap/src/core/build_steps/tool.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ bootstrap_tool!(
526526
FeaturesStatusDump, "src/tools/features-status-dump", "features-status-dump";
527527
OptimizedDist, "src/tools/opt-dist", "opt-dist", submodules = &["src/tools/rustc-perf"];
528528
RunMakeSupport, "src/tools/run-make-support", "run_make_support", artifact_kind = ToolArtifactKind::Library;
529+
RelnotesApiList, "src/tools/relnotes-api-list", "relnotes-api-list";
529530
);
530531

531532
/// These are the submodules that are required for rustbook to work due to

‎src/bootstrap/src/core/builder/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,7 @@ impl<'a> Builder<'a> {
980980
tool::CoverageDump,
981981
tool::LlvmBitcodeLinker,
982982
tool::RustcPerf,
983+
tool::RelnotesApiList,
983984
),
984985
Kind::Clippy => describe!(
985986
clippy::Std,
@@ -1156,7 +1157,8 @@ impl<'a> Builder<'a> {
11561157
dist::PlainSourceTarball,
11571158
dist::BuildManifest,
11581159
dist::ReproducibleArtifacts,
1159-
dist::Gcc
1160+
dist::Gcc,
1161+
dist::RelnotesApiList,
11601162
),
11611163
Kind::Install => describe!(
11621164
install::Docs,

‎src/tools/relnotes-api-list/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "relnotes-api-list"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
anyhow = "1"
8+
serde = { version = "1", features = ["derive"] }
9+
serde_json = "1"
10+
rustdoc-json-types = { path = "../../rustdoc-json-types" }
11+
tempfile = "3.20.0"

‎src/tools/relnotes-api-list/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# API list generator for the release notes
2+
3+
Rust's [release notes] include a "Stabilized APIs" section for each
4+
release, listing all APIs that became stable each release. This tool supports
5+
the creation of that section by generating a JSON file containing a concise
6+
representation of the standard library API. The [relnotes tool] will then
7+
compare the JSON files of two releases to generate the section.
8+
9+
The tool is executed by CI and produces the `relnotes-api-list-$target.json`
10+
dist artifact. You can also run the tool locally with:
11+
12+
```
13+
./x dist relnotes-api-list
14+
```
15+
16+
[release notes]: https://doc.rust-lang.org/stable/releases.html
17+
[relnotes tool]: https://github.com/rust-lang/relnotes
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//! We generate a lot of URLs in the resulting JSON, and we need all those URLs to be correct. While
2+
//! some URLs are fairly trivial to generate, others are quite tricky (especially `impl` blocks).
3+
//!
4+
//! To ensure we always generate good URLs, we prepare a temporary HTML file containing `<a>` tags for
5+
//! every itme we collected, and we run it through linkchecker. If this fails, it means the code
6+
//! generating URLs has a bug.
7+
8+
use crate::schema::{Schema, SchemaItem};
9+
use anyhow::{Error, anyhow, bail};
10+
use std::fs::File;
11+
use std::io::Write;
12+
use std::path::PathBuf;
13+
use std::process::Command;
14+
use tempfile::tempdir;
15+
16+
pub(crate) fn check_urls(schema: &Schema) -> Result<(), Error> {
17+
let mut urls = Vec::new();
18+
collect_urls(&mut urls, &schema.items);
19+
20+
let html_dir = tempdir()?;
21+
22+
let mut file = File::create(html_dir.path().join("urls.html"))?;
23+
file.write_all(render_html(&urls).as_bytes())?;
24+
25+
eprintln!("checking that all generated URLs are valid...");
26+
let result = Command::new(require_env("LINKCHECKER_PATH")?)
27+
.arg(html_dir.path())
28+
.arg("--extra-target")
29+
.arg(require_env("STD_HTML_DOCS")?)
30+
.status()?;
31+
32+
if !result.success() {
33+
bail!("some URLs are broken, the relnotes-api-list tool has a bug");
34+
}
35+
36+
dbg!(require_env("STD_HTML_DOCS")?);
37+
38+
Ok(())
39+
}
40+
41+
fn collect_urls<'a>(result: &mut Vec<&'a str>, items: &'a [SchemaItem]) {
42+
for item in items {
43+
if let Some(url) = &item.url {
44+
result.push(url);
45+
}
46+
collect_urls(result, &item.children);
47+
}
48+
}
49+
50+
fn render_html(urls: &[&str]) -> String {
51+
let mut content = "<!DOCTYPE html>\n".to_string();
52+
for url in urls {
53+
content.push_str(&format!("<a href=\"{url}\"></a>\n"));
54+
}
55+
content
56+
}
57+
58+
fn require_env(name: &str) -> Result<PathBuf, Error> {
59+
match std::env::var_os(name) {
60+
Some(value) => Ok(value.into()),
61+
None => Err(anyhow!("missing environment variable {name}")),
62+
}
63+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /