|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +`watcher` is an R package that provides R bindings for libfswatch, a cross-platform file system monitoring library. It enables asynchronous background monitoring of filesystem changes using optimal event-driven APIs for each platform (ReadDirectoryChangesW on Windows, FSEvents on macOS, inotify on Linux, kqueue on BSD, File Events Notification on Solaris/Illumos). |
| 8 | + |
| 9 | +## Build and Test Commands |
| 10 | + |
| 11 | +### Package Development |
| 12 | +```bash |
| 13 | +# Build and check the package |
| 14 | +R CMD build . |
| 15 | +R CMD check watcher_*.tar.gz |
| 16 | + |
| 17 | +# Install from source (triggers configure script) |
| 18 | +R CMD INSTALL . |
| 19 | + |
| 20 | +# Run tests |
| 21 | +Rscript -e "testthat::test_dir('tests/testthat')" |
| 22 | + |
| 23 | +# Or interactively in R |
| 24 | +R -e "devtools::test()" |
| 25 | +``` |
| 26 | + |
| 27 | +### Single Test Execution |
| 28 | +```r |
| 29 | +# In R console |
| 30 | +testthat::test_file("tests/testthat/test-watch.R") |
| 31 | +``` |
| 32 | + |
| 33 | +### Documentation |
| 34 | +```bash |
| 35 | +# Generate documentation with roxygen2 |
| 36 | +Rscript -e "roxygen2::roxygenize()" |
| 37 | +``` |
| 38 | + |
| 39 | +### CI/CD |
| 40 | +The package uses GitHub Actions workflows in `.github/workflows/`: |
| 41 | +- `R-CMD-check.yaml`: Comprehensive R CMD check across multiple OS/R versions |
| 42 | +- `test-coverage.yaml`: Code coverage reporting |
| 43 | +- `pkgdown.yaml`: Documentation site generation |
| 44 | + |
| 45 | +## Architecture |
| 46 | + |
| 47 | +### Core Components |
| 48 | + |
| 49 | +**R Layer (`R/watch.R`):** |
| 50 | +- `watcher()`: Factory function that creates a `Watcher` R6 object |
| 51 | +- `Watcher`: R6 class that wraps the C interface with methods: |
| 52 | + - `$start()`: Start background monitoring |
| 53 | + - `$stop()`: Stop monitoring |
| 54 | + - `$is_running()`: Check monitor status |
| 55 | + - `$get_path()`: Get watched path(s) |
| 56 | +- The R6 class maintains a reference to the C-level FSW_HANDLE via an external pointer |
| 57 | + |
| 58 | +**C Layer (`src/`):** |
| 59 | +- `watcher.c`: Core implementation with three main entry points: |
| 60 | + - `watcher_create()`: Initialize fswatch session with paths, callback, and latency |
| 61 | + - `watcher_start_monitor()`: Spawn detached pthread running `fsw_start_monitor()` |
| 62 | + - `watcher_stop_monitor()`: Stop the monitoring thread |
| 63 | +- `init.c`: R package initialization that: |
| 64 | + - Initializes libfswatch library |
| 65 | + - Obtains `execLaterNative2` from the 'later' package for async callbacks |
| 66 | + - Registers C callable methods |
| 67 | +- `watcher.h`: Header file defining structures and function signatures |
| 68 | + |
| 69 | +**Callback Mechanism:** |
| 70 | +- File events trigger `process_events()` callback in C |
| 71 | +- Events are bundled by path and passed to R via the 'later' package's `execLaterNative2` |
| 72 | +- R callbacks execute when R is idle or when `later::run_now()` is called |
| 73 | +- If no callback is provided, events are printed to stdout |
| 74 | + |
| 75 | +### Build System |
| 76 | + |
| 77 | +**Configure Scripts:** |
| 78 | +- `configure` (Linux/macOS): Detects system-installed libfswatch in standard locations (`/usr/local`, `/usr`, homebrew paths). If not found, compiles bundled libfswatch (v1.19.0-dev) using cmake. Handles special cases like ARM atomic operations. |
| 79 | +- `configure.win` (Windows non-UCRT): Compiles libfswatch from source for both x64 and i386 architectures |
| 80 | +- `configure.ucrt` (Windows UCRT): Simplified version for modern Windows R builds |
| 81 | +- All scripts generate `src/Makevars` with appropriate compiler flags |
| 82 | + |
| 83 | +**Key Dependencies:** |
| 84 | +- libfswatch (bundled as `src/fswatch-5c443d2p.tar.gz`) |
| 85 | +- cmake (required for compiling libfswatch from source) |
| 86 | +- pthread (for background monitoring thread) |
| 87 | +- 'later' R package (for async callback execution) |
| 88 | + |
| 89 | +### Event Filtering |
| 90 | + |
| 91 | +The package filters filesystem events to only report main event types (Created, Updated, Removed, Renamed) to prevent excessive callbacks. Some platforms generate events for file reads, which are intentionally excluded. |
| 92 | + |
| 93 | +### Threading Model |
| 94 | + |
| 95 | +- File monitoring runs in a detached pthread spawned by `watcher_start_monitor()` |
| 96 | +- The thread runs `fsw_start_monitor()` which blocks until stopped |
| 97 | +- Events from the monitoring thread are safely passed to R via the 'later' package |
| 98 | +- External pointer finalizer ensures proper cleanup when Watcher objects are garbage collected |
| 99 | + |
| 100 | +## Platform-Specific Notes |
| 101 | + |
| 102 | +### Windows |
| 103 | +- Uses ReadDirectoryChangesW API (always recursive) |
| 104 | +- Windows latency has been specifically addressed (see NEWS.md - patch in v0.1.4.9000) |
| 105 | +- Builds require cmake and compile libfswatch from bundled source |
| 106 | + |
| 107 | +### macOS |
| 108 | +- Uses FSEvents API (always recursive) |
| 109 | +- Can use system libfswatch if installed via homebrew/MacPorts |
| 110 | +- MACOSX_DEPLOYMENT_TARGET is automatically extracted from compiler flags |
| 111 | + |
| 112 | +### Linux |
| 113 | +- Uses inotify API |
| 114 | +- Recursive monitoring is explicitly enabled to match Windows/macOS behavior |
| 115 | +- May require -latomic on ARM architectures (Raspberry Pi) |
| 116 | + |
| 117 | +### Testing |
| 118 | +Tests in `tests/testthat/test-watch.R` cover: |
| 119 | +- Basic start/stop lifecycle |
| 120 | +- Multiple watched paths |
| 121 | +- Callbacks with rlang-style formulas |
| 122 | +- Unicode/international filenames (Japanese, French, Chinese characters) |
| 123 | +- Error handling (negative latency) |
| 124 | +- Some tests skip on aarch64 unless NOT_CRAN=true |
0 commit comments