Skip to content

Commit ef55898

Browse files
committed
Merge branch '📝-common' into 🦆
2 parents bd1b603 + 894d535 commit ef55898

File tree

28 files changed

+282
-17
lines changed

28 files changed

+282
-17
lines changed

.github/workflows/doc.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,26 @@ jobs:
1818

1919
- name: Install dependencies (Linux)
2020
run: .github/scripts/install-deno.sh
21+
- name: Set rustdoc flags
22+
run: |
23+
# [ref:doc_global_styling]
24+
echo "RUSTDOCFLAGS=--html-in-header `pwd`/src/r3/src/common.md" >> $GITHUB_ENV
2125
2226
- name: Build Documentation
2327
uses: actions-rs/cargo@v1
2428
with:
2529
command: doc
30+
# Documentate all published packages with all features enabled [tag:doc_all_features]
2631
args: -p r3_port_std -p r3_port_arm -p r3_port_arm_m -p r3_port_riscv -p r3_support_rp2040 -p r3_support_rza1 -p r3_portkit -p r3_kernel -p r3 -p r3_core --all-features
2732

2833
- name: Replace the non-local crate documentation with redirect pages to docs.rs
2934
run: deno run -A scripts/externalize-non-local-docs.ts -y
3035

36+
- name: Check the generated documentation
37+
run: |
38+
# Don't fail the pipeline on errors because they are mostly minor
39+
deno run --allow-read scripts/check-doc.ts || true
40+
3141
- name: Generate Badge
3242
run: |
3343
rev=`git show-ref --head HEAD | cut -b 1-7`

