Add new tool to check HTML:
* Make html-checker run by default on rust compiler docs as well * Ensure html-checker is run on CI * Lazify tidy binary presence check
This commit is contained in:
parent
451e98e7b0
commit
83a2bc31b9
9 changed files with 166 additions and 4 deletions
|
@ -1578,6 +1578,13 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "html-checker"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "html5ever"
|
name = "html5ever"
|
||||||
version = "0.25.1"
|
version = "0.25.1"
|
||||||
|
|
|
@ -34,6 +34,7 @@ members = [
|
||||||
"src/tools/unicode-table-generator",
|
"src/tools/unicode-table-generator",
|
||||||
"src/tools/expand-yaml-anchors",
|
"src/tools/expand-yaml-anchors",
|
||||||
"src/tools/jsondocck",
|
"src/tools/jsondocck",
|
||||||
|
"src/tools/html-checker",
|
||||||
]
|
]
|
||||||
|
|
||||||
exclude = [
|
exclude = [
|
||||||
|
|
|
@ -450,6 +450,7 @@ impl<'a> Builder<'a> {
|
||||||
test::RustdocTheme,
|
test::RustdocTheme,
|
||||||
test::RustdocUi,
|
test::RustdocUi,
|
||||||
test::RustdocJson,
|
test::RustdocJson,
|
||||||
|
test::HtmlCheck,
|
||||||
// Run bootstrap close to the end as it's unlikely to fail
|
// Run bootstrap close to the end as it's unlikely to fail
|
||||||
test::Bootstrap,
|
test::Bootstrap,
|
||||||
// Run run-make last, since these won't pass without make on Windows
|
// Run run-make last, since these won't pass without make on Windows
|
||||||
|
|
|
@ -501,8 +501,8 @@ impl Step for Std {
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct Rustc {
|
pub struct Rustc {
|
||||||
stage: u32,
|
pub stage: u32,
|
||||||
target: TargetSelection,
|
pub target: TargetSelection,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Step for Rustc {
|
impl Step for Rustc {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use std::fmt;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
use build_helper::{self, output, t};
|
use build_helper::{self, output, t};
|
||||||
|
|
||||||
|
@ -161,6 +161,49 @@ You can skip linkcheck with --exclude src/tools/linkchecker"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_if_tidy_is_installed() -> bool {
|
||||||
|
Command::new("tidy")
|
||||||
|
.arg("--version")
|
||||||
|
.stdout(Stdio::null())
|
||||||
|
.status()
|
||||||
|
.map_or(false, |status| status.success())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct HtmlCheck {
|
||||||
|
target: TargetSelection,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Step for HtmlCheck {
|
||||||
|
type Output = ();
|
||||||
|
const DEFAULT: bool = true;
|
||||||
|
const ONLY_HOSTS: bool = true;
|
||||||
|
|
||||||
|
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||||
|
let run = run.path("src/tools/html-checker");
|
||||||
|
run.lazy_default_condition(Box::new(check_if_tidy_is_installed))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_run(run: RunConfig<'_>) {
|
||||||
|
run.builder.ensure(HtmlCheck { target: run.target });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(self, builder: &Builder<'_>) {
|
||||||
|
if !check_if_tidy_is_installed() {
|
||||||
|
eprintln!("not running HTML-check tool because `tidy` is missing");
|
||||||
|
eprintln!(
|
||||||
|
"Note that `tidy` is not the in-tree `src/tools/tidy` but needs to be installed"
|
||||||
|
);
|
||||||
|
panic!("Cannot run html-check tests");
|
||||||
|
}
|
||||||
|
// Ensure that a few different kinds of documentation are available.
|
||||||
|
builder.default_doc(&[]);
|
||||||
|
builder.ensure(crate::doc::Rustc { target: self.target, stage: builder.top_stage });
|
||||||
|
|
||||||
|
try_run(builder, builder.tool_cmd(Tool::HtmlChecker).arg(builder.doc_out(self.target)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Cargotest {
|
pub struct Cargotest {
|
||||||
stage: u32,
|
stage: u32,
|
||||||
|
|
|
@ -376,6 +376,7 @@ bootstrap_tool!(
|
||||||
ExpandYamlAnchors, "src/tools/expand-yaml-anchors", "expand-yaml-anchors";
|
ExpandYamlAnchors, "src/tools/expand-yaml-anchors", "expand-yaml-anchors";
|
||||||
LintDocs, "src/tools/lint-docs", "lint-docs";
|
LintDocs, "src/tools/lint-docs", "lint-docs";
|
||||||
JsonDocCk, "src/tools/jsondocck", "jsondocck";
|
JsonDocCk, "src/tools/jsondocck", "jsondocck";
|
||||||
|
HtmlChecker, "src/tools/html-checker", "html-checker";
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
|
||||||
|
|
|
@ -12,7 +12,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
cmake \
|
cmake \
|
||||||
libssl-dev \
|
libssl-dev \
|
||||||
sudo \
|
sudo \
|
||||||
xz-utils
|
xz-utils \
|
||||||
|
tidy
|
||||||
|
|
||||||
# Install dependencies for chromium browser
|
# Install dependencies for chromium browser
|
||||||
RUN apt-get install -y \
|
RUN apt-get install -y \
|
||||||
|
|
12
src/tools/html-checker/Cargo.toml
Normal file
12
src/tools/html-checker/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "html-checker"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Guillaume Gomez <guillaume1.gomez@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "html-checker"
|
||||||
|
path = "main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
walkdir = "2"
|
96
src/tools/html-checker/main.rs
Normal file
96
src/tools/html-checker/main.rs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::{Command, Output};
|
||||||
|
|
||||||
|
fn check_html_file(file: &Path) -> usize {
|
||||||
|
let to_mute = &[
|
||||||
|
// "disabled" on <link> or "autocomplete" on <select> emit this warning
|
||||||
|
"PROPRIETARY_ATTRIBUTE",
|
||||||
|
// It complains when multiple in the same page link to the same anchor for some reason...
|
||||||
|
"ANCHOR_NOT_UNIQUE",
|
||||||
|
// If a <span> contains only HTML elements and no text, it complains about it.
|
||||||
|
"TRIM_EMPTY_ELEMENT",
|
||||||
|
// FIXME: the three next warnings are about <pre> elements which are not supposed to
|
||||||
|
// contain HTML. The solution here would be to replace them with a <div> with
|
||||||
|
// ""
|
||||||
|
"MISSING_ENDTAG_BEFORE",
|
||||||
|
"INSERTING_TAG",
|
||||||
|
"DISCARDING_UNEXPECTED",
|
||||||
|
// FIXME: mdbook repeats the name attribute on <input>. When the fix is merged upstream,
|
||||||
|
// this warning can be used again.
|
||||||
|
"REPEATED_ATTRIBUTE",
|
||||||
|
// FIXME: mdbook uses "align" attribute on <td>, which is not allowed.
|
||||||
|
"MISMATCHED_ATTRIBUTE_WARN",
|
||||||
|
// FIXME: mdbook doesn't add "alt" attribute on images.
|
||||||
|
"MISSING_ATTRIBUTE",
|
||||||
|
// FIXME: mdbook doesn't escape `&` (in "&String" for example).
|
||||||
|
"UNKNOWN_ENTITY",
|
||||||
|
// Compiler docs have some inlined <style> in the markdown.
|
||||||
|
"MOVED_STYLE_TO_HEAD",
|
||||||
|
];
|
||||||
|
let to_mute_s = to_mute.join(",");
|
||||||
|
let mut command = Command::new("tidy");
|
||||||
|
command
|
||||||
|
.arg("-errors")
|
||||||
|
.arg("-quiet")
|
||||||
|
.arg("--mute-id") // this option is useful in case we want to mute more warnings
|
||||||
|
.arg("yes")
|
||||||
|
.arg("--mute")
|
||||||
|
.arg(&to_mute_s)
|
||||||
|
.arg(file);
|
||||||
|
|
||||||
|
let Output { status, stderr, .. } = command.output().expect("failed to run tidy command");
|
||||||
|
if status.success() {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
let stderr = String::from_utf8(stderr).expect("String::from_utf8 failed...");
|
||||||
|
if stderr.is_empty() && status.code() != Some(2) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
"=> Errors for `{}` (error code: {}) <=",
|
||||||
|
file.display(),
|
||||||
|
status.code().unwrap_or(-1)
|
||||||
|
);
|
||||||
|
eprintln!("{}", stderr);
|
||||||
|
stderr.lines().count()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the number of files read and the number of errors.
|
||||||
|
fn find_all_html_files(dir: &Path) -> (usize, usize) {
|
||||||
|
let mut files_read = 0;
|
||||||
|
let mut errors = 0;
|
||||||
|
|
||||||
|
for entry in walkdir::WalkDir::new(dir) {
|
||||||
|
let entry = entry.expect("failed to read file");
|
||||||
|
if !entry.file_type().is_file() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let entry = entry.path();
|
||||||
|
if entry.extension().and_then(|s| s.to_str()) == Some("html") {
|
||||||
|
errors += check_html_file(&entry);
|
||||||
|
files_read += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(files_read, errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), String> {
|
||||||
|
let args = env::args().collect::<Vec<_>>();
|
||||||
|
if args.len() != 2 {
|
||||||
|
return Err(format!("Usage: {} <doc folder>", args[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Running HTML checker...");
|
||||||
|
|
||||||
|
let (files_read, errors) = find_all_html_files(&Path::new(&args[1]));
|
||||||
|
println!("Done! Read {} files...", files_read);
|
||||||
|
if errors > 0 {
|
||||||
|
Err(format!("HTML check failed: {} errors", errors))
|
||||||
|
} else {
|
||||||
|
println!("No error found!");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue