diff --git a/config/pkg/ext/ext-amqp.yml b/config/pkg/ext/ext-amqp.yml index 6c73bf203..35bce4d93 100644 --- a/config/pkg/ext/ext-amqp.yml +++ b/config/pkg/ext/ext-amqp.yml @@ -2,8 +2,8 @@ ext-amqp: type: php-extension artifact: source: - type: pecl - name: amqp + type: pie + repo: php-amqp/php-amqp metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-apcu.yml b/config/pkg/ext/ext-apcu.yml index 331b04f76..42e215408 100644 --- a/config/pkg/ext/ext-apcu.yml +++ b/config/pkg/ext/ext-apcu.yml @@ -2,8 +2,8 @@ ext-apcu: type: php-extension artifact: source: - type: pecl - name: APCu + type: pie + repo: apcu/apcu metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-ast.yml b/config/pkg/ext/ext-ast.yml index 776b82dff..201010f11 100644 --- a/config/pkg/ext/ext-ast.yml +++ b/config/pkg/ext/ext-ast.yml @@ -2,8 +2,8 @@ ext-ast: type: php-extension artifact: source: - type: pecl - name: ast + type: pie + repo: nikic/php-ast metadata: license-files: [LICENSE] license: BSD-3-Clause diff --git a/config/pkg/ext/ext-brotli.yml b/config/pkg/ext/ext-brotli.yml index b87fb9ef3..15d101caa 100644 --- a/config/pkg/ext/ext-brotli.yml +++ b/config/pkg/ext/ext-brotli.yml @@ -4,7 +4,6 @@ ext-brotli: source: type: ghtagtar repo: kjdev/php-ext-brotli - extract: php-src/ext/brotli metadata: license-files: [LICENSE] license: MIT diff --git a/config/pkg/ext/ext-clickhouse.yml b/config/pkg/ext/ext-clickhouse.yml index 3e8479bd2..8096d967a 100644 --- a/config/pkg/ext/ext-clickhouse.yml +++ b/config/pkg/ext/ext-clickhouse.yml @@ -4,7 +4,6 @@ ext-clickhouse: source: type: ghtar repo: iliaal/php_clickhouse - extract: php-src/ext/clickhouse prefer-stable: true metadata: license-files: [LICENSE] diff --git a/config/pkg/ext/ext-decimal.yml b/config/pkg/ext/ext-decimal.yml index da5419115..a35fac053 100644 --- a/config/pkg/ext/ext-decimal.yml +++ b/config/pkg/ext/ext-decimal.yml @@ -5,7 +5,6 @@ ext-decimal: type: ghtagtar repo: php-decimal/ext-decimal match: 'v2\.\d.*' - extract: php-src/ext/decimal metadata: license-files: [LICENSE] license: MIT diff --git a/config/pkg/ext/ext-deepclone.yml b/config/pkg/ext/ext-deepclone.yml index ae54dbb59..c8434800a 100644 --- a/config/pkg/ext/ext-deepclone.yml +++ b/config/pkg/ext/ext-deepclone.yml @@ -4,7 +4,6 @@ ext-deepclone: source: type: ghtagtar repo: symfony/php-ext-deepclone - extract: php-src/ext/deepclone metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-dio.yml b/config/pkg/ext/ext-dio.yml index b445940c3..18201a795 100644 --- a/config/pkg/ext/ext-dio.yml +++ b/config/pkg/ext/ext-dio.yml @@ -2,8 +2,8 @@ ext-dio: type: php-extension artifact: source: - type: pecl - name: dio + type: pie + repo: pecl/dio metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-ds.yml b/config/pkg/ext/ext-ds.yml index 779582574..551f2bbdd 100644 --- a/config/pkg/ext/ext-ds.yml +++ b/config/pkg/ext/ext-ds.yml @@ -5,7 +5,6 @@ ext-ds: type: git url: 'https://github.com/php-ds/ext-ds.git' rev: master - extract: php-src/ext/ds metadata: license-files: [LICENSE] license: MIT diff --git a/config/pkg/ext/ext-ev.yml b/config/pkg/ext/ext-ev.yml index 174e5f843..dea1a1fab 100644 --- a/config/pkg/ext/ext-ev.yml +++ b/config/pkg/ext/ext-ev.yml @@ -2,8 +2,8 @@ ext-ev: type: php-extension artifact: source: - type: pecl - name: ev + type: pie + repo: osmanov/pecl-ev metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-event.yml b/config/pkg/ext/ext-event.yml index 537af066b..839f9b419 100644 --- a/config/pkg/ext/ext-event.yml +++ b/config/pkg/ext/ext-event.yml @@ -4,7 +4,6 @@ ext-event: source: type: url url: 'https://bitbucket.org/osmanov/pecl-event/get/3.1.4.tar.gz' - extract: php-src/ext/event metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-excimer.yml b/config/pkg/ext/ext-excimer.yml index a896fe0bd..65658de72 100644 --- a/config/pkg/ext/ext-excimer.yml +++ b/config/pkg/ext/ext-excimer.yml @@ -2,8 +2,8 @@ ext-excimer: type: php-extension artifact: source: - type: pecl - name: excimer + type: ghtagtar + repo: wikimedia/mediawiki-php-excimer metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-fastchart.yml b/config/pkg/ext/ext-fastchart.yml index 0abbc8923..599d893c7 100644 --- a/config/pkg/ext/ext-fastchart.yml +++ b/config/pkg/ext/ext-fastchart.yml @@ -4,7 +4,6 @@ ext-fastchart: source: type: ghtar repo: iliaal/fastchart - extract: php-src/ext/fastchart prefer-stable: true metadata: license-files: [LICENSE] diff --git a/config/pkg/ext/ext-fastjson.yml b/config/pkg/ext/ext-fastjson.yml index 4644e8201..bdcfbbab9 100644 --- a/config/pkg/ext/ext-fastjson.yml +++ b/config/pkg/ext/ext-fastjson.yml @@ -4,7 +4,6 @@ ext-fastjson: source: type: ghtar repo: iliaal/fastjson - extract: php-src/ext/fastjson prefer-stable: true metadata: license-files: [LICENSE] diff --git a/config/pkg/ext/ext-gearman.yml b/config/pkg/ext/ext-gearman.yml index 7699a344b..c71999aa6 100644 --- a/config/pkg/ext/ext-gearman.yml +++ b/config/pkg/ext/ext-gearman.yml @@ -2,8 +2,8 @@ ext-gearman: type: php-extension artifact: source: - type: pecl - name: gearman + type: ghtar + repo: php/pecl-networking-gearman metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-gmssl.yml b/config/pkg/ext/ext-gmssl.yml index 41a32fb9b..6ed275713 100644 --- a/config/pkg/ext/ext-gmssl.yml +++ b/config/pkg/ext/ext-gmssl.yml @@ -4,7 +4,6 @@ ext-gmssl: source: type: ghtar repo: gmssl/GmSSL-PHP - extract: php-src/ext/gmssl metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-grpc.yml b/config/pkg/ext/ext-grpc.yml index ae63cad2b..72b4d3550 100644 --- a/config/pkg/ext/ext-grpc.yml +++ b/config/pkg/ext/ext-grpc.yml @@ -2,8 +2,8 @@ ext-grpc: type: php-extension artifact: source: - type: pecl - name: grpc + type: pie + repo: grpc/grpc-php-ext metadata: license-files: [LICENSE] license: Apache-2.0 diff --git a/config/pkg/ext/ext-igbinary.yml b/config/pkg/ext/ext-igbinary.yml index 1a80831bb..c4b931e8e 100644 --- a/config/pkg/ext/ext-igbinary.yml +++ b/config/pkg/ext/ext-igbinary.yml @@ -2,8 +2,9 @@ ext-igbinary: type: php-extension artifact: source: - type: pecl - name: igbinary + type: pie + repo: igbinary/igbinary + prefer-stable: false metadata: license-files: [COPYING] license: BSD-3-Clause diff --git a/config/pkg/ext/ext-imagick.yml b/config/pkg/ext/ext-imagick.yml index 89ddb341a..a2ae6abeb 100644 --- a/config/pkg/ext/ext-imagick.yml +++ b/config/pkg/ext/ext-imagick.yml @@ -2,8 +2,8 @@ ext-imagick: type: php-extension artifact: source: - type: pecl - name: imagick + type: pie + repo: imagick/imagick metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-imap.yml b/config/pkg/ext/ext-imap.yml index 3abcebb8b..9db326d20 100644 --- a/config/pkg/ext/ext-imap.yml +++ b/config/pkg/ext/ext-imap.yml @@ -2,8 +2,8 @@ ext-imap: type: php-extension artifact: source: - type: pecl - name: imap + type: ghtagtar + repo: php/pecl-mail-imap metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-inotify.yml b/config/pkg/ext/ext-inotify.yml index d69847ee4..ee8ec8bb6 100644 --- a/config/pkg/ext/ext-inotify.yml +++ b/config/pkg/ext/ext-inotify.yml @@ -2,8 +2,8 @@ ext-inotify: type: php-extension artifact: source: - type: pecl - name: inotify + type: pie + repo: arnaud-lb/inotify metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-lz4.yml b/config/pkg/ext/ext-lz4.yml index 8e16d54e4..1963cdbc6 100644 --- a/config/pkg/ext/ext-lz4.yml +++ b/config/pkg/ext/ext-lz4.yml @@ -5,7 +5,6 @@ ext-lz4: type: git url: 'https://github.com/kjdev/php-ext-lz4.git' rev: master - extract: php-src/ext/lz4 metadata: license-files: [LICENSE] license: MIT diff --git a/config/pkg/ext/ext-memcache.yml b/config/pkg/ext/ext-memcache.yml index 6e1f4385c..4c53f5469 100644 --- a/config/pkg/ext/ext-memcache.yml +++ b/config/pkg/ext/ext-memcache.yml @@ -2,8 +2,8 @@ ext-memcache: type: php-extension artifact: source: - type: pecl - name: memcache + type: ghtar + repo: websupport-sk/pecl-memcache metadata: license-files: [LICENSE] license: PHP-3.0 diff --git a/config/pkg/ext/ext-memcached.yml b/config/pkg/ext/ext-memcached.yml index b320c6699..5f3eb81b3 100644 --- a/config/pkg/ext/ext-memcached.yml +++ b/config/pkg/ext/ext-memcached.yml @@ -2,8 +2,8 @@ ext-memcached: type: php-extension artifact: source: - type: pecl - name: memcached + type: pie + repo: php-memcached/php-memcached metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-mongodb.yml b/config/pkg/ext/ext-mongodb.yml index 97f9f0a0d..f67dba27c 100644 --- a/config/pkg/ext/ext-mongodb.yml +++ b/config/pkg/ext/ext-mongodb.yml @@ -5,7 +5,6 @@ ext-mongodb: type: ghrel repo: mongodb/mongo-php-driver match: mongodb.+\.tgz - extract: php-src/ext/mongodb metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-msgpack.yml b/config/pkg/ext/ext-msgpack.yml index 8b230c31a..52e35edec 100644 --- a/config/pkg/ext/ext-msgpack.yml +++ b/config/pkg/ext/ext-msgpack.yml @@ -2,8 +2,8 @@ ext-msgpack: type: php-extension artifact: source: - type: pecl - name: msgpack + type: pie + repo: msgpack/msgpack-php metadata: license-files: [LICENSE] license: BSD-3-Clause diff --git a/config/pkg/ext/ext-mysqlnd_ed25519.yml b/config/pkg/ext/ext-mysqlnd_ed25519.yml index e7aa3de89..f6f79977f 100644 --- a/config/pkg/ext/ext-mysqlnd_ed25519.yml +++ b/config/pkg/ext/ext-mysqlnd_ed25519.yml @@ -4,7 +4,6 @@ ext-mysqlnd_ed25519: source: type: pie repo: mariadb/mysqlnd_ed25519 - extract: php-src/ext/mysqlnd_ed25519 metadata: license-files: [LICENSE] license: BSD-3-Clause diff --git a/config/pkg/ext/ext-mysqlnd_parsec.yml b/config/pkg/ext/ext-mysqlnd_parsec.yml index 903d65c40..955058e7c 100644 --- a/config/pkg/ext/ext-mysqlnd_parsec.yml +++ b/config/pkg/ext/ext-mysqlnd_parsec.yml @@ -4,7 +4,6 @@ ext-mysqlnd_parsec: source: type: pie repo: mariadb/mysqlnd_parsec - extract: php-src/ext/mysqlnd_parsec metadata: license-files: [LICENSE] license: BSD-3-Clause diff --git a/config/pkg/ext/ext-opentelemetry.yml b/config/pkg/ext/ext-opentelemetry.yml index 5caebef2a..bbe162662 100644 --- a/config/pkg/ext/ext-opentelemetry.yml +++ b/config/pkg/ext/ext-opentelemetry.yml @@ -2,8 +2,9 @@ ext-opentelemetry: type: php-extension artifact: source: - type: pecl - name: opentelemetry + type: pie + repo: open-telemetry/ext-opentelemetry + source-root: ext metadata: license-files: [LICENSE] license: Apache-2.0 diff --git a/config/pkg/ext/ext-parallel.yml b/config/pkg/ext/ext-parallel.yml index 94103f578..d9ed0b2fe 100644 --- a/config/pkg/ext/ext-parallel.yml +++ b/config/pkg/ext/ext-parallel.yml @@ -2,8 +2,8 @@ ext-parallel: type: php-extension artifact: source: - type: pecl - name: parallel + type: pie + repo: pecl/parallel metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-pcov.yml b/config/pkg/ext/ext-pcov.yml index 3fac61d08..59a5fbbed 100644 --- a/config/pkg/ext/ext-pcov.yml +++ b/config/pkg/ext/ext-pcov.yml @@ -2,8 +2,8 @@ ext-pcov: type: php-extension artifact: source: - type: pecl - name: pcov + type: pie + repo: pecl/pcov metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-rar.yml b/config/pkg/ext/ext-rar.yml index 1770788a6..8d433c23f 100644 --- a/config/pkg/ext/ext-rar.yml +++ b/config/pkg/ext/ext-rar.yml @@ -5,7 +5,6 @@ ext-rar: type: git url: 'https://github.com/static-php/php-rar.git' rev: issue-php82 - extract: php-src/ext/rar metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-rdkafka.yml b/config/pkg/ext/ext-rdkafka.yml index de53386ca..e1e749dd1 100644 --- a/config/pkg/ext/ext-rdkafka.yml +++ b/config/pkg/ext/ext-rdkafka.yml @@ -4,7 +4,6 @@ ext-rdkafka: source: type: ghtar repo: php-rdkafka/php-rdkafka - extract: php-src/ext/rdkafka metadata: license-files: [LICENSE] license: MIT diff --git a/config/pkg/ext/ext-redis.yml b/config/pkg/ext/ext-redis.yml index c05b4ee26..84f603e3f 100644 --- a/config/pkg/ext/ext-redis.yml +++ b/config/pkg/ext/ext-redis.yml @@ -2,8 +2,8 @@ ext-redis: type: php-extension artifact: source: - type: pecl - name: redis + type: pie + repo: phpredis/phpredis metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-snappy.yml b/config/pkg/ext/ext-snappy.yml index 394527779..ee50f1b68 100644 --- a/config/pkg/ext/ext-snappy.yml +++ b/config/pkg/ext/ext-snappy.yml @@ -5,7 +5,6 @@ ext-snappy: type: git url: 'https://github.com/kjdev/php-ext-snappy' rev: master - extract: php-src/ext/snappy metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-spx.yml b/config/pkg/ext/ext-spx.yml index edf41f514..32f7a2e11 100644 --- a/config/pkg/ext/ext-spx.yml +++ b/config/pkg/ext/ext-spx.yml @@ -4,7 +4,6 @@ ext-spx: source: type: pie repo: noisebynorthwest/php-spx - extract: php-src/ext/spx metadata: license-files: [LICENSE] license: GPL-3.0-or-later diff --git a/config/pkg/ext/ext-ssh2.yml b/config/pkg/ext/ext-ssh2.yml index 14c9bf327..03020646b 100644 --- a/config/pkg/ext/ext-ssh2.yml +++ b/config/pkg/ext/ext-ssh2.yml @@ -2,8 +2,8 @@ ext-ssh2: type: php-extension artifact: source: - type: pecl - name: ssh2 + type: ghtar + repo: php/pecl-networking-ssh2 metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-swoole.yml b/config/pkg/ext/ext-swoole.yml index c8e9355e1..400514cdf 100644 --- a/config/pkg/ext/ext-swoole.yml +++ b/config/pkg/ext/ext-swoole.yml @@ -4,7 +4,6 @@ ext-swoole: source: type: ghtar repo: swoole/swoole-src - extract: php-src/ext/swoole prefer-stable: true metadata: license-files: [LICENSE] diff --git a/config/pkg/ext/ext-swow.yml b/config/pkg/ext/ext-swow.yml index 9d5d18c4f..bd2244198 100644 --- a/config/pkg/ext/ext-swow.yml +++ b/config/pkg/ext/ext-swow.yml @@ -2,10 +2,10 @@ ext-swow: type: php-extension artifact: source: - extract: php-src/ext/swow-src type: ghtar repo: swow/swow prefer-stable: true + source-root: ext metadata: license: Apache-2.0 license-files: [LICENSE] diff --git a/config/pkg/ext/ext-trader.yml b/config/pkg/ext/ext-trader.yml index 03dcadc60..9fc652f9a 100644 --- a/config/pkg/ext/ext-trader.yml +++ b/config/pkg/ext/ext-trader.yml @@ -2,8 +2,8 @@ ext-trader: type: php-extension artifact: source: - type: pecl - name: trader + type: ghtagtar + repo: php/pecl-math-trader metadata: license-files: [LICENSE] license: BSD-2-Clause diff --git a/config/pkg/ext/ext-uuid.yml b/config/pkg/ext/ext-uuid.yml index 3d71aa8b4..e2e055310 100644 --- a/config/pkg/ext/ext-uuid.yml +++ b/config/pkg/ext/ext-uuid.yml @@ -2,8 +2,8 @@ ext-uuid: type: php-extension artifact: source: - type: pecl - name: uuid + type: pie + repo: pecl/uuid metadata: license-files: [LICENSE] license: LGPL-2.1-only diff --git a/config/pkg/ext/ext-uv.yml b/config/pkg/ext/ext-uv.yml index 59d4a8c90..974a6a382 100644 --- a/config/pkg/ext/ext-uv.yml +++ b/config/pkg/ext/ext-uv.yml @@ -2,9 +2,8 @@ ext-uv: type: php-extension artifact: source: - type: pecl - name: uv - prefer-stable: false + type: ghtagtar + repo: amphp/ext-uv metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-xdebug.yml b/config/pkg/ext/ext-xdebug.yml index 20c6b4600..43b8795ad 100644 --- a/config/pkg/ext/ext-xdebug.yml +++ b/config/pkg/ext/ext-xdebug.yml @@ -4,6 +4,7 @@ ext-xdebug: source: type: pie repo: xdebug/xdebug + extract: ext-xdebug metadata: license-files: [LICENSE] license: Xdebug-1.03 diff --git a/config/pkg/ext/ext-xhprof.yml b/config/pkg/ext/ext-xhprof.yml index a554d8375..53e7a9e62 100644 --- a/config/pkg/ext/ext-xhprof.yml +++ b/config/pkg/ext/ext-xhprof.yml @@ -2,9 +2,9 @@ ext-xhprof: type: php-extension artifact: source: - type: pecl - name: xhprof - extract: php-src/ext/xhprof-src + type: ghtar + repo: longxinH/xhprof + source-root: extension metadata: license-files: [LICENSE] license: Apache-2.0 diff --git a/config/pkg/ext/ext-xz.yml b/config/pkg/ext/ext-xz.yml index 1551eaec4..a2040a426 100644 --- a/config/pkg/ext/ext-xz.yml +++ b/config/pkg/ext/ext-xz.yml @@ -5,7 +5,6 @@ ext-xz: type: git url: 'https://github.com/codemasher/php-ext-xz' rev: main - extract: php-src/ext/xz metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-yac.yml b/config/pkg/ext/ext-yac.yml index e10bea064..782c7742f 100644 --- a/config/pkg/ext/ext-yac.yml +++ b/config/pkg/ext/ext-yac.yml @@ -2,8 +2,8 @@ ext-yac: type: php-extension artifact: source: - type: pecl - name: yac + type: ghtagtar + repo: laruence/yac metadata: license-files: [LICENSE] license: PHP-3.01 diff --git a/config/pkg/ext/ext-yaml.yml b/config/pkg/ext/ext-yaml.yml index a60b62547..0e0c09c94 100644 --- a/config/pkg/ext/ext-yaml.yml +++ b/config/pkg/ext/ext-yaml.yml @@ -5,7 +5,6 @@ ext-yaml: type: git url: 'https://github.com/php/pecl-file_formats-yaml' rev: php7 - extract: php-src/ext/yaml metadata: license-files: [LICENSE] license: MIT diff --git a/config/pkg/ext/ext-zip.yml b/config/pkg/ext/ext-zip.yml index 3b164e38b..9b90e91c2 100644 --- a/config/pkg/ext/ext-zip.yml +++ b/config/pkg/ext/ext-zip.yml @@ -2,8 +2,8 @@ ext-zip: type: php-extension artifact: source: - type: pecl - name: zip + type: pie + repo: pecl/zip extract: ext-zip metadata: license-files: [LICENSE] diff --git a/config/pkg/ext/ext-zstd.yml b/config/pkg/ext/ext-zstd.yml index b07db490c..c3e1e3c96 100644 --- a/config/pkg/ext/ext-zstd.yml +++ b/config/pkg/ext/ext-zstd.yml @@ -4,7 +4,6 @@ ext-zstd: source: type: ghtar repo: kjdev/php-ext-zstd - extract: php-src/ext/zstd metadata: license-files: [LICENSE] license: MIT diff --git a/config/pkg/lib/krb5.yml b/config/pkg/lib/krb5.yml index e3234ade5..ccfac35c7 100644 --- a/config/pkg/lib/krb5.yml +++ b/config/pkg/lib/krb5.yml @@ -4,10 +4,10 @@ krb5: source: type: url url: 'https://web.mit.edu/kerberos/dist/krb5/1.22/krb5-1.22.2.tar.gz' + source-root: src metadata: license-files: [NOTICE] license: BSD-3-Clause - source-root: src depends: - openssl suggests: diff --git a/docs/en/develop/artifact-model.md b/docs/en/develop/artifact-model.md index e3d130ebe..ae03d18a2 100644 --- a/docs/en/develop/artifact-model.md +++ b/docs/en/develop/artifact-model.md @@ -74,7 +74,6 @@ Full artifact object format: metadata: # (optional) license: "" # (optional) SPDX identifier license-files: ["LICENSE"] # License files from the source directory - source-root: "subdir" # (optional) Use if the actual source root is inside a subdirectory ``` The basic format of a `source-object`: @@ -84,11 +83,34 @@ The basic format of a `source-object`: type: "url" # Download type # ...: Additional keys depend on the type; see below extract: "path/to/dir" # (optional) Override extract path; default: SOURCE_PATH/{artifact-name} + source-root: "src" # (optional) Subdirectory of the extracted source that contains the buildable source root +``` + +## Common Fields + +Besides the type-specific keys, every source object accepts the following optional fields: + +### extract + +- **Type**: `string` (optional) +- **Description**: Overrides the extraction directory. Defaults to `SOURCE_PATH/{artifact-name}`. + +### source-root + +- **Type**: `string` (optional) +- **Description**: When the buildable source root is located inside a subdirectory of the extracted archive, this field specifies that subdirectory path. The framework will use `source dir + source-root` as the working directory during the build instead of the top-level extraction directory. For example, a PIE package whose `config.m4` lives in an `ext/` subdirectory, or krb5 whose actual source root is in `src/` after extraction. For `php-extension` packages, sources are always extracted to the default source dir; before the in-tree PHP build runs `buildconf`, the php target links each extension's source root into `php-src/ext/{name}` — a symlink on Unix, an NTFS junction on Windows (with a plain copy as fallback) — so the build can find `config.m4`/`config.w32` at the extension root and build-stage patches always hit the tree actually being compiled. Extension package classes can use `getBuildDir()` to locate that tree (it equals `getSourceRoot()` for shared phpize builds), while `getSourceDir()`/`getSourceRoot()` always refer to the artifact source. + +```yaml +# krb5's actual source root is in the src/ subdirectory after extraction +source: + type: url + url: 'https://web.mit.edu/kerberos/dist/krb5/1.22/krb5-1.22.2.tar.gz' + source-root: src ``` ## Metadata -The `metadata` field provides supplementary information about an artifact. It supports three subfields: +The `metadata` field provides supplementary information about an artifact. It supports two subfields: ### license @@ -127,17 +149,6 @@ metadata: license-files: ['@/bzip2.txt'] ``` -### source-root - -- **Type**: `string` (optional) -- **Description**: When the actual source root is located inside a subdirectory of the extracted archive, this field specifies that subdirectory name. The framework will use this path as the working directory during the build instead of the top-level extraction directory. - -```yaml -# krb5's actual source root is in the src/ subdirectory after extraction -metadata: - source-root: src -``` - ## Download Types Artifacts support a variety of download types. Choose the one that best fits where the package is hosted. @@ -359,6 +370,7 @@ Downloads a PHP extension from [Packagist](https://repo.packagist.org) following - **Required**: `repo` — Packagist package path in `vendor/package` format - **Optional**: - `extract` — override extraction directory + - `prefer-stable` — skip pre-release versions (default: `true`) ```yaml # xdebug downloaded from Packagist diff --git a/docs/zh/develop/artifact-model.md b/docs/zh/develop/artifact-model.md index 1f38c662a..51875a829 100644 --- a/docs/zh/develop/artifact-model.md +++ b/docs/zh/develop/artifact-model.md @@ -70,7 +70,6 @@ Artifact 包含 `source`、`binary` 和 `metadata` 三个部分。 metadata: # (optional) license: "" # (optional) SPDX license-files: ["LICENSE"] # License files from original source dir - source-root: "subdir" # (optional) If package source is in subdir, use this to change base ``` 下面是 `source-object` 的基本格式: @@ -80,11 +79,34 @@ Artifact 包含 `source`、`binary` 和 `metadata` 三个部分。 type: "url" # Download type # ...: Different type requires differnt keys here, read below extract: "path/to/dir" # (optional) Change extract dir, default: `SOURCE_PATH/{artifact-name}` + source-root: "src" # (optional) Subdirectory of the extracted source that contains the buildable source root +``` + +## 通用字段 + +除了各类型特有的字段外,所有 source 对象都支持以下可选字段: + +### extract + +- **类型**:`string`(选填) +- **说明**:覆盖解压目录,默认为 `SOURCE_PATH/{artifact-name}`。 + +### source-root + +- **类型**:`string`(选填) +- **说明**:当可构建的源码根目录位于解压目录的子目录中时,使用该字段指定子目录路径。框架在构建时会以 `源码目录 + source-root` 作为工作目录,而非解压后的顶层目录。例如 `config.m4` 位于 `ext/` 子目录中的 PIE 扩展包,或解压后实际源码根在 `src/` 子目录下的 krb5。对于 `php-extension` 类型的包,源码始终解压到默认源码目录;在 PHP 内联构建执行 `buildconf` 之前,php target 会将每个扩展的源码根目录链接到 `php-src/ext/{name}`——Unix 上使用符号链接,Windows 上使用 NTFS junction(失败时回退为普通复制)——以便构建系统能在扩展根目录找到 `config.m4`/`config.w32`,且构建阶段的补丁始终作用于实际被编译的源码树。扩展包类可以使用 `getBuildDir()` 定位该树(shared phpize 构建时等同于 `getSourceRoot()`),而 `getSourceDir()`/`getSourceRoot()` 始终指向 artifact 源码侧。 + +```yaml +# krb5 的源码解压后实际根目录在 src/ 子目录下 +source: + type: url + url: 'https://web.mit.edu/kerberos/dist/krb5/1.22/krb5-1.22.2.tar.gz' + source-root: src ``` ## Metadata -`metadata` 字段用于声明 Artifact 的附加信息,目前支持以下三个子字段: +`metadata` 字段用于声明 Artifact 的附加信息,目前支持以下两个子字段: ### license @@ -123,17 +145,6 @@ metadata: license-files: ['@/bzip2.txt'] ``` -### source-root - -- **类型**:`string`(选填) -- **说明**:当 Artifact 解压后,实际的源码根目录位于解压目录的子目录中时,使用该字段指定子目录名。框架在执行构建时会将工作目录切换到该子目录,而非解压后的顶层目录。 - -```yaml -# krb5 的源码解压后实际根目录在 src/ 子目录下 -metadata: - source-root: src -``` - ## 下载来源 Artifact 支持多种下载来源类型。你可以根据实际情况选择对应包的下载来源。 @@ -355,6 +366,7 @@ artifact: - **必填**:`repo` — Packagist 包路径,格式 `vendor/package` - **选填**: - `extract` — 解压目标目录 + - `prefer-stable` — 是否跳过预发布版本(默认 `true`) ```yaml # xdebug 从 Packagist 下载 diff --git a/src/Package/Artifact/imagick.php b/src/Package/Artifact/imagick.php index 568729e18..c2ef99d5e 100644 --- a/src/Package/Artifact/imagick.php +++ b/src/Package/Artifact/imagick.php @@ -12,10 +12,10 @@ class imagick { #[AfterSourceExtract('ext-imagick')] #[PatchDescription('Patch imagick for PHP 8.4 compatibility (versions < 3.8.0)')] - public function patchImagickWith84(): void + public function patchImagickWith84(string $target_path): void { // match imagick version id - $file = SOURCE_PATH . '/php-src/ext/imagick/php_imagick.h'; + $file = $target_path . '/php_imagick.h'; if (!file_exists($file)) { return; } @@ -25,7 +25,7 @@ public function patchImagickWith84(): void } $extnum = intval($match[1]); if ($extnum < 30800) { - SourcePatcher::patchFile('imagick_php84_before_30800.patch', SOURCE_PATH . '/php-src/ext/imagick'); + SourcePatcher::patchFile('imagick_php84_before_30800.patch', $target_path); } } } diff --git a/src/Package/Artifact/pdo_sqlsrv.php b/src/Package/Artifact/pdo_sqlsrv.php new file mode 100644 index 000000000..593ca92dc --- /dev/null +++ b/src/Package/Artifact/pdo_sqlsrv.php @@ -0,0 +1,25 @@ +isBuildStatic()) { + FileSystem::replaceFileStr( + $this->getBuildDir() . '/config.m4', + 'LDFLAGS="$LDFLAGS -Wl,-exported_symbol,_get_module"', + '' + ); + } + } } diff --git a/src/Package/Extension/decimal.php b/src/Package/Extension/decimal.php index 93e3d28b9..10bddad0e 100644 --- a/src/Package/Extension/decimal.php +++ b/src/Package/Extension/decimal.php @@ -21,7 +21,7 @@ class decimal extends PhpExtensionPackage public function patchBeforeBuildconf(): void { FileSystem::replaceFileStr( - $this->getSourceDir() . '/php_decimal.c', + $this->getBuildDir() . '/php_decimal.c', [ 'zend_module_entry decimal_module_entry', 'ZEND_GET_MODULE(decimal)', @@ -38,7 +38,7 @@ public function patchBeforeBuildconf(): void public function patchConfigW32(): void { FileSystem::replaceFileStr( - $this->getSourceDir() . '/config.w32', + $this->getBuildDir() . '/config.w32', 'ARG_WITH("decimal", "for decimal support", "no");', 'ARG_WITH("decimal", "for decimal support", "no");' . "\n" . 'ADD_EXTENSION_DEP("decimal", "json");' diff --git a/src/Package/Extension/dio.php b/src/Package/Extension/dio.php index 70ac387e3..702cf8765 100644 --- a/src/Package/Extension/dio.php +++ b/src/Package/Extension/dio.php @@ -16,8 +16,8 @@ class dio extends PhpExtensionPackage #[BeforeStage('php', [php::class, 'buildconfForUnix'], 'ext-dio')] public function patchBeforeBuildconf(): void { - if (!file_exists("{$this->getSourceDir()}/php_dio.h")) { - FileSystem::writeFile("{$this->getSourceDir()}/php_dio.h", FileSystem::readFile("{$this->getSourceDir()}/src/php_dio.h")); + if (!file_exists("{$this->getBuildDir()}/php_dio.h")) { + FileSystem::writeFile("{$this->getBuildDir()}/php_dio.h", FileSystem::readFile("{$this->getBuildDir()}/src/php_dio.h")); } } } diff --git a/src/Package/Extension/ev.php b/src/Package/Extension/ev.php index b4b4b9427..e1d9921e9 100644 --- a/src/Package/Extension/ev.php +++ b/src/Package/Extension/ev.php @@ -21,7 +21,7 @@ public function patchBeforeBuildconf(): bool * to EXTENSION('ev', php_ev_sources, PHP_EV_SHARED, ' /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); */ FileSystem::replaceFileLineContainsString( - "{$this->getSourceDir()}\\config.w32", + "{$this->getBuildDir()}\\config.w32", 'EXTENSION(\'ev\'', " EXTENSION('ev', php_ev_sources, PHP_EV_SHARED, ' /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1');" ); diff --git a/src/Package/Extension/glfw.php b/src/Package/Extension/glfw.php index 8c73cb483..aa66a9bde 100644 --- a/src/Package/Extension/glfw.php +++ b/src/Package/Extension/glfw.php @@ -15,16 +15,6 @@ #[Extension('glfw')] class glfw extends PhpExtensionPackage { - #[BeforeStage('php', [php::class, 'buildconfForUnix'], 'ext-glfw')] - #[BeforeStage('php', [php::class, 'buildconfForWindows'], 'ext-glfw')] - #[PatchDescription('Patch glfw extension before buildconf')] - public function patchBeforeBuildconf(): void - { - if (!file_exists(SOURCE_PATH . '/php-src/ext/glfw')) { - FileSystem::copyDir($this->getSourceDir(), SOURCE_PATH . '/php-src/ext/glfw'); - } - } - #[BeforeStage('php', [php::class, 'configureForUnix'], 'ext-glfw')] #[PatchDescription('Patch glfw extension before configure')] public function patchBeforeConfigure(): void diff --git a/src/Package/Extension/gmssl.php b/src/Package/Extension/gmssl.php index a230f407f..ee23eed00 100644 --- a/src/Package/Extension/gmssl.php +++ b/src/Package/Extension/gmssl.php @@ -21,7 +21,7 @@ public function patchSm2VerifyCtx(): void { // See: https://github.com/crazywhalecc/static-php-cli/issues/1182 FileSystem::replaceFileStr( - "{$this->getSourceDir()}/gmssl.c", + "{$this->getBuildDir()}/gmssl.c", 'SM2_VERIFY_CTX', 'SM2_SIGN_CTX' ); @@ -33,7 +33,7 @@ public function patchSm2VerifyCtx(): void public function patchPbkdf2Rename(): void { FileSystem::replaceFileStr( - "{$this->getSourceDir()}/gmssl.c", + "{$this->getBuildDir()}/gmssl.c", 'pbkdf2_hmac_sm3_genkey', 'sm3_pbkdf2' ); @@ -43,7 +43,7 @@ public function patchPbkdf2Rename(): void #[PatchDescription('Add CHECK_LIB to config.w32 for static Windows builds')] public function patchBeforeBuildconfWin(): bool { - $configW32 = "{$this->getSourceDir()}/config.w32"; + $configW32 = "{$this->getBuildDir()}/config.w32"; if (str_contains(FileSystem::readFile($configW32), 'CHECK_LIB(')) { return false; } diff --git a/src/Package/Extension/grpc.php b/src/Package/Extension/grpc.php index d1ce1511e..ce7e93cfa 100644 --- a/src/Package/Extension/grpc.php +++ b/src/Package/Extension/grpc.php @@ -24,13 +24,13 @@ public function patchBeforeBuildconf(): void // Fix deprecated PHP API usage in call.c FileSystem::replaceFileStr( - "{$this->getSourceDir()}/src/php/ext/grpc/call.c", + "{$this->getBuildDir()}/src/php/ext/grpc/call.c", 'zend_exception_get_default(TSRMLS_C),', 'zend_ce_exception,', ); // Fix include path conflict with pdo_sqlsrv: grpc's PHP ext dir is added to the global include path via - $grpc_php_dir = "{$this->getSourceDir()}/src/php/ext/grpc"; + $grpc_php_dir = "{$this->getBuildDir()}/src/php/ext/grpc"; if (file_exists("{$grpc_php_dir}/version.h")) { copy("{$grpc_php_dir}/version.h", "{$grpc_php_dir}/php_grpc_version.h"); unlink("{$grpc_php_dir}/version.h"); @@ -69,11 +69,11 @@ public function patchBeforeBuildconf(): void M4; $replace = get_pack_replace(); // load grpc c files from src/php/ext/grpc - $c_files = glob("{$this->getSourceDir()}/src/php/ext/grpc/*.c"); + $c_files = glob("{$this->getBuildDir()}/src/php/ext/grpc/*.c"); $replace['@grpc_c_files@'] = implode(" \\\n ", array_map(fn ($f) => 'src/php/ext/grpc/' . basename($f), $c_files)); $config_m4 = str_replace(array_keys($replace), array_values($replace), $config_m4); - file_put_contents("{$this->getSourceDir()}/config.m4", $config_m4); + file_put_contents("{$this->getBuildDir()}/config.m4", $config_m4); - copy("{$this->getSourceDir()}/src/php/ext/grpc/php_grpc.h", "{$this->getSourceDir()}/php_grpc.h"); + copy("{$this->getBuildDir()}/src/php/ext/grpc/php_grpc.h", "{$this->getBuildDir()}/php_grpc.h"); } } diff --git a/src/Package/Extension/imagick.php b/src/Package/Extension/imagick.php index 0be886693..06edf9fde 100644 --- a/src/Package/Extension/imagick.php +++ b/src/Package/Extension/imagick.php @@ -36,7 +36,7 @@ public function getWindowsConfigureArg(bool $shared): string #[PatchDescription('Add the Win32 system libraries the static ImageMagick stack needs')] public function patchConfigW32ForWindows(): void { - $config = $this->getSourceDir() . '/config.w32'; + $config = $this->getBuildDir() . '/config.w32'; // Idempotency guard (the source dir may be patched in place and reused across builds). if (str_contains(FileSystem::readFile($config), 'LIBS_IMAGICK')) { diff --git a/src/Package/Extension/imap.php b/src/Package/Extension/imap.php index df53cfe70..c761eb0d8 100644 --- a/src/Package/Extension/imap.php +++ b/src/Package/Extension/imap.php @@ -34,7 +34,7 @@ public function patchBeforeMake(): void // zend_zval_value_name() was introduced in PHP 8.3; PHP 8.2 imap backported the call but not the declaration // replace with the equivalent PHP 8.2-compatible function FileSystem::replaceFileStr( - "{$this->getSourceDir()}/php_imap.c", + "{$this->getBuildDir()}/php_imap.c", 'zend_zval_value_name(data)', 'zend_zval_type_name(data)' ); @@ -46,11 +46,11 @@ public function patchBeforeBuildconf(PackageInstaller $installer): void if ($installer->getLibraryPackage('openssl')) { // sometimes imap with openssl does not contain zlib (required by openssl) // we need to add it manually - FileSystem::replaceFileStr("{$this->getSourceDir()}/config.m4", 'TST_LIBS="$DLIBS $IMAP_SHARED_LIBADD"', 'TST_LIBS="$DLIBS $IMAP_SHARED_LIBADD -lz"'); + FileSystem::replaceFileStr("{$this->getBuildDir()}/config.m4", 'TST_LIBS="$DLIBS $IMAP_SHARED_LIBADD"', 'TST_LIBS="$DLIBS $IMAP_SHARED_LIBADD -lz"'); } // c-client is built with PASSWDTYPE=nul so libcrypt is not referenced. FileSystem::replaceFileStr( - "{$this->getSourceDir()}/config.m4", + "{$this->getBuildDir()}/config.m4", " PHP_CHECK_LIBRARY(crypt, crypt,\n [\n PHP_ADD_LIBRARY(crypt,, IMAP_SHARED_LIBADD)\n AC_DEFINE(HAVE_LIBCRYPT,1,[ ])\n ])", ' dnl Skipped: crypt check not needed (c-client built with PASSWDTYPE=nul)' ); diff --git a/src/Package/Extension/maxminddb.php b/src/Package/Extension/maxminddb.php index 9d04fcb94..b9e4e69c0 100644 --- a/src/Package/Extension/maxminddb.php +++ b/src/Package/Extension/maxminddb.php @@ -19,13 +19,13 @@ class maxminddb extends PhpExtensionPackage #[PatchDescription('Patch maxminddb extension for buildconf to support new source structure')] public function patchBeforeBuildconf(): void { - if (file_exists("{$this->getSourceDir()}/config.m4")) { + if (file_exists("{$this->getBuildDir()}/config.m4")) { return; } // move ext/maxminddb/ext/* to ext/maxminddb/ - $files = FileSystem::scanDirFiles("{$this->getSourceDir()}/ext", false, true); + $files = FileSystem::scanDirFiles("{$this->getBuildDir()}/ext", false, true); foreach ($files as $file) { - rename("{$this->getSourceDir()}/ext/{$file}", "{$this->getSourceDir()}/{$file}"); + rename("{$this->getBuildDir()}/ext/{$file}", "{$this->getBuildDir()}/{$file}"); } } } diff --git a/src/Package/Extension/memcache.php b/src/Package/Extension/memcache.php index 3fac40228..0b3cdf880 100644 --- a/src/Package/Extension/memcache.php +++ b/src/Package/Extension/memcache.php @@ -20,12 +20,12 @@ public function patchBeforeBuildconf(): bool // PHP 8.5 moved php_smart_string*.h from ext/standard/ to Zend/ foreach (['src/memcache_pool.h', 'src/memcache_pool.c', 'src/memcache_session.c', 'src/memcache_ascii_protocol.c', 'src/memcache_binary_protocol.c'] as $file) { FileSystem::replaceFileStr( - "{$this->getSourceDir()}/{$file}", + "{$this->getBuildDir()}/{$file}", '#include "ext/standard/php_smart_string_public.h"', '#include "Zend/zend_smart_string_public.h"', ); FileSystem::replaceFileStr( - "{$this->getSourceDir()}/{$file}", + "{$this->getBuildDir()}/{$file}", '#include "ext/standard/php_smart_string.h"', '#include "Zend/zend_smart_string.h"', ); @@ -35,18 +35,18 @@ public function patchBeforeBuildconf(): bool return false; } FileSystem::replaceFileStr( - "{$this->getSourceDir()}/config9.m4", + "{$this->getBuildDir()}/config9.m4", 'if test -d $abs_srcdir/src ; then', 'if test -d $abs_srcdir/main ; then' ); FileSystem::replaceFileStr( - "{$this->getSourceDir()}/config9.m4", + "{$this->getBuildDir()}/config9.m4", 'export CPPFLAGS="$CPPFLAGS $INCLUDES"', 'export CPPFLAGS="$CPPFLAGS $INCLUDES -I$abs_srcdir/main"' ); // add for in-tree building file_put_contents( - "{$this->getSourceDir()}/php_memcache.h", + "{$this->getBuildDir()}/php_memcache.h", <<<'EOF' #ifndef PHP_MEMCACHE_H #define PHP_MEMCACHE_H @@ -68,12 +68,12 @@ public function patchBeforeSharedConfigure(): bool return false; } FileSystem::replaceFileStr( - "{$this->getSourceDir()}/config9.m4", + "{$this->getBuildDir()}/config9.m4", 'if test -d $abs_srcdir/main ; then', 'if test -d $abs_srcdir/src ; then', ); FileSystem::replaceFileStr( - "{$this->getSourceDir()}/config9.m4", + "{$this->getBuildDir()}/config9.m4", 'export CPPFLAGS="$CPPFLAGS $INCLUDES -I$abs_srcdir/main"', 'export CPPFLAGS="$CPPFLAGS $INCLUDES"', ); diff --git a/src/Package/Extension/mongodb.php b/src/Package/Extension/mongodb.php index 6bf723998..9f4e3e7b7 100644 --- a/src/Package/Extension/mongodb.php +++ b/src/Package/Extension/mongodb.php @@ -29,7 +29,7 @@ public function exportPhpVersionIdForUnix(): void public function patchBeforeBuild(): void { FileSystem::replaceFileStr( - "{$this->getSourceDir()}/config.w32", + "{$this->getBuildDir()}/config.w32", 'ADD_FLAG("CFLAGS_MONGODB", "/D KMS_MESSAGE_LITTLE_ENDIAN=1 /D MONGOCRYPT_LITTLE_ENDIAN=1 /D MLIB_USER=1");', 'ADD_FLAG("CFLAGS_MONGODB", "/D KMS_MESSAGE_LITTLE_ENDIAN=1 /D MONGOCRYPT_LITTLE_ENDIAN=1 /D MLIB_USER=1");' . "\n ADD_FLAG(\"CFLAGS_MONGODB\", \"/utf-8\");", ); @@ -40,7 +40,7 @@ public function patchBeforeBuild(): void public function patchBeforeBuildconfUnix(): void { FileSystem::replaceFileRegex( - $this->getSourceDir() . '/config.m4', + $this->getBuildDir() . '/config.m4', '/^(\s+)(src\/libmongoc\/)/m', '$1${ac_config_dir}/$2' ); diff --git a/src/Package/Extension/parallel.php b/src/Package/Extension/parallel.php index 0ec585951..d3b957f27 100644 --- a/src/Package/Extension/parallel.php +++ b/src/Package/Extension/parallel.php @@ -29,7 +29,7 @@ public function validate(PackageBuilder $builder): void #[PatchDescription('Fix parallel m4 hardcoded PHP_VERSION check')] public function patchBeforeBuildconf(): bool { - FileSystem::replaceFileRegex("{$this->getSourceDir()}/config.m4", '/PHP_VERSION=.*/m', ''); + FileSystem::replaceFileRegex("{$this->getBuildDir()}/config.m4", '/PHP_VERSION=.*/m', ''); return true; } } diff --git a/src/Package/Extension/pdo_odbc.php b/src/Package/Extension/pdo_odbc.php index f8835d134..9eb2fefb1 100644 --- a/src/Package/Extension/pdo_odbc.php +++ b/src/Package/Extension/pdo_odbc.php @@ -17,7 +17,7 @@ class pdo_odbc extends PhpExtensionPackage #[BeforeStage('php', [php::class, 'buildconfForUnix'], 'ext-pdo_odbc')] public function patchBeforeBuildconf(): void { - FileSystem::replaceFileStr("{$this->getSourceDir()}/config.m4", 'PDO_ODBC_LDFLAGS="$pdo_odbc_def_ldflags', 'PDO_ODBC_LDFLAGS="-liconv $pdo_odbc_def_ldflags'); + FileSystem::replaceFileStr("{$this->getBuildDir()}/config.m4", 'PDO_ODBC_LDFLAGS="$pdo_odbc_def_ldflags', 'PDO_ODBC_LDFLAGS="-liconv $pdo_odbc_def_ldflags'); } #[CustomPhpConfigureArg('Linux')] diff --git a/src/Package/Extension/pdo_sqlsrv.php b/src/Package/Extension/pdo_sqlsrv.php index 00134db14..07649ca09 100644 --- a/src/Package/Extension/pdo_sqlsrv.php +++ b/src/Package/Extension/pdo_sqlsrv.php @@ -22,18 +22,4 @@ public function patchBeforeBuildconfForWindows(): void FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/pdo_sqlsrv/config.w32', '/sdl', ''); } } - - #[BeforeStage('php', [php::class, 'buildconfForUnix'], 'ext-pdo_sqlsrv')] - #[PatchDescription('Fix pdo_sqlsrv directory structure for PHP 8.5+ (source layout changed)')] - public function patchDirectoryStructureForPhp85(): void - { - $source_dir = SOURCE_PATH . '/php-src/ext/pdo_sqlsrv'; - if (!file_exists($source_dir . '/config.m4') && is_dir($source_dir . '/source/pdo_sqlsrv')) { - FileSystem::moveFileOrDir($source_dir . '/LICENSE', $source_dir . '/source/pdo_sqlsrv/LICENSE'); - FileSystem::moveFileOrDir($source_dir . '/source/shared', $source_dir . '/source/pdo_sqlsrv/shared'); - FileSystem::moveFileOrDir($source_dir . '/source/pdo_sqlsrv', SOURCE_PATH . '/pdo_sqlsrv'); - FileSystem::removeDir($source_dir); - FileSystem::moveFileOrDir(SOURCE_PATH . '/pdo_sqlsrv', $source_dir); - } - } } diff --git a/src/Package/Extension/phar.php b/src/Package/Extension/phar.php index 2220f7278..adece5d04 100644 --- a/src/Package/Extension/phar.php +++ b/src/Package/Extension/phar.php @@ -33,7 +33,7 @@ public function afterMicroUnixBuild(): void public function beforeBuildShared(PhpExtensionPackage $pkg): void { FileSystem::replaceFileStr( - "{$pkg->getSourceDir()}/config.m4", + "{$pkg->getBuildDir()}/config.m4", ['$ext_dir/phar.1', '$ext_dir/phar.phar.1'], ['${ext_dir}phar.1', '${ext_dir}phar.phar.1'] ); @@ -43,7 +43,7 @@ public function beforeBuildShared(PhpExtensionPackage $pkg): void public function afterBuildShared(PhpExtensionPackage $pkg): void { FileSystem::replaceFileStr( - "{$pkg->getSourceDir()}/config.m4", + "{$pkg->getBuildDir()}/config.m4", ['${ext_dir}phar.1', '${ext_dir}phar.phar.1'], ['$ext_dir/phar.1', '$ext_dir/phar.phar.1'] ); diff --git a/src/Package/Extension/rar.php b/src/Package/Extension/rar.php index 2fc20ed14..3a49f8a85 100644 --- a/src/Package/Extension/rar.php +++ b/src/Package/Extension/rar.php @@ -21,7 +21,7 @@ public function patchBeforeBuildconf(): void { // workaround for newer Xcode clang (>= 15.0) if (SystemTarget::getTargetOS() === 'Darwin') { - FileSystem::replaceFileStr("{$this->getSourceDir()}/config.m4", '-Wall -fvisibility=hidden', '-Wall -Wno-incompatible-function-pointer-types -fvisibility=hidden'); + FileSystem::replaceFileStr("{$this->getBuildDir()}/config.m4", '-Wall -fvisibility=hidden', '-Wall -Wno-incompatible-function-pointer-types -fvisibility=hidden'); } } } diff --git a/src/Package/Extension/rdkafka.php b/src/Package/Extension/rdkafka.php index 2a77ab611..f833791a6 100644 --- a/src/Package/Extension/rdkafka.php +++ b/src/Package/Extension/rdkafka.php @@ -21,9 +21,9 @@ class rdkafka extends PhpExtensionPackage #[PatchDescription('Patch rdkafka extension config.m4 to support pkg-config and fix library check')] public function patchBeforeBuildconf(): bool { - FileSystem::replaceFileStr("{$this->getSourceDir()}/config.m4", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm\n", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm \$RDKAFKA_LIBS\n"); - FileSystem::replaceFileStr("{$this->getSourceDir()}/config.m4", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm\"\n", '-L$RDKAFKA_DIR/$PHP_LIBDIR -lm $RDKAFKA_LIBS"'); - FileSystem::replaceFileStr("{$this->getSourceDir()}/config.m4", 'PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,', 'AC_CHECK_LIB([$LIBNAME], [$LIBSYMBOL],'); + FileSystem::replaceFileStr("{$this->getBuildDir()}/config.m4", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm\n", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm \$RDKAFKA_LIBS\n"); + FileSystem::replaceFileStr("{$this->getBuildDir()}/config.m4", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm\"\n", '-L$RDKAFKA_DIR/$PHP_LIBDIR -lm $RDKAFKA_LIBS"'); + FileSystem::replaceFileStr("{$this->getBuildDir()}/config.m4", 'PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,', 'AC_CHECK_LIB([$LIBNAME], [$LIBSYMBOL],'); return true; } @@ -33,12 +33,12 @@ public function patchBeforeMake(): bool { // when compiling rdkafka with inline builds, it shows some errors, I don't know why. FileSystem::replaceFileStr( - "{$this->getSourceDir()}/rdkafka.c", + "{$this->getBuildDir()}/rdkafka.c", "#ifdef HAS_RD_KAFKA_TRANSACTIONS\n#include \"kafka_error_exception.h\"\n#endif", '#include "kafka_error_exception.h"' ); FileSystem::replaceFileStr( - "{$this->getSourceDir()}/kafka_error_exception.h", + "{$this->getBuildDir()}/kafka_error_exception.h", ['#ifdef HAS_RD_KAFKA_TRANSACTIONS', '#endif'], '' ); diff --git a/src/Package/Extension/simdjson.php b/src/Package/Extension/simdjson.php index 4939c8116..2dcaa424b 100644 --- a/src/Package/Extension/simdjson.php +++ b/src/Package/Extension/simdjson.php @@ -25,17 +25,17 @@ public function patchBeforeBuildconf(PackageInstaller $installer): bool $php = $installer->getTargetPackage('php'); $php_ver = php::getPHPVersionID(); FileSystem::replaceFileRegex( - "{$this->getSourceDir()}/config.m4", + "{$this->getBuildDir()}/config.m4", '/php_version=(`.*`)$/m', "php_version={$php_ver}" ); FileSystem::replaceFileStr( - "{$this->getSourceDir()}/config.m4", + "{$this->getBuildDir()}/config.m4", 'if test -z "$PHP_CONFIG"; then', 'if false; then' ); FileSystem::replaceFileStr( - "{$this->getSourceDir()}/config.w32", + "{$this->getBuildDir()}/config.w32", "'yes',", 'PHP_SIMDJSON_SHARED,' ); diff --git a/src/Package/Extension/snmp.php b/src/Package/Extension/snmp.php index d161c6026..26dbc9071 100644 --- a/src/Package/Extension/snmp.php +++ b/src/Package/Extension/snmp.php @@ -21,11 +21,11 @@ public function patchBeforeBuildconf(): bool { // Overwrite m4 config using newer PHP version if (php::getPHPVersionID() < 80400) { - FileSystem::copy(ROOT_DIR . '/src/globals/extra/snmp-ext-config-old.m4', "{$this->getSourceDir()}/config.m4"); + FileSystem::copy(ROOT_DIR . '/src/globals/extra/snmp-ext-config-old.m4', "{$this->getBuildDir()}/config.m4"); } $libs = implode(' ', PkgConfigUtil::getLibsArray('netsnmp')); FileSystem::replaceFileStr( - "{$this->getSourceDir()}/config.m4", + "{$this->getBuildDir()}/config.m4", 'PHP_EVAL_LIBLINE([$SNMP_LIBS], [SNMP_SHARED_LIBADD])', "SNMP_LIBS=\"{$libs}\"\nPHP_EVAL_LIBLINE([\$SNMP_LIBS], [SNMP_SHARED_LIBADD])" ); diff --git a/src/Package/Extension/spx.php b/src/Package/Extension/spx.php index 96a8c75da..cb9aa3b56 100644 --- a/src/Package/Extension/spx.php +++ b/src/Package/Extension/spx.php @@ -32,16 +32,16 @@ public function getUnixConfigureArg(bool $shared, PackageInstaller $installer): public function patchBeforeBuildconf(): bool { FileSystem::replaceFileStr( - "{$this->getSourceDir()}/config.m4", + "{$this->getBuildDir()}/config.m4", 'CFLAGS="$CFLAGS -Werror -Wall -O3 -pthread -std=gnu90"', 'CFLAGS="$CFLAGS -pthread"' ); FileSystem::replaceFileStr( - "{$this->getSourceDir()}/src/php_spx.h", + "{$this->getBuildDir()}/src/php_spx.h", "extern zend_module_entry spx_module_entry;\n", "extern zend_module_entry spx_module_entry;;\n#define phpext_spx_ptr &spx_module_entry\n" ); - FileSystem::copy("{$this->getSourceDir()}/src/php_spx.h", "{$this->getSourceDir()}/php_spx.h"); + FileSystem::copy("{$this->getBuildDir()}/src/php_spx.h", "{$this->getBuildDir()}/php_spx.h"); return true; } @@ -50,9 +50,9 @@ public function patchBeforeBuildconf(): bool public function patchBeforeConfigure(): void { FileSystem::replaceFileStr( - "{$this->getSourceDir()}/Makefile.frag", + "{$this->getBuildDir()}/Makefile.frag", '@cp -r assets/web-ui/*', - "@cp -r {$this->getSourceDir()}/assets/web-ui/*", + "@cp -r {$this->getBuildDir()}/assets/web-ui/*", ); } diff --git a/src/Package/Extension/swoole.php b/src/Package/Extension/swoole.php index 70ca033a6..20257a226 100644 --- a/src/Package/Extension/swoole.php +++ b/src/Package/Extension/swoole.php @@ -45,7 +45,7 @@ public function validate(PackageInstaller $installer): void #[PatchDescription('Fix maximum version check for Swoole 6.2')] public function patchBeforeMake(): void { - FileSystem::replaceFileStr($this->getSourceDir() . '/ext-src/php_swoole_private.h', 'PHP_VERSION_ID > 80500', 'PHP_VERSION_ID >= 80600'); + FileSystem::replaceFileStr($this->getBuildDir() . '/ext-src/php_swoole_private.h', 'PHP_VERSION_ID > 80500', 'PHP_VERSION_ID >= 80600'); } #[BeforeStage('php', [php::class, 'makeForUnix'], 'ext-swoole')] @@ -56,7 +56,7 @@ public function patchBeforeMake2(): void // Fix swoole with event extension conflict bug $util_path = shell()->execWithResult('xcrun --show-sdk-path', false)[1][0] . '/usr/include/util.h'; FileSystem::replaceFileStr( - "{$this->getSourceDir()}/thirdparty/php/standard/proc_open.cc", + "{$this->getBuildDir()}/thirdparty/php/standard/proc_open.cc", 'include ', "include \"{$util_path}\"", ); diff --git a/src/Package/Extension/swow.php b/src/Package/Extension/swow.php index c49bc191b..296da5512 100644 --- a/src/Package/Extension/swow.php +++ b/src/Package/Extension/swow.php @@ -28,18 +28,10 @@ public function configureArg(PackageInstaller $installer): string #[BeforeStage('php', [php::class, 'buildconfForUnix'], 'ext-swow')] #[BeforeStage('php', [php::class, 'buildconfForWindows'], 'ext-swow')] - public function patchBeforeBuildconf(PackageInstaller $installer): bool + public function patchBeforeBuildconf(): bool { - $php_src = $installer->getTargetPackage('php')->getSourceDir(); - if (php::getPHPVersionID() >= 80000 && !is_link("{$php_src}/ext/swow")) { - if (PHP_OS_FAMILY === 'Windows') { - f_passthru("cd {$php_src}/ext && mklink /D swow swow-src\\ext"); - } else { - f_passthru("cd {$php_src}/ext && ln -s swow-src/ext swow"); - } - } // replace AC_DEFUN([SWOW_PKG_CHECK_MODULES] to AC_DEFUN([SWOW_PKG_CHECK_MODULES_STATIC] - FileSystem::replaceFileStr($this->getSourceDir() . '/ext/config.m4', 'AC_DEFUN([SWOW_PKG_CHECK_MODULES]', 'AC_DEFUN([SWOW_PKG_CHECK_MODULES_STATIC]'); + FileSystem::replaceFileStr($this->getBuildDir() . '/config.m4', 'AC_DEFUN([SWOW_PKG_CHECK_MODULES]', 'AC_DEFUN([SWOW_PKG_CHECK_MODULES_STATIC]'); return true; } } diff --git a/src/Package/Extension/trader.php b/src/Package/Extension/trader.php index 546b073a1..9f8492830 100644 --- a/src/Package/Extension/trader.php +++ b/src/Package/Extension/trader.php @@ -17,7 +17,7 @@ class trader extends PhpExtensionPackage #[BeforeStage('php', [php::class, 'buildconfForUnix'], 'ext-trader')] public function patchBeforeBuildconf(PackageInstaller $installer): bool { - FileSystem::replaceFileStr("{$this->getSourceDir()}/config.m4", 'PHP_TA', 'PHP_TRADER'); + FileSystem::replaceFileStr("{$this->getBuildDir()}/config.m4", 'PHP_TA', 'PHP_TRADER'); return true; } } diff --git a/src/Package/Extension/uv.php b/src/Package/Extension/uv.php index 68a796ada..ddc38353a 100644 --- a/src/Package/Extension/uv.php +++ b/src/Package/Extension/uv.php @@ -28,12 +28,12 @@ public function validate(): void public function patchBeforeBuild(): void { FileSystem::replaceFileStr( - "{$this->getSourceDir()}/php_uv.c", + "{$this->getBuildDir()}/php_uv.c", '#if !defined(PHP_WIN32) || defined(HAVE_SOCKET)', '#if !defined(PHP_WIN32) || (defined(HAVE_SOCKETS) && !defined(COMPILE_DL_SOCKETS))', ); FileSystem::replaceFileStr( - "{$this->getSourceDir()}/config.w32", + "{$this->getBuildDir()}/config.w32", 'CHECK_LIB("Ws2_32.lib","uv", PHP_UV);', "CHECK_LIB(\"Ws2_32.lib\",\"uv\" , PHP_UV);\n\tCHECK_LIB(\"dbghelp.lib\",\"uv\", PHP_UV);", ); @@ -45,7 +45,7 @@ public function patchBeforeSharedMake(PhpExtensionPackage $pkg): bool if (SystemTarget::getTargetOS() !== 'Linux' || SystemTarget::getTargetArch() !== 'aarch64') { return false; } - FileSystem::replaceFileRegex("{$pkg->getSourceDir()}/Makefile", '/^(LDFLAGS =.*)$/m', '$1 -luv -ldl -lrt -pthread'); + FileSystem::replaceFileRegex("{$pkg->getBuildDir()}/Makefile", '/^(LDFLAGS =.*)$/m', '$1 -luv -ldl -lrt -pthread'); return true; } } diff --git a/src/Package/Extension/xhprof.php b/src/Package/Extension/xhprof.php index 91c23facf..015c0c466 100644 --- a/src/Package/Extension/xhprof.php +++ b/src/Package/Extension/xhprof.php @@ -7,7 +7,6 @@ use Package\Target\php; use StaticPHP\Attribute\Package\BeforeStage; use StaticPHP\Attribute\Package\Extension; -use StaticPHP\Package\PackageInstaller; use StaticPHP\Package\PhpExtensionPackage; use StaticPHP\Util\FileSystem; @@ -15,21 +14,14 @@ class xhprof extends PhpExtensionPackage { #[BeforeStage('php', [php::class, 'buildconfForUnix'], 'ext-xhprof')] - public function patchBeforeBuildconf(PackageInstaller $installer): bool + public function patchBeforeBuildconf(): bool { - $php_src = $installer->getTargetPackage('php')->getSourceDir(); - $link = "{$php_src}/ext/xhprof"; - if (!is_link($link)) { - shell()->cd("{$php_src}/ext")->exec('ln -s xhprof-src/extension xhprof'); - - // patch config.m4 - FileSystem::replaceFileStr( - "{$this->getSourceDir()}/extension/config.m4", - 'if test -f $phpincludedir/ext/pcre/php_pcre.h; then', - 'if test -f $abs_srcdir/ext/pcre/php_pcre.h; then' - ); - return true; - } - return false; + // patch config.m4 + FileSystem::replaceFileStr( + "{$this->getBuildDir()}/config.m4", + 'if test -f $phpincludedir/ext/pcre/php_pcre.h; then', + 'if test -f $abs_srcdir/ext/pcre/php_pcre.h; then' + ); + return true; } } diff --git a/src/Package/Extension/xlswriter.php b/src/Package/Extension/xlswriter.php index bcb9cafdf..f2de3a815 100644 --- a/src/Package/Extension/xlswriter.php +++ b/src/Package/Extension/xlswriter.php @@ -34,7 +34,7 @@ public function getUnixConfigureArg(bool $shared, PackageInstaller $installer): public function patchConfigForStaticExpat(): void { FileSystem::replaceFileStr( - "{$this->getSourceDir()}/config.w32", + "{$this->getBuildDir()}/config.w32", "' /D USE_SYSTEM_MINIZIP", "' /D XML_STATIC /D USE_SYSTEM_MINIZIP" ); @@ -44,7 +44,7 @@ public function patchConfigForStaticExpat(): void #[PatchDescription('Fix Windows build: apply win32 patch and add UTF-8 BOM to theme.c')] public function patchBeforeMakeForWindows(): void { - $source_dir = $this->getSourceDir(); + $source_dir = $this->getBuildDir(); $theme_file = "{$source_dir}/library/libxlsx/src/theme.c"; // fix windows build with openssl extension duplicate symbol bug diff --git a/src/Package/Extension/xz.php b/src/Package/Extension/xz.php index a1f0d88d3..991d69a86 100644 --- a/src/Package/Extension/xz.php +++ b/src/Package/Extension/xz.php @@ -16,6 +16,6 @@ class xz extends PhpExtensionPackage #[BeforeStage('php', [php::class, 'buildconfForWindows'], 'ext-xz')] public function patchBeforeBuildconf(): void { - FileSystem::replaceFileStr($this->getSourceDir() . '/config.w32', 'true', 'PHP_XZ_SHARED'); + FileSystem::replaceFileStr($this->getBuildDir() . '/config.w32', 'true', 'PHP_XZ_SHARED'); } } diff --git a/src/Package/Extension/yac.php b/src/Package/Extension/yac.php index 4bf2cf664..6b5678bce 100644 --- a/src/Package/Extension/yac.php +++ b/src/Package/Extension/yac.php @@ -17,9 +17,9 @@ class yac extends PhpExtensionPackage #[BeforeStage('php', [php::class, 'buildconfForUnix'], 'ext-yac')] public function patchBeforeBuildconf(PackageInstaller $installer): bool { - FileSystem::replaceFileStr("{$this->getSourceDir()}/storage/allocator/yac_allocator.h", 'defined(HAVE_SHM_MMAP_ANON)', 'defined(YAC_ALLOCATOR_H)'); - FileSystem::replaceFileStr("{$this->getSourceDir()}/serializer/igbinary.c", '#ifdef YAC_ENABLE_IGBINARY', '#if 1'); - FileSystem::replaceFileStr("{$this->getSourceDir()}/serializer/json.c", '#if YAC_ENABLE_JSON', '#if 1'); + FileSystem::replaceFileStr("{$this->getBuildDir()}/storage/allocator/yac_allocator.h", 'defined(HAVE_SHM_MMAP_ANON)', 'defined(YAC_ALLOCATOR_H)'); + FileSystem::replaceFileStr("{$this->getBuildDir()}/serializer/igbinary.c", '#ifdef YAC_ENABLE_IGBINARY', '#if 1'); + FileSystem::replaceFileStr("{$this->getBuildDir()}/serializer/json.c", '#if YAC_ENABLE_JSON', '#if 1'); return true; } } diff --git a/src/Package/Target/php.php b/src/Package/Target/php.php index c9d31eafb..37e59feab 100644 --- a/src/Package/Target/php.php +++ b/src/Package/Target/php.php @@ -399,6 +399,60 @@ public function beforeBuild(PackageBuilder $builder, Package $package): void FileSystem::removeDir(BUILD_MODULES_PATH); } + /** + * Ensure every resolved extension with an artifact is present in php-src/ext. + * Extension sources are extracted to the default source dir (source/{name}); the + * in-tree PHP build requires each extension at php-src/ext/{name} with + * config.m4/config.w32 directly inside, so link the extension source root into + * place before buildconf: a symlink on Unix, an NTFS junction on Windows (with a + * plain copy as fallback), so patches always hit the tree actually compiled. + * + * Existing dirs are classified by markers: a link/junction is our own (refreshed + * when its target differs), a .spc-ext-sync marker is our Windows copy fallback + * (refreshed), anything else is bundled with php-src (e.g. zip, or imap on + * PHP < 8.5) and left untouched. + */ + #[BeforeStage('php', 'build')] + public function syncExtensionSources(PackageInstaller $installer): void + { + foreach ($installer->getResolvedPackages(PhpExtensionPackage::class) as $ext) { + if ($ext->getArtifact() === null) { + continue; + } + $source_root = $ext->getSourceRoot(); + if (!is_dir($source_root)) { + continue; + } + $ext_dir = FileSystem::convertPath(SOURCE_PATH . '/php-src/ext/' . $ext->getExtensionName()); + // already in place: bundled with php-src (getBuildDir prefers it for static + // builds), extracted directly in-tree, or a correct link/junction + if (realpath($ext_dir) === realpath($source_root)) { + continue; + } + if (FileSystem::isLink($ext_dir)) { + FileSystem::removeLink($ext_dir); + } elseif (is_dir($ext_dir)) { + if (!file_exists("{$ext_dir}/.spc-ext-sync")) { + // bundled with php-src, keep it (used when the extension is built shared only) + logger()->debug("Extension [{$ext->getName()}] keeps bundled source at {$ext_dir}."); + continue; + } + FileSystem::removeDir($ext_dir); + } + if (PHP_OS_FAMILY === 'Windows') { + logger()->debug("Junctioning extension [{$ext->getName()}] source root to {$ext_dir}..."); + if (!FileSystem::linkDir($source_root, $ext_dir)) { + logger()->notice("Cannot junction extension [{$ext->getName()}] source root, falling back to copy."); + FileSystem::copyDir($source_root, $ext_dir); + FileSystem::writeFile("{$ext_dir}/.spc-ext-sync", $source_root); + } + } else { + logger()->debug("Linking extension [{$ext->getName()}] source root to {$ext_dir}..."); + symlink($source_root, $ext_dir); + } + } + } + #[Stage('postInstall')] public function postInstall(TargetPackage $package, PackageInstaller $installer): void { diff --git a/src/StaticPHP/Artifact/Artifact.php b/src/StaticPHP/Artifact/Artifact.php index 666939fef..d37d350b8 100644 --- a/src/StaticPHP/Artifact/Artifact.php +++ b/src/StaticPHP/Artifact/Artifact.php @@ -320,14 +320,13 @@ public function getSourceDir(): string } /** - * Get source build root directory. - * It's only worked when 'source-root' is defined in artifact config. - * Normally it's equal to source dir. + * Get the root directory of the buildable source tree. + * Equals the source dir unless source.source-root is declared in artifact config. */ public function getSourceRoot(): string { - if (isset($this->config['metadata']['source-root'])) { - return FileSystem::convertPath($this->getSourceDir() . '/' . ltrim($this->config['metadata']['source-root'], '/')); + if (isset($this->config['source']['source-root'])) { + return FileSystem::convertPath($this->getSourceDir() . '/' . ltrim($this->config['source']['source-root'], '/')); } return $this->getSourceDir(); } diff --git a/src/StaticPHP/Artifact/ArtifactExtractor.php b/src/StaticPHP/Artifact/ArtifactExtractor.php index a51916867..2237e0bff 100644 --- a/src/StaticPHP/Artifact/ArtifactExtractor.php +++ b/src/StaticPHP/Artifact/ArtifactExtractor.php @@ -158,6 +158,9 @@ protected function extractSource(Artifact $artifact): int $extract_config = $artifact->getDownloadConfig('source')['extract'] ?? null; if (is_array($extract_config)) { $this->doSelectiveExtract($name, $cache_info, $extract_config); + if (PHP_OS_FAMILY === 'Windows') { + FileSystem::sanitizeSymlinks($target_path); + } $artifact->emitAfterSourceExtract($target_path); logger()->debug("Emitted after-source-extract hooks for [{$name}]"); return SPC_STATUS_EXTRACTED; @@ -180,6 +183,12 @@ protected function extractSource(Artifact $artifact): int logger()->info("Extracting source [{$name}] to {$target_path}..."); $this->doStandardExtract($name, $cache_info, $target_path); + if (PHP_OS_FAMILY === 'Windows') { + // symlinks in archives (e.g. LICENSE-zstd in php-ext-zstd) break + // tooling like xcopy, materialize them as regular files + FileSystem::sanitizeSymlinks($target_path); + } + // Emit after hooks $artifact->emitAfterSourceExtract($target_path); logger()->debug("Emitted after-source-extract hooks for [{$name}]"); diff --git a/src/StaticPHP/Artifact/Downloader/Type/PECL.php b/src/StaticPHP/Artifact/Downloader/Type/PECL.php index df2da341f..eb106a821 100644 --- a/src/StaticPHP/Artifact/Downloader/Type/PECL.php +++ b/src/StaticPHP/Artifact/Downloader/Type/PECL.php @@ -33,7 +33,7 @@ public function download(string $name, array $config, ArtifactDownloader $downlo $path = DOWNLOAD_PATH . DIRECTORY_SEPARATOR . $filename; logger()->debug("Downloading {$name} from URL: {$url}"); default_shell()->executeCurlDownload($url, $path, retries: $downloader->getRetry()); - $extract = $config['extract'] ?? ('php-src/ext/' . $this->getExtractName($name)); + $extract = $config['extract'] ?? null; return DownloadResult::archive($filename, $config, $extract, version: $version, downloader: static::class); } diff --git a/src/StaticPHP/Artifact/Downloader/Type/PIE.php b/src/StaticPHP/Artifact/Downloader/Type/PIE.php index 14996c5af..cd7e2c8f2 100644 --- a/src/StaticPHP/Artifact/Downloader/Type/PIE.php +++ b/src/StaticPHP/Artifact/Downloader/Type/PIE.php @@ -22,12 +22,13 @@ public function download(string $name, array $config, ArtifactDownloader $downlo if (!$dist_url || !$dist_type) { throw new DownloaderException("failed to find {$name} dist info from packagist"); } + $extract = $config['extract'] ?? null; $name = str_replace('/', '_', $config['repo']); $version = $first['version'] ?? 'unknown'; $filename = "{$name}-{$version}." . ($dist_type === 'zip' ? 'zip' : 'tar.gz'); $path = DOWNLOAD_PATH . DIRECTORY_SEPARATOR . $filename; default_shell()->executeCurlDownload($dist_url, $path, retries: $downloader->getRetry()); - return DownloadResult::archive($filename, $config, $config['extract'] ?? null, version: $version, downloader: static::class); + return DownloadResult::archive($filename, $config, $extract, version: $version, downloader: static::class); } public function checkUpdate(string $name, array $config, ?string $old_version, ArtifactDownloader $downloader): CheckUpdateResult @@ -56,7 +57,26 @@ protected function fetchPackagistInfo(string $name, array $config, ArtifactDownl if (!isset($data['packages'][$config['repo']]) || !is_array($data['packages'][$config['repo']])) { throw new DownloaderException("failed to find {$name} repo info from packagist"); } - $first = $data['packages'][$config['repo']][0] ?? []; + // p2 metadata is minified: each entry only carries fields changed vs the previous one, '__unset' removes a field + $releases = []; + $current = []; + foreach ($data['packages'][$config['repo']] as $release) { + $current = array_merge($current, $release); + $current = array_filter($current, static fn ($v) => $v !== '__unset'); + $releases[] = $current; + } + // packagist lists newest first including RC/beta/alpha — pick the newest stable release, + // unless prefer-stable is false, then take the newest tagged release even if it's a prerelease + $first = null; + if ($config['prefer-stable'] ?? true) { + foreach ($releases as $release) { + if (!preg_match('/[._-]?(?:alpha|a|beta|b|rc)[._-]?\d*$/i', $release['version'] ?? '')) { + $first = $release; + break; + } + } + } + $first ??= $releases[0] ?? []; if (!isset($first['php-ext'])) { throw new DownloaderException("failed to find {$name} php-ext info from packagist, maybe not a php extension package"); } diff --git a/src/StaticPHP/Config/ConfigValidator.php b/src/StaticPHP/Config/ConfigValidator.php index 6bb4e11bd..8498295a6 100644 --- a/src/StaticPHP/Config/ConfigValidator.php +++ b/src/StaticPHP/Config/ConfigValidator.php @@ -126,7 +126,7 @@ class ConfigValidator 'url' => [['url'], ['filename', 'extract', 'version']], 'bitbuckettag' => [['repo'], ['extract']], 'local' => [['dirname'], ['extract']], - 'pie' => [['repo'], ['extract']], + 'pie' => [['repo'], ['extract', 'prefer-stable']], 'pecl' => [['name'], ['extract', 'prefer-stable']], 'php-release' => [['domain'], ['extract']], 'custom' => [[], ['func']], @@ -327,8 +327,8 @@ private static function validateArtifactObjectField(string $item_name, array $da throw new ValidationException("Artifact source object of type '{$type}' must have required field '{$field}'"); } } - // check for unknown fields - $allowed_fields = array_merge(['type'], $required_fields, $optional_fields); + // check for unknown fields ('source-root': optional build source root subdir, applies to any type) + $allowed_fields = array_merge(['type', 'source-root'], $required_fields, $optional_fields); self::validateNoInvalidFields('artifact object', $item_name, $data, $allowed_fields); } diff --git a/src/StaticPHP/Package/Package.php b/src/StaticPHP/Package/Package.php index ba53f81df..adc353234 100644 --- a/src/StaticPHP/Package/Package.php +++ b/src/StaticPHP/Package/Package.php @@ -264,9 +264,8 @@ public function getSourceDir(): string } /** - * Get source build root directory. - * It's only worked when 'source-root' is defined in artifact config. - * Normally it's equal to source dir. + * Get the root directory of the buildable source tree. + * Equals the source dir unless source.source-root is declared in artifact config. */ public function getSourceRoot(): string { diff --git a/src/StaticPHP/Package/PhpExtensionPackage.php b/src/StaticPHP/Package/PhpExtensionPackage.php index 68a5b8a33..0c40daa96 100644 --- a/src/StaticPHP/Package/PhpExtensionPackage.php +++ b/src/StaticPHP/Package/PhpExtensionPackage.php @@ -59,6 +59,42 @@ public function getSourceDir(): string return parent::getSourceDir(); } + /** + * Directory the extension is actually compiled in; build-stage patches and + * validations must target this path to hit the tree being compiled. + * For in-tree builds (static, or shared with build-with-php) this is the + * source present in php-src/ext: a dir bundled with php-src (e.g. zip, or + * imap on PHP < 8.5), or the link (Unix) / junction (Windows, copy as + * fallback) placed by the php target's sync step. + * For shared (phpize) builds this equals getSourceRoot(). + */ + public function getBuildDir(): string + { + $path = SOURCE_PATH . '/php-src/ext/' . $this->getExtensionName(); + if (($this->isBuildStatic() || $this->isBuildWithPhp()) && is_dir($path)) { + return $path; + } + if ($this->getArtifact() === null) { + return $this->getSourceDir(); + } + return $this->getSourceRoot(); + } + + /** + * Root of the buildable artifact source tree: equals getSourceDir() unless the + * artifact declares source.source-root (e.g. a PIE package whose config.m4 + * lives in an ext/ subdir). This is what the php target links/junctions into + * php-src/ext/{name} before buildconf, and where shared (phpize) builds run; + * use getBuildDir() for the tree actually being compiled. + */ + public function getSourceRoot(): string + { + if ($this->getArtifact() === null) { + return $this->getSourceDir(); + } + return parent::getSourceRoot(); + } + public function getExtensionName(): string { return str_replace('ext-', '', $this->getName()); @@ -293,7 +329,7 @@ public function getSharedExtensionEnv(): array #[Stage] public function phpizeForUnix(array $env, PhpExtensionPackage $package): void { - shell()->cd($package->getSourceDir())->setEnv($env)->exec(BUILD_BIN_PATH . '/phpize'); + shell()->cd($package->getSourceRoot())->setEnv($env)->exec(BUILD_BIN_PATH . '/phpize'); } /** @@ -303,7 +339,7 @@ public function phpizeForUnix(array $env, PhpExtensionPackage $package): void public function configureForUnix(array $env, PhpExtensionPackage $package): void { $phpvars = getenv('SPC_EXTRA_PHP_VARS') ?: ''; - shell()->cd($package->getSourceDir()) + shell()->cd($package->getSourceRoot()) ->setEnv($env) ->exec( './configure ' . $this->getPhpConfigureArg(SystemTarget::getTargetOS(), true) . @@ -318,11 +354,15 @@ public function configureForUnix(array $env, PhpExtensionPackage $package): void #[Stage] public function makeForUnix(array $env, PhpExtensionPackage $package, PackageBuilder $builder): void { - shell()->cd($package->getSourceDir()) + shell()->cd($package->getSourceRoot()) ->setEnv($env) ->exec('make clean') ->exec("make -j{$builder->concurrency}") - ->exec('make install'); + ->exec('make install') + // distclean after install: phpize build residues (.lo/.libs/config.status) would + // poison a later static in-tree build sharing this source dir (php-src's make + // clean uses find, which does not follow the ext symlink) + ->exec('make distclean'); } /** diff --git a/src/StaticPHP/Util/FileSystem.php b/src/StaticPHP/Util/FileSystem.php index 61f2d7263..27cbf5c44 100644 --- a/src/StaticPHP/Util/FileSystem.php +++ b/src/StaticPHP/Util/FileSystem.php @@ -467,6 +467,102 @@ public static function removeFileIfExists(string $string): void } } + /** + * Whether the path is a link to another location. Besides symlinks this also + * detects NTFS directory junctions on Windows, which PHP's is_link() does not + * recognize (php-src only flags IO_REPARSE_TAG_SYMLINK as S_IFLNK in lstat), + * while readlink() resolves junctions just fine. + * + * @param string $path Path to check + */ + public static function isLink(string $path): bool + { + if (is_link($path)) { + return true; + } + return PHP_OS_FAMILY === 'Windows' && @readlink($path) !== false; + } + + /** + * Remove a link itself without touching its target. On Windows, directory + * links (symlinks and junctions) must be removed with rmdir(), whose + * underlying RemoveDirectory deletes the link rather than its contents. + * + * @param string $path Link path to remove + */ + public static function removeLink(string $path): bool + { + if (PHP_OS_FAMILY === 'Windows' && is_dir($path)) { + return rmdir($path); + } + return unlink($path); + } + + /** + * Link a directory to another location. On Windows an NTFS junction is + * created via mklink /J: unlike symlinks, junctions need no elevated + * privileges or developer mode, and they are transparent to all Win32 + * tooling (configure.js, nmake, buildconf). + * + * @param string $target Existing directory the link points to + * @param string $link Link path to create + * @return bool Whether the link was created + */ + public static function linkDir(string $target, string $link): bool + { + $target_path = self::convertPath($target); + $link_path = self::convertPath($link); + if (PHP_OS_FAMILY !== 'Windows') { + return symlink($target_path, $link_path); + } + [$code] = cmd(false)->execWithResult('mklink /J "' . $link_path . '" "' . $target_path . '"', false); + return $code === 0 && is_dir($link_path); + } + + /** + * Replace every symlink under a directory with a regular copy of its target, + * dropping links whose target does not exist. Used on Windows after archive + * extraction: tools like xcopy fail on (dangling) symlinks, e.g. the + * LICENSE-zstd link shipped in the php-ext-zstd tarball. + * + * @param string $dir Directory to sanitize + */ + public static function sanitizeSymlinks(string $dir): void + { + $dir = self::convertPath($dir); + if (!is_dir($dir)) { + return; + } + $items = scandir($dir); + if ($items === false) { + return; + } + foreach ($items as $item) { + if ($item === '.' || $item === '..') { + continue; + } + $path = $dir . DIRECTORY_SEPARATOR . $item; + if (is_link($path)) { + $target = realpath($path); + unlink($path); + if ($target === false) { + logger()->debug("Removed dangling symlink: {$path}"); + continue; + } + if (is_dir($target)) { + self::copyDir($target, $path); + self::sanitizeSymlinks($path); + } else { + copy($target, $path); + } + continue; + } + if (is_dir($path)) { + self::sanitizeSymlinks($path); + } + } + } + /** * Replace line in file that contains specific string * diff --git a/tests/StaticPHP/Artifact/ArtifactTest.php b/tests/StaticPHP/Artifact/ArtifactTest.php index 1a8526726..3a86ec3f9 100644 --- a/tests/StaticPHP/Artifact/ArtifactTest.php +++ b/tests/StaticPHP/Artifact/ArtifactTest.php @@ -278,15 +278,14 @@ public function testGetSourceRootDefaultsToSourceDir(): void $this->assertSame($artifact->getSourceDir(), $artifact->getSourceRoot()); } - public function testGetSourceRootUsesMetadataSourceRoot(): void + public function testGetSourceRootUsesSourceRootField(): void { $cache = $this->makeStubbedArtifactCache([]); ApplicationContext::initialize(); ApplicationContext::set(ArtifactCache::class, $cache); $artifact = new Artifact('my-pkg', [ - 'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz'], - 'metadata' => ['source-root' => 'src'], + 'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz', 'source-root' => 'src'], ]); $expected = $artifact->getSourceDir() . DIRECTORY_SEPARATOR . 'src';