diff --git a/.github/workflows/ext-matrix-tests.yml b/.github/workflows/ext-matrix-tests.yml index e38ed2ed5..2396718c1 100644 --- a/.github/workflows/ext-matrix-tests.yml +++ b/.github/workflows/ext-matrix-tests.yml @@ -82,7 +82,7 @@ jobs: - zlib - zstd php-version: - - "8.4" + - "git" operating-system: - "ubuntu-latest" #- "macos-13" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a360e54a8..f399a043e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -145,6 +145,10 @@ jobs: os: ${{ fromJSON(needs.define-matrix.outputs.os) }} fail-fast: false steps: + - name: "Update runner packages" + if: ${{ startsWith(matrix.os, 'ubuntu-') }} + run: sudo apt-get update && sudo apt-get install -y ca-certificates + - name: "Checkout" uses: actions/checkout@v4 diff --git a/README-zh.md b/README-zh.md index 0a0c45780..402fd6356 100755 --- a/README-zh.md +++ b/README-zh.md @@ -76,22 +76,23 @@ static-php-cli(简称 `spc`)有许多特性: 当前支持编译的 PHP 版本: -> :warning: 支持,但 static-php-cli 作者可能不再提供补丁修复 +> :warning: 部分支持,对于新的测试版和旧版本可能存在问题。 > > :heavy_check_mark: 支持 > > :x: 不支持 -| PHP Version | Status | Comment | -|-------------|--------------------|----------------------------------------------| -| 7.2 | :x: | | -| 7.3 | :x: | phpmicro 和许多扩展不支持 7.3、7.4 版本 | -| 7.4 | :x: | phpmicro 和许多扩展不支持 7.3、7.4 版本 | -| 8.0 | :warning: | PHP 官方已停止 8.0 的维护,我们不再处理 8.0 相关的 backport 支持 | -| 8.1 | :heavy_check_mark: | PHP 官方仅对 8.1 提供安全更新 | -| 8.2 | :heavy_check_mark: | | -| 8.3 | :heavy_check_mark: | | -| 8.4 | :heavy_check_mark: | | +| PHP Version | Status | Comment | +|-------------|--------------------|---------------------------------------------------------| +| 7.2 | :x: | | +| 7.3 | :x: | phpmicro 和许多扩展不支持 7.3、7.4 版本 | +| 7.4 | :x: | phpmicro 和许多扩展不支持 7.3、7.4 版本 | +| 8.0 | :warning: | PHP 官方已停止 8.0 的维护,我们不再处理 8.0 相关的 backport 支持 | +| 8.1 | :heavy_check_mark: | PHP 官方仅对 8.1 提供安全更新,在 8.5 发布后我们不再处理 8.1 相关的 backport 支持 | +| 8.2 | :heavy_check_mark: | | +| 8.3 | :heavy_check_mark: | | +| 8.4 | :heavy_check_mark: | | +| 8.5 (alpha) | :warning: | PHP 8.5 目前处于 alpha 阶段 | > 这个表格的支持状态是 static-php-cli 对构建对应版本的支持情况,不是 PHP 官方对该版本的支持情况。 diff --git a/README.md b/README.md index 46200f09f..22eda03fc 100755 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Here is the supported OS and arch, where :octocat: represents support for GitHub Currently supported PHP versions for compilation: -> :warning: supported but not maintained by static-php-cli authors +> :warning: Partial support, there may be issues with newer test versions or older versions. > > :heavy_check_mark: supported > @@ -95,10 +95,11 @@ Currently supported PHP versions for compilation: | 7.3 | :x: | phpmicro and some extensions not supported on 7.x | | 7.4 | :x: | phpmicro and some extensions not supported on 7.x | | 8.0 | :warning: | PHP official has stopped maintenance of 8.0, we no longer provide backport support for version 8.0 | -| 8.1 | :heavy_check_mark: | PHP official has security fixes only | +| 8.1 | :heavy_check_mark: | PHP official has security fixes only, we no longer provide backport support when 8.5 released | | 8.2 | :heavy_check_mark: | | | 8.3 | :heavy_check_mark: | | | 8.4 | :heavy_check_mark: | | +| 8.5 (alpha) | :warning: | PHP 8.5 is in alpha | > This table shows the support status for static-php-cli in building the corresponding version, > not the official PHP support status for that version. diff --git a/bin/spc-alpine-docker b/bin/spc-alpine-docker index 18e981f81..204273e33 100755 --- a/bin/spc-alpine-docker +++ b/bin/spc-alpine-docker @@ -104,6 +104,7 @@ RUN apk update; \ m4 \ make \ pkgconfig \ + re2c \ wget \ xz \ gettext-dev \ diff --git a/bin/spc-gnu-docker b/bin/spc-gnu-docker index 1c0331349..0d7084924 100755 --- a/bin/spc-gnu-docker +++ b/bin/spc-gnu-docker @@ -3,7 +3,7 @@ set -e # This file is using docker to run commands -SPC_DOCKER_VERSION=v4 +SPC_DOCKER_VERSION=v5 # Detect docker can run if ! which docker >/dev/null; then @@ -102,17 +102,20 @@ RUN curl -o cmake.tgz -#fSL https://github.com/Kitware/CMake/releases/download/v tar -xzf cmake.tgz -C /cmake --strip-components 1 WORKDIR /app -ADD ./src /app/src COPY ./composer.* /app/ ADD ./bin/setup-runtime /app/bin/setup-runtime ADD ./bin/spc /app/bin/spc RUN /app/bin/setup-runtime +ADD ./src /app/src RUN /app/bin/php /app/bin/composer install --no-dev ENV SPC_LIBC=glibc ENV PATH="/app/bin:/cmake/bin:/opt/rh/devtoolset-10/root/usr/bin:\$PATH" -ADD ./config/env.ini /app/config/env.ini +ADD ./config /app/config RUN CC=gcc bin/spc doctor --auto-fix --debug +RUN if [ -f /app/buildroot/bin/re2c ]; then \ + cp /app/buildroot/bin/re2c /usr/local/bin/re2c ;\ + fi RUN curl -o make.tgz -fsSL https://ftp.gnu.org/gnu/make/make-4.4.tar.gz && \ tar -zxvf make.tgz && \ diff --git a/composer.json b/composer.json index a3226d692..f3a290576 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,6 @@ "captainhook/hook-installer": "^1.0", "friendsofphp/php-cs-fixer": "^3.60", "humbug/box": "^4.5.0 || ^4.6.0", - "nunomaduro/collision": "^7.8", "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^10.3 || ^9.5" }, diff --git a/composer.lock b/composer.lock index a05a1e0d7..5beddd6a7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d705cec8270679a463d9e04310208967", + "content-hash": "dbefe1ee5c584f6f0cc64c26fb83451c", "packages": [ { "name": "illuminate/collections", @@ -416,47 +416,47 @@ }, { "name": "symfony/console", - "version": "v6.4.23", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "9056771b8eca08d026cd3280deeec3cfd99c4d93" + "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/9056771b8eca08d026cd3280deeec3cfd99c4d93", - "reference": "9056771b8eca08d026cd3280deeec3cfd99c4d93", + "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", + "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" + "symfony/string": "^7.2" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", "symfony/http-foundation": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -490,7 +490,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.23" + "source": "https://github.com/symfony/console/tree/v7.3.2" }, "funding": [ { @@ -501,12 +501,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T19:37:22+00:00" + "time": "2025-07-30T17:13:41+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1040,16 +1044,16 @@ }, { "name": "symfony/string", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125" + "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125", + "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", + "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", "shasum": "" }, "require": { @@ -1107,7 +1111,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.0" + "source": "https://github.com/symfony/string/tree/v7.3.2" }, "funding": [ { @@ -1118,25 +1122,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-20T20:19:01+00:00" + "time": "2025-07-10T08:47:49+00:00" }, { "name": "symfony/yaml", - "version": "v7.3.1", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "0c3555045a46ab3cd4cc5a69d161225195230edb" + "reference": "b8d7d868da9eb0919e99c8830431ea087d6aae30" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/0c3555045a46ab3cd4cc5a69d161225195230edb", - "reference": "0c3555045a46ab3cd4cc5a69d161225195230edb", + "url": "https://api.github.com/repos/symfony/yaml/zipball/b8d7d868da9eb0919e99c8830431ea087d6aae30", + "reference": "b8d7d868da9eb0919e99c8830431ea087d6aae30", "shasum": "" }, "require": { @@ -1179,7 +1187,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.3.1" + "source": "https://github.com/symfony/yaml/tree/v7.3.2" }, "funding": [ { @@ -1190,12 +1198,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-03T06:57:57+00:00" + "time": "2025-07-10T08:47:49+00:00" }, { "name": "zhamao/logger", @@ -2078,7 +2090,7 @@ }, { "name": "captainhook/captainhook-phar", - "version": "5.25.6", + "version": "5.25.10", "source": { "type": "git", "url": "https://github.com/captainhook-git/captainhook-phar.git", @@ -2132,7 +2144,7 @@ ], "support": { "issues": "https://github.com/captainhook-git/captainhook/issues", - "source": "https://github.com/captainhook-git/captainhook-phar/tree/5.25.6" + "source": "https://github.com/captainhook-git/captainhook-phar/tree/5.25.10" }, "funding": [ { @@ -2831,93 +2843,22 @@ ], "time": "2025-02-13T23:05:19+00:00" }, - { - "name": "filp/whoops", - "version": "2.18.3", - "source": { - "type": "git", - "url": "https://github.com/filp/whoops.git", - "reference": "59a123a3d459c5a23055802237cb317f609867e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/59a123a3d459c5a23055802237cb317f609867e5", - "reference": "59a123a3d459c5a23055802237cb317f609867e5", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0", - "psr/log": "^1.0.1 || ^2.0 || ^3.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^4.0 || ^5.0" - }, - "suggest": { - "symfony/var-dumper": "Pretty print complex values better with var-dumper available", - "whoops/soap": "Formats errors as SOAP responses" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Whoops\\": "src/Whoops/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Filipe Dobreira", - "homepage": "https://github.com/filp", - "role": "Developer" - } - ], - "description": "php error handling for cool kids", - "homepage": "https://filp.github.io/whoops/", - "keywords": [ - "error", - "exception", - "handling", - "library", - "throwable", - "whoops" - ], - "support": { - "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.3" - }, - "funding": [ - { - "url": "https://github.com/denis-sokolov", - "type": "github" - } - ], - "time": "2025-06-16T00:02:10+00:00" - }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.84.0", + "version": "v3.85.1", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "38dad0767bf2a9b516b976852200ae722fe984ca" + "reference": "2fb6d7f6c3398dca5786a1635b27405d73a417ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/38dad0767bf2a9b516b976852200ae722fe984ca", - "reference": "38dad0767bf2a9b516b976852200ae722fe984ca", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/2fb6d7f6c3398dca5786a1635b27405d73a417ba", + "reference": "2fb6d7f6c3398dca5786a1635b27405d73a417ba", "shasum": "" }, "require": { - "clue/ndjson-react": "^1.0", + "clue/ndjson-react": "^1.3", "composer/semver": "^3.4", "composer/xdebug-handler": "^3.0.5", "ext-filter": "*", @@ -2927,12 +2868,12 @@ "fidry/cpu-core-counter": "^1.2", "php": "^7.4 || ^8.0", "react/child-process": "^0.6.6", - "react/event-loop": "^1.0", - "react/promise": "^2.11 || ^3.0", - "react/socket": "^1.0", - "react/stream": "^1.0", + "react/event-loop": "^1.5", + "react/promise": "^3.2", + "react/socket": "^1.16", + "react/stream": "^1.4", "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", - "symfony/console": "^5.4.45 || ^6.4.13 || ^7.0", + "symfony/console": "^5.4.47 || ^6.4.13 || ^7.0", "symfony/event-dispatcher": "^5.4.45 || ^6.4.13 || ^7.0", "symfony/filesystem": "^5.4.45 || ^6.4.13 || ^7.0", "symfony/finder": "^5.4.45 || ^6.4.17 || ^7.0", @@ -2997,7 +2938,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.84.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.85.1" }, "funding": [ { @@ -3005,7 +2946,7 @@ "type": "github" } ], - "time": "2025-07-15T18:21:57+00:00" + "time": "2025-07-29T22:22:50+00:00" }, { "name": "humbug/box", @@ -3660,16 +3601,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.5.0", + "version": "v5.6.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" + "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/221b0d0fdf1369c71047ad1d18bb5880017bbc56", + "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56", "shasum": "" }, "require": { @@ -3712,190 +3653,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.0" }, - "time": "2025-05-31T08:24:38+00:00" - }, - { - "name": "nunomaduro/collision", - "version": "v7.12.0", - "source": { - "type": "git", - "url": "https://github.com/nunomaduro/collision.git", - "reference": "995245421d3d7593a6960822063bdba4f5d7cf1a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/995245421d3d7593a6960822063bdba4f5d7cf1a", - "reference": "995245421d3d7593a6960822063bdba4f5d7cf1a", - "shasum": "" - }, - "require": { - "filp/whoops": "^2.17.0", - "nunomaduro/termwind": "^1.17.0", - "php": "^8.1.0", - "symfony/console": "^6.4.17" - }, - "conflict": { - "laravel/framework": ">=11.0.0" - }, - "require-dev": { - "brianium/paratest": "^7.4.8", - "laravel/framework": "^10.48.29", - "laravel/pint": "^1.21.2", - "laravel/sail": "^1.41.0", - "laravel/sanctum": "^3.3.3", - "laravel/tinker": "^2.10.1", - "nunomaduro/larastan": "^2.10.0", - "orchestra/testbench-core": "^8.35.0", - "pestphp/pest": "^2.36.0", - "phpunit/phpunit": "^10.5.36", - "sebastian/environment": "^6.1.0", - "spatie/laravel-ignition": "^2.9.1" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" - ] - } - }, - "autoload": { - "files": [ - "./src/Adapters/Phpunit/Autoload.php" - ], - "psr-4": { - "NunoMaduro\\Collision\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "Cli error handling for console/command-line PHP applications.", - "keywords": [ - "artisan", - "cli", - "command-line", - "console", - "error", - "handling", - "laravel", - "laravel-zero", - "php", - "symfony" - ], - "support": { - "issues": "https://github.com/nunomaduro/collision/issues", - "source": "https://github.com/nunomaduro/collision" - }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://www.patreon.com/nunomaduro", - "type": "patreon" - } - ], - "time": "2025-03-14T22:35:49+00:00" - }, - { - "name": "nunomaduro/termwind", - "version": "v1.17.0", - "source": { - "type": "git", - "url": "https://github.com/nunomaduro/termwind.git", - "reference": "5369ef84d8142c1d87e4ec278711d4ece3cbf301" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/5369ef84d8142c1d87e4ec278711d4ece3cbf301", - "reference": "5369ef84d8142c1d87e4ec278711d4ece3cbf301", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": "^8.1", - "symfony/console": "^6.4.15" - }, - "require-dev": { - "illuminate/console": "^10.48.24", - "illuminate/support": "^10.48.24", - "laravel/pint": "^1.18.2", - "pestphp/pest": "^2.36.0", - "pestphp/pest-plugin-mock": "2.0.0", - "phpstan/phpstan": "^1.12.11", - "phpstan/phpstan-strict-rules": "^1.6.1", - "symfony/var-dumper": "^6.4.15", - "thecodingmachine/phpstan-strict-rules": "^1.0.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Termwind\\Laravel\\TermwindServiceProvider" - ] - } - }, - "autoload": { - "files": [ - "src/Functions.php" - ], - "psr-4": { - "Termwind\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "Its like Tailwind CSS, but for the console.", - "keywords": [ - "cli", - "console", - "css", - "package", - "php", - "style" - ], - "support": { - "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v1.17.0" - }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://github.com/xiCO2k", - "type": "github" - } - ], - "time": "2024-11-21T10:36:35+00:00" + "time": "2025-07-27T20:03:57+00:00" }, { "name": "phar-io/composer-distributor", @@ -6881,16 +6641,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", "shasum": "" }, "require": { @@ -6927,7 +6687,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.3.0" + "source": "https://github.com/symfony/filesystem/tree/v7.3.2" }, "funding": [ { @@ -6938,25 +6698,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-25T15:15:23+00:00" + "time": "2025-07-07T08:17:47+00:00" }, { "name": "symfony/finder", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d" + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ec2344cf77a48253bbca6939aa3d2477773ea63d", - "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d", + "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", "shasum": "" }, "require": { @@ -6991,7 +6755,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.3.0" + "source": "https://github.com/symfony/finder/tree/v7.3.2" }, "funding": [ { @@ -7002,25 +6766,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-30T19:00:26+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "afb9a8038025e5dbc657378bfab9198d75f10fca" + "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/afb9a8038025e5dbc657378bfab9198d75f10fca", - "reference": "afb9a8038025e5dbc657378bfab9198d75f10fca", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37", + "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37", "shasum": "" }, "require": { @@ -7058,7 +6826,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.3.0" + "source": "https://github.com/symfony/options-resolver/tree/v7.3.2" }, "funding": [ { @@ -7069,12 +6837,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-04T13:12:05+00:00" + "time": "2025-07-15T11:36:08+00:00" }, { "name": "symfony/polyfill-iconv", @@ -7296,16 +7068,16 @@ }, { "name": "symfony/var-dumper", - "version": "v7.3.1", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "6e209fbe5f5a7b6043baba46fe5735a4b85d0d42" + "reference": "53205bea27450dc5c65377518b3275e126d45e75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6e209fbe5f5a7b6043baba46fe5735a4b85d0d42", - "reference": "6e209fbe5f5a7b6043baba46fe5735a4b85d0d42", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/53205bea27450dc5c65377518b3275e126d45e75", + "reference": "53205bea27450dc5c65377518b3275e126d45e75", "shasum": "" }, "require": { @@ -7317,7 +7089,6 @@ "symfony/console": "<6.4" }, "require-dev": { - "ext-iconv": "*", "symfony/console": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/process": "^6.4|^7.0", @@ -7360,7 +7131,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.1" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.2" }, "funding": [ { @@ -7371,12 +7142,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T19:55:54+00:00" + "time": "2025-07-29T20:02:46+00:00" }, { "name": "thecodingmachine/safe", diff --git a/config/env.ini b/config/env.ini index 465f72d5b..040583059 100644 --- a/config/env.ini +++ b/config/env.ini @@ -10,6 +10,7 @@ ; ; Here's a list of env vars, these value cannot be changed anywhere: ; +; SPC_VERSION: the version of static-php-cli. ; WORKING_DIR: the working directory of the build process. (default: `$(pwd)`) ; ROOT_DIR: the root directory of static-php-cli. (default: `/path/to/static-php-cli`, when running in phar or micro mode: `phar://path/to/spc.phar`) ; BUILD_ROOT_PATH: the root path of the build process. (default: `$(pwd)/buildroot`) @@ -43,6 +44,8 @@ SPC_SKIP_PHP_VERSION_CHECK="no" SPC_SKIP_DOCTOR_CHECK_ITEMS="" ; extra modules that xcaddy will include in the FrankenPHP build SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/frankenphp/caddy --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy --with github.com/dunglas/caddy-cbrotli" +; The display message for php version output (PHP >= 8.4 available) +PHP_BUILD_PROVIDER="static-php-cli ${SPC_VERSION}" ; EXTENSION_DIR where the built php will look for extension when a .ini instructs to load them ; only useful for builds targeting not pure-static linking diff --git a/config/ext.json b/config/ext.json index 2cf81b180..14eabf17e 100644 --- a/config/ext.json +++ b/config/ext.json @@ -488,6 +488,7 @@ "opcache": { "type": "builtin", "arg-type-unix": "custom", + "arg-type-windows": "enable", "zend-extension": true }, "openssl": { @@ -530,9 +531,10 @@ }, "notes": true, "type": "builtin", - "arg-type": "with-path", + "arg-type": "custom", "lib-depends": [ - "libargon2" + "libargon2", + "openssl" ] }, "pcntl": { diff --git a/config/lib.json b/config/lib.json index d12950744..b8abd577e 100644 --- a/config/lib.json +++ b/config/lib.json @@ -797,6 +797,12 @@ "depot.h" ] }, + "re2c": { + "source": "re2c", + "bin-unix": [ + "re2c" + ] + }, "readline": { "source": "readline", "static-libs-unix": [ diff --git a/config/source.json b/config/source.json index 0fc9bca75..c89ebe7eb 100644 --- a/config/source.json +++ b/config/source.json @@ -733,7 +733,7 @@ "micro": { "type": "git", "path": "php-src/sapi/micro", - "rev": "84beta", + "rev": "php-85-win", "url": "https://github.com/static-php/phpmicro", "license": { "type": "file", @@ -932,6 +932,20 @@ "path": "LICENSE" } }, + "re2c": { + "type": "ghrel", + "repo": "skvadrik/re2c", + "match": "re2c.+\\.tar\\.xz", + "prefer-stable": true, + "alt": { + "type": "url", + "url": "https://dl.static-php.dev/static-php-cli/deps/re2c/re2c-4.3.tar.xz" + }, + "license": { + "type": "file", + "path": "LICENSE" + } + }, "readline": { "type": "filelist", "url": "https://ftp.gnu.org/pub/gnu/readline/", diff --git a/docs/.vitepress/components/CliGenerator.vue b/docs/.vitepress/components/CliGenerator.vue index ad17783ac..f9f3613d1 100644 --- a/docs/.vitepress/components/CliGenerator.vue +++ b/docs/.vitepress/components/CliGenerator.vue @@ -238,6 +238,7 @@ const availablePhpVersions = [ '8.2', '8.3', '8.4', + '8.5', ]; const I18N = { diff --git a/docs/en/guide/index.md b/docs/en/guide/index.md index 9f9bee4cd..253562b53 100644 --- a/docs/en/guide/index.md +++ b/docs/en/guide/index.md @@ -32,7 +32,7 @@ Windows currently only supports the x86_64 architecture, and does not support 32 ## Supported PHP Version -Currently, static php cli supports PHP versions 8.1 to 8.4, and theoretically supports PHP 8.0 and earlier versions. +Currently, static php cli supports PHP versions 8.1 to 8.5, and theoretically supports PHP 8.0 and earlier versions. Simply select the earlier version when downloading. However, due to some extensions and special components that have stopped supporting earlier versions of PHP, static-php-cli will not explicitly support earlier versions. diff --git a/docs/en/guide/manual-build.md b/docs/en/guide/manual-build.md index e8e4510e4..e9afaa4cc 100644 --- a/docs/en/guide/manual-build.md +++ b/docs/en/guide/manual-build.md @@ -242,8 +242,8 @@ Also, it is available when downloading with the `--for-extensions` option. ```bash -# Specifying to download a beta version of PHP8.3 -bin/spc download --all -U "php-src:https://downloads.php.net/~eric/php-8.3.0beta1.tar.gz" +# Specifying to download a alpha version of PHP 8.5 +bin/spc download --all -U "php-src:https://downloads.php.net/~edorian/php-8.5.0alpha2.tar.xz" # Specifying to download an older version of the curl library bin/spc download --all -U "curl:https://curl.se/download/curl-7.88.1.tar.gz" diff --git a/docs/zh/guide/index.md b/docs/zh/guide/index.md index 331923609..318c40b87 100644 --- a/docs/zh/guide/index.md +++ b/docs/zh/guide/index.md @@ -29,6 +29,6 @@ Windows 目前只支持 x86_64 架构,不支持 32 位 x86、不支持 arm64 ## PHP 支持版本 -目前,static-php-cli 对 PHP 8.1 ~ 8.4 版本是支持的,对于 PHP 8.0 及更早版本理论上支持,只需下载时选择早期版本即可。 +目前,static-php-cli 对 PHP 8.1 ~ 8.5 版本是支持的,对于 PHP 8.0 及更早版本理论上支持,只需下载时选择早期版本即可。 但由于部分扩展和特殊组件已对早期版本的 PHP 停止了支持,所以 static-php-cli 不会明确支持早期版本。 我们推荐你编译尽可能新的 PHP 版本,以获得更好的体验。 diff --git a/docs/zh/guide/manual-build.md b/docs/zh/guide/manual-build.md index 5301fca14..dcc9d255f 100644 --- a/docs/zh/guide/manual-build.md +++ b/docs/zh/guide/manual-build.md @@ -209,8 +209,8 @@ bin/spc download --from-zip=/path/to/your/download.zip 让下载器强制使用你指定的链接下载此 source 的包。使用方法为 `{source-name}:{url}` 即可,可同时重写多个库的下载地址。在使用 `--for-extensions` 选项下载时同样可用。 ```bash -# 例如:指定下载测试版的 PHP8.3 -bin/spc download --all -U "php-src:https://downloads.php.net/~eric/php-8.3.0beta1.tar.gz" +# 例如:指定下载 Alpha 版的 PHP8.5 +bin/spc download --all -U "php-src:https://downloads.php.net/~edorian/php-8.5.0alpha2.tar.xz" # 指定下载旧版本的 curl 库 bin/spc download --all -U "curl:https://curl.se/download/curl-7.88.1.tar.gz" diff --git a/src/SPC/builder/BuilderBase.php b/src/SPC/builder/BuilderBase.php index becd0f437..5dec6ad81 100644 --- a/src/SPC/builder/BuilderBase.php +++ b/src/SPC/builder/BuilderBase.php @@ -339,16 +339,21 @@ public function getPHPVersionID(): int throw new RuntimeException('PHP version file format is malformed, please remove it and download again'); } - public function getPHPVersion(): string + public function getPHPVersion(bool $exception_on_failure = true): string { if (!file_exists(SOURCE_PATH . '/php-src/main/php_version.h')) { + if (!$exception_on_failure) { + return 'unknown'; + } throw new WrongUsageException('PHP source files are not available, you need to download them first'); } $file = file_get_contents(SOURCE_PATH . '/php-src/main/php_version.h'); if (preg_match('/PHP_VERSION "(.*)"/', $file, $match) !== 0) { return $match[1]; } - + if (!$exception_on_failure) { + return 'unknown'; + } throw new RuntimeException('PHP version file format is malformed, please remove it and download again'); } @@ -366,7 +371,7 @@ public function getPHPVersionFromArchive(?string $file = null): false|string } $file = LockFile::getLockFullPath($lock); } - if (preg_match('/php-(\d+\.\d+\.\d+(?:RC\d+)?)\.tar\.(?:gz|bz2|xz)/', $file, $match)) { + if (preg_match('/php-(\d+\.\d+\.\d+(?:RC\d+|alpha\d+|beta\d+)?)\.tar\.(?:gz|bz2|xz)/', $file, $match)) { return $match[1]; } return false; diff --git a/src/SPC/builder/Extension.php b/src/SPC/builder/Extension.php index 90f2e27e0..435582c06 100644 --- a/src/SPC/builder/Extension.php +++ b/src/SPC/builder/Extension.php @@ -183,6 +183,14 @@ public function patchBeforeConfigure(): bool return false; } + /** + * Patch code before ./configure.bat for Windows + */ + public function patchBeforeWindowsConfigure(): bool + { + return false; + } + /** * Patch code before make * If you need to patch some code, overwrite this diff --git a/src/SPC/builder/LibraryBase.php b/src/SPC/builder/LibraryBase.php index 5f03d72cb..e1a3b03a5 100644 --- a/src/SPC/builder/LibraryBase.php +++ b/src/SPC/builder/LibraryBase.php @@ -288,6 +288,16 @@ public function patchBeforeConfigure(): bool return false; } + /** + * Patch code before windows configure.bat + * If you need to patch some code, overwrite this + * return true if you patched something, false if not + */ + public function patchBeforeWindowsConfigure(): bool + { + return false; + } + /** * Patch code before make * If you need to patch some code, overwrite this diff --git a/src/SPC/builder/extension/curl.php b/src/SPC/builder/extension/curl.php index a9079830d..17db61d3a 100644 --- a/src/SPC/builder/extension/curl.php +++ b/src/SPC/builder/extension/curl.php @@ -61,7 +61,7 @@ public function patchBeforeConfigure(): bool public function patchBeforeMake(): bool { $patched = parent::patchBeforeMake(); - $extra_libs = getenv('SPC_EXTRA_LIBS'); + $extra_libs = getenv('SPC_EXTRA_LIBS') ?: ''; if ($this->builder instanceof WindowsBuilder && !str_contains($extra_libs, 'secur32.lib')) { $extra_libs .= ' secur32.lib'; putenv('SPC_EXTRA_LIBS=' . trim($extra_libs)); diff --git a/src/SPC/builder/extension/imap.php b/src/SPC/builder/extension/imap.php index 81bb31648..d220bc9b0 100644 --- a/src/SPC/builder/extension/imap.php +++ b/src/SPC/builder/extension/imap.php @@ -49,7 +49,7 @@ public function patchBeforeMake(): bool if (PHP_OS_FAMILY !== 'Linux' || SystemUtil::isMuslDist()) { return $patched; } - $extra_libs = trim(getenv('SPC_EXTRA_LIBS') . ' -lcrypt'); + $extra_libs = trim((getenv('SPC_EXTRA_LIBS') ?: '') . ' -lcrypt'); f_putenv('SPC_EXTRA_LIBS=' . $extra_libs); return true; } diff --git a/src/SPC/builder/extension/opcache.php b/src/SPC/builder/extension/opcache.php index 5d9dda0ad..b73dbbd67 100644 --- a/src/SPC/builder/extension/opcache.php +++ b/src/SPC/builder/extension/opcache.php @@ -26,19 +26,26 @@ public function validate(): void public function patchBeforeBuildconf(): bool { + $version = $this->builder->getPHPVersion(); if (file_exists(SOURCE_PATH . '/php-src/.opcache_patched')) { return false; } // if 8.2.0 <= PHP_VERSION < 8.2.23, we need to patch from legacy patch file - if (version_compare($this->builder->getPHPVersion(), '8.2.0', '>=') && version_compare($this->builder->getPHPVersion(), '8.2.23', '<')) { + if (version_compare($version, '8.2.0', '>=') && version_compare($version, '8.2.23', '<')) { SourcePatcher::patchFile('spc_fix_static_opcache_before_80222.patch', SOURCE_PATH . '/php-src'); } // if 8.3.0 <= PHP_VERSION < 8.3.11, we need to patch from legacy patch file - elseif (version_compare($this->builder->getPHPVersion(), '8.3.0', '>=') && version_compare($this->builder->getPHPVersion(), '8.3.11', '<')) { + elseif (version_compare($version, '8.3.0', '>=') && version_compare($version, '8.3.11', '<')) { SourcePatcher::patchFile('spc_fix_static_opcache_before_80310.patch', SOURCE_PATH . '/php-src'); - } else { + } + // if 8.3.12 <= PHP_VERSION < 8.5.0-dev, we need to patch from legacy patch file + elseif (version_compare($version, '8.5.0-dev', '<')) { SourcePatcher::patchMicro(items: ['static_opcache']); } + // PHP 8.5.0-dev and later supports static opcache without patching + else { + return false; + } return file_put_contents(SOURCE_PATH . '/php-src/.opcache_patched', '1') !== false; } diff --git a/src/SPC/builder/extension/openssl.php b/src/SPC/builder/extension/openssl.php index 3ef4998f2..bf61fa371 100644 --- a/src/SPC/builder/extension/openssl.php +++ b/src/SPC/builder/extension/openssl.php @@ -27,6 +27,19 @@ public function patchBeforeMake(): bool public function getUnixConfigureArg(bool $shared = false): string { $openssl_dir = $this->builder->getPHPVersionID() >= 80400 ? '' : ' --with-openssl-dir=' . BUILD_ROOT_PATH; - return '--with-openssl=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $openssl_dir; + $args = '--with-openssl=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $openssl_dir; + if ($this->builder->getPHPVersionID() >= 80500 || ($this->builder->getPHPVersionID() >= 80400 && !$this->builder->getOption('enable-zts'))) { + $args .= ' --with-openssl-argon2 OPENSSL_LIBS="-lz"'; + } + return $args; + } + + public function getWindowsConfigureArg(bool $shared = false): string + { + $args = '--with-openssl'; + if ($this->builder->getPHPVersionID() >= 80500 || ($this->builder->getPHPVersionID() >= 80400 && !$this->builder->getOption('enable-zts'))) { + $args .= ' --with-openssl-argon2'; + } + return $args; } } diff --git a/src/SPC/builder/extension/password_argon2.php b/src/SPC/builder/extension/password_argon2.php index 4a7fd9d07..7a3d8e0cf 100644 --- a/src/SPC/builder/extension/password_argon2.php +++ b/src/SPC/builder/extension/password_argon2.php @@ -42,4 +42,14 @@ public function patchBeforeMake(): bool } return $patched; } + + public function getConfigureArg(bool $shared = false): string + { + if ($this->builder->getLib('openssl') !== null) { + if ($this->builder->getPHPVersionID() >= 80500 || ($this->builder->getPHPVersionID() >= 80400 && !$this->builder->getOption('enable-zts'))) { + return '--without-password-argon2'; // use --with-openssl-argon2 in openssl extension instead + } + } + return '--with-password-argon2'; + } } diff --git a/src/SPC/builder/extension/sqlsrv.php b/src/SPC/builder/extension/sqlsrv.php index edf5d919b..04bd52261 100644 --- a/src/SPC/builder/extension/sqlsrv.php +++ b/src/SPC/builder/extension/sqlsrv.php @@ -24,7 +24,7 @@ public function patchBeforeBuildconf(): bool return false; } - public function patchBeforeConfigure(): bool + public function patchBeforeWindowsConfigure(): bool { if ($this->pdo_sqlsrv_patched) { // revert pdo_sqlsrv patch diff --git a/src/SPC/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php index 28edeb223..38990403b 100644 --- a/src/SPC/builder/linux/LinuxBuilder.php +++ b/src/SPC/builder/linux/LinuxBuilder.php @@ -66,6 +66,17 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void $phpVersionID = $this->getPHPVersionID(); $json_74 = $phpVersionID < 80000 ? '--enable-json ' : ''; + $opcache_jit = !$this->getOption('disable-opcache-jit', false); + if ($opcache_jit && ($phpVersionID >= 80500 || $this->getExt('opcache'))) { + // php 8.5 contains opcache extension by default, + // if opcache_jit is enabled for 8.5 or opcache enabled, + // we need to disable undefined behavior sanitizer. + f_putenv('SPC_COMPILER_EXTRA=-fno-sanitize=undefined'); + } elseif ($opcache_jit) { + $opcache_jit = false; + } + $opcache_jit_arg = $opcache_jit ? '--enable-opcache-jit ' : '--disable-opcache-jit '; + if ($this->getOption('enable-zts', false)) { $maxExecutionTimers = $phpVersionID >= 80100 ? '--enable-zend-max-execution-timers ' : ''; $zts = '--enable-zts --disable-zend-signals '; @@ -73,10 +84,7 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void $maxExecutionTimers = ''; $zts = ''; } - $disable_jit = $this->getOption('disable-opcache-jit', false) ? '--disable-opcache-jit ' : ''; - if (!$disable_jit && $this->getExt('opcache')) { - f_putenv('SPC_COMPILER_EXTRA=-fno-sanitize=undefined'); - } + $config_file_path = $this->getOption('with-config-file-path', false) ? ('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : ''; $config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ? @@ -89,9 +97,10 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void $enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP; // prepare build php envs + // $musl_flag = SPCTarget::getLibc() === 'musl' ? ' -D__MUSL__' : ' -U__MUSL__'; $php_configure_env = SystemUtil::makeEnvVarString([ 'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'), - 'CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH, + 'CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH, // . ' -Dsomethinghere', // . $musl_flag, 'LDFLAGS' => '-L' . BUILD_LIB_PATH, // 'LIBS' => SPCTarget::getRuntimeLibs(), // do not pass static libraries here yet, they may contain polyfills for libc functions! ]); @@ -114,7 +123,7 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void ($enableMicro ? '--enable-micro=all-static ' : '--disable-micro ') . $config_file_path . $config_file_scan_dir . - $disable_jit . + $opcache_jit_arg . $json_74 . $zts . $maxExecutionTimers . @@ -335,6 +344,15 @@ protected function buildEmbed(): void $this->patchPhpScripts(); } + /** + * Return extra variables for php make command. + * + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + * @throws \ReflectionException + * @throws \Throwable + */ private function getMakeExtraVars(): array { $config = (new SPCConfigUtil($this, ['libs_only_deps' => true, 'absolute_libs' => true]))->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs')); diff --git a/src/SPC/builder/linux/library/openssl.php b/src/SPC/builder/linux/library/openssl.php index 161182cba..3da4bb95a 100644 --- a/src/SPC/builder/linux/library/openssl.php +++ b/src/SPC/builder/linux/library/openssl.php @@ -28,6 +28,8 @@ class openssl extends LinuxLibraryBase { + use \SPC\builder\traits\openssl; + public const NAME = 'openssl'; /** @@ -37,8 +39,6 @@ class openssl extends LinuxLibraryBase */ public function build(): void { - [,,$destdir] = SEPARATED_PATH; - $extra = ''; $ex_lib = '-ldl -pthread'; $arch = getenv('SPC_ARCH'); diff --git a/src/SPC/builder/linux/library/re2c.php b/src/SPC/builder/linux/library/re2c.php new file mode 100644 index 000000000..91c6ad50b --- /dev/null +++ b/src/SPC/builder/linux/library/re2c.php @@ -0,0 +1,15 @@ +emitPatchPoint('before-php-configure'); SourcePatcher::patchBeforeConfigure($this); - $json_74 = $this->getPHPVersionID() < 80000 ? '--enable-json ' : ''; + $phpVersionID = $this->getPHPVersionID(); + $json_74 = $phpVersionID < 80000 ? '--enable-json ' : ''; $zts = $this->getOption('enable-zts', false) ? '--enable-zts --disable-zend-signals ' : ''; + $opcache_jit = !$this->getOption('disable-opcache-jit', false); + // disable opcache jit for PHP < 8.5.0 when opcache is not enabled + if ($opcache_jit && $phpVersionID < 80500 && !$this->getExt('opcache')) { + $opcache_jit = false; + } + $opcache_jit_arg = $opcache_jit ? '--enable-opcache-jit ' : '--disable-opcache-jit '; + $config_file_path = $this->getOption('with-config-file-path', false) ? ('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : ''; $config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ? @@ -135,6 +143,7 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void ($enableFpm ? '--enable-fpm ' : '--disable-fpm ') . ($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') . ($enableMicro ? '--enable-micro ' : '--disable-micro ') . + $opcache_jit_arg . $config_file_path . $config_file_scan_dir . $json_74 . diff --git a/src/SPC/builder/macos/library/openssl.php b/src/SPC/builder/macos/library/openssl.php index c1826b5c5..df0f4a526 100644 --- a/src/SPC/builder/macos/library/openssl.php +++ b/src/SPC/builder/macos/library/openssl.php @@ -28,6 +28,8 @@ class openssl extends MacOSLibraryBase { + use \SPC\builder\traits\openssl; + public const NAME = 'openssl'; /** diff --git a/src/SPC/builder/macos/library/re2c.php b/src/SPC/builder/macos/library/re2c.php new file mode 100644 index 000000000..925d1f287 --- /dev/null +++ b/src/SPC/builder/macos/library/re2c.php @@ -0,0 +1,12 @@ +source_dir}/VERSION.dat")) { + // parse as INI + $version = parse_ini_file("{$this->source_dir}/VERSION.dat"); + if ($version !== false) { + return "{$version['MAJOR']}.{$version['MINOR']}.{$version['PATCH']}"; + } + } + // get openssl version from pkg-config + if (PHP_OS_FAMILY !== 'Windows') { + try { + return PkgConfigUtil::getModuleVersion('openssl'); + } catch (RuntimeException) { + } + } + // get openssl version from header openssl/opensslv.h + if (file_exists(BUILD_INCLUDE_PATH . '/openssl/opensslv.h')) { + if (preg_match('/OPENSSL_VERSION_STR "(.*)"/', FileSystem::readFile(BUILD_INCLUDE_PATH . '/openssl/opensslv.h'), $match)) { + return $match[1]; + } + } + return null; + } +} diff --git a/src/SPC/builder/unix/library/re2c.php b/src/SPC/builder/unix/library/re2c.php new file mode 100644 index 000000000..cd6a562ad --- /dev/null +++ b/src/SPC/builder/unix/library/re2c.php @@ -0,0 +1,32 @@ +addConfigureArgs( + '-DRE2C_BUILD_TESTS=OFF', + '-DRE2C_BUILD_EXAMPLES=OFF', + '-DRE2C_BUILD_DOCS=OFF', + '-DRE2C_BUILD_RE2D=OFF', + '-DRE2C_BUILD_RE2GO=OFF', + '-DRE2C_BUILD_RE2HS=OFF', + '-DRE2C_BUILD_RE2JAVA=OFF', + '-DRE2C_BUILD_RE2JS=OFF', + '-DRE2C_BUILD_RE2OCAML=OFF', + '-DRE2C_BUILD_RE2PY=OFF', + '-DRE2C_BUILD_RE2RUST=OFF', + '-DRE2C_BUILD_RE2SWIFT=OFF', + '-DRE2C_BUILD_RE2V=OFF', + '-DRE2C_BUILD_RE2ZIG=OFF', + ) + ->build(); + } +} diff --git a/src/SPC/builder/windows/WindowsBuilder.php b/src/SPC/builder/windows/WindowsBuilder.php index fb49a0131..ac29ae3a2 100644 --- a/src/SPC/builder/windows/WindowsBuilder.php +++ b/src/SPC/builder/windows/WindowsBuilder.php @@ -89,6 +89,9 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void } } + $opcache_jit = !$this->getOption('disable-opcache-jit', false); + $opcache_jit_arg = $opcache_jit ? '--enable-opcache-jit=yes ' : '--enable-opcache-jit=no '; + if (($logo = $this->getOption('with-micro-logo')) !== null) { // realpath // $logo = realpath($logo); @@ -115,6 +118,7 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void ($enableMicro ? ('--enable-micro=yes ' . $micro_logo . $micro_w32) : '--enable-micro=no ') . ($enableEmbed ? '--enable-embed=yes ' : '--enable-embed=no ') . $config_file_scan_dir . + $opcache_jit_arg . "{$this->makeStaticExtensionArgs()} " . $zts . '"' diff --git a/src/SPC/command/BuildPHPCommand.php b/src/SPC/command/BuildPHPCommand.php index e914c70d4..229b4e897 100644 --- a/src/SPC/command/BuildPHPCommand.php +++ b/src/SPC/command/BuildPHPCommand.php @@ -159,7 +159,7 @@ public function handle(): int $indent_texts['UPX Pack'] = 'enabled'; } - $ver = $builder->getPHPVersionFromArchive() ?: $builder->getPHPVersion(); + $ver = $builder->getPHPVersionFromArchive() ?: $builder->getPHPVersion(false); $indent_texts['PHP Version'] = $ver; if (!empty($not_included)) { @@ -269,6 +269,7 @@ public function handle(): int } catch (WrongUsageException $e) { // WrongUsageException is not an exception, it's a user error, so we just print the error message logger()->critical($e->getMessage()); + logger()->error($e->getTraceAsString()); return static::FAILURE; } catch (\Throwable $e) { if ($this->getOption('debug')) { diff --git a/src/SPC/command/DownloadCommand.php b/src/SPC/command/DownloadCommand.php index dfa4c9eea..ddcb50ae1 100644 --- a/src/SPC/command/DownloadCommand.php +++ b/src/SPC/command/DownloadCommand.php @@ -108,12 +108,9 @@ public function handle(): int // Define PHP major version $ver = $this->php_major_ver = $this->getOption('with-php'); define('SPC_BUILD_PHP_VERSION', $ver); - // match x.y - preg_match('/^\d+\.\d+$/', $ver, $matches); - if (!$matches) { - // match x.y.z - preg_match('/^\d+\.\d+\.\d+$/', $ver, $matches); - if (!$matches) { + if ($ver !== 'git' && !preg_match('/^\d+\.\d+$/', $ver)) { + // If not git, we need to check the version format + if (!preg_match('/^\d+\.\d+(\.\d+)?$/', $ver)) { logger()->error("bad version arg: {$ver}, x.y or x.y.z required!"); return static::FAILURE; } diff --git a/src/SPC/command/dev/LibVerCommand.php b/src/SPC/command/dev/LibVerCommand.php index 717fd155f..246618e46 100644 --- a/src/SPC/command/dev/LibVerCommand.php +++ b/src/SPC/command/dev/LibVerCommand.php @@ -8,6 +8,7 @@ use SPC\command\BaseCommand; use SPC\exception\WrongUsageException; use SPC\store\Config; +use SPC\util\DependencyUtil; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -41,7 +42,10 @@ public function handle(): int return static::FAILURE; } - $builder->proveLibs([$this->getArgument('library')]); + // parse the dependencies + [, $libs] = DependencyUtil::getExtsAndLibs([], [$this->getArgument('library')]); + + $builder->proveLibs($libs); // Check whether lib is extracted if (!is_dir(SOURCE_PATH . '/' . $this->getArgument('library'))) { @@ -51,7 +55,7 @@ public function handle(): int $version = $builder->getLib($this->getArgument('library'))->getLibVersion(); if ($version === null) { - $this->output->writeln("Failed to get version of library {$this->getArgument('library')}"); + $this->output->writeln("Failed to get version of library {$this->getArgument('library')}. The version getter for [{$this->getArgument('library')}] is not implemented."); return static::FAILURE; } $this->output->writeln("{$version}"); diff --git a/src/SPC/doctor/AsCheckItem.php b/src/SPC/doctor/AsCheckItem.php index f64d914be..0fa7466fd 100644 --- a/src/SPC/doctor/AsCheckItem.php +++ b/src/SPC/doctor/AsCheckItem.php @@ -4,7 +4,7 @@ namespace SPC\doctor; -#[\Attribute(\Attribute::TARGET_METHOD)] +#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class AsCheckItem { public mixed $callback = null; diff --git a/src/SPC/doctor/item/LinuxToolCheckList.php b/src/SPC/doctor/item/LinuxToolCheckList.php index b5c9cb034..58ad8a317 100644 --- a/src/SPC/doctor/item/LinuxToolCheckList.php +++ b/src/SPC/doctor/item/LinuxToolCheckList.php @@ -16,7 +16,7 @@ class LinuxToolCheckList use UnixSystemUtilTrait; public const TOOLS_ALPINE = [ - 'make', 'bison', 'flex', + 'make', 'bison', 're2c', 'flex', 'git', 'autoconf', 'automake', 'gettext-dev', 'tar', 'unzip', 'gzip', 'bzip2', 'cmake', 'gcc', @@ -26,7 +26,7 @@ class LinuxToolCheckList ]; public const TOOLS_DEBIAN = [ - 'make', 'bison', 'flex', + 'make', 'bison', 're2c', 'flex', 'git', 'autoconf', 'automake', 'autopoint', 'tar', 'unzip', 'gzip', 'bzip2', 'cmake', 'patch', @@ -35,7 +35,7 @@ class LinuxToolCheckList ]; public const TOOLS_RHEL = [ - 'perl', 'make', 'bison', 'flex', + 'perl', 'make', 'bison', 're2c', 'flex', 'git', 'autoconf', 'automake', 'tar', 'unzip', 'gzip', 'gcc', 'bzip2', 'cmake', 'patch', 'which', diff --git a/src/SPC/doctor/item/MacOSToolCheckList.php b/src/SPC/doctor/item/MacOSToolCheckList.php index b4043a1d3..249b618e6 100644 --- a/src/SPC/doctor/item/MacOSToolCheckList.php +++ b/src/SPC/doctor/item/MacOSToolCheckList.php @@ -19,6 +19,7 @@ class MacOSToolCheckList 'curl', 'make', 'bison', + 're2c', 'flex', 'pkg-config', 'git', @@ -61,6 +62,30 @@ public function checkCliTools(): ?CheckResult return CheckResult::ok(); } + #[AsCheckItem('if bison version is 3.0 or later', limit_os: 'Darwin')] + public function checkBisonVersion(array $command_path = []): ?CheckResult + { + // if the bison command is /usr/bin/bison, it is the system bison that may be too old + if (($bison = $this->findCommand('bison', $command_path)) === null) { + return CheckResult::fail('bison is not installed or too old', 'build-tools', [['bison']]); + } + // check version: bison (GNU Bison) x.y(.z) + $version = shell()->execWithResult("{$bison} --version", false); + if (preg_match('/bison \(GNU Bison\) (\d+)\.(\d+)(?:\.(\d+))?/', $version[1][0], $matches)) { + $major = (int) $matches[1]; + // major should be 3 or later + if ($major < 3) { + // find homebrew keg-only bison + if ($command_path !== []) { + return CheckResult::fail("Current {$bison} version is too old: " . $matches[0]); + } + return $this->checkBisonVersion(['/opt/homebrew/opt/bison/bin', '/usr/local/opt/bison/bin']); + } + return CheckResult::ok($matches[0]); + } + return CheckResult::fail('bison version cannot be determined'); + } + #[AsFixItem('brew')] public function fixBrew(): bool { diff --git a/src/SPC/doctor/item/Re2cVersionCheck.php b/src/SPC/doctor/item/Re2cVersionCheck.php new file mode 100644 index 000000000..1c130379c --- /dev/null +++ b/src/SPC/doctor/item/Re2cVersionCheck.php @@ -0,0 +1,53 @@ += 1.0.3', limit_os: 'Linux', level: 20)] + #[AsCheckItem('if re2c version >= 1.0.3', limit_os: 'Darwin', level: 20)] + public function checkRe2cVersion(): ?CheckResult + { + $ver = shell(false)->execWithResult('re2c --version', false); + // match version: re2c X.X(.X) + if ($ver[0] !== 0 || !preg_match('/re2c\s+(\d+\.\d+(\.\d+)?)/', $ver[1][0], $matches)) { + return CheckResult::fail('Failed to get re2c version', 'build-re2c'); + } + $version_string = $matches[1]; + if (version_compare($version_string, '1.0.3') < 0) { + return CheckResult::fail('re2c version is too low (' . $version_string . ')', 'build-re2c'); + } + return CheckResult::ok($version_string); + } + + #[AsFixItem('build-re2c')] + public function buildRe2c(): bool + { + try { + Downloader::downloadSource('re2c'); + } catch (DownloaderException) { + logger()->warning('Failed to download re2c version, trying alternative'); + $alt = Config::getSource('re2c'); + $alt = [...$alt, ...$alt['alt'] ?? []]; + Downloader::downloadSource('re2c', $alt); + } + $builder = BuilderProvider::makeBuilderByInput(new ArgvInput([])); + $builder->proveLibs(['re2c']); + $builder->setupLibs(); + return true; + } +} diff --git a/src/SPC/exception/ExceptionHandler.php b/src/SPC/exception/ExceptionHandler.php index 544575dc9..d103b9574 100644 --- a/src/SPC/exception/ExceptionHandler.php +++ b/src/SPC/exception/ExceptionHandler.php @@ -10,20 +10,6 @@ class ExceptionHandler private static ?ExceptionHandler $obj = null; - private function __construct() - { - $whoops_class = 'Whoops\Run'; - $collision_class = 'NunoMaduro\Collision\Handler'; - if (class_exists($collision_class) && class_exists($whoops_class)) { - /* @phpstan-ignore-next-line */ - $this->whoops = new $whoops_class(); - $this->whoops->allowQuit(false); - $this->whoops->writeToOutput(false); - $this->whoops->pushHandler(new $collision_class()); - $this->whoops->register(); - } - } - public static function getInstance(): ExceptionHandler { if (self::$obj === null) { @@ -34,13 +20,8 @@ public static function getInstance(): ExceptionHandler public function handle(\Throwable $e): void { - if (is_null($this->whoops)) { - logger()->error('Uncaught ' . get_class($e) . ': ' . $e->getMessage() . ' at ' . $e->getFile() . '(' . $e->getLine() . ')'); - logger()->error($e->getTraceAsString()); - return; - } - $this->whoops->handleException($e); - + logger()->error('Uncaught ' . get_class($e) . ': ' . $e->getMessage() . ' at ' . $e->getFile() . '(' . $e->getLine() . ')'); + logger()->error($e->getTraceAsString()); logger()->critical('You can report this exception to static-php-cli GitHub repo.'); } } diff --git a/src/SPC/store/SourcePatcher.php b/src/SPC/store/SourcePatcher.php index 02a64624b..85b7ddf10 100644 --- a/src/SPC/store/SourcePatcher.php +++ b/src/SPC/store/SourcePatcher.php @@ -7,6 +7,7 @@ use SPC\builder\BuilderBase; use SPC\builder\linux\SystemUtil; use SPC\builder\unix\UnixBuilderBase; +use SPC\builder\windows\WindowsBuilder; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; @@ -68,6 +69,35 @@ public static function patchBeforeBuildconf(BuilderBase $builder): void ); } + // Fix PHP VS version + if ($builder instanceof WindowsBuilder) { + // get vs version + $vc = \SPC\builder\windows\SystemUtil::findVisualStudio(); + $vc_matches = match ($vc['version']) { + 'vs17' => ['VS17', 'Visual C++ 2022'], + 'vs16' => ['VS16', 'Visual C++ 2019'], + default => ['unknown', 'unknown'], + }; + // patch php-src/win32/build/confutils.js + FileSystem::replaceFileStr( + SOURCE_PATH . '\php-src\win32\build\confutils.js', + 'var name = "unknown";', + "var name = short ? \"{$vc_matches[0]}\" : \"{$vc_matches[1]}\";return name;" + ); + } + + // patch configure.ac + $musl = SPCTarget::getLibc() === 'musl'; + FileSystem::backupFile(SOURCE_PATH . '/php-src/configure.ac'); + FileSystem::replaceFileStr( + SOURCE_PATH . '/php-src/configure.ac', + 'if command -v ldd >/dev/null && ldd --version 2>&1 | grep ^musl >/dev/null 2>&1', + 'if ' . ($musl ? 'true' : 'false') + ); + if (getenv('SPC_LIBC') === false && ($libc = SPCTarget::getLibc()) !== null) { + putenv("SPC_LIBC={$libc}"); + } + // patch php-src/build/php.m4 PKG_CHECK_MODULES -> PKG_CHECK_MODULES_STATIC FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/build/php.m4', 'PKG_CHECK_MODULES(', 'PKG_CHECK_MODULES_STATIC('); @@ -87,7 +117,8 @@ public static function patchBeforeBuildconf(BuilderBase $builder): void public static function patchBeforeConfigure(BuilderBase $builder): void { foreach ($builder->getExts() as $ext) { - if ($ext->patchBeforeConfigure() === true) { + $patch = $builder instanceof WindowsBuilder ? $ext->patchBeforeWindowsConfigure() : $ext->patchBeforeConfigure(); + if ($patch === true) { logger()->info("Extension [{$ext->getName()}] patched before configure"); } } @@ -97,7 +128,14 @@ public static function patchBeforeConfigure(BuilderBase $builder): void } } // patch capstone - FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/have_capstone="yes"/', 'have_capstone="no"'); + if (is_unix()) { + FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/have_capstone="yes"/', 'have_capstone="no"'); + } + + if (file_exists(SOURCE_PATH . '/php-src/configure.ac.bak')) { + // restore configure.ac + FileSystem::restoreBackupFile(SOURCE_PATH . '/php-src/configure.ac'); + } } /** @@ -132,7 +170,7 @@ public static function patchMicro(?array $items = null): bool } $patch_list = $spc_micro_patches; $patches = []; - $serial = ['80', '81', '82', '83', '84']; + $serial = ['80', '81', '82', '83', '84', '85']; foreach ($patch_list as $patchName) { if (file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}.patch")) { $patches[] = "sapi/micro/patches/{$patchName}.patch"; @@ -584,7 +622,7 @@ public static function patchGDWin32(): bool */ public static function patchSPCVersionToPHP(string $version = 'unknown'): void { - // detect patch + // detect patch (remove this when 8.3 deprecated) $file = FileSystem::readFile(SOURCE_PATH . '/php-src/main/main.c'); if (!str_contains($file, 'static-php-cli.version')) { logger()->debug('Inserting static-php-cli.version to php-src'); diff --git a/src/SPC/store/source/PhpSource.php b/src/SPC/store/source/PhpSource.php index d6807cd1c..72a16849a 100644 --- a/src/SPC/store/source/PhpSource.php +++ b/src/SPC/store/source/PhpSource.php @@ -21,8 +21,14 @@ class PhpSource extends CustomSourceBase */ public function fetch(bool $force = false, ?array $config = null, int $lock_as = SPC_DOWNLOAD_SOURCE): void { - $major = defined('SPC_BUILD_PHP_VERSION') ? SPC_BUILD_PHP_VERSION : '8.3'; - Downloader::downloadSource('php-src', $this->getLatestPHPInfo($major), $force); + $major = defined('SPC_BUILD_PHP_VERSION') ? SPC_BUILD_PHP_VERSION : '8.4'; + if ($major === '8.5') { + Downloader::downloadSource('php-src', ['type' => 'url', 'url' => 'https://downloads.php.net/~daniels/php-8.5.0alpha4.tar.xz'], $force); + } elseif ($major === 'git') { + Downloader::downloadSource('php-src', ['type' => 'git', 'url' => 'https://github.com/php/php-src.git', 'rev' => 'master'], $force); + } else { + Downloader::downloadSource('php-src', self::getLatestPHPInfo($major), $force); + } } /** @@ -36,7 +42,7 @@ public function getLatestPHPInfo(string $major_version): array // 查找最新的小版本号 $info = json_decode(Downloader::curlExec( url: "https://www.php.net/releases/index.php?json&version={$major_version}", - retries: (int) getenv('SPC_DOWNLOAD_RETRIES') + retries: intval(getenv('SPC_DOWNLOAD_RETRIES') ?: 0) ), true); if (!isset($info['version'])) { throw new DownloaderException("Version {$major_version} not found."); diff --git a/src/SPC/toolchain/ClangNativeToolchain.php b/src/SPC/toolchain/ClangNativeToolchain.php index aef844d7d..6cccb4afc 100644 --- a/src/SPC/toolchain/ClangNativeToolchain.php +++ b/src/SPC/toolchain/ClangNativeToolchain.php @@ -7,6 +7,7 @@ use SPC\builder\freebsd\SystemUtil as FreeBSDSystemUtil; use SPC\builder\linux\SystemUtil as LinuxSystemUtil; use SPC\builder\macos\SystemUtil as MacOSSystemUtil; +use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; use SPC\util\GlobalEnvManager; @@ -37,8 +38,19 @@ public function afterInit(): void 'Linux' => LinuxSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."), 'Darwin' => MacOSSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."), 'BSD' => FreeBSDSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."), - default => throw new \RuntimeException(__CLASS__ . ' is not supported on ' . PHP_OS_FAMILY . '.'), + default => throw new RuntimeException(__CLASS__ . ' is not supported on ' . PHP_OS_FAMILY . '.'), }; } } + + public function getCompilerInfo(): ?string + { + $compiler = getenv('CC') ?: 'clang'; + $version = shell(false)->execWithResult("{$compiler} --version", false); + $head = pathinfo($compiler, PATHINFO_BASENAME); + if ($version[0] === 0 && preg_match('/clang version (\d+.\d+.\d+)/', $version[1][0], $match)) { + return "{$head} {$match[1]}"; + } + return $head; + } } diff --git a/src/SPC/toolchain/GccNativeToolchain.php b/src/SPC/toolchain/GccNativeToolchain.php index 1ada28b54..05fe026a0 100644 --- a/src/SPC/toolchain/GccNativeToolchain.php +++ b/src/SPC/toolchain/GccNativeToolchain.php @@ -35,4 +35,15 @@ public function afterInit(): void }; } } + + public function getCompilerInfo(): ?string + { + $compiler = getenv('CC') ?: 'gcc'; + $version = shell(false)->execWithResult("{$compiler} --version", false); + $head = pathinfo($compiler, PATHINFO_BASENAME); + if ($version[0] === 0 && preg_match('/gcc.*(\d+.\d+.\d+)/', $version[1][0], $match)) { + return "{$head} {$match[1]}"; + } + return $head; + } } diff --git a/src/SPC/toolchain/MSVCToolchain.php b/src/SPC/toolchain/MSVCToolchain.php index 567cff38d..55559e8a1 100644 --- a/src/SPC/toolchain/MSVCToolchain.php +++ b/src/SPC/toolchain/MSVCToolchain.php @@ -9,4 +9,9 @@ class MSVCToolchain implements ToolchainInterface public function initEnv(): void {} public function afterInit(): void {} + + public function getCompilerInfo(): ?string + { + return null; + } } diff --git a/src/SPC/toolchain/MuslToolchain.php b/src/SPC/toolchain/MuslToolchain.php index e996ef1fa..684473c6d 100644 --- a/src/SPC/toolchain/MuslToolchain.php +++ b/src/SPC/toolchain/MuslToolchain.php @@ -36,4 +36,15 @@ public function afterInit(): void throw new WrongUsageException('You are building with musl-libc target in glibc distro, but musl-toolchain is not installed, please install musl-toolchain first. (You can use `doctor` command to install it)'); } } + + public function getCompilerInfo(): ?string + { + $compiler = getenv('CC') ?: getenv('SPC_LINUX_DEFAULT_CC'); + $version = shell(false)->execWithResult("{$compiler} --version", false); + $head = pathinfo($compiler, PATHINFO_BASENAME); + if ($version[0] === 0 && preg_match('/linux-musl-cc.*(\d+.\d+.\d+)/', $version[1][0], $match)) { + return "{$head} {$match[1]}"; + } + return $head; + } } diff --git a/src/SPC/toolchain/ToolchainInterface.php b/src/SPC/toolchain/ToolchainInterface.php index 62cf2b2d2..a08fb8691 100644 --- a/src/SPC/toolchain/ToolchainInterface.php +++ b/src/SPC/toolchain/ToolchainInterface.php @@ -27,4 +27,12 @@ public function initEnv(): void; * post-initialization setup or validation. */ public function afterInit(): void; + + /** + * Returns the compiler name and version for toolchains. + * + * If the toolchain does not support compiler information, + * this method can return null. + */ + public function getCompilerInfo(): ?string; } diff --git a/src/SPC/toolchain/ToolchainManager.php b/src/SPC/toolchain/ToolchainManager.php index 0c92c58a9..9672977d7 100644 --- a/src/SPC/toolchain/ToolchainManager.php +++ b/src/SPC/toolchain/ToolchainManager.php @@ -61,6 +61,10 @@ public static function afterInitToolchain(): void } $toolchain = getenv('SPC_TOOLCHAIN'); /* @var ToolchainInterface $toolchain */ - (new $toolchain())->afterInit(); + $instance = new $toolchain(); + $instance->afterInit(); + if (getenv('PHP_BUILD_COMPILER') === false && ($compiler_info = $instance->getCompilerInfo())) { + GlobalEnvManager::putenv("PHP_BUILD_COMPILER={$compiler_info}"); + } } } diff --git a/src/SPC/toolchain/ZigToolchain.php b/src/SPC/toolchain/ZigToolchain.php index 613c0f979..93d004a5e 100644 --- a/src/SPC/toolchain/ZigToolchain.php +++ b/src/SPC/toolchain/ZigToolchain.php @@ -68,4 +68,10 @@ public function afterInit(): void GlobalEnvManager::putenv("SPC_EXTRA_LIBS={$extra_libs}"); } } + + public function getCompilerInfo(): ?string + { + $version = shell(false)->execWithResult('zig version', false)[1][0] ?? ''; + return trim("zig {$version}"); + } } diff --git a/src/SPC/util/ConfigValidator.php b/src/SPC/util/ConfigValidator.php index cfb9c2c2c..baa42b598 100644 --- a/src/SPC/util/ConfigValidator.php +++ b/src/SPC/util/ConfigValidator.php @@ -47,20 +47,20 @@ public static function validateSource(array $data): void // check if license is valid if (isset($src['license'])) { - if (!is_assoc_array($src['license'])) { - throw new ValidationException("source {$name} license must be object"); - } - if (!isset($src['license']['type'])) { - throw new ValidationException("source {$name} license must have type"); - } - if (!in_array($src['license']['type'], ['file', 'text'])) { - throw new ValidationException("source {$name} license type is invalid"); - } - if ($src['license']['type'] === 'file' && !isset($src['license']['path'])) { - throw new ValidationException("source {$name} license file must have path"); - } - if ($src['license']['type'] === 'text' && !isset($src['license']['text'])) { - throw new ValidationException("source {$name} license text must have text"); + if (!is_array($src['license'])) { + throw new ValidationException("source {$name} license must be an object or array"); + } + if (is_assoc_array($src['license'])) { + self::checkSingleLicense($src['license'], $name); + } elseif (is_list_array($src['license'])) { + foreach ($src['license'] as $license) { + if (!is_assoc_array($license)) { + throw new ValidationException("source {$name} license must be an object or array"); + } + self::checkSingleLicense($license, $name); + } + } else { + throw new ValidationException("source {$name} license must be an object or array"); } } } @@ -415,6 +415,31 @@ public static function validateAndParseCraftFile(mixed $craft_file, Command $com return $craft; } + /** + * @throws ValidationException + */ + private static function checkSingleLicense(array $license, string $name): void + { + if (!is_assoc_array($license)) { + throw new ValidationException("source {$name} license must be an object"); + } + if (!isset($license['type'])) { + throw new ValidationException("source {$name} license must have type"); + } + if (!in_array($license['type'], ['file', 'text'])) { + throw new ValidationException("source {$name} license type is invalid"); + } + if (!in_array($license['type'], ['file', 'text'])) { + throw new ValidationException("source {$name} license type is invalid"); + } + if ($license['type'] === 'file' && !isset($license['path'])) { + throw new ValidationException("source {$name} license file must have path"); + } + if ($license['type'] === 'text' && !isset($license['text'])) { + throw new ValidationException("source {$name} license text must have text"); + } + } + /** * Validate source type configuration (shared between source.json and pkg.json) * diff --git a/src/SPC/util/GlobalEnvManager.php b/src/SPC/util/GlobalEnvManager.php index f5dafea25..9d756d7ef 100644 --- a/src/SPC/util/GlobalEnvManager.php +++ b/src/SPC/util/GlobalEnvManager.php @@ -4,6 +4,7 @@ namespace SPC\util; +use SPC\builder\macos\SystemUtil; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; use SPC\toolchain\ToolchainManager; @@ -117,6 +118,15 @@ public static function afterInit(): void if (!filter_var(getenv('SPC_SKIP_TOOLCHAIN_CHECK'), FILTER_VALIDATE_BOOL)) { ToolchainManager::afterInitToolchain(); } + // test bison + if (PHP_OS_FAMILY === 'Darwin') { + if ($bison = SystemUtil::findCommand('bison', ['/opt/homebrew/opt/bison/bin', '/usr/local/opt/bison/bin'])) { + self::putenv("BISON={$bison}"); + } + if ($yacc = SystemUtil::findCommand('yacc', ['/opt/homebrew/opt/bison/bin', '/usr/local/opt/bison/bin'])) { + self::putenv("YACC={$yacc}"); + } + } } /** diff --git a/src/SPC/util/PkgConfigUtil.php b/src/SPC/util/PkgConfigUtil.php index fd11df612..8bb7e985b 100644 --- a/src/SPC/util/PkgConfigUtil.php +++ b/src/SPC/util/PkgConfigUtil.php @@ -14,6 +14,21 @@ */ class PkgConfigUtil { + /** + * Returns the version of a module. + * This method uses `pkg-config --modversion` to get the version of the specified module. + * If the module is not found, it will throw a RuntimeException. + * + * @param string $pkg_config_str .pc file str, accepts multiple files + * @return string version string, e.g. "1.2.3" + * @throws RuntimeException + */ + public static function getModuleVersion(string $pkg_config_str): string + { + $result = self::execWithResult("pkg-config --modversion {$pkg_config_str}"); + return trim($result); + } + /** * Get CFLAGS from pkg-config * diff --git a/src/SPC/util/SPCTarget.php b/src/SPC/util/SPCTarget.php index d4164ceb6..c6519a9a4 100644 --- a/src/SPC/util/SPCTarget.php +++ b/src/SPC/util/SPCTarget.php @@ -99,8 +99,11 @@ public static function getRuntimeLibs(): string */ public static function getLibcVersion(): ?string { - $libc = self::getLibc(); - return SystemUtil::getLibcVersionIfExists($libc); + if (PHP_OS_FAMILY === 'Linux') { + $libc = self::getLibc(); + return SystemUtil::getLibcVersionIfExists($libc); + } + return null; } /** diff --git a/src/globals/ext-tests/curl.php b/src/globals/ext-tests/curl.php index 7c3eecf79..16b31e6e9 100644 --- a/src/globals/ext-tests/curl.php +++ b/src/globals/ext-tests/curl.php @@ -6,13 +6,27 @@ assert(function_exists('curl_setopt')); assert(function_exists('curl_exec')); assert(function_exists('curl_close')); +assert(function_exists('curl_version')); $curl_version = curl_version(); if (stripos($curl_version['ssl_version'], 'schannel') !== false) { - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, 'https://captive.apple.com/'); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($curl, CURLOPT_HEADER, 0); - $data = curl_exec($curl); - curl_close($curl); - assert($data !== false); + $domain_list = [ + 'https://captive.apple.com/', + 'https://detectportal.firefox.com/', + 'https://static-php.dev/', + 'https://www.example.com/', + ]; + $valid = false; + foreach ($domain_list as $domain) { + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $domain); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_HEADER, 0); + $data = curl_exec($curl); + curl_close($curl); + if ($data !== false) { + $valid = true; + break; + } + } + assert($valid); } diff --git a/src/globals/ext-tests/openssl.php b/src/globals/ext-tests/openssl.php index 0453101a6..c687d0fc7 100644 --- a/src/globals/ext-tests/openssl.php +++ b/src/globals/ext-tests/openssl.php @@ -5,5 +5,32 @@ assert(function_exists('openssl_digest')); assert(openssl_digest('123456', 'md5') === 'e10adc3949ba59abbe56e057f20f883e'); if (file_exists('/etc/ssl/openssl.cnf')) { - assert(file_get_contents('https://captive.apple.com/') !== false); + $domain_list = [ + 'captive.apple.com', + 'detectportal.firefox.com', + 'static-php.dev', + 'www.example.com', + ]; + $valid = false; + foreach ($domain_list as $domain) { + $ssloptions = [ + 'capture_peer_cert' => true, + 'capture_peer_cert_chain' => true, + 'allow_self_signed' => false, + 'CN_match' => $domain, + 'verify_peer' => true, + 'SNI_enabled' => true, + 'SNI_server_name' => $domain, + ]; + $context = stream_context_create(['ssl' => $ssloptions]); + $result = stream_socket_client("ssl://{$domain}:443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context); + if ($result !== false) { + $valid = true; + break; + } + } + assert($valid); +} +if (PHP_VERSION_ID >= 80500 && defined('OPENSSL_VERSION_NUMBER') && OPENSSL_VERSION_NUMBER >= 0x30200000) { + assert(function_exists('openssl_password_hash')); } diff --git a/src/globals/internal-env.php b/src/globals/internal-env.php index 1046848e0..22918fdf3 100644 --- a/src/globals/internal-env.php +++ b/src/globals/internal-env.php @@ -6,9 +6,12 @@ use SPC\builder\linux\SystemUtil as LinuxSystemUtil; use SPC\builder\macos\SystemUtil as MacOSSystemUtil; use SPC\builder\windows\SystemUtil as WindowsSystemUtil; +use SPC\ConsoleApplication; use SPC\store\FileSystem; use SPC\util\GlobalEnvManager; +// static-php-cli version string +const SPC_VERSION = ConsoleApplication::VERSION; // output path for everything, other paths are defined relative to this by default define('BUILD_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_ROOT_PATH')) ? $a : (WORKING_DIR . '/buildroot'))); // output path for header files for development @@ -44,6 +47,7 @@ ]); // add these to env vars with same name +GlobalEnvManager::putenv('SPC_VERSION=' . SPC_VERSION); GlobalEnvManager::putenv('BUILD_ROOT_PATH=' . BUILD_ROOT_PATH); GlobalEnvManager::putenv('BUILD_INCLUDE_PATH=' . BUILD_INCLUDE_PATH); GlobalEnvManager::putenv('BUILD_LIB_PATH=' . BUILD_LIB_PATH); diff --git a/src/globals/test-extensions.php b/src/globals/test-extensions.php index f47340a03..d311962db 100644 --- a/src/globals/test-extensions.php +++ b/src/globals/test-extensions.php @@ -17,15 +17,17 @@ // '8.2', // '8.3', '8.4', + // '8.5', + 'git', ]; // test os (macos-13, macos-14, macos-15, ubuntu-latest, windows-latest are available) $test_os = [ - 'macos-13', // bin/spc for x86_64 + // 'macos-13', // bin/spc for x86_64 // 'macos-14', // bin/spc for arm64 - 'macos-15', // bin/spc for arm64 - // 'ubuntu-latest', // bin/spc-alpine-docker for x86_64 - 'ubuntu-22.04', // bin/spc-gnu-docker for x86_64 + // 'macos-15', // bin/spc for arm64 + 'ubuntu-latest', // bin/spc-alpine-docker for x86_64 + // 'ubuntu-22.04', // bin/spc-gnu-docker for x86_64 'ubuntu-24.04', // bin/spc for x86_64 'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64 'ubuntu-24.04-arm', // bin/spc for arm64 @@ -41,15 +43,15 @@ $upx = false; // whether to test frankenphp build, only available for macos and linux -$frankenphp = true; +$frankenphp = false; // prefer downloading pre-built packages to speed up the build process $prefer_pre_built = false; // If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`). $extensions = match (PHP_OS_FAMILY) { - 'Linux', 'Darwin' => 'ast,bcmath,calendar,ctype,dba,dom,exif,fileinfo,filter,libxml,mbregex,mbstring,pcntl,phar,posix,session,simplexml,sockets,sodium,tokenizer,xml,xmlreader,xmlwriter,zlib', - 'Windows' => 'intl', + 'Linux', 'Darwin' => 'bcmath,bz2,calendar,ctype,curl,dom,exif,fileinfo,filter,ftp,iconv,xml,mbstring,mbregex,mysqlnd,openssl,pdo,pdo_mysql,pdo_sqlite,phar,session,simplexml,soap,sockets,sqlite3,tokenizer,xmlwriter,xmlreader,zlib,zip', + 'Windows' => 'bcmath,bz2,calendar,ctype,curl,dom,exif,fileinfo,filter,ftp,iconv,xml,mbstring,mbregex,mysqlnd,openssl,pdo,pdo_mysql,pdo_sqlite,phar,session,simplexml,soap,sockets,sqlite3,tokenizer,xmlwriter,xmlreader,zlib,zip', }; // If you want to test shared extensions, add them below (comma separated, example `bcmath,openssl`). @@ -60,7 +62,7 @@ }; // If you want to test lib-suggests for all extensions and libraries, set it to true. -$with_suggested_libs = true; +$with_suggested_libs = false; // If you want to test extra libs for extensions, add them below (comma separated, example `libwebp,libavif`). Unnecessary, when $with_suggested_libs is true. $with_libs = match (PHP_OS_FAMILY) { @@ -72,7 +74,7 @@ // You can use `common`, `bulk`, `minimal` or `none`. // note: combination is only available for *nix platform. Windows must use `none` combination $base_combination = match (PHP_OS_FAMILY) { - 'Linux', 'Darwin' => 'minimal', + 'Linux', 'Darwin' => 'none', 'Windows' => 'none', }; diff --git a/tests/SPC/util/PkgConfigUtilTest.php b/tests/SPC/util/PkgConfigUtilTest.php index 6d8988fd4..64882a547 100644 --- a/tests/SPC/util/PkgConfigUtilTest.php +++ b/tests/SPC/util/PkgConfigUtilTest.php @@ -19,6 +19,10 @@ final class PkgConfigUtilTest extends TestCase public static function setUpBeforeClass(): void { + if (PHP_OS_FAMILY === 'Windows') { + // Skip tests on Windows as pkg-config is not typically available + self::markTestSkipped('PkgConfigUtil tests are not applicable on Windows.'); + } parent::setUpBeforeClass(); // Save original PATH diff --git a/tests/SPC/util/SPCConfigUtilTest.php b/tests/SPC/util/SPCConfigUtilTest.php index 7b8baa6c5..0c0fbc7ec 100644 --- a/tests/SPC/util/SPCConfigUtilTest.php +++ b/tests/SPC/util/SPCConfigUtilTest.php @@ -21,6 +21,10 @@ class SPCConfigUtilTest extends TestCase */ public static function setUpBeforeClass(): void { + if (PHP_OS_FAMILY === 'Windows') { + // Skip tests on Windows as SPCConfigUtil is not applicable + self::markTestSkipped('SPCConfigUtil tests are not applicable on Windows.'); + } $testdir = WORKING_DIR . '/.configtest'; FileSystem::createDir($testdir); FileSystem::writeFile($testdir . '/lib.json', file_get_contents(ROOT_DIR . '/config/lib.json'));