Skip to content

Commit b5d4343

Browse files
authored
Rollup merge of #86059 - GuillaumeGomez:html-checker2, r=Mark-Simulacrum
Add new tool to check HTML Re-opening of #84480. r? `@Mark-Simulacrum`
2 parents a435b49 + 785b705 commit b5d4343

File tree

10 files changed

+165
-5
lines changed

10 files changed

+165
-5
lines changed

Cargo.lock

+7
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,13 @@ dependencies = [
15781578
"winapi 0.3.9",
15791579
]
15801580

1581+
[[package]]
1582+
name = "html-checker"
1583+
version = "0.1.0"
1584+
dependencies = [
1585+
"walkdir",
1586+
]
1587+
15811588
[[package]]
15821589
name = "html5ever"
15831590
version = "0.25.1"

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ members = [
3434
"src/tools/unicode-table-generator",
3535
"src/tools/expand-yaml-anchors",
3636
"src/tools/jsondocck",
37+
"src/tools/html-checker",
3738
]
3839

3940
exclude = [

src/bootstrap/builder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ impl<'a> Builder<'a> {
450450
test::RustdocTheme,
451451
test::RustdocUi,
452452
test::RustdocJson,
453+
test::HtmlCheck,
453454
// Run bootstrap close to the end as it's unlikely to fail
454455
test::Bootstrap,
455456
// Run run-make last, since these won't pass without make on Windows

src/bootstrap/doc.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -501,8 +501,8 @@ impl Step for Std {
501501

502502
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
503503
pub struct Rustc {
504-
stage: u32,
505-
target: TargetSelection,
504+
pub stage: u32,
505+
pub target: TargetSelection,
506506
}
507507

508508
impl Step for Rustc {

src/bootstrap/test.rs

+44-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::fmt;
99
use std::fs;
1010
use std::iter;
1111
use std::path::{Path, PathBuf};
12-
use std::process::Command;
12+
use std::process::{Command, Stdio};
1313

1414
use build_helper::{self, output, t};
1515

@@ -161,6 +161,49 @@ You can skip linkcheck with --exclude src/tools/linkchecker"
161161
}
162162
}
163163

164+
fn check_if_tidy_is_installed() -> bool {
165+
Command::new("tidy")
166+
.arg("--version")
167+
.stdout(Stdio::null())
168+
.status()
169+
.map_or(false, |status| status.success())
170+
}
171+
172+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
173+
pub struct HtmlCheck {
174+
target: TargetSelection,
175+
}
176+
177+
impl Step for HtmlCheck {
178+
type Output = ();
179+
const DEFAULT: bool = true;
180+
const ONLY_HOSTS: bool = true;
181+
182+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
183+
let run = run.path("src/tools/html-checker");
184+
run.lazy_default_condition(Box::new(check_if_tidy_is_installed))
185+
}
186+
187+
fn make_run(run: RunConfig<'_>) {
188+
run.builder.ensure(HtmlCheck { target: run.target });
189+
}
190+
191+
fn run(self, builder: &Builder<'_>) {
192+
if !check_if_tidy_is_installed() {
193+
eprintln!("not running HTML-check tool because `tidy` is missing");
194+
eprintln!(
195+
"Note that `tidy` is not the in-tree `src/tools/tidy` but needs to be installed"
196+
);
197+
panic!("Cannot run html-check tests");
198+
}
199+
// Ensure that a few different kinds of documentation are available.
200+
builder.default_doc(&[]);
201+
builder.ensure(crate::doc::Rustc { target: self.target, stage: builder.top_stage });
202+
203+
try_run(builder, builder.tool_cmd(Tool::HtmlChecker).arg(builder.doc_out(self.target)));
204+
}
205+
}
206+
164207
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
165208
pub struct Cargotest {
166209
stage: u32,

src/bootstrap/tool.rs

+1
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ bootstrap_tool!(
376376
ExpandYamlAnchors, "src/tools/expand-yaml-anchors", "expand-yaml-anchors";
377377
LintDocs, "src/tools/lint-docs", "lint-docs";
378378
JsonDocCk, "src/tools/jsondocck", "jsondocck";
379+
HtmlChecker, "src/tools/html-checker", "html-checker";
379380
);
380381

381382
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]

src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
1717
libgl1-mesa-dev \
1818
llvm-dev \
1919
libfreetype6-dev \
20-
libexpat1-dev
20+
libexpat1-dev \
21+
tidy
2122

2223
COPY scripts/sccache.sh /scripts/
2324
RUN sh /scripts/sccache.sh

src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
1212
cmake \
1313
libssl-dev \
1414
sudo \
15-
xz-utils
15+
xz-utils \
16+
tidy
1617

1718
# Install dependencies for chromium browser
1819
RUN apt-get install -y \

src/tools/html-checker/Cargo.toml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "html-checker"
3+
version = "0.1.0"
4+
authors = ["Guillaume Gomez <[email protected]>"]
5+
edition = "2018"
6+
7+
[[bin]]
8+
name = "html-checker"
9+
path = "main.rs"
10+
11+
[dependencies]
12+
walkdir = "2"

src/tools/html-checker/main.rs

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use std::env;
2+
use std::path::Path;
3+
use std::process::{Command, Output};
4+
5+
fn check_html_file(file: &Path) -> usize {
6+
let to_mute = &[
7+
// "disabled" on <link> or "autocomplete" on <select> emit this warning
8+
"PROPRIETARY_ATTRIBUTE",
9+
// It complains when multiple in the same page link to the same anchor for some reason...
10+
"ANCHOR_NOT_UNIQUE",
11+
// If a <span> contains only HTML elements and no text, it complains about it.
12+
"TRIM_EMPTY_ELEMENT",
13+
// FIXME: the three next warnings are about <pre> elements which are not supposed to
14+
// contain HTML. The solution here would be to replace them with a <div>
15+
"MISSING_ENDTAG_BEFORE",
16+
"INSERTING_TAG",
17+
"DISCARDING_UNEXPECTED",
18+
];
19+
let to_mute_s = to_mute.join(",");
20+
let mut command = Command::new("tidy");
21+
command
22+
.arg("-errors")
23+
.arg("-quiet")
24+
.arg("--mute-id") // this option is useful in case we want to mute more warnings
25+
.arg("yes")
26+
.arg("--mute")
27+
.arg(&to_mute_s)
28+
.arg(file);
29+
30+
let Output { status, stderr, .. } = command.output().expect("failed to run tidy command");
31+
if status.success() {
32+
0
33+
} else {
34+
let stderr = String::from_utf8(stderr).expect("String::from_utf8 failed...");
35+
if stderr.is_empty() && status.code() != Some(2) {
36+
0
37+
} else {
38+
eprintln!(
39+
"=> Errors for `{}` (error code: {}) <=",
40+
file.display(),
41+
status.code().unwrap_or(-1)
42+
);
43+
eprintln!("{}", stderr);
44+
stderr.lines().count()
45+
}
46+
}
47+
}
48+
49+
const DOCS_TO_CHECK: &[&str] =
50+
&["alloc", "core", "proc_macro", "implementors", "src", "std", "test"];
51+
52+
// Returns the number of files read and the number of errors.
53+
fn find_all_html_files(dir: &Path) -> (usize, usize) {
54+
let mut files_read = 0;
55+
let mut errors = 0;
56+
57+
for entry in walkdir::WalkDir::new(dir).into_iter().filter_entry(|e| {
58+
e.depth() != 1
59+
|| e.file_name()
60+
.to_str()
61+
.map(|s| DOCS_TO_CHECK.into_iter().any(|d| *d == s))
62+
.unwrap_or(false)
63+
}) {
64+
let entry = entry.expect("failed to read file");
65+
if !entry.file_type().is_file() {
66+
continue;
67+
}
68+
let entry = entry.path();
69+
if entry.extension().and_then(|s| s.to_str()) == Some("html") {
70+
errors += check_html_file(&entry);
71+
files_read += 1;
72+
}
73+
}
74+
(files_read, errors)
75+
}
76+
77+
fn main() -> Result<(), String> {
78+
let args = env::args().collect::<Vec<_>>();
79+
if args.len() != 2 {
80+
return Err(format!("Usage: {} <doc folder>", args[0]));
81+
}
82+
83+
println!("Running HTML checker...");
84+
85+
let (files_read, errors) = find_all_html_files(&Path::new(&args[1]));
86+
println!("Done! Read {} files...", files_read);
87+
if errors > 0 {
88+
Err(format!("HTML check failed: {} errors", errors))
89+
} else {
90+
println!("No error found!");
91+
Ok(())
92+
}
93+
}

0 commit comments

Comments
 (0)