xtask: remove assumptions from docs command

This slightly changes the site layout: crate documentation is now flat
rather than being nested under "user" and  "bpf".

- Run `cargo clean --doc` before generating docs to ensure hermiticity.
- Generate header.html into a temporary directory.
- Remove "site" on each run to ensure hermiticity.
- Invoke cargo only once.
- Avoid editing sources.
reviewable/pr652/r1
Tamir Duberstein 1 year ago
parent e2b673eb41
commit b86d42d1b0
No known key found for this signature in database

1
.gitignore vendored

@ -3,5 +3,4 @@ target/
.vscode/ .vscode/
!.vscode/settings.json !.vscode/settings.json
site/ site/
header.html
.idea/ .idea/

@ -5,8 +5,6 @@
# NB: this file gets loaded only if you run cargo from this directory, it's # NB: this file gets loaded only if you run cargo from this directory, it's
# ignored if you run from the workspace root. See # ignored if you run from the workspace root. See
# https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure # https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure
#
# NB-2: cargo xtask docs currently relies on this file existing.
[build] [build]
target = "bpfel-unknown-none" target = "bpfel-unknown-none"

@ -13,3 +13,4 @@ indoc = "2.0"
proc-macro2 = "1" proc-macro2 = "1"
quote = "1" quote = "1"
syn = "2" syn = "2"
tempfile = "3"

@ -1,12 +1,7 @@
use anyhow::{anyhow, Context as _, Result}; use anyhow::{anyhow, Context as _, Result};
use std::{ use cargo_metadata::{Metadata, MetadataCommand};
path::{Path, PathBuf}, use indoc::{indoc, writedoc};
process::Command, use std::{ffi::OsString, fs, io::Write as _, process::Command};
};
use std::{fs, io, io::Write};
use indoc::indoc;
pub fn exec(cmd: &mut Command) -> Result<()> { pub fn exec(cmd: &mut Command) -> Result<()> {
let status = cmd let status = cmd
@ -22,95 +17,92 @@ pub fn exec(cmd: &mut Command) -> Result<()> {
} }
pub fn docs() -> Result<()> { pub fn docs() -> Result<()> {
let current_dir = PathBuf::from("."); const PACKAGE_TO_DESCRIPTION: &[(&str, &str)] =
let header_path = current_dir.join("header.html"); &[("aya", "User-space"), ("aya-bpf", "Kernel-space")];
let mut header = fs::File::create(&header_path).expect("can't create header.html");
header let Metadata {
.write_all(r#"<meta name="robots" content="noindex">"#.as_bytes()) workspace_root,
.expect("can't write header.html contents"); target_directory,
header.flush().expect("couldn't flush contents"); ..
let abs_header_path = fs::canonicalize(&header_path).unwrap(); } = MetadataCommand::new().exec().context("cargo metadata")?;
exec(
Command::new("cargo")
.current_dir(&workspace_root)
.args(["clean", "--doc"]),
)?;
let tmp = tempfile::tempdir().context("create tempdir")?;
let header = tmp.path().join("header.html");
fs::write(&header, r#"<meta name="robots" content="noindex">"#).context("write header.html")?;
let mut rustdocflags = OsString::new();
rustdocflags.push("--cfg docsrs --html-in-header ");
rustdocflags.push(header);
rustdocflags.push(" -D warnings");
build_docs(&current_dir.join("aya"), &abs_header_path)?; exec(
build_docs(&current_dir.join("bpf/aya-bpf"), &abs_header_path)?; Command::new("cargo")
copy_dir_all("./target/doc".as_ref(), "./site/user".as_ref())?; .current_dir(&workspace_root)
copy_dir_all( .env("RUSTDOCFLAGS", rustdocflags)
"./target/bpfel-unknown-none/doc".as_ref(), .args(["+nightly", "doc", "--no-deps", "--all-features"])
"./site/bpf".as_ref(), .args(
PACKAGE_TO_DESCRIPTION
.iter()
.flat_map(|(package, _)| ["--package", package]),
),
)?; )?;
let mut robots = fs::File::create("site/robots.txt").expect("can't create robots.txt"); let site = workspace_root.join("site");
robots match fs::remove_dir_all(&site) {
.write_all( Ok(()) => {}
indoc! {r#" Err(err) => {
if err.kind() != std::io::ErrorKind::NotFound {
return Err(err).context(format!("remove {site:?}"));
}
}
}
let doc = target_directory.join("doc");
fs::rename(&doc, &site).with_context(|| format!("rename {doc:?} to {site:?}"))?;
exec(Command::new("sh").current_dir(&site).args([
"-c",
"grep -FRl crabby.svg | xargs sed -i s/crabby.svg/crabby_dev.svg/g",
]))?;
fs::write(
site.join("robots.txt"),
indoc! {r#"
User-Agent:* User-Agent:*
Disallow: / Disallow: /
"#} "#},
.as_bytes(), )
) .context("can't write robots.txt")?;
.expect("can't write robots.txt");
let mut index = fs::File::create("site/index.html").expect("can't create index.html"); let mut index = fs::File::create(site.join("index.html"))
index .with_context(|| format!("create {site:?}/index.html"))?;
.write_all( writedoc! {&mut index, r#"
indoc! {r#"
<html> <html>
<meta name="robots" content="noindex"> <meta name="robots" content="noindex">
<body> <body>
<ul> <ul>
<li><a href="user/aya/index.html">Aya User-space Development Documentation</a></li> "#}
<li><a href="bpf/aya_bpf/index.html">Aya Kernel-space Development Documentation</a></li> .context("write to index.html")?;
for (package, description) in PACKAGE_TO_DESCRIPTION {
let package = package.replace('-', "_");
writedoc! {&mut index, r#"
<li><a href="{package}/index.html">Aya {description} Development Documentation</a></li>
"#}
.context("write to string")?;
}
writedoc! {&mut index, r#"
</ul> </ul>
</body> </body>
</html> </html>
"#} "#}
.as_bytes(), .context("write to index.html")?;
)
.expect("can't write index.html");
Ok(())
}
fn build_docs(working_dir: &Path, abs_header_path: &Path) -> Result<()> {
exec(Command::new("sed").current_dir(working_dir).args([
"-i.bak",
"s/crabby.svg/crabby_dev.svg/",
"src/lib.rs",
]))?;
exec(
Command::new("cargo")
.current_dir(working_dir)
.env(
"RUSTDOCFLAGS",
format!(
"--cfg docsrs --html-in-header {} -D warnings",
abs_header_path.to_str().unwrap()
),
)
.args(["+nightly", "doc", "--no-deps", "--all-features"]),
)?;
fs::rename(
working_dir.join("src/lib.rs.bak"),
working_dir.join("src/lib.rs"),
)
.context("Failed to rename lib.rs.bak to lib.rs")
}
fn copy_dir_all(src: &Path, dst: &Path) -> io::Result<()> {
fs::create_dir_all(dst)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
let ty = entry.file_type()?;
let src = entry.path();
let src = src.as_path();
let dst = dst.join(entry.file_name());
let dst = dst.as_path();
if ty.is_dir() {
copy_dir_all(src, dst)?;
} else if !dst.exists() {
fs::copy(src, dst)?;
}
}
Ok(()) Ok(())
} }

Loading…
Cancel
Save