feat(xtask): add --watch option for build-wasm and

`check-wasm-exports` xtask commands
This commit is contained in:
WillLillis 2025-01-06 22:36:16 -05:00 committed by Amaan Qureshi
parent 207ef9796e
commit 2c6f70cc57
5 changed files with 252 additions and 20 deletions

View file

@ -1,13 +1,21 @@
use std::{
collections::HashSet,
ffi::{OsStr, OsString},
fmt::Write,
fs,
path::PathBuf,
process::Command,
time::Duration,
};
use anyhow::{anyhow, Result};
use notify::{
event::{AccessKind, AccessMode},
EventKind, RecursiveMode,
};
use notify_debouncer_full::new_debouncer;
use crate::{bail_on_err, BuildWasm, EMSCRIPTEN_TAG};
use crate::{bail_on_err, watch_wasm, BuildWasm, EMSCRIPTEN_TAG};
#[derive(PartialEq, Eq)]
enum EmccSource {
@ -95,9 +103,10 @@ pub fn run_wasm(args: &BuildWasm) -> Result<()> {
fs::create_dir_all("target/scratch").unwrap();
let exported_functions = concat!(
include_str!("../../lib/src/wasm/stdlib-symbols.txt"),
include_str!("../../lib/binding_web/exports.txt")
let exported_functions = format!(
"{}{}",
fs::read_to_string("lib/src/wasm/stdlib-symbols.txt")?,
fs::read_to_string("lib/binding_web/exports.txt")?
)
.replace('"', "")
.lines()
@ -159,9 +168,20 @@ pub fn run_wasm(args: &BuildWasm) -> Result<()> {
"-o",
"target/scratch/tree-sitter.js",
]);
let command = command.args(&emscripten_flags);
if args.watch {
watch_wasm!(|| build_wasm(command));
} else {
build_wasm(command)?;
}
Ok(())
}
fn build_wasm(cmd: &mut Command) -> Result<()> {
bail_on_err(
&command.args(emscripten_flags).spawn()?.wait_with_output()?,
&cmd.spawn()?.wait_with_output()?,
"Failed to compile the Tree-sitter WASM library",
)?;

View file

@ -1,12 +1,19 @@
use std::{
collections::HashSet,
io::BufRead,
path::PathBuf,
process::{Command, Stdio},
time::Duration,
};
use anyhow::{anyhow, Result};
use notify::{
event::{AccessKind, AccessMode},
EventKind, RecursiveMode,
};
use notify_debouncer_full::new_debouncer;
use crate::{bail_on_err, build_wasm::run_wasm, BuildWasm};
use crate::{bail_on_err, build_wasm::run_wasm, watch_wasm, BuildWasm, CheckWasmExports};
const EXCLUDES: [&str; 28] = [
// Unneeded because the JS side has its own way of implementing it
@ -44,15 +51,26 @@ const EXCLUDES: [&str; 28] = [
"ts_query_cursor_timeout_micros",
];
pub fn run() -> Result<()> {
pub fn run(args: &CheckWasmExports) -> Result<()> {
if args.watch {
watch_wasm!(check_wasm_exports);
} else {
check_wasm_exports()?;
}
Ok(())
}
fn check_wasm_exports() -> Result<()> {
// Build the wasm module with debug symbols for wasm-objdump
run_wasm(&BuildWasm {
debug: true,
verbose: false,
docker: false,
watch: false,
})?;
let mut wasm_exports = include_str!("../../lib/binding_web/exports.txt")
let mut wasm_exports = std::fs::read_to_string("lib/binding_web/exports.txt")?
.lines()
.map(|s| s.replace("_wasm", "").replace("byte", "index"))
// remove leading and trailing quotes, trailing comma

View file

@ -30,7 +30,7 @@ enum Commands {
/// Bumps the version of the workspace.
BumpVersion(BumpVersion),
/// Checks that WASM exports are synced.
CheckWasmExports,
CheckWasmExports(CheckWasmExports),
/// Runs `cargo clippy`.
Clippy(Clippy),
/// Fetches emscripten.
@ -82,6 +82,9 @@ struct BuildWasm {
/// Run emscripten with verbose output.
#[arg(long, short)]
verbose: bool,
/// Rebuild when relevant files are changed.
#[arg(long, short)]
watch: bool,
}
#[derive(Args)]
@ -91,6 +94,13 @@ struct BumpVersion {
version: Option<Version>,
}
#[derive(Args)]
struct CheckWasmExports {
/// Recheck when relevant files are changed.
#[arg(long, short)]
watch: bool,
}
#[derive(Args)]
struct Clippy {
/// Automatically apply lint suggestions (`clippy --fix`).
@ -207,7 +217,7 @@ fn run() -> Result<()> {
Commands::BuildWasm(build_wasm_options) => build_wasm::run_wasm(&build_wasm_options)?,
Commands::BuildWasmStdlib => build_wasm::run_wasm_stdlib()?,
Commands::BumpVersion(bump_options) => bump::run(bump_options)?,
Commands::CheckWasmExports => check_wasm_exports::run()?,
Commands::CheckWasmExports(check_options) => check_wasm_exports::run(&check_options)?,
Commands::Clippy(clippy_options) => clippy::run(&clippy_options)?,
Commands::FetchEmscripten => fetch::run_emscripten()?,
Commands::FetchFixtures => fetch::run_fixtures()?,
@ -289,3 +299,57 @@ pub fn create_commit(repo: &Repository, msg: &str, paths: &[&str]) -> Result<Oid
&[&parent_commit],
)?)
}
#[macro_export]
macro_rules! watch_wasm {
($watch_fn:expr) => {
if let Err(e) = $watch_fn() {
eprintln!("{e}");
}
let watch_files = [
"binding.c",
"binding.js",
"exports.txt",
"imports.js",
"prefix.js",
"suffix.js",
]
.iter()
.map(PathBuf::from)
.collect::<HashSet<PathBuf>>();
let (tx, rx) = std::sync::mpsc::channel();
let mut debouncer = new_debouncer(Duration::from_secs(1), None, tx)?;
debouncer.watch("lib/binding_web", RecursiveMode::NonRecursive)?;
for result in rx {
match result {
Ok(events) => {
for event in events {
if event.kind == EventKind::Access(AccessKind::Close(AccessMode::Write))
&& event
.paths
.iter()
.filter_map(|p| p.file_name())
.any(|p| watch_files.contains(&PathBuf::from(p)))
{
if let Err(e) = $watch_fn() {
eprintln!("{e}");
}
}
}
}
Err(errors) => {
return Err(anyhow!(
"{}",
errors
.into_iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join("\n")
));
}
}
}
};
}