scripts/check-doc.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// This [Deno] program scans the compiled API documentation to check for errors.
2+
//
3+
// [Deno]: https://deno.land/
4+
//
5+
// Usage: deno run --allow-read scripts/check-workspace.ts
6+
import { parse as parseFlags } from "https://deno.land/std@0.125.0/flags/mod.ts";
7+
import { walk } from "https://deno.land/std@0.125.0/fs/mod.ts";
8+
import * as path from "https://deno.land/std@0.125.0/path/mod.ts";
9+
import * as log from "https://deno.land/std@0.125.0/log/mod.ts";
10+
11+
const parsedArgs = parseFlags(Deno.args, {
12+
"alias": {
13+
h: "help",
14+
d: "rustdoc-output",
15+
},
16+
"string": [
17+
"rustdoc-output",
18+
],
19+
});
20+
21+
if (parsedArgs["help"]) {
22+
console.log("Arguments:");
23+
console.log(" -h --help Displays this message");
24+
console.log(" -d DIRECTORY --rustdoc-output=DIRECTORY");
25+
console.log(" Specifies the rustdoc output directory to scan. " +
26+
"Defaults to `./target/doc` when unspecified.");
27+
}
28+
29+
await log.setup({
30+
handlers: {
31+
console: new log.handlers.ConsoleHandler("DEBUG"),
32+
},
33+
34+
loggers: {
35+
default: {
36+
level: "INFO",
37+
handlers: ["console"],
38+
},
39+
},
40+
});
41+
42+
const logger = log.getLogger();
43+
let hasError = false;
44+
let expectedRepository: string | null = null;
45+
46+
// A code fragment indicating the presence of `common.md`
47+
const COMMON_CSS_FRAGMENT = /\.toc-header \+ ul::before {/g;
48+
// Code fragments that require the presence of `common.md`
49+
const COMMON_CSS_USES = [
50+
/class="toc-header"/,
51+
// The negative lookbehind is intended to avoid matching
52+
// the example code in `common.md`
53+
/(?<!\* *<div )class="admonition-follows"/,
54+
/class="disabled-feature-warning"/,
55+
// The negative lookbehind is intended to avoid matching
56+
// the example code in `common.md`
57+
/(?<!\* *<span )class="class"/,
58+
'<cneter>',
59+
];
60+
61+
await validateRustdocOutput(parsedArgs.d || "./target/doc");
62+
63+
if (hasError) {
64+
Deno.exit(1);
65+
}
66+
67+
async function validateRustdocOutput(docPath: string): Promise<void> {
68+
let numFilesScanned = 0;
69+
70+
logger.info(`Scanning ${docPath}`);
71+
for await (const { path } of walk(docPath, { includeDirs: false })) {
72+
if (!path.endsWith(".html") && !path.endsWith(".htm")) {
73+
continue;
74+
}
75+
76+
logger.debug(`# ${path}`);
77+
78+
const html = await Deno.readTextFile(path);
79+
numFilesScanned += 1;
80+
81+
const numCommonCssInstances =
82+
Array.from(html.matchAll(COMMON_CSS_FRAGMENT)).length;
83+
if (numCommonCssInstances === 0) {
84+
// Maybe a redirect page?
85+
logger.debug(`${path}: Doesn't contain a fragment of 'common.md' - ignoring`);
86+
continue;
87+
} else if (numCommonCssInstances >= 2) {
88+
// `#[doc = ...]` (per-file) + `$RUSTDOCFLAGS` [ref:doc_global_styling]
89+
logger.debug(`${path}: There's a per-file inclusion of 'common.md' - ignoring`);
90+
91+
if (numCommonCssInstances > 2) {
92+
logger.warning(`${path}: Includes too many instances of 'common.md'`);
93+
}
94+
95+
continue;
96+
} else {
97+
// This file lacks a per-file incluson of `common.md`.
98+
}
99+
100+
// To prevent the degradation of the appearance in the absence of the
101+
// appropriate `$RUSTDOCFLAGS`, this page must be devoid of constructs
102+
// that make use of the styling rules defined by `common.md`.
103+
for (const cssUsage of COMMON_CSS_USES) {
104+
if (html.match(cssUsage)) {
105+
logger.error(`${path}: This file lacks a per-file inclusion of 'common.md', ` +
106+
`but includes a code fragment '${cssUsage}'`);
107+
hasError = true;
108+
}
109+
}
110+
}
111+
112+
logger.info(`${numFilesScanned} file(s) have been checked`);
113+
} // validateRustdocOutput

scripts/check-workspace.ts

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { parse as parseFlags } from "https://deno.land/std@0.125.0/flags/mod.ts"
1010
import { parse as parseToml } from "https://deno.land/std@0.125.0/encoding/toml.ts";
1111
import * as path from "https://deno.land/std@0.125.0/path/mod.ts";
1212
import * as log from "https://deno.land/std@0.125.0/log/mod.ts";
13+
import { exists } from "https://deno.land/std@0.125.0/fs/exists.ts";
1314

1415
const parsedArgs = parseFlags(Deno.args, {
1516
"alias": {
@@ -42,10 +43,17 @@ await log.setup({
4243
});
4344

4445
const EXPECTED_SOURCE_FRAGMENTS = [
45-
// We want published crates to have consistent logos
46-
'#![doc(html_logo_url = "https://r3-os.github.io/r3/logo-small.svg")]',
46+
// We want published crates to have consistent logos, which should
47+
// appear conditionally [ref:doc_feature]
48+
`#![cfg_attr(
49+
feature = "doc",
50+
doc(html_logo_url = "https://r3-os.github.io/r3/logo-small.svg")
51+
)]`,
4752
];
4853

54+
const COMMON_HEADER_PATH = "src/common.md";
55+
const EXPECTED_RUSTDOC_ARGS = `--html-in-header ${COMMON_HEADER_PATH}`;
56+
4957
const logger = log.getLogger();
5058
let hasError = false;
5159
let expectedRepository: string | null = null;
@@ -83,7 +91,7 @@ async function validateWorkspace(workspacePath: string): Promise<void> {
8391
continue;
8492
}
8593

86-
const {package: pkg, dependencies = {}} = crateMeta;
94+
const {package: pkg, dependencies = {}, features = {}} = crateMeta;
8795
const {publish = true, version} = pkg;
8896

8997
// CC-VER-UNPUBLISHED
@@ -146,6 +154,63 @@ async function validateWorkspace(workspacePath: string): Promise<void> {
146154
}
147155
}
148156
}
157+
158+
// We want `common.md` applied for the entire crate in order that the
159+
// upper-left custom logo is properly styled in official documentation
160+
// builds [tag:doc_global_styling]
161+
const docsMetadata = pkg?.metadata?.docs?.rs ?? {};
162+
if (publish) {
163+
const docsArgs = docsMetadata['rustdoc-args'] ?? [];
164+
165+
// Poor man's `docsArgs.windows(2).any(|x| x == ...)`
166+
const docsArgsConcat = docsArgs.join(' ');
167+
if (docsArgsConcat.indexOf(EXPECTED_RUSTDOC_ARGS) < 0) {
168+
logger.error(`${crateRelPath}: package.metadata.docs.rs.rustdoc-args doesn't ` +
169+
`include '${EXPECTED_RUSTDOC_ARGS}'.`);
170+
hasError = true;
171+
}
172+
173+
// The file referenced by `EXPECTED_RUSTDOC_ARGS` should actually
174+
// exist
175+
const commonHeaderPath = path.join(cratePath, COMMON_HEADER_PATH);
176+
if (!await exists(commonHeaderPath)) {
177+
logger.error(`${crateRelPath}: '${commonHeaderPath}' doesn't exist.`);
178+
hasError = true;
179+
}
180+
}
181+
182+
// The custom logo needs a custom stylesheet [ref:doc_global_styling],
183+
// so it must be disabled conditionally if the custom stylesheet isn't
184+
// applied globally. The `doc` Cargo feature is used to toggle this.
185+
// [tag:doc_feature] In summary, there are two cases we consider:
186+
//
187+
// - In official documentation builds (docs.rs and our API
188+
// documentation website), the `doc` Cargo feature is enabled, and
189+
// the custom stylesheet is applied globally using `RUSTDOCFLAGS`.
190+
// The result is a custom logo styled consistently.
191+
//
192+
// - In unofficial documentation builds (e.g., `cargo doc` in
193+
// downstream workspaces), the `doc` Cargo feature is disabled by
194+
// default, and the custom stylesheet is not applied globally. The
195+
// custom logo is disabled by `cfg_attr` in this case.
196+
//
197+
// We don't handle the cases where `doc` is enabled in unofficial
198+
// builds. In such cases, the custom logo will be styled properly or
199+
// improperly depending on whether `common.md` is included by `#[doc =
200+
// ...]` in that file.
201+
if (publish) {
202+
if (typeof features.doc == "undefined") {
203+
logger.error(`${crateRelPath}: features.doc is not present.`);
204+
hasError = true;
205+
}
206+
}
207+
208+
// The enabled features must be consistent between docs.rs and
209+
// our API documentation website [ref:doc_all_features]
210+
if (publish && !docsMetadata["all-features"]) {
211+
logger.error(`${crateRelPath}: package.metadata.docs.rs.all-features is ` +
212+
`not set.`);
213+
}
149214
}
150215
}
151216

