Skip to content

Commit 7ec4b1a

Browse files
author
Pat Hickey
authored
make witx cli a new crate, refactor how we validate repo contents (#398)
* make the witx cli tool a separate crate * delete phases.rs: not the witx crate's responsibility * use `witx docs --check` to replace specialized tests * repo-docs can be a shell script * parallelize CI a little * wibble * typo; only need to validate witx on one os * add ephemeral * delete wasi-docs test file * i love too program in yaml * repo-docs: reduce duplication * drop dev-dependencies in witx
1 parent 2a26951 commit 7ec4b1a

File tree

8 files changed

+174
-201
lines changed

8 files changed

+174
-201
lines changed

.github/workflows/main.yml

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ on: [push, pull_request]
33

44
jobs:
55
test:
6-
name: Test
6+
name: Test witx tools
77
runs-on: ${{ matrix.os }}
88
strategy:
99
matrix:
@@ -21,17 +21,27 @@ jobs:
2121
if: matrix.os == 'macos-latest'
2222
- run: cargo fetch
2323
working-directory: tools/witx
24-
- run: cargo build
25-
working-directory: tools/witx
26-
- run: cargo test
24+
- run: cargo test --all
2725
working-directory: tools/witx
2826

27+
validate:
28+
name: Validate witx files and docs
29+
runs-on: ubuntu-latest
30+
steps:
31+
- uses: actions/checkout@v1
32+
- name: Install Rust (rustup)
33+
shell: bash
34+
run: rustup update stable --no-self-update && rustup default stable
35+
36+
- name: Check that repository docs reflect witx
37+
run: ./tools/repo_docs.sh --check
38+
2939
rustfmt:
3040
name: Rustfmt
3141
runs-on: ubuntu-latest
3242
steps:
3343
- uses: actions/checkout@v1
3444
- name: Install Rust
3545
run: rustup update stable && rustup default stable && rustup component add rustfmt
36-
- run: cargo fmt -- --check
46+
- run: cargo fmt --all -- --check
3747
working-directory: tools/witx

tools/repo_docs.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env bash
2+
set -ex
3+
cd $(dirname $(realpath $0))/witx
4+
cargo run -p witx-cli -- docs $1 ../../phases/snapshot/witx/wasi_snapshot_preview1.witx --output ../../phases/snapshot/docs.md
5+
cargo run -p witx-cli -- docs $1 ../../phases/old/snapshot_0/witx/wasi_unstable.witx --output ../../phases/old/snapshot_0/docs.md
6+
cargo run -p witx-cli -- docs $1 \
7+
../../phases/ephemeral/witx/wasi_ephemeral_args.witx \
8+
../../phases/ephemeral/witx/wasi_ephemeral_clock.witx \
9+
../../phases/ephemeral/witx/wasi_ephemeral_environ.witx \
10+
../../phases/ephemeral/witx/wasi_ephemeral_fd.witx \
11+
../../phases/ephemeral/witx/wasi_ephemeral_path.witx \
12+
../../phases/ephemeral/witx/wasi_ephemeral_poll.witx \
13+
../../phases/ephemeral/witx/wasi_ephemeral_proc.witx \
14+
../../phases/ephemeral/witx/wasi_ephemeral_random.witx \
15+
../../phases/ephemeral/witx/wasi_ephemeral_sched.witx \
16+
../../phases/ephemeral/witx/wasi_ephemeral_sock.witx \
17+
--output ../../phases/ephemeral/docs.md

tools/witx/Cargo.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ thiserror = "1.0"
2020
wast = { version = "33.0.0", default-features = false }
2121

2222
[dev-dependencies]
23-
diff = "0.1.11"
24-
pretty_env_logger = "0.4"
25-
structopt = "0.3"
2623
rayon = "1.0"
2724

2825
[[test]]
2926
name = "witxt"
3027
harness = false
28+
29+
[workspace]
30+
members = [
31+
"cli",
32+
]

tools/witx/cli/Cargo.toml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[package]
2+
name = "witx-cli"
3+
version = "0.9.0"
4+
description = "CLI for operating on witx file format"
5+
homepage = "https://github.com/WebAssembly/WASI"
6+
repository = "https://github.com/WebAssembly/WASI"
7+
license = "Apache-2.0"
8+
categories = ["wasm"]
9+
keywords = ["webassembly", "wasm"]
10+
authors = ["Pat Hickey <[email protected]>", "Alex Crichton <[email protected]>"]
11+
edition = "2018"
12+
13+
[[bin]]
14+
name = "witx"
15+
path = "src/main.rs"
16+
17+
[dependencies]
18+
witx = { path = "../", version = "0.9.0" }
19+
anyhow = "1"
20+
log = "0.4"
21+
thiserror = "1.0"
22+
diff = "0.1.11"
23+
pretty_env_logger = "0.4"
24+
structopt = "0.3"
25+
rayon = "1.0"

tools/witx/examples/witx.rs renamed to tools/witx/cli/src/main.rs

Lines changed: 112 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::io::Write;
44
use std::path::{Path, PathBuf};
55
use std::process;
66
use structopt::{clap::AppSettings, StructOpt};
7-
use witx::{load, phases, Document, Documentation};
7+
use witx::{load, Document, Documentation};
88

99
/// Validate and process witx files
1010
#[derive(StructOpt, Debug)]
@@ -31,6 +31,9 @@ enum Command {
3131
/// Path to root of witx document
3232
#[structopt(number_of_values = 1, value_name = "INPUT", parse(from_os_str))]
3333
input: Vec<PathBuf>,
34+
/// Perform check that output matches witx documents
35+
#[structopt(long = "check")]
36+
check: bool,
3437
/// Path to generated documentation in Markdown format
3538
#[structopt(
3639
short = "o",
@@ -40,8 +43,6 @@ enum Command {
4043
)]
4144
output: Option<PathBuf>,
4245
},
43-
/// Update documentation in WASI repository to reflect witx specs
44-
RepoDocs,
4546
/// Examine differences between interfaces
4647
Polyfill {
4748
/// Path to root of witx document
@@ -80,22 +81,32 @@ pub fn main() {
8081
let verbose = args.verbose;
8182

8283
match args.cmd {
83-
Command::Docs { input, output } => {
84+
Command::Docs {
85+
input,
86+
check,
87+
output,
88+
} => {
8489
let doc = load_witx(&input, "input", verbose);
85-
if let Some(output) = output {
86-
write_docs(&doc, output)
90+
if check {
91+
let output = output.expect("output argument required in docs --check mode");
92+
if diff_against_filesystem(&doc.to_md(), &output).is_err() {
93+
println!("Docs in tree are out-of-date with witx files. Re-run this executable with the following arguments to to re-generate:");
94+
println!(
95+
"> witx docs {} --output {}",
96+
input
97+
.iter()
98+
.map(|p| p.to_string_lossy().into_owned())
99+
.collect::<Vec<String>>()
100+
.join(" "),
101+
output.to_string_lossy(),
102+
);
103+
}
87104
} else {
88-
println!("{}", doc.to_md())
89-
}
90-
}
91-
Command::RepoDocs => {
92-
for phase in &[
93-
phases::snapshot().unwrap(),
94-
phases::ephemeral().unwrap(),
95-
phases::old::snapshot_0().unwrap(),
96-
] {
97-
let doc = load(&phase).expect("parse phase");
98-
write_docs(&doc, phases::docs_path(&phase));
105+
if let Some(output) = output {
106+
write_docs(&doc, output)
107+
} else {
108+
println!("{}", doc.to_md())
109+
}
99110
}
100111
}
101112
Command::Polyfill {
@@ -173,3 +184,87 @@ fn parse_module_mapping(m: &str) -> Result<(String, String)> {
173184
};
174185
Ok((n.to_string(), o.to_string()))
175186
}
187+
188+
fn dos2unix(s: &str) -> String {
189+
let mut t = String::new();
190+
t.reserve(s.len());
191+
for c in s.chars() {
192+
if c != '\r' {
193+
t.push(c)
194+
}
195+
}
196+
t
197+
}
198+
199+
fn diff_against_filesystem(expected: &str, path: &Path) -> Result<(), ()> {
200+
let actual = std::fs::read_to_string(path)
201+
.unwrap_or_else(|e| panic!("couldn't read {}: {:?}", Path::display(path), e));
202+
// Git may checkout the file with dos line endings on windows. Strip all \r:
203+
let actual = dos2unix(&actual);
204+
if &actual == expected {
205+
return Ok(());
206+
}
207+
208+
eprintln!("The following diff was found between the docs generated from .witx and the");
209+
eprintln!("source {:?} in the tree:", path);
210+
eprintln!();
211+
212+
let mut expected_line = 1;
213+
let mut actual_line = 1;
214+
let mut separated = false;
215+
let mut any_lines = false;
216+
for diff in diff::lines(&expected, &actual) {
217+
match diff {
218+
diff::Result::Left(l) => {
219+
eprintln!("line {}: -{}", expected_line, l);
220+
expected_line += 1;
221+
separated = false;
222+
any_lines = true;
223+
}
224+
diff::Result::Both(_, _) => {
225+
expected_line += 1;
226+
actual_line += 1;
227+
if !separated {
228+
eprintln!("...");
229+
separated = true;
230+
}
231+
}
232+
diff::Result::Right(r) => {
233+
eprintln!("line {}: +{}", actual_line, r);
234+
actual_line += 1;
235+
separated = false;
236+
any_lines = true;
237+
}
238+
}
239+
}
240+
241+
if !any_lines {
242+
eprintln!();
243+
eprintln!(
244+
"Somehow there was a diff with no lines differing. Lengths: {} and {}.",
245+
expected.len(),
246+
actual.len()
247+
);
248+
for (index, (a, b)) in actual.chars().zip(expected.chars()).enumerate() {
249+
if a != b {
250+
eprintln!("char difference at index {}: '{}' != '{}'", index, a, b);
251+
}
252+
}
253+
for (index, (a, b)) in actual.bytes().zip(expected.bytes()).enumerate() {
254+
if a != b {
255+
eprintln!("byte difference at index {}: b'{}' != b'{}'", index, a, b);
256+
}
257+
}
258+
eprintln!();
259+
eprintln!("actual: {}", actual);
260+
eprintln!();
261+
eprintln!("expected: {}", expected);
262+
}
263+
264+
eprintln!();
265+
eprintln!(
266+
"To regenerate the files, run `cd tools/witx && cargo run --example witx repo-docs`."
267+
);
268+
eprintln!();
269+
Err(())
270+
}

tools/witx/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ mod io;
1010
mod layout;
1111
/// Witx syntax parsing from SExprs
1212
pub mod parser;
13-
/// Paths to witx documents for various proposal phases
14-
pub mod phases;
1513
/// Calculate required polyfill between interfaces
1614
pub mod polyfill;
1715
/// Render ast to text

tools/witx/src/phases.rs

Lines changed: 0 additions & 75 deletions
This file was deleted.

0 commit comments

Comments
 (0)