Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Ship the turbo extension as a shared core plus a thin extension per P…
…HP version

phar.yml builds one profile-guided core per platform (turbo-compile-core,
-core-musl-arm64, -core-windows for NTS and TS; the jobs turbo-origins now
reuses from earlier runs) and links each PHP version's extension against
it in the existing turbo-compile* legs, whose artifacts carry the core
next to the extension so every test job finds it there.
.github/scripts/turbo-dist-layout.sh maps the artifacts onto the dist
layout (turbo-ext/<platform>/phpstan_turbo_core.so|.dll next to
phpstan_turbo-<minor>.so|.dll) for the aggregate artifact and the commit
job, refusing legs of one platform that carry different cores.
turbo-shared-core-gate (bin/shared-core/check-linux.sh) compiles the shared
sources against every supported PHP's headers with clang and fails on any
difference outside the four version-specific sources; the commit job waits
for it.

config.w32 gains --enable-phpstan-turbo=core / =extension; the extension
DLL delay-loads the core from its own directory. TurboExtensionSelector
offers no binary when the core is missing next to the extension. The
Makefile's default goal stays the extension (it had become `objects`), and
a plain `make strip` leaves a core put in place untouched.

README.md documents the layout, the mechanisms that keep shared code
version-neutral, the gate, and the procedure for supporting a new PHP
version; turbo-ext/CLAUDE.md the rules for code in shared sources.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017h1GfB7vfJPgwcTHD9Fth6
  • Loading branch information
ondrejmirtes and claude committed Oct 8, 2026
commit 99790406e0999eda0ba49088d393f5960f4dab41
25 changes: 25 additions & 0 deletions .github/scripts/download-php-windows-devel.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Shared by the Windows turbo jobs in phar.yml: exports the extension version
# (the short SHA of the last commit touching turbo-ext/src, the Makefile's
# computation) and fetches the php-devel-pack matching the installed PHP
# (MATRIX_TS: nts/zts, MATRIX_VS: its toolset infix) into C:\php-devel and the
# php-sdk-binary-tools at PHP_SDK_COMMIT into C:\php-sdk.
set -euo pipefail

SHA=$(git log -1 --format=%H -- turbo-ext/src | cut -c1-7)
echo "extension version: $SHA"
echo "PHPSTANTURBO_VERSION=$SHA" >> "$GITHUB_ENV"

FULL=$(php -r 'echo PHP_VERSION;')
# thread-safe devel packs carry no infix, NTS ones carry -nts
INFIX=$([ "$MATRIX_TS" = "zts" ] && echo "" || echo "-nts")
PACK="php-devel-pack-$FULL$INFIX-Win32-$MATRIX_VS-x64.zip"
echo "devel pack: $PACK"
curl -fsSLo devel-pack.zip "https://windows.php.net/downloads/releases/$PACK" \
|| curl -fsSLo devel-pack.zip "https://windows.php.net/downloads/releases/archives/$PACK" \
|| curl -fsSLo devel-pack.zip "https://downloads.php.net/~windows/qa/$PACK" \
|| curl -fsSLo devel-pack.zip "https://downloads.php.net/~windows/qa/archives/$PACK"
unzip -q devel-pack.zip -d /c/php-devel

