diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..025b8911e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml, vue}] +indent_size = 2 diff --git a/.gitignore b/.gitignore index 0560542f0..814b90202 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,7 @@ packlib_files.txt /docs/.vitepress/cache/ package-lock.json pnpm-lock.yaml + +# craft.log +craft.log +craft.yml diff --git a/bin/spc-alpine-docker b/bin/spc-alpine-docker index 607329086..a8702739e 100755 --- a/bin/spc-alpine-docker +++ b/bin/spc-alpine-docker @@ -1,6 +1,7 @@ #!/usr/bin/env sh # This file is using docker to run commands +SPC_DOCKER_VERSION=v3 # Detect docker can run if ! which docker >/dev/null; then @@ -50,9 +51,9 @@ else fi # Detect docker env is setup -if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-$SPC_USE_ARCH-v2; then +if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION; then echo "Docker container does not exist. Building docker image ..." - $DOCKER_EXECUTABLE build -t cwcc-spc-$SPC_USE_ARCH-v2 -f- . < /tmp/spc-gnu-docker.env diff --git a/composer.json b/composer.json index 97d835fb5..a3226d692 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,8 @@ "ext-zlib": "*", "laravel/prompts": "^0.1.12", "symfony/console": "^5.4 || ^6 || ^7", + "symfony/process": "^7.2", + "symfony/yaml": "^7.2", "zhamao/logger": "^1.0" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 6b2465b6d..3004785e2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7e3bd0c36428da870d1a0ae206c3b83e", + "content-hash": "d705cec8270679a463d9e04310208967", "packages": [ { "name": "illuminate/collections", - "version": "v11.44.1", + "version": "v11.44.4", "source": { "type": "git", "url": "https://github.com/illuminate/collections.git", - "reference": "48f5357348093bc8b94c691aa3ffc861d2ecc2a0" + "reference": "856b1da953e46281ba61d7c82d337072d3ee1825" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/collections/zipball/48f5357348093bc8b94c691aa3ffc861d2ecc2a0", - "reference": "48f5357348093bc8b94c691aa3ffc861d2ecc2a0", + "url": "https://api.github.com/repos/illuminate/collections/zipball/856b1da953e46281ba61d7c82d337072d3ee1825", + "reference": "856b1da953e46281ba61d7c82d337072d3ee1825", "shasum": "" }, "require": { @@ -60,20 +60,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-02-28T16:55:51+00:00" + "time": "2025-03-24T11:54:20+00:00" }, { "name": "illuminate/conditionable", - "version": "v11.44.1", + "version": "v11.44.4", "source": { "type": "git", "url": "https://github.com/illuminate/conditionable.git", - "reference": "911df1bda950a3b799cf80671764e34eede131c6" + "reference": "319b717e0587bd7c8a3b44464f0e27867b4bcda9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/conditionable/zipball/911df1bda950a3b799cf80671764e34eede131c6", - "reference": "911df1bda950a3b799cf80671764e34eede131c6", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/319b717e0587bd7c8a3b44464f0e27867b4bcda9", + "reference": "319b717e0587bd7c8a3b44464f0e27867b4bcda9", "shasum": "" }, "require": { @@ -106,20 +106,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-11-21T16:28:56+00:00" + "time": "2025-03-24T11:54:20+00:00" }, { "name": "illuminate/contracts", - "version": "v11.44.1", + "version": "v11.44.4", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", - "reference": "b350a3cd8450846325cb49e1cbc1293598b18898" + "reference": "4b2a67d1663f50085bc91e6371492697a5d2d4e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/b350a3cd8450846325cb49e1cbc1293598b18898", - "reference": "b350a3cd8450846325cb49e1cbc1293598b18898", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/4b2a67d1663f50085bc91e6371492697a5d2d4e8", + "reference": "4b2a67d1663f50085bc91e6371492697a5d2d4e8", "shasum": "" }, "require": { @@ -154,11 +154,11 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-02-10T14:20:57+00:00" + "time": "2025-03-24T11:54:20+00:00" }, { "name": "illuminate/macroable", - "version": "v11.44.1", + "version": "v11.44.4", "source": { "type": "git", "url": "https://github.com/illuminate/macroable.git", @@ -416,16 +416,16 @@ }, { "name": "symfony/console", - "version": "v6.4.17", + "version": "v6.4.20", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "799445db3f15768ecc382ac5699e6da0520a0a04" + "reference": "2e4af9c952617cc3f9559ff706aee420a8464c36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/799445db3f15768ecc382ac5699e6da0520a0a04", - "reference": "799445db3f15768ecc382ac5699e6da0520a0a04", + "url": "https://api.github.com/repos/symfony/console/zipball/2e4af9c952617cc3f9559ff706aee420a8464c36", + "reference": "2e4af9c952617cc3f9559ff706aee420a8464c36", "shasum": "" }, "require": { @@ -490,7 +490,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.17" + "source": "https://github.com/symfony/console/tree/v6.4.20" }, "funding": [ { @@ -506,7 +506,7 @@ "type": "tidelift" } ], - "time": "2024-12-07T12:07:30+00:00" + "time": "2025-03-03T17:16:38+00:00" }, { "name": "symfony/deprecation-contracts", @@ -893,6 +893,67 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/process", + "version": "v7.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "87b7c93e57df9d8e39a093d32587702380ff045d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/87b7c93e57df9d8e39a093d32587702380ff045d", + "reference": "87b7c93e57df9d8e39a093d32587702380ff045d", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-03-13T12:21:46+00:00" + }, { "name": "symfony/service-contracts", "version": "v3.5.1", @@ -1063,18 +1124,90 @@ ], "time": "2024-11-13T13:31:26+00:00" }, + { + "name": "symfony/yaml", + "version": "v7.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912", + "reference": "4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-03-03T07:12:39+00:00" + }, { "name": "zhamao/logger", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/zhamao-robot/zhamao-logger.git", - "reference": "1b7e34349330a842887d816f0344aba996cfeec0" + "reference": "1d7427abd8b1b3387ed7d9244953049356db3e62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zhamao-robot/zhamao-logger/zipball/1b7e34349330a842887d816f0344aba996cfeec0", - "reference": "1b7e34349330a842887d816f0344aba996cfeec0", + "url": "https://api.github.com/repos/zhamao-robot/zhamao-logger/zipball/1d7427abd8b1b3387ed7d9244953049356db3e62", + "reference": "1d7427abd8b1b3387ed7d9244953049356db3e62", "shasum": "" }, "require": { @@ -1128,9 +1261,9 @@ "description": "Another Console Logger for CLI Applications", "support": { "issues": "https://github.com/zhamao-robot/zhamao-logger/issues", - "source": "https://github.com/zhamao-robot/zhamao-logger/tree/1.1.1" + "source": "https://github.com/zhamao-robot/zhamao-logger/tree/1.1.2" }, - "time": "2023-03-09T15:41:10+00:00" + "time": "2025-04-24T02:02:28+00:00" } ], "packages-dev": [ @@ -1217,16 +1350,16 @@ }, { "name": "amphp/byte-stream", - "version": "v2.1.1", + "version": "v2.1.2", "source": { "type": "git", "url": "https://github.com/amphp/byte-stream.git", - "reference": "daa00f2efdbd71565bf64ffefa89e37542addf93" + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/byte-stream/zipball/daa00f2efdbd71565bf64ffefa89e37542addf93", - "reference": "daa00f2efdbd71565bf64ffefa89e37542addf93", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46", "shasum": "" }, "require": { @@ -1280,7 +1413,7 @@ ], "support": { "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v2.1.1" + "source": "https://github.com/amphp/byte-stream/tree/v2.1.2" }, "funding": [ { @@ -1288,7 +1421,7 @@ "type": "github" } ], - "time": "2024-02-17T04:49:38+00:00" + "time": "2025-03-16T17:10:27+00:00" }, { "name": "amphp/cache", @@ -1592,16 +1725,16 @@ }, { "name": "amphp/pipeline", - "version": "v1.2.2", + "version": "v1.2.3", "source": { "type": "git", "url": "https://github.com/amphp/pipeline.git", - "reference": "97cbf289f4d8877acfe58dd90ed5a4370a43caa4" + "reference": "7b52598c2e9105ebcddf247fc523161581930367" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/pipeline/zipball/97cbf289f4d8877acfe58dd90ed5a4370a43caa4", - "reference": "97cbf289f4d8877acfe58dd90ed5a4370a43caa4", + "url": "https://api.github.com/repos/amphp/pipeline/zipball/7b52598c2e9105ebcddf247fc523161581930367", + "reference": "7b52598c2e9105ebcddf247fc523161581930367", "shasum": "" }, "require": { @@ -1647,7 +1780,7 @@ ], "support": { "issues": "https://github.com/amphp/pipeline/issues", - "source": "https://github.com/amphp/pipeline/tree/v1.2.2" + "source": "https://github.com/amphp/pipeline/tree/v1.2.3" }, "funding": [ { @@ -1655,7 +1788,7 @@ "type": "github" } ], - "time": "2025-01-19T15:42:46+00:00" + "time": "2025-03-16T16:33:53+00:00" }, { "name": "amphp/process", @@ -1944,23 +2077,23 @@ }, { "name": "captainhook/captainhook-phar", - "version": "5.25.0", + "version": "5.25.2", "source": { "type": "git", - "url": "https://github.com/captainhookphp/captainhook-phar.git", - "reference": "de7a0e8a2d4ee7b63f46ac61c6f9a89ff0a30011" + "url": "https://github.com/captainhook-git/captainhook-phar.git", + "reference": "ab47b0561ec7997bf9b88181d090857874f2cf8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/captainhookphp/captainhook-phar/zipball/de7a0e8a2d4ee7b63f46ac61c6f9a89ff0a30011", - "reference": "de7a0e8a2d4ee7b63f46ac61c6f9a89ff0a30011", + "url": "https://api.github.com/repos/captainhook-git/captainhook-phar/zipball/ab47b0561ec7997bf9b88181d090857874f2cf8c", + "reference": "ab47b0561ec7997bf9b88181d090857874f2cf8c", "shasum": "" }, "require": { "composer-plugin-api": "^1.1||^2.0", "ext-json": "*", "ext-spl": "*", - "phar-io/composer-distributor": "^1.0", + "phar-io/composer-distributor": "^1.0.1", "php": ">=8.0" }, "require-dev": { @@ -1998,7 +2131,7 @@ ], "support": { "issues": "https://github.com/captainhookphp/captainhook/issues", - "source": "https://github.com/captainhookphp/captainhook-phar/tree/5.25.0" + "source": "https://github.com/captainhook-git/captainhook-phar/tree/5.25.2" }, "funding": [ { @@ -2006,20 +2139,20 @@ "type": "github" } ], - "time": "2025-01-16T12:46:49+00:00" + "time": "2025-03-30T17:19:44+00:00" }, { "name": "captainhook/hook-installer", - "version": "1.0.3", + "version": "1.0.4", "source": { "type": "git", - "url": "https://github.com/captainhookphp/hook-installer.git", - "reference": "3308a9152727af4e3d1c7b63ca219d6938b702b8" + "url": "https://github.com/captainhook-git/hook-installer.git", + "reference": "fb3c45f6204b08baba999f4ffc4ae707bf684e8b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/captainhookphp/hook-installer/zipball/3308a9152727af4e3d1c7b63ca219d6938b702b8", - "reference": "3308a9152727af4e3d1c7b63ca219d6938b702b8", + "url": "https://api.github.com/repos/captainhook-git/hook-installer/zipball/fb3c45f6204b08baba999f4ffc4ae707bf684e8b", + "reference": "fb3c45f6204b08baba999f4ffc4ae707bf684e8b", "shasum": "" }, "require": { @@ -2050,10 +2183,10 @@ ], "description": "Composer Plugin that makes everyone activate the CaptainHook git hooks locally", "support": { - "issues": "https://github.com/captainhookphp/hook-installer/issues", - "source": "https://github.com/captainhookphp/hook-installer/tree/1.0.3" + "issues": "https://github.com/captainhook-git/hook-installer/issues", + "source": "https://github.com/captainhook-git/hook-installer/tree/1.0.4" }, - "time": "2024-03-21T13:39:59+00:00" + "time": "2025-04-08T07:12:26+00:00" }, { "name": "clue/ndjson-react", @@ -2391,26 +2524,29 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.4", + "version": "1.1.5", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9" + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/31610dbb31faa98e6b5447b62340826f54fbc4e9", - "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=13" + }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12", - "phpstan/phpstan": "1.4.10 || 2.0.3", + "doctrine/coding-standard": "^9 || ^12 || ^13", + "phpstan/phpstan": "1.4.10 || 2.1.11", "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", "psr/log": "^1 || ^2 || ^3" }, "suggest": { @@ -2430,9 +2566,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.4" + "source": "https://github.com/doctrine/deprecations/tree/1.1.5" }, - "time": "2024-12-07T21:18:45+00:00" + "time": "2025-04-07T20:06:18+00:00" }, { "name": "evenement/evenement", @@ -2696,16 +2832,16 @@ }, { "name": "filp/whoops", - "version": "2.17.0", + "version": "2.18.0", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "075bc0c26631110584175de6523ab3f1652eb28e" + "reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/075bc0c26631110584175de6523ab3f1652eb28e", - "reference": "075bc0c26631110584175de6523ab3f1652eb28e", + "url": "https://api.github.com/repos/filp/whoops/zipball/a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", + "reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", "shasum": "" }, "require": { @@ -2755,7 +2891,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.17.0" + "source": "https://github.com/filp/whoops/tree/2.18.0" }, "funding": [ { @@ -2763,20 +2899,20 @@ "type": "github" } ], - "time": "2025-01-25T12:00:00+00:00" + "time": "2025-03-15T12:00:00+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.70.2", + "version": "v3.75.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "1ca468270efbb75ce0c7566a79cca8ea2888584d" + "reference": "399a128ff2fdaf4281e4e79b755693286cdf325c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/1ca468270efbb75ce0c7566a79cca8ea2888584d", - "reference": "1ca468270efbb75ce0c7566a79cca8ea2888584d", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/399a128ff2fdaf4281e4e79b755693286cdf325c", + "reference": "399a128ff2fdaf4281e4e79b755693286cdf325c", "shasum": "" }, "require": { @@ -2784,6 +2920,7 @@ "composer/semver": "^3.4", "composer/xdebug-handler": "^3.0.3", "ext-filter": "*", + "ext-hash": "*", "ext-json": "*", "ext-tokenizer": "*", "fidry/cpu-core-counter": "^1.2", @@ -2806,18 +2943,18 @@ "symfony/stopwatch": "^5.4 || ^6.4 || ^7.0" }, "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.5", - "infection/infection": "^0.29.10", - "justinrainbow/json-schema": "^5.3 || ^6.0", + "facile-it/paraunit": "^1.3.1 || ^2.6", + "infection/infection": "^0.29.14", + "justinrainbow/json-schema": "^5.3 || ^6.2", "keradus/cli-executor": "^2.1", "mikey179/vfsstream": "^1.6.12", "php-coveralls/php-coveralls": "^2.7", "php-cs-fixer/accessible-object": "^1.1", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.22 || ^10.5.45 || ^11.5.7", - "symfony/var-dumper": "^5.4.48 || ^6.4.18 || ^7.2.0", - "symfony/yaml": "^5.4.45 || ^6.4.18 || ^7.2.0" + "phpunit/phpunit": "^9.6.22 || ^10.5.45 || ^11.5.12", + "symfony/var-dumper": "^5.4.48 || ^6.4.18 || ^7.2.3", + "symfony/yaml": "^5.4.45 || ^6.4.18 || ^7.2.3" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -2858,7 +2995,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.70.2" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.75.0" }, "funding": [ { @@ -2866,7 +3003,7 @@ "type": "github" } ], - "time": "2025-03-03T21:07:23+00:00" + "time": "2025-03-31T18:40:42+00:00" }, { "name": "humbug/box", @@ -3579,40 +3716,40 @@ }, { "name": "nunomaduro/collision", - "version": "v7.11.0", + "version": "v7.12.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "994ea93df5d4132f69d3f1bd74730509df6e8a05" + "reference": "995245421d3d7593a6960822063bdba4f5d7cf1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/994ea93df5d4132f69d3f1bd74730509df6e8a05", - "reference": "994ea93df5d4132f69d3f1bd74730509df6e8a05", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/995245421d3d7593a6960822063bdba4f5d7cf1a", + "reference": "995245421d3d7593a6960822063bdba4f5d7cf1a", "shasum": "" }, "require": { - "filp/whoops": "^2.16.0", - "nunomaduro/termwind": "^1.15.1", + "filp/whoops": "^2.17.0", + "nunomaduro/termwind": "^1.17.0", "php": "^8.1.0", - "symfony/console": "^6.4.12" + "symfony/console": "^6.4.17" }, "conflict": { "laravel/framework": ">=11.0.0" }, "require-dev": { - "brianium/paratest": "^7.3.1", - "laravel/framework": "^10.48.22", - "laravel/pint": "^1.18.1", - "laravel/sail": "^1.36.0", + "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.0", - "nunomaduro/larastan": "^2.9.8", - "orchestra/testbench-core": "^8.28.3", - "pestphp/pest": "^2.35.1", + "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.8.0" + "spatie/laravel-ignition": "^2.9.1" }, "type": "library", "extra": { @@ -3671,7 +3808,7 @@ "type": "patreon" } ], - "time": "2024-10-15T15:12:40+00:00" + "time": "2025-03-14T22:35:49+00:00" }, { "name": "nunomaduro/termwind", @@ -4145,16 +4282,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.6.1", + "version": "5.6.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8" + "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8", - "reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/92dde6a5919e34835c506ac8c523ef095a95ed62", + "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62", "shasum": "" }, "require": { @@ -4203,9 +4340,9 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.1" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.2" }, - "time": "2024-12-07T09:39:29+00:00" + "time": "2025-04-13T19:20:35+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -4314,16 +4451,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.20", + "version": "1.12.24", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "3240b1972042c7f73cf1045e879ea5bd5f761bb7" + "reference": "338b92068f58d9f8035b76aed6cf2b9e5624c025" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/3240b1972042c7f73cf1045e879ea5bd5f761bb7", - "reference": "3240b1972042c7f73cf1045e879ea5bd5f761bb7", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/338b92068f58d9f8035b76aed6cf2b9e5624c025", + "reference": "338b92068f58d9f8035b76aed6cf2b9e5624c025", "shasum": "" }, "require": { @@ -4368,7 +4505,7 @@ "type": "github" } ], - "time": "2025-03-05T13:37:43+00:00" + "time": "2025-04-16T13:01:53+00:00" }, { "name": "phpunit/php-code-coverage", @@ -7085,67 +7222,6 @@ ], "time": "2024-09-09T12:04:04+00:00" }, - { - "name": "symfony/process", - "version": "v7.2.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", - "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v7.2.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-02-05T08:33:46+00:00" - }, { "name": "symfony/stopwatch", "version": "v7.2.4", @@ -7293,16 +7369,16 @@ }, { "name": "thecodingmachine/safe", - "version": "v3.0.2", + "version": "v3.1.0", "source": { "type": "git", "url": "https://github.com/thecodingmachine/safe.git", - "reference": "22ffad3248982a784f9870a37aeb2e522bd19645" + "reference": "e14ac96126e6c19ea9d1f4029abb51487f4cf2cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/22ffad3248982a784f9870a37aeb2e522bd19645", - "reference": "22ffad3248982a784f9870a37aeb2e522bd19645", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/e14ac96126e6c19ea9d1f4029abb51487f4cf2cf", + "reference": "e14ac96126e6c19ea9d1f4029abb51487f4cf2cf", "shasum": "" }, "require": { @@ -7412,7 +7488,7 @@ "description": "PHP core functions that throw exceptions instead of returning FALSE on error", "support": { "issues": "https://github.com/thecodingmachine/safe/issues", - "source": "https://github.com/thecodingmachine/safe/tree/v3.0.2" + "source": "https://github.com/thecodingmachine/safe/tree/v3.1.0" }, "funding": [ { @@ -7428,7 +7504,7 @@ "type": "github" } ], - "time": "2025-02-19T19:23:00+00:00" + "time": "2025-04-12T06:41:26+00:00" }, { "name": "theseer/tokenizer", @@ -7545,7 +7621,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "~8.4.0", + "php": ">= 8.3", "ext-mbstring": "*", "ext-zlib": "*" }, diff --git a/docs/.vitepress/components/CliGenerator.vue b/docs/.vitepress/components/CliGenerator.vue index e3537d87c..0d830b730 100644 --- a/docs/.vitepress/components/CliGenerator.vue +++ b/docs/.vitepress/components/CliGenerator.vue @@ -175,6 +175,12 @@ {{ buildCommandString }} +
+ craft.yml +
+ {{ craftCommandString }} +
+
@@ -499,6 +505,38 @@ const buildCommandString = computed(() => { return `${spcCommand.value} build ${buildCommand.value} "${extList.value}"${additionalLibs.value}${debug.value ? ' --debug' : ''}${zts.value ? ' --enable-zts' : ''}${enableUPX.value ? ' --with-upx-pack' : ''}${displayINI.value}`; }); +const craftCommandString = computed(() => { + let str = `php-version: ${selectedPhpVersion.value}\n`; + str += `extensions: "${extList.value}"\n`; + if (checkedTargets.value.join(',') === 'all') { + str += 'sapi: ' + ['cli', 'fpm', 'micro', 'embed'].join(',') + '\n'; + } else { + str += `sapi: ${checkedTargets.value.join(',')}\n`; + } + if (additionalLibs.value) { + str += `libs: ${additionalLibs.value.replace('--with-libs="', '').replace('"', '').trim()}\n`; + } + if (debug.value) { + str += 'debug: true\n'; + } + str += '{{position_hold}}'; + if (enableUPX.value) { + str += ' with-upx-pack: true\n'; + } + if (zts.value) { + str += ' enable-zts: true\n'; + } + if (preBuilt.value) { + str += ' prefer-pre-built: true\n'; + } + if (!str.endsWith('{{position_hold}}')) { + str = str.replace('{{position_hold}}', 'build-options:\n'); + } else { + str = str.replace('{{position_hold}}', ''); + } + return str; +}); + const calculateExtLibDepends = (input) => { const result = new Set(); @@ -617,6 +655,12 @@ h2 { overflow-wrap: break-word; } +.pre { + white-space: pre-wrap; + word-break: break-word; + overflow-wrap: break-word; +} + .option-line { padding: 4px 8px; } diff --git a/docs/.vitepress/sidebar.en.ts b/docs/.vitepress/sidebar.en.ts index 1bd0686d0..54bcb0e1a 100644 --- a/docs/.vitepress/sidebar.en.ts +++ b/docs/.vitepress/sidebar.en.ts @@ -1,56 +1,57 @@ export default { - '/en/guide/': [ - { - text: 'Basic Build Guides', - items: [ - {text: 'Guide', link: '/en/guide/'}, - {text: 'Build (Local)', link: '/en/guide/manual-build'}, - {text: 'Build (CI)', link: '/en/guide/action-build'}, - {text: 'Supported Extensions', link: '/en/guide/extensions'}, - {text: 'Extension Notes', link: '/en/guide/extension-notes'}, - {text: 'Build Command Generator', link: '/en/guide/cli-generator'}, - {text: 'Environment Variables', link: '/en/guide/env-vars', collapsed: true,}, - {text: 'Dependency Table', link: '/en/guide/deps-map'}, - ] - }, - { - text: 'Extended Build Guides', - items: [ - {text: 'Troubleshooting', link: '/en/guide/troubleshooting'}, - {text: 'Build on Windows', link: '/en/guide/build-on-windows'}, - {text: 'Build with GNU libc', link: '/en/guide/build-with-glibc'}, - ], - } - ], - '/en/develop/': [ - { - text: 'Development', - items: [ - {text: 'Get Started', link: '/en/develop/'}, - {text: 'Project Structure', link: '/en/develop/structure'}, - {text: 'PHP Source Modification', link: '/en/develop/php-src-changes'}, - ], - }, - { - text: 'Module', - items: [ - {text: 'Doctor ', link: '/en/develop/doctor-module'}, - {text: 'Source', link: '/en/develop/source-module'}, - ] - }, - { - text: 'Extra', - items: [ - {text: 'Compilation Tools', link: '/en/develop/system-build-tools'}, - ] - } - ], - '/en/contributing/': [ - { - text: 'Contributing', - items: [ - {text: 'Contributing', link: '/en/contributing/'}, - ], - } - ], + '/en/guide/': [ + { + text: 'Basic Build Guides', + items: [ + {text: 'Guide', link: '/en/guide/'}, + {text: 'Build (Local)', link: '/en/guide/manual-build'}, + {text: 'Build (CI)', link: '/en/guide/action-build'}, + {text: 'Supported Extensions', link: '/en/guide/extensions'}, + {text: 'Extension Notes', link: '/en/guide/extension-notes'}, + {text: 'Build Command Generator', link: '/en/guide/cli-generator'}, + {text: 'Environment Variables', link: '/en/guide/env-vars', collapsed: true,}, + {text: 'Dependency Table', link: '/en/guide/deps-map'}, + ] + }, + { + text: 'Extended Build Guides', + items: [ + {text: 'Troubleshooting', link: '/en/guide/troubleshooting'}, + {text: 'Build on Windows', link: '/en/guide/build-on-windows'}, + {text: 'Build with GNU libc', link: '/en/guide/build-with-glibc'}, + ], + } + ], + '/en/develop/': [ + { + text: 'Development', + items: [ + {text: 'Get Started', link: '/en/develop/'}, + {text: 'Project Structure', link: '/en/develop/structure'}, + {text: 'PHP Source Modification', link: '/en/develop/php-src-changes'}, + ], + }, + { + text: 'Module', + items: [ + {text: 'Doctor ', link: '/en/develop/doctor-module'}, + {text: 'Source', link: '/en/develop/source-module'}, + ] + }, + { + text: 'Extra', + items: [ + {text: 'Compilation Tools', link: '/en/develop/system-build-tools'}, + {text: 'craft.yml Configuration', link: '/zh/develop/craft-yml'}, + ] + } + ], + '/en/contributing/': [ + { + text: 'Contributing', + items: [ + {text: 'Contributing', link: '/en/contributing/'}, + ], + } + ], }; diff --git a/docs/.vitepress/sidebar.zh.ts b/docs/.vitepress/sidebar.zh.ts index cf1630b11..63592b93b 100644 --- a/docs/.vitepress/sidebar.zh.ts +++ b/docs/.vitepress/sidebar.zh.ts @@ -1,56 +1,57 @@ export default { - '/zh/guide/': [ - { - text: '构建指南', - items: [ - {text: '指南', link: '/zh/guide/'}, - {text: '本地构建', link: '/zh/guide/manual-build'}, - {text: 'Actions 构建', link: '/zh/guide/action-build'}, - {text: '扩展列表', link: '/zh/guide/extensions'}, - {text: '扩展注意事项', link: '/zh/guide/extension-notes'}, - {text: '编译命令生成器', link: '/zh/guide/cli-generator'}, - {text: '环境变量列表', link: '/zh/guide/env-vars'}, - {text: '依赖关系图表', link: '/zh/guide/deps-map'}, - ] - }, - { - text: '扩展构建指南', - items: [ - {text: '故障排除', link: '/zh/guide/troubleshooting'}, - {text: '在 Windows 上构建', link: '/zh/guide/build-on-windows'}, - {text: '构建 GNU libc 兼容的二进制', link: '/zh/guide/build-with-glibc'}, - ], - } - ], - '/zh/develop/': [ - { - text: '开发指南', - items: [ - { text: '开发简介', link: '/zh/develop/' }, - { text: '项目结构简介', link: '/zh/develop/structure' }, - {text: '对 PHP 源码的修改', link: '/zh/develop/php-src-changes'}, - ], - }, - { - text: '模块', - items: [ - { text: 'Doctor 环境检查工具', link: '/zh/develop/doctor-module' }, - { text: '资源模块', link: '/zh/develop/source-module' }, - ] - }, - { - text: '其他', - items: [ - {text: '系统编译工具', link: '/zh/develop/system-build-tools'}, - ] - } - ], - '/zh/contributing/': [ - { - text: '贡献指南', - items: [ - {text: '贡献指南', link: '/zh/contributing/'}, - ], - } - ], + '/zh/guide/': [ + { + text: '构建指南', + items: [ + {text: '指南', link: '/zh/guide/'}, + {text: '本地构建', link: '/zh/guide/manual-build'}, + {text: 'Actions 构建', link: '/zh/guide/action-build'}, + {text: '扩展列表', link: '/zh/guide/extensions'}, + {text: '扩展注意事项', link: '/zh/guide/extension-notes'}, + {text: '编译命令生成器', link: '/zh/guide/cli-generator'}, + {text: '环境变量列表', link: '/zh/guide/env-vars'}, + {text: '依赖关系图表', link: '/zh/guide/deps-map'}, + ] + }, + { + text: '扩展构建指南', + items: [ + {text: '故障排除', link: '/zh/guide/troubleshooting'}, + {text: '在 Windows 上构建', link: '/zh/guide/build-on-windows'}, + {text: '构建 GNU libc 兼容的二进制', link: '/zh/guide/build-with-glibc'}, + ], + } + ], + '/zh/develop/': [ + { + text: '开发指南', + items: [ + {text: '开发简介', link: '/zh/develop/'}, + {text: '项目结构简介', link: '/zh/develop/structure'}, + {text: '对 PHP 源码的修改', link: '/zh/develop/php-src-changes'}, + ], + }, + { + text: '模块', + items: [ + {text: 'Doctor 环境检查工具', link: '/zh/develop/doctor-module'}, + {text: '资源模块', link: '/zh/develop/source-module'}, + ] + }, + { + text: '其他', + items: [ + {text: '系统编译工具', link: '/zh/develop/system-build-tools'}, + {text: 'craft.yml 配置详解', link: '/zh/develop/craft-yml'}, + ] + } + ], + '/zh/contributing/': [ + { + text: '贡献指南', + items: [ + {text: '贡献指南', link: '/zh/contributing/'}, + ], + } + ], }; diff --git a/docs/deps-craft-yml.md b/docs/deps-craft-yml.md new file mode 100644 index 000000000..9e660be88 --- /dev/null +++ b/docs/deps-craft-yml.md @@ -0,0 +1,67 @@ +```yaml +# PHP version to build (default: 8.4) +php-version: 8.4 +# [REQUIRED] Static PHP extensions to build (list or comma-separated are both accepted) +extensions: bcmath,fileinfo,phar,zlib,sodium,posix,pcntl +# Extra libraries to build (list or comma-separated are both accepted) +libs: [ ] +# [REQUIRED] Build SAPIs (list or comma-separated are both accepted) +sapi: cli,micro,fpm +# Show full console output (default: false) +debug: false +# Build options (same as `build` command options, all options are optional) +build-options: + # Before build, remove all old build files and sources (default: false) + with-clean: false + # Build with all suggested libraries (default: false) + with-suggested-libs: false + # Build with all suggested extensions (default: false) + with-suggested-exts: false + # Build extra shared extensions (list or comma-separated are both accepted) + build-shared: [ ] + # Build without stripping the binary (default: false) + no-strip: false + # Disable Opcache JIT (default: false) + disable-opcache-jit: false + # PHP configuration options (same as --with-config-file-path) + with-config-file-path: "" + # PHP configuration options (same as --with-config-file-scan-dir) + with-config-file-scan-dir: "" + # Hardcoded INI options for cli and micro SAPI (e.g. "memory_limit=4G", list accepted) + with-hardcoded-ini: [ ] + # Pretend micro SAPI as cli SAPI to avoid some frameworks to limit the usage of micro SAPI + with-micro-fake-cli: false + # Additional patch point injection files (e.g. "path/to/patch.php", list accepted) + with-added-patch: [ ] + # Ignore micro extension tests (if you are using micro SAPI, default: false) + without-micro-ext-test: false + # UPX pack the binary (default: false) + with-upx-pack: false + # Set the micro.exe program icon (only for Windows, default: "") + with-micro-logo: "" + # Set micro SAPI as win32 mode, without this, micro SAPI will be compiled as a console application (only for Windows, default: false) + enable-micro-win32: false + +# Download options +download-options: + # Use custom url for specified sources, format: "{source-name}:{url}" (e.g. "php-src:https://example.com/php-8.4.0.tar.gz") + custom-url: [ ] + # Use custom git repo for specified sources, format: "{source-name}:{branch}:{url}" (e.g. "php-src:master:https://github.com/php/php-src.git") + custom-git: [ ] + # Retries count for downloading sources (default: 5) + retry: 5 + # Use pre-built libraries if available (default: false) + prefer-pre-built: true + # Do not download from alternative sources (default: false) + no-alt: false + +craft-options: + doctor: true + download: true + build: true + +# Extra environment variables +extra-env: + # e.g. Use github token to avoid rate limit + GITHUB_TOKEN: your-github-token +``` diff --git a/docs/en/develop/craft-yml.md b/docs/en/develop/craft-yml.md new file mode 100644 index 000000000..e0aea228f --- /dev/null +++ b/docs/en/develop/craft-yml.md @@ -0,0 +1,7 @@ +--- +aside: false +--- + +# craft.yml Configuration + + diff --git a/docs/en/guide/manual-build.md b/docs/en/guide/manual-build.md index c935f5c8c..3c40d5eb6 100644 --- a/docs/en/guide/manual-build.md +++ b/docs/en/guide/manual-build.md @@ -1,3 +1,7 @@ +--- +outline: 'deep' +--- + # Build (Linux, macOS, FreeBSD) This section covers the build process for Linux, macOS, and FreeBSD. If you want to build on Windows, @@ -133,7 +137,47 @@ and then install the latest version of PHP and tokenizer, XML, and phar extensio Older versions of Debian may have an older (<= 7.4) version of PHP installed by default, it is recommended to upgrade Debian first. ::: -## Command - download +## Build with craft (recommended) + +Using `bin/spc craft`, you can use a configuration file and a command to automatically check the environment, download source code, build dependency libraries, build PHP and extensions, etc. + +You need to write a `craft.yml` file and save it in the current working directory. `craft.yml` can be generated by [command generator](./cli-generator) or written manually. + +For manual writing, please refer to the comments in [craft.yml configuration](../develop/craft-yml.md) to write it. +Let's assume that you compile an extension combination and choose PHP 8.4, outputting `cli` and `fpm`: + +```yaml +# path/to/craft.yml +php-version: 8.4 +extensions: bcmath,posix,phar,zlib,openssl,curl,fileinfo,tokenizer +sapi: + - cli + - fpm +``` + +Then use the `bin/spc craft` command to compile: + +```bash +bin/spc craft --debug +``` + +If the build is successful, you will see the `buildroot/bin` directory in the current directory, which contains the compiled PHP binary file, or the corresponding SAPI. + +- cli: The build result is `buildroot/bin/php.exe` on Windows and `buildroot/bin/php` on other platforms. +- fpm: The build result is `buildroot/bin/php-fpm`. +- micro: The build result is `buildroot/bin/micro.sfx`. If you need to further package it with PHP code, please refer to [Packaging micro binary](./manual-build#command-micro-combine). +- embed: See [Using embed](./manual-build#embed-usage). + +If the build fails, you can use the `--debug` parameter to view detailed error information, +or use the `--with-clean` to clear the old compilation results and recompile. + +If the build still fails to use the above method, please submit an issue and attach your `craft.yml` and `craft.log`. + +## Step-by-step build command + +If you have customized requirements, or the need to download and compile PHP and dependent libraries separately, you can use the `bin/spc` command to execute step by step. + +### Command download - Download dependency packages Use the command `bin/spc download` to download the source code required for compilation, including php-src and the source code of various dependent libraries. @@ -217,7 +261,7 @@ bin/spc download --for-extensions=redis -G "php-src:master:https://github.com/ph bin/spc download --for-extensions=swoole -G "swoole:master:https://github.com/swoole/swoole-src.git" ``` -## Command - doctor +### Command - doctor If you can run `bin/spc` normally but cannot compile static PHP or dependent libraries normally, you can run `bin/spc doctor` first to check whether the system itself lacks dependencies. @@ -230,13 +274,13 @@ bin/spc doctor bin/spc doctor --auto-fix ``` -## Command - build +### Command - build Use the build command to start building the static php binary. Before executing the `bin/spc build` command, be sure to use the `download` command to download sources. It is recommended to use `doctor` to check the environment. -### Basic build +#### Basic build You need to go to [Extension List](./extensions) or [Command Generator](./cli-generator) to select the extension you want to add, and then use the command `bin/spc build` to compile. @@ -283,16 +327,7 @@ bin/spc build bcmath,curl,openssl,ftp,posix,pcntl --build-cli ``` ::: -### Debug - -If you encounter problems during the compilation process, or want to view each executing shell command, -you can use `--debug` to enable debug mode and view all terminal logs: - -```bash -bin/spc build mysqlnd,pdo_mysql --build-all --debug -``` - -### Build Options +#### Build Options During the compilation process, in some special cases, the compiler and the content of the compilation directory need to be intervened. @@ -322,6 +357,15 @@ For hardcoding INI options, it works for cli, micro, embed sapi. Here is a simpl bin/spc build bcmath,pcntl,posix --build-all -I "memory_limit=4G" -I "disable_functions=system" ``` +## Debug + +If you encounter problems during the compilation process, or want to view each executing shell command, +you can use `--debug` to enable debug mode and view all terminal logs: + +```bash +bin/spc build mysqlnd,pdo_mysql --build-all --debug +``` + ## Command - micro:combine Use the `micro:combine` command to build the compiled `micro.sfx` and your code (`.php` or `.phar` file) into an executable binary. diff --git a/docs/zh/develop/craft-yml.md b/docs/zh/develop/craft-yml.md new file mode 100644 index 000000000..b96842f91 --- /dev/null +++ b/docs/zh/develop/craft-yml.md @@ -0,0 +1,7 @@ +--- +aside: false +--- + +# craft.yml 配置 + + diff --git a/docs/zh/guide/manual-build.md b/docs/zh/guide/manual-build.md index 955d98c86..ca0395c8c 100644 --- a/docs/zh/guide/manual-build.md +++ b/docs/zh/guide/manual-build.md @@ -1,3 +1,7 @@ +--- +outline: 'deep' +--- + # 本地构建(Linux、macOS、FreeBSD) 本章节为 Linux、macOS、FreeBSD 的构建过程,如果你要在 Windows 上构建,请到 [在 Windows 上构建](./build-on-windows)。 @@ -112,7 +116,45 @@ sudo apt install php-cli composer php-tokenizer 较老版本的 Debian 默认安装的可能为旧版本(<= 8.3)版本的 PHP,建议先升级 Debian 或使用 Docker 或自带的静态二进制环境。 ::: -## 命令 download - 下载依赖包 +## 使用 craft 构建(推荐) + +使用 `bin/spc craft` 可以使用一个配置文件,一个命令实现自动检查环境、下载源代码、构建依赖库、构建 PHP 及扩展等。 + +你需要编写一个 `craft.yml` 文件,存放在当前工作目录下。`craft.yml` 可以由 [命令生成器](./cli-generator) 生成,或者手动编写。 + +手动编写可参考 [craft.yml 配置](../develop/craft-yml.md) 中的注释来编写。我们下面假设你编译一个扩展组合,并选用 PHP 8.4,输出 `cli` 和 `fpm`: + +```yaml +# path/to/craft.yml +php-version: 8.4 +extensions: bcmath,posix,phar,zlib,openssl,curl,fileinfo,tokenizer +sapi: + - cli + - fpm +``` + +然后使用 `bin/spc craft` 命令来编译: + +```bash +bin/spc craft --debug +``` + +如果构建成功,你会在当前目录下看到 `buildroot/bin` 目录,里面包含了编译好的 PHP 二进制文件,或相应的 SAPI。 + +- cli: Windows 下构建结果为 `buildroot/bin/php.exe`,其他平台为 `buildroot/bin/php`。 +- fpm: 构建结果为 `buildroot/bin/php-fpm`。 +- micro: 构建结果为 `buildroot/bin/micro.sfx`,如需进一步与 PHP 代码打包,请查看 [打包 micro 二进制](./manual-build#命令-micro-combine-打包-micro-二进制)。 +- embed: 参见 [embed 使用](./manual-build#embed-使用)。 + +如果中途构建出错,你可以使用 `--debug` 参数查看详细的错误信息,或者使用 `--with-clean` 参数清除旧的编译结果,重新编译。 + +如使用以上方式仍构建失败,请提交一个 issue,附上你的 `craft.yml`、`craft.log`。 + +## 分步构建命令 + +如果你有定制化需求,或分开下载、编译 PHP 和依赖库的需求,可以使用 `bin/spc` 命令分步执行。 + +### 命令 download - 下载依赖包 使用命令 `bin/spc download` 可以下载编译需要的源代码,包括 php-src 以及依赖的各种库的源码。 @@ -184,7 +226,7 @@ bin/spc download --for-extensions=redis,phar -G "php-src:master:https://github.c bin/spc download --for-extensions=swoole -G "swoole:master:https://github.com/swoole/swoole-src.git" ``` -## 命令 doctor - 环境检查 +### 命令 doctor - 环境检查 如果你可以正常运行 `bin/spc` 但无法正常编译静态的 PHP 或依赖库,可以先运行 `bin/spc doctor` 检查系统自身是否缺少依赖。 @@ -196,11 +238,11 @@ bin/spc doctor bin/spc doctor --auto-fix ``` -## 命令 build - 编译 PHP +### 命令 build - 编译 PHP 使用 build 命令可以开始构建静态 php 二进制,在执行 `bin/spc build` 命令前,务必先使用 `download` 命令下载资源,建议使用 `doctor` 检查环境。 -### 基本用法 +#### 基本用法 你需要先到 [扩展列表](./extensions) 或 [命令生成器](./cli-generator) 选择你要加入的扩展,然后使用命令 `bin/spc build` 进行编译。你需要指定一个编译目标,从如下参数中选择: @@ -244,15 +286,7 @@ bin/spc build bcmath,curl,openssl,ftp,posix,pcntl --build-cli ``` ::: -### 调试 - -如果你在编译过程中遇到了问题,或者想查看每个执行的 shell 命令,可以使用 `--debug` 开启 debug 模式,查看所有终端日志: - -```bash -bin/spc build mysqlnd,pdo_mysql --build-all --debug -``` - -### 编译运行选项 +#### 编译运行选项 在编译过程中,有些特殊情况需要对编译器、编译目录的内容进行干预,可以尝试使用以下命令: @@ -340,6 +374,14 @@ memory_limit=1G 如果要打包 phar,只需要将 `a.php` 替换为打包好的 phar 文件即可。但要注意,phar 下的 micro.sfx 需要额外注意路径问题,见 [Developing - Phar 路径问题](../develop/structure#phar-应用目录问题) +## 调试 + +如果你在编译过程中遇到了问题,或者想查看每个执行的 shell 命令,可以使用 `--debug` 开启 debug 模式,查看所有终端日志: + +```bash +bin/spc build mysqlnd,pdo_mysql --build-all --debug +``` + ## 命令 extract - 手动解压某个库 使用命令 `bin/spc extract` 可以解包和拷贝编译需要的源代码,包括 php-src 以及依赖的各种库的源码(需要自己指定要解包的库名)。 diff --git a/src/SPC/ConsoleApplication.php b/src/SPC/ConsoleApplication.php index bab8a0083..0ec10cedd 100644 --- a/src/SPC/ConsoleApplication.php +++ b/src/SPC/ConsoleApplication.php @@ -6,6 +6,7 @@ use SPC\command\BuildLibsCommand; use SPC\command\BuildPHPCommand; +use SPC\command\CraftCommand; use SPC\command\DeleteDownloadCommand; use SPC\command\dev\AllExtCommand; use SPC\command\dev\ExtVerCommand; @@ -43,6 +44,8 @@ public function __construct() $this->addCommands( [ + // Craft command + new CraftCommand(), // Common commands new BuildPHPCommand(), new BuildLibsCommand(), diff --git a/src/SPC/command/BaseCommand.php b/src/SPC/command/BaseCommand.php index a1610cae7..c04b3fa84 100644 --- a/src/SPC/command/BaseCommand.php +++ b/src/SPC/command/BaseCommand.php @@ -9,6 +9,7 @@ use Psr\Log\LogLevel; use SPC\ConsoleApplication; use SPC\exception\ExceptionHandler; +use SPC\exception\ValidationException; use SPC\exception\WrongUsageException; use SPC\util\GlobalEnvManager; use Symfony\Component\Console\Command\Command; @@ -58,12 +59,12 @@ public function initialize(InputInterface $input, OutputInterface $output): void }); $version = ConsoleApplication::VERSION; if (!$this->no_motd) { - echo " _ _ _ _ - ___| |_ __ _| |_(_) ___ _ __ | |__ _ __ -/ __| __/ _` | __| |/ __|____| '_ \\| '_ \\| '_ \\ + echo " _ _ _ _ + ___| |_ __ _| |_(_) ___ _ __ | |__ _ __ +/ __| __/ _` | __| |/ __|____| '_ \\| '_ \\| '_ \\ \\__ \\ || (_| | |_| | (_|_____| |_) | | | | |_) | |___/\\__\\__,_|\\__|_|\\___| | .__/|_| |_| .__/ v{$version} - |_| |_| + |_| |_| "; } } @@ -104,7 +105,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int // show raw argv list for logger()->debug logger()->debug('argv: ' . implode(' ', $_SERVER['argv'])); return $this->handle(); - } catch (WrongUsageException $e) { + } catch (ValidationException|WrongUsageException $e) { $msg = explode("\n", $e->getMessage()); foreach ($msg as $v) { logger()->error($v); diff --git a/src/SPC/command/CraftCommand.php b/src/SPC/command/CraftCommand.php new file mode 100644 index 000000000..7cfc15c10 --- /dev/null +++ b/src/SPC/command/CraftCommand.php @@ -0,0 +1,167 @@ +addArgument('craft', null, 'Path to craft.yml file', WORKING_DIR . '/craft.yml'); + } + + public function handle(): int + { + $craft_file = $this->getArgument('craft'); + // Check if the craft.yml file exists + if (!file_exists($craft_file)) { + $this->output->writeln('craft.yml not found, please create one!'); + return static::FAILURE; + } + + // Check if the craft.yml file is valid + try { + $craft = ConfigValidator::validateAndParseCraftFile($craft_file, $this); + if ($craft['debug']) { + $this->input->setOption('debug', true); + } + } catch (ValidationException $e) { + $this->output->writeln('craft.yml parse error: ' . $e->getMessage() . ''); + return static::FAILURE; + } + + // Craft!!! + $this->output->writeln('Crafting...'); + + // apply env + if (isset($craft['extra-env'])) { + $env = $craft['extra-env']; + foreach ($env as $key => $val) { + f_putenv("{$key}={$val}"); + } + } + + $extensions = implode(',', $craft['extensions']); + $libs = implode(',', $craft['libs']); + + // init log + if (file_exists(WORKING_DIR . '/craft.log')) { + unlink(WORKING_DIR . '/craft.log'); + } + + // craft doctor + if ($craft['craft-options']['doctor']) { + $retcode = $this->runCommand('doctor', '--auto-fix'); + if ($retcode !== 0) { + $this->output->writeln('craft doctor failed'); + $this->log("craft doctor failed with code: {$retcode}", true); + return static::FAILURE; + } + } + // craft download + if ($craft['craft-options']['download']) { + $args = ["--for-extensions={$extensions}"]; + if ($craft['libs'] !== []) { + $args[] = "--for-libs={$libs}"; + } + if (isset($craft['php-version'])) { + $args[] = '--with-php=' . $craft['php-version']; + if (!array_key_exists('ignore-cache-sources', $craft['download-options']) || $craft['download-options']['ignore-cache-sources'] === false) { + $craft['download-options']['ignore-cache-sources'] = 'php-src'; + } elseif ($craft['download-options']['ignore-cache-sources'] !== null) { + $craft['download-options']['ignore-cache-sources'] .= ',php-src'; + } + } + $download_options = $craft['download-options']; + foreach ($download_options as $option => $val) { + if (is_bool($val) && $val || is_null($val)) { + $args[] = "--{$option}"; + } elseif (is_string($val)) { + $args[] = "--{$option}={$val}"; + } elseif (is_array($val)) { + foreach ($val as $v) { + if (is_string($v)) { + $args[] = "--{$option}={$v}"; + } + } + } + } + $retcode = $this->runCommand('download', ...$args); + if ($retcode !== 0) { + $this->output->writeln('craft download failed'); + $this->log('craft download failed with code: ' . $retcode, true); + return static::FAILURE; + } + } + + // craft build + if ($craft['craft-options']['build']) { + $args = [$extensions, "--with-libs={$libs}", ...array_map(fn ($x) => "--build-{$x}", $craft['sapi'])]; + $retcode = $this->runCommand('build', ...$args); + if ($retcode !== 0) { + $this->output->writeln('craft build failed'); + $this->log('craft build failed with code: ' . $retcode, true); + return static::FAILURE; + } + } + + return 0; + } + + public function processLogCallback($type, $buffer): void + { + if ($type === Process::ERR) { + fwrite(STDERR, $buffer); + } else { + fwrite(STDOUT, $buffer); + $this->log($buffer); + } + } + + private function log(string $log, bool $master_log = false): void + { + if ($master_log) { + $log = "\n[static-php-cli]> " . $log . "\n\n"; + } else { + $log = preg_replace('/\x1b\[[0-9;]*m/', '', $log); + } + file_put_contents('craft.log', $log, FILE_APPEND); + } + + private function runCommand(string $cmd, ...$args): int + { + global $argv; + if ($this->getOption('debug')) { + array_unshift($args, '--debug'); + } + $prefix = PHP_SAPI === 'cli' ? [PHP_BINARY, $argv[0]] : [$argv[0]]; + + $process = new Process([...$prefix, $cmd, '--no-motd', ...$args], timeout: null); + $this->log("Running: {$process->getCommandLine()}", true); + + if (PHP_OS_FAMILY === 'Windows') { + sapi_windows_set_ctrl_handler(function () use ($process) { + if ($process->isRunning()) { + $process->signal(-1073741510); + } + }); + } elseif (extension_loaded('pcntl')) { + pcntl_signal(SIGINT, function () use ($process) { + /* @noinspection PhpComposerExtensionStubsInspection */ + $process->signal(SIGINT); + }); + } else { + logger()->debug('You have not enabled `pcntl` extension, cannot prevent download file corruption when Ctrl+C'); + } + // $process->setTty(true); + $process->run([$this, 'processLogCallback']); + return $process->getExitCode(); + } +} diff --git a/src/SPC/util/ConfigValidator.php b/src/SPC/util/ConfigValidator.php index c992d90ff..d455dda50 100644 --- a/src/SPC/util/ConfigValidator.php +++ b/src/SPC/util/ConfigValidator.php @@ -5,6 +5,9 @@ namespace SPC\util; use SPC\exception\ValidationException; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Yaml; class ConfigValidator { @@ -110,4 +113,123 @@ public static function validatePkgs(mixed $data): void { is_array($data) || throw new ValidationException('pkg.json is broken'); } + + /** + * @param mixed $craft_file craft.yml path + * @param Command $command craft command instance + * @return array{ + * php-version?: string, + * extensions: array, + * libs?: array, + * sapi: array, + * debug?: bool, + * clean-build?: bool, + * build-options?: array, + * download-options?: array, + * extra-env?: array, + * craft-options?: array{ + * doctor?: bool, + * download?: bool, + * build?: bool + * } + * } + * @throws ValidationException + */ + public static function validateAndParseCraftFile(mixed $craft_file, Command $command): array + { + $build_options = $command->getApplication()->find('build')->getDefinition()->getOptions(); + $download_options = $command->getApplication()->find('download')->getDefinition()->getOptions(); + + try { + $craft = Yaml::parse(file_get_contents($craft_file)); + } catch (ParseException $e) { + throw new ValidationException('Craft file is broken: ' . $e->getMessage()); + } + if (!is_assoc_array($craft)) { + throw new ValidationException('Craft file is broken'); + } + // check php-version + if (isset($craft['php-version'])) { + // validdate version, accept 8.x, 7.x, 8.x.x, 7.x.x, 8, 7 + $version = strval($craft['php-version']); + if (!preg_match('/^(\d+)(\.\d+)?(\.\d+)?$/', $version, $matches)) { + throw new ValidationException('Craft file php-version is invalid'); + } + } + // check extensions + if (!isset($craft['extensions'])) { + throw new ValidationException('Craft file must have extensions'); + } + if (is_string($craft['extensions'])) { + $craft['extensions'] = array_filter(array_map(fn ($x) => trim($x), explode(',', $craft['extensions']))); + } + // check libs + if (isset($craft['libs']) && is_string($craft['libs'])) { + $craft['libs'] = array_filter(array_map(fn ($x) => trim($x), explode(',', $craft['libs']))); + } elseif (!isset($craft['libs'])) { + $craft['libs'] = []; + } + // check sapi + if (!isset($craft['sapi'])) { + throw new ValidationException('Craft file must have sapi'); + } + if (is_string($craft['sapi'])) { + $craft['sapi'] = array_filter(array_map(fn ($x) => trim($x), explode(',', $craft['sapi']))); + } + // debug as boolean + if (isset($craft['debug'])) { + $craft['debug'] = filter_var($craft['debug'], FILTER_VALIDATE_BOOLEAN); + } else { + $craft['debug'] = false; + } + // check clean-build + $craft['clean-build'] ??= false; + // check build-options + if (isset($craft['build-options'])) { + if (!is_assoc_array($craft['build-options'])) { + throw new ValidationException('Craft file build-options must be an object'); + } + foreach ($craft['build-options'] as $key => $value) { + if (!isset($build_options[$key])) { + throw new ValidationException("Craft file build-options {$key} is invalid"); + } + // check an array + if ($build_options[$key]->isArray() && !is_array($value)) { + throw new ValidationException("Craft file build-options {$key} must be an array"); + } + } + } else { + $craft['build-options'] = []; + } + // check download options + if (isset($craft['download-options'])) { + if (!is_assoc_array($craft['download-options'])) { + throw new ValidationException('Craft file download-options must be an object'); + } + foreach ($craft['download-options'] as $key => $value) { + if (!isset($download_options[$key])) { + throw new ValidationException("Craft file download-options {$key} is invalid"); + } + // check an array + if ($download_options[$key]->isArray() && !is_array($value)) { + throw new ValidationException("Craft file download-options {$key} must be an array"); + } + } + } else { + $craft['download-options'] = []; + } + // check extra-env + if (isset($craft['extra-env'])) { + if (!is_assoc_array($craft['extra-env'])) { + throw new ValidationException('Craft file extra-env must be an object'); + } + } else { + $craft['extra-env'] = []; + } + // check craft-options + $craft['craft-options']['doctor'] ??= true; + $craft['craft-options']['download'] ??= true; + $craft['craft-options']['build'] ??= true; + return $craft; + } } diff --git a/src/globals/defines.php b/src/globals/defines.php index 699cecc1b..f3d290ba6 100644 --- a/src/globals/defines.php +++ b/src/globals/defines.php @@ -85,4 +85,4 @@ const AUTOCONF_ALL = 15; ConsoleLogger::$date_format = 'H:i:s'; -ConsoleLogger::$format = '[%date%] [I] %body%'; +ConsoleLogger::$format = '[%date%] [%level_short%] %body%';