diff --git a/Cargo.lock b/Cargo.lock index 3dbc7a21..3ca5cf0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,7 +108,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags", + "bitflags 2.6.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -122,6 +122,12 @@ dependencies = [ "syn", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.6.0" @@ -570,6 +576,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "file-id" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bc904b9bbefcadbd8e3a9fb0d464a9b979de6324c03b3c663e8994f46a5be36" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "filetime" version = "0.2.25" @@ -617,6 +632,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "fuzzy-matcher" version = "0.3.7" @@ -654,7 +678,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" dependencies = [ - "bitflags", + "bitflags 2.6.0", "libc", "libgit2-sys", "log", @@ -881,6 +905,35 @@ version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" +[[package]] +name = "inotify" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -952,6 +1005,26 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1006,7 +1079,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags", + "bitflags 2.6.0", "libc", "redox_syscall", ] @@ -1094,6 +1167,18 @@ dependencies = [ "adler2", ] +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.52.0", +] + [[package]] name = "ndk-context" version = "0.1.1" @@ -1106,7 +1191,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags", + "bitflags 2.6.0", "cfg-if", "cfg_aliases", "libc", @@ -1122,6 +1207,47 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "notify" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009" +dependencies = [ + "bitflags 2.6.0", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.52.0", +] + +[[package]] +name = "notify-debouncer-full" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcf855483228259b2353f89e99df35fc639b2b2510d1166e4858e3f67ec1afb" +dependencies = [ + "file-id", + "log", + "notify", + "notify-types", + "walkdir", +] + +[[package]] +name = "notify-types" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585d3cb5e12e01aed9e8a1f70d5c6b5e86fe2a6e48fc8cd0b3e0b8df6f6eb174" +dependencies = [ + "instant", +] + [[package]] name = "objc-sys" version = "0.3.5" @@ -1150,7 +1276,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags", + "bitflags 2.6.0", "block2", "libc", "objc2", @@ -1337,7 +1463,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags", + "bitflags 2.6.0", ] [[package]] @@ -1409,7 +1535,7 @@ version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ - "bitflags", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -2116,7 +2242,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c771866898879073c53b565a6c7b49953795159836714ac56a5befb581227c5" dependencies = [ "ahash", - "bitflags", + "bitflags 2.6.0", "hashbrown 0.14.5", "indexmap", "semver", @@ -2129,7 +2255,7 @@ version = "0.221.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083" dependencies = [ - "bitflags", + "bitflags 2.6.0", "hashbrown 0.15.2", "indexmap", "semver", @@ -2154,7 +2280,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b79302e3e084713249cc5622e8608e7410afdeeea8c8026d04f491d1fab0b4b" dependencies = [ "anyhow", - "bitflags", + "bitflags 2.6.0", "bumpalo", "cc", "cfg-if", @@ -2646,6 +2772,8 @@ dependencies = [ "clap", "git2", "indoc", + "notify", + "notify-debouncer-full", "regex", "semver", "serde", diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 774fd8e8..67cef0c8 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -28,3 +28,5 @@ semver.workspace = true serde.workspace = true serde_json.workspace = true ureq = "2.12.1" +notify = "7.0.0" +notify-debouncer-full = "0.4.0" diff --git a/xtask/src/build_wasm.rs b/xtask/src/build_wasm.rs index 6bf2eddd..77a198b9 100644 --- a/xtask/src/build_wasm.rs +++ b/xtask/src/build_wasm.rs @@ -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", )?; diff --git a/xtask/src/check_wasm_exports.rs b/xtask/src/check_wasm_exports.rs index 3d0cab40..4bc9e9fb 100644 --- a/xtask/src/check_wasm_exports.rs +++ b/xtask/src/check_wasm_exports.rs @@ -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 diff --git a/xtask/src/main.rs b/xtask/src/main.rs index a1333d65..9d8069df 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -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, } +#[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 { + 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::>(); + 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::>() + .join("\n") + )); + } + } + } + }; +}