From a69d75fd968a44a2efeacf16349d66228e5761e7 Mon Sep 17 00:00:00 2001 From: "ondrej@mirtes.cz" Date: Wed, 28 Oct 2026 22:56:59 +0000 Subject: [PATCH 1/2] Build the pie extensions from the core's profile instead of training again The self-contained extensions of the pie legs ran a full `make pgo` each: an instrumented build, a training run and the optimised build, 6-18 minutes per leg. The core jobs now pack the core's share of their profile (`make SPLIT=1 pgo-pack`, uploaded as turbo-pgo-profile- and carried over with a reused core), and the pie legs build from it with `make pgo-from-profile`, which falls back to `make pgo` when another compiler recorded the profile. The core compiles to the same code against every version's headers, but GCC instruments before optimising: with the 8.5 core's profile, 85 of 33136 functions mismatch on 8.2-zts (none on 8.4, 8.4-zts, 8.5), and those build without a profile. --- .github/workflows/phar.yml | 81 ++++++++++++++++++++++++++++++++++---- turbo-ext/.gitignore | 1 + turbo-ext/CLAUDE.md | 2 + turbo-ext/Makefile | 54 +++++++++++++++++++++++-- 4 files changed, 127 insertions(+), 11 deletions(-) diff --git a/.github/workflows/phar.yml b/.github/workflows/phar.yml index c6e2dee39a8..ac538c2675b 100644 --- a/.github/workflows/phar.yml +++ b/.github/workflows/phar.yml @@ -497,6 +497,7 @@ jobs: PHP_MINOR: ${{ matrix.target.php-version }} PHP_ZTS: "0" TURBO_ARTIFACT: "phpstan_turbo_core-${{ matrix.target.name }}" + PGO_PROFILE_ARTIFACT: "turbo-pgo-profile-${{ matrix.target.name }}" ORIGIN_RUN_ID: ${{ fromJSON(needs.turbo-origins.outputs.origins || '{}')[format('phpstan_turbo_core-{0}', matrix.target.name)] }} strategy: @@ -544,6 +545,17 @@ jobs: run-id: ${{ env.ORIGIN_RUN_ID }} github-token: ${{ github.token }} + # the profile that core was compiled with, uploaded before it (see + # "Upload the core's profile") + - name: "Download the profile of the core compiled from identical build inputs" + if: env.ORIGIN_RUN_ID != '' + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + name: ${{ env.PGO_PROFILE_ARTIFACT }} + path: "turbo-ext" + run-id: ${{ env.ORIGIN_RUN_ID }} + github-token: ${{ github.token }} + - name: "Record the run that compiled the core" if: env.ORIGIN_RUN_ID != '' run: echo "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$ORIGIN_RUN_ID" | tee turbo-reused.txt @@ -614,8 +626,11 @@ jobs: # locally, so a profile-guided core can be reproduced outside CI. # The strict set itself lives in turbo-ext/Makefile # (STRICT_WARN_FLAGS), which documents why each exemption is there. + # `make pgo-pack` keeps the core's share of the profile for the pie + # legs of turbo-compile, which build with it instead of training again. run: | make SPLIT=1 pgo WARN_FLAGS="$(make -s print-warn-flags)" -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc)" + make SPLIT=1 pgo-pack # Before signing (stripping invalidates a signature) and before the # upload. The symbol tables are a quarter of the binary; see `strip` @@ -651,6 +666,16 @@ jobs: echo "core + extension report: $REPORTED, enabler expects: $EXPECTED" [ "$REPORTED" = "$EXPECTED" ] + # Before the core, so that a run carrying the core (all that + # find-turbo-origins.sh checks) carries its profile too. A reused one + # is uploaded again, like the core. + - name: "Upload the core's profile" + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: ${{ env.PGO_PROFILE_ARTIFACT }} + path: "turbo-ext/pgo-profile.tar" + if-no-files-found: "error" + # A reused core is uploaded again under the same name, so the # turbo-compile legs find it in this run. - name: "Upload core artifact" @@ -688,6 +713,7 @@ jobs: PHP_MINOR: "8.5" PHP_ZTS: "0" TURBO_ARTIFACT: "phpstan_turbo_core-linux-musl-arm64" + PGO_PROFILE_ARTIFACT: "turbo-pgo-profile-linux-musl-arm64" ORIGIN_RUN_ID: ${{ fromJSON(needs.turbo-origins.outputs.origins || '{}')['phpstan_turbo_core-linux-musl-arm64'] }} steps: @@ -696,7 +722,7 @@ jobs: with: egress-policy: audit - # See the same two steps in turbo-compile-core. + # See the same three steps in turbo-compile-core. - name: "Download the core compiled from identical build inputs" if: env.ORIGIN_RUN_ID != '' uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 @@ -706,6 +732,15 @@ jobs: run-id: ${{ env.ORIGIN_RUN_ID }} github-token: ${{ github.token }} + - name: "Download the profile of the core compiled from identical build inputs" + if: env.ORIGIN_RUN_ID != '' + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + name: ${{ env.PGO_PROFILE_ARTIFACT }} + path: "turbo-ext" + run-id: ${{ env.ORIGIN_RUN_ID }} + github-token: ${{ github.token }} + - name: "Record the run that compiled the core" if: env.ORIGIN_RUN_ID != '' run: echo "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$ORIGIN_RUN_ID" | tee turbo-reused.txt @@ -751,6 +786,7 @@ jobs: run: | docker exec -i -w /work/turbo-ext alpine-build sh -e <<'EOF' make SPLIT=1 pgo WARN_FLAGS="$(make -s print-warn-flags)" -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc)" + make SPLIT=1 pgo-pack make SPLIT=1 strip ls -l phpstan_turbo_core.so phpstan_turbo.so EOF @@ -765,6 +801,13 @@ jobs: [ "$REPORTED" = "$EXPECTED" ] EOF + - name: "Upload the core's profile" + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: ${{ env.PGO_PROFILE_ARTIFACT }} + path: "turbo-ext/pgo-profile.tar" + if-no-files-found: "error" + - name: "Upload core artifact" uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: @@ -791,7 +834,8 @@ jobs: # there, and the commit job checks that every leg of a platform carries # the same core. # The `pie` variant legs build the same version self-contained instead, - # profile-guided like the core — PIE installs a pre-packaged binary by + # profile-guided with the profile the platform's core was built with + # (`make pgo-from-profile`) — PIE installs a pre-packaged binary by # copying the one extension file, so the PIE release assets cannot be # the thin extension (see .github/scripts/turbo-pie-assets.sh). Nothing # else ships them, so they only run for release tags (turbo-pie-release @@ -910,8 +954,21 @@ jobs: run: | make SPLIT=1 extension WARN_FLAGS="$(make -s print-warn-flags)" -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc)" + # The profile the platform's core was compiled with (turbo-compile-core, + # `make pgo-pack`): the core is all but the four version-specific + # sources and compiles to the same code against every version's + # headers, so the self-contained build of this version uses its profile + # instead of training one of its own — which took most of the leg. + - name: "Download the profile of the platform's core" + if: matrix.variant == 'pie' + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + name: "turbo-pgo-profile-${{ matrix.target.name }}" + path: "turbo-ext" + # see the same steps in turbo-compile-core: the training run of - # `make pgo` runs bin/phpstan on the checkout + # `make pgo` runs bin/phpstan on the checkout (pgo-from-profile falls + # back to it when another compiler recorded the profile) - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0 if: matrix.variant == 'pie' && matrix.target.family == 'macos' with: @@ -924,12 +981,12 @@ jobs: php composer.phar install --no-interaction --no-progress rm composer.phar - # A plain `make pgo` builds one self-contained phpstan_turbo.so. + # A plain `make pgo-use` builds one self-contained phpstan_turbo.so. - name: "Compile the self-contained extension with strict warnings (profile-guided)" if: matrix.variant == 'pie' working-directory: "turbo-ext" run: | - make pgo WARN_FLAGS="$(make -s print-warn-flags)" -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc)" + make pgo-from-profile WARN_FLAGS="$(make -s print-warn-flags)" -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc)" # Before signing and the upload; a plain `make strip` leaves the core # (already stripped and signed by its own job) untouched. @@ -1027,6 +1084,14 @@ jobs: name: "phpstan_turbo_core-linux-musl-arm64" path: "turbo-ext" + # see turbo-compile + - name: "Download the profile of the platform's core" + if: matrix.variant == 'pie' + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + name: "turbo-pgo-profile-linux-musl-arm64" + path: "turbo-ext" + - name: "Start the Alpine build container" env: ALPINE_IMAGE: ${{ matrix.php-version == '8.6' && matrix.ts == 'nts' && 'alpine:edge' || 'alpine:3.24' }} @@ -1052,8 +1117,8 @@ jobs: ls -l phpstan_turbo.so phpstan_turbo_core.so EOF - # see turbo-compile: the training run of `make pgo` runs bin/phpstan - # on the checkout + # see turbo-compile: the training run of `make pgo`, which + # pgo-from-profile falls back to, runs bin/phpstan on the checkout - name: "Compile the self-contained extension with strict warnings (profile-guided)" if: matrix.variant == 'pie' run: | @@ -1062,7 +1127,7 @@ jobs: php composer.phar install --no-interaction --no-progress rm composer.phar cd turbo-ext - make pgo WARN_FLAGS="$(make -s print-warn-flags)" -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc)" + make pgo-from-profile WARN_FLAGS="$(make -s print-warn-flags)" -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc)" make strip ls -l phpstan_turbo.so EOF diff --git a/turbo-ext/.gitignore b/turbo-ext/.gitignore index ecbe646f11f..e6b0dadd622 100644 --- a/turbo-ext/.gitignore +++ b/turbo-ext/.gitignore @@ -45,6 +45,7 @@ /pgo/ /src/*.gcda /src/parser/*.gcda +/pgo-profile.tar # the compilation database clang-tidy reads (make lint-turbo), regenerated # from the Makefile's own flags by bin/generate-compile-commands.php diff --git a/turbo-ext/CLAUDE.md b/turbo-ext/CLAUDE.md index 7e65625e330..3f8fee262e7 100644 --- a/turbo-ext/CLAUDE.md +++ b/turbo-ext/CLAUDE.md @@ -834,6 +834,8 @@ make SPLIT=1 # phpstan_turbo_core.so + phpstan_turbo.so (loads t make SPLIT=1 pgo # the same, profile-guided (what turbo-compile-core runs) make SPLIT=1 extension # only the extension, against a phpstan_turbo_core.so already here make SPLIT=1 strip # both; a plain `make strip` leaves the core untouched +make SPLIT=1 pgo-pack # after `pgo`: the core's profile, into pgo-profile.tar +make pgo-from-profile # self-contained, from that profile (CI's pie legs); trains if it does not fit ``` A plain `make`, the phpize build (`config.m4`, what PIE drives) and diff --git a/turbo-ext/Makefile b/turbo-ext/Makefile index c636d72e48f..a45dcebd0c4 100644 --- a/turbo-ext/Makefile +++ b/turbo-ext/Makefile @@ -332,18 +332,66 @@ pgo: $(MAKE) pgo-clean $(MAKE) PGO_FLAGS="$(PGO_GEN_FLAGS)" phpstan_turbo.so PGO_DIR="$(PGO_DIR)" bin/pgo-train.sh - rm -f $(OBJECTS) phpstan_turbo.so $(CORE_LIB) ifeq ($(CXX_IS_CLANG),1) $(LLVM_PROFDATA) merge -output=$(PGO_DIR)/turbo.profdata $(PGO_DIR)/*.profraw endif + $(MAKE) pgo-use + +# the optimised stage of `make pgo` alone, from a profile already in place: +# the one `make pgo` just recorded, or one unpacked from $(PGO_PROFILE) +pgo-use: + rm -f $(OBJECTS) phpstan_turbo.so $(CORE_LIB) $(MAKE) PGO_FLAGS="$(PGO_USE_FLAGS)" phpstan_turbo.so +# The core's share of the profile `make SPLIT=1 pgo` recorded, with the +# compiler that recorded it (a profile only fits that one: GCC rejects +# another version's .gcda files), so that `make pgo-use` builds the +# self-contained extension of every other PHP version from it without a +# training run of its own — the CI pie legs (phar.yml). The core compiles to +# the same code against every supported version's headers +# (bin/shared-core/gate-*.sh), so its profile fits nearly all of them (see +# pgo-from-profile); the shim's +# records stay out, since those objects do not (GCC rejects them as a +# coverage mismatch), and the shim builds without a profile, like the shim +# of the split extension. Clang's one merged file is taken whole: it drops +# the records of functions that changed on its own. +PGO_PROFILE := pgo-profile.tar +CXX_VERSION = $(shell $(CXX) --version | head -n 1) +pgo-pack: + mkdir -p $(PGO_DIR) + echo '$(CXX_VERSION)' > $(PGO_DIR)/compiler.txt +ifeq ($(CXX_IS_CLANG),1) + tar -cf $(PGO_PROFILE) $(PGO_DIR)/compiler.txt $(PGO_DIR)/turbo.profdata +else + tar -cf $(PGO_PROFILE) $(PGO_DIR)/compiler.txt $(wildcard $(CORE_OBJECTS:.o=.gcda)) +endif + +# `make pgo-use` from the $(PGO_PROFILE) pgo-pack wrote, when the compiler +# that recorded it is the one in use; `make pgo` otherwise (or without one). +# The same code is not the same control flow when GCC instruments it, before +# its optimisations: against other headers a few functions count other arcs +# (with the GCC 11.4 profile of an 8.5 core, 85 of 33136 functions for +# 8.2-zts, none for 8.4, 8.4-zts or 8.5). GCC rejects the profile of such a +# function, which then builds without one, like the shim; the warning would +# fail the strict build, so it is off. +ifneq ($(CXX_IS_CLANG),1) +PGO_FOREIGN_FLAGS := -Wno-coverage-mismatch +endif +pgo-from-profile: + @if [ -f $(PGO_PROFILE) ] && [ "$$(tar -xOf $(PGO_PROFILE) $(PGO_DIR)/compiler.txt)" = '$(CXX_VERSION)' ]; then \ + echo "building with the profile in $(PGO_PROFILE)"; \ + tar -xf $(PGO_PROFILE) && $(MAKE) pgo-use PGO_USE_FLAGS="$(PGO_USE_FLAGS) $(PGO_FOREIGN_FLAGS)"; \ + else \ + echo "no profile recorded by $(CXX_VERSION) in $(PGO_PROFILE); training one"; \ + $(MAKE) pgo; \ + fi + # llvm-profdata must match the clang in use: Xcode ships it behind xcrun, # Linux distributions suffix it with the LLVM major version LLVM_PROFDATA ?= $(shell command -v llvm-profdata 2>/dev/null || (command -v xcrun > /dev/null 2>&1 && echo "xcrun llvm-profdata") || ls /usr/bin/llvm-profdata-* 2>/dev/null | sort -V | tail -1) pgo-clean: - rm -f $(OBJECTS) phpstan_turbo.so $(CORE_LIB) version.stamp + rm -f $(OBJECTS) phpstan_turbo.so $(CORE_LIB) version.stamp $(PGO_PROFILE) rm -rf $(PGO_DIR) find src -name '*.gcda' -delete @@ -432,4 +480,4 @@ sanitize: done $(MAKE) clean -.PHONY: clean lint pgo pgo-clean print-clang-tidy-version print-warn-flags sanitize strip FORCE +.PHONY: clean lint pgo pgo-clean pgo-from-profile pgo-pack pgo-use print-clang-tidy-version print-warn-flags sanitize strip FORCE From 0098eda90f5d0302aec220e385f8e69a7f31605d Mon Sep 17 00:00:00 2001 From: "ondrej@mirtes.cz" Date: Wed, 28 Oct 2026 22:57:06 +0000 Subject: [PATCH 2/2] Cache the PHP the musl legs build from source The musl thread-safe legs and 8.2 build PHP from the release tarball, about 6 minutes per leg. install-alpine-php.sh now keeps that build in $PHP_CACHE_DIR, which phar.yml restores with actions/cache under the key `install-alpine-php.sh --resolve` prints (Alpine release, architecture, PHP release, thread safety), so a new patch release or RC builds again. Installing from the cache takes seconds. Amp-Thread-ID: https://ampcode.com/threads/T-01a18495-f2db-74a3-a734-f1c0dbe24b9f --- .github/scripts/install-alpine-php.sh | 52 ++++++++++++++++++++++++--- .github/turbo-build/build-php.sh | 6 ++++ .github/workflows/phar.yml | 47 ++++++++++++++++++++++-- .gitignore | 1 + 4 files changed, 99 insertions(+), 7 deletions(-) diff --git a/.github/scripts/install-alpine-php.sh b/.github/scripts/install-alpine-php.sh index 6cb58f71844..e1ad5debb93 100644 --- a/.github/scripts/install-alpine-php.sh +++ b/.github/scripts/install-alpine-php.sh @@ -5,19 +5,63 @@ # no thread-safe PHP, while the official php:*-alpine Docker images are # thread-safe from 8.6 on — and for a minor the Alpine release no longer # packages (8.2). +# +# That build takes minutes, so with $PHP_CACHE_DIR set the PHP it installs +# into /usr/local (where nothing else lives in the Alpine images) is kept +# there as php.tar, and installed from there when the directory already has +# it. phar.yml keeps the directory in actions/cache under the key +# `install-alpine-php.sh --resolve` prints: the Alpine release, the +# architecture, the PHP release build-php.sh resolves and the thread +# safety, so a new patch release (or release candidate) or a new Alpine +# snapshot builds again. --resolve installs no PHP and prints +# `release=` when this leg builds PHP, `release=` when it installs +# Alpine's packages. set -eu -apk add --no-cache bash curl git make g++ musl-dev linux-headers patch tar zstd +apk add --no-cache bash curl git make g++ musl-dev linux-headers patch tar zstd >&2 V="$(echo "$PHP_MINOR" | tr -d .)" # Alpine's php86 packages are currently only in edge/testing. -if [ "$PHP_MINOR" = "8.6" ]; then - echo 'https://dl-cdn.alpinelinux.org/alpine/edge/testing' >> /etc/apk/repositories +EDGE_TESTING='https://dl-cdn.alpinelinux.org/alpine/edge/testing' +if [ "$PHP_MINOR" = "8.6" ] && ! grep -qx "$EDGE_TESTING" /etc/apk/repositories; then + echo "$EDGE_TESTING" >> /etc/apk/repositories fi +BUILD_PHP="$(dirname "$0")/../turbo-build/build-php.sh" +BUILDS_PHP=0 if [ "${PHP_ZTS:-0}" = "1" ] || [ -z "$(apk search -q -x "php$V")" ]; then + BUILDS_PHP=1 +fi + +if [ "${1:-}" = "--resolve" ]; then + if [ "$BUILDS_PHP" = "0" ]; then + echo "release=" + exit 0 + fi + apk add --no-cache jq >&2 + RELEASE="$(PHP_MINOR="$PHP_MINOR" PHP_RESOLVE_ONLY=1 sh "$BUILD_PHP")" + ZTS_SUFFIX="" + if [ "${PHP_ZTS:-0}" = "1" ]; then + ZTS_SUFFIX="-zts" + fi + echo "release=alpine$(cat /etc/alpine-release)-$(uname -m)-php$RELEASE$ZTS_SUFFIX" + exit 0 +fi + +if [ "$BUILDS_PHP" = "1" ]; then apk add --no-cache jq pkgconf xz libxml2-dev oniguruma-dev curl-dev openssl-dev zlib-dev - PHP_MINOR="$PHP_MINOR" PHP_ZTS="${PHP_ZTS:-0}" sh "$(dirname "$0")/../turbo-build/build-php.sh" + if [ -n "${PHP_CACHE_DIR:-}" ] && [ -f "$PHP_CACHE_DIR/php.tar" ]; then + if tar -xf "$PHP_CACHE_DIR/php.tar" -C /usr/local && php -r 'exit((int) PHP_ZTS === (int) getenv("PHP_ZTS") ? 0 : 1);'; then + echo "install-alpine-php.sh: installed PHP $(php -r 'echo PHP_VERSION;') from $PHP_CACHE_DIR" + exit 0 + fi + echo "install-alpine-php.sh: the PHP in $PHP_CACHE_DIR does not run here; building it again" >&2 + fi + PHP_MINOR="$PHP_MINOR" PHP_ZTS="${PHP_ZTS:-0}" sh "$BUILD_PHP" + if [ -n "${PHP_CACHE_DIR:-}" ]; then + mkdir -p "$PHP_CACHE_DIR" + tar -cf "$PHP_CACHE_DIR/php.tar" -C /usr/local . + fi exit 0 fi diff --git a/.github/turbo-build/build-php.sh b/.github/turbo-build/build-php.sh index 2cb50b12348..16b4ce78c7d 100644 --- a/.github/turbo-build/build-php.sh +++ b/.github/turbo-build/build-php.sh @@ -43,6 +43,12 @@ if [ -z "$PHP_URL" ] || [ -z "$PHP_SHA256" ]; then echo "build-php.sh: php.net lists no tar.xz with a sha256 for PHP $PHP_VERSION" >&2 exit 1 fi +# install-alpine-php.sh --resolve: the release alone, for the cache key of +# the PHP it builds +if [ "${PHP_RESOLVE_ONLY:-0}" = "1" ]; then + echo "$PHP_VERSION" + exit 0 +fi echo "build-php.sh: building PHP $PHP_VERSION from $PHP_URL" export PHP_VERSION PHP_ZTS="${PHP_ZTS:-0}" diff --git a/.github/workflows/phar.yml b/.github/workflows/phar.yml index ac538c2675b..8fc1d8aa7e2 100644 --- a/.github/workflows/phar.yml +++ b/.github/workflows/phar.yml @@ -928,10 +928,27 @@ jobs: - name: "Trust the checkout despite the container/host uid mismatch" run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + # The thread-safe legs and 8.2 build PHP from source, minutes on every + # leg; install-alpine-php.sh keeps that build in alpine-php-cache, under + # a key naming exactly what it built (see the script). Nothing to cache + # for a leg installing Alpine's packages (release= is empty). + - name: "Resolve the PHP build to cache (Alpine container)" + id: alpine-php + if: matrix.target.family == 'musl' + run: sh .github/scripts/install-alpine-php.sh --resolve >> "$GITHUB_OUTPUT" + + - name: "Restore the PHP build (Alpine container)" + if: steps.alpine-php.outputs.release != '' + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: "alpine-php-cache" + key: "alpine-php-${{ steps.alpine-php.outputs.release }}-${{ hashFiles('.github/turbo-build/build-php.sh') }}" + # The build needs php/php-config on PATH for the matrix PHP version. - name: "Install PHP and build tools (Alpine container)" if: matrix.target.family == 'musl' - run: sh .github/scripts/install-alpine-php.sh + # the container's path of the workspace, which github.workspace is not + run: PHP_CACHE_DIR="$GITHUB_WORKSPACE/alpine-php-cache" sh .github/scripts/install-alpine-php.sh - name: "Install PHP (macOS)" if: matrix.target.family == 'macos' @@ -1098,9 +1115,21 @@ jobs: run: | docker run -d --name alpine-build \ -v "$PWD:/work" -w /work \ - -e PHP_MINOR -e PHP_ZTS \ + -e PHP_MINOR -e PHP_ZTS -e PHP_CACHE_DIR=/work/alpine-php-cache \ "$ALPINE_IMAGE" sleep 7200 + # see turbo-compile + - name: "Resolve the PHP build to cache" + id: alpine-php + run: docker exec alpine-build sh .github/scripts/install-alpine-php.sh --resolve >> "$GITHUB_OUTPUT" + + - name: "Restore the PHP build" + if: steps.alpine-php.outputs.release != '' + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: "alpine-php-cache" + key: "alpine-php-${{ steps.alpine-php.outputs.release }}-${{ hashFiles('.github/turbo-build/build-php.sh') }}" + - name: "Install PHP and build tools" run: docker exec alpine-build sh .github/scripts/install-alpine-php.sh @@ -2154,9 +2183,21 @@ jobs: run: | docker run -d --name alpine-test \ -v "$PWD:/work" -w /work \ - -e PHP_MINOR -e PHP_ZTS \ + -e PHP_MINOR -e PHP_ZTS -e PHP_CACHE_DIR=/work/alpine-php-cache \ "$ALPINE_IMAGE" sleep 7200 + # see turbo-compile + - name: "Resolve the PHP build to cache" + id: alpine-php + run: docker exec alpine-test sh .github/scripts/install-alpine-php.sh --resolve >> "$GITHUB_OUTPUT" + + - name: "Restore the PHP build" + if: steps.alpine-php.outputs.release != '' + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: "alpine-php-cache" + key: "alpine-php-${{ steps.alpine-php.outputs.release }}-${{ hashFiles('.github/turbo-build/build-php.sh') }}" + - name: "Install PHP and tools" run: docker exec alpine-test sh .github/scripts/install-alpine-php.sh diff --git a/.gitignore b/.gitignore index 9848a14eacf..4144e5c7af9 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ e2e/bashunit e2e/restart-manager/probe.log e2e/restart-manager/starts.log e2e/restart-manager/out.txt +/alpine-php-cache