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/settings.json
site/
header.html
.idea/

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

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

@ -1,12 +1,7 @@
use anyhow::{anyhow, Context as _, Result};
use std::{
path::{Path, PathBuf},
process::Command,
};
use std::{fs, io, io::Write};
use indoc::indoc;
use cargo_metadata::{Metadata, MetadataCommand};
use indoc::{indoc, writedoc};
use std::{ffi::OsString, fs, io::Write as _, process::Command};
pub fn exec(cmd: &mut Command) -> Result<()> {
let status = cmd
@ -22,95 +17,92 @@ pub fn exec(cmd: &mut Command) -> Result<()> {
}
pub fn docs() -> Result<()> {
let current_dir = PathBuf::from(".");
let header_path = current_dir.join("header.html");
let mut header = fs::File::create(&header_path).expect("can't create header.html");
header
.write_all(r#"<meta name="robots" content="noindex">"#.as_bytes())
.expect("can't write header.html contents");
header.flush().expect("couldn't flush contents");
let abs_header_path = fs::canonicalize(&header_path).unwrap();
const PACKAGE_TO_DESCRIPTION: &[(&str, &str)] =
&[("aya", "User-space"), ("aya-bpf", "Kernel-space")];
let Metadata {
workspace_root,
target_directory,
..
} = 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)?;
build_docs(&current_dir.join("bpf/aya-bpf"), &abs_header_path)?;
copy_dir_all("./target/doc".as_ref(), "./site/user".as_ref())?;
copy_dir_all(
"./target/bpfel-unknown-none/doc".as_ref(),
"./site/bpf".as_ref(),
exec(
Command::new("cargo")
.current_dir(&workspace_root)
.env("RUSTDOCFLAGS", rustdocflags)
.args(["+nightly", "doc", "--no-deps", "--all-features"])
.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");
robots
.write_all(
indoc! {r#"
let site = workspace_root.join("site");
match fs::remove_dir_all(&site) {
Ok(()) => {}
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:*
Disallow: /
"#}
.as_bytes(),
)
.expect("can't write robots.txt");
"#},
)
.context("can't write robots.txt")?;
let mut index = fs::File::create("site/index.html").expect("can't create index.html");
index
.write_all(
indoc! {r#"
let mut index = fs::File::create(site.join("index.html"))
.with_context(|| format!("create {site:?}/index.html"))?;
writedoc! {&mut index, r#"
<html>
<meta name="robots" content="noindex">
<body>
<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>
</body>
</html>
"#}
.as_bytes(),
)
.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",
]))?;
.context("write to index.html")?;
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(())
}

Loading…
Cancel
Save