git clone -q https://github.com/php/php-sdk-binary-tools.git /c/php-sdk
git -C /c/php-sdk checkout -q "$PHP_SDK_COMMIT"
23 changes: 13 additions & 10 deletions .github/scripts/find-turbo-origins.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#!/usr/bin/env bash
# Finds, for every turbo extension binary the compile jobs in phar.yml
# produce, the newest earlier run on $BRANCH that compiled it from build
# inputs identical to HEAD's, and writes {"<artifact name>": <run id>} to
# the `origins` step output. A compile leg whose artifact is listed
# downloads it from that run instead of building it again (see the
# turbo-origins job in phar.yml).
# Finds, for every shared core the turbo-compile-core jobs in phar.yml
# produce (phpstan_turbo_core-<platform>), the newest earlier run on $BRANCH
# that compiled it from build inputs identical to HEAD's, and writes
# {"<artifact name>": <run id>} to the `origins` step output. A core leg
# whose artifact is listed downloads it from that run instead of building
# (and profiling) it again (see the turbo-origins job in phar.yml). The
# per-PHP-version extensions are not reused: they compile in a minute,
# against whichever core this run carries.
#
# The search walks HEAD's first-parent history (on a pull request's merge
# commit, the base branch) and asks the API for the runs of each commit
Expand Down Expand Up @@ -45,11 +47,12 @@ BUILD_INPUT_PATHS=(
.github/turbo-build
.github/scripts/install-alpine-php.sh
.github/scripts/install-php86-windows.sh
.github/scripts/download-php-windows-devel.sh
)
WORKFLOW_PATH=".github/workflows/phar.yml"
# Workflow-level env and the three compile jobs, as JSON: comment-only edits
# do not change it.
COMPILE_JOBS_QUERY='[.env, .jobs["turbo-compile"], .jobs["turbo-compile-musl-arm64"], .jobs["turbo-compile-windows"]]'
# Workflow-level env and the three core compile jobs, as JSON: comment-only
# edits do not change it.
COMPILE_JOBS_QUERY='[.env, .jobs["turbo-compile-core"], .jobs["turbo-compile-core-musl-arm64"], .jobs["turbo-compile-core-windows"]]'
# Commits whose runs are looked up through the API, newest first. The
# commits git alone rules out do not count.
COMMITS_LIMIT=50
Expand Down Expand Up @@ -123,7 +126,7 @@ while read -r sha; do
fi
if ! origins="$(jq -c --argjson run "$run_id" --arg artifacts "$artifacts" '
($artifacts | split("\n")) as $names
| reduce ($names[] | select(startswith("phpstan_turbo-"))) as $name (.;
| reduce ($names[] | select(startswith("phpstan_turbo_core-"))) as $name (.;
if has($name) or ($names | any(. == "turbo-reused-" + $name)) then . else .[$name] = $run end)
' <<< "$origins")"; then
origins='{}'
Expand Down
59 changes: 59 additions & 0 deletions .github/scripts/turbo-dist-layout.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
# Maps the turbo compile artifacts of a phar.yml run onto the layout
# phpstan/phpstan ships (turbo-ext/<target>/...), printing one
# "<artifact file><TAB><path under turbo-ext/>" line per distributed file.
#
# An artifact phpstan_turbo-<target>-php<minor>[-zts] carries the extension
# of that PHP version (phpstan_turbo.so, php_phpstan_turbo.dll) and the
# platform's shared core next to it (phpstan_turbo_core*.so/.dll), which the
# extension loads from its own directory:
# extension -> <target>/phpstan_turbo-<minor>[-zts].<so|dll>
# core -> <target>/<its own name>
# Every leg of a platform links against the one core turbo-compile-core
# built for it, so each of its artifacts carries the same core: it is listed
# once, and different bytes under one name fail the mapping — such a set
# would ship extensions next to a core they were not linked against.
#
# Usage: turbo-dist-layout.sh <artifacts directory>
set -euo pipefail

artifacts=$1
declare -A core_source=()
status=0
mapped=0

for dir in "$artifacts"/phpstan_turbo-*; do
[ -d "$dir" ] || continue
name="${dir#"$artifacts"/phpstan_turbo-}"
>
minor="${name##*-php}"
for file in "$dir"/*; do
[ -f "$file" ] || continue
base="${file##*/}"
case "$base" in
phpstan_turbo_core*)
dest="$target/$base"
if [ -n "${core_source[$dest]:-}" ]; then
if ! cmp -s "${core_source[$dest]}" "$file"; then
echo "::error::$file and ${core_source[$dest]} carry different cores for $dest" >&2
status=1
fi
continue
fi
core_source[$dest]="$file"
;;
*)
dest="$target/phpstan_turbo-$minor.${base##*.}"
;;
esac
printf '%s\t%s\n' "$file" "$dest"
mapped=$((mapped + 1))
done
done

if [ "$mapped" -eq 0 ]; then
echo "::error::no phpstan_turbo-* artifacts found in $artifacts" >&2
status=1
fi

exit "$status"
Loading