From 2d14c9e432a7dead8f5948823bc5b751c2efaa8e Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 25 Aug 2026 20:02:06 +0800 Subject: [PATCH] ``` chore(tests): preserve generated C++ sources for CI artifacts - Remove copilot instructions file that was causing confusion - Add logic to copy generated .cc and .h files when PHPT tests fail - Set up TYPEPHP_PHPT_GENERATED_ARTIFACT_DIR environment variable in CI - Include generated source files in uploaded artifacts for debugging - Replace curl extension with embed extension in workflow configuration - Remove explicit PHP embed library installation from CI steps - Update PHPT runner to use bootstrap compiler via --compiler flag ``` --- .github/copilot-instructions.md | 90 --------------------------------- .github/workflows/tests.yml | 10 ++-- run-tests.php | 45 +++++++++++++++++ 3 files changed, 51 insertions(+), 94 deletions(-) delete mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index b21112d4..00000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,90 +0,0 @@ -# Copilot instructions for this repository - -## Project overview - -TypePHP is a PHP native compilation project. Its `tpc` command is TypePHP Compiler (AOT), which translates PHP source into C++, then compiles and links it into a native binary or a PHP extension. The primary entrypoint boots `src/compiler.php`; that drives `TypePhp\Translator` through a fixed pipeline: - -1. `prepare()` scans files, parses ASTs, collects symbols, and topologically sorts PHP files by cross-file symbol usage. -2. `convert()` turns PHP ASTs into generated `.cc` files while passing through native source files (`.cpp`, `.c`, `.s`, `.m`, `.mm`). -3. `compile()` chooses the platform/compiler backend, generates support sources and headers, and compiles sources, using `pcntl` parallelism when available. -4. `build()` links object files into the final executable or extension. - -`src/CompilerBase.php` contains most PHP-to-C++ translation logic and mixes in many traits for syntax handling and optimizations. `src/Preprocessor.php` owns dependency discovery and file ordering. Platform-specific behavior lives under `src/Platform/`, compiler backends under `src/Backend/`, and metadata/state objects under `src/Entity/` and `src/Context/`. - -## Setup and build commands - -The repo expects PHP 8.4+, GCC 9+ with C++17, CMake 3.24+, and a compiled `swoole/phpx` dependency. Install PHP dependencies with: - -```bash -composer install -``` - -Build `phpx` before relying on compiler runs: - -```bash -cd vendor/swoole/phpx -cmake . -make -j32 -``` - -Compile a project, directory, single file, or `project.yml`: - -```bash -./tpc -./tpc -O2 -./tpc --mode=ext -o -``` - -## Test commands - -Run the PHPUnit suite: - -```bash -./vendor/bin/phpunit -``` - -Run a single PHPUnit file or a single test method: - -```bash -./vendor/bin/phpunit phpunit/src/Platform/PlatformTest.php -./vendor/bin/phpunit --filter testWindowsBasic phpunit/src/Platform/PlatformTest.php -``` - -Run PHPT integration tests: - -```bash -php run-tests.php tests/compiler/ -php run-tests.php tests/compiler/arrays.phpt -``` - -For parser/runtime comparison without AOT compilation, there are docs using: - -```bash -php run-tests.php --no-aot tests/compiler/arrow-functions.phpt -``` - -## Formatting - -The repo ships a PHP CS Fixer config in `.php-cs-fixer.dist.php`: - -```bash -php vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php -``` - -Generated C++ is auto-formatted by the compiler itself when `clang-format` is available. - -## Configuration and repository conventions - -- `project.yml` is the project-level build config. Important keys include `name`, `build-mode`, `cxx-std`, `cxx-flags`, `ld-flags`, `sources`, `ignore`, and `resource`. -- Command-line options intentionally override YAML values. `Translator` parses YAML first, then applies CLI arguments last. -- YAML parsing accepts both hyphenated and underscored variants for several keys, but existing examples use hyphenated names such as `build-mode` and `cxx-std`. -- In `bin` mode, compiled programs must define `main()`. In `ext` mode they do not. -- File discovery is mixed-language by design: PHP is translated, while native sources are compiled directly if they appear in configured sources. -- Generated files are written under `build/`, with generated C++ paths mirroring the source tree and generated headers under `build/include/`. -- Platform/compiler selection is centralized: `PlatformFactory` detects the OS, and `CompilerFactory` picks the backend (`Gcc`, `Clang`, `Msvc`) with environment/config overrides. - -## Test-specific conventions - -- PHPUnit tests for compiler internals should use `CompilerTest::create(ROOT_PATH)`, which enables test mode instead of normal fatal exits. -- `phpunit/bootstrap.php` exposes a `BaseTest::exec()` helper that expects compilation failures to surface as `TypePhp\Exception\TestError`. -- PHPT end-to-end tests live in `tests/compiler/`; existing guidance and examples generally put executable test logic inside a `main()` function. diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 60c26a5a..ab529d6c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -169,6 +169,7 @@ jobs: PHPX_HOME: ${{ github.workspace }}/vendor/swoole/phpx NO_INTERACTION: 1 REPORT_EXIT_STATUS: 1 + TYPEPHP_PHPT_GENERATED_ARTIFACT_DIR: ${{ github.workspace }}/build/phpt-generated steps: - name: Checkout TypePHP @@ -179,7 +180,7 @@ jobs: with: php-version: ${{ matrix.php }} coverage: none - extensions: curl, redis, mbstring, ffi + extensions: embed, curl, redis, mbstring, ffi ini-values: ffi.enable=1, phpy.enable_operator_overloading=0, opcache.jit=0, precision=17, memory_limit=4G, error_reporting=E_ERROR|E_WARNING, display_errors=1, display_startup_errors=1, log_errors=0 tools: composer:v2 @@ -189,8 +190,7 @@ jobs: - name: Install native build dependencies run: | sudo apt-get update - sudo apt-get install --yes build-essential cmake libgmp-dev libmpfr-dev pkg-config python3-dev \ - "libphp${{ matrix.php }}-embed" + sudo apt-get install --yes build-essential cmake libgmp-dev libmpfr-dev pkg-config python3-dev - name: Configure version-matched PHP embed library shell: bash @@ -264,7 +264,7 @@ jobs: - name: Run compiler PHPT suite with bootstrap compiler run: | mkdir -p build - php run-tests.php -q -j8 \ + php run-tests.php -q -j8 --compiler ./tpc \ -w build/failed-tests.txt -W build/test-results.txt tests/compiler - name: Upload PHPT failure artifacts @@ -277,6 +277,8 @@ jobs: path: | build/failed-tests.txt build/test-results.txt + build/**/*.cc + build/**/*.h php_test_results_*.txt tests/compiler/**/*.diff tests/compiler/**/*.log diff --git a/run-tests.php b/run-tests.php index 5bd49949..e5cbc226 100755 --- a/run-tests.php +++ b/run-tests.php @@ -1686,6 +1686,9 @@ escape: error("$testsInProgress test batches “in progress”, which is less than zero. THIS SHOULD NOT HAPPEN."); } + if ($PHP_FAILED_TESTS['FAILED']) { + copy_aot_generated_sources_for_artifact($aot_parallel_root); + } remove_directory($aot_parallel_root); $aot_parallel_root = null; } @@ -4624,6 +4627,48 @@ function ensure_directory_exists(string $directory): void } } +/** + * Preserve generated C++ and header files before the parallel PHPT workspace + * is removed. CI sets TYPEPHP_PHPT_GENERATED_ARTIFACT_DIR to a directory under + * the checkout so actions/upload-artifact can include these diagnostics. + */ +function copy_aot_generated_sources_for_artifact(string $sourceRoot): void +{ + $artifactRoot = getenv('TYPEPHP_PHPT_GENERATED_ARTIFACT_DIR'); + if (!is_string($artifactRoot) || $artifactRoot === '' || !is_dir($sourceRoot)) { + return; + } + + $sourceRoot = rtrim($sourceRoot, '/\\'); + $destinationRoot = rtrim($artifactRoot, '/\\') + . DIRECTORY_SEPARATOR . basename($sourceRoot); + + try { + $copied = 0; + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($sourceRoot, FilesystemIterator::SKIP_DOTS), + RecursiveIteratorIterator::LEAVES_ONLY, + ); + foreach ($iterator as $entry) { + if (!$entry->isFile() || !in_array(strtolower($entry->getExtension()), ['cc', 'h'], true)) { + continue; + } + + $relativePath = substr($entry->getPathname(), strlen($sourceRoot) + 1); + $destination = $destinationRoot . DIRECTORY_SEPARATOR . $relativePath; + ensure_directory_exists(dirname($destination)); + if (!copy($entry->getPathname(), $destination)) { + throw new RuntimeException('Cannot copy generated source: ' . $entry->getPathname()); + } + $copied++; + } + fwrite(STDERR, "Preserved {$copied} generated PHPT source files in {$destinationRoot}" . PHP_EOL); + } catch (Throwable $e) { + fwrite(STDERR, 'Warning: failed to preserve PHPT generated sources: ' + . $e->getMessage() . PHP_EOL); + } +} + function remove_directory(string $directory): void { if (!is_dir($directory)) {