From 63750644bb237e96567c9fbf137a72995bd8e40b Mon Sep 17 00:00:00 2001 From: PurHur Date: Tue, 19 May 2026 10:13:37 +0000 Subject: [PATCH] CI: fail ci-local.sh when LLVM present but JIT compliance 100% skipped After the @group llvm PHPUnit stage, parse JUnit XML and exit non-zero if JITTest ran zero tests while libLLVM-9.so.1 exists. Override with PHP_COMPILER_ALLOW_JIT_SKIP=1 for broken dev environments. Closes #250 Co-authored-by: Cursor --- README.md | 1 + docs/bootstrap-inventory.md | 20 ++++++--- script/check-jit-compliance-ran.php | 69 +++++++++++++++++++++++++++++ script/ci-local.sh | 9 +++- 4 files changed, 91 insertions(+), 8 deletions(-) create mode 100755 script/check-jit-compliance-ran.php diff --git a/README.md b/README.md index bbc372b0..5dd27ae2 100755 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ composer install | `PHP_COMPILER_LLVM_PATH` | Path to LLVM 9 `clang`, `ld`, and `libLLVM-9.so.1` (default: repo `.llvm/` after install) | | `PHP_COMPILER_SKIP_SERVE_TESTS` | Skip `ServeTest` / `ServeAotTest` (set on GitHub Actions; use in sandboxes that cannot bind TCP) | | `PHP_COMPILER_RUN_SERVE_TESTS` | Force HTTP serve integration tests even when loopback bind probe fails | +| `PHP_COMPILER_ALLOW_JIT_SKIP` | Do not fail `ci-local.sh` when LLVM is present but JIT compliance tests are 100% skipped (broken dev env only) | `script/ci-local.sh` sets LLVM paths automatically when `.llvm/libLLVM-9.so.1` exists. It probes `127.0.0.1` bind capability and runs `@group serve` tests when allowed. **GitHub Actions** sets `PHP_COMPILER_SKIP_SERVE_TESTS=1` because hosted runners often block listeners; **local and Docker CI** (`make test-docker`, `./script/docker-ci-local.sh`) must run those tests — do not export the skip variable there. diff --git a/docs/bootstrap-inventory.md b/docs/bootstrap-inventory.md index e8ae112b..f90dca40 100644 --- a/docs/bootstrap-inventory.md +++ b/docs/bootstrap-inventory.md @@ -8,9 +8,9 @@ Regenerate: `php script/bootstrap-inventory.php` | Metric | Count | |--------|------:| -| PHP files on vm.php path | 192 | +| PHP files on vm.php path | 193 | | Source constructs flagged (blockers) | 10 | -| Source constructs flagged (warnings) | 514 | +| Source constructs flagged (warnings) | 515 | ## Compiler CFG gaps (`lib/Compiler.php`) @@ -44,6 +44,7 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered: | `ext/standard/JitRealpath.php` | 0 | 1 | | `ext/standard/JitStrPad.php` | 0 | 1 | | `ext/standard/JitStrRepeat.php` | 0 | 1 | +| `ext/standard/JitStrReplace.php` | 0 | 1 | | `ext/standard/JitStringConcat.php` | 0 | 1 | | `ext/standard/JitStringIndex.php` | 0 | 1 | | `ext/standard/JitStrpos.php` | 0 | 1 | @@ -281,6 +282,11 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered: **Warnings** (review for bootstrap subset): - 1 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler +### `ext/standard/JitStrReplace.php` + +**Warnings** (review for bootstrap subset): +- 1 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler + ### `ext/standard/JitStringConcat.php` **Warnings** (review for bootstrap subset): @@ -809,7 +815,7 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered: ### `ext/standard/str_replace.php` **Warnings** (review for bootstrap subset): -- 2 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler +- 3 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler ### `ext/standard/str_split.php` @@ -1048,10 +1054,10 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered: - new JIT\Call\Native (line 149) - new ext\standard\boolval (line 249) - new Variable (line 413) -- new Variable (line 712) -- new Operand\Literal (line 782) -- new Operand\Literal (line 786) -- new Operand\Literal (line 790) +- new Variable (line 718) +- new Operand\Literal (line 788) +- new Operand\Literal (line 792) +- new Operand\Literal (line 796) - 11 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler ### `lib/JIT/Analyzer.php` diff --git a/script/check-jit-compliance-ran.php b/script/check-jit-compliance-ran.php new file mode 100755 index 00000000..1884692c --- /dev/null +++ b/script/check-jit-compliance-ran.php @@ -0,0 +1,69 @@ +#!/usr/bin/env php + [llvm-dir]\n"); + exit(2); +} + +$junitPath = $argv[1]; +$llvmDir = $argv[2] ?? getenv('PHP_COMPILER_LLVM_PATH') ?: ''; + +if (!is_file($junitPath)) { + fwrite(STDERR, "JIT compliance guard: JUnit log not found: {$junitPath}\n"); + exit(1); +} + +$xml = @simplexml_load_file($junitPath); +if (false === $xml) { + fwrite(STDERR, "JIT compliance guard: could not parse JUnit XML: {$junitPath}\n"); + exit(1); +} + +$total = 0; +$skipped = 0; +$executed = 0; + +/** @var \SimpleXMLElement $testcase */ +foreach ($xml->xpath('//testcase') ?: [] as $testcase) { + $classname = (string) ($testcase['classname'] ?? ''); + $file = (string) ($testcase['file'] ?? ''); + if (!str_contains($classname, 'JITTest') && !str_contains($file, 'JITTest.php')) { + continue; + } + ++$total; + if (isset($testcase->skipped)) { + ++$skipped; + } else { + ++$executed; + } +} + +if (0 === $total) { + fwrite(STDERR, "JIT compliance guard: no JITTest cases in {$junitPath}\n"); + fwrite(STDERR, " Ensure @group llvm includes test/compliance/JITTest.php.\n"); + exit(1); +} + +if (0 === $executed) { + fwrite(STDERR, "JIT compliance guard FAILED: LLVM is present but all {$total} JIT tests were skipped.\n"); + if ('' !== $llvmDir) { + fwrite(STDERR, " LLVM dir: {$llvmDir}\n"); + } + fwrite(STDERR, " Fix: export PHP_COMPILER_LLVM_PATH to a tree containing libLLVM-9.so.1\n"); + fwrite(STDERR, " and prepend that directory to LD_LIBRARY_PATH and PATH.\n"); + fwrite(STDERR, " Docker: use /opt/llvm9 (image #237) — avoid a broken host .llvm/ bind-mount override.\n"); + fwrite(STDERR, " Override (broken dev env only): PHP_COMPILER_ALLOW_JIT_SKIP=1\n"); + exit(1); +} + +fwrite(STDOUT, "JIT compliance guard OK: {$executed} of {$total} JIT tests executed ({$skipped} skipped).\n"); +exit(0); diff --git a/script/ci-local.sh b/script/ci-local.sh index 85f5621c..a98d87a0 100755 --- a/script/ci-local.sh +++ b/script/ci-local.sh @@ -72,5 +72,12 @@ fi if [[ -f "$LLVM_DIR/libLLVM-9.so.1" ]]; then echo "PHPUnit: JIT, AOT (web fixtures + examples, ExamplesCompileTest AOT lint)..." - "$PHP_BIN" "${PHP_OPTS[@]}" vendor/bin/phpunit --group llvm --exclude-group serve "$@" + LLVM_JUNIT="$(mktemp "${TMPDIR:-/tmp}/llvm-junit.XXXXXX.xml")" + "$PHP_BIN" "${PHP_OPTS[@]}" vendor/bin/phpunit --group llvm --exclude-group serve --log-junit "$LLVM_JUNIT" "$@" + if [[ -n "${PHP_COMPILER_ALLOW_JIT_SKIP:-}" ]]; then + echo "JIT compliance guard skipped (PHP_COMPILER_ALLOW_JIT_SKIP is set)." + else + "$PHP_BIN" "${PHP_OPTS[@]}" script/check-jit-compliance-ran.php "$LLVM_JUNIT" "$LLVM_DIR" + fi + rm -f "$LLVM_JUNIT" fi