@@ -165,7 +230,16 @@ interface CargoMeta {
165230
keywords?: string[],
166231
repository?: string,
167232
publish?: boolean,
233+
metadata?: {
234+
docs?: {
235+
rs?: {
236+
"rustdoc-args"?: string[],
237+
"all-features"?: boolean,
238+
},
239+
},
240+
},
168241
},
242+
features?: { [name: string]: string[] },
169243
dependencies?: { [name: string]: Dep },
170244
"dev-dependencies"?: { [name: string]: Dep },
171245
}

src/r3/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ sync = []
1717
# Exposes `r3_core`'s features'
1818
chrono_0p4 = ["r3_core/chrono_0p4"]
1919

20-
# Enable the diagram rendering by svgbob and other fancy stuff
20+
# Enable the diagram rendering by svgbob and other fancy stuff [ref:doc_feature]
2121
doc = ["svgbobdoc/enable", "embed-doc-image", "r3_core/doc"]
2222

2323
# Displays a "some features are disabled" warning in the documentation
@@ -39,3 +39,4 @@ r3_kernel = { path = "../r3_kernel" }
3939
[package.metadata.docs.rs]
4040
all-features = true
4141
targets = []
42+
rustdoc-args = ["--html-in-header", "src/common.md"] # [ref:doc_global_styling]

