diff --git a/README-en.md b/README-en.md deleted file mode 100755 index 505fd70a2..000000000 --- a/README-en.md +++ /dev/null @@ -1,3 +0,0 @@ -# static-php-cli - -English README has been moved to [README.md](README.md). diff --git a/composer.json b/composer.json index fb17f9a0d..fbdbc94b8 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "captainhook/hook-installer": "^1.0", "friendsofphp/php-cs-fixer": "^3.60", "humbug/box": "^4.5.0 || ^4.6.0", - "phpstan/phpstan": "^1.10", + "phpstan/phpstan": "^2.1", "phpunit/phpunit": "^10.3 || ^9.5" }, "autoload": { diff --git a/composer.lock b/composer.lock index a0538ce8e..40489a068 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": "edb3243ddaa8b05d8f6545266a146e93", + "content-hash": "f30595c9c60e55083112410cd1ffb203", "packages": [ { "name": "laravel/prompts", - "version": "v0.3.8", + "version": "v0.3.11", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "096748cdfb81988f60090bbb839ce3205ace0d35" + "reference": "dd2a2ed95acacbcccd32fd98dee4c946ae7a7217" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/096748cdfb81988f60090bbb839ce3205ace0d35", - "reference": "096748cdfb81988f60090bbb839ce3205ace0d35", + "url": "https://api.github.com/repos/laravel/prompts/zipball/dd2a2ed95acacbcccd32fd98dee4c946ae7a7217", + "reference": "dd2a2ed95acacbcccd32fd98dee4c946ae7a7217", "shasum": "" }, "require": { @@ -61,22 +61,22 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.3.8" + "source": "https://github.com/laravel/prompts/tree/v0.3.11" }, - "time": "2025-11-21T20:52:52+00:00" + "time": "2026-01-27T02:55:06+00:00" }, { "name": "laravel/serializable-closure", - "version": "v2.0.7", + "version": "v2.0.8", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd" + "reference": "7581a4407012f5f53365e11bafc520fd7f36bc9b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/cb291e4c998ac50637c7eeb58189c14f5de5b9dd", - "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/7581a4407012f5f53365e11bafc520fd7f36bc9b", + "reference": "7581a4407012f5f53365e11bafc520fd7f36bc9b", "shasum": "" }, "require": { @@ -124,7 +124,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2025-11-21T20:52:36+00:00" + "time": "2026-01-08T16:22:46+00:00" }, { "name": "nette/php-generator", @@ -200,16 +200,16 @@ }, { "name": "nette/utils", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "fa1f0b8261ed150447979eb22e373b7b7ad5a8e0" + "reference": "c99059c0315591f1a0db7ad6002000288ab8dc72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/fa1f0b8261ed150447979eb22e373b7b7ad5a8e0", - "reference": "fa1f0b8261ed150447979eb22e373b7b7ad5a8e0", + "url": "https://api.github.com/repos/nette/utils/zipball/c99059c0315591f1a0db7ad6002000288ab8dc72", + "reference": "c99059c0315591f1a0db7ad6002000288ab8dc72", "shasum": "" }, "require": { @@ -283,9 +283,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.1.0" + "source": "https://github.com/nette/utils/tree/v4.1.1" }, - "time": "2025-12-01T17:49:23+00:00" + "time": "2025-12-22T12:14:32+00:00" }, { "name": "php-di/invoker", @@ -520,16 +520,16 @@ }, { "name": "symfony/console", - "version": "v7.4.0", + "version": "v7.4.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8" + "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8", - "reference": "0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8", + "url": "https://api.github.com/repos/symfony/console/zipball/41e38717ac1dd7a46b6bda7d6a82af2d98a78894", + "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894", "shasum": "" }, "require": { @@ -594,7 +594,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.0" + "source": "https://github.com/symfony/console/tree/v7.4.4" }, "funding": [ { @@ -614,7 +614,7 @@ "type": "tidelift" } ], - "time": "2025-11-27T13:27:24+00:00" + "time": "2026-01-13T11:36:38+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1020,16 +1020,16 @@ }, { "name": "symfony/process", - "version": "v7.4.0", + "version": "v7.4.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "7ca8dc2d0dcf4882658313aba8be5d9fd01026c8" + "reference": "608476f4604102976d687c483ac63a79ba18cc97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/7ca8dc2d0dcf4882658313aba8be5d9fd01026c8", - "reference": "7ca8dc2d0dcf4882658313aba8be5d9fd01026c8", + "url": "https://api.github.com/repos/symfony/process/zipball/608476f4604102976d687c483ac63a79ba18cc97", + "reference": "608476f4604102976d687c483ac63a79ba18cc97", "shasum": "" }, "require": { @@ -1061,7 +1061,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.4.0" + "source": "https://github.com/symfony/process/tree/v7.4.5" }, "funding": [ { @@ -1081,7 +1081,7 @@ "type": "tidelift" } ], - "time": "2025-10-16T11:21:06+00:00" + "time": "2026-01-26T15:07:59+00:00" }, { "name": "symfony/service-contracts", @@ -1172,16 +1172,16 @@ }, { "name": "symfony/string", - "version": "v8.0.0", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f929eccf09531078c243df72398560e32fa4cf4f" + "reference": "758b372d6882506821ed666032e43020c4f57194" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f929eccf09531078c243df72398560e32fa4cf4f", - "reference": "f929eccf09531078c243df72398560e32fa4cf4f", + "url": "https://api.github.com/repos/symfony/string/zipball/758b372d6882506821ed666032e43020c4f57194", + "reference": "758b372d6882506821ed666032e43020c4f57194", "shasum": "" }, "require": { @@ -1238,7 +1238,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.0" + "source": "https://github.com/symfony/string/tree/v8.0.4" }, "funding": [ { @@ -1258,20 +1258,20 @@ "type": "tidelift" } ], - "time": "2025-09-11T14:37:55+00:00" + "time": "2026-01-12T12:37:40+00:00" }, { "name": "symfony/yaml", - "version": "v7.4.0", + "version": "v7.4.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810" + "reference": "24dd4de28d2e3988b311751ac49e684d783e2345" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/6c84a4b55aee4cd02034d1c528e83f69ddf63810", - "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810", + "url": "https://api.github.com/repos/symfony/yaml/zipball/24dd4de28d2e3988b311751ac49e684d783e2345", + "reference": "24dd4de28d2e3988b311751ac49e684d783e2345", "shasum": "" }, "require": { @@ -1314,7 +1314,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.4.0" + "source": "https://github.com/symfony/yaml/tree/v7.4.1" }, "funding": [ { @@ -1334,7 +1334,7 @@ "type": "tidelift" } ], - "time": "2025-11-16T10:14:42+00:00" + "time": "2025-12-04T18:11:45+00:00" }, { "name": "zhamao/logger", @@ -1719,16 +1719,16 @@ }, { "name": "amphp/parallel", - "version": "v2.3.2", + "version": "v2.3.3", "source": { "type": "git", "url": "https://github.com/amphp/parallel.git", - "reference": "321b45ae771d9c33a068186b24117e3cd1c48dce" + "reference": "296b521137a54d3a02425b464e5aee4c93db2c60" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/parallel/zipball/321b45ae771d9c33a068186b24117e3cd1c48dce", - "reference": "321b45ae771d9c33a068186b24117e3cd1c48dce", + "url": "https://api.github.com/repos/amphp/parallel/zipball/296b521137a54d3a02425b464e5aee4c93db2c60", + "reference": "296b521137a54d3a02425b464e5aee4c93db2c60", "shasum": "" }, "require": { @@ -1791,7 +1791,7 @@ ], "support": { "issues": "https://github.com/amphp/parallel/issues", - "source": "https://github.com/amphp/parallel/tree/v2.3.2" + "source": "https://github.com/amphp/parallel/tree/v2.3.3" }, "funding": [ { @@ -1799,7 +1799,7 @@ "type": "github" } ], - "time": "2025-08-27T21:55:40+00:00" + "time": "2025-11-15T06:23:42+00:00" }, { "name": "amphp/parser", @@ -2217,7 +2217,7 @@ }, { "name": "captainhook/captainhook-phar", - "version": "5.27.3", + "version": "5.27.5", "source": { "type": "git", "url": "https://github.com/captainhook-git/captainhook-phar.git", @@ -2271,7 +2271,7 @@ ], "support": { "issues": "https://github.com/captainhook-git/captainhook/issues", - "source": "https://github.com/captainhook-git/captainhook-phar/tree/5.27.3" + "source": "https://github.com/captainhook-git/captainhook-phar/tree/5.27.5" }, "funding": [ { @@ -2968,16 +2968,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.91.0", + "version": "v3.93.1", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "c4a25f20390337789c26b693ae46faa125040352" + "reference": "b3546ab487c0762c39f308dc1ec0ea2c461fc21a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/c4a25f20390337789c26b693ae46faa125040352", - "reference": "c4a25f20390337789c26b693ae46faa125040352", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/b3546ab487c0762c39f308dc1ec0ea2c461fc21a", + "reference": "b3546ab487c0762c39f308dc1ec0ea2c461fc21a", "shasum": "" }, "require": { @@ -3009,16 +3009,17 @@ }, "require-dev": { "facile-it/paraunit": "^1.3.1 || ^2.7", - "infection/infection": "^0.31.0", - "justinrainbow/json-schema": "^6.5", - "keradus/cli-executor": "^2.2", + "infection/infection": "^0.32", + "justinrainbow/json-schema": "^6.6", + "keradus/cli-executor": "^2.3", "mikey179/vfsstream": "^1.6.12", "php-coveralls/php-coveralls": "^2.9", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", - "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2 || ^8.0", - "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2 || ^8.0" + "phpunit/phpunit": "^9.6.31 || ^10.5.60 || ^11.5.48", + "symfony/polyfill-php85": "^1.33", + "symfony/var-dumper": "^5.4.48 || ^6.4.26 || ^7.4.0 || ^8.0", + "symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -3033,7 +3034,7 @@ "PhpCsFixer\\": "src/" }, "exclude-from-classmap": [ - "src/Fixer/Internal/*" + "src/**/Internal/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3059,7 +3060,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.91.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.93.1" }, "funding": [ { @@ -3067,7 +3068,7 @@ "type": "github" } ], - "time": "2025-11-28T22:07:42+00:00" + "time": "2026-01-28T23:50:50+00:00" }, { "name": "humbug/box", @@ -3316,21 +3317,21 @@ }, { "name": "justinrainbow/json-schema", - "version": "6.6.2", + "version": "6.6.4", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "3c25fe750c1599716ef26aa997f7c026cee8c4b7" + "reference": "2eeb75d21cf73211335888e7f5e6fd7440723ec7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/3c25fe750c1599716ef26aa997f7c026cee8c4b7", - "reference": "3c25fe750c1599716ef26aa997f7c026cee8c4b7", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/2eeb75d21cf73211335888e7f5e6fd7440723ec7", + "reference": "2eeb75d21cf73211335888e7f5e6fd7440723ec7", "shasum": "" }, "require": { "ext-json": "*", - "marc-mabe/php-enum": "^4.0", + "marc-mabe/php-enum": "^4.4", "php": "^7.2 || ^8.0" }, "require-dev": { @@ -3385,9 +3386,9 @@ ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/6.6.2" + "source": "https://github.com/jsonrainbow/json-schema/tree/6.6.4" }, - "time": "2025-11-28T15:24:03+00:00" + "time": "2025-12-19T15:01:32+00:00" }, { "name": "kelunik/certificate", @@ -3449,20 +3450,20 @@ }, { "name": "league/uri", - "version": "7.6.0", + "version": "7.8.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "f625804987a0a9112d954f9209d91fec52182344" + "reference": "4436c6ec8d458e4244448b069cc572d088230b76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/f625804987a0a9112d954f9209d91fec52182344", - "reference": "f625804987a0a9112d954f9209d91fec52182344", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/4436c6ec8d458e4244448b069cc572d088230b76", + "reference": "4436c6ec8d458e4244448b069cc572d088230b76", "shasum": "" }, "require": { - "league/uri-interfaces": "^7.6", + "league/uri-interfaces": "^7.8", "php": "^8.1", "psr/http-factory": "^1" }, @@ -3476,11 +3477,11 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "ext-uri": "to use the PHP native URI class", - "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", - "league/uri-components": "Needed to easily manipulate URI objects components", - "league/uri-polyfill": "Needed to backport the PHP URI extension for older versions of PHP", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-components": "to provide additional tools to manipulate URI objects components", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", "php-64bit": "to improve IPV4 host parsing", - "rowbot/url": "to handle WHATWG URL", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -3535,7 +3536,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.6.0" + "source": "https://github.com/thephpleague/uri/tree/7.8.0" }, "funding": [ { @@ -3543,20 +3544,20 @@ "type": "github" } ], - "time": "2025-11-18T12:17:23+00:00" + "time": "2026-01-14T17:24:56+00:00" }, { "name": "league/uri-interfaces", - "version": "7.6.0", + "version": "7.8.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "ccbfb51c0445298e7e0b7f4481b942f589665368" + "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/ccbfb51c0445298e7e0b7f4481b942f589665368", - "reference": "ccbfb51c0445298e7e0b7f4481b942f589665368", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/c5c5cd056110fc8afaba29fa6b72a43ced42acd4", + "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4", "shasum": "" }, "require": { @@ -3569,7 +3570,7 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "php-64bit": "to improve IPV4 host parsing", - "rowbot/url": "to handle WHATWG URL", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -3619,7 +3620,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.6.0" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.0" }, "funding": [ { @@ -3627,7 +3628,7 @@ "type": "github" } ], - "time": "2025-11-18T12:17:23+00:00" + "time": "2026-01-15T06:54:53+00:00" }, { "name": "marc-mabe/php-enum", @@ -3816,16 +3817,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.2", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -3868,9 +3869,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2025-10-21T19:32:17+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "phar-io/composer-distributor", @@ -4259,16 +4260,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.6.5", + "version": "5.6.6", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "90614c73d3800e187615e2dd236ad0e2a01bf761" + "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/90614c73d3800e187615e2dd236ad0e2a01bf761", - "reference": "90614c73d3800e187615e2dd236ad0e2a01bf761", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/5cee1d3dfc2d2aa6599834520911d246f656bcb8", + "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8", "shasum": "" }, "require": { @@ -4278,7 +4279,7 @@ "phpdocumentor/reflection-common": "^2.2", "phpdocumentor/type-resolver": "^1.7", "phpstan/phpdoc-parser": "^1.7|^2.0", - "webmozart/assert": "^1.9.1" + "webmozart/assert": "^1.9.1 || ^2" }, "require-dev": { "mockery/mockery": "~1.3.5 || ~1.6.0", @@ -4317,9 +4318,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.5" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.6" }, - "time": "2025-11-27T19:50:05+00:00" + "time": "2025-12-22T21:13:58+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -4381,16 +4382,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "2.3.0", + "version": "2.3.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", "shasum": "" }, "require": { @@ -4422,21 +4423,21 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" }, - "time": "2025-08-30T15:50:23+00:00" + "time": "2026-01-25T14:56:51+00:00" }, { "name": "phpstan/phpstan", - "version": "1.12.32", + "version": "2.1.38", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/2770dcdf5078d0b0d53f94317e06affe88419aa8", - "reference": "2770dcdf5078d0b0d53f94317e06affe88419aa8", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dfaf1f530e1663aa167bc3e52197adb221582629", + "reference": "dfaf1f530e1663aa167bc3e52197adb221582629", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -4477,7 +4478,7 @@ "type": "github" } ], - "time": "2025-09-30T10:16:31+00:00" + "time": "2026-01-30T17:12:46+00:00" }, { "name": "phpunit/php-code-coverage", @@ -4802,16 +4803,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.58", + "version": "10.5.63", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e24fb46da450d8e6a5788670513c1af1424f16ca" + "reference": "33198268dad71e926626b618f3ec3966661e4d90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e24fb46da450d8e6a5788670513c1af1424f16ca", - "reference": "e24fb46da450d8e6a5788670513c1af1424f16ca", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/33198268dad71e926626b618f3ec3966661e4d90", + "reference": "33198268dad71e926626b618f3ec3966661e4d90", "shasum": "" }, "require": { @@ -4832,7 +4833,7 @@ "phpunit/php-timer": "^6.0.0", "sebastian/cli-parser": "^2.0.1", "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.4", + "sebastian/comparator": "^5.0.5", "sebastian/diff": "^5.1.1", "sebastian/environment": "^6.1.0", "sebastian/exporter": "^5.1.4", @@ -4883,7 +4884,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.58" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.63" }, "funding": [ { @@ -4907,7 +4908,7 @@ "type": "tidelift" } ], - "time": "2025-09-28T12:04:46+00:00" + "time": "2026-01-27T05:48:37+00:00" }, { "name": "psr/event-dispatcher", @@ -5141,16 +5142,16 @@ }, { "name": "react/child-process", - "version": "v0.6.6", + "version": "v0.6.7", "source": { "type": "git", "url": "https://github.com/reactphp/child-process.git", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/970f0e71945556422ee4570ccbabaedc3cf04ad3", + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3", "shasum": "" }, "require": { @@ -5204,7 +5205,7 @@ ], "support": { "issues": "https://github.com/reactphp/child-process/issues", - "source": "https://github.com/reactphp/child-process/tree/v0.6.6" + "source": "https://github.com/reactphp/child-process/tree/v0.6.7" }, "funding": [ { @@ -5212,7 +5213,7 @@ "type": "open_collective" } ], - "time": "2025-01-01T16:37:48+00:00" + "time": "2025-12-23T15:25:20+00:00" }, { "name": "react/dns", @@ -5835,16 +5836,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.4", + "version": "5.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e" + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e8e53097718d2b53cfb2aa859b06a41abf58c62e", - "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55dfef806eb7dfeb6e7a6935601fef866f8ca48d", + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d", "shasum": "" }, "require": { @@ -5900,7 +5901,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.4" + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.5" }, "funding": [ { @@ -5920,7 +5921,7 @@ "type": "tidelift" } ], - "time": "2025-09-07T05:25:07+00:00" + "time": "2026-01-24T09:25:16+00:00" }, { "name": "sebastian/complexity", @@ -6732,16 +6733,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v8.0.0", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "573f95783a2ec6e38752979db139f09fec033f03" + "reference": "99301401da182b6cfaa4700dbe9987bb75474b47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/573f95783a2ec6e38752979db139f09fec033f03", - "reference": "573f95783a2ec6e38752979db139f09fec033f03", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/99301401da182b6cfaa4700dbe9987bb75474b47", + "reference": "99301401da182b6cfaa4700dbe9987bb75474b47", "shasum": "" }, "require": { @@ -6793,7 +6794,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.4" }, "funding": [ { @@ -6813,7 +6814,7 @@ "type": "tidelift" } ], - "time": "2025-10-30T14:17:19+00:00" + "time": "2026-01-05T11:45:55+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -6963,16 +6964,16 @@ }, { "name": "symfony/finder", - "version": "v7.4.0", + "version": "v7.4.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "340b9ed7320570f319028a2cbec46d40535e94bd" + "reference": "ad4daa7c38668dcb031e63bc99ea9bd42196a2cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/340b9ed7320570f319028a2cbec46d40535e94bd", - "reference": "340b9ed7320570f319028a2cbec46d40535e94bd", + "url": "https://api.github.com/repos/symfony/finder/zipball/ad4daa7c38668dcb031e63bc99ea9bd42196a2cb", + "reference": "ad4daa7c38668dcb031e63bc99ea9bd42196a2cb", "shasum": "" }, "require": { @@ -7007,7 +7008,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.0" + "source": "https://github.com/symfony/finder/tree/v7.4.5" }, "funding": [ { @@ -7027,7 +7028,7 @@ "type": "tidelift" } ], - "time": "2025-11-05T05:42:40+00:00" + "time": "2026-01-26T15:07:59+00:00" }, { "name": "symfony/options-resolver", @@ -7332,16 +7333,16 @@ }, { "name": "symfony/var-dumper", - "version": "v7.4.0", + "version": "v7.4.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "41fd6c4ae28c38b294b42af6db61446594a0dece" + "reference": "0e4769b46a0c3c62390d124635ce59f66874b282" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/41fd6c4ae28c38b294b42af6db61446594a0dece", - "reference": "41fd6c4ae28c38b294b42af6db61446594a0dece", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0e4769b46a0c3c62390d124635ce59f66874b282", + "reference": "0e4769b46a0c3c62390d124635ce59f66874b282", "shasum": "" }, "require": { @@ -7395,7 +7396,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.4.0" + "source": "https://github.com/symfony/var-dumper/tree/v7.4.4" }, "funding": [ { @@ -7415,7 +7416,7 @@ "type": "tidelift" } ], - "time": "2025-10-27T20:36:44+00:00" + "time": "2026-01-01T22:13:48+00:00" }, { "name": "thecodingmachine/safe", diff --git a/config/env.ini b/config/env.ini index bd2f3d4f6..c77f6bcd0 100644 --- a/config/env.ini +++ b/config/env.ini @@ -82,8 +82,10 @@ SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime ; - musl-native: used for alpine linux, can build `musl` and `musl -dynamic` target. ; - gnu-native: used for general linux distros, can build gnu target for the installed glibc version only. -; LEGACY option to specify the target -SPC_LIBC=musl +; option to specify the target, superceded by SPC_TARGET if set +; SPC_LIBC=musl +; uncomment to link libc dynamically on musl +; SPC_MUSL_DYNAMIC=true ; Recommended: specify your target here. Zig toolchain will be used. ; examples: @@ -92,13 +94,13 @@ SPC_LIBC=musl ; `native-native` - links against system libc dynamically ; `native-native-musl` - links against musl libc statically ; `native-native-musl -dynamic` - links against musl libc dynamically -; SPC_TARGET= +SPC_TARGET=native-native-musl -; compiler environments -CC=${SPC_LINUX_DEFAULT_CC} -CXX=${SPC_LINUX_DEFAULT_CXX} -AR=${SPC_LINUX_DEFAULT_AR} -LD=${SPC_LINUX_DEFAULT_LD} +; compiler environments (default value is defined by selected toolchain) +CC=${SPC_DEFAULT_CC} +CXX=${SPC_DEFAULT_CXX} +AR=${SPC_DEFAULT_AR} +LD=${SPC_DEFAULT_LD} ; default compiler flags, used in CMake toolchain file, openssl and pkg-config build SPC_DEFAULT_C_FLAGS="-fPIC -Os" SPC_DEFAULT_CXX_FLAGS="-fPIC -Os" @@ -130,11 +132,11 @@ OPENSSLDIR="" ; build target: macho or macho (possibly we could support macho-universal in the future) ; Currently we do not support universal and cross-compilation for macOS. SPC_TARGET=native-macos -; compiler environments -CC=clang -CXX=clang++ -AR=ar -LD=ld +; compiler environments (default value is defined by selected toolchain) +CC=${SPC_DEFAULT_CC} +CXX=${SPC_DEFAULT_CXX} +AR=${SPC_DEFAULT_AR} +LD=${SPC_DEFAULT_LD} ; default compiler flags, used in CMake toolchain file, openssl and pkg-config build SPC_DEFAULT_C_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" diff --git a/config/pkg.ext.json b/config/pkg.ext.json index b1e819eb3..d1ca0b3f0 100644 --- a/config/pkg.ext.json +++ b/config/pkg.ext.json @@ -1,5 +1,6 @@ { "ext-amqp": { + "type": "php-extension", "artifact": "amqp", "depends": [ "librabbitmq" @@ -16,29 +17,29 @@ "BSD": "wip" }, "arg-type": "custom" - }, - "type": "php-extension" + } }, "ext-apcu": { + "type": "php-extension", "artifact": "apcu", "license": { "type": "file", "path": "LICENSE" - }, - "type": "php-extension" + } }, "ext-ast": { + "type": "php-extension", "artifact": "ast", "license": { "type": "file", "path": "LICENSE" - }, - "type": "php-extension" + } }, "ext-bcmath": { "type": "php-extension" }, "ext-brotli": { + "type": "php-extension", "artifact": "ext-brotli", "depends": [ "brotli" @@ -49,18 +50,17 @@ }, "php-extension": { "arg-type": "enable" - }, - "type": "php-extension" + } }, "ext-bz2": { + "type": "php-extension", "depends": [ "bzip2" ], "php-extension": { "arg-type@windows": "with", "arg-type": "with-path" - }, - "type": "php-extension" + } }, "ext-calendar": { "type": "php-extension" @@ -69,6 +69,7 @@ "type": "php-extension" }, "ext-curl": { + "type": "php-extension", "depends": [ "curl" ], @@ -79,19 +80,19 @@ "php-extension": { "arg-type": "with", "notes": true - }, - "type": "php-extension" + } }, "ext-dba": { - "php-extension": { - "arg-type": "custom" - }, + "type": "php-extension", "suggests": [ "qdbm" ], - "type": "php-extension" + "php-extension": { + "arg-type": "custom" + } }, "ext-dio": { + "type": "php-extension", "artifact": "dio", "license": { "type": "file", @@ -101,10 +102,10 @@ "support": { "BSD": "wip" } - }, - "type": "php-extension" + } }, "ext-dom": { + "type": "php-extension", "depends": [ "libxml2", "zlib" @@ -118,18 +119,18 @@ }, "arg-type": "custom", "arg-type@windows": "with" - }, - "type": "php-extension" + } }, "ext-ds": { + "type": "php-extension", "artifact": "ext-ds", "license": { "type": "file", "path": "LICENSE" - }, - "type": "php-extension" + } }, "ext-enchant": { + "type": "php-extension", "php-extension": { "support": { "Windows": "wip", @@ -137,10 +138,10 @@ "Darwin": "wip", "Linux": "wip" } - }, - "type": "php-extension" + } }, "ext-ev": { + "type": "php-extension", "artifact": "ev", "depends": [ "ext-sockets" @@ -151,15 +152,18 @@ }, "php-extension": { "arg-type@windows": "with" - }, - "type": "php-extension" + } }, "ext-event": { + "type": "php-extension", "artifact": "ext-event", "depends": [ "libevent", "ext-openssl" ], + "suggests": [ + "ext-sockets" + ], "license": { "type": "file", "path": "LICENSE" @@ -171,16 +175,13 @@ }, "arg-type": "custom", "notes": true - }, - "suggests": [ - "ext-sockets" - ], - "type": "php-extension" + } }, "ext-exif": { "type": "php-extension" }, "ext-ffi": { + "type": "php-extension", "depends": [ "libffi" ], @@ -194,8 +195,7 @@ }, "arg-type": "custom", "notes": true - }, - "type": "php-extension" + } }, "ext-fileinfo": { "type": "php-extension" @@ -204,17 +204,24 @@ "type": "php-extension" }, "ext-ftp": { + "type": "php-extension", "suggests": [ "openssl" - ], - "type": "php-extension" + ] }, "ext-gd": { + "type": "php-extension", "depends": [ "zlib", "libpng", "ext-zlib" ], + "suggests": [ + "libavif", + "libwebp", + "libjpeg", + "freetype" + ], "php-extension": { "support": { "BSD": "wip" @@ -222,16 +229,10 @@ "arg-type": "custom", "arg-type@windows": "with", "notes": true - }, - "suggests": [ - "libavif", - "libwebp", - "libjpeg", - "freetype" - ], - "type": "php-extension" + } }, "ext-gettext": { + "type": "php-extension", "depends": [ "gettext" ], @@ -241,10 +242,10 @@ "BSD": "wip" }, "arg-type": "with-path" - }, - "type": "php-extension" + } }, "ext-glfw": { + "type": "php-extension", "artifact": "ext-glfw", "depends": [ "glfw" @@ -262,10 +263,10 @@ }, "arg-type": "custom", "notes": true - }, - "type": "php-extension" + } }, "ext-gmp": { + "type": "php-extension", "depends": [ "gmp" ], @@ -275,10 +276,10 @@ "BSD": "wip" }, "arg-type": "with-path" - }, - "type": "php-extension" + } }, "ext-gmssl": { + "type": "php-extension", "artifact": "ext-gmssl", "depends": [ "gmssl" @@ -291,10 +292,10 @@ "support": { "BSD": "wip" } - }, - "type": "php-extension" + } }, "ext-grpc": { + "type": "php-extension", "artifact": "grpc", "depends": [ "grpc" @@ -310,10 +311,10 @@ "BSD": "wip" }, "arg-type": "enable-path" - }, - "type": "php-extension" + } }, "ext-iconv": { + "type": "php-extension", "depends": [ "libiconv" ], @@ -326,11 +327,15 @@ }, "arg-type": "with-path", "arg-type@windows": "with" - }, - "type": "php-extension" + } }, "ext-igbinary": { + "type": "php-extension", "artifact": "igbinary", + "suggests": [ + "ext-session", + "ext-apcu" + ], "license": { "type": "file", "path": "COPYING" @@ -339,14 +344,10 @@ "support": { "BSD": "wip" } - }, - "suggests": [ - "ext-session", - "ext-apcu" - ], - "type": "php-extension" + } }, "ext-imagick": { + "type": "php-extension", "artifact": "ext-imagick", "depends": [ "imagemagick" @@ -362,14 +363,17 @@ }, "arg-type": "custom", "notes": true - }, - "type": "php-extension" + } }, "ext-imap": { + "type": "php-extension", "artifact": "ext-imap", "depends": [ "imap" ], + "suggests": [ + "ext-openssl" + ], "license": { "type": "file", "path": [ @@ -383,13 +387,10 @@ }, "arg-type": "custom", "notes": true - }, - "suggests": [ - "ext-openssl" - ], - "type": "php-extension" + } }, "ext-inotify": { + "type": "php-extension", "artifact": "inotify", "license": { "type": "file", @@ -401,10 +402,10 @@ "BSD": "wip", "Darwin": "no" } - }, - "type": "php-extension" + } }, "ext-intl": { + "type": "php-extension", "depends": [ "icu" ], @@ -415,28 +416,28 @@ "support": { "BSD": "wip" } - }, - "type": "php-extension" + } }, "ext-ldap": { + "type": "php-extension", "depends": [ "ldap" ], + "suggests": [ + "gmp", + "libsodium", + "ext-openssl" + ], "php-extension": { "support": { "Windows": "wip", "BSD": "wip" }, "arg-type": "with-path" - }, - "suggests": [ - "gmp", - "libsodium", - "ext-openssl" - ], - "type": "php-extension" + } }, "ext-libxml": { + "type": "php-extension", "depends": [ "ext-xml" ], @@ -448,10 +449,10 @@ "build-shared": false, "build-static": true, "build-with-php": true - }, - "type": "php-extension" + } }, "ext-lz4": { + "type": "php-extension", "artifact": "ext-lz4", "depends": [ "liblz4" @@ -468,10 +469,10 @@ "BSD": "wip" }, "arg-type": "custom" - }, - "type": "php-extension" + } }, "ext-mbregex": { + "type": "php-extension", "depends": [ "onig", "ext-mbstring" @@ -480,16 +481,16 @@ "arg-type": "custom", "build-shared": false, "build-static": true - }, - "type": "php-extension" + } }, "ext-mbstring": { + "type": "php-extension", "php-extension": { "arg-type": "custom" - }, - "type": "php-extension" + } }, "ext-mcrypt": { + "type": "php-extension", "php-extension": { "support": { "Windows": "no", @@ -498,10 +499,10 @@ "Linux": "no" }, "notes": true - }, - "type": "php-extension" + } }, "ext-memcache": { + "type": "php-extension", "artifact": "ext-memcache", "depends": [ "ext-zlib", @@ -518,10 +519,10 @@ }, "arg-type": "custom", "build-with-php": true - }, - "type": "php-extension" + } }, "ext-memcached": { + "type": "php-extension", "artifact": "memcached", "depends": [ "libmemcached", @@ -529,6 +530,12 @@ "ext-session", "ext-zlib" ], + "suggests": [ + "zstd", + "ext-igbinary", + "ext-msgpack", + "ext-session" + ], "lang": "cpp", "license": { "type": "file", @@ -540,17 +547,17 @@ "BSD": "wip" }, "arg-type": "custom" - }, - "suggests": [ - "zstd", - "ext-igbinary", - "ext-msgpack", - "ext-session" - ], - "type": "php-extension" + } }, "ext-mongodb": { + "type": "php-extension", "artifact": "mongodb", + "suggests": [ + "icu", + "openssl", + "zstd", + "zlib" + ], "frameworks": [ "CoreFoundation", "Security" @@ -565,16 +572,10 @@ "Windows": "wip" }, "arg-type": "custom" - }, - "suggests": [ - "icu", - "openssl", - "zstd", - "zlib" - ], - "type": "php-extension" + } }, "ext-msgpack": { + "type": "php-extension", "artifact": "msgpack", "depends": [ "ext-session" @@ -589,30 +590,30 @@ }, "arg-type@windows": "enable", "arg-type": "with" - }, - "type": "php-extension" + } }, "ext-mysqli": { + "type": "php-extension", "depends": [ "ext-mysqlnd" ], "php-extension": { "arg-type": "with", "build-with-php": true - }, - "type": "php-extension" + } }, "ext-mysqlnd": { + "type": "php-extension", "depends": [ "zlib" ], "php-extension": { "arg-type@windows": "with", "build-with-php": true - }, - "type": "php-extension" + } }, "ext-oci8": { + "type": "php-extension", "php-extension": { "support": { "Windows": "wip", @@ -621,10 +622,10 @@ "Linux": "no" }, "notes": true - }, - "type": "php-extension" + } }, "ext-odbc": { + "type": "php-extension", "depends": [ "unixodbc" ], @@ -634,18 +635,18 @@ "Windows": "wip" }, "arg-type": "custom" - }, - "type": "php-extension" + } }, "ext-opcache": { + "type": "php-extension", "php-extension": { "arg-type@windows": "enable", "arg-type": "custom", "zend-extension": true - }, - "type": "php-extension" + } }, "ext-openssl": { + "type": "php-extension", "depends": [ "openssl", "zlib", @@ -656,10 +657,10 @@ "arg-type@windows": "with", "build-with-php": true, "notes": true - }, - "type": "php-extension" + } }, "ext-opentelemetry": { + "type": "php-extension", "artifact": "opentelemetry", "license": { "type": "file", @@ -669,10 +670,10 @@ "support": { "BSD": "wip" } - }, - "type": "php-extension" + } }, "ext-parallel": { + "type": "php-extension", "artifact": "parallel", "depends@windows": [ "pthreads4w" @@ -687,10 +688,10 @@ }, "arg-type@windows": "with", "notes": true - }, - "type": "php-extension" + } }, "ext-password-argon2": { + "type": "php-extension", "depends": [ "libargon2", "openssl" @@ -702,31 +703,31 @@ }, "arg-type": "custom", "notes": true - }, - "type": "php-extension" + } }, "ext-pcntl": { + "type": "php-extension", "php-extension": { "support": { "Windows": "no" } - }, - "type": "php-extension" + } }, "ext-pdo": { "type": "php-extension" }, "ext-pdo_mysql": { + "type": "php-extension", "depends": [ "ext-pdo", "ext-mysqlnd" ], "php-extension": { "arg-type": "with" - }, - "type": "php-extension" + } }, "ext-pdo_odbc": { + "type": "php-extension", "depends": [ "unixodbc", "ext-pdo", @@ -737,10 +738,10 @@ "BSD": "wip" }, "arg-type": "custom" - }, - "type": "php-extension" + } }, "ext-pdo_pgsql": { + "type": "php-extension", "depends": [ "postgresql", "ext-pdo", @@ -755,10 +756,10 @@ }, "arg-type": "with-path", "arg-type@windows": "custom" - }, - "type": "php-extension" + } }, "ext-pdo_sqlite": { + "type": "php-extension", "depends": [ "sqlite", "ext-pdo", @@ -769,10 +770,10 @@ "BSD": "wip" }, "arg-type": "with" - }, - "type": "php-extension" + } }, "ext-pdo_sqlsrv": { + "type": "php-extension", "artifact": "pdo_sqlsrv", "depends": [ "ext-pdo", @@ -787,10 +788,10 @@ "BSD": "wip" }, "arg-type": "with" - }, - "type": "php-extension" + } }, "ext-pgsql": { + "type": "php-extension", "depends": [ "postgresql" ], @@ -803,24 +804,24 @@ }, "arg-type": "custom", "notes": true - }, - "type": "php-extension" + } }, "ext-phar": { + "type": "php-extension", "depends": [ "ext-zlib" - ], - "type": "php-extension" + ] }, "ext-posix": { + "type": "php-extension", "php-extension": { "support": { "Windows": "no" } - }, - "type": "php-extension" + } }, "ext-protobuf": { + "type": "php-extension", "artifact": "protobuf", "license": { "type": "file", @@ -831,10 +832,10 @@ "Windows": "wip", "BSD": "wip" } - }, - "type": "php-extension" + } }, "ext-rar": { + "type": "php-extension", "artifact": "rar", "lang": "cpp", "license": { @@ -847,10 +848,10 @@ "Darwin": "partial" }, "notes": true - }, - "type": "php-extension" + } }, "ext-rdkafka": { + "type": "php-extension", "artifact": "ext-rdkafka", "depends": [ "librdkafka" @@ -866,10 +867,10 @@ "Windows": "wip" }, "arg-type": "custom" - }, - "type": "php-extension" + } }, "ext-readline": { + "type": "php-extension", "depends": [ "libedit" ], @@ -881,11 +882,18 @@ "arg-type": "with-path", "build-shared": false, "build-static": true - }, - "type": "php-extension" + } }, "ext-redis": { + "type": "php-extension", "artifact": "redis", + "suggests": [ + "zstd", + "liblz4", + "ext-session", + "ext-igbinary", + "ext-msgpack" + ], "license": { "type": "file", "path": [ @@ -898,38 +906,31 @@ "BSD": "wip" }, "arg-type": "custom" - }, - "suggests": [ - "zstd", - "liblz4", - "ext-session", - "ext-igbinary", - "ext-msgpack" - ], - "type": "php-extension" + } }, "ext-session": { + "type": "php-extension", "php-extension": { "build-with-php": true - }, - "type": "php-extension" + } }, "ext-shmop": { + "type": "php-extension", "php-extension": { "build-with-php": true - }, - "type": "php-extension" + } }, "ext-simdjson": { + "type": "php-extension", "artifact": "ext-simdjson", "lang": "cpp", "license": { "type": "file", "path": "LICENSE" - }, - "type": "php-extension" + } }, "ext-simplexml": { + "type": "php-extension", "depends": [ "libxml2" ], @@ -942,14 +943,17 @@ }, "arg-type": "custom", "build-with-php": true - }, - "type": "php-extension" + } }, "ext-snappy": { + "type": "php-extension", "artifact": "ext-snappy", "depends": [ "snappy" ], + "suggests": [ + "ext-apcu" + ], "lang": "cpp", "license": { "type": "file", @@ -961,13 +965,10 @@ "BSD": "wip" }, "arg-type": "custom" - }, - "suggests": [ - "ext-apcu" - ], - "type": "php-extension" + } }, "ext-snmp": { + "type": "php-extension", "depends": [ "net-snmp" ], @@ -978,10 +979,10 @@ }, "arg-type@windows": "with", "arg-type": "with" - }, - "type": "php-extension" + } }, "ext-soap": { + "type": "php-extension", "depends": [ "ext-libxml", "ext-session" @@ -991,13 +992,13 @@ "BSD": "wip" }, "arg-type": "custom" - }, - "type": "php-extension" + } }, "ext-sockets": { "type": "php-extension" }, "ext-sodium": { + "type": "php-extension", "depends": [ "libsodium" ], @@ -1006,10 +1007,10 @@ "BSD": "wip" }, "arg-type": "with" - }, - "type": "php-extension" + } }, "ext-spx": { + "type": "php-extension", "artifact": "spx", "depends": [ "zlib" @@ -1025,10 +1026,10 @@ }, "arg-type": "custom", "notes": true - }, - "type": "php-extension" + } }, "ext-sqlite3": { + "type": "php-extension", "depends": [ "sqlite" ], @@ -1039,10 +1040,10 @@ "arg-type": "with-path", "arg-type@windows": "with", "build-with-php": true - }, - "type": "php-extension" + } }, "ext-sqlsrv": { + "type": "php-extension", "artifact": "sqlsrv", "depends": [ "unixodbc" @@ -1059,10 +1060,10 @@ "support": { "BSD": "wip" } - }, - "type": "php-extension" + } }, "ext-ssh2": { + "type": "php-extension", "artifact": "ext-ssh2", "depends": [ "libssh2", @@ -1079,10 +1080,10 @@ }, "arg-type": "with-path", "arg-type@windows": "with" - }, - "type": "php-extension" + } }, "ext-swoole": { + "type": "php-extension", "artifact": "swoole", "depends": [ "libcares", @@ -1092,19 +1093,6 @@ "ext-openssl", "ext-curl" ], - "lang": "cpp", - "license": { - "type": "file", - "path": "LICENSE" - }, - "php-extension": { - "support": { - "Windows": "no", - "BSD": "wip" - }, - "arg-type": "custom", - "notes": true - }, "suggests": [ "zstd", "ext-sockets", @@ -1117,15 +1105,31 @@ "zstd", "liburing" ], - "type": "php-extension" + "lang": "cpp", + "license": { + "type": "file", + "path": "LICENSE" + }, + "php-extension": { + "support": { + "Windows": "no", + "BSD": "wip" + }, + "arg-type": "custom", + "notes": true + } }, "ext-swoole-hook-mysql": { + "type": "php-extension", "depends": [ "ext-mysqlnd", "ext-pdo", "ext-pdo_mysql", "ext-swoole" ], + "suggests": [ + "ext-mysqli" + ], "php-extension": { "support": { "Windows": "no", @@ -1133,13 +1137,10 @@ }, "arg-type": "none", "notes": true - }, - "suggests": [ - "ext-mysqli" - ], - "type": "php-extension" + } }, "ext-swoole-hook-odbc": { + "type": "php-extension", "depends": [ "unixodbc", "ext-pdo", @@ -1152,10 +1153,10 @@ }, "arg-type": "none", "notes": true - }, - "type": "php-extension" + } }, "ext-swoole-hook-pgsql": { + "type": "php-extension", "depends": [ "ext-pgsql", "ext-pdo", @@ -1169,10 +1170,10 @@ }, "arg-type": "none", "notes": true - }, - "type": "php-extension" + } }, "ext-swoole-hook-sqlite": { + "type": "php-extension", "depends": [ "ext-sqlite3", "ext-pdo", @@ -1185,11 +1186,17 @@ }, "arg-type": "none", "notes": true - }, - "type": "php-extension" + } }, "ext-swow": { + "type": "php-extension", "artifact": "swow", + "suggests": [ + "openssl", + "curl", + "ext-openssl", + "ext-curl" + ], "license": { "type": "file", "path": "LICENSE" @@ -1200,42 +1207,36 @@ }, "arg-type": "custom", "notes": true - }, - "suggests": [ - "openssl", - "curl", - "ext-openssl", - "ext-curl" - ], - "type": "php-extension" + } }, "ext-sysvmsg": { + "type": "php-extension", "php-extension": { "support": { "Windows": "no", "BSD": "wip" } - }, - "type": "php-extension" + } }, "ext-sysvsem": { + "type": "php-extension", "php-extension": { "support": { "Windows": "no", "BSD": "wip" } - }, - "type": "php-extension" + } }, "ext-sysvshm": { + "type": "php-extension", "php-extension": { "support": { "BSD": "wip" } - }, - "type": "php-extension" + } }, "ext-tidy": { + "type": "php-extension", "depends": [ "tidy" ], @@ -1245,16 +1246,16 @@ "BSD": "wip" }, "arg-type": "with-path" - }, - "type": "php-extension" + } }, "ext-tokenizer": { + "type": "php-extension", "php-extension": { "build-with-php": true - }, - "type": "php-extension" + } }, "ext-trader": { + "type": "php-extension", "artifact": "ext-trader", "license": { "type": "file", @@ -1265,10 +1266,10 @@ "BSD": "wip", "Windows": "wip" } - }, - "type": "php-extension" + } }, "ext-uuid": { + "type": "php-extension", "artifact": "ext-uuid", "depends": [ "libuuid" @@ -1283,10 +1284,10 @@ "BSD": "wip" }, "arg-type": "with-path" - }, - "type": "php-extension" + } }, "ext-uv": { + "type": "php-extension", "artifact": "ext-uv", "depends": [ "libuv", @@ -1302,10 +1303,10 @@ "BSD": "wip" }, "arg-type": "with-path" - }, - "type": "php-extension" + } }, "ext-xdebug": { + "type": "php-extension", "artifact": "xdebug", "license": { "type": "file", @@ -1322,10 +1323,10 @@ "build-static": false, "notes": true, "zend-extension": true - }, - "type": "php-extension" + } }, "ext-xhprof": { + "type": "php-extension", "artifact": "xhprof", "depends": [ "ext-ctype" @@ -1341,15 +1342,18 @@ }, "build-with-php": true, "notes": true - }, - "type": "php-extension" + } }, "ext-xlswriter": { + "type": "php-extension", "artifact": "xlswriter", "depends": [ "ext-zlib", "ext-zip" ], + "suggests": [ + "openssl" + ], "license": { "type": "file", "path": "LICENSE" @@ -1359,13 +1363,10 @@ "BSD": "wip" }, "arg-type": "custom" - }, - "suggests": [ - "openssl" - ], - "type": "php-extension" + } }, "ext-xml": { + "type": "php-extension", "depends": [ "libxml2" ], @@ -1380,10 +1381,10 @@ "arg-type@windows": "with", "build-with-php": true, "notes": true - }, - "type": "php-extension" + } }, "ext-xmlreader": { + "type": "php-extension", "depends": [ "libxml2" ], @@ -1397,10 +1398,10 @@ }, "arg-type": "custom", "build-with-php": true - }, - "type": "php-extension" + } }, "ext-xmlwriter": { + "type": "php-extension", "depends": [ "libxml2" ], @@ -1413,10 +1414,10 @@ }, "arg-type": "custom", "build-with-php": true - }, - "type": "php-extension" + } }, "ext-xsl": { + "type": "php-extension", "depends": [ "libxslt", "ext-xml", @@ -1428,10 +1429,10 @@ "BSD": "wip" }, "arg-type": "with-path" - }, - "type": "php-extension" + } }, "ext-xz": { + "type": "php-extension", "artifact": "ext-xz", "depends": [ "xz" @@ -1442,10 +1443,10 @@ }, "php-extension": { "arg-type": "with" - }, - "type": "php-extension" + } }, "ext-yac": { + "type": "php-extension", "artifact": "yac", "depends": [ "fastlz", @@ -1460,10 +1461,10 @@ "BSD": "wip" }, "arg-type": "custom" - }, - "type": "php-extension" + } }, "ext-yaml": { + "type": "php-extension", "artifact": "yaml", "depends": [ "libyaml" @@ -1478,10 +1479,10 @@ }, "arg-type@windows": "with", "arg-type": "with-path" - }, - "type": "php-extension" + } }, "ext-zip": { + "type": "php-extension", "artifact": "ext-zip", "depends": [ "libzip" @@ -1504,10 +1505,10 @@ }, "arg-type": "custom", "arg-type@windows": "enable" - }, - "type": "php-extension" + } }, "ext-zlib": { + "type": "php-extension", "depends": [ "zlib" ], @@ -1517,10 +1518,10 @@ "build-shared": false, "build-static": true, "build-with-php": true - }, - "type": "php-extension" + } }, "ext-zstd": { + "type": "php-extension", "artifact": "ext-zstd", "depends": [ "zstd" @@ -1535,7 +1536,6 @@ "BSD": "wip" }, "arg-type": "custom" - }, - "type": "php-extension" + } } } diff --git a/config/pkg.target.json b/config/pkg.target.json index 8e04df905..a5c20b78c 100644 --- a/config/pkg.target.json +++ b/config/pkg.target.json @@ -1,5 +1,6 @@ { "frankenphp": { + "type": "virtual-target", "artifact": "frankenphp", "depends": [ "php-embed", @@ -9,90 +10,78 @@ "php-embed", "go-xcaddy", "libxml2" - ], - "type": "virtual-target" + ] }, "go-xcaddy": { + "type": "target", "artifact": "go-xcaddy", "static-bins": [ "xcaddy" - ], - "type": "target" + ] }, "musl-toolchain": { - "artifact": "musl-toolchain", - "type": "target" + "type": "target", + "artifact": "musl-toolchain" }, "nasm": { - "artifact": "nasm", - "type": "target" + "type": "target", + "artifact": "nasm" }, "php": { + "type": "target", "artifact": "php-src", "depends@macos": [ "libxml2" - ], - "type": "target" + ] }, "php-cgi": { + "type": "virtual-target", "depends": [ "php" - ], - "type": "virtual-target" + ] }, "php-cli": { + "type": "virtual-target", "depends": [ "php" - ], - "type": "virtual-target" + ] }, "php-embed": { + "type": "virtual-target", "depends": [ "php" - ], - "type": "virtual-target" + ] }, "php-fpm": { + "type": "virtual-target", "depends": [ "php" - ], - "type": "virtual-target" + ] }, "php-micro": { + "type": "virtual-target", "artifact": "micro", "depends": [ "php" - ], - "type": "virtual-target" + ] }, "php-sdk-binary-tools": { - "artifact": "php-sdk-binary-tools", - "type": "target" - }, - "pkg-config": { - "artifact": "pkg-config", - "static-bins": [ - "pkg-config" - ], - "type": "target" + "type": "target", + "artifact": "php-sdk-binary-tools" }, "strawberry-perl": { - "artifact": "strawberry-perl", - "type": "target" + "type": "target", + "artifact": "strawberry-perl" }, "upx": { - "artifact": "upx", - "type": "target" + "type": "target", + "artifact": "upx" }, "vswhere": { + "type": "target", "artifact": "vswhere", "static-bins@windows": [ "vswhere.exe" - ], - "type": "target" - }, - "zig": { - "artifact": "zig", - "type": "target" + ] } } diff --git a/config/pkg/lib/attr.yml b/config/pkg/lib/attr.yml new file mode 100644 index 000000000..289bcd67b --- /dev/null +++ b/config/pkg/lib/attr.yml @@ -0,0 +1,14 @@ +attr: + type: library + artifact: + source: + type: url + url: 'https://download.savannah.nongnu.org/releases/attr/attr-2.5.2.tar.gz' + source-mirror: + type: url + url: 'https://mirror.souseiseki.middlendian.com/nongnu/attr/attr-2.5.2.tar.gz' + metadata: + license-files: [doc/COPYING.LGPL] + license: LGPL-2.1-or-later + static-libs@unix: + - libattr.a diff --git a/config/pkg/lib/brotli.yml b/config/pkg/lib/brotli.yml new file mode 100644 index 000000000..524f9ddc8 --- /dev/null +++ b/config/pkg/lib/brotli.yml @@ -0,0 +1,17 @@ +brotli: + type: library + artifact: + source: + type: ghtagtar + repo: google/brotli + match: 'v1\.\d.*' + binary: hosted + metadata: + license-files: [LICENSE] + license: MIT + headers: + - brotli + pkg-configs: + - libbrotlicommon + - libbrotlidec + - libbrotlienc diff --git a/config/pkg/lib/bzip2.yml b/config/pkg/lib/bzip2.yml new file mode 100644 index 000000000..132a24ffa --- /dev/null +++ b/config/pkg/lib/bzip2.yml @@ -0,0 +1,18 @@ +bzip2: + type: library + artifact: + source: + type: url + url: 'https://dl.static-php.dev/static-php-cli/deps/bzip2/bzip2-1.0.8.tar.gz' + source-mirror: + type: filelist + url: 'https://sourceware.org/pub/bzip2/' + regex: '/href="(?bzip2-(?[^"]+)\.tar\.gz)"/' + binary: hosted + metadata: + license-files: ['{registry_root}/src/globals/licenses/bzip2.txt'] + license: bzip2-1.0.6 + headers: + - bzlib.h + static-libs@unix: + - libbz2.a diff --git a/config/pkg/lib/curl.yml b/config/pkg/lib/curl.yml new file mode 100644 index 000000000..f183b21e0 --- /dev/null +++ b/config/pkg/lib/curl.yml @@ -0,0 +1,33 @@ +curl: + type: library + artifact: + source: + type: ghrel + repo: curl/curl + match: curl.+\.tar\.xz + prefer-stable: true + metadata: + license-files: [COPYING] + license: curl + depends@unix: + - openssl + - zlib + suggests@unix: + - libssh2 + - brotli + - nghttp2 + - nghttp3 + - ngtcp2 + - zstd + - libcares + - ldap + - idn2 + - krb5 + frameworks: + - CoreFoundation + - CoreServices + - SystemConfiguration + headers: + - curl + static-libs@unix: + - libcurl.a diff --git a/config/pkg/lib/fastlz.yml b/config/pkg/lib/fastlz.yml new file mode 100644 index 000000000..e3296831b --- /dev/null +++ b/config/pkg/lib/fastlz.yml @@ -0,0 +1,14 @@ +fastlz: + type: library + artifact: + source: + type: git + url: 'https://github.com/ariya/FastLZ.git' + rev: master + metadata: + license-files: [LICENSE.MIT] + license: MIT + headers: + - fastlz.h + static-libs@unix: + - libfastlz.a diff --git a/config/pkg/lib/gettext.yml b/config/pkg/lib/gettext.yml new file mode 100644 index 000000000..58f541f81 --- /dev/null +++ b/config/pkg/lib/gettext.yml @@ -0,0 +1,19 @@ +gettext: + type: library + artifact: + source: + type: filelist + url: 'https://ftp.gnu.org/pub/gnu/gettext/' + regex: '/href="(?gettext-(?[^"]+)\.tar\.xz)"/' + metadata: + license-files: [gettext-runtime/intl/COPYING.LIB] + license: LGPL-2.1-or-later + depends: + - libiconv + suggests: + - ncurses + - libxml2 + frameworks: + - CoreFoundation + static-libs@unix: + - libintl.a diff --git a/config/pkg/lib/gmp.yml b/config/pkg/lib/gmp.yml new file mode 100644 index 000000000..bdc13b559 --- /dev/null +++ b/config/pkg/lib/gmp.yml @@ -0,0 +1,19 @@ +gmp: + type: library + artifact: + source: + type: filelist + url: 'https://gmplib.org/download/gmp/' + regex: '/href="(?gmp-(?[^"]+)\.tar\.xz)"/' + source-mirror: + type: url + url: 'https://dl.static-php.dev/static-php-cli/deps/gmp/gmp-6.3.0.tar.xz' + metadata: + license-files: ['@/gmp.txt'] + license: Custom + headers: + - gmp.h + pkg-configs: + - gmp + static-libs@unix: + - libgmp.a diff --git a/config/pkg/lib/idn2.yml b/config/pkg/lib/idn2.yml new file mode 100644 index 000000000..cef2a1496 --- /dev/null +++ b/config/pkg/lib/idn2.yml @@ -0,0 +1,21 @@ +idn2: + type: library + artifact: + source: + type: filelist + url: 'https://ftp.gnu.org/gnu/libidn/' + regex: '/href="(?libidn2-(?[^"]+)\.tar\.gz)"/' + metadata: + license-files: [COPYING.LESSERv3] + license: LGPL-3.0-or-later + depends@macos: + - libiconv + - gettext + suggests@unix: + - libiconv + - gettext + - libunistring + headers: + - idn2.h + pkg-configs: + - libidn2 diff --git a/config/pkg/lib/krb5.yml b/config/pkg/lib/krb5.yml new file mode 100644 index 000000000..07fa3327f --- /dev/null +++ b/config/pkg/lib/krb5.yml @@ -0,0 +1,23 @@ +krb5: + type: library + artifact: + source: + type: ghtagtar + repo: krb5/krb5 + match: krb5.+-final + metadata: + license-files: [NOTICE] + license: BSD-3-Clause + source-root: src + depends: + - openssl + suggests: + - ldap + - libedit + frameworks: + - Kerberos + headers: + - krb5.h + - gssapi/gssapi.h + pkg-configs: + - krb5-gssapi diff --git a/config/pkg/lib/ldap.yml b/config/pkg/lib/ldap.yml new file mode 100644 index 000000000..d1e7db2ae --- /dev/null +++ b/config/pkg/lib/ldap.yml @@ -0,0 +1,17 @@ +ldap: + type: library + artifact: + source: + type: filelist + url: 'https://www.openldap.org/software/download/OpenLDAP/openldap-release/' + regex: '/href="(?openldap-(?[^"]+)\.tgz)"/' + metadata: + license-files: [LICENSE] + depends: + - openssl + - zlib + - gmp + - libsodium + pkg-configs: + - ldap + - lber diff --git a/config/pkg/lib/libcares.yml b/config/pkg/lib/libcares.yml new file mode 100644 index 000000000..5d3f8c9d1 --- /dev/null +++ b/config/pkg/lib/libcares.yml @@ -0,0 +1,23 @@ +libcares: + type: library + artifact: + source: + type: ghrel + repo: c-ares/c-ares + match: c-ares-.+\.tar\.gz + prefer-stable: true + source-mirror: + type: filelist + url: 'https://c-ares.org/download/' + regex: '/href="\/download\/(?c-ares-(?[^"]+)\.tar\.gz)"/' + binary: hosted + metadata: + license-files: [LICENSE.md] + headers@unix: + - ares.h + - ares_dns.h + - ares_nameser.h + pkg-configs: + - libcares + static-libs@unix: + - libcares.a diff --git a/config/pkg/lib/libedit.yml b/config/pkg/lib/libedit.yml new file mode 100644 index 000000000..02d6dd810 --- /dev/null +++ b/config/pkg/lib/libedit.yml @@ -0,0 +1,15 @@ +libedit: + type: library + artifact: + source: + type: filelist + url: 'https://thrysoee.dk/editline/' + regex: '/href="(?libedit-(?[^"]+)\.tar\.gz)"/' + binary: hosted + metadata: + license-files: [COPYING] + license: BSD-3-Clause + depends: + - ncurses + static-libs@unix: + - libedit.a diff --git a/config/pkg/lib/libiconv.yml b/config/pkg/lib/libiconv.yml new file mode 100644 index 000000000..7fc3bddad --- /dev/null +++ b/config/pkg/lib/libiconv.yml @@ -0,0 +1,18 @@ +libiconv: + type: library + artifact: + source: + type: filelist + url: 'https://ftp.gnu.org/gnu/libiconv/' + regex: '/href="(?libiconv-(?[^"]+)\.tar\.gz)"/' + binary: hosted + metadata: + license-files: [COPYING.LIB] + license: LGPL-2.0-or-later + headers: + - iconv.h + - libcharset.h + - localcharset.h + static-libs@unix: + - libiconv.a + - libcharset.a diff --git a/config/pkg/lib/libsodium.yml b/config/pkg/lib/libsodium.yml new file mode 100644 index 000000000..f5a551b93 --- /dev/null +++ b/config/pkg/lib/libsodium.yml @@ -0,0 +1,15 @@ +libsodium: + type: library + artifact: + source: + type: ghrel + repo: jedisct1/libsodium + match: 'libsodium-(?!1\.0\.21)\d+(\.\d+)*\.tar\.gz' + prefer-stable: true + binary: hosted + metadata: + license-files: [LICENSE] + pkg-configs: + - libsodium + static-libs@unix: + - libsodium.a diff --git a/config/pkg/lib/libssh2.yml b/config/pkg/lib/libssh2.yml new file mode 100644 index 000000000..8e1d82754 --- /dev/null +++ b/config/pkg/lib/libssh2.yml @@ -0,0 +1,22 @@ +libssh2: + type: library + artifact: + source: + type: ghrel + repo: libssh2/libssh2 + match: libssh2.+\.tar\.gz + prefer-stable: true + binary: hosted + metadata: + license-files: [COPYING] + license: BSD-3-Clause + depends: + - openssl + headers: + - libssh2.h + - libssh2_publickey.h + - libssh2_sftp.h + pkg-configs: + - libssh2 + static-libs@unix: + - libssh2.a diff --git a/config/pkg/lib/libunistring.yml b/config/pkg/lib/libunistring.yml new file mode 100644 index 000000000..2b2ffd334 --- /dev/null +++ b/config/pkg/lib/libunistring.yml @@ -0,0 +1,16 @@ +libunistring: + type: library + artifact: + source: + type: filelist + url: 'https://ftp.gnu.org/gnu/libunistring/' + regex: '/href="(?libunistring-(?[^"]+)\.tar\.gz)"/' + binary: hosted + metadata: + license-files: [COPYING.LIB] + license: LGPL-3.0-or-later + headers: + - unistr.h + - unistring/ + static-libs@unix: + - libunistring.a diff --git a/config/pkg/lib/libxml2.yml b/config/pkg/lib/libxml2.yml new file mode 100644 index 000000000..db88e8b14 --- /dev/null +++ b/config/pkg/lib/libxml2.yml @@ -0,0 +1,19 @@ +libxml2: + type: library + artifact: + source: + type: ghtagtar + repo: GNOME/libxml2 + match: v2\.\d+\.\d+$ + metadata: + license-files: [Copyright] + license: MIT + depends@unix: + - libiconv + suggests@unix: + - xz + - zlib + headers: + - libxml2 + pkg-configs: + - libxml-2.0 diff --git a/config/pkg/lib/ncurses.yml b/config/pkg/lib/ncurses.yml new file mode 100644 index 000000000..cbc1ba676 --- /dev/null +++ b/config/pkg/lib/ncurses.yml @@ -0,0 +1,12 @@ +ncurses: + type: library + artifact: + source: + type: filelist + url: 'https://ftp.gnu.org/pub/gnu/ncurses/' + regex: '/href="(?ncurses-(?[^"]+)\.tar\.gz)"/' + binary: hosted + metadata: + license-files: [COPYING] + static-libs@unix: + - libncurses.a diff --git a/config/pkg/lib/nghttp2.yml b/config/pkg/lib/nghttp2.yml new file mode 100644 index 000000000..166c33ac5 --- /dev/null +++ b/config/pkg/lib/nghttp2.yml @@ -0,0 +1,24 @@ +nghttp2: + type: library + artifact: + source: + type: ghrel + repo: nghttp2/nghttp2 + match: nghttp2.+\.tar\.xz + prefer-stable: true + metadata: + license-files: [COPYING] + depends: + - zlib + - openssl + suggests: + - libxml2 + - nghttp3 + - ngtcp2 + - brotli + headers: + - nghttp2 + pkg-configs: + - libnghttp2 + static-libs@unix: + - libnghttp2.a diff --git a/config/pkg/lib/nghttp3.yml b/config/pkg/lib/nghttp3.yml new file mode 100644 index 000000000..f9adc05b5 --- /dev/null +++ b/config/pkg/lib/nghttp3.yml @@ -0,0 +1,19 @@ +nghttp3: + type: library + artifact: + source: + type: ghrel + repo: ngtcp2/nghttp3 + match: nghttp3.+\.tar\.xz + prefer-stable: true + metadata: + license-files: [COPYING] + license: MIT + depends: + - openssl + headers: + - nghttp3 + pkg-configs: + - libnghttp3 + static-libs@unix: + - libnghttp3.a diff --git a/config/pkg/lib/ngtcp2.yml b/config/pkg/lib/ngtcp2.yml new file mode 100644 index 000000000..c864739a7 --- /dev/null +++ b/config/pkg/lib/ngtcp2.yml @@ -0,0 +1,24 @@ +ngtcp2: + type: library + artifact: + source: + type: ghrel + repo: ngtcp2/ngtcp2 + match: ngtcp2.+\.tar\.xz + prefer-stable: true + metadata: + license-files: [COPYING] + license: MIT + depends: + - openssl + suggests: + - nghttp3 + - brotli + headers: + - ngtcp2 + pkg-configs: + - libngtcp2 + - libngtcp2_crypto_ossl + static-libs@unix: + - libngtcp2.a + - libngtcp2_crypto_ossl.a diff --git a/config/pkg/lib/openssl.yml b/config/pkg/lib/openssl.yml new file mode 100644 index 000000000..22d065088 --- /dev/null +++ b/config/pkg/lib/openssl.yml @@ -0,0 +1,23 @@ +openssl: + type: library + artifact: + source: + type: ghrel + repo: openssl/openssl + match: openssl.+\.tar\.gz + prefer-stable: true + source-mirror: + type: filelist + url: 'https://www.openssl.org/source/' + regex: '/href="(?openssl-(?[^"]+)\.tar\.gz)"/' + binary: hosted + metadata: + license-files: [LICENSE.txt] + license: OpenSSL + depends: + - zlib + headers: + - openssl + static-libs@unix: + - libssl.a + - libcrypto.a diff --git a/config/pkg/lib/xz.yml b/config/pkg/lib/xz.yml new file mode 100644 index 000000000..7d0af682b --- /dev/null +++ b/config/pkg/lib/xz.yml @@ -0,0 +1,20 @@ +xz: + type: library + artifact: + source: + type: ghrel + repo: tukaani-project/xz + match: xz.+\.tar\.xz + prefer-stable: true + binary: hosted + metadata: + license-files: [COPYING] + license: 0BSD + depends@unix: + - libiconv + headers@unix: + - lzma + pkg-configs: + - liblzma + static-libs@unix: + - liblzma.a diff --git a/config/pkg/lib/zlib.yml b/config/pkg/lib/zlib.yml new file mode 100644 index 000000000..cf7f11ba0 --- /dev/null +++ b/config/pkg/lib/zlib.yml @@ -0,0 +1,16 @@ +zlib: + type: library + artifact: + source: + type: ghrel + repo: madler/zlib + match: zlib.+\.tar\.gz + binary: hosted + metadata: + license-files: ['@/zlib.txt'] + license: Zlib-Custom + headers: + - zlib.h + - zconf.h + static-libs@unix: + - libz.a diff --git a/config/pkg/lib/zstd.yml b/config/pkg/lib/zstd.yml new file mode 100644 index 000000000..3d76e270c --- /dev/null +++ b/config/pkg/lib/zstd.yml @@ -0,0 +1,19 @@ +zstd: + type: library + artifact: + source: + type: ghrel + repo: facebook/zstd + match: zstd.+\.tar\.gz + prefer-stable: true + metadata: + license-files: [LICENSE] + license: BSD-3-Clause + headers@unix: + - zdict.h + - zstd.h + - zstd_errors.h + pkg-configs: + - libzstd + static-libs@unix: + - libzstd.a diff --git a/config/pkg/target/pkg-config.yml b/config/pkg/target/pkg-config.yml new file mode 100644 index 000000000..9b6d16035 --- /dev/null +++ b/config/pkg/target/pkg-config.yml @@ -0,0 +1,9 @@ +pkg-config: + type: target + artifact: + source: 'https://dl.static-php.dev/static-php-cli/deps/pkg-config/pkg-config-0.29.2.tar.gz' + binary: + linux-x86_64: { type: ghrel, repo: static-php/static-php-cli-hosted, match: pkg-config-x86_64-linux-musl-1.2.5.txz, extract: { bin/pkg-config: '{pkg_root_path}/bin/pkg-config' } } + linux-aarch64: { type: ghrel, repo: static-php/static-php-cli-hosted, match: pkg-config-aarch64-linux-musl-1.2.5.txz, extract: { bin/pkg-config: '{pkg_root_path}/bin/pkg-config' } } + macos-x86_64: { type: ghrel, repo: static-php/static-php-cli-hosted, match: pkg-config-x86_64-darwin.txz, extract: { bin/pkg-config: '{pkg_root_path}/bin/pkg-config' } } + macos-aarch64: { type: ghrel, repo: static-php/static-php-cli-hosted, match: pkg-config-aarch64-darwin.txz, extract: { bin/pkg-config: '{pkg_root_path}/bin/pkg-config' } } diff --git a/config/pkg/target/zig.yml b/config/pkg/target/zig.yml new file mode 100644 index 000000000..dda72b55b --- /dev/null +++ b/config/pkg/target/zig.yml @@ -0,0 +1,4 @@ +zig: + type: target + artifact: + binary: custom diff --git a/phpstan.neon b/phpstan.neon index 45e512ba0..ce7cdb2d2 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -11,11 +11,13 @@ parameters: - '#Function Swoole\\Coroutine\\run not found.#' - '#Static call to instance method ZM\\Logger\\ConsoleColor#' - '#Constant GNU_ARCH not found#' + - + identifiers: + - 'if.alwaysFalse' dynamicConstantNames: - PHP_OS_FAMILY excludePaths: analyseAndScan: - - ./src/globals/ext-tests/swoole.php - - ./src/globals/ext-tests/swoole.phpt + - ./src/globals/ext-tests/ - ./src/globals/test-extensions.php - ./src/SPC/ diff --git a/spc.registry.json b/spc.registry.json index 7c5a8ce7b..b3d34939a 100644 --- a/spc.registry.json +++ b/spc.registry.json @@ -1,5 +1,5 @@ { - "name": "internal", + "name": "core", "autoload": "vendor/autoload.php", "doctor": { "psr-4": { @@ -12,14 +12,12 @@ }, "config": [ "config/pkg.ext.json", - "config/pkg.lib.json", + "config/pkg/lib", + "config/pkg/target", "config/pkg.target.json" ] }, "artifact": { - "config": [ - "config/artifact.json" - ], "psr-4": { "Package\\Artifact": "src/Package/Artifact" } diff --git a/src/Package/Artifact/attr.php b/src/Package/Artifact/attr.php new file mode 100644 index 000000000..e80c8c831 --- /dev/null +++ b/src/Package/Artifact/attr.php @@ -0,0 +1,23 @@ +getSourceDir()); + } + } +} diff --git a/src/Package/Artifact/bzip2.php b/src/Package/Artifact/bzip2.php new file mode 100644 index 000000000..a6a1e58e5 --- /dev/null +++ b/src/Package/Artifact/bzip2.php @@ -0,0 +1,24 @@ +getSourceDir() . '/Makefile', + 'CFLAGS=-Wall', + 'CFLAGS=-fPIC -Wall' + ); + } +} diff --git a/src/Package/Artifact/openssl.php b/src/Package/Artifact/openssl.php new file mode 100644 index 000000000..325e6a09e --- /dev/null +++ b/src/Package/Artifact/openssl.php @@ -0,0 +1,35 @@ +', + "#include \n#include " + ); + } +} diff --git a/src/Package/Library/attr.php b/src/Package/Library/attr.php new file mode 100644 index 000000000..40637e80a --- /dev/null +++ b/src/Package/Library/attr.php @@ -0,0 +1,29 @@ +appendEnv([ + 'CFLAGS' => '-Wno-int-conversion -Wno-implicit-function-declaration', + ]) + ->exec('libtoolize --force --copy') + ->exec('./autogen.sh || autoreconf -if') + ->configure('--disable-nls') + ->make('install-attributes_h install-data install-libattr_h install-libLTLIBRARIES install-pkgincludeHEADERS install-pkgconfDATA', with_install: false); + $lib->patchPkgconfPrefix(['libattr.pc'], PKGCONF_PATCH_PREFIX); + } +} diff --git a/src/Package/Library/brotli.php b/src/Package/Library/brotli.php new file mode 100644 index 000000000..f22b9ef29 --- /dev/null +++ b/src/Package/Library/brotli.php @@ -0,0 +1,55 @@ +setBuildDir($lib->getSourceDir() . '/build-dir') + ->addConfigureArgs("-DSHARE_INSTALL_PREFIX={$lib->getBuildRootPath()}") + ->build(); + + // Patch pkg-config files + $lib->patchPkgconfPrefix(['libbrotlicommon.pc', 'libbrotlidec.pc', 'libbrotlienc.pc'], PKGCONF_PATCH_PREFIX); + + // Add -lbrotlicommon to libbrotlidec.pc and libbrotlienc.pc + FileSystem::replaceFileLineContainsString( + $lib->getLibDir() . '/pkgconfig/libbrotlidec.pc', + 'Libs: -L${libdir} -lbrotlidec', + 'Libs: -L${libdir} -lbrotlidec -lbrotlicommon' + ); + FileSystem::replaceFileLineContainsString( + $lib->getLibDir() . '/pkgconfig/libbrotlienc.pc', + 'Libs: -L${libdir} -lbrotlienc', + 'Libs: -L${libdir} -lbrotlienc -lbrotlicommon' + ); + + // Create symlink: libbrotli.a -> libbrotlicommon.a + shell()->cd($lib->getLibDir())->exec('ln -sf libbrotlicommon.a libbrotli.a'); + + // Remove dynamic libraries + foreach (FileSystem::scanDirFiles($lib->getLibDir(), false, true) as $filename) { + if (str_starts_with($filename, 'libbrotli') && (str_contains($filename, '.so') || str_ends_with($filename, '.dylib'))) { + unlink($lib->getLibDir() . '/' . $filename); + } + } + + // Remove brotli binary if exists + if (file_exists($lib->getBinDir() . '/brotli')) { + unlink($lib->getBinDir() . '/brotli'); + } + } +} diff --git a/src/Package/Library/bzip2.php b/src/Package/Library/bzip2.php new file mode 100644 index 000000000..7f554ab48 --- /dev/null +++ b/src/Package/Library/bzip2.php @@ -0,0 +1,25 @@ +cd($lib->getSourceDir())->initializeEnv($lib) + ->exec("make PREFIX='{$lib->getBuildRootPath()}' clean") + ->exec("make -j{$builder->concurrency} PREFIX='{$lib->getBuildRootPath()}' libbz2.a") + ->exec('cp libbz2.a ' . $lib->getLibDir()) + ->exec('cp bzlib.h ' . $lib->getIncludeDir()); + } +} diff --git a/src/Package/Library/curl.php b/src/Package/Library/curl.php new file mode 100644 index 000000000..283c765a1 --- /dev/null +++ b/src/Package/Library/curl.php @@ -0,0 +1,61 @@ +cd($lib->getSourceDir())->exec('sed -i.save s@\${CMAKE_C_IMPLICIT_LINK_LIBRARIES}@@ ./CMakeLists.txt'); + if (SystemTarget::getTargetOS() === 'Darwin') { + FileSystem::replaceFileRegex("{$lib->getSourceDir()}/CMakeLists.txt", '/NOT COREFOUNDATION_FRAMEWORK/m', 'FALSE'); + FileSystem::replaceFileRegex("{$lib->getSourceDir()}/CMakeLists.txt", '/NOT SYSTEMCONFIGURATION_FRAMEWORK/m', 'FALSE'); + FileSystem::replaceFileRegex("{$lib->getSourceDir()}/CMakeLists.txt", '/NOT CORESERVICES_FRAMEWORK/m', 'FALSE'); + } + return true; + } + + #[BuildFor('Linux')] + #[BuildFor('Darwin')] + public function build(LibraryPackage $lib): void + { + UnixCMakeExecutor::create($lib) + ->optionalPackage('openssl', '-DCURL_USE_OPENSSL=ON -DCURL_CA_BUNDLE=OFF -DCURL_CA_PATH=OFF -DCURL_CA_FALLBACK=ON', '-DCURL_USE_OPENSSL=OFF -DCURL_ENABLE_SSL=OFF') + ->optionalPackage('brotli', ...cmake_boolean_args('CURL_BROTLI')) + ->optionalPackage('libssh2', ...cmake_boolean_args('CURL_USE_LIBSSH2')) + ->optionalPackage('nghttp2', ...cmake_boolean_args('USE_NGHTTP2')) + ->optionalPackage('nghttp3', ...cmake_boolean_args('USE_NGHTTP3')) + ->optionalPackage('ngtcp2', ...cmake_boolean_args('USE_NGTCP2')) + ->optionalPackage('ldap', ...cmake_boolean_args('CURL_DISABLE_LDAP', true)) + ->optionalPackage('zstd', ...cmake_boolean_args('CURL_ZSTD')) + ->optionalPackage('idn2', ...cmake_boolean_args('USE_LIBIDN2')) + ->optionalPackage('psl', ...cmake_boolean_args('CURL_USE_LIBPSL')) + ->optionalPackage('krb5', ...cmake_boolean_args('CURL_USE_GSSAPI')) + ->optionalPackage('idn2', ...cmake_boolean_args('CURL_USE_IDN2')) + ->optionalPackage('libcares', '-DENABLE_ARES=ON') + ->addConfigureArgs( + '-DBUILD_CURL_EXE=OFF', + '-DBUILD_LIBCURL_DOCS=OFF', + ) + ->build(); + + // patch pkgconf + $lib->patchPkgconfPrefix(['libcurl.pc']); + shell()->cd("{$lib->getLibDir()}/cmake/CURL/") + ->exec("sed -ie 's|\"/lib/libcurl.a\"|\"{$lib->getLibDir()}/libcurl.a\"|g' CURLTargets-release.cmake"); + } +} diff --git a/src/Package/Library/fastlz.php b/src/Package/Library/fastlz.php new file mode 100644 index 000000000..f9dd010dc --- /dev/null +++ b/src/Package/Library/fastlz.php @@ -0,0 +1,36 @@ +cd($lib->getSourceDir())->initializeEnv($lib) + ->exec("{$cc} -c -O3 -fPIC fastlz.c -o fastlz.o") + ->exec("{$ar} rcs libfastlz.a fastlz.o"); + + // Copy header file + if (!copy($lib->getSourceDir() . '/fastlz.h', $lib->getIncludeDir() . '/fastlz.h')) { + throw new BuildFailureException('Failed to copy fastlz.h'); + } + + // Copy static library + if (!copy($lib->getSourceDir() . '/libfastlz.a', $lib->getLibDir() . '/libfastlz.a')) { + throw new BuildFailureException('Failed to copy libfastlz.a'); + } + } +} diff --git a/src/Package/Library/gettext.php b/src/Package/Library/gettext.php new file mode 100644 index 000000000..7af69905e --- /dev/null +++ b/src/Package/Library/gettext.php @@ -0,0 +1,48 @@ +optionalPackage('ncurses', "--with-libncurses-prefix={$pkg->getBuildRootPath()}") + ->optionalPackage('libxml2', "--with-libxml2-prefix={$pkg->getBuildRootPath()}") + ->addConfigureArgs( + '--disable-java', + '--disable-c++', + '--disable-d', + '--disable-rpath', + '--disable-modula2', + '--disable-libasprintf', + '--with-included-libintl', + "--with-iconv-prefix={$pkg->getBuildRootPath()}", + ); + + // zts + if ($builder->getOption('enable-zts')) { + $autoconf->addConfigureArgs('--enable-threads=isoc+posix') + ->appendEnv([ + 'CFLAGS' => '-lpthread -D_REENTRANT', + 'LDFLGAS' => '-lpthread', + ]); + } else { + $autoconf->addConfigureArgs('--disable-threads'); + } + + $autoconf->configure()->make(dir: "{$pkg->getSourceDir()}/gettext-runtime/intl"); + $pkg->patchLaDependencyPrefix(); + } +} diff --git a/src/Package/Library/gmp.php b/src/Package/Library/gmp.php new file mode 100644 index 000000000..f8eb5f8e4 --- /dev/null +++ b/src/Package/Library/gmp.php @@ -0,0 +1,25 @@ +appendEnv(['CFLAGS' => '-std=c17']) + ->configure('--enable-fat') + ->make(); + $lib->patchPkgconfPrefix(['gmp.pc']); + } +} diff --git a/src/Package/Library/idn2.php b/src/Package/Library/idn2.php new file mode 100644 index 000000000..3cffd9be5 --- /dev/null +++ b/src/Package/Library/idn2.php @@ -0,0 +1,33 @@ +configure( + '--disable-nls', + '--disable-doc', + '--enable-year2038', + '--disable-rpath' + ) + ->optionalPackage('libiconv', '--with-libiconv-prefix=' . BUILD_ROOT_PATH) + ->optionalPackage('libunistring', '--with-libunistring-prefix=' . BUILD_ROOT_PATH) + ->optionalPackage('gettext', '--with-libnintl-prefix=' . BUILD_ROOT_PATH) + ->make(); + $lib->patchPkgconfPrefix(['libidn2.pc']); + $lib->patchLaDependencyPrefix(); + } +} diff --git a/src/Package/Library/krb5.php b/src/Package/Library/krb5.php new file mode 100644 index 000000000..303c3b63e --- /dev/null +++ b/src/Package/Library/krb5.php @@ -0,0 +1,63 @@ +cd($lib->getSourceRoot())->exec('autoreconf -if'); + + $resolved = array_keys($installer->getResolvedPackages()); + $spc = new SPCConfigUtil(['no_php' => true, 'libs_only_deps' => true]); + $config = $spc->getPackageDepsConfig($lib->getName(), $resolved, include_suggests: true); + $extraEnv = [ + 'CFLAGS' => '-fcommon', + 'LIBS' => $config['libs'], + ]; + if (getenv('SPC_LD_LIBRARY_PATH') && getenv('SPC_LIBRARY_PATH')) { + $extraEnv = [...$extraEnv, ...[ + 'LD_LIBRARY_PATH' => getenv('SPC_LD_LIBRARY_PATH'), + 'LIBRARY_PATH' => getenv('SPC_LIBRARY_PATH'), + ]]; + } + $args = [ + '--disable-nls', + '--disable-rpath', + '--without-system-verto', + ]; + if (SystemTarget::getTargetOS() === 'Darwin') { + $extraEnv['LDFLAGS'] = '-framework Kerberos'; + $args[] = 'ac_cv_func_secure_getenv=no'; + } + UnixAutoconfExecutor::create($lib) + ->appendEnv($extraEnv) + ->optionalPackage('ldap', '--with-ldap', '--without-ldap') + ->optionalPackage('libedit', '--with-libedit', '--without-libedit') + ->configure(...$args) + ->make(); + $lib->patchPkgconfPrefix([ + 'krb5-gssapi.pc', + 'krb5.pc', + 'kadm-server.pc', + 'kadm-client.pc', + 'kdb.pc', + 'mit-krb5-gssapi.pc', + 'mit-krb5.pc', + 'gssrpc.pc', + ]); + } +} diff --git a/src/Package/Library/ldap.php b/src/Package/Library/ldap.php new file mode 100644 index 000000000..21e7f8c04 --- /dev/null +++ b/src/Package/Library/ldap.php @@ -0,0 +1,59 @@ +getSourceDir() . '/configure', '"-lssl -lcrypto', '"-lssl -lcrypto -lz ' . $extra); + return true; + } + + #[BuildFor('Linux')] + #[BuildFor('Darwin')] + public function build(LibraryPackage $lib): void + { + UnixAutoconfExecutor::create($lib) + ->optionalPackage('openssl', '--with-tls=openssl') + ->optionalPackage('gmp', '--with-mp=gmp') + ->optionalPackage('libsodium', '--with-argon2=libsodium', '--enable-argon2=no') + ->addConfigureArgs( + '--disable-slapd', + '--without-systemd', + '--without-cyrus-sasl', + 'ac_cv_func_pthread_kill_other_threads_np=no' + ) + ->appendEnv([ + 'CFLAGS' => '-Wno-date-time', + 'LDFLAGS' => "-L{$lib->getLibDir()}", + 'CPPFLAGS' => "-I{$lib->getIncludeDir()}", + ]) + ->configure() + ->exec('sed -i -e "s/SUBDIRS= include libraries clients servers tests doc/SUBDIRS= include libraries clients servers/g" Makefile') + ->make(); + + FileSystem::replaceFileLineContainsString( + $lib->getLibDir() . '/pkgconfig/ldap.pc', + 'Libs: -L${libdir} -lldap', + 'Libs: -L${libdir} -lldap -llber' + ); + $lib->patchPkgconfPrefix(['ldap.pc', 'lber.pc']); + $lib->patchLaDependencyPrefix(); + } +} diff --git a/src/Package/Library/libcares.php b/src/Package/Library/libcares.php new file mode 100644 index 000000000..fbfb58303 --- /dev/null +++ b/src/Package/Library/libcares.php @@ -0,0 +1,38 @@ +getSourceDir()}/src/lib/thirdparty/apple/dnsinfo.h")) { + FileSystem::createDir("{$lib->getSourceDir()}/src/lib/thirdparty/apple"); + copy(ROOT_DIR . '/src/globals/extra/libcares_dnsinfo.h', "{$lib->getSourceDir()}/src/lib/thirdparty/apple/dnsinfo.h"); + return true; + } + return false; + } + + #[BuildFor('Linux')] + #[BuildFor('Darwin')] + public function build(LibraryPackage $lib): void + { + UnixAutoconfExecutor::create($lib)->configure('--disable-tests')->make(); + + $lib->patchPkgconfPrefix(['libcares.pc'], PKGCONF_PATCH_PREFIX); + } +} diff --git a/src/Package/Library/libedit.php b/src/Package/Library/libedit.php index 08a435da7..c06dbae3f 100644 --- a/src/Package/Library/libedit.php +++ b/src/Package/Library/libedit.php @@ -4,9 +4,9 @@ namespace Package\Library; -use StaticPHP\Attribute\Package\BeforeStage; use StaticPHP\Attribute\Package\BuildFor; use StaticPHP\Attribute\Package\Library; +use StaticPHP\Attribute\Package\PatchBeforeBuild; use StaticPHP\Package\LibraryPackage; use StaticPHP\Runtime\Executor\UnixAutoconfExecutor; use StaticPHP\Util\FileSystem; @@ -14,7 +14,7 @@ #[Library('libedit')] class libedit extends LibraryPackage { - #[BeforeStage(stage: 'build')] + #[PatchBeforeBuild] public function patchBeforeBuild(): void { FileSystem::replaceFileRegex( diff --git a/src/Package/Library/libiconv.php b/src/Package/Library/libiconv.php index ac91d188a..d2ac4d26c 100644 --- a/src/Package/Library/libiconv.php +++ b/src/Package/Library/libiconv.php @@ -12,16 +12,17 @@ #[Library('libiconv')] class libiconv { + #[BuildFor('Linux')] #[BuildFor('Darwin')] - public function build(LibraryPackage $package): void + public function build(LibraryPackage $lib): void { - UnixAutoconfExecutor::create($package) + UnixAutoconfExecutor::create($lib) ->configure( '--enable-extra-encodings', '--enable-year2038', ) ->make('install-lib', with_install: false) - ->make('install-lib', with_install: false, dir: "{$package->getSourceDir()}/libcharset"); - $package->patchLaDependencyPrefix(); + ->make('install-lib', with_install: false, dir: $lib->getSourceDir() . '/libcharset'); + $lib->patchLaDependencyPrefix(); } } diff --git a/src/Package/Library/libsodium.php b/src/Package/Library/libsodium.php new file mode 100644 index 000000000..50d706ba4 --- /dev/null +++ b/src/Package/Library/libsodium.php @@ -0,0 +1,24 @@ +configure()->make(); + + // Patch pkg-config file + $lib->patchPkgconfPrefix(['libsodium.pc'], PKGCONF_PATCH_PREFIX); + } +} diff --git a/src/Package/Library/libssh2.php b/src/Package/Library/libssh2.php new file mode 100644 index 000000000..f71d508ac --- /dev/null +++ b/src/Package/Library/libssh2.php @@ -0,0 +1,29 @@ +optionalPackage('zlib', ...cmake_boolean_args('ENABLE_ZLIB_COMPRESSION')) + ->addConfigureArgs( + '-DBUILD_EXAMPLES=OFF', + '-DBUILD_TESTING=OFF' + ) + ->build(); + + $lib->patchPkgconfPrefix(['libssh2.pc']); + } +} diff --git a/src/Package/Library/libunistring.php b/src/Package/Library/libunistring.php new file mode 100644 index 000000000..5f5575dc2 --- /dev/null +++ b/src/Package/Library/libunistring.php @@ -0,0 +1,25 @@ +configure('--disable-nls') + ->make(); + + $lib->patchLaDependencyPrefix(); + } +} diff --git a/src/Package/Library/libxml2.php b/src/Package/Library/libxml2.php index 4767efcb0..7c35d6855 100644 --- a/src/Package/Library/libxml2.php +++ b/src/Package/Library/libxml2.php @@ -8,47 +8,68 @@ use StaticPHP\Attribute\Package\Library; use StaticPHP\Package\LibraryPackage; use StaticPHP\Runtime\Executor\UnixCMakeExecutor; -use StaticPHP\Runtime\SystemTarget; use StaticPHP\Util\FileSystem; #[Library('libxml2')] class libxml2 { - #[BuildFor('Darwin')] - public function build(LibraryPackage $package): void + #[BuildFor('Linux')] + public function buildForLinux(LibraryPackage $lib): void { - $cmake = UnixCMakeExecutor::create($package) + UnixCMakeExecutor::create($lib) ->optionalPackage( 'zlib', '-DLIBXML2_WITH_ZLIB=ON ' . - "-DZLIB_LIBRARY={$package->getLibDir()}/libz.a " . - "-DZLIB_INCLUDE_DIR={$package->getIncludeDir()}", + "-DZLIB_LIBRARY={$lib->getLibDir()}/libz.a " . + "-DZLIB_INCLUDE_DIR={$lib->getIncludeDir()}", '-DLIBXML2_WITH_ZLIB=OFF', ) ->optionalPackage('xz', ...cmake_boolean_args('LIBXML2_WITH_LZMA')) ->addConfigureArgs( '-DLIBXML2_WITH_ICONV=ON', + '-DIconv_IS_BUILT_IN=OFF', '-DLIBXML2_WITH_ICU=OFF', // optional, but discouraged: https://gitlab.gnome.org/GNOME/libxml2/-/blob/master/README.md '-DLIBXML2_WITH_PYTHON=OFF', '-DLIBXML2_WITH_PROGRAMS=OFF', '-DLIBXML2_WITH_TESTS=OFF', - ); - - if (SystemTarget::getTargetOS() === 'Linux') { - $cmake->addConfigureArgs('-DIconv_IS_BUILT_IN=OFF'); - } - - $cmake->build(); - - FileSystem::replaceFileStr( - BUILD_LIB_PATH . '/pkgconfig/libxml-2.0.pc', - '-lxml2 -liconv', - '-lxml2' - ); - FileSystem::replaceFileStr( - BUILD_LIB_PATH . '/pkgconfig/libxml-2.0.pc', - '-lxml2', - '-lxml2 -liconv' - ); + ) + ->build(); + + $this->patchPkgConfig($lib); + } + + #[BuildFor('Darwin')] + public function buildForDarwin(LibraryPackage $lib): void + { + UnixCMakeExecutor::create($lib) + ->optionalPackage( + 'zlib', + '-DLIBXML2_WITH_ZLIB=ON ' . + "-DZLIB_LIBRARY={$lib->getLibDir()}/libz.a " . + "-DZLIB_INCLUDE_DIR={$lib->getIncludeDir()}", + '-DLIBXML2_WITH_ZLIB=OFF', + ) + ->optionalPackage('xz', ...cmake_boolean_args('LIBXML2_WITH_LZMA')) + ->addConfigureArgs( + '-DLIBXML2_WITH_ICONV=ON', + '-DLIBXML2_WITH_ICU=OFF', + '-DLIBXML2_WITH_PYTHON=OFF', + '-DLIBXML2_WITH_PROGRAMS=OFF', + '-DLIBXML2_WITH_TESTS=OFF', + ) + ->build(); + + $this->patchPkgConfig($lib); + } + + private function patchPkgConfig(LibraryPackage $lib): void + { + $pcFile = "{$lib->getLibDir()}/pkgconfig/libxml-2.0.pc"; + + // Remove -liconv from original + FileSystem::replaceFileStr($pcFile, '-lxml2 -liconv', '-lxml2'); + + // Add -liconv after -lxml2 + FileSystem::replaceFileStr($pcFile, '-lxml2', '-lxml2 -liconv'); } } diff --git a/src/Package/Library/nghttp2.php b/src/Package/Library/nghttp2.php new file mode 100644 index 000000000..3a85ada4a --- /dev/null +++ b/src/Package/Library/nghttp2.php @@ -0,0 +1,41 @@ +optionalPackage('zlib', ...ac_with_args('zlib', true)) + ->optionalPackage('openssl', ...ac_with_args('openssl', true)) + ->optionalPackage('libxml2', ...ac_with_args('libxml2', true)) + ->optionalPackage('ngtcp2', ...ac_with_args('libngtcp2', true)) + ->optionalPackage('nghttp3', ...ac_with_args('libnghttp3', true)) + ->optionalPackage( + 'brotli', + fn (LibraryPackage $brotli) => implode(' ', [ + '--with-brotlidec=yes', + "LIBBROTLIDEC_CFLAGS=\"-I{$brotli->getIncludeDir()}\"", + "LIBBROTLIDEC_LIBS=\"{$brotli->getStaticLibFiles()}\"", + '--with-libbrotlienc=yes', + "LIBBROTLIENC_CFLAGS=\"-I{$brotli->getIncludeDir()}\"", + "LIBBROTLIENC_LIBS=\"{$brotli->getStaticLibFiles()}\"", + ]) + ) + ->configure('--enable-lib-only') + ->make(); + + $lib->patchPkgconfPrefix(['libnghttp2.pc'], PKGCONF_PATCH_PREFIX); + } +} diff --git a/src/Package/Library/nghttp3.php b/src/Package/Library/nghttp3.php new file mode 100644 index 000000000..1f686b7b5 --- /dev/null +++ b/src/Package/Library/nghttp3.php @@ -0,0 +1,25 @@ +configure('--enable-lib-only') + ->make(); + + $lib->patchPkgconfPrefix(['libnghttp3.pc'], PKGCONF_PATCH_PREFIX); + } +} diff --git a/src/Package/Library/ngtcp2.php b/src/Package/Library/ngtcp2.php new file mode 100644 index 000000000..15821225b --- /dev/null +++ b/src/Package/Library/ngtcp2.php @@ -0,0 +1,52 @@ +optionalPackage( + 'openssl', + fn (LibraryPackage $openssl) => implode(' ', [ + '--with-openssl=yes', + "OPENSSL_LIBS=\"{$openssl->getStaticLibFiles()}\"", + "OPENSSL_CFLAGS=\"-I{$openssl->getIncludeDir()}\"", + ]), + '--with-openssl=no' + ) + ->optionalPackage('nghttp3', ...ac_with_args('libnghttp3', true)) + ->optionalPackage( + 'brotli', + fn (LibraryPackage $brotli) => implode(' ', [ + '--with-brotlidec=yes', + "LIBBROTLIDEC_CFLAGS=\"-I{$brotli->getIncludeDir()}\"", + "LIBBROTLIDEC_LIBS=\"{$brotli->getStaticLibFiles()}\"", + '--with-libbrotlienc=yes', + "LIBBROTLIENC_CFLAGS=\"-I{$brotli->getIncludeDir()}\"", + "LIBBROTLIENC_LIBS=\"{$brotli->getStaticLibFiles()}\"", + ]) + ) + ->appendEnv(['PKG_CONFIG' => '$PKG_CONFIG --static']) + ->configure('--enable-lib-only') + ->make(); + + $lib->patchPkgconfPrefix(['libngtcp2.pc', 'libngtcp2_crypto_ossl.pc'], PKGCONF_PATCH_PREFIX); + + // On macOS, the static library may contain other static libraries + // ld: archive member 'libssl.a' not a mach-o file in libngtcp2_crypto_ossl.a + $AR = getenv('AR') ?: 'ar'; + shell()->cd($lib->getLibDir())->exec("{$AR} -t libngtcp2_crypto_ossl.a | grep '\\.a\$' | xargs -n1 {$AR} d libngtcp2_crypto_ossl.a"); + } +} diff --git a/src/Package/Library/openssl.php b/src/Package/Library/openssl.php new file mode 100644 index 000000000..e0ee9edc3 --- /dev/null +++ b/src/Package/Library/openssl.php @@ -0,0 +1,89 @@ +getInstaller()->getLibraryPackage('zlib')->getStaticLibFiles(); + $arch = getenv('SPC_ARCH'); + + shell()->cd($pkg->getSourceDir())->initializeEnv($pkg) + ->exec( + './Configure no-shared zlib ' . + "--prefix={$pkg->getBuildRootPath()} " . + '--libdir=lib ' . + '--openssldir=/etc/ssl ' . + "darwin64-{$arch}-cc" + ) + ->exec('make clean') + ->exec("make -j{$pkg->getBuilder()->concurrency} CNF_EX_LIBS=\"{$zlib_libs}\"") + ->exec('make install_sw'); + $this->patchPkgConfig($pkg); + } + + #[BuildFor('Linux')] + public function build(LibraryPackage $lib): void + { + $arch = getenv('SPC_ARCH'); + + $env = "CC='" . getenv('CC') . ' -idirafter ' . BUILD_INCLUDE_PATH . + ' -idirafter /usr/include/ ' . + ' -idirafter /usr/include/' . getenv('SPC_ARCH') . '-linux-gnu/ ' . + "' "; + + $ex_lib = trim($lib->getInstaller()->getLibraryPackage('zlib')->getStaticLibFiles()) . ' -ldl -pthread'; + $zlib_extra = + '--with-zlib-include=' . BUILD_INCLUDE_PATH . ' ' . + '--with-zlib-lib=' . BUILD_LIB_PATH . ' '; + + $openssl_dir = getenv('OPENSSLDIR') ?: null; + // TODO: in v3 use the following: $openssl_dir ??= SystemUtil::getOSRelease()['dist'] === 'redhat' ? '/etc/pki/tls' : '/etc/ssl'; + $openssl_dir ??= '/etc/ssl'; + $ex_lib = trim($ex_lib); + + shell()->cd($lib->getSourceDir())->initializeEnv($lib) + ->exec( + "{$env} ./Configure no-shared zlib " . + "--prefix={$lib->getBuildRootPath()} " . + "--libdir={$lib->getLibDir()} " . + "--openssldir={$openssl_dir} " . + "{$zlib_extra}" . + 'enable-pie ' . + 'no-legacy ' . + 'no-tests ' . + "linux-{$arch}" + ) + ->exec('make clean') + ->exec("make -j{$lib->getBuilder()->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"") + ->exec('make install_sw'); + $this->patchPkgConfig($lib); + } + + private function patchPkgConfig(LibraryPackage $pkg): void + { + $pkg->patchPkgconfPrefix(['libssl.pc', 'openssl.pc', 'libcrypto.pc']); + // patch for openssl 3.3.0+ + if (!str_contains($file = FileSystem::readFile("{$pkg->getLibDir()}/pkgconfig/libssl.pc"), 'prefix=')) { + FileSystem::writeFile("{$pkg->getLibDir()}/pkgconfig/libssl.pc", "prefix={$pkg->getBuildRootPath()}\n{$file}"); + } + if (!str_contains($file = FileSystem::readFile("{$pkg->getLibDir()}/pkgconfig/openssl.pc"), 'prefix=')) { + FileSystem::writeFile("{$pkg->getLibDir()}/pkgconfig/openssl.pc", "prefix={$pkg->getBuildRootPath()}\n{$file}"); + } + if (!str_contains($file = FileSystem::readFile("{$pkg->getLibDir()}/pkgconfig/libcrypto.pc"), 'prefix=')) { + FileSystem::writeFile("{$pkg->getLibDir()}/pkgconfig/libcrypto.pc", "prefix={$pkg->getBuildRootPath()}\n{$file}"); + } + FileSystem::replaceFileRegex("{$pkg->getLibDir()}/pkgconfig/libcrypto.pc", '/Libs.private:.*/m', 'Requires.private: zlib'); + FileSystem::replaceFileRegex("{$pkg->getLibDir()}/cmake/OpenSSL/OpenSSLConfig.cmake", '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")'); + } +} diff --git a/src/Package/Library/xz.php b/src/Package/Library/xz.php new file mode 100644 index 000000000..3486d4c17 --- /dev/null +++ b/src/Package/Library/xz.php @@ -0,0 +1,30 @@ +configure( + '--disable-scripts', + '--disable-doc', + '--with-libiconv', + '--bindir=/tmp/xz', // xz binary will corrupt `tar` command, that's really strange. + ) + ->make(); + $lib->patchPkgconfPrefix(['liblzma.pc']); + $lib->patchLaDependencyPrefix(); + } +} diff --git a/src/Package/Library/zlib.php b/src/Package/Library/zlib.php new file mode 100644 index 000000000..8706dfe9b --- /dev/null +++ b/src/Package/Library/zlib.php @@ -0,0 +1,24 @@ +exec("./configure --static --prefix={$lib->getBuildRootPath()}")->make(); + + // Patch pkg-config file + $lib->patchPkgconfPrefix(['zlib.pc'], PKGCONF_PATCH_PREFIX); + } +} diff --git a/src/Package/Library/zstd.php b/src/Package/Library/zstd.php new file mode 100644 index 000000000..ab538358e --- /dev/null +++ b/src/Package/Library/zstd.php @@ -0,0 +1,29 @@ +setBuildDir("{$lib->getSourceDir()}/build/cmake/build") + ->addConfigureArgs( + '-DZSTD_BUILD_STATIC=ON', + '-DZSTD_BUILD_SHARED=OFF', + ) + ->build(); + + $lib->patchPkgconfPrefix(['libzstd.pc'], PKGCONF_PATCH_PREFIX); + } +} diff --git a/src/Package/Target/php.php b/src/Package/Target/php.php index 8cd6e9407..32e8a13b1 100644 --- a/src/Package/Target/php.php +++ b/src/Package/Target/php.php @@ -241,7 +241,6 @@ private function makeStaticExtensionString(PackageInstaller $installer): string { $arg = []; foreach ($installer->getResolvedPackages() as $package) { - /** @var PhpExtensionPackage $package */ if ($package->getType() !== 'php-extension' || !$package instanceof PhpExtensionPackage) { continue; } diff --git a/src/Package/Target/php/unix.php b/src/Package/Target/php/unix.php index 23d465dfb..1ce2ff95a 100644 --- a/src/Package/Target/php/unix.php +++ b/src/Package/Target/php/unix.php @@ -436,7 +436,7 @@ private function processLibphpSoFile(string $libphpSo, PackageInstaller $install */ private function makeVars(PackageInstaller $installer): array { - $config = (new SPCConfigUtil(['libs_only_deps' => true]))->config(array_map(fn ($x) => $x->getName(), $installer->getResolvedPackages())); + $config = new SPCConfigUtil(['libs_only_deps' => true])->config(array_map(fn ($x) => $x->getName(), $installer->getResolvedPackages())); $static = ApplicationContext::get(ToolchainInterface::class)->isStatic() ? '-all-static' : ''; $pie = SystemTarget::getTargetOS() === 'Linux' ? '-pie' : ''; diff --git a/src/SPC/builder/Extension.php b/src/SPC/builder/Extension.php index 925a4c8ef..f5a5d9561 100644 --- a/src/SPC/builder/Extension.php +++ b/src/SPC/builder/Extension.php @@ -542,7 +542,7 @@ public function getLibraryDependencies(bool $recursive = false): array */ protected function getSharedExtensionEnv(): array { - $config = (new SPCConfigUtil($this->builder))->getExtensionConfig($this); + $config = (new SPCConfigUtil($this->builder, ['no_php' => true]))->getExtensionConfig($this); [$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']); $preStatic = PHP_OS_FAMILY === 'Darwin' ? '' : '-Wl,--start-group '; $postStatic = PHP_OS_FAMILY === 'Darwin' ? '' : ' -Wl,--end-group '; diff --git a/src/SPC/builder/extension/excimer.php b/src/SPC/builder/extension/excimer.php deleted file mode 100644 index 03dd8f228..000000000 --- a/src/SPC/builder/extension/excimer.php +++ /dev/null @@ -1,19 +0,0 @@ -source_dir . '/src/php_spx.h', $this->source_dir . '/php_spx.h'); return true; } + + public function getSharedExtensionEnv(): array + { + $env = parent::getSharedExtensionEnv(); + $env['SPX_SHARED_LIBADD'] = $env['LIBS']; + return $env; + } } diff --git a/src/SPC/builder/unix/library/gettext.php b/src/SPC/builder/unix/library/gettext.php index 332e25c96..d383faf77 100644 --- a/src/SPC/builder/unix/library/gettext.php +++ b/src/SPC/builder/unix/library/gettext.php @@ -16,7 +16,11 @@ protected function build(): void ->addConfigureArgs( '--disable-java', '--disable-c++', - '--with-included-gettext', + '--disable-d', + '--disable-rpath', + '--disable-modula2', + '--disable-libasprintf', + '--with-included-libintl', "--with-iconv-prefix={$this->getBuildRootPath()}", ); diff --git a/src/SPC/util/SPCTarget.php b/src/SPC/util/SPCTarget.php index b8f5367ae..037c6d8c3 100644 --- a/src/SPC/util/SPCTarget.php +++ b/src/SPC/util/SPCTarget.php @@ -27,10 +27,10 @@ public static function isStatic(): bool return true; } if (ToolchainManager::getToolchainClass() === GccNativeToolchain::class) { - return PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist(); + return PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist() && !getenv('SPC_MUSL_DYNAMIC'); } if (ToolchainManager::getToolchainClass() === ClangNativeToolchain::class) { - return PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist(); + return PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist() && !getenv('SPC_MUSL_DYNAMIC'); } // if SPC_LIBC is set, it means the target is static, remove it when 3.0 is released if ($target = getenv('SPC_TARGET')) { diff --git a/src/StaticPHP/Artifact/Artifact.php b/src/StaticPHP/Artifact/Artifact.php index b5cf74c00..b3e27371f 100644 --- a/src/StaticPHP/Artifact/Artifact.php +++ b/src/StaticPHP/Artifact/Artifact.php @@ -164,7 +164,14 @@ public function isBinaryExtracted(?string $target_os = null, bool $compare_hash // For selective mode, cannot reliably check extraction status if ($mode === 'selective') { - return false; + // check files existence + foreach ($extract_config['files'] as $target_file) { + $target_file = FileSystem::replacePathVariable($target_file); + if (!file_exists($target_file)) { + return false; + } + } + return true; } // For standalone mode, check directory or file and hash @@ -268,6 +275,19 @@ public function getSourceDir(): string return FileSystem::convertPath(SOURCE_PATH . '/' . $path); } + /** + * Get source build root directory. + * It's only worked when 'source-root' is defined in artifact config. + * Normally it's equal to source dir. + */ + public function getSourceRoot(): string + { + if (isset($this->config['metadata']['source-root'])) { + return $this->getSourceDir() . '/' . ltrim($this->config['metadata']['source-root'], '/'); + } + return $this->getSourceDir(); + } + /** * Get binary extraction directory and mode. * diff --git a/src/StaticPHP/Artifact/ArtifactDownloader.php b/src/StaticPHP/Artifact/ArtifactDownloader.php index b53ddd8a2..b07c56617 100644 --- a/src/StaticPHP/Artifact/ArtifactDownloader.php +++ b/src/StaticPHP/Artifact/ArtifactDownloader.php @@ -312,7 +312,7 @@ public function download(bool $interactive = true): void FileSystem::createDir(DOWNLOAD_PATH); } logger()->info('Downloading' . implode(', ', array_map(fn ($x) => " '{$x->getName()}'", $this->artifacts)) . " with concurrency {$this->parallel} ..."); - // Download artifacts parallely + // Download artifacts parallelly if ($this->parallel > 1) { $this->downloadWithConcurrency(); } else { @@ -438,7 +438,7 @@ private function downloadWithType(Artifact $artifact, int $current, int $total, break; } } - $vvv = ApplicationContext::isDebug() ? "\nIf the problem persists, consider using `-vvv` to enable verbose mode, and disable parallel downloading for more details." : ''; + $vvv = !ApplicationContext::isDebug() ? "\nIf the problem persists, consider using `-vvv` to enable verbose mode, or disable parallel downloading for more details." : ''; throw new DownloaderException("Download artifact '{$artifact->getName()}' failed. Please check your internet connection and try again.{$vvv}"); } diff --git a/src/StaticPHP/Artifact/ArtifactExtractor.php b/src/StaticPHP/Artifact/ArtifactExtractor.php index 93b363823..217a6f54f 100644 --- a/src/StaticPHP/Artifact/ArtifactExtractor.php +++ b/src/StaticPHP/Artifact/ArtifactExtractor.php @@ -247,6 +247,7 @@ protected function extractBinary(Artifact $artifact): int $artifact->emitAfterBinaryExtract($target_path, $platform); logger()->debug("Emitted after-binary-extract hooks for [{$name}]"); + /* @phpstan-ignore-next-line */ if ($hash !== null && $cache_info['cache_type'] !== 'file') { FileSystem::writeFile("{$target_path}/.spc-hash", $hash); } diff --git a/src/StaticPHP/Artifact/Downloader/Type/FileList.php b/src/StaticPHP/Artifact/Downloader/Type/FileList.php index 8290a1cc3..314f0c330 100644 --- a/src/StaticPHP/Artifact/Downloader/Type/FileList.php +++ b/src/StaticPHP/Artifact/Downloader/Type/FileList.php @@ -20,6 +20,7 @@ public function download(string $name, array $config, ArtifactDownloader $downlo throw new DownloaderException("Failed to get {$name} file list from {$config['url']}"); } $versions = []; + logger()->debug('Matched ' . count($matches['version']) . " versions for {$name}"); foreach ($matches['version'] as $i => $version) { $lower = strtolower($version); foreach (['alpha', 'beta', 'rc', 'pre', 'nightly', 'snapshot', 'dev'] as $beta) { diff --git a/src/StaticPHP/Attribute/Package/PatchBeforeBuild.php b/src/StaticPHP/Attribute/Package/PatchBeforeBuild.php new file mode 100644 index 000000000..254e1a6b6 --- /dev/null +++ b/src/StaticPHP/Attribute/Package/PatchBeforeBuild.php @@ -0,0 +1,8 @@ +addArgument('libraries', InputArgument::REQUIRED, 'The library packages will be compiled, comma separated'); + // Builder options + $this->getDefinition()->addOptions([ + new InputOption('with-suggests', ['L', 'E'], null, 'Resolve and install suggested packages as well'), + new InputOption('with-packages', null, InputOption::VALUE_REQUIRED, 'add additional packages to install/build, comma separated', ''), + new InputOption('no-download', null, null, 'Skip downloading artifacts (use existing cached files)'), + ...V2CompatLayer::getLegacyBuildOptions(), + ]); + // Downloader options (with 'dl-' prefix to avoid conflicts) + $this->getDefinition()->addOptions(DownloaderOptions::getConsoleOptions('dl')); } public function handle(): int { $libs = parse_comma_list($this->input->getArgument('libraries')); - $installer = new \StaticPHP\Package\PackageInstaller($this->input->getOptions()); + $installer = new PackageInstaller($this->input->getOptions()); foreach ($libs as $lib) { $installer->addBuildPackage($lib); } diff --git a/src/StaticPHP/Command/Dev/LintConfigCommand.php b/src/StaticPHP/Command/Dev/LintConfigCommand.php new file mode 100644 index 000000000..8149af14d --- /dev/null +++ b/src/StaticPHP/Command/Dev/LintConfigCommand.php @@ -0,0 +1,121 @@ +sortConfigFile($file, 'artifact'); + } + $loaded_pkg_configs = Registry::getLoadedPackageConfigs(); + foreach ($loaded_pkg_configs as $file) { + $this->sortConfigFile($file, 'package'); + } + return static::SUCCESS; + } + + public function artifactSortKey(string $a, string $b): int + { + // sort by predefined order, other not matching keys go to the end alphabetically + $order = ['source', 'source-mirror', 'binary', 'binary-mirror', 'metadata']; + + $pos_a = array_search($a, $order, true); + $pos_b = array_search($b, $order, true); + + // Both in order list + if ($pos_a !== false && $pos_b !== false) { + return $pos_a <=> $pos_b; + } + + // Only $a in order list + if ($pos_a !== false) { + return -1; + } + + // Only $b in order list + if ($pos_b !== false) { + return 1; + } + + // Neither in order list, sort alphabetically + return $a <=> $b; + } + + public function packageSortKey(string $a, string $b): int + { + // sort by predefined order, other not matching keys go to the end alphabetically + $order = ['type', 'artifact', 'depends', 'suggests', 'frameworks']; + + // Handle suffix patterns (e.g., 'depends@unix', 'static-libs@windows') + $base_a = preg_replace('/@(unix|windows|macos|linux|freebsd|bsd)$/', '', $a); + $base_b = preg_replace('/@(unix|windows|macos|linux|freebsd|bsd)$/', '', $b); + + $pos_a = array_search($base_a, $order, true); + $pos_b = array_search($base_b, $order, true); + + // Both in order list + if ($pos_a !== false && $pos_b !== false) { + if ($pos_a === $pos_b) { + // Same base field, sort by suffix + return $a <=> $b; + } + return $pos_a <=> $pos_b; + } + + // Only $a in order list + if ($pos_a !== false) { + return -1; + } + + // Only $b in order list + if ($pos_b !== false) { + return 1; + } + + // Neither in order list, sort alphabetically + return $a <=> $b; + } + + private function sortConfigFile(mixed $file, string $config_type): void + { + // read file content with different extensions + $content = file_get_contents($file); + if ($content === false) { + $this->output->writeln("Failed to read artifact config file: {$file}"); + return; + } + $data = match (pathinfo($file, PATHINFO_EXTENSION)) { + 'json' => json_decode($content, true), + 'yml', 'yaml' => Yaml::parse($content), // skip yaml files for now + default => null, + }; + if (!is_array($data)) { + $this->output->writeln("Invalid JSON format in artifact config file: {$file}"); + return; + } + ksort($data); + foreach ($data as $artifact_name => &$config) { + uksort($config, $config_type === 'artifact' ? [$this, 'artifactSortKey'] : [$this, 'packageSortKey']); + } + unset($config); + $new_content = match (pathinfo($file, PATHINFO_EXTENSION)) { + 'json' => json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n", + 'yml', 'yaml' => Yaml::dump($data, 4, 2), + default => null, + }; + file_put_contents($file, $new_content); + $this->output->writeln("Sorted artifact config file: {$file}"); + } +} diff --git a/src/StaticPHP/Command/Dev/SortConfigCommand.php b/src/StaticPHP/Command/Dev/SortConfigCommand.php deleted file mode 100644 index aa3a9ecdb..000000000 --- a/src/StaticPHP/Command/Dev/SortConfigCommand.php +++ /dev/null @@ -1,49 +0,0 @@ -sortConfigFile($file); - } - $loaded_pkg_configs = Registry::getLoadedPackageConfigs(); - foreach ($loaded_pkg_configs as $file) { - $this->sortConfigFile($file); - } - return static::SUCCESS; - } - - private function sortConfigFile(mixed $file): void - { - $content = file_get_contents($file); - if ($content === false) { - $this->output->writeln("Failed to read artifact config file: {$file}"); - return; - } - $data = json_decode($content, true); - if (!is_array($data)) { - $this->output->writeln("Invalid JSON format in artifact config file: {$file}"); - return; - } - ksort($data); - foreach ($data as $artifact_name => &$config) { - ksort($config); - } - unset($config); - $new_content = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n"; - file_put_contents($file, $new_content); - $this->output->writeln("Sorted artifact config file: {$file}"); - } -} diff --git a/src/StaticPHP/Command/DumpLicenseCommand.php b/src/StaticPHP/Command/DumpLicenseCommand.php new file mode 100644 index 000000000..d90ecbf95 --- /dev/null +++ b/src/StaticPHP/Command/DumpLicenseCommand.php @@ -0,0 +1,147 @@ +addArgument('artifacts', InputArgument::OPTIONAL, 'Specific artifacts to dump licenses, comma separated, e.g "php-src,openssl,curl"'); + + // v2 compatible options + $this->addOption('for-extensions', 'e', InputOption::VALUE_REQUIRED, 'Dump by extensions (automatically includes php-src), e.g "openssl,mbstring"'); + $this->addOption('for-libs', 'l', InputOption::VALUE_REQUIRED, 'Dump by libraries, e.g "openssl,zlib,curl"'); + + // v3 options + $this->addOption('for-packages', 'p', InputOption::VALUE_REQUIRED, 'Dump by packages, e.g "php,libssl,libcurl"'); + $this->addOption('dump-dir', 'd', InputOption::VALUE_REQUIRED, 'Target directory for dumped licenses', BUILD_ROOT_PATH . '/license'); + $this->addOption('without-suggests', null, null, 'Do not include suggested packages when using --for-extensions or --for-packages'); + } + + public function handle(): int + { + $dumper = new LicenseDumper(); + $dump_dir = $this->getOption('dump-dir'); + $artifacts_to_dump = []; + + // Handle direct artifact argument + if ($artifacts = $this->getArgument('artifacts')) { + $artifacts_to_dump = array_merge($artifacts_to_dump, parse_comma_list($artifacts)); + } + + // Handle --for-extensions option + if ($exts = $this->getOption('for-extensions')) { + $artifacts_to_dump = array_merge( + $artifacts_to_dump, + $this->resolveFromExtensions(parse_extension_list($exts)) + ); + } + + // Handle --for-libs option (v2 compat) + if ($libs = $this->getOption('for-libs')) { + $artifacts_to_dump = array_merge( + $artifacts_to_dump, + $this->resolveFromPackages(parse_comma_list($libs)) + ); + } + + // Handle --for-packages option + if ($packages = $this->getOption('for-packages')) { + $artifacts_to_dump = array_merge( + $artifacts_to_dump, + $this->resolveFromPackages(parse_comma_list($packages)) + ); + } + + // Check if any artifacts to dump + if (empty($artifacts_to_dump)) { + $this->output->writeln('No artifacts specified. Use one of:'); + $this->output->writeln(' - Direct argument: dump-license php-src,openssl,curl'); + $this->output->writeln(' - --for-extensions: dump-license --for-extensions=openssl,mbstring'); + $this->output->writeln(' - --for-libs: dump-license --for-libs=openssl,zlib'); + $this->output->writeln(' - --for-packages: dump-license --for-packages=php,libssl'); + return self::FAILURE; + } + + // Deduplicate artifacts + $artifacts_to_dump = array_values(array_unique($artifacts_to_dump)); + + logger()->info('Dumping licenses for ' . count($artifacts_to_dump) . ' artifact(s)'); + logger()->debug('Artifacts: ' . implode(', ', $artifacts_to_dump)); + + // Add artifacts to dumper + $dumper->addArtifacts($artifacts_to_dump); + + // Dump + $success = $dumper->dump($dump_dir); + + if ($success) { + InteractiveTerm::success('Licenses dumped successfully: ' . $dump_dir); + // $this->output->writeln("✓ Successfully dumped licenses to: {$dump_dir}"); + // $this->output->writeln(" Total artifacts: " . count($artifacts_to_dump) . ''); + return self::SUCCESS; + } + + $this->output->writeln('Failed to dump licenses'); + return self::FAILURE; + } + + /** + * Resolve artifacts from extension names. + * + * @param array $extensions Extension names + * @return array Artifact names + */ + private function resolveFromExtensions(array $extensions): array + { + // Convert extension names to package names + $packages = array_map(fn ($ext) => "ext-{$ext}", $extensions); + + // Automatically include php-related artifacts + array_unshift($packages, 'php'); + array_unshift($packages, 'php-micro'); + array_unshift($packages, 'php-embed'); + array_unshift($packages, 'php-fpm'); + + return $this->resolveFromPackages($packages); + } + + /** + * Resolve artifacts from package names. + * + * @param array $packages Package names + * @return array Artifact names + */ + private function resolveFromPackages(array $packages): array + { + $artifacts = []; + $include_suggests = !$this->getOption('without-suggests'); + + // Resolve package dependencies + $resolved_packages = DependencyResolver::resolve($packages, [], $include_suggests); + + foreach ($resolved_packages as $pkg_name) { + try { + $pkg = PackageLoader::getPackage($pkg_name); + if ($artifact = $pkg->getArtifact()) { + $artifacts[] = $artifact->getName(); + } + } catch (\Throwable $e) { + logger()->debug("Package {$pkg_name} has no artifact or failed to load: {$e->getMessage()}"); + } + } + + return array_unique($artifacts); + } +} diff --git a/src/StaticPHP/Config/ArtifactConfig.php b/src/StaticPHP/Config/ArtifactConfig.php index 49abae926..49bfec233 100644 --- a/src/StaticPHP/Config/ArtifactConfig.php +++ b/src/StaticPHP/Config/ArtifactConfig.php @@ -6,6 +6,7 @@ use StaticPHP\Exception\WrongUsageException; use StaticPHP\Registry\Registry; +use Symfony\Component\Yaml\Yaml; class ArtifactConfig { @@ -40,7 +41,11 @@ public static function loadFromFile(string $file, string $registry_name): string if ($content === false) { throw new WrongUsageException("Failed to read artifact config file: {$file}"); } - $data = json_decode($content, true); + $data = match (pathinfo($file, PATHINFO_EXTENSION)) { + 'json' => json_decode($content, true), + 'yml', 'yaml' => Yaml::parse($content), + default => throw new WrongUsageException("Unsupported artifact config file format: {$file}"), + }; if (!is_array($data)) { throw new WrongUsageException("Invalid JSON format in artifact config file: {$file}"); } @@ -72,4 +77,19 @@ public static function get(string $artifact_name): ?array { return self::$artifact_configs[$artifact_name] ?? null; } + + /** + * Register an inline artifact configuration. + * Used when artifact is defined inline within a package configuration. + * + * @param string $artifact_name Artifact name (usually same as package name) + * @param array $config Artifact configuration + * @param string $registry_name Registry name + * @param string $source_info Source info for debugging + */ + public static function registerInlineArtifact(string $artifact_name, array $config, string $registry_name, string $source_info = 'inline'): void + { + self::$artifact_configs[$artifact_name] = $config; + Registry::_bindArtifactConfigFile($artifact_name, $registry_name, $source_info); + } } diff --git a/src/StaticPHP/Config/ConfigValidator.php b/src/StaticPHP/Config/ConfigValidator.php index b32f41063..1f3d5b9d9 100644 --- a/src/StaticPHP/Config/ConfigValidator.php +++ b/src/StaticPHP/Config/ConfigValidator.php @@ -18,7 +18,7 @@ class ConfigValidator 'type' => ConfigType::STRING, 'depends' => ConfigType::LIST_ARRAY, // @ 'suggests' => ConfigType::LIST_ARRAY, // @ - 'artifact' => ConfigType::STRING, + 'artifact' => [self::class, 'validateArtifactField'], // STRING or OBJECT 'license' => [ConfigType::class, 'validateLicenseField'], 'lang' => ConfigType::STRING, 'frameworks' => ConfigType::LIST_ARRAY, // @ @@ -102,7 +102,14 @@ public static function validateAndLintArtifacts(string $config_file_name, mixed if (!is_array($data)) { throw new ValidationException("{$config_file_name} is broken"); } + + // Define allowed artifact fields + $allowed_artifact_fields = ['source', 'source-mirror', 'binary', 'binary-mirror', 'metadata']; + foreach ($data as $name => $artifact) { + // First pass: validate unknown fields + self::validateNoInvalidFields('artifact', $name, $artifact, $allowed_artifact_fields); + foreach ($artifact as $k => $v) { // check source field if ($k === 'source' || $k === 'source-mirror') { @@ -202,6 +209,11 @@ public static function validateAndLintPackages(string $config_file_name, mixed & throw new ValidationException("Package [{$name}] in {$config_file_name} of type '{$pkg['type']}' must have an 'artifact' field"); } + // validate and lint inline artifact object if present + if (isset($pkg['artifact']) && is_array($pkg['artifact'])) { + self::validateAndLintInlineArtifact($name, $data[$name]['artifact']); + } + // check if "php-extension" package has php-extension specific fields and validate inside if ($pkg['type'] === 'php-extension') { self::validatePhpExtensionFields($name, $pkg); @@ -234,6 +246,19 @@ public static function validatePlatformString(string $platform): void } } + /** + * Validate artifact field - can be string (reference) or object (inline). + * + * @param mixed $value Field value + */ + public static function validateArtifactField(mixed $value): bool + { + if (!is_string($value) && !is_assoc_array($value)) { + return false; + } + return true; + } + /** * Validate an artifact download object field. * @@ -373,4 +398,19 @@ private static function validateNoInvalidFields(string $config_type, int|string } } } + + /** + * Validate and lint inline artifact object structure. + * + * @param string $pkg_name Package name + * @param array $artifact Inline artifact configuration (passed by reference to apply linting) + */ + private static function validateAndLintInlineArtifact(string $pkg_name, array &$artifact): void + { + // Validate and lint as if it's a standalone artifact + $temp_data = [$pkg_name => $artifact]; + self::validateAndLintArtifacts("inline artifact in package '{$pkg_name}'", $temp_data); + // Write back the linted artifact configuration + $artifact = $temp_data[$pkg_name]; + } } diff --git a/src/StaticPHP/Config/PackageConfig.php b/src/StaticPHP/Config/PackageConfig.php index 56ef7ab1c..bd9786c3d 100644 --- a/src/StaticPHP/Config/PackageConfig.php +++ b/src/StaticPHP/Config/PackageConfig.php @@ -7,6 +7,7 @@ use StaticPHP\Exception\WrongUsageException; use StaticPHP\Registry\Registry; use StaticPHP\Runtime\SystemTarget; +use Symfony\Component\Yaml\Yaml; class PackageConfig { @@ -22,7 +23,7 @@ public static function loadFromDir(string $dir, string $registry_name): array throw new WrongUsageException("Directory {$dir} does not exist, cannot load pkg.json config."); } $loaded = []; - $files = glob("{$dir}/pkg.*.json"); + $files = glob("{$dir}/*"); if (is_array($files)) { foreach ($files as $file) { self::loadFromFile($file, $registry_name); @@ -47,18 +48,49 @@ public static function loadFromFile(string $file, string $registry_name): string if ($content === false) { throw new WrongUsageException("Failed to read package config file: {$file}"); } - $data = json_decode($content, true); - if (!is_array($data)) { - throw new WrongUsageException("Invalid JSON format in package config file: {$file}"); - } + // judge extension + $data = match (pathinfo($file, PATHINFO_EXTENSION)) { + 'json' => json_decode($content, true), + 'yml', 'yaml' => Yaml::parse($content), + default => throw new WrongUsageException("Unsupported package config file format: {$file}"), + }; ConfigValidator::validateAndLintPackages(basename($file), $data); foreach ($data as $pkg_name => $config) { self::$package_configs[$pkg_name] = $config; Registry::_bindPackageConfigFile($pkg_name, $registry_name, $file); + + // Register inline artifact if present + if (isset($config['artifact']) && is_array($config['artifact'])) { + ArtifactConfig::registerInlineArtifact( + $pkg_name, + $config['artifact'], + $registry_name, + "inline in {$file}" + ); + } } return $file; } + public static function loadFromArray(array $data, string $registry_name): void + { + ConfigValidator::validateAndLintPackages('array_input', $data); + foreach ($data as $pkg_name => $config) { + self::$package_configs[$pkg_name] = $config; + Registry::_bindPackageConfigFile($pkg_name, $registry_name, 'array_input'); + + // Register inline artifact if present + if (isset($config['artifact']) && is_array($config['artifact'])) { + ArtifactConfig::registerInlineArtifact( + $pkg_name, + $config['artifact'], + $registry_name, + 'inline in array_input' + ); + } + } + } + /** * Check if a package configuration exists. */ diff --git a/src/StaticPHP/ConsoleApplication.php b/src/StaticPHP/ConsoleApplication.php index 9e37698c6..e65617053 100644 --- a/src/StaticPHP/ConsoleApplication.php +++ b/src/StaticPHP/ConsoleApplication.php @@ -8,10 +8,11 @@ use StaticPHP\Command\BuildTargetCommand; use StaticPHP\Command\Dev\EnvCommand; use StaticPHP\Command\Dev\IsInstalledCommand; +use StaticPHP\Command\Dev\LintConfigCommand; use StaticPHP\Command\Dev\ShellCommand; -use StaticPHP\Command\Dev\SortConfigCommand; use StaticPHP\Command\DoctorCommand; use StaticPHP\Command\DownloadCommand; +use StaticPHP\Command\DumpLicenseCommand; use StaticPHP\Command\ExtractCommand; use StaticPHP\Command\InstallPackageCommand; use StaticPHP\Command\SPCConfigCommand; @@ -55,12 +56,13 @@ public function __construct() new BuildLibsCommand(), new ExtractCommand(), new SPCConfigCommand(), + new DumpLicenseCommand(), // dev commands new ShellCommand(), new IsInstalledCommand(), new EnvCommand(), - new SortConfigCommand(), + new LintConfigCommand(), ]); // add additional commands from registries diff --git a/src/StaticPHP/DI/CallbackInvoker.php b/src/StaticPHP/DI/CallbackInvoker.php index 0d77f7aab..5b70d7b2a 100644 --- a/src/StaticPHP/DI/CallbackInvoker.php +++ b/src/StaticPHP/DI/CallbackInvoker.php @@ -5,6 +5,7 @@ namespace StaticPHP\DI; use DI\Container; +use StaticPHP\Exception\SkipException; use StaticPHP\Exception\SPCInternalException; /** @@ -92,7 +93,12 @@ public function invoke(callable $callback, array $context = []): mixed ); } - return $callback(...$args); + try { + return $callback(...$args); + } catch (SkipException $e) { + logger()->debug("Skipped invocation: {$e->getMessage()}"); + return null; + } } /** diff --git a/src/StaticPHP/Doctor/Doctor.php b/src/StaticPHP/Doctor/Doctor.php index d86e42ac2..36db37ae8 100644 --- a/src/StaticPHP/Doctor/Doctor.php +++ b/src/StaticPHP/Doctor/Doctor.php @@ -146,7 +146,8 @@ private function getValidCheckList(): iterable foreach (DoctorLoader::getDoctorItems() as [$item, $optional]) { /* @var CheckItem $item */ // optional check - if ($optional !== null && !call_user_func($optional)) { + /* @phpstan-ignore-next-line */ + if (is_callable($optional) && !call_user_func($optional)) { continue; // skip this when the optional check is false } // limit_os check diff --git a/src/StaticPHP/Doctor/Item/LinuxMuslCheck.php b/src/StaticPHP/Doctor/Item/LinuxMuslCheck.php index 4d7a86bee..b64df85b2 100644 --- a/src/StaticPHP/Doctor/Item/LinuxMuslCheck.php +++ b/src/StaticPHP/Doctor/Item/LinuxMuslCheck.php @@ -27,25 +27,25 @@ class LinuxMuslCheck public static function optionalCheck(): bool { $toolchain = ApplicationContext::get(ToolchainInterface::class); - return $toolchain instanceof MuslToolchain || $toolchain instanceof ZigToolchain && !LinuxUtil::isMuslDist(); + return $toolchain instanceof MuslToolchain || $toolchain instanceof ZigToolchain && !LinuxUtil::isMuslDist() && !str_contains(getenv('SPC_TARGET') ?: '', 'gnu'); } /** @noinspection PhpUnused */ #[CheckItem('if musl-wrapper is installed', limit_os: 'Linux', level: 800)] - public function checkMusl(): CheckResult + public function checkMusl(): ?CheckResult { $musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m')); if (file_exists($musl_wrapper_lib) && (file_exists('/usr/local/musl/lib/libc.a') || getenv('SPC_TOOLCHAIN') === ZigToolchain::class)) { - return CheckResult::ok(); + return null; } return CheckResult::fail('musl-wrapper is not installed on your system', 'fix-musl-wrapper'); } #[CheckItem('if musl-cross-make is installed', limit_os: 'Linux', level: 799)] - public function checkMuslCrossMake(): CheckResult + public function checkMuslCrossMake(): ?CheckResult { if (getenv('SPC_TOOLCHAIN') === ZigToolchain::class && !LinuxUtil::isMuslDist()) { - return CheckResult::ok(); + return null; } $arch = arch2gnu(php_uname('m')); $cross_compile_lib = "/usr/local/musl/{$arch}-linux-musl/lib/libc.a"; diff --git a/src/StaticPHP/Doctor/Item/LinuxToolCheck.php b/src/StaticPHP/Doctor/Item/LinuxToolCheck.php index 09161c74e..f4cdc3111 100644 --- a/src/StaticPHP/Doctor/Item/LinuxToolCheck.php +++ b/src/StaticPHP/Doctor/Item/LinuxToolCheck.php @@ -19,7 +19,6 @@ class LinuxToolCheck 'bzip2', 'cmake', 'gcc', 'g++', 'patch', 'binutils-gold', 'libtoolize', 'which', - 'patchelf', ]; public const TOOLS_DEBIAN = [ @@ -28,7 +27,6 @@ class LinuxToolCheck 'tar', 'unzip', 'gzip', 'gcc', 'g++', 'bzip2', 'cmake', 'patch', 'xz', 'libtoolize', 'which', - 'patchelf', ]; public const TOOLS_RHEL = [ @@ -36,8 +34,7 @@ class LinuxToolCheck 'git', 'autoconf', 'automake', 'tar', 'unzip', 'gzip', 'gcc', 'g++', 'bzip2', 'cmake', 'patch', 'which', - 'xz', 'libtool', 'gettext-devel', - 'patchelf', 'file', + 'xz', 'libtool', 'gettext-devel', 'file', ]; public const TOOLS_ARCH = [ diff --git a/src/StaticPHP/Exception/SkipException.php b/src/StaticPHP/Exception/SkipException.php new file mode 100644 index 000000000..af25b6da5 --- /dev/null +++ b/src/StaticPHP/Exception/SkipException.php @@ -0,0 +1,7 @@ + true, 'absolute_libs' => true]); + $res = $config->config([$this->getName()]); + return $res['libs']; + } + /** * Get extra LIBS for current package. * You need to define the environment variable in the format of {LIBRARY_NAME}_LIBS diff --git a/src/StaticPHP/Package/Package.php b/src/StaticPHP/Package/Package.php index aa8ab6f00..cd4f38409 100644 --- a/src/StaticPHP/Package/Package.php +++ b/src/StaticPHP/Package/Package.php @@ -135,6 +135,22 @@ public function hasBuildFunctionForCurrentOS(): bool return isset($this->build_functions[PHP_OS_FAMILY]); } + /** + * Get the PackageBuilder instance for this package. + */ + public function getBuilder(): PackageBuilder + { + return ApplicationContext::get(PackageBuilder::class); + } + + /** + * Get the PackageInstaller instance for this package. + */ + public function getInstaller(): PackageInstaller + { + return ApplicationContext::get(PackageInstaller::class); + } + /** * Get the name of the package. */ @@ -159,8 +175,21 @@ public function getType(): string public function getArtifact(): ?Artifact { // find config - $artifact_name = PackageConfig::get($this->name, 'artifact'); - return $artifact_name !== null ? ArtifactLoader::getArtifactInstance($artifact_name) : null; + $artifact_field = PackageConfig::get($this->name, 'artifact'); + + if ($artifact_field === null) { + return null; + } + + if (is_string($artifact_field)) { + return ArtifactLoader::getArtifactInstance($artifact_field); + } + + if (is_array($artifact_field)) { + return ArtifactLoader::getArtifactInstance($this->name); + } + + return null; } /** @@ -183,6 +212,19 @@ public function getSourceDir(): string throw new SPCInternalException("Source directory for package {$this->name} is not available because the source artifact is missing."); } + /** + * Get source build root directory. + * It's only worked when 'source-root' is defined in artifact config. + * Normally it's equal to source dir. + */ + public function getSourceRoot(): string + { + if (($artifact = $this->getArtifact()) && $artifact->hasSource()) { + return $artifact->getSourceRoot(); + } + throw new SPCInternalException("Source root for package {$this->name} is not available because the source artifact is missing."); + } + /** * Check if the package has a binary available for current OS and architecture. */ diff --git a/src/StaticPHP/Package/PackageCallbacksTrait.php b/src/StaticPHP/Package/PackageCallbacksTrait.php index 96ad039ee..6c6b87382 100644 --- a/src/StaticPHP/Package/PackageCallbacksTrait.php +++ b/src/StaticPHP/Package/PackageCallbacksTrait.php @@ -16,7 +16,7 @@ trait PackageCallbacksTrait protected mixed $validate_callback = null; - protected mixed $patch_before_build_callback = null; + protected mixed $patch_before_build_callbacks = null; public function setInfoCallback(callable $callback): void { @@ -48,9 +48,9 @@ public function setValidateCallback(callable $callback): void $this->validate_callback = $callback; } - public function setPatchBeforeBuildCallback(callable $callback): void + public function addPatchBeforeBuildCallback(callable $callback): void { - $this->patch_before_build_callback = $callback; + $this->patch_before_build_callbacks[] = $callback; } public function patchBeforeBuild(): void @@ -58,16 +58,18 @@ public function patchBeforeBuild(): void if (file_exists("{$this->getSourceDir()}/.spc-patched")) { return; } - if ($this->patch_before_build_callback === null) { + if ($this->patch_before_build_callbacks === null) { return; } // Use CallbackInvoker with current package as context - $ret = ApplicationContext::invoke($this->patch_before_build_callback, [ - Package::class => $this, - static::class => $this, - ]); - if ($ret === true) { - FileSystem::writeFile("{$this->getSourceDir()}/.spc-patched", 'PATCHED!!!'); + foreach ($this->patch_before_build_callbacks as $callback) { + $ret = ApplicationContext::invoke($callback, [ + Package::class => $this, + static::class => $this, + ]); + if ($ret === true) { + FileSystem::writeFile("{$this->getSourceDir()}/.spc-patched", 'PATCHED!!!'); + } } } diff --git a/src/StaticPHP/Package/PackageInstaller.php b/src/StaticPHP/Package/PackageInstaller.php index ae3b7346a..2469159af 100644 --- a/src/StaticPHP/Package/PackageInstaller.php +++ b/src/StaticPHP/Package/PackageInstaller.php @@ -17,6 +17,7 @@ use StaticPHP\Util\FileSystem; use StaticPHP\Util\GlobalEnvManager; use StaticPHP\Util\InteractiveTerm; +use StaticPHP\Util\LicenseDumper; use StaticPHP\Util\V2CompatLayer; use ZM\Logger\ConsoleColor; @@ -208,6 +209,11 @@ public function run(bool $interactive = true, bool $disable_delay_msg = false): } } } + + $this->dumpLicenseFiles($this->packages); + if ($interactive) { + InteractiveTerm::success('Exported package licenses', true); + } } public function isBuildPackage(Package|string $package): bool @@ -253,7 +259,7 @@ public function isPackageInstalled(Package|string $package_name): bool if ($this->isBuildPackage($package)) { return $package->isInstalled(); } - if ($package instanceof LibraryPackage && $package->getArtifact()->shouldUseBinary()) { + if ($package->getArtifact() !== null && $package->getArtifact()->shouldUseBinary()) { $artifact = $package->getArtifact(); return $artifact->isBinaryExtracted(); } @@ -402,11 +408,79 @@ public function installBinary(Package $package): int return SPC_STATUS_INSTALLED; } + /** + * @internal internally calling only, for users, please use specific getter, such as 'getLibraryPackage', 'getTaretPackage', etc + * @param string $package_name Package name + */ public function getPackage(string $package_name): ?Package { return $this->packages[$package_name] ?? null; } + /** + * Get a library package by name. + * + * @param string $package_name Package name + * @return null|LibraryPackage The library package instance or null if not found + */ + public function getLibraryPackage(string $package_name): ?LibraryPackage + { + $pkg = $this->getPackage($package_name); + if ($pkg instanceof LibraryPackage) { + return $pkg; + } + return null; + } + + /** + * Get a target package by name. + * + * @param string $package_name Package name + * @return null|TargetPackage The target package instance or null if not found + */ + public function getTargetPackage(string $package_name): ?TargetPackage + { + $pkg = $this->getPackage($package_name); + if ($pkg instanceof TargetPackage) { + return $pkg; + } + return null; + } + + /** + * Get a PHP extension by name. + * + * @param string $package_or_ext_name Extension name + * @return null|PhpExtensionPackage The target package instance or null if not found + */ + public function getPhpExtensionPackage(string $package_or_ext_name): ?PhpExtensionPackage + { + $pkg = $this->getPackage($package_or_ext_name); + if ($pkg instanceof PhpExtensionPackage) { + return $pkg; + } + $pkg = $this->getPackage("ext-{$package_or_ext_name}"); + if ($pkg instanceof PhpExtensionPackage) { + return $pkg; + } + return null; + } + + /** + * @param Package[] $packages + */ + private function dumpLicenseFiles(array $packages): void + { + $dumper = new LicenseDumper(); + foreach ($packages as $package) { + $artifact = $package->getArtifact(); + if ($artifact !== null) { + $dumper->addArtifacts([$artifact->getName()]); + } + } + $dumper->dump(BUILD_ROOT_PATH . '/license'); + } + /** * Validate that a package has required artifacts. */ diff --git a/src/StaticPHP/Registry/PackageLoader.php b/src/StaticPHP/Registry/PackageLoader.php index 29ce0a96b..424363eb3 100644 --- a/src/StaticPHP/Registry/PackageLoader.php +++ b/src/StaticPHP/Registry/PackageLoader.php @@ -12,6 +12,7 @@ use StaticPHP\Attribute\Package\Info; use StaticPHP\Attribute\Package\InitPackage; use StaticPHP\Attribute\Package\Library; +use StaticPHP\Attribute\Package\PatchBeforeBuild; use StaticPHP\Attribute\Package\ResolveBuild; use StaticPHP\Attribute\Package\Stage; use StaticPHP\Attribute\Package\Target; @@ -168,7 +169,7 @@ public static function loadFromClass(mixed $class): void } } - $pkg = self::$packages[$attribute_instance->name]; + $pkg = self::$packages[$attribute_instance->name] ?? null; // Use the package instance if it's a Package subclass, otherwise create a new instance $instance_class = is_a($class_name, Package::class, true) ? $pkg : $refClass->newInstance(); @@ -183,7 +184,7 @@ public static function loadFromClass(mixed $class): void if (!in_array($package_type, $pkg_type_attr, true)) { throw new RegistryException("Package [{$attribute_instance->name}] type mismatch: config type is [{$package_type}], but attribute type is [" . implode('|', $pkg_type_attr) . '].'); } - if ($pkg !== null && !PackageConfig::isPackageExists($pkg->getName())) { + if ($pkg instanceof Package && !PackageConfig::isPackageExists($pkg->getName())) { throw new RegistryException("Package [{$pkg->getName()}] config not found for class {$class}"); } @@ -196,6 +197,8 @@ public static function loadFromClass(mixed $class): void match ($method_attribute->getName()) { // #[BuildFor(PHP_OS_FAMILY)] BuildFor::class => self::addBuildFunction($pkg, $method_instance, [$instance_class, $method->getName()]), + // #[BeforeBuild] + PatchBeforeBuild::class => self::addPatchBeforeBuildFunction($pkg, [$instance_class, $method->getName()]), // #[CustomPhpConfigureArg(PHP_OS_FAMILY)] CustomPhpConfigureArg::class => self::bindCustomPhpConfigureArg($pkg, $method_attribute->newInstance(), [$instance_class, $method->getName()]), // #[Stage('stage_name')] @@ -332,6 +335,11 @@ private static function addBuildFunction(Package $pkg, object $attr, callable $f $pkg->addBuildFunction($attr->os, $fn); } + private static function addPatchBeforeBuildFunction(Package $pkg, callable $fn): void + { + $pkg->addPatchBeforeBuildCallback($fn); + } + private static function addStage(\ReflectionMethod $method, Package $pkg, object $instance_class, object $method_instance): void { $name = $method_instance->function; @@ -347,7 +355,7 @@ private static function addBeforeStage(\ReflectionMethod $method, ?Package $pkg, $stage = $method_instance->stage; $stage = match (true) { is_string($stage) => $stage, - is_array($stage) && count($stage) === 2 => $stage[1], + count($stage) === 2 => $stage[1], default => throw new RegistryException('Invalid stage definition in BeforeStage attribute.'), }; if ($method_instance->package_name === '' && $pkg === null) { diff --git a/src/StaticPHP/Registry/Registry.php b/src/StaticPHP/Registry/Registry.php index e464ed471..68aa766d2 100644 --- a/src/StaticPHP/Registry/Registry.php +++ b/src/StaticPHP/Registry/Registry.php @@ -13,6 +13,8 @@ class Registry { + private static ?string $current_registry_name = null; + /** @var string[] List of loaded registries */ private static array $loaded_registries = []; @@ -35,7 +37,7 @@ class Registry public static function getRegistryConfig(?string $registry_name = null): array { if ($registry_name === null && spc_mode(SPC_MODE_SOURCE)) { - return self::$registry_configs['internal']; + return self::$registry_configs['core']; } if ($registry_name !== null && isset(self::$registry_configs[$registry_name])) { return self::$registry_configs[$registry_name]; @@ -83,6 +85,8 @@ public static function loadRegistry(string $registry_file, bool $auto_require = logger()->debug("Loading registry '{$registry_name}' from file: {$registry_file}"); + self::$current_registry_name = $registry_name; + // Load composer autoload if specified (for external registries with their own dependencies) if (isset($data['autoload']) && is_string($data['autoload'])) { $autoload_path = FileSystem::fullpath($data['autoload'], dirname($registry_file)); @@ -94,24 +98,6 @@ public static function loadRegistry(string $registry_file, bool $auto_require = } } - // load doctor items from PSR-4 directories - if (isset($data['doctor']['psr-4']) && is_assoc_array($data['doctor']['psr-4'])) { - foreach ($data['doctor']['psr-4'] as $namespace => $path) { - $path = FileSystem::fullpath($path, dirname($registry_file)); - DoctorLoader::loadFromPsr4Dir($path, $namespace, $auto_require); - } - } - - // load doctor items from specific classes - // Supports both array format ["ClassName"] and map format {"ClassName": "path/to/file.php"} - if (isset($data['doctor']['classes']) && is_array($data['doctor']['classes'])) { - foreach ($data['doctor']['classes'] as $key => $value) { - [$class, $file] = self::parseClassEntry($key, $value); - self::requireClassFile($class, $file, dirname($registry_file), $auto_require); - DoctorLoader::loadFromClass($class); - } - } - // load package configs if (isset($data['package']['config']) && is_array($data['package']['config'])) { foreach ($data['package']['config'] as $path) { @@ -136,6 +122,24 @@ public static function loadRegistry(string $registry_file, bool $auto_require = } } + // load doctor items from PSR-4 directories + if (isset($data['doctor']['psr-4']) && is_assoc_array($data['doctor']['psr-4'])) { + foreach ($data['doctor']['psr-4'] as $namespace => $path) { + $path = FileSystem::fullpath($path, dirname($registry_file)); + DoctorLoader::loadFromPsr4Dir($path, $namespace, $auto_require); + } + } + + // load doctor items from specific classes + // Supports both array format ["ClassName"] and map format {"ClassName": "path/to/file.php"} + if (isset($data['doctor']['classes']) && is_array($data['doctor']['classes'])) { + foreach ($data['doctor']['classes'] as $key => $value) { + [$class, $file] = self::parseClassEntry($key, $value); + self::requireClassFile($class, $file, dirname($registry_file), $auto_require); + DoctorLoader::loadFromClass($class); + } + } + // load packages from PSR-4 directories if (isset($data['package']['psr-4']) && is_assoc_array($data['package']['psr-4'])) { foreach ($data['package']['psr-4'] as $namespace => $path) { @@ -193,6 +197,7 @@ public static function loadRegistry(string $registry_file, bool $auto_require = } ConsoleApplication::_addAdditionalCommands($instances); } + self::$current_registry_name = null; } /** @@ -300,6 +305,11 @@ public static function getLoadedArtifactConfigs(): array return self::$loaded_artifact_configs; } + public static function getCurrentRegistryName(): ?string + { + return self::$current_registry_name; + } + /** * Parse a class entry from the classes array. * Supports two formats: diff --git a/src/StaticPHP/Runtime/Executor/UnixAutoconfExecutor.php b/src/StaticPHP/Runtime/Executor/UnixAutoconfExecutor.php index e75a7ed06..41bc6e784 100644 --- a/src/StaticPHP/Runtime/Executor/UnixAutoconfExecutor.php +++ b/src/StaticPHP/Runtime/Executor/UnixAutoconfExecutor.php @@ -169,7 +169,7 @@ private function getDefaultConfigureArgs(): array */ private function initShell(): void { - $this->shell = shell()->cd($this->package->getSourceDir())->initializeEnv($this->package)->appendEnv([ + $this->shell = shell()->cd($this->package->getSourceRoot())->initializeEnv($this->package)->appendEnv([ 'CFLAGS' => "-I{$this->package->getIncludeDir()}", 'CXXFLAGS' => "-I{$this->package->getIncludeDir()}", 'LDFLAGS' => "-L{$this->package->getLibDir()}", @@ -185,12 +185,12 @@ private function seekLogFileOnException(mixed $callable): static $callable(); return $this; } catch (SPCException $e) { - if (file_exists("{$this->package->getSourceDir()}/config.log")) { - logger()->debug("Config log file found: {$this->package->getSourceDir()}/config.log"); + if (file_exists("{$this->package->getSourceRoot()}/config.log")) { + logger()->debug("Config log file found: {$this->package->getSourceRoot()}/config.log"); $log_file = "lib.{$this->package->getName()}.console.log"; logger()->debug('Saved config log file to: ' . SPC_LOGS_DIR . "/{$log_file}"); $e->addExtraLogFile("{$this->package->getName()} library config.log", $log_file); - copy("{$this->package->getSourceDir()}/config.log", SPC_LOGS_DIR . "/{$log_file}"); + copy("{$this->package->getSourceRoot()}/config.log", SPC_LOGS_DIR . "/{$log_file}"); } throw $e; } diff --git a/src/StaticPHP/Runtime/Executor/UnixCMakeExecutor.php b/src/StaticPHP/Runtime/Executor/UnixCMakeExecutor.php index 107aac116..3d628b894 100644 --- a/src/StaticPHP/Runtime/Executor/UnixCMakeExecutor.php +++ b/src/StaticPHP/Runtime/Executor/UnixCMakeExecutor.php @@ -233,7 +233,7 @@ private function getDefaultCMakeArgs(): array private function initBuildDir(): void { if ($this->build_dir === null) { - $this->build_dir = "{$this->package->getSourceDir()}/build"; + $this->build_dir = "{$this->package->getSourceRoot()}/build"; } } @@ -295,7 +295,7 @@ private function makeCmakeToolchainFile(): string */ private function initShell(): void { - $this->shell = shell()->cd($this->package->getSourceDir())->initializeEnv($this->package)->appendEnv([ + $this->shell = shell()->cd($this->package->getSourceRoot())->initializeEnv($this->package)->appendEnv([ 'CFLAGS' => "-I{$this->package->getIncludeDir()}", 'CXXFLAGS' => "-I{$this->package->getIncludeDir()}", 'LDFLAGS' => "-L{$this->package->getLibDir()}", diff --git a/src/StaticPHP/Toolchain/ClangNativeToolchain.php b/src/StaticPHP/Toolchain/ClangNativeToolchain.php index c34e619cd..27ae2b65b 100644 --- a/src/StaticPHP/Toolchain/ClangNativeToolchain.php +++ b/src/StaticPHP/Toolchain/ClangNativeToolchain.php @@ -18,10 +18,10 @@ class ClangNativeToolchain implements UnixToolchainInterface { public function initEnv(): void { - GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=clang'); - GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=clang++'); - GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=ar'); - GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld'); + GlobalEnvManager::putenv('SPC_DEFAULT_CC=clang'); + GlobalEnvManager::putenv('SPC_DEFAULT_CXX=clang++'); + GlobalEnvManager::putenv('SPC_DEFAULT_AR=ar'); + GlobalEnvManager::putenv('SPC_DEFAULT_LD=ld'); } public function afterInit(): void @@ -52,6 +52,6 @@ public function getCompilerInfo(): ?string public function isStatic(): bool { - return PHP_OS_FAMILY === 'Linux' && LinuxUtil::isMuslDist(); + return PHP_OS_FAMILY === 'Linux' && LinuxUtil::isMuslDist() && !getenv('SPC_MUSL_DYNAMIC'); } } diff --git a/src/StaticPHP/Toolchain/GccNativeToolchain.php b/src/StaticPHP/Toolchain/GccNativeToolchain.php index 92b82892e..7c339e69e 100644 --- a/src/StaticPHP/Toolchain/GccNativeToolchain.php +++ b/src/StaticPHP/Toolchain/GccNativeToolchain.php @@ -15,10 +15,10 @@ class GccNativeToolchain implements UnixToolchainInterface { public function initEnv(): void { - GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=gcc'); - GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=g++'); - GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=ar'); - GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld'); + GlobalEnvManager::putenv('SPC_DEFAULT_CC=gcc'); + GlobalEnvManager::putenv('SPC_DEFAULT_CXX=g++'); + GlobalEnvManager::putenv('SPC_DEFAULT_AR=ar'); + GlobalEnvManager::putenv('SPC_DEFAULT_LD=ld'); } public function afterInit(): void @@ -49,6 +49,6 @@ public function getCompilerInfo(): ?string public function isStatic(): bool { - return PHP_OS_FAMILY === 'Linux' && LinuxUtil::isMuslDist(); + return PHP_OS_FAMILY === 'Linux' && LinuxUtil::isMuslDist() && !getenv('SPC_MUSL_DYNAMIC'); } } diff --git a/src/StaticPHP/Toolchain/MuslToolchain.php b/src/StaticPHP/Toolchain/MuslToolchain.php index c7b39dbf6..6963a3645 100644 --- a/src/StaticPHP/Toolchain/MuslToolchain.php +++ b/src/StaticPHP/Toolchain/MuslToolchain.php @@ -14,10 +14,10 @@ public function initEnv(): void { $arch = getenv('GNU_ARCH'); // Set environment variables for musl toolchain - GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_CC={$arch}-linux-musl-gcc"); - GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_CXX={$arch}-linux-musl-g++"); - GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_AR={$arch}-linux-musl-ar"); - GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_LD={$arch}-linux-musl-ld"); + GlobalEnvManager::putenv("SPC_DEFAULT_CC={$arch}-linux-musl-gcc"); + GlobalEnvManager::putenv("SPC_DEFAULT_CXX={$arch}-linux-musl-g++"); + GlobalEnvManager::putenv("SPC_DEFAULT_AR={$arch}-linux-musl-ar"); + GlobalEnvManager::putenv("SPC_DEFAULT_LD={$arch}-linux-musl-ld"); GlobalEnvManager::addPathIfNotExists('/usr/local/musl/bin'); GlobalEnvManager::addPathIfNotExists("/usr/local/musl/{$arch}-linux-musl/bin"); @@ -40,7 +40,7 @@ public function afterInit(): void public function getCompilerInfo(): ?string { - $compiler = getenv('CC') ?: getenv('SPC_LINUX_DEFAULT_CC'); + $compiler = getenv('CC') ?: getenv('SPC_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)) { diff --git a/src/StaticPHP/Util/DependencyResolver.php b/src/StaticPHP/Util/DependencyResolver.php index defb00ba2..b03f80892 100644 --- a/src/StaticPHP/Util/DependencyResolver.php +++ b/src/StaticPHP/Util/DependencyResolver.php @@ -55,6 +55,58 @@ public static function resolve(array $packages, array $dependency_overrides = [] return $resolved; } + /** + * Get all dependencies of a specific package within a resolved package set. + * This is useful when you need to get build flags for a specific library and its deps. + * + * The method will only include dependencies that exist in the resolved set, + * which properly handles optional dependencies (suggests) - only those that + * were actually resolved will be included. + * + * @param string $package_name The package to get dependencies for + * @param string[] $resolved_packages The resolved package list (from resolve()) + * @param bool $include_suggests Whether to include suggests that are in resolved set + * @return string[] Dependencies of the package (NOT including itself), ordered for building + */ + public static function getSubDependencies(string $package_name, array $resolved_packages, bool $include_suggests = false): array + { + // Create a lookup set for O(1) membership check + $resolved_set = array_flip($resolved_packages); + + // Verify the target package is in the resolved set + if (!isset($resolved_set[$package_name])) { + return []; + } + + // Build dependency map from config + $dep_map = []; + foreach ($resolved_packages as $pkg) { + $dep_map[$pkg] = [ + 'depends' => PackageConfig::get($pkg, 'depends', []), + 'suggests' => PackageConfig::get($pkg, 'suggests', []), + ]; + } + + // Collect all sub-dependencies recursively (excluding the package itself) + $visited = []; + $sorted = []; + + // Get dependencies to process for the target package + $deps = $dep_map[$package_name]['depends'] ?? []; + if ($include_suggests) { + $deps = array_merge($deps, $dep_map[$package_name]['suggests'] ?? []); + } + + // Only visit dependencies that are in the resolved set + foreach ($deps as $dep) { + if (isset($resolved_set[$dep])) { + self::visitSubDeps($dep, $dep_map, $resolved_set, $include_suggests, $visited, $sorted); + } + } + + return $sorted; + } + /** * Build a reverse dependency map for the resolved packages. * For each package that is depended upon, list which packages depend on it. @@ -89,6 +141,39 @@ private static function buildReverseDependencyMap(array $resolved, array $dep_li return $why; } + /** + * Recursive helper for getSubDependencies. + * Visits dependencies in topological order (dependencies first). + */ + private static function visitSubDeps( + string $pkg_name, + array $dep_map, + array $resolved_set, + bool $include_suggests, + array &$visited, + array &$sorted + ): void { + if (isset($visited[$pkg_name])) { + return; + } + $visited[$pkg_name] = true; + + // Get dependencies to process + $deps = $dep_map[$pkg_name]['depends'] ?? []; + if ($include_suggests) { + $deps = array_merge($deps, $dep_map[$pkg_name]['suggests'] ?? []); + } + + // Only visit dependencies that are in the resolved set + foreach ($deps as $dep) { + if (isset($resolved_set[$dep])) { + self::visitSubDeps($dep, $dep_map, $resolved_set, $include_suggests, $visited, $sorted); + } + } + + $sorted[] = $pkg_name; + } + /** * Visitor pattern implementation for dependency resolution. * diff --git a/src/StaticPHP/Util/LicenseDumper.php b/src/StaticPHP/Util/LicenseDumper.php new file mode 100644 index 000000000..b15d81cde --- /dev/null +++ b/src/StaticPHP/Util/LicenseDumper.php @@ -0,0 +1,270 @@ + Artifact names to dump */ + private array $artifacts = []; + + /** + * Add artifacts by name. + * + * @param array $artifacts Artifact names + */ + public function addArtifacts(array $artifacts): self + { + $this->artifacts = array_unique(array_merge($this->artifacts, $artifacts)); + return $this; + } + + /** + * Dump all collected artifact licenses to target directory. + * + * @param string $target_dir Target directory path + * @return bool True on success + */ + public function dump(string $target_dir): bool + { + // Create target directory if not exists (don't clean existing files) + if (!is_dir($target_dir)) { + FileSystem::createDir($target_dir); + } else { + logger()->debug("Target directory exists, will append/update licenses: {$target_dir}"); + } + + $license_summary = []; + $dumped_count = 0; + + foreach ($this->artifacts as $artifact_name) { + $artifact = ArtifactLoader::getArtifactInstance($artifact_name); + if ($artifact === null) { + logger()->warning("Artifact not found, skipping: {$artifact_name}"); + continue; + } + + try { + $result = $this->dumpArtifactLicense($artifact, $target_dir, $license_summary); + if ($result) { + ++$dumped_count; + } + } catch (\Throwable $e) { + logger()->warning("Failed to dump license for {$artifact_name}: {$e->getMessage()}"); + } + } + + // Generate SUMMARY.json (read-modify-write) + $this->generateSummary($target_dir, $license_summary); + + logger()->info("Successfully dumped {$dumped_count} license(s) to: {$target_dir}"); + return true; + } + + /** + * Dump license for a single artifact. + * + * @param Artifact $artifact Artifact instance + * @param string $target_dir Target directory + * @param array &$license_summary Summary data to populate + * @return bool True if dumped + * @throws SPCInternalException + */ + private function dumpArtifactLicense(Artifact $artifact, string $target_dir, array &$license_summary): bool + { + $artifact_name = $artifact->getName(); + + // Get metadata from ArtifactConfig + $artifact_config = ArtifactConfig::get($artifact_name); + $config = $artifact_config['metadata'] ?? null; + + if ($config === null) { + logger()->debug("No metadata for artifact: {$artifact_name}"); + return false; + } + + $license_type = $config['license'] ?? null; + $license_files = $config['license-files'] ?? []; + + // Ensure license_files is array + if (is_string($license_files)) { + $license_files = [$license_files]; + } + + if (empty($license_files)) { + logger()->debug("No license files specified for: {$artifact_name}"); + return false; + } + + // Record in summary + $summary_license = $license_type ?? 'Custom'; + if (!isset($license_summary[$summary_license])) { + $license_summary[$summary_license] = []; + } + $license_summary[$summary_license][] = $artifact_name; + + // Dump each license file + $file_count = count($license_files); + $dumped_any = false; + + foreach ($license_files as $index => $license_file_path) { + // Construct output filename + if ($file_count === 1) { + $output_filename = "{$artifact_name}_LICENSE.txt"; + } else { + $output_filename = "{$artifact_name}_LICENSE_{$index}.txt"; + } + + $output_path = "{$target_dir}/{$output_filename}"; + + // Skip if file already exists (avoid duplicate writes) + if (file_exists($output_path)) { + logger()->debug("License file already exists, skipping: {$output_filename}"); + $dumped_any = true; // Still count as dumped + continue; + } + + // Try to read license file from source directory + $license_content = $this->readLicenseFile($artifact, $license_file_path); + if ($license_content === null) { + logger()->warning("License file not found for {$artifact_name}: {$license_file_path}"); + continue; + } + + // Write to target + if (file_put_contents($output_path, $license_content) === false) { + throw new SPCInternalException("Failed to write license file: {$output_path}"); + } + + logger()->info("Dumped license: {$output_filename}"); + $dumped_any = true; + } + + return $dumped_any; + } + + /** + * Read license file content from artifact's source directory. + * + * @param Artifact $artifact Artifact instance + * @param string $license_file_path Relative path to license file + * @return null|string License content, or null if not found + */ + private function readLicenseFile(Artifact $artifact, string $license_file_path): ?string + { + $artifact_name = $artifact->getName(); + + // replace + if (str_starts_with($license_file_path, '@/')) { + $license_file_path = str_replace('@/', ROOT_DIR . '/src/globals/licenses/', $license_file_path); + } + + $source_dir = $artifact->getSourceDir(); + if (FileSystem::isRelativePath($license_file_path)) { + $full_path = "{$source_dir}/{$license_file_path}"; + } else { + $full_path = $license_file_path; + } + // Try source directory first (if extracted) + if ($artifact->isSourceExtracted() || file_exists($full_path)) { + logger()->debug("Checking license file: {$full_path}"); + if (file_exists($full_path)) { + logger()->info("Reading license from source: {$full_path}"); + return file_get_contents($full_path); + } + } else { + logger()->warning("Artifact source not extracted: {$artifact_name}"); + } + + // Fallback: try SOURCE_PATH directly + $fallback_path = SOURCE_PATH . "/{$artifact_name}/{$license_file_path}"; + logger()->debug("Checking fallback path: {$fallback_path}"); + if (file_exists($fallback_path)) { + logger()->info("Reading license from fallback path: {$fallback_path}"); + return file_get_contents($fallback_path); + } + + logger()->debug("License file not found in any location for {$artifact_name}"); + return null; + } + + /** + * Generate SUMMARY.json file with read-modify-write support. + * + * @param string $target_dir Target directory + * @param array $license_summary License summary data (license_type => [artifacts]) + */ + private function generateSummary(string $target_dir, array $license_summary): void + { + if (empty($license_summary)) { + logger()->debug('No licenses to summarize'); + return; + } + + $summary_file = "{$target_dir}/SUMMARY.json"; + + // Read existing summary if exists + $existing_data = []; + if (file_exists($summary_file)) { + $content = file_get_contents($summary_file); + $existing_data = json_decode($content, true) ?? []; + logger()->debug('Loaded existing SUMMARY.json'); + } + + // Initialize structure + if (!isset($existing_data['artifacts'])) { + $existing_data['artifacts'] = []; + } + if (!isset($existing_data['summary'])) { + $existing_data['summary'] = ['license-types' => []]; + } + + // Merge new license information + foreach ($license_summary as $license_type => $artifacts) { + foreach ($artifacts as $artifact_name) { + // Add/update artifact info + $existing_data['artifacts'][$artifact_name] = [ + 'license' => $license_type, + 'dumped-at' => date('Y-m-d H:i:s'), + ]; + + // Update license_types summary + if (!isset($existing_data['summary']['license-types'][$license_type])) { + $existing_data['summary']['license-types'][$license_type] = []; + } + if (!in_array($artifact_name, $existing_data['summary']['license-types'][$license_type])) { + $existing_data['summary']['license-types'][$license_type][] = $artifact_name; + } + } + } + + // Sort license types and artifacts + ksort($existing_data['summary']['license-types']); + foreach ($existing_data['summary']['license-types'] as &$artifacts) { + sort($artifacts); + } + ksort($existing_data['artifacts']); + + // Update totals + $existing_data['summary']['total-artifacts'] = count($existing_data['artifacts']); + $existing_data['summary']['total-license-types'] = count($existing_data['summary']['license-types']); + $existing_data['summary']['last-updated'] = date('Y-m-d H:i:s'); + + // Write JSON file + file_put_contents( + $summary_file, + json_encode($existing_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) + ); + + logger()->info('Generated SUMMARY.json with ' . $existing_data['summary']['total-artifacts'] . ' artifact(s)'); + } +} diff --git a/src/StaticPHP/Util/PkgConfigUtil.php b/src/StaticPHP/Util/PkgConfigUtil.php index 5efd33de9..d5b03757f 100644 --- a/src/StaticPHP/Util/PkgConfigUtil.php +++ b/src/StaticPHP/Util/PkgConfigUtil.php @@ -28,7 +28,12 @@ public static function findPkgConfig(): ?string ]; $found = null; foreach ($find_list as $file) { - if (file_exists($file) && is_executable($file)) { + $exists = file_exists($file); + $executable = is_executable($file); + if (!$exists) { + continue; + } + if (!$executable && chmod($file, 0755) || $executable) { $found = $file; break; } diff --git a/src/StaticPHP/Util/SPCConfigUtil.php b/src/StaticPHP/Util/SPCConfigUtil.php index c525f8c79..a31b771f8 100644 --- a/src/StaticPHP/Util/SPCConfigUtil.php +++ b/src/StaticPHP/Util/SPCConfigUtil.php @@ -149,6 +149,117 @@ public function getLibraryConfig(array|LibraryPackage $lib, bool $include_sugges return $ret; } + /** + * Get build configuration for a package and its sub-dependencies within a resolved set. + * + * This is useful when you need to statically link something against a specific + * library and all its transitive dependencies. It properly handles optional + * dependencies by only including those that were actually resolved. + * + * @param string $package_name The package to get config for + * @param string[] $resolved_packages The full resolved package list + * @param bool $include_suggests Whether to include resolved suggests + * @return array{ + * cflags: string, + * ldflags: string, + * libs: string + * } + */ + public function getPackageDepsConfig(string $package_name, array $resolved_packages, bool $include_suggests = false): array + { + // Get sub-dependencies within the resolved set + $sub_deps = DependencyResolver::getSubDependencies($package_name, $resolved_packages, $include_suggests); + + if (empty($sub_deps)) { + return [ + 'cflags' => '', + 'ldflags' => '', + 'libs' => '', + ]; + } + + // Use libs_only_deps mode and no_php for library linking + $save_no_php = $this->no_php; + $save_libs_only_deps = $this->libs_only_deps; + $this->no_php = true; + $this->libs_only_deps = true; + + $ret = $this->configWithResolvedPackages($sub_deps); + + $this->no_php = $save_no_php; + $this->libs_only_deps = $save_libs_only_deps; + + return $ret; + } + + /** + * Get configuration using already-resolved packages (skip dependency resolution). + * + * @param string[] $resolved_packages Already resolved package names in build order + * @return array{ + * cflags: string, + * ldflags: string, + * libs: string + * } + */ + public function configWithResolvedPackages(array $resolved_packages): array + { + $ldflags = $this->getLdflagsString(); + $cflags = $this->getIncludesString($resolved_packages); + $libs = $this->getLibsString($resolved_packages, !$this->absolute_libs); + + // additional OS-specific libraries (e.g. macOS -lresolv) + if ($extra_libs = SystemTarget::getRuntimeLibs()) { + $libs .= " {$extra_libs}"; + } + + $extra_env = getenv('SPC_EXTRA_LIBS'); + if (is_string($extra_env) && !empty($extra_env)) { + $libs .= " {$extra_env}"; + } + + // package frameworks + if (SystemTarget::getTargetOS() === 'Darwin') { + $libs .= " {$this->getFrameworksString($resolved_packages)}"; + } + + // C++ + if ($this->hasCpp($resolved_packages)) { + $libcpp = SystemTarget::getTargetOS() === 'Darwin' ? '-lc++' : '-lstdc++'; + $libs = str_replace($libcpp, '', $libs) . " {$libcpp}"; + } + + if ($this->libs_only_deps) { + // mimalloc must come first + if (in_array('mimalloc', $resolved_packages) && file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) { + $libs = BUILD_LIB_PATH . '/libmimalloc.a ' . str_replace([BUILD_LIB_PATH . '/libmimalloc.a', '-lmimalloc'], ['', ''], $libs); + } + return [ + 'cflags' => clean_spaces(getenv('CFLAGS') . ' ' . $cflags), + 'ldflags' => clean_spaces(getenv('LDFLAGS') . ' ' . $ldflags), + 'libs' => clean_spaces(getenv('LIBS') . ' ' . $libs), + ]; + } + + // embed + if (!$this->no_php) { + $libs = "-lphp {$libs} -lc"; + } + + $allLibs = getenv('LIBS') . ' ' . $libs; + + // mimalloc must come first + if (in_array('mimalloc', $resolved_packages) && file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) { + $allLibs = BUILD_LIB_PATH . '/libmimalloc.a ' . str_replace([BUILD_LIB_PATH . '/libmimalloc.a', '-lmimalloc'], ['', ''], $allLibs); + } + + return [ + 'cflags' => clean_spaces(getenv('CFLAGS') . ' ' . $cflags), + 'ldflags' => clean_spaces(getenv('LDFLAGS') . ' ' . $ldflags), + 'libs' => clean_spaces($allLibs), + ]; + } + private function hasCpp(array $packages): bool { foreach ($packages as $package) { @@ -209,18 +320,21 @@ private function getLibsString(array $packages, bool $use_short_libs = true): st $frameworks = []; foreach ($packages as $package) { - // add pkg-configs libs - $pkg_configs = PackageConfig::get($package, 'pkg-configs', []); - foreach ($pkg_configs as $pkg_config) { - if (!file_exists(BUILD_LIB_PATH . "/pkgconfig/{$pkg_config}.pc")) { - throw new WrongUsageException("pkg-config file '{$pkg_config}.pc' for lib [{$package}] does not exist in '" . BUILD_LIB_PATH . "/pkgconfig'. Please build it first."); + // parse pkg-configs only for unix systems + if (SystemTarget::isUnix()) { + // add pkg-configs libs + $pkg_configs = PackageConfig::get($package, 'pkg-configs', []); + foreach ($pkg_configs as $pkg_config) { + if (!file_exists(BUILD_LIB_PATH . "/pkgconfig/{$pkg_config}.pc")) { + throw new WrongUsageException("pkg-config file '{$pkg_config}.pc' for lib [{$package}] does not exist in '" . BUILD_LIB_PATH . "/pkgconfig'. Please build it first."); + } + } + $pkg_configs = implode(' ', $pkg_configs); + if ($pkg_configs !== '') { + // static libs with dependencies come in reverse order, so reverse this too + $pc_libs = array_reverse(PkgConfigUtil::getLibsArray($pkg_configs)); + $lib_names = [...$lib_names, ...$pc_libs]; } - } - $pkg_configs = implode(' ', $pkg_configs); - if ($pkg_configs !== '') { - // static libs with dependencies come in reverse order, so reverse this too - $pc_libs = array_reverse(PkgConfigUtil::getLibsArray($pkg_configs)); - $lib_names = [...$lib_names, ...$pc_libs]; } // convert all static-libs to short names $libs = array_reverse(PackageConfig::get($package, 'static-libs', [])); diff --git a/src/StaticPHP/Util/System/UnixUtil.php b/src/StaticPHP/Util/System/UnixUtil.php index 8dd606188..aca50d9e4 100644 --- a/src/StaticPHP/Util/System/UnixUtil.php +++ b/src/StaticPHP/Util/System/UnixUtil.php @@ -44,7 +44,7 @@ public static function exportDynamicSymbols(string $lib_file): void continue; } $name = preg_replace('/@.*$/', '', $name); - if ($name !== '' && $name !== false) { + if (!empty($name)) { $defined[] = $name; } } diff --git a/src/globals/functions.php b/src/globals/functions.php index bb22f3a71..e04bad5e0 100644 --- a/src/globals/functions.php +++ b/src/globals/functions.php @@ -331,6 +331,20 @@ function spc_container(): DI\Container return \StaticPHP\DI\ApplicationContext::getContainer(); } +/** + * Skip the current operation if the condition is true. + * You should ALWAYS use this function inside an attribute callback. + * + * @param bool $condition Condition to evaluate + * @param string $message Optional message for the skip exception + */ +function spc_skip_if(bool $condition, string $message = ''): void +{ + if ($condition) { + throw new StaticPHP\Exception\SkipException($message); + } +} + /** * Parse extension list from string, replace alias and filter internal extensions. * diff --git a/src/globals/licenses/bzip2.txt b/src/globals/licenses/bzip2.txt new file mode 100644 index 000000000..2f2ed1cd0 --- /dev/null +++ b/src/globals/licenses/bzip2.txt @@ -0,0 +1,14 @@ +This program, "bzip2", the associated library "libbzip2", and all documentation, are copyright (C) 1996-2010 Julian R Seward. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + 2. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. + 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + 4. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Julian Seward, jseward@bzip.org bzip2/libbzip2 version 1.0.6 of 6 September 2010 + +PATENTS: To the best of my knowledge, bzip2 and libbzip2 do not use any patented algorithms. However, I do not have the resources to carry out a patent search. Therefore I cannot give any guarantee of the above statement. diff --git a/src/globals/licenses/gmp.txt b/src/globals/licenses/gmp.txt new file mode 100644 index 000000000..488f3b902 --- /dev/null +++ b/src/globals/licenses/gmp.txt @@ -0,0 +1 @@ +Since version 6, GMP is distributed under the dual licenses, GNU LGPL v3 and GNU GPL v2. These licenses make the library free to use, share, and improve, and allow you to pass on the result. The GNU licenses give freedoms, but also set firm restrictions on the use with non-free programs. diff --git a/src/globals/licenses/zlib.txt b/src/globals/licenses/zlib.txt new file mode 100644 index 000000000..b698f3434 --- /dev/null +++ b/src/globals/licenses/zlib.txt @@ -0,0 +1,20 @@ +(C) 1995-2022 Jean-loup Gailly and Mark Adler + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +Jean-loup Gailly Mark Adler +jloup@gzip.org madler@alumni.caltech.edu