From b86d42d1b0c1d974e79e8000c76c1c6bbf9612b4 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Thu, 13 Jul 2023 14:38:01 -0400 Subject: [PATCH] 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. --- .gitignore | 1 - bpf/.cargo/config.toml | 2 - xtask/Cargo.toml | 1 + xtask/src/docs/mod.rs | 158 +++++++++++++++++++---------------------- 4 files changed, 76 insertions(+), 86 deletions(-) diff --git a/.gitignore b/.gitignore index 38ed638e..f9965580 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,4 @@ target/ .vscode/ !.vscode/settings.json site/ -header.html .idea/ diff --git a/bpf/.cargo/config.toml b/bpf/.cargo/config.toml index 7dc8f90d..61fc56a9 100644 --- a/bpf/.cargo/config.toml +++ b/bpf/.cargo/config.toml @@ -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" diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 24d91ef4..6610e982 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -13,3 +13,4 @@ indoc = "2.0" proc-macro2 = "1" quote = "1" syn = "2" +tempfile = "3" diff --git a/xtask/src/docs/mod.rs b/xtask/src/docs/mod.rs index c7333d4c..a8ef37cb 100644 --- a/xtask/src/docs/mod.rs +++ b/xtask/src/docs/mod.rs @@ -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#""#.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#""#).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(¤t_dir.join("aya"), &abs_header_path)?; - build_docs(¤t_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#" "#} - .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(()) }