diff --git a/.Rbuildignore b/.Rbuildignore index fd19815..d77493b 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -13,5 +13,3 @@ ^\.vscode$ ^\.claude$ ^CLAUDE.md$ -^w-build$ -^w-install$ diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index e783e01..17b0fba 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -21,12 +21,11 @@ jobs: config: - {os: ubuntu-24.04-arm, r: 'devel', http-user-agent: 'release'} - {os: ubuntu-latest, r: 'release'} - - {os: ubuntu-latest, r: 'oldrel-1'} - {os: ubuntu-22.04, r: 'oldrel-3'} - {os: macOS-latest, r: 'release'} - {os: macos-15-intel, r: 'oldrel-2'} - {os: windows-latest, r: 'release'} - - {os: windows-latest, r: 'oldrel-4'} + - {os: windows-latest, r: 'oldrel-5'} env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 0cdc162..7a286c3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,6 @@ .Rhistory .RData .Ruserdata -/w-build/ -/w-install/ /docs/ /revdep/ /.claude/ diff --git a/CLAUDE.md b/CLAUDE.md index 6020b64..70e6575 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -74,17 +74,51 @@ The package uses GitHub Actions workflows in `.github/workflows/`: ### Build System -**Configure Scripts:** -- `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. -- `configure.win` (Windows non-UCRT): Compiles libfswatch from source for both x64 and i386 architectures -- `configure.ucrt` (Windows UCRT): Simplified version for modern Windows R builds -- All scripts generate `src/Makevars` with appropriate compiler flags - -**Key Dependencies:** -- libfswatch (bundled source in `src/fswatch/`) -- cmake (required for compiling libfswatch from source) -- pthread (for background monitoring thread) -- 'later' R package (for async callback execution) +The bundled libfswatch sources (`src/fswatch/`) are compiled **directly into the +package shared object** alongside `init.c`/`watcher.c` — no cmake, no static +archive, no separate library. Platform feature selection comes from a +hand-maintained config header, not host probing. + +**Configure scripts:** +- `configure` (Linux/macOS/other Unix): detects a system libfswatch + (`/usr/local`, `/usr`, Homebrew); if absent, sets up the in-place bundled + build. Probes for `-latomic` (ARM) and adds `-framework CoreServices` (macOS), + emits the `CXX_STD` line (only on R < 4.3), and substitutes `src/Makevars.in` + → `src/Makevars`. +- `configure.ucrt` (Windows UCRT): computes only the `CXX_STD` line, + substituting `src/Makevars.ucrt.in` → `src/Makevars.ucrt`. +- Legacy non-UCRT Windows has no `configure.win`; it uses the static + `src/Makevars.win`, and `Biarch: true` keeps the dual i386/x64 build. + +**Key pieces:** +- `src/fswatch/.../libfswatch_config.h`: hand-maintained; selects `HAVE_*` + features from compiler platform macros (replaces the cmake/autotools probe). +- `src/Makevars{.in,.win,.ucrt.in}`: declare the bundled objects and carry one + portable explicit compile rule per object (no GNU-make extensions). The POSIX + object list is `tools/fsw_objects_posix.list`; the Windows object list is + separate because those files include ``. +- `src/link.cpp`: empty `.cpp` that forces R to link with the C++ linker. +- `tools/update_libfswatch.sh`: re-vendors libfswatch and regenerates the config + header, object list, and Makevars; calls `tools/patch_libfswatch.sh`. +- `tools/patch_libfswatch.sh`: idempotent source patches — a null-format guard + in `string_utils`, self-guards on the fsevents/inotify/fanotify monitors, and + neutering of libfswatch's stdout/stderr/cerr logging (for the R CMD check + "compiled code" policy). + +**Key dependencies:** an R C/C++ toolchain plus `make`; pthread; the +'later' R package. A system libfswatch is used automatically when present. + +### C++ Standard and Minimum R Version + +The bundled libfswatch uses `std::filesystem` (in `path_utils`, `poll_monitor`, +and every monitor's `scan()`), which mandates **C++17** — there is no portable +pre-C++17 substitute. Because the package now compiles libfswatch itself rather +than via cmake, it relies on R's own `CXX_STD = CXX17`, support for which was +added in **R 3.5.0**. That is precisely why `DESCRIPTION` requires +`R (>= 3.5)`: it is the lowest R that can build the package (R < 3.5 cannot +request C++17), not an arbitrary floor. `configure`/`configure.ucrt` emit +`CXX_STD = CXX17` only on R < 4.3, since R >= 4.3 defaults to C++17 and +specifying it then trips a CRAN "drop specification unless essential" NOTE. ### Event Filtering @@ -102,12 +136,13 @@ The package filters filesystem events to only report main event types (Created, ### Windows - Uses ReadDirectoryChangesW API (always recursive) - Windows latency has been specifically addressed (see NEWS.md - patch in v0.1.4.9000) -- Builds require cmake and compile libfswatch from bundled source +- Compiles the bundled libfswatch in place: UCRT via `Makevars.ucrt` (generated + by `configure.ucrt`), legacy non-UCRT via the static `Makevars.win` (gcc 8.3 + needs `-lstdc++fs` for `std::filesystem`); `Biarch: true` builds i386 + x64 ### macOS - Uses FSEvents API (always recursive) -- Can use system libfswatch if installed via homebrew/MacPorts -- MACOSX_DEPLOYMENT_TARGET is automatically extracted from compiler flags +- Links `-framework CoreServices`; can use a system libfswatch (Homebrew/MacPorts) ### Linux - Uses inotify API diff --git a/DESCRIPTION b/DESCRIPTION index 048b09d..5a5ee2e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -34,5 +34,5 @@ Config/roxygen2/version: 8.0.0 Config/testthat/edition: 3 Config/usethis/last-upkeep: 2025-04-23 Encoding: UTF-8 -SystemRequirements: 'libfswatch', or 'cmake' to compile from package - sources +SystemRequirements: 'libfswatch' (optional, bundled copy compiled + otherwise) diff --git a/NEWS.md b/NEWS.md index 6ae5fe2..9db5967 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # watcher (development version) +* Building the bundled 'libfswatch' from source no longer requires 'cmake', now directly using the R C/C++ toolchain. + A system 'libfswatch' is still used when one is available. + # watcher 0.1.6 * Updates bundled 'libfswatch' source to 1.20.1 release. diff --git a/README.Rmd b/README.Rmd index b3a65da..dd83edf 100644 --- a/README.Rmd +++ b/README.Rmd @@ -53,7 +53,6 @@ watcher requires the 'libfswatch' library. - On Linux / MacOS, an installed version will be used if found in the standard filesystem locations. - On Windows, or if not found, the bundled version of 'libfswatch' 1.20.1 will be compiled from source. -- Source compilation of the library requires 'cmake'. ## Quick Start diff --git a/README.md b/README.md index b841cdc..90c2564 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,6 @@ watcher requires the ‘libfswatch’ library. standard filesystem locations. - On Windows, or if not found, the bundled version of ‘libfswatch’ 1.20.1 will be compiled from source. -- Source compilation of the library requires ‘cmake’. ## Quick Start diff --git a/cleanup b/cleanup index abb582c..a0633de 100755 --- a/cleanup +++ b/cleanup @@ -1,2 +1,3 @@ #!/bin/sh -rm -rf src/Makevars w-build w-install +rm -f src/Makevars src/*.so +find src -name '*.o' -delete 2>/dev/null diff --git a/cleanup.win b/cleanup.win index 9c75dc1..6bab7a1 100755 --- a/cleanup.win +++ b/cleanup.win @@ -1 +1,2 @@ -rm -rf src/Makevars w-build w-install +rm -f src/Makevars src/Makevars.ucrt src/*.dll +find src -name '*.o' -delete 2>/dev/null diff --git a/codecov.yml b/codecov.yml index 04c5585..bf2a76c 100644 --- a/codecov.yml +++ b/codecov.yml @@ -12,3 +12,5 @@ coverage: target: auto threshold: 1% informational: true +ignore: + - "src/fswatch" diff --git a/configure b/configure index dda384a..1a8a350 100755 --- a/configure +++ b/configure @@ -1,22 +1,8 @@ #!/bin/sh # Initialise -PKG_CFLAGS="" PKG_LIBS="-lpthread" -# Helper function: detect cmake -detect_cmake() { - if which cmake > /dev/null 2>&1; then - return 0 - fi - export PATH=$PATH:/Applications/CMake.app/Contents/bin - if which cmake > /dev/null 2>&1; then - return 0 - fi - echo "Required 'cmake' not found" - exit 1 -} - # Helper function: find library paths # Usage: find_lib_paths # Sets: LIB_CFLAGS, LIB_LIBS @@ -51,9 +37,11 @@ CPPFLAGS=`"${R_HOME}/bin/R" CMD config CPPFLAGS` LDFLAGS=`"${R_HOME}/bin/R" CMD config LDFLAGS` export CC CFLAGS CPPFLAGS LDFLAGS -if [ -z "$MACOSX_DEPLOYMENT_TARGET" ]; then - export MACOSX_DEPLOYMENT_TARGET=`echo $CC | sed -En 's/.*-version-min=([0-9][0-9.]*).*/\1/p'` -fi +# C++ standard: the bundled libfswatch requires C++17. R >= 4.3 compiles C++ as +# C++17 by default, so requesting it explicitly is redundant and trips a CRAN +# "please drop specification unless essential" NOTE. Only emit CXX_STD on older +# R, where the default is below C++17. +CXX_STD_LINE=`"${R_HOME}/bin/Rscript" -e 'cat(if (getRversion() < "4.3.0") "CXX_STD = CXX17" else "")'` # Detect -latomic linker flag for ARM architectures (Raspberry Pi etc.) echo "#include @@ -66,14 +54,14 @@ if [ $? -ne 0 ]; then PKG_LIBS="$PKG_LIBS -latomic" fi -# Determine whether to compile bundled library +# Determine whether to compile the bundled library compile_fswatch=0 if [ -n "$WATCHER_LIBS" ]; then echo "WATCHER_LIBS is set... building from source" compile_fswatch=1 else - # Find libfswatch + # Find a system libfswatch find_lib_paths "fswatch" "libfswatch" "-lfswatch" FSWATCH_CFLAGS="$LIB_CFLAGS" FSWATCH_LIBS="$LIB_LIBS" @@ -88,36 +76,37 @@ int main() { compile_fswatch=1 else echo "Found 'libfswatch' $FSWATCH_CFLAGS" - PKG_CFLAGS="$FSWATCH_CFLAGS $PKG_CFLAGS" PKG_LIBS="$FSWATCH_LIBS $PKG_LIBS" fi fi -# Compile libfswatch if needed -if [ $compile_fswatch -eq 1 ]; then - echo "Compiling 'libfswatch' from source ..." - detect_cmake - cmake -S src/fswatch -B w-build \ - -DCMAKE_INSTALL_PREFIX=w-install \ - -DCMAKE_INSTALL_LIBDIR=lib \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_POSITION_INDEPENDENT_CODE=1 \ - -DCMAKE_COLOR_MAKEFILE=0 \ - -DCMAKE_INSTALL_MESSAGE=NEVER \ - -DBUILD_LIBS_ONLY=1 \ - -DUSE_NLS=0 - cmake --build w-build --target install - rm -rf w-build -fi - -# Set flags for bundled library -if [ -d "w-install/lib" ]; then - PKG_CFLAGS="-I../w-install/include $PKG_CFLAGS" - PKG_LIBS="../w-install/lib/libfswatch.a $PKG_LIBS" +if [ "$compile_fswatch" -eq 1 ]; then + # Compile the bundled libfswatch in place: put its source tree on the include + # path and build the POSIX object list (common core + the self-guarding POSIX + # monitors). The monitors self-guard on platform macros, so no uname-based + # source selection is needed. + echo "Compiling bundled 'libfswatch' in place ..." + PKG_CPPFLAGS="-Ifswatch/libfswatch/src" + # Single whitespace-separated line: embedded newlines would terminate the make + # variable assignment. + FSW_OBJECTS=`tr '\n' ' ' < tools/fsw_objects_posix.list | sed 's/ *$//'` + case `uname -s` in + Darwin) PKG_LIBS="$PKG_LIBS -framework CoreServices" ;; + esac +else + # System libfswatch: compile against ITS headers only. The bundled tree is + # deliberately kept off the include path so the package compiles against the + # same libfswatch version it links. No bundled objects are built. + PKG_CPPFLAGS="$FSWATCH_CFLAGS" + FSW_OBJECTS="" fi # Write to Makevars -sed -e "s|@cflags@|$PKG_CFLAGS|" -e "s|@libs@|$PKG_LIBS|" src/Makevars.in > src/Makevars +sed -e "s|@cppflags@|$PKG_CPPFLAGS|" \ + -e "s|@libs@|$PKG_LIBS|" \ + -e "s|@fsw_objects@|$FSW_OBJECTS|" \ + -e "s|@cxx_std@|$CXX_STD_LINE|" \ + src/Makevars.in > src/Makevars # Success exit 0 diff --git a/configure.ucrt b/configure.ucrt index 89cbfca..cddf6ee 100755 --- a/configure.ucrt +++ b/configure.ucrt @@ -1,22 +1,15 @@ -# Find compiler and export flags -CC=`"${R_HOME}/bin/R" CMD config CC` -CFLAGS=`"${R_HOME}/bin/R" CMD config CFLAGS` -CPPFLAGS=`"${R_HOME}/bin/R" CMD config CPPFLAGS` -LDFLAGS=`"${R_HOME}/bin/R" CMD config LDFLAGS` -export CC CFLAGS CPPFLAGS LDFLAGS +#!/bin/sh -echo "Compiling 'libfswatch' from source ..." -cmake -S src/fswatch -B w-build -G "Unix Makefiles" \ - -DCMAKE_INSTALL_PREFIX=w-install \ - -DCMAKE_INSTALL_LIBDIR=lib \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_POSITION_INDEPENDENT_CODE=1 \ - -DCMAKE_COLOR_MAKEFILE=0 \ - -DCMAKE_INSTALL_MESSAGE=NEVER \ - -DBUILD_LIBS_ONLY=1 \ - -DUSE_NLS=0 -cmake --build w-build --target install -rm -rf w-build +# UCRT (Rtools42+) configure: the only thing to compute on Windows is the C++ +# standard request. R >= 4.3 compiles C++ as C++17 by default, so specifying +# CXX_STD = CXX17 explicitly trips a CRAN "please drop specification unless +# essential" NOTE. Emit CXX_STD only on older R (R 4.2), where the default is +# below the C++17 that the bundled libfswatch needs. Everything else in +# Makevars.ucrt is static (legacy Makevars.win stays fully static -- Rtools40 +# only ever serves R <= 4.1). + +CXX_STD_LINE=`"${R_HOME}/bin/Rscript" -e 'cat(if (getRversion() < "4.3.0") "CXX_STD = CXX17" else "")'` + +sed -e "s|@cxx_std@|$CXX_STD_LINE|" src/Makevars.ucrt.in > src/Makevars.ucrt -# Success exit 0 diff --git a/configure.win b/configure.win deleted file mode 100755 index 12ac227..0000000 --- a/configure.win +++ /dev/null @@ -1,30 +0,0 @@ -for ARCH in x64 i386; do - - if [ -e "${R_HOME}/bin/${ARCH}/R" ]; then - CC=`"${R_HOME}/bin/${ARCH}/R" CMD config CC` - CFLAGS=`"${R_HOME}/bin/${ARCH}/R" CMD config CFLAGS` - CPPFLAGS=`"${R_HOME}/bin/${ARCH}/R" CMD config CPPFLAGS` - LDFLAGS=`"${R_HOME}/bin/${ARCH}/R" CMD config LDFLAGS` - export CC CFLAGS CPPFLAGS LDFLAGS - - echo "Compiling 'libfswatch' from source for ${ARCH} ..." - cmake -S src/fswatch -B w-build -G "Unix Makefiles" \ - -DCMAKE_INSTALL_PREFIX=w-install/${ARCH} \ - -DCMAKE_INSTALL_LIBDIR=lib \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_POSITION_INDEPENDENT_CODE=1 \ - -DCMAKE_COLOR_MAKEFILE=0 \ - -DCMAKE_INSTALL_MESSAGE=NEVER \ - -DBUILD_LIBS_ONLY=1 \ - -DUSE_NLS=0 - cmake --build w-build --target install - rm -rf w-build - - else - echo "Note: ${ARCH} not installed, skipping ..." - fi - -done - -# Success -exit 0 diff --git a/src/.gitignore b/src/.gitignore index e838631..0f6c845 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -2,3 +2,4 @@ *.so *.dll Makevars +Makevars.ucrt diff --git a/src/Makevars.in b/src/Makevars.in index 7530c82..2c9d70f 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -1,17 +1,73 @@ -PKG_CFLAGS = @cflags@ $(C_VISIBILITY) +# Generated by tools/update_libfswatch.sh -- do not edit by hand. +# +# Compile the bundled libfswatch sources directly into the package shared object +# (no cmake, no static archive). configure substitutes the include flags, the +# link flags, the bundled object list (empty when a system libfswatch is used) +# and the C++ standard line (a CXX_STD request set only on R < 4.3 -- see +# configure). +# +# PKG_CPPFLAGS carries the include path so it reaches BOTH the C package files +# and the bundled C++ via $(ALL_CPPFLAGS). +PKG_CPPFLAGS = @cppflags@ +PKG_CFLAGS = $(C_VISIBILITY) +PKG_CXXFLAGS = $(CXX_VISIBILITY) PKG_LIBS = @libs@ -SOURCES = init.c watcher.c -OBJECTS = $(SOURCES:.c=.o) +@cxx_std@ -.PHONY: all cleanup clean +# Top-level package sources (built by R's own suffix rules) +P_OBJECTS = init.o watcher.o +# Bundled libfswatch objects (empty when a system libfswatch is used) +FSW_OBJECTS = @fsw_objects@ -all: cleanup +OBJECTS = $(P_OBJECTS) $(FSW_OBJECTS) -cleanup: $(SHLIB) - @rm -rf ../w-install +FSW = fswatch/libfswatch/src/libfswatch + +# `all:` MUST be the first target: an explicit object rule below would otherwise +# become make's default goal when R CMD SHLIB invokes make with no target. +all: $(SHLIB) $(SHLIB): $(OBJECTS) +# --- Explicit compile rules for the bundled (subdirectory) sources ----------- +# One rule per object; recipe identical to R's own .cpp.o suffix rule. Each +# names its source explicitly and uses only portable make variables, so any +# POSIX make builds them (no automatic-variable source refs, no pattern rules, +# no directory search, no file inclusion). +$(FSW)/c/cevent.o: $(FSW)/c/cevent.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c/cevent.cpp -o $@ +$(FSW)/c/libfswatch.o: $(FSW)/c/libfswatch.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c/libfswatch.cpp -o $@ +$(FSW)/c/libfswatch_log.o: $(FSW)/c/libfswatch_log.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c/libfswatch_log.cpp -o $@ +$(FSW)/c++/event.o: $(FSW)/c++/event.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/event.cpp -o $@ +$(FSW)/c++/filter.o: $(FSW)/c++/filter.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/filter.cpp -o $@ +$(FSW)/c++/libfswatch_exception.o: $(FSW)/c++/libfswatch_exception.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/libfswatch_exception.cpp -o $@ +$(FSW)/c++/monitor.o: $(FSW)/c++/monitor.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/monitor.cpp -o $@ +$(FSW)/c++/monitor_factory.o: $(FSW)/c++/monitor_factory.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/monitor_factory.cpp -o $@ +$(FSW)/c++/path_utils.o: $(FSW)/c++/path_utils.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/path_utils.cpp -o $@ +$(FSW)/c++/poll_monitor.o: $(FSW)/c++/poll_monitor.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/poll_monitor.cpp -o $@ +$(FSW)/c++/string/string_utils.o: $(FSW)/c++/string/string_utils.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/string/string_utils.cpp -o $@ +$(FSW)/c++/fsevents_monitor.o: $(FSW)/c++/fsevents_monitor.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/fsevents_monitor.cpp -o $@ +$(FSW)/c++/kqueue_monitor.o: $(FSW)/c++/kqueue_monitor.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/kqueue_monitor.cpp -o $@ +$(FSW)/c++/inotify_monitor.o: $(FSW)/c++/inotify_monitor.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/inotify_monitor.cpp -o $@ +$(FSW)/c++/fanotify_monitor.o: $(FSW)/c++/fanotify_monitor.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/fanotify_monitor.cpp -o $@ +$(FSW)/c++/fen_monitor.o: $(FSW)/c++/fen_monitor.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/fen_monitor.cpp -o $@ + +.PHONY: all clean clean: rm -f $(OBJECTS) $(SHLIB) diff --git a/src/Makevars.ucrt b/src/Makevars.ucrt deleted file mode 100644 index 8ab10a1..0000000 --- a/src/Makevars.ucrt +++ /dev/null @@ -1,17 +0,0 @@ -PKG_CFLAGS = -I../w-install/include $(C_VISIBILITY) -PKG_LIBS = ../w-install/lib/libfswatch.a -pthread - -SOURCES = init.c watcher.c -OBJECTS = $(SOURCES:.c=.o) - -.PHONY: all cleaup clean - -all: cleanup - -cleanup: $(SHLIB) - @rm -rf ../w-install - -$(SHLIB): $(OBJECTS) - -clean: - rm -f $(OBJECTS) $(SHLIB) diff --git a/src/Makevars.ucrt.in b/src/Makevars.ucrt.in new file mode 100644 index 0000000..ae844ee --- /dev/null +++ b/src/Makevars.ucrt.in @@ -0,0 +1,63 @@ +# Generated by tools/update_libfswatch.sh -- do not edit by hand. +# Template: configure.ucrt fills in the C++ standard line to produce +# src/Makevars.ucrt. +# +# Rtools42/43/44 (gcc 10/12/13). Identical to Makevars.win except that +# std::filesystem is folded into libstdc++, so -lstdc++fs is not needed, and the +# CXX_STD request is emitted only on R < 4.3 (R >= 4.3 defaults to C++17; +# specifying it then trips a CRAN "please drop specification" NOTE). +PKG_CPPFLAGS = -Ifswatch/libfswatch/src +PKG_CFLAGS = $(C_VISIBILITY) +PKG_CXXFLAGS = $(CXX_VISIBILITY) +PKG_LIBS = -pthread + +@cxx_std@ + +P_OBJECTS = init.o watcher.o +FSW = fswatch/libfswatch/src/libfswatch +FSW_OBJECTS = $(FSW)/c/cevent.o $(FSW)/c/libfswatch.o $(FSW)/c/libfswatch_log.o $(FSW)/c++/event.o $(FSW)/c++/filter.o $(FSW)/c++/libfswatch_exception.o $(FSW)/c++/monitor.o $(FSW)/c++/monitor_factory.o $(FSW)/c++/path_utils.o $(FSW)/c++/poll_monitor.o $(FSW)/c++/string/string_utils.o $(FSW)/c++/windows_monitor.o $(FSW)/c++/windows/win_directory_change_event.o $(FSW)/c++/windows/win_error_message.o $(FSW)/c++/windows/win_handle.o $(FSW)/c++/windows/win_paths.o $(FSW)/c++/windows/win_strings.o + +OBJECTS = $(P_OBJECTS) $(FSW_OBJECTS) + +all: $(SHLIB) + +$(SHLIB): $(OBJECTS) + +$(FSW)/c/cevent.o: $(FSW)/c/cevent.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c/cevent.cpp -o $@ +$(FSW)/c/libfswatch.o: $(FSW)/c/libfswatch.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c/libfswatch.cpp -o $@ +$(FSW)/c/libfswatch_log.o: $(FSW)/c/libfswatch_log.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c/libfswatch_log.cpp -o $@ +$(FSW)/c++/event.o: $(FSW)/c++/event.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/event.cpp -o $@ +$(FSW)/c++/filter.o: $(FSW)/c++/filter.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/filter.cpp -o $@ +$(FSW)/c++/libfswatch_exception.o: $(FSW)/c++/libfswatch_exception.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/libfswatch_exception.cpp -o $@ +$(FSW)/c++/monitor.o: $(FSW)/c++/monitor.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/monitor.cpp -o $@ +$(FSW)/c++/monitor_factory.o: $(FSW)/c++/monitor_factory.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/monitor_factory.cpp -o $@ +$(FSW)/c++/path_utils.o: $(FSW)/c++/path_utils.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/path_utils.cpp -o $@ +$(FSW)/c++/poll_monitor.o: $(FSW)/c++/poll_monitor.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/poll_monitor.cpp -o $@ +$(FSW)/c++/string/string_utils.o: $(FSW)/c++/string/string_utils.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/string/string_utils.cpp -o $@ +$(FSW)/c++/windows_monitor.o: $(FSW)/c++/windows_monitor.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/windows_monitor.cpp -o $@ +$(FSW)/c++/windows/win_directory_change_event.o: $(FSW)/c++/windows/win_directory_change_event.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/windows/win_directory_change_event.cpp -o $@ +$(FSW)/c++/windows/win_error_message.o: $(FSW)/c++/windows/win_error_message.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/windows/win_error_message.cpp -o $@ +$(FSW)/c++/windows/win_handle.o: $(FSW)/c++/windows/win_handle.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/windows/win_handle.cpp -o $@ +$(FSW)/c++/windows/win_paths.o: $(FSW)/c++/windows/win_paths.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/windows/win_paths.cpp -o $@ +$(FSW)/c++/windows/win_strings.o: $(FSW)/c++/windows/win_strings.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/windows/win_strings.cpp -o $@ + +.PHONY: all clean +clean: + rm -f $(OBJECTS) $(SHLIB) diff --git a/src/Makevars.win b/src/Makevars.win index b1dd878..8dea2d0 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -1,17 +1,65 @@ -PKG_CFLAGS = -I../w-install${R_ARCH}/include $(C_VISIBILITY) -PKG_LIBS = ../w-install${R_ARCH}/lib/libfswatch.a -pthread -lstdc++fs +# Generated by tools/update_libfswatch.sh -- do not edit by hand. +# +# Rtools40 (gcc 8.3), dual i386 + x64. Always compiles the bundled libfswatch +# (no system-library path on Windows). std::filesystem is used in the common +# core, so -lstdc++fs is required on gcc 8.x/9.x. +# +# CXX_STD stays static here: Rtools40 only ever serves R <= 4.1 (< 4.3), whose +# default is below C++17, so requesting it is always needed and never trips the +# CRAN "please drop specification" NOTE. (UCRT is conditional -- see +# Makevars.ucrt.in / configure.ucrt.) +PKG_CPPFLAGS = -Ifswatch/libfswatch/src +PKG_CFLAGS = $(C_VISIBILITY) +PKG_CXXFLAGS = $(CXX_VISIBILITY) +PKG_LIBS = -pthread -lstdc++fs -SOURCES = init.c watcher.c -OBJECTS = $(SOURCES:.c=.o) +CXX_STD = CXX17 -.PHONY: all cleanup clean +P_OBJECTS = init.o watcher.o +FSW = fswatch/libfswatch/src/libfswatch +FSW_OBJECTS = $(FSW)/c/cevent.o $(FSW)/c/libfswatch.o $(FSW)/c/libfswatch_log.o $(FSW)/c++/event.o $(FSW)/c++/filter.o $(FSW)/c++/libfswatch_exception.o $(FSW)/c++/monitor.o $(FSW)/c++/monitor_factory.o $(FSW)/c++/path_utils.o $(FSW)/c++/poll_monitor.o $(FSW)/c++/string/string_utils.o $(FSW)/c++/windows_monitor.o $(FSW)/c++/windows/win_directory_change_event.o $(FSW)/c++/windows/win_error_message.o $(FSW)/c++/windows/win_handle.o $(FSW)/c++/windows/win_paths.o $(FSW)/c++/windows/win_strings.o -all: cleanup +OBJECTS = $(P_OBJECTS) $(FSW_OBJECTS) -cleanup: $(SHLIB) - @rm -rf ../w-install${R_ARCH} +all: $(SHLIB) $(SHLIB): $(OBJECTS) +$(FSW)/c/cevent.o: $(FSW)/c/cevent.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c/cevent.cpp -o $@ +$(FSW)/c/libfswatch.o: $(FSW)/c/libfswatch.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c/libfswatch.cpp -o $@ +$(FSW)/c/libfswatch_log.o: $(FSW)/c/libfswatch_log.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c/libfswatch_log.cpp -o $@ +$(FSW)/c++/event.o: $(FSW)/c++/event.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/event.cpp -o $@ +$(FSW)/c++/filter.o: $(FSW)/c++/filter.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/filter.cpp -o $@ +$(FSW)/c++/libfswatch_exception.o: $(FSW)/c++/libfswatch_exception.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/libfswatch_exception.cpp -o $@ +$(FSW)/c++/monitor.o: $(FSW)/c++/monitor.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/monitor.cpp -o $@ +$(FSW)/c++/monitor_factory.o: $(FSW)/c++/monitor_factory.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/monitor_factory.cpp -o $@ +$(FSW)/c++/path_utils.o: $(FSW)/c++/path_utils.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/path_utils.cpp -o $@ +$(FSW)/c++/poll_monitor.o: $(FSW)/c++/poll_monitor.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/poll_monitor.cpp -o $@ +$(FSW)/c++/string/string_utils.o: $(FSW)/c++/string/string_utils.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/string/string_utils.cpp -o $@ +$(FSW)/c++/windows_monitor.o: $(FSW)/c++/windows_monitor.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/windows_monitor.cpp -o $@ +$(FSW)/c++/windows/win_directory_change_event.o: $(FSW)/c++/windows/win_directory_change_event.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/windows/win_directory_change_event.cpp -o $@ +$(FSW)/c++/windows/win_error_message.o: $(FSW)/c++/windows/win_error_message.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/windows/win_error_message.cpp -o $@ +$(FSW)/c++/windows/win_handle.o: $(FSW)/c++/windows/win_handle.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/windows/win_handle.cpp -o $@ +$(FSW)/c++/windows/win_paths.o: $(FSW)/c++/windows/win_paths.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/windows/win_paths.cpp -o $@ +$(FSW)/c++/windows/win_strings.o: $(FSW)/c++/windows/win_strings.cpp + $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $(FSW)/c++/windows/win_strings.cpp -o $@ + +.PHONY: all clean clean: rm -f $(OBJECTS) $(SHLIB) diff --git a/src/fswatch/CMakeLists.txt b/src/fswatch/CMakeLists.txt deleted file mode 100644 index 6fff8ec..0000000 --- a/src/fswatch/CMakeLists.txt +++ /dev/null @@ -1,83 +0,0 @@ -# -# Copyright (c) 2014-2026 Enrico M. Crisostomo -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 3, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . -# -cmake_minimum_required(VERSION 3.14) -project(fswatch VERSION 1.20.1 LANGUAGES C CXX) - -set(VERSION_MODIFIER "") -set(FULL_VERSION "${PROJECT_VERSION}${VERSION_MODIFIER}") - -#@formatter:off -set(PACKAGE "${PROJECT_NAME}") -set(PACKAGE_NAME "${PACKAGE}") -set(PACKAGE_VERSION "${FULL_VERSION}") -set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") -set(PACKAGE_AUTHOR "enrico.m.crisostomo@gmail.com") -set(PACKAGE_BUGREPORT "${PACKAGE_AUTHOR}") -set(PACKAGE_TARNAME "${PACKAGE}") -set(PACKAGE_URL "https://github.com/emcrisostomo/${PACKAGE}") -set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/share/locale" CACHE FILEPATH "locale dir") -#@formatter:on - -if (NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 17) -endif () -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -# Add option to choose between shared and static libraries -option(BUILD_SHARED_LIBS "Build shared libraries" OFF) -# Add option to build libfswatch only (without fswatch or tests) -option(BUILD_LIBS_ONLY "Build libfswatch only" OFF) - -# include modules -include(FindGettext) -include(FindIntl) -include(CheckIncludeFileCXX) -include(CheckStructHasMember) -include(CheckCXXSymbolExists) -include(CTest) - -# check for gettext and libintl -check_include_file_cxx(getopt.h HAVE_GETOPT_H) - -if (HAVE_GETOPT_H) - check_cxx_symbol_exists(getopt_long getopt.h HAVE_GETOPT_LONG) -endif (HAVE_GETOPT_H) - -# If both gettext and libintl are found, define the USE_NLS variable to -# optionally disable them -if (Intl_FOUND AND GETTEXT_FOUND) - option(USE_NLS "Use NLS" ON) -endif () - -if (USE_NLS) - set(ENABLE_NLS 1) - - # gettext configuration - set(LINGUAS en en@quot en@boldquot it es) - - foreach (language ${LINGUAS}) - configure_file(${PROJECT_SOURCE_DIR}/po/${language}.po ${PROJECT_BINARY_DIR}/${language}.po COPYONLY) - endforeach () - - gettext_process_pot_file(po/${PACKAGE}.pot ALL INSTALL_DESTINATION share/locale LANGUAGES ${LINGUAS}) -endif () - -add_subdirectory(libfswatch) -if (NOT BUILD_LIBS_ONLY) -add_subdirectory(fswatch/src) -add_subdirectory(test/src) -endif () diff --git a/src/fswatch/libfswatch/CMakeLists.txt b/src/fswatch/libfswatch/CMakeLists.txt deleted file mode 100644 index c401d1d..0000000 --- a/src/fswatch/libfswatch/CMakeLists.txt +++ /dev/null @@ -1,236 +0,0 @@ -# -# Copyright (c) 2014-2026 Enrico M. Crisostomo -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 3, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . -# - -# Define symbols to conditionally define FSEvents flags -if(APPLE) - if (${CMAKE_SYSTEM_VERSION} VERSION_GREATER_EQUAL "9.0") - set(HAVE_MACOS_GE_10_5 1) - endif() - - if (${CMAKE_SYSTEM_VERSION} VERSION_GREATER_EQUAL "10.0") - set(HAVE_MACOS_GE_10_6 1) - endif() - - if (${CMAKE_SYSTEM_VERSION} VERSION_GREATER_EQUAL "11.0") - set(HAVE_MACOS_GE_10_7 1) - endif() - - if (${CMAKE_SYSTEM_VERSION} VERSION_GREATER_EQUAL "13.0") - set(HAVE_MACOS_GE_10_9 1) - endif() - - if (${CMAKE_SYSTEM_VERSION} VERSION_GREATER_EQUAL "14.0") - set(HAVE_MACOS_GE_10_10 1) - endif() - - if (${CMAKE_SYSTEM_VERSION} VERSION_GREATER_EQUAL "17.0") - set(HAVE_MACOS_GE_10_13 1) - endif() -endif() - -set(LIBFSWATCH_HEADER_FILES - src/libfswatch/c/cevent.h - src/libfswatch/c/cfilter.h - src/libfswatch/c/cmonitor.h - src/libfswatch/c/error.h - src/libfswatch/c/libfswatch.h - src/libfswatch/c/libfswatch_log.h - src/libfswatch/c/libfswatch_types.h - src/libfswatch/c++/event.hpp - src/libfswatch/c++/filter.hpp - src/libfswatch/c++/libfswatch_exception.hpp - src/libfswatch/c++/monitor.hpp - src/libfswatch/c++/monitor_factory.hpp - src/libfswatch/c++/path_utils.hpp - src/libfswatch/c++/poll_monitor.hpp - src/libfswatch/c++/string/string_utils.hpp - src/libfswatch/gettext.h - src/libfswatch/gettext_defs.h - ${CMAKE_CURRENT_BINARY_DIR}/libfswatch_config.h) - -set(LIB_SOURCE_FILES - src/libfswatch/c/cevent.cpp - src/libfswatch/c/libfswatch.cpp - src/libfswatch/c/libfswatch_log.cpp - src/libfswatch/c++/event.cpp - src/libfswatch/c++/filter.cpp - src/libfswatch/c++/libfswatch_exception.cpp - src/libfswatch/c++/monitor.cpp - src/libfswatch/c++/monitor_factory.cpp - src/libfswatch/c++/path_utils.cpp - src/libfswatch/c++/poll_monitor.cpp - src/libfswatch/c++/string/string_utils.cpp) - -check_struct_has_member("struct stat" st_mtime sys/stat.h HAVE_STRUCT_STAT_ST_MTIME) -check_struct_has_member("struct stat" st_mtimespec sys/stat.h HAVE_STRUCT_STAT_ST_MTIMESPEC) - -check_include_file_cxx(sys/inotify.h HAVE_SYS_INOTIFY_H) -check_include_file_cxx(sys/epoll.h HAVE_SYS_EPOLL_H) -check_include_file_cxx(sys/eventfd.h HAVE_SYS_EVENTFD_H) - -if (HAVE_SYS_INOTIFY_H AND HAVE_SYS_EPOLL_H AND HAVE_SYS_EVENTFD_H) - set(CMAKE_REQUIRED_DEFINITIONS_SAVED ${CMAKE_REQUIRED_DEFINITIONS}) - list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) - check_cxx_symbol_exists(inotify_init1 sys/inotify.h HAVE_INOTIFY_INIT1) - check_cxx_symbol_exists(epoll_create1 sys/epoll.h HAVE_EPOLL_CREATE1) - check_cxx_symbol_exists(eventfd sys/eventfd.h HAVE_EVENTFD) - set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS_SAVED}) -endif () - -if (HAVE_SYS_INOTIFY_H AND HAVE_SYS_EPOLL_H AND HAVE_SYS_EVENTFD_H AND - HAVE_INOTIFY_INIT1 AND HAVE_EPOLL_CREATE1 AND HAVE_EVENTFD) - set(HAVE_INOTIFY_MONITOR 1 CACHE INTERNAL "Linux inotify monitor is available") - set(LIBFSWATCH_HEADER_FILES - ${LIBFSWATCH_HEADER_FILES} - src/libfswatch/c++/inotify_monitor.hpp) - set(LIB_SOURCE_FILES - ${LIB_SOURCE_FILES} - src/libfswatch/c++/inotify_monitor.cpp) -endif () - -check_include_file_cxx(sys/fanotify.h HAVE_SYS_FANOTIFY_H) -check_include_file_cxx(sys/epoll.h HAVE_SYS_EPOLL_H) -check_include_file_cxx(sys/eventfd.h HAVE_SYS_EVENTFD_H) - -if (HAVE_SYS_FANOTIFY_H AND HAVE_SYS_EPOLL_H AND HAVE_SYS_EVENTFD_H) - check_cxx_symbol_exists(fanotify_init sys/fanotify.h HAVE_FANOTIFY_INIT) - check_cxx_symbol_exists(fanotify_mark sys/fanotify.h HAVE_FANOTIFY_MARK) - check_cxx_symbol_exists(epoll_create1 sys/epoll.h HAVE_EPOLL_CREATE1) - check_cxx_symbol_exists(epoll_ctl sys/epoll.h HAVE_EPOLL_CTL) - check_cxx_symbol_exists(epoll_wait sys/epoll.h HAVE_EPOLL_WAIT) - check_cxx_symbol_exists(eventfd sys/eventfd.h HAVE_EVENTFD) - check_cxx_symbol_exists(FAN_REPORT_DFID_NAME sys/fanotify.h HAVE_FAN_REPORT_DFID_NAME) - check_cxx_symbol_exists(FAN_CREATE sys/fanotify.h HAVE_FAN_CREATE) - check_cxx_symbol_exists(FAN_DELETE sys/fanotify.h HAVE_FAN_DELETE) - check_cxx_symbol_exists(FAN_MOVED_FROM sys/fanotify.h HAVE_FAN_MOVED_FROM) - check_cxx_symbol_exists(FAN_MOVED_TO sys/fanotify.h HAVE_FAN_MOVED_TO) - check_cxx_symbol_exists(FAN_EVENT_ON_CHILD sys/fanotify.h HAVE_FAN_EVENT_ON_CHILD) - check_cxx_symbol_exists(FAN_Q_OVERFLOW sys/fanotify.h HAVE_FAN_Q_OVERFLOW) - - if (HAVE_FANOTIFY_INIT AND HAVE_FANOTIFY_MARK AND - HAVE_EPOLL_CREATE1 AND HAVE_EPOLL_CTL AND HAVE_EPOLL_WAIT AND HAVE_EVENTFD AND - HAVE_FAN_REPORT_DFID_NAME AND HAVE_FAN_CREATE AND HAVE_FAN_DELETE AND - HAVE_FAN_MOVED_FROM AND HAVE_FAN_MOVED_TO AND - HAVE_FAN_EVENT_ON_CHILD AND HAVE_FAN_Q_OVERFLOW) - set(HAVE_FANOTIFY ON CACHE BOOL "Enable fanotify support") - set(LIBFSWATCH_HEADER_FILES - ${LIBFSWATCH_HEADER_FILES} - src/libfswatch/c++/fanotify_monitor.hpp) - set(LIB_SOURCE_FILES - ${LIB_SOURCE_FILES} - src/libfswatch/c++/fanotify_monitor.cpp) - endif () -endif () - -check_include_file_cxx(sys/event.h HAVE_SYS_EVENT_H) - -if (HAVE_SYS_EVENT_H) - set(LIBFSWATCH_HEADER_FILES - ${LIBFSWATCH_HEADER_FILES} - src/libfswatch/c++/kqueue_monitor.hpp) - set(LIB_SOURCE_FILES - ${LIB_SOURCE_FILES} - src/libfswatch/c++/kqueue_monitor.cpp) -endif (HAVE_SYS_EVENT_H) - -check_include_file_cxx(port.h HAVE_PORT_H) - -if (HAVE_PORT_H) - set(LIBFSWATCH_HEADER_FILES - ${LIBFSWATCH_HEADER_FILES} - src/libfswatch/c++/fen_monitor.hpp) - set(LIB_SOURCE_FILES - ${LIB_SOURCE_FILES} - src/libfswatch/c++/fen_monitor.cpp) -endif (HAVE_PORT_H) - -check_cxx_symbol_exists(FindFirstChangeNotification windows.h HAVE_WINDOWS_HEADER) - -if (HAVE_WINDOWS_HEADER) - - set(LIBFSWATCH_HEADER_FILES - ${LIBFSWATCH_HEADER_FILES} - src/libfswatch/c++/windows/win_directory_change_event.hpp - src/libfswatch/c++/windows/win_error_message.hpp - src/libfswatch/c++/windows/win_handle.hpp - src/libfswatch/c++/windows/win_paths.hpp - src/libfswatch/c++/windows/win_strings.hpp - src/libfswatch/c++/windows_monitor.hpp) - - set(LIB_SOURCE_FILES - ${LIB_SOURCE_FILES} - src/libfswatch/c++/windows/win_directory_change_event.cpp - src/libfswatch/c++/windows/win_error_message.cpp - src/libfswatch/c++/windows/win_handle.cpp - src/libfswatch/c++/windows/win_paths.cpp - src/libfswatch/c++/windows/win_strings.cpp - src/libfswatch/c++/windows_monitor.cpp) - set(HAVE_WINDOWS ON CACHE BOOL "Enable Windows support") - -endif (HAVE_WINDOWS_HEADER) - -if (APPLE) - check_include_file_cxx(CoreServices/CoreServices.h HAVE_FSEVENTS_FILE_EVENTS) - - if (HAVE_FSEVENTS_FILE_EVENTS) - find_library(CORESERVICES_LIBRARY CoreServices) - - if (CORESERVICES_LIBRARY) - set(CMAKE_REQUIRED_LIBRARIES ${CORESERVICES_LIBRARY}) - check_symbol_exists(FSEventStreamSetDispatchQueue "CoreServices/CoreServices.h" HAVE_FSEVENTS_FSEVENTSTREAMSETDISPATCHQUEUE) - - if (HAVE_FSEVENTS_FSEVENTSTREAMSETDISPATCHQUEUE) - set(EXTRA_LIBS ${EXTRA_LIBS} ${CORESERVICES_LIBRARY}) - set(LIBFSWATCH_HEADER_FILES - ${LIBFSWATCH_HEADER_FILES} - src/libfswatch/c++/fsevents_monitor.hpp) - set(LIB_SOURCE_FILES - ${LIB_SOURCE_FILES} - src/libfswatch/c++/fsevents_monitor.cpp) - endif (HAVE_FSEVENTS_FSEVENTSTREAMSETDISPATCHQUEUE) - endif () - endif (HAVE_FSEVENTS_FILE_EVENTS) -endif (APPLE) - -# Add a configuration file processed by cmake -configure_file(libfswatch_config.in libfswatch_config.h) -# TODO: consider removing it -add_definitions(-DHAVE_LIBFSWATCH_CONFIG_H) - -add_library(libfswatch ${LIB_SOURCE_FILES} ${LIBFSWATCH_HEADER_FILES}) -set_target_properties(libfswatch PROPERTIES PREFIX "") - -# check for gettext and libintl -if (USE_NLS) - if (Intl_LIBRARIES) - target_link_libraries(libfswatch PRIVATE ${Intl_LIBRARIES}) - endif () -endif () - -target_include_directories(libfswatch PUBLIC src/libfswatch) -target_include_directories(libfswatch PUBLIC src) -target_include_directories(libfswatch PUBLIC ${Intl_INCLUDE_DIRS}) -target_include_directories(libfswatch PRIVATE ${PROJECT_BINARY_DIR}) -target_include_directories(libfswatch PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) -target_link_libraries(libfswatch PRIVATE ${EXTRA_LIBS}) - -install(TARGETS libfswatch LIBRARY DESTINATION lib) -# TODO: should migrate to target_source(file_set) to install headers -install(DIRECTORY src/libfswatch - DESTINATION include - FILES_MATCHING - PATTERN "*.hpp" - PATTERN "*.h") diff --git a/src/fswatch/libfswatch/libfswatch_config.in b/src/fswatch/libfswatch/libfswatch_config.in deleted file mode 100644 index 330feba..0000000 --- a/src/fswatch/libfswatch/libfswatch_config.in +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2026 Enrico M. Crisostomo - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#cmakedefine ENABLE_NLS @ENABLE_NLS@ -#cmakedefine LOCALEDIR "@LOCALEDIR@" -#cmakedefine PACKAGE "@PACKAGE@" -#cmakedefine PACKAGE_NAME "@PACKAGE_NAME@" -#cmakedefine PACKAGE_VERSION "@PACKAGE_VERSION@" -#cmakedefine PACKAGE_STRING "@PACKAGE_STRING@" -#cmakedefine PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@" -#cmakedefine PACKAGE_TARNAME "@PACKAGE_TARNAME@" -#cmakedefine PACKAGE_URL "@PACKAGE_URL@" -#cmakedefine HAVE_GETOPT_LONG - -#cmakedefine HAVE_FSEVENTS_FSEVENTSTREAMSETDISPATCHQUEUE -#cmakedefine HAVE_FANOTIFY -#cmakedefine HAVE_PORT_H -#cmakedefine HAVE_STRUCT_STAT_ST_MTIME -#cmakedefine HAVE_STRUCT_STAT_ST_MTIMESPEC -#cmakedefine HAVE_INOTIFY_MONITOR -#cmakedefine HAVE_SYS_EPOLL_H -#cmakedefine HAVE_SYS_EVENTFD_H -#cmakedefine HAVE_SYS_EVENT_H -#cmakedefine HAVE_SYS_FANOTIFY_H -#cmakedefine HAVE_SYS_INOTIFY_H -#cmakedefine HAVE_WINDOWS - -#cmakedefine HAVE_MACOS_GE_10_5 -#cmakedefine HAVE_MACOS_GE_10_6 -#cmakedefine HAVE_MACOS_GE_10_7 -#cmakedefine HAVE_MACOS_GE_10_9 -#cmakedefine HAVE_MACOS_GE_10_10 -#cmakedefine HAVE_MACOS_GE_10_13 diff --git a/src/fswatch/libfswatch/src/libfswatch.pc.in b/src/fswatch/libfswatch/src/libfswatch.pc.in deleted file mode 100644 index 34196ce..0000000 --- a/src/fswatch/libfswatch/src/libfswatch.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: libfswatch -Description: libfswatch -URL: https://github.com/emcrisostomo/fswatch/tree/master/libfswatch -Version: @VERSION@ -Requires: -Libs: -L${libdir} -lfswatch -Cflags: -I${includedir} diff --git a/src/fswatch/libfswatch/src/libfswatch/c++/fanotify_monitor.cpp b/src/fswatch/libfswatch/src/libfswatch/c++/fanotify_monitor.cpp index fa69760..75e2ff9 100644 --- a/src/fswatch/libfswatch/src/libfswatch/c++/fanotify_monitor.cpp +++ b/src/fswatch/libfswatch/src/libfswatch/c++/fanotify_monitor.cpp @@ -20,6 +20,8 @@ #include +#ifdef HAVE_FANOTIFY + #include "fanotify_monitor.hpp" #include "libfswatch/gettext_defs.h" #include "libfswatch_exception.hpp" @@ -734,3 +736,5 @@ namespace fsw } } } + +#endif /* WATCHER guard HAVE_FANOTIFY */ diff --git a/src/fswatch/libfswatch/src/libfswatch/c++/fsevents_monitor.cpp b/src/fswatch/libfswatch/src/libfswatch/c++/fsevents_monitor.cpp index f3862c4..112b093 100644 --- a/src/fswatch/libfswatch/src/libfswatch/c++/fsevents_monitor.cpp +++ b/src/fswatch/libfswatch/src/libfswatch/c++/fsevents_monitor.cpp @@ -14,6 +14,8 @@ * this program. If not, see . */ #include + +#ifdef HAVE_FSEVENTS_FSEVENTSTREAMSETDISPATCHQUEUE #include #include // isatty() #include // fileno() @@ -267,7 +269,6 @@ namespace fsw std::vector path_buffer(max_path_size); if (!CFStringGetCString(path, path_buffer.data(), max_path_size, kCFStringEncodingUTF8)) { - std::cerr << "Warning: Failed to convert CFStringRef to C string." << std::endl; continue; } @@ -363,3 +364,5 @@ namespace fsw streamFlags); } } + +#endif /* WATCHER guard HAVE_FSEVENTS_FSEVENTSTREAMSETDISPATCHQUEUE */ diff --git a/src/fswatch/libfswatch/src/libfswatch/c++/inotify_monitor.cpp b/src/fswatch/libfswatch/src/libfswatch/c++/inotify_monitor.cpp index a8077d8..7931464 100644 --- a/src/fswatch/libfswatch/src/libfswatch/c++/inotify_monitor.cpp +++ b/src/fswatch/libfswatch/src/libfswatch/c++/inotify_monitor.cpp @@ -19,6 +19,8 @@ #include +#ifdef HAVE_INOTIFY_MONITOR + #include "libfswatch/gettext_defs.h" #include "inotify_monitor.hpp" #include @@ -645,3 +647,5 @@ namespace fsw } } } + +#endif /* WATCHER guard HAVE_INOTIFY_MONITOR */ diff --git a/src/fswatch/libfswatch/src/libfswatch/c++/windows/win_directory_change_event.cpp b/src/fswatch/libfswatch/src/libfswatch/c++/windows/win_directory_change_event.cpp index a7d3be2..9dd93e5 100644 --- a/src/fswatch/libfswatch/src/libfswatch/c++/windows/win_directory_change_event.cpp +++ b/src/fswatch/libfswatch/src/libfswatch/c++/windows/win_directory_change_event.cpp @@ -148,7 +148,6 @@ namespace fsw } else { - cerr << _("File name unexpectedly empty.") << endl; } curr_entry = (currEntry->NextEntryOffset == 0) ? nullptr : curr_entry + currEntry->NextEntryOffset; diff --git a/src/fswatch/libfswatch/src/libfswatch/c++/windows_monitor.cpp b/src/fswatch/libfswatch/src/libfswatch/c++/windows_monitor.cpp index 12eaae5..e43e44c 100644 --- a/src/fswatch/libfswatch/src/libfswatch/c++/windows_monitor.cpp +++ b/src/fswatch/libfswatch/src/libfswatch/c++/windows_monitor.cpp @@ -108,7 +108,6 @@ namespace fsw if (!win_handle::is_valid(h)) { - fprintf(stderr, _("Invalid handle when opening %s.\n"), win_strings::wstring_to_string(path).c_str()); return false; } diff --git a/src/fswatch/libfswatch/src/libfswatch/c/libfswatch_log.cpp b/src/fswatch/libfswatch/src/libfswatch/c/libfswatch_log.cpp index bbcf4c0..0ca5126 100644 --- a/src/fswatch/libfswatch/src/libfswatch/c/libfswatch_log.cpp +++ b/src/fswatch/libfswatch/src/libfswatch/c/libfswatch_log.cpp @@ -13,61 +13,14 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -#include "libfswatch.h" +/* watcher: logging neutered. libfswatch's diagnostics must not write to + * stdout/stderr from compiled code; watcher never enables verbose logging, + * so these are no-ops in normal use. See tools/patch_libfswatch.sh. */ #include "libfswatch_log.h" -#include "../c++/string/string_utils.hpp" -#include -using namespace std; -using namespace fsw; - -void fsw_log(const char *msg) -{ - if (fsw_is_verbose()) printf("%s", msg); -} - -void fsw_flog(FILE *f, const char *msg) -{ - if (fsw_is_verbose()) fprintf(f, "%s", msg); -} - -void fsw_logf(const char *format, ...) -{ - if (!fsw_is_verbose()) return; - - va_list args; - va_start(args, format); - - vfprintf(stdout, format, args); - - va_end(args); -} - -void fsw_flogf(FILE *f, const char *format, ...) -{ - if (!fsw_is_verbose()) return; - - va_list args; - va_start(args, format); - - vfprintf(f, format, args); - - va_end(args); -} - -void fsw_log_perror(const char *msg) -{ - if (fsw_is_verbose()) perror(msg); -} - -void fsw_logf_perror(const char *format, ...) -{ - if (!fsw_is_verbose()) return; - - va_list args; - va_start(args, format); - - perror(string_utils::vstring_from_format(format, args).c_str()); - - va_end(args); -} +void fsw_log(const char *) {} +void fsw_flog(FILE *, const char *) {} +void fsw_logf(const char *, ...) {} +void fsw_flogf(FILE *, const char *, ...) {} +void fsw_log_perror(const char *) {} +void fsw_logf_perror(const char *, ...) {} diff --git a/src/fswatch/libfswatch/src/libfswatch/c/libfswatch_log.h b/src/fswatch/libfswatch/src/libfswatch/c/libfswatch_log.h index f061328..5b070e4 100644 --- a/src/fswatch/libfswatch/src/libfswatch/c/libfswatch_log.h +++ b/src/fswatch/libfswatch/src/libfswatch/c/libfswatch_log.h @@ -65,30 +65,30 @@ void fsw_logf_perror(const char * format, ...); * @brief Log the specified message to the standard output prepended by the * source line number. */ -# define FSW_LOG(msg) fsw_logf("%s: ", __func__); fsw_log(msg) +# define FSW_LOG(msg) ((void)0) /** * @brief Log the specified message to the standard error prepended by the * source line number. */ -# define FSW_ELOG(msg) fsw_flogf(stderr, "%s: ", __func__); fsw_flog(stderr, msg) +# define FSW_ELOG(msg) ((void)0) /** * @brief Log the specified `printf()`-like message to the standard output * prepended by the source line number. */ -# define FSW_LOGF(msg, ...) fsw_logf("%s: ", __func__); fsw_logf(msg, __VA_ARGS__) +# define FSW_LOGF(msg, ...) ((void)0) /** * @brief Log the specified `printf()`-like message to the standard error * prepended by the source line number. */ -# define FSW_ELOGF(msg, ...) fsw_flogf(stderr, "%s: ", __func__); fsw_flogf(stderr, msg, __VA_ARGS__) +# define FSW_ELOGF(msg, ...) ((void)0) /** * @brief Log the specified `printf()`-like message to the specified file * descriptor prepended by the source line number. */ -# define FSW_FLOGF(f, msg, ...) fsw_flogf(f, "%s: ", __func__); fsw_flogf(f, msg, __VA_ARGS__) +# define FSW_FLOGF(f, msg, ...) ((void)0) #endif /* LIBFSW_LOG_H */ diff --git a/src/fswatch/libfswatch/src/libfswatch/libfswatch_config.h b/src/fswatch/libfswatch/src/libfswatch/libfswatch_config.h new file mode 100644 index 0000000..a163dcb --- /dev/null +++ b/src/fswatch/libfswatch/src/libfswatch/libfswatch_config.h @@ -0,0 +1,65 @@ +/* Hand-maintained replacement for the cmake/autotools-generated config header. + * Feature selection is keyed on compiler-predefined platform macros. + * Regenerated/owned by the watcher package — not by upstream tooling. */ +#ifndef LIBFSWATCH_CONFIG_H +#define LIBFSWATCH_CONFIG_H + +/* C++17 guarantees these; used by libfswatch_map.hpp / libfswatch_set.hpp */ +#define HAVE_UNORDERED_MAP 1 +#define HAVE_UNORDERED_SET 1 + +#if defined(__APPLE__) +/* FSEvents + kqueue. The HAVE_MACOS_GE_* gates are *derived from the deployment + * target* (a compile-time "probe" of MAC_OS_X_VERSION_MIN_REQUIRED) rather than + * hard-set true: this stays correct for any floor R might use, costs nothing, + * and needs no configure machinery. Compare against the named MAC_OS_X_VERSION_* + * constants — their numeric encoding changed (1090 vs 101000) at 10.10. */ +# include +# define HAVE_FSEVENTS_FSEVENTSTREAMSETDISPATCHQUEUE 1 +# define HAVE_SYS_EVENT_H 1 +# define HAVE_STRUCT_STAT_ST_MTIMESPEC 1 +# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 +# define HAVE_MACOS_GE_10_5 1 +# endif +# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 +# define HAVE_MACOS_GE_10_6 1 +# endif +# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7 +# define HAVE_MACOS_GE_10_7 1 +# endif +# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9 +# define HAVE_MACOS_GE_10_9 1 +# endif +# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10 +# define HAVE_MACOS_GE_10_10 1 +# endif +# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_13 +# define HAVE_MACOS_GE_10_13 1 +# endif + +#elif defined(_WIN32) +# define HAVE_WINDOWS 1 +# define HAVE_STRUCT_STAT_ST_MTIME 1 + +#elif defined(__linux__) +# define HAVE_SYS_INOTIFY_H 1 +# define HAVE_SYS_EPOLL_H 1 +# define HAVE_SYS_EVENTFD_H 1 +# define HAVE_INOTIFY_MONITOR 1 +# define HAVE_STRUCT_STAT_ST_MTIME 1 +/* fanotify intentionally OFF: requires CAP_SYS_ADMIN/root and a large symbol surface. */ + +#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +# define HAVE_SYS_EVENT_H 1 +# define HAVE_STRUCT_STAT_ST_MTIMESPEC 1 + +#elif defined(__sun) +# define HAVE_PORT_H 1 +# define HAVE_STRUCT_STAT_ST_MTIME 1 + +#else +/* Generic POSIX fallback: stat()-based poll monitor only. */ +# define HAVE_STRUCT_STAT_ST_MTIME 1 +#endif + +#endif /* LIBFSWATCH_CONFIG_H */ diff --git a/tools/fsw_objects_posix.list b/tools/fsw_objects_posix.list new file mode 100644 index 0000000..c7761d8 --- /dev/null +++ b/tools/fsw_objects_posix.list @@ -0,0 +1,16 @@ +$(FSW)/c/cevent.o +$(FSW)/c/libfswatch.o +$(FSW)/c/libfswatch_log.o +$(FSW)/c++/event.o +$(FSW)/c++/filter.o +$(FSW)/c++/libfswatch_exception.o +$(FSW)/c++/monitor.o +$(FSW)/c++/monitor_factory.o +$(FSW)/c++/path_utils.o +$(FSW)/c++/poll_monitor.o +$(FSW)/c++/string/string_utils.o +$(FSW)/c++/fsevents_monitor.o +$(FSW)/c++/kqueue_monitor.o +$(FSW)/c++/inotify_monitor.o +$(FSW)/c++/fanotify_monitor.o +$(FSW)/c++/fen_monitor.o diff --git a/tools/patch_libfswatch.sh b/tools/patch_libfswatch.sh new file mode 100755 index 0000000..cfe3f45 --- /dev/null +++ b/tools/patch_libfswatch.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +# Apply watcher's local patches to the vendored libfswatch sources. +# +# Usage: ./tools/patch_libfswatch.sh [LIBFSWATCH_SRC_DIR] +# +# LIBFSWATCH_SRC_DIR defaults to the package's vendored tree +# (src/fswatch/libfswatch/src/libfswatch). update_libfswatch.sh passes the +# staging tree instead. +# +# Every patch is IDEMPOTENT: running this on already-patched sources is a no-op. +# The patches are: +# 1. string_utils.cpp - guard against a null format string (GCC +# -Wformat-truncation in stricter CRAN/rhub builds). +# 2. Monitor self-guards - wrap fsevents/inotify/fanotify monitor bodies in +# their platform #ifdef so they compile to empty objects on the wrong OS +# (kqueue/fen/windows monitors already self-guard upstream). +# 3. Logging neutering - libfswatch's diagnostics must not write to +# stdout/stderr from compiled code (R CMD check 'compiled code' policy). +# watcher never enables libfswatch verbose logging, so this is a no-op in +# normal use. Neutering removes the printf/stdout/stderr/cerr symbols that +# would otherwise be flagged when libfswatch is compiled into watcher.so. + +set -e + +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +LIBFSW_SRC="${1:-$SCRIPT_DIR/../src/fswatch/libfswatch/src/libfswatch}" + +if [ ! -d "$LIBFSW_SRC" ]; then + echo "Error: libfswatch source dir not found: $LIBFSW_SRC" >&2 + exit 1 +fi + +# Wrap a monitor source body in #ifdef ... #endif so it compiles to an +# empty object on platforms that lack the feature. Idempotent and portable. +add_guard() { + file=$1; macro=$2 + if grep -q "WATCHER guard $macro" "$file" 2>/dev/null; then + echo -e "${GREEN} ✓ self-guard $macro already present${NC}"; return 0 + fi + awk -v m="$macro" ' + { print } + $0 == "#include " && !done { + print "" + print "#ifdef " m + done = 1 + } + END { print ""; print "#endif /* WATCHER guard " m " */" } + ' "$file" > "$file.tmp" && mv "$file.tmp" "$file" + echo -e "${GREEN} ✓ self-guard $macro applied${NC}" +} + +# --- Patch 1: string_utils.cpp null-format guard ----------------------------- +STRING_UTILS="$LIBFSW_SRC/c++/string/string_utils.cpp" +if [ -f "$STRING_UTILS" ]; then + if grep -q "if (!format) return" "$STRING_UTILS"; then + echo -e "${GREEN} ✓ string_utils null-format guard already present${NC}" + else + sed -i.bak 's|^ size_t current_buffer_size = 0;$| if (!format) return string();\ + size_t current_buffer_size = 0;|' "$STRING_UTILS" + rm -f "$STRING_UTILS.bak" + echo -e "${GREEN} ✓ string_utils null-format guard applied${NC}" + fi +else + echo -e "${YELLOW} ⚠ string_utils.cpp not found, skipping${NC}" +fi + +# --- Patch 2: self-guard the three non-self-guarding monitors ---------------- +add_guard "$LIBFSW_SRC/c++/fsevents_monitor.cpp" HAVE_FSEVENTS_FSEVENTSTREAMSETDISPATCHQUEUE +add_guard "$LIBFSW_SRC/c++/inotify_monitor.cpp" HAVE_INOTIFY_MONITOR +add_guard "$LIBFSW_SRC/c++/fanotify_monitor.cpp" HAVE_FANOTIFY + +# --- Patch 3a: neuter the FSW_*LOG* macros (they expand `stderr` inline) ------ +LOG_H="$LIBFSW_SRC/c/libfswatch_log.h" +if grep -q 'fsw_flogf(stderr' "$LOG_H" 2>/dev/null; then + perl -pi -e 's/^(# define FSW_[A-Z]+\([^)]*\))\s+.*$/$1 ((void)0)/' "$LOG_H" + echo -e "${GREEN} ✓ FSW_*LOG* macros neutered${NC}" +else + echo -e "${GREEN} ✓ FSW_*LOG* macros already neutered${NC}" +fi + +# --- Patch 3b: empty the fsw_log* function bodies ---------------------------- +LOG_CPP="$LIBFSW_SRC/c/libfswatch_log.cpp" +if grep -q 'watcher: logging neutered' "$LOG_CPP" 2>/dev/null; then + echo -e "${GREEN} ✓ libfswatch_log.cpp already neutered${NC}" +else + # Preserve the upstream licence header (everything before the first #include). + HEADER=$(awk '/^#include/{exit} {print}' "$LOG_CPP") + { + printf '%s\n' "$HEADER" + cat <<'EOF' +/* watcher: logging neutered. libfswatch's diagnostics must not write to + * stdout/stderr from compiled code; watcher never enables verbose logging, + * so these are no-ops in normal use. See tools/patch_libfswatch.sh. */ +#include "libfswatch_log.h" + +void fsw_log(const char *) {} +void fsw_flog(FILE *, const char *) {} +void fsw_logf(const char *, ...) {} +void fsw_flogf(FILE *, const char *, ...) {} +void fsw_log_perror(const char *) {} +void fsw_logf_perror(const char *, ...) {} +EOF + } > "$LOG_CPP.tmp" && mv "$LOG_CPP.tmp" "$LOG_CPP" + echo -e "${GREEN} ✓ libfswatch_log.cpp bodies emptied${NC}" +fi + +# --- Patch 3c: drop the lone std::cerr in fsevents_monitor.cpp --------------- +FSEVENTS="$LIBFSW_SRC/c++/fsevents_monitor.cpp" +if grep -q 'std::cerr << "Warning: Failed to convert CFStringRef' "$FSEVENTS" 2>/dev/null; then + perl -ni -e 'print unless /std::cerr << "Warning: Failed to convert CFStringRef/' "$FSEVENTS" + echo -e "${GREEN} ✓ fsevents_monitor.cpp std::cerr removed${NC}" +else + echo -e "${GREEN} ✓ fsevents_monitor.cpp std::cerr already removed${NC}" +fi + +# --- Patch 3d: drop the Windows-only stderr/cerr diagnostics ----------------- +# These compile only on Windows (so they escape the POSIX checks); same policy +# as 3a-3c. perror() is left as-is throughout -- R's compiled-code check does +# not flag it. Each write is the lone statement in a braced block, so removing +# the line leaves valid code (an empty else / a bare return false). +WIN_DCE="$LIBFSW_SRC/c++/windows/win_directory_change_event.cpp" +if grep -q 'cerr << _("File name unexpectedly empty' "$WIN_DCE" 2>/dev/null; then + perl -ni -e 'print unless /cerr << _\("File name unexpectedly empty/' "$WIN_DCE" + echo -e "${GREEN} ✓ win_directory_change_event.cpp cerr removed${NC}" +else + echo -e "${GREEN} ✓ win_directory_change_event.cpp cerr already removed${NC}" +fi + +WIN_MON="$LIBFSW_SRC/c++/windows_monitor.cpp" +if grep -q 'fprintf(stderr, _("Invalid handle when opening' "$WIN_MON" 2>/dev/null; then + perl -ni -e 'print unless /fprintf\(stderr, _\("Invalid handle when opening/' "$WIN_MON" + echo -e "${GREEN} ✓ windows_monitor.cpp fprintf(stderr) removed${NC}" +else + echo -e "${GREEN} ✓ windows_monitor.cpp fprintf(stderr) already removed${NC}" +fi diff --git a/tools/update_libfswatch.sh b/tools/update_libfswatch.sh index dd67478..ac88e74 100755 --- a/tools/update_libfswatch.sh +++ b/tools/update_libfswatch.sh @@ -4,7 +4,11 @@ # Usage: ./tools/update_libfswatch.sh [version/commit/tag] # # This script reproduces how to get the current vendored code from the -# upstream libfswatch repository and applies local patches. +# upstream libfswatch repository and applies local patches. It also +# (re)generates the hand-maintained config header, the per-object compile +# rules in src/Makevars.in / Makevars.win / Makevars.ucrt, and the POSIX +# object list read by ./configure. No cmake or autotools artifacts are kept: +# the bundled sources compile straight into the package shared object. set -e @@ -17,8 +21,242 @@ NC='\033[0m' # No Color # Configuration FSWATCH_REPO="https://github.com/emcrisostomo/fswatch.git" FSWATCH_VERSION="${1:-master}" # Default to master branch if no version specified -WORK_DIR="$(pwd)/fswatch-update-tmp" -TARGET_DIR="$(pwd)/src/fswatch" +PKG_ROOT="$(pwd)" +WORK_DIR="${PKG_ROOT}/fswatch-update-tmp" +TARGET_DIR="${PKG_ROOT}/src/fswatch" + +# Bundled libfswatch object lists (single source of truth, paths use $(FSW)). +# Keep in sync with ./configure (POSIX) and Makevars.win/.ucrt (Windows). +COMMON_CORE='$(FSW)/c/cevent.o +$(FSW)/c/libfswatch.o +$(FSW)/c/libfswatch_log.o +$(FSW)/c++/event.o +$(FSW)/c++/filter.o +$(FSW)/c++/libfswatch_exception.o +$(FSW)/c++/monitor.o +$(FSW)/c++/monitor_factory.o +$(FSW)/c++/path_utils.o +$(FSW)/c++/poll_monitor.o +$(FSW)/c++/string/string_utils.o' + +POSIX_MONITORS='$(FSW)/c++/fsevents_monitor.o +$(FSW)/c++/kqueue_monitor.o +$(FSW)/c++/inotify_monitor.o +$(FSW)/c++/fanotify_monitor.o +$(FSW)/c++/fen_monitor.o' + +WINDOWS_MONITORS='$(FSW)/c++/windows_monitor.o +$(FSW)/c++/windows/win_directory_change_event.o +$(FSW)/c++/windows/win_error_message.o +$(FSW)/c++/windows/win_handle.o +$(FSW)/c++/windows/win_paths.o +$(FSW)/c++/windows/win_strings.o' + +# Emit one explicit compile rule per object (object paths read from stdin). +emit_rules() { + while IFS= read -r obj; do + [ -n "$obj" ] || continue + src="${obj%.o}.cpp" + printf '%s: %s\n\t$(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c %s -o $@\n' "$obj" "$src" "$src" + done +} + +# Join an object list (stdin) into one trimmed space-separated line. +join_objs() { tr '\n' ' ' | sed 's/ *$//'; } + +# Write the hand-maintained config header (replaces the cmake template). +write_config_header() { + cat > "$1" <<'EOF' +/* Hand-maintained replacement for the cmake/autotools-generated config header. + * Feature selection is keyed on compiler-predefined platform macros. + * Regenerated/owned by the watcher package — not by upstream tooling. */ +#ifndef LIBFSWATCH_CONFIG_H +#define LIBFSWATCH_CONFIG_H + +/* C++17 guarantees these; used by libfswatch_map.hpp / libfswatch_set.hpp */ +#define HAVE_UNORDERED_MAP 1 +#define HAVE_UNORDERED_SET 1 + +#if defined(__APPLE__) +/* FSEvents + kqueue. The HAVE_MACOS_GE_* gates are *derived from the deployment + * target* (a compile-time "probe" of MAC_OS_X_VERSION_MIN_REQUIRED) rather than + * hard-set true: this stays correct for any floor R might use, costs nothing, + * and needs no configure machinery. Compare against the named MAC_OS_X_VERSION_* + * constants — their numeric encoding changed (1090 vs 101000) at 10.10. */ +# include +# define HAVE_FSEVENTS_FSEVENTSTREAMSETDISPATCHQUEUE 1 +# define HAVE_SYS_EVENT_H 1 +# define HAVE_STRUCT_STAT_ST_MTIMESPEC 1 +# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 +# define HAVE_MACOS_GE_10_5 1 +# endif +# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 +# define HAVE_MACOS_GE_10_6 1 +# endif +# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7 +# define HAVE_MACOS_GE_10_7 1 +# endif +# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9 +# define HAVE_MACOS_GE_10_9 1 +# endif +# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10 +# define HAVE_MACOS_GE_10_10 1 +# endif +# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_13 +# define HAVE_MACOS_GE_10_13 1 +# endif + +#elif defined(_WIN32) +# define HAVE_WINDOWS 1 +# define HAVE_STRUCT_STAT_ST_MTIME 1 + +#elif defined(__linux__) +# define HAVE_SYS_INOTIFY_H 1 +# define HAVE_SYS_EPOLL_H 1 +# define HAVE_SYS_EVENTFD_H 1 +# define HAVE_INOTIFY_MONITOR 1 +# define HAVE_STRUCT_STAT_ST_MTIME 1 +/* fanotify intentionally OFF: requires CAP_SYS_ADMIN/root and a large symbol surface. */ + +#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +# define HAVE_SYS_EVENT_H 1 +# define HAVE_STRUCT_STAT_ST_MTIMESPEC 1 + +#elif defined(__sun) +# define HAVE_PORT_H 1 +# define HAVE_STRUCT_STAT_ST_MTIME 1 + +#else +/* Generic POSIX fallback: stat()-based poll monitor only. */ +# define HAVE_STRUCT_STAT_ST_MTIME 1 +#endif + +#endif /* LIBFSWATCH_CONFIG_H */ +EOF +} + +# (Re)generate the POSIX object list and the three Makevars files. +generate_build_files() { + printf '%s\n%s\n' "$COMMON_CORE" "$POSIX_MONITORS" > "$PKG_ROOT/tools/fsw_objects_posix.list" + + # --- src/Makevars.in (POSIX; configure fills @cppflags@/@libs@/@fsw_objects@) + { + cat <<'EOF' +# Generated by tools/update_libfswatch.sh -- do not edit by hand. +# +# Compile the bundled libfswatch sources directly into the package shared object +# (no cmake, no static archive). configure substitutes the include flags, the +# link flags, the bundled object list (empty when a system libfswatch is used) +# and the C++ standard line (a CXX_STD request set only on R < 4.3 -- see +# configure). +# +# PKG_CPPFLAGS carries the include path so it reaches BOTH the C package files +# and the bundled C++ via $(ALL_CPPFLAGS). +PKG_CPPFLAGS = @cppflags@ +PKG_CFLAGS = $(C_VISIBILITY) +PKG_CXXFLAGS = $(CXX_VISIBILITY) +PKG_LIBS = @libs@ + +@cxx_std@ + +# Top-level package sources (built by R's own suffix rules) +P_OBJECTS = init.o watcher.o +# Bundled libfswatch objects (empty when a system libfswatch is used) +FSW_OBJECTS = @fsw_objects@ + +OBJECTS = $(P_OBJECTS) $(FSW_OBJECTS) + +FSW = fswatch/libfswatch/src/libfswatch + +# `all:` MUST be the first target: an explicit object rule below would otherwise +# become make's default goal when R CMD SHLIB invokes make with no target. +all: $(SHLIB) + +$(SHLIB): $(OBJECTS) + +# --- Explicit compile rules for the bundled (subdirectory) sources ----------- +# One rule per object; recipe identical to R's own .cpp.o suffix rule. Each +# names its source explicitly and uses only portable make variables, so any +# POSIX make builds them (no automatic-variable source refs, no pattern rules, +# no directory search, no file inclusion). +EOF + printf '%s\n%s\n' "$COMMON_CORE" "$POSIX_MONITORS" | emit_rules + printf '\n.PHONY: all clean\nclean:\n\trm -f $(OBJECTS) $(SHLIB)\n' + } > "$PKG_ROOT/src/Makevars.in" + + # --- src/Makevars.win (Rtools40, gcc 8.3, dual i386 + x64) ------------------ + win_objs=$(printf '%s\n%s\n' "$COMMON_CORE" "$WINDOWS_MONITORS" | join_objs) + { + cat <<'EOF' +# Generated by tools/update_libfswatch.sh -- do not edit by hand. +# +# Rtools40 (gcc 8.3), dual i386 + x64. Always compiles the bundled libfswatch +# (no system-library path on Windows). std::filesystem is used in the common +# core, so -lstdc++fs is required on gcc 8.x/9.x. +# +# CXX_STD stays static here: Rtools40 only ever serves R <= 4.1 (< 4.3), whose +# default is below C++17, so requesting it is always needed and never trips the +# CRAN "please drop specification" NOTE. (UCRT is conditional -- see +# Makevars.ucrt.in / configure.ucrt.) +PKG_CPPFLAGS = -Ifswatch/libfswatch/src +PKG_CFLAGS = $(C_VISIBILITY) +PKG_CXXFLAGS = $(CXX_VISIBILITY) +PKG_LIBS = -pthread -lstdc++fs + +CXX_STD = CXX17 + +P_OBJECTS = init.o watcher.o +FSW = fswatch/libfswatch/src/libfswatch +EOF + printf 'FSW_OBJECTS = %s\n' "$win_objs" + cat <<'EOF' + +OBJECTS = $(P_OBJECTS) $(FSW_OBJECTS) + +all: $(SHLIB) + +$(SHLIB): $(OBJECTS) + +EOF + printf '%s\n%s\n' "$COMMON_CORE" "$WINDOWS_MONITORS" | emit_rules + printf '\n.PHONY: all clean\nclean:\n\trm -f $(OBJECTS) $(SHLIB)\n' + } > "$PKG_ROOT/src/Makevars.win" + + # --- src/Makevars.ucrt (Rtools42/43/44; std::filesystem in libstdc++) ------- + { + cat <<'EOF' +# Generated by tools/update_libfswatch.sh -- do not edit by hand. +# Template: configure.ucrt fills in the C++ standard line to produce +# src/Makevars.ucrt. +# +# Rtools42/43/44 (gcc 10/12/13). Identical to Makevars.win except that +# std::filesystem is folded into libstdc++, so -lstdc++fs is not needed, and the +# CXX_STD request is emitted only on R < 4.3 (R >= 4.3 defaults to C++17; +# specifying it then trips a CRAN "please drop specification" NOTE). +PKG_CPPFLAGS = -Ifswatch/libfswatch/src +PKG_CFLAGS = $(C_VISIBILITY) +PKG_CXXFLAGS = $(CXX_VISIBILITY) +PKG_LIBS = -pthread + +@cxx_std@ + +P_OBJECTS = init.o watcher.o +FSW = fswatch/libfswatch/src/libfswatch +EOF + printf 'FSW_OBJECTS = %s\n' "$win_objs" + cat <<'EOF' + +OBJECTS = $(P_OBJECTS) $(FSW_OBJECTS) + +all: $(SHLIB) + +$(SHLIB): $(OBJECTS) + +EOF + printf '%s\n%s\n' "$COMMON_CORE" "$WINDOWS_MONITORS" | emit_rules + printf '\n.PHONY: all clean\nclean:\n\trm -f $(OBJECTS) $(SHLIB)\n' + } > "$PKG_ROOT/src/Makevars.ucrt.in" +} echo -e "${GREEN}=== LibFSWatch Update Script ===${NC}" echo "Repository: ${FSWATCH_REPO}" @@ -63,45 +301,40 @@ echo -e "${YELLOW}Step 2: Extracting required files...${NC}" STAGING_DIR="${WORK_DIR}/staging" mkdir -p "${STAGING_DIR}" -# Copy the files we need (excluding fswatch CLI tool, tests, and build infrastructure) +# Copy the files we need (excluding fswatch CLI tool, tests, and all build +# infrastructure — we compile in place, so no CMakeLists.txt is wanted). echo "Copying libfswatch source files..." cp -r libfswatch "${STAGING_DIR}/" -# Copy top-level files needed for the build -cp CMakeLists.txt "${STAGING_DIR}/" +# Copy attribution files (required by DESCRIPTION cph entries / src/LICENSE.note) cp AUTHORS "${STAGING_DIR}/" cp AUTHORS.libfswatch "${STAGING_DIR}/" cp LICENSE-2.0.txt "${STAGING_DIR}/" cp README.md "${STAGING_DIR}/" -# Remove unnecessary files from libfswatch +# Remove docs, automake files, and every cmake/config artifact: we own the +# config header and drive the build from the package Makevars. echo "Cleaning up unnecessary files..." rm -rf "${STAGING_DIR}/libfswatch/doc" find "${STAGING_DIR}" -name "Makefile.am" -delete +rm -f "${STAGING_DIR}/libfswatch/CMakeLists.txt" +rm -f "${STAGING_DIR}/libfswatch/src/libfswatch.pc.in" +rm -f "${STAGING_DIR}/libfswatch/libfswatch_config.in" echo "" -# Step 3: Apply patches specific to watcher package +# Step 3: Apply patches and write the owned config header echo -e "${YELLOW}Step 3: Applying patches for watcher package...${NC}" -# Patch: Avoid GCC -Wformat-truncation "null format string" warning in -# string_utils.cpp by adding an explicit null check on the format argument -# before the vsnprintf call. Surfaces in stricter CRAN/rhub Linux builds. -echo "Applying string_utils.cpp null-format guard..." -STRING_UTILS="${STAGING_DIR}/libfswatch/src/libfswatch/c++/string/string_utils.cpp" - -if [ -f "${STRING_UTILS}" ]; then - if ! grep -q "if (!format) return" "${STRING_UTILS}"; then - sed -i.bak 's|^ size_t current_buffer_size = 0;$| if (!format) return string();\ - size_t current_buffer_size = 0;|' "${STRING_UTILS}" - rm -f "${STRING_UTILS}.bak" - echo -e "${GREEN} ✓ Null-format guard applied${NC}" - else - echo -e "${GREEN} ✓ Null-format guard already present${NC}" - fi -else - echo -e "${YELLOW} ⚠ string_utils.cpp not found, skipping patch${NC}" -fi +LIBFSW_SRC="${STAGING_DIR}/libfswatch/src/libfswatch" + +echo "Writing hand-maintained libfswatch_config.h..." +write_config_header "${LIBFSW_SRC}/libfswatch_config.h" +echo -e "${GREEN} ✓ config header written${NC}" + +# Apply all watcher source patches (string_utils guard, monitor self-guards, +# logging neutering) idempotently to the staged tree. +"${PKG_ROOT}/tools/patch_libfswatch.sh" "${LIBFSW_SRC}" echo "" @@ -112,18 +345,27 @@ mv "${STAGING_DIR}" "${TARGET_DIR}" echo -e "${GREEN}Vendored code updated successfully!${NC}" echo "" -# Step 5: Clean up -echo -e "${YELLOW}Step 5: Cleaning up...${NC}" -cd "$(dirname "${WORK_DIR}")" +# Step 5: Regenerate build files (object list + Makevars) +echo -e "${YELLOW}Step 5: Regenerating build files...${NC}" +generate_build_files +echo -e "${GREEN} ✓ tools/fsw_objects_posix.list${NC}" +echo -e "${GREEN} ✓ src/Makevars.in, src/Makevars.win, src/Makevars.ucrt${NC}" +echo "" + +# Step 6: Clean up +echo -e "${YELLOW}Step 6: Cleaning up...${NC}" +cd "${PKG_ROOT}" rm -rf "${WORK_DIR}" echo -e "${GREEN}Temporary files removed${NC}" echo "" -# Step 6: Show summary +# Step 7: Show summary echo -e "${GREEN}=== Update Complete ===${NC}" echo "" echo "Summary:" echo " - Updated from commit: ${CURRENT_COMMIT}" echo " - Updated from tag: ${CURRENT_TAG}" -echo " - Patches applied: string_utils.cpp null-format guard" +echo " - Owned libfswatch_config.h written" +echo " - Source patches applied via tools/patch_libfswatch.sh" +echo " - Regenerated Makevars + tools/fsw_objects_posix.list" echo ""