diff --git a/.github/actions/watcher/action.yaml b/.github/actions/watcher/action.yaml new file mode 100644 index 00000000..a6ba7ccd --- /dev/null +++ b/.github/actions/watcher/action.yaml @@ -0,0 +1,38 @@ +name: watcher +description: Install e-dant/watcher +runs: + using: composite + steps: + - + name: Determine e-dant/watcher version + id: determine-watcher-version + run: echo version="$(gh release view --repo e-dant/watcher --json tagName --template '{{ .tagName }}')" >> "${GITHUB_OUTPUT}" + shell: bash + env: + GH_TOKEN: ${{ github.token }} + - + name: Cache e-dant/watcher + id: cache-watcher + uses: actions/cache@v4 + with: + path: watcher/target + key: watcher-${{ runner.os }}-${{ runner.arch }}-${{ steps.determine-watcher-version.outputs.version }}-${{ env.CC && env.CC || 'gcc' }} + - + if: steps.cache-watcher.outputs.cache-hit != 'true' + name: Compile e-dant/watcher + run: | + mkdir watcher + gh release download --repo e-dant/watcher -A tar.gz -O - | tar -xz -C watcher --strip-components 1 + cd watcher + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release + cmake --build build + sudo cmake --install build --prefix target + shell: bash + env: + GH_TOKEN: ${{ github.token }} + - + name: Update LD_LIBRARY_PATH + run: | + sudo sh -c "echo ${PWD}/watcher/target/lib > /etc/ld.so.conf.d/watcher.conf" + sudo ldconfig + shell: bash diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 1610d5d4..f1635369 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -214,7 +214,7 @@ jobs: run: | docker run --platform=${{ matrix.platform }} --rm \ "$(jq -r '."builder-${{ matrix.variant }}"."containerimage.config.digest"' <<< "${METADATA}")" \ - sh -c 'go test -tags watcher ${{ matrix.race }} -v ./... && cd caddy && go test -tags watcher,brotli,nobadger,nomysql,nopgx ${{ matrix.race }} -v ./...' + sh -c 'go test -tags ${{ matrix.race }} -v ./... && cd caddy && go test -tags nobadger,nomysql,nopgx ${{ matrix.race }} -v ./...' env: METADATA: ${{ steps.build.outputs.metadata }} # Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/ diff --git a/.github/workflows/sanitizers.yaml b/.github/workflows/sanitizers.yaml index 486cf13c..8be1726e 100644 --- a/.github/workflows/sanitizers.yaml +++ b/.github/workflows/sanitizers.yaml @@ -28,9 +28,8 @@ jobs: CC: clang CXX: clang++ USE_ZEND_ALLOC: 0 - LIBRARY_PATH: ${{ github.workspace }}/php/target/lib + LIBRARY_PATH: ${{ github.workspace }}/php/target/lib:${{ github.workspace }}/watcher/target/lib LD_LIBRARY_PATH: ${{ github.workspace }}/php/target/lib - EDANT_WATCHER_VERSION: next steps: - name: Remove local PHP @@ -97,26 +96,13 @@ jobs: name: Add PHP to the PATH run: echo "$(pwd)/php/target/bin" >> "$GITHUB_PATH" - - if: matrix.sanitizer != 'msan' - uses: actions/checkout@v4 - name: Checkout watcher - with: - repository: e-dant/watcher - ref: ${{ env.EDANT_WATCHER_VERSION }} - path: 'edant/watcher' - - - if: matrix.sanitizer != 'msan' - name: Compile edant/watcher - run: | - cd edant/watcher/watcher-c/ - "$CC" -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra -fPIC -shared ${{ matrix.sanitizer == 'msan' && '-fsanitize=memory -fno-omit-frame-pointer -fno-optimize-sibling-calls' || '' }} - sudo cp libwatcher.so /usr/local/lib/libwatcher.so - sudo ldconfig + name: Install e-dant/watcher + uses: ./.github/actions/watcher - name: Set Set CGO flags run: | { - echo "CGO_CFLAGS=$CFLAGS $(php-config --includes)" + echo "CGO_CFLAGS=$CFLAGS -I${PWD}/watcher/target/include $(php-config --includes)" echo "CGO_LDFLAGS=$LDFLAGS $(php-config --ldflags) $(php-config --libs)" } >> "$GITHUB_ENV" - diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index dcb17910..a08ffea8 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -23,7 +23,7 @@ jobs: env: GOEXPERIMENT: cgocheck2 GOMAXPROCS: 10 - EDANT_WATCHER_VERSION: next + LIBRARY_PATH: ${{ github.workspace }}/watcher/target/lib steps: - uses: actions/checkout@v4 @@ -45,23 +45,11 @@ jobs: phpts: ts debug: true - - uses: actions/checkout@v4 - name: Checkout watcher - with: - repository: e-dant/watcher - ref: ${{ env.EDANT_WATCHER_VERSION }} - path: 'edant/watcher' + name: Install e-dant/watcher + uses: ./.github/actions/watcher - - name: Compile edant/watcher - run: | - cd edant/watcher/watcher-c/ - cc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -O3 -Wall -Wextra -fPIC -shared - sudo cp libwatcher.so /usr/local/lib/libwatcher.so - sudo ldconfig - - - name: Set CGO flags - run: | - echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV" + name: Set Set CGO flags + run: echo "CGO_CFLAGS=-I${PWD}/watcher/target/include $(php-config --includes)" >> "${GITHUB_ENV}" - name: Build run: go build @@ -71,11 +59,11 @@ jobs: run: go build - name: Run library tests - run: go test -tags watcher -race -v ./... + run: go test -race -v ./... - name: Run Caddy module tests working-directory: caddy/ - run: go test -tags watcher,brotli,nobadger,nomysql,nopgx -race -v ./... + run: go test -tags nobadger,nomysql,nopgx -race -v ./... - name: Build the server working-directory: caddy/frankenphp/ diff --git a/Dockerfile b/Dockerfile index cb16e1c9..24312888 100644 --- a/Dockerfile +++ b/Dockerfile @@ -61,6 +61,7 @@ ENV PATH=/usr/local/go/bin:$PATH # This is required to link the FrankenPHP binary to the PHP binary RUN apt-get update && \ apt-get -y --no-install-recommends install \ + cmake \ libargon2-dev \ libbrotli-dev \ libcurl4-openssl-dev \ @@ -89,19 +90,26 @@ COPY --link caddy caddy COPY --link internal internal COPY --link testdata testdata -# install edant/watcher (necessary for file watching) -ARG EDANT_WATCHER_VERSION=release +# Install e-dant/watcher (necessary for file watching) WORKDIR /usr/local/src/watcher -RUN curl -L https://github.com/e-dant/watcher/archive/refs/heads/$EDANT_WATCHER_VERSION.tar.gz | tar xz -WORKDIR /usr/local/src/watcher/watcher-$EDANT_WATCHER_VERSION/watcher-c -RUN cc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -O3 -Wall -Wextra -fPIC -shared && \ - cp libwatcher.so /usr/local/lib/libwatcher.so && \ - ldconfig /usr/local/lib +RUN curl -s https://api.github.com/repos/e-dant/watcher/releases/latest | \ + grep tarball_url | \ + awk '{ print $2 }' | \ + sed 's/,$//' | \ + sed 's/"//g' | \ + xargs curl -L | \ + tar xz --strip-components 1 && \ + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && \ + cmake --build build && \ + cmake --install build && \ + ldconfig # See https://github.com/docker-library/php/blob/master/8.3/bookworm/zts/Dockerfile#L57-L59 for PHP values ENV CGO_CFLAGS="-DFRANKENPHP_VERSION=$FRANKENPHP_VERSION $PHP_CFLAGS" ENV CGO_CPPFLAGS=$PHP_CPPFLAGS -ENV CGO_LDFLAGS="-lssl -lcrypto -lreadline -largon2 -lcurl -lonig -lz $PHP_LDFLAGS" +ENV CGO_LDFLAGS="-L/usr/local/lib -lssl -lcrypto -lreadline -largon2 -lcurl -lonig -lz $PHP_LDFLAGS" + +RUN echo $CGO_LDFLAGS WORKDIR /go/src/app/caddy/frankenphp RUN GOBIN=/usr/local/bin go install -tags 'nobadger,nomysql,nopgx' -ldflags "-w -s -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION PHP $PHP_VERSION Caddy'" && \ diff --git a/alpine.Dockerfile b/alpine.Dockerfile index c3f75668..0270825d 100644 --- a/alpine.Dockerfile +++ b/alpine.Dockerfile @@ -71,7 +71,8 @@ RUN apk add --no-cache --virtual .build-deps \ git \ gnu-libiconv-dev \ libsodium-dev \ - # Needed for the file watcher + # Needed for the file watcher \ + cmake \ libstdc++ \ libxml2-dev \ linux-headers \ @@ -111,14 +112,18 @@ COPY --link caddy caddy COPY --link internal internal COPY --link testdata testdata -# install edant/watcher (necessary for file watching) -ARG EDANT_WATCHER_VERSION=release +# Install e-dant/watcher (necessary for file watching) WORKDIR /usr/local/src/watcher -RUN curl -L https://github.com/e-dant/watcher/archive/refs/heads/$EDANT_WATCHER_VERSION.tar.gz | tar xz -WORKDIR /usr/local/src/watcher/watcher-$EDANT_WATCHER_VERSION/watcher-c -RUN cc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -O3 -Wall -Wextra -fPIC -shared && \ - cp libwatcher.so /usr/local/lib/libwatcher.so && \ - ldconfig /usr/local/lib +RUN curl -s https://api.github.com/repos/e-dant/watcher/releases/latest | \ + grep tarball_url | \ + awk '{ print $2 }' | \ + sed 's/,$//' | \ + sed 's/"//g' | \ + xargs curl -L | \ + tar xz --strip-components 1 && \ + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && \ + cmake --build build && \ + cmake --install build # See https://github.com/docker-library/php/blob/master/8.3/alpine3.20/zts/Dockerfile#L53-L55 ENV CGO_CFLAGS="-DFRANKENPHP_VERSION=$FRANKENPHP_VERSION $PHP_CFLAGS" diff --git a/build-static.sh b/build-static.sh index 5e7c6ac0..c1121fef 100755 --- a/build-static.sh +++ b/build-static.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -o errexit set -x @@ -120,6 +120,29 @@ else ./bin/spc build --debug --enable-zts --build-embed ${extraOpts} "${PHP_EXTENSIONS}" --with-libs="${PHP_EXTENSION_LIBS}" fi +curlGitHubHeaders=(--header "X-GitHub-Api-Version: 2022-11-28") +if [ "${GITHUB_TOKEN}" ]; then + curlGitHubHeaders+=(--header "Authorization: Bearer ${GITHUB_TOKEN}") +fi + +# Compile e-dant/watcher as a static library +mkdir watcher +cd watcher +curl -f --retry 5 "${curlGitHubHeaders[@]}" https://api.github.com/repos/e-dant/watcher/releases/latest | + grep tarball_url | + awk '{ print $2 }' | + sed 's/,$//' | + sed 's/"//g' | + xargs curl -fL --retry 5 "${curlGitHubHeaders[@]}" | + tar xz --strip-components 1 +cd watcher-c +cc -c -o libwatcher-c.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra -fPIC +ar rcs libwatcher-c.a libwatcher-c.o +cp libwatcher-c.a ../../buildroot/lib/libwatcher-c.a +mkdir -p ../../buildroot/include/wtr +cp -R include/wtr/watcher-c.h ../../buildroot/include/wtr/watcher-c.h +cd ../../ + # See https://github.com/docker-library/php/blob/master/8.3/alpine3.20/zts/Dockerfile#L53-L55 CGO_CFLAGS="-DFRANKENPHP_VERSION=${FRANKENPHP_VERSION} -I${PWD}/buildroot/include/ $(./buildroot/bin/php-config --includes | sed s#-I/#-I"${PWD}"/buildroot/#g)" if [ -n "${DEBUG_SYMBOLS}" ]; then @@ -136,22 +159,13 @@ elif [ "${os}" = "linux" ] && [ -z "${DEBUG_SYMBOLS}" ]; then CGO_LDFLAGS="-Wl,-O1 -pie" fi -CGO_LDFLAGS="${CGO_LDFLAGS} ${PWD}/buildroot/lib/libbrotlicommon.a ${PWD}/buildroot/lib/libbrotlienc.a ${PWD}/buildroot/lib/libbrotlidec.a $(./buildroot/bin/php-config --ldflags || true) $(./buildroot/bin/php-config --libs | sed -e 's/-lgcc_s//g' || true)" +CGO_LDFLAGS="${CGO_LDFLAGS} ${PWD}/buildroot/lib/libbrotlicommon.a ${PWD}/buildroot/lib/libbrotlienc.a ${PWD}/buildroot/lib/libbrotlidec.a ${PWD}/buildroot/lib/libwatcher-c.a $(./buildroot/bin/php-config --ldflags || true) $(./buildroot/bin/php-config --libs | sed -e 's/-lgcc_s//g' || true)" if [ "${os}" = "linux" ]; then if echo "${PHP_EXTENSIONS}" | grep -qE "\b(intl|imagick|grpc|v8js|protobuf|mongodb|tbb)\b"; then CGO_LDFLAGS="${CGO_LDFLAGS} -lstdc++" fi fi -# install edant/watcher for file watching (static version) -git clone --branch="${EDANT_WATCHER_VERSION:-release}" https://github.com/e-dant/watcher watcher -cd watcher/watcher-c -cc -c -o libwatcher.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra -fPIC -ar rcs libwatcher.a libwatcher.o -cp libwatcher.a "../../buildroot/lib/libwatcher.a" -cd ../../ -CGO_LDFLAGS="${CGO_LDFLAGS} -lstdc++ ${PWD}/buildroot/lib/libwatcher.a" - export CGO_LDFLAGS LIBPHP_VERSION="$(./buildroot/bin/php-config --version)" @@ -182,7 +196,7 @@ if [ "${os}" = "linux" ]; then git checkout "$(git describe --tags "$(git rev-list --tags --max-count=1 || true)" || true)" - curl -f -L --retry 5 https://raw.githubusercontent.com/tweag/rust-alpine-mimalloc/b26002b49d466a295ea8b50828cb7520a71a872a/mimalloc.diff -o mimalloc.diff + curl -fL --retry 5 https://raw.githubusercontent.com/tweag/rust-alpine-mimalloc/b26002b49d466a295ea8b50828cb7520a71a872a/mimalloc.diff -o mimalloc.diff patch -p1 > /usr/local/lib/php.ini && \ php --version -# install edant/watcher (necessary for file watching) +# Install e-dant/watcher (necessary for file watching) ARG EDANT_WATCHER_VERSION=release WORKDIR /usr/local/src/watcher -RUN git clone --branch=$EDANT_WATCHER_VERSION https://github.com/e-dant/watcher . -WORKDIR /usr/local/src/watcher/watcher-c -RUN cc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -O3 -Wall -Wextra -fPIC -shared && \ - cp libwatcher.so /usr/local/lib/libwatcher.so && \ - ldconfig /usr/local/lib +RUN git clone --branch=$EDANT_WATCHER_VERSION https://github.com/e-dant/watcher . && \ + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && \ + cmake --build build/ && \ + cmake --install build WORKDIR /go/src/app COPY . . diff --git a/dev.Dockerfile b/dev.Dockerfile index 4dc9e65c..fc7ca033 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -36,6 +36,7 @@ RUN apt-get update && \ # Dev tools \ git \ clang \ + cmake \ llvm \ gdb \ valgrind \ @@ -65,14 +66,13 @@ RUN git clone --branch=PHP-8.3 https://github.com/php/php-src.git . && \ echo "opcache.enable=1" >> /usr/local/lib/php.ini && \ php --version -# install edant/watcher (necessary for file watching) +# Install e-dant/watcher (necessary for file watching) ARG EDANT_WATCHER_VERSION=release WORKDIR /usr/local/src/watcher -RUN git clone --branch=$EDANT_WATCHER_VERSION https://github.com/e-dant/watcher . -WORKDIR /usr/local/src/watcher/watcher-c -RUN cc -o libwatcher.so ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -O3 -Wall -Wextra -fPIC -shared && \ - cp libwatcher.so /usr/local/lib/libwatcher.so && \ - ldconfig /usr/local/lib +RUN git clone --branch=$EDANT_WATCHER_VERSION https://github.com/e-dant/watcher . && \ + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && \ + cmake --build build/ && \ + cmake --install build WORKDIR /go/src/app COPY . . diff --git a/frankenphp.go b/frankenphp.go index ec808134..2882a1c1 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -14,7 +14,7 @@ package frankenphp // #cgo darwin pkg-config: libxml-2.0 // #cgo CFLAGS: -Wall -Werror -// #cgo CFLAGS: -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib +// #cgo CFLAGS: -I/usr/local/include -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib // #cgo linux CFLAGS: -D_GNU_SOURCE // #cgo darwin LDFLAGS: -L/opt/homebrew/opt/libiconv/lib -liconv // #cgo linux LDFLAGS: -lresolv diff --git a/internal/watcher/watcher-c.h b/internal/watcher/watcher-c.h deleted file mode 100644 index 621cfde3..00000000 --- a/internal/watcher/watcher-c.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* Represents "what happened" to a path. */ -static const int8_t WTR_WATCHER_EFFECT_RENAME = 0; -static const int8_t WTR_WATCHER_EFFECT_MODIFY = 1; -static const int8_t WTR_WATCHER_EFFECT_CREATE = 2; -static const int8_t WTR_WATCHER_EFFECT_DESTROY = 3; -static const int8_t WTR_WATCHER_EFFECT_OWNER = 4; -static const int8_t WTR_WATCHER_EFFECT_OTHER = 5; - -/* Represents "what kind" of path it is. */ -static const int8_t WTR_WATCHER_PATH_DIR = 0; -static const int8_t WTR_WATCHER_PATH_FILE = 1; -static const int8_t WTR_WATCHER_PATH_HARD_LINK = 2; -static const int8_t WTR_WATCHER_PATH_SYM_LINK = 3; -static const int8_t WTR_WATCHER_PATH_WATCHER = 4; -static const int8_t WTR_WATCHER_PATH_OTHER = 5; - -/* The `event` object is used to carry information about - filesystem events to the user through the (user-supplied) - callback given to `watch`. - The `event` object will contain the: - - `path_name`: The path to the event. - - `path_type`: One of: - - dir - - file - - hard_link - - sym_link - - watcher - - other - - `effect_type`: One of: - - rename - - modify - - create - - destroy - - owner - - other - - `effect_time`: - The time of the event in nanoseconds since epoch. -*/ -struct wtr_watcher_event { - int64_t effect_time; - char const *path_name; - char const *associated_path_name; - int8_t effect_type; - int8_t path_type; -}; - -/* Ensure the user's callback can receive - events and will return nothing. */ -typedef void (*wtr_watcher_callback)(struct wtr_watcher_event event, - void *context); - -void *wtr_watcher_open(char const *const path, wtr_watcher_callback callback, - void *context); - -bool wtr_watcher_close(void *watcher); - -#ifdef __cplusplus -} -#endif diff --git a/internal/watcher/watcher.c b/internal/watcher/watcher.c index 51d10a2b..73814f78 100644 --- a/internal/watcher/watcher.c +++ b/internal/watcher/watcher.c @@ -2,7 +2,7 @@ //go:build !nowatcher // clang-format on #include "_cgo_export.h" -#include "watcher-c.h" +#include "wtr/watcher-c.h" void handle_event(struct wtr_watcher_event event, void *data) { go_handle_file_watcher_event((char *)event.path_name, event.effect_type, diff --git a/internal/watcher/watcher.go b/internal/watcher/watcher.go index 153e4c3a..4b1086ba 100644 --- a/internal/watcher/watcher.go +++ b/internal/watcher/watcher.go @@ -1,11 +1,8 @@ //go:build !nowatcher -//go:generate curl --silent https://raw.githubusercontent.com/e-dant/watcher/refs/heads/release/watcher-c/include/wtr/watcher-c.h -o watcher-c.h - package watcher -// #cgo LDFLAGS: -lwatcher -lstdc++ -// #cgo CFLAGS: -Wall -Werror +// #cgo LDFLAGS: -lwatcher-c -lstdc++ // #include // #include // #include "watcher.h" diff --git a/static-builder.Dockerfile b/static-builder.Dockerfile index 92744cd7..4a8ad347 100644 --- a/static-builder.Dockerfile +++ b/static-builder.Dockerfile @@ -12,9 +12,6 @@ ENV FRANKENPHP_VERSION=${FRANKENPHP_VERSION} ARG PHP_VERSION='' ENV PHP_VERSION=${PHP_VERSION} -ARG EDANT_WATCHER_VERSION='' -ENV EDANT_WATCHER_VERSION=${EDANT_WATCHER_VERSION} - ARG PHP_EXTENSIONS='' ARG PHP_EXTENSION_LIBS='' ARG CLEAN=''