src/r3/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
#![feature(decl_macro)]
1212
#![feature(doc_cfg)]
1313
#![deny(unsafe_op_in_unsafe_fn)]
14-
#![doc(html_logo_url = "https://r3-os.github.io/r3/logo-small.svg")]
14+
#![cfg_attr(
15+
feature = "doc",
16+
doc(html_logo_url = "https://r3-os.github.io/r3/logo-small.svg")
17+
)]
1518
#![doc = include_str!("./lib.md")]
1619
#![doc = include_str!("./common.md")]
1720
// Work-around for [ref:rustdoc_images]

src/r3_core/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ repository = "https://github.com/r3-os/r3"
1111
[features]
1212
default = []
1313

14-
# Enable the diagram rendering by svgbob and other fancy stuff
14+
# Enable the diagram rendering by svgbob and other fancy stuff [ref:doc_feature]
1515
doc = ["svgbobdoc/enable"]
1616

1717
# Displays a "some features are disabled" warning in the documentation
@@ -41,3 +41,4 @@ log = "0.4.8"
4141
[package.metadata.docs.rs]
4242
all-features = true
4343
targets = []
44+
rustdoc-args = ["--html-in-header", "src/common.md"] # [ref:doc_global_styling]

src/r3_core/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@
5151
#![feature(doc_cfg)] // `#[doc(cfg(...))]`
5252
#![cfg_attr(test, feature(is_sorted))]
5353
#![deny(unsafe_op_in_unsafe_fn)]
54-
#![doc(html_logo_url = "https://r3-os.github.io/r3/logo-small.svg")]
54+
#![cfg_attr(
55+
feature = "doc",
56+
doc(html_logo_url = "https://r3-os.github.io/r3/logo-small.svg")
57+
)]
5558
#![doc = include_str!("./lib.md")]
5659
#![doc = include_str!("./common.md")]
5760
#![doc = include!("../doc/trait_binding.rs")] // `![trait_binding]`

src/r3_kernel/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ inline_syscall = []
1515
priority_boost = []
1616
system_time = []
1717

18-
# Enable the diagram rendering by svgbob
18+
# Enable the diagram rendering by svgbob and other stuff [ref:doc_feature]
1919
doc = ["svgbobdoc/enable"]
2020

2121
# Enable all optional kernel features
@@ -53,3 +53,4 @@ log = "0.4.8"
5353
[package.metadata.docs.rs]
5454
all-features = true
5555
targets = []
56+
rustdoc-args = ["--html-in-header", "src/common.md"] # [ref:doc_global_styling]

src/r3_kernel/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@
3030
#![feature(let_else)]
3131
#![feature(doc_cfg)] // `#[doc(cfg(...))]`
3232
#![deny(unsafe_op_in_unsafe_fn)]
33-
#![doc(html_logo_url = "https://r3-os.github.io/r3/logo-small.svg")]
33+
#![cfg_attr(
34+
feature = "doc",
35+
doc(html_logo_url = "https://r3-os.github.io/r3/logo-small.svg")
36+
)]
3437
#![doc = include_str!("./lib.md")]
3538
#![doc = include_str!("./common.md")]
3639
#![doc = include!("../doc/traits.rs")] // `![traits]`

src/r3_port_arm/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ repository = "https://github.com/r3-os/r3"
1111
[features]
1212
preload-registers = []
1313

14+
# Used for documentation builds [ref:doc_feature]
15+
doc = []
16+
1417
[dependencies]
1518
r3_portkit = { version = "0.1.0", path = "../r3_portkit" }
1619
r3_kernel = { version = "0.0.0", path = "../r3_kernel" }
@@ -21,4 +24,6 @@ memoffset = { version = "0.6.5", features = ["unstable_const"] }
2124
r0 = { version = "1.0.0" }
2225

2326
[package.metadata.docs.rs]
27+
all-features = true
2428
targets = []
29+
rustdoc-args = ["--html-in-header", "src/common.md"] # [ref:doc_global_styling]

0 commit comments

Comments
 (0)