mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-18 22:35:36 +08:00
Compare commits
282 Commits
2.6.0
...
a6d68b9ecf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6d68b9ecf | ||
|
|
d275d6cc69 | ||
|
|
fd89e83798 | ||
|
|
88f9172866 | ||
|
|
27f57cc23a | ||
|
|
f6c6011061 | ||
|
|
408b3b4060 | ||
|
|
55fd7ba8e6 | ||
|
|
e34eb502db | ||
|
|
292df38c10 | ||
|
|
a483c42893 | ||
|
|
bebcb978f6 | ||
|
|
ba7f994a71 | ||
|
|
9c7e2171f6 | ||
|
|
b7e8587608 | ||
|
|
0a33fba23e | ||
|
|
5370af4a84 | ||
|
|
df0d5fdbe8 | ||
|
|
7b33ee79dd | ||
|
|
ad8322b6a6 | ||
|
|
c43a10027b | ||
|
|
2ebdc4509c | ||
|
|
4cb4c9c6a5 | ||
|
|
9190335a70 | ||
|
|
4e244789a2 | ||
|
|
6de7b8e63b | ||
|
|
68e500eea0 | ||
|
|
840e09a684 | ||
|
|
d00a5223d3 | ||
|
|
3965a899c7 | ||
|
|
956667bf08 | ||
|
|
51e23d21cd | ||
|
|
44c6d6288e | ||
|
|
e5848086c3 | ||
|
|
138e5588e9 | ||
|
|
ab5828a560 | ||
|
|
1839f13149 | ||
|
|
320c21d272 | ||
|
|
e1e48922de | ||
|
|
3940f032af | ||
|
|
956688d9f4 | ||
|
|
7f45f4aeec | ||
|
|
78666ff7de | ||
|
|
dbdf10c75a | ||
|
|
2564da2615 | ||
|
|
bd25de15cc | ||
|
|
fcaa7c5f42 | ||
|
|
e36800b951 | ||
|
|
977fbaa8ef | ||
|
|
6ed62aa05c | ||
|
|
1d29ac228c | ||
|
|
a44d90671c | ||
|
|
d6b0912492 | ||
|
|
d588e6e4fd | ||
|
|
19ddddf618 | ||
|
|
a7bf1e9676 | ||
|
|
02f2837f86 | ||
|
|
0598eff9c5 | ||
|
|
a6364389ba | ||
|
|
9e9474a026 | ||
|
|
c23c5ae614 | ||
|
|
de3cb2f686 | ||
|
|
454b5a77ad | ||
|
|
4a493f3f6d | ||
|
|
61274cb865 | ||
|
|
8145a7536b | ||
|
|
e41d7899c7 | ||
|
|
88d99a7dea | ||
|
|
90823e3b17 | ||
|
|
3357f286ab | ||
|
|
ad080da026 | ||
|
|
25c2def710 | ||
|
|
604131b31d | ||
|
|
924da6ae69 | ||
|
|
12aadf18cc | ||
|
|
aa516522aa | ||
|
|
efb1ff5a93 | ||
|
|
aff9ff20e8 | ||
|
|
4c8a173213 | ||
|
|
b6240f16fb | ||
|
|
f5281535d9 | ||
|
|
96babd0939 | ||
|
|
464c2dca85 | ||
|
|
43dc04b4d0 | ||
|
|
e92f043cfa | ||
|
|
ff95b464ce | ||
|
|
69e1acd5ae | ||
|
|
e162a0ecd6 | ||
|
|
383045d1c0 | ||
|
|
e6bec8eb57 | ||
|
|
d2dea83c63 | ||
|
|
797aaf1ea8 | ||
|
|
5a7fd8c24e | ||
|
|
cc4ec4b598 | ||
|
|
55d78579c8 | ||
|
|
d94542049c | ||
|
|
6dec25b4f4 | ||
|
|
8c7049913b | ||
|
|
7edd8e6e19 | ||
|
|
2b4eb7127d | ||
|
|
9a3ce775a8 | ||
|
|
2ff78e531e | ||
|
|
03a33d87ff | ||
|
|
176e5627e5 | ||
|
|
5801d05047 | ||
|
|
815ba75c76 | ||
|
|
fff16b4019 | ||
|
|
e408a2cf1d | ||
|
|
b535600297 | ||
|
|
762c554468 | ||
|
|
a8c7057aba | ||
|
|
935f3fc37c | ||
|
|
fae2878c41 | ||
|
|
d30ccb810c | ||
|
|
8d75a85d6d | ||
|
|
625bfd1bec | ||
|
|
b04ffadf13 | ||
|
|
b142610800 | ||
|
|
5681722c09 | ||
|
|
9a3a536479 | ||
|
|
55f01149e4 | ||
|
|
6f5fc43490 | ||
|
|
cb62ffdfc2 | ||
|
|
40ac705c46 | ||
|
|
3ac4a71085 | ||
|
|
2963ced1d5 | ||
|
|
175aafee50 | ||
|
|
b75a3d6e81 | ||
|
|
5334727528 | ||
|
|
253206de22 | ||
|
|
5a60f4c02b | ||
|
|
63fcdd9db4 | ||
|
|
832c0230e2 | ||
|
|
e1028b0348 | ||
|
|
2433752273 | ||
|
|
1b0b551fc6 | ||
|
|
74849a8d48 | ||
|
|
ee0de6933f | ||
|
|
bd863dba34 | ||
|
|
f9af24e246 | ||
|
|
864c55feaf | ||
|
|
7628847a46 | ||
|
|
955b586908 | ||
|
|
32a1fd45df | ||
|
|
15fbde29d4 | ||
|
|
2cce02bfa9 | ||
|
|
7e35de8a0e | ||
|
|
9a98fd9cff | ||
|
|
f6d25153c6 | ||
|
|
853294e168 | ||
|
|
1a4296386a | ||
|
|
c464f78340 | ||
|
|
8992c96014 | ||
|
|
62a13f2da6 | ||
|
|
f9005757bd | ||
|
|
cd6aca832d | ||
|
|
1b4eb039ae | ||
|
|
6c47065686 | ||
|
|
9bfcea6feb | ||
|
|
58d979712e | ||
|
|
a0f99858e3 | ||
|
|
2abbb75f98 | ||
|
|
3f92df0865 | ||
|
|
be0b98c467 | ||
|
|
7d45415990 | ||
|
|
f3f581fe2d | ||
|
|
f6837079d3 | ||
|
|
5cd987ba3a | ||
|
|
8293160a01 | ||
|
|
625ee2703d | ||
|
|
c3b520c3d4 | ||
|
|
80687dfea3 | ||
|
|
7f9da6478f | ||
|
|
056971fcbc | ||
|
|
ba26359dde | ||
|
|
a2f0640b1b | ||
|
|
16a4245ad7 | ||
|
|
608a5559ac | ||
|
|
dfac385d21 | ||
|
|
40d602c82e | ||
|
|
fbd6360bda | ||
|
|
a0047e3ad7 | ||
|
|
437d6810b7 | ||
|
|
82ec7733ba | ||
|
|
1357990c4c | ||
|
|
61a9264802 | ||
|
|
4a70f260f3 | ||
|
|
bcea2007bd | ||
|
|
fcf2c967ab | ||
|
|
2f8e225abd | ||
|
|
5f3f999222 | ||
|
|
2bc9fef758 | ||
|
|
aec03b2f24 | ||
|
|
ba6ed137c6 | ||
|
|
32dc5d3cdb | ||
|
|
597db25178 | ||
|
|
c2d6b9ad2c | ||
|
|
4977286936 | ||
|
|
cc09184183 | ||
|
|
a9713c3bfa | ||
|
|
3af40a66dd | ||
|
|
6e70f16e1b | ||
|
|
16fccf8184 | ||
|
|
804468f7b9 | ||
|
|
7dc3b7c8ac | ||
|
|
b42409efd1 | ||
|
|
cb010d81ac | ||
|
|
15979d4636 | ||
|
|
1a164fa057 | ||
|
|
a76f49f927 | ||
|
|
becee5b426 | ||
|
|
4ecaffd908 | ||
|
|
74b1dda884 | ||
|
|
d6858e18df | ||
|
|
92284e92c9 | ||
|
|
f709f3bb18 | ||
|
|
8e2dffc3b5 | ||
|
|
ae569316ff | ||
|
|
eee2ff6d61 | ||
|
|
65b828c424 | ||
|
|
f10ba86218 | ||
|
|
24e19deb58 | ||
|
|
f7a3f80689 | ||
|
|
b4168d09b5 | ||
|
|
ba0ea5b40a | ||
|
|
04cefda66c | ||
|
|
8c6a708764 | ||
|
|
a1e76d9d02 | ||
|
|
82ee6f0dee | ||
|
|
d58534b07d | ||
|
|
f37c863092 | ||
|
|
e71f76288b | ||
|
|
d094824d76 | ||
|
|
d635b10e24 | ||
|
|
dca43d6d8d | ||
|
|
abf3bfb98e | ||
|
|
c46f8513dd | ||
|
|
92338d478e | ||
|
|
c1e68323c7 | ||
|
|
f64eb0dea5 | ||
|
|
c1870af1b1 | ||
|
|
71783088c0 | ||
|
|
7057a135cf | ||
|
|
5cb107b844 | ||
|
|
57b22782d3 | ||
|
|
cb0a90d1d9 | ||
|
|
1c439a01a1 | ||
|
|
2bfc8e92ef | ||
|
|
68548cf248 | ||
|
|
3a64feefd0 | ||
|
|
da75d2d707 | ||
|
|
3a85d96fa4 | ||
|
|
883cc4b6fd | ||
|
|
aa61a9e77b | ||
|
|
9de5c62136 | ||
|
|
7b3ea7e12e | ||
|
|
bafa67c8de | ||
|
|
0d3a80e582 | ||
|
|
5a401a5f92 | ||
|
|
0e88cdb258 | ||
|
|
e5cd3adf97 | ||
|
|
6253b7a912 | ||
|
|
fe455bf901 | ||
|
|
3a0d21eb44 | ||
|
|
45ec0cef24 | ||
|
|
1468bb99f0 | ||
|
|
3efabee153 | ||
|
|
962de5b25f | ||
|
|
b265d6dd56 | ||
|
|
302cf8345d | ||
|
|
d249391816 | ||
|
|
0a24a6af1f | ||
|
|
52f40b7f9f | ||
|
|
8fbe6ee8ff | ||
|
|
5a3a8db772 | ||
|
|
56cd6711ce | ||
|
|
29339b962c | ||
|
|
6b330fa869 | ||
|
|
5cc753dec4 | ||
|
|
f7a0f50f87 | ||
|
|
8466970a1f | ||
|
|
48cb87ada2 |
12
.github/pull_request_template.md
vendored
12
.github/pull_request_template.md
vendored
@@ -7,7 +7,11 @@
|
|||||||
> If your PR involves the changes mentioned below and completed the action, please tick the corresponding option.
|
> If your PR involves the changes mentioned below and completed the action, please tick the corresponding option.
|
||||||
> If a modification is not involved, please skip it directly.
|
> If a modification is not involved, please skip it directly.
|
||||||
|
|
||||||
- [ ] If you modified `*.php`, run `composer cs-fix` at local machine.
|
- If you modified `*.php` or `*.json`, run them locally to ensure your changes are valid:
|
||||||
- [ ] If it's an extension or dependency update, make sure adding related extensions in `src/global/test-extensions.php`.
|
- [ ] `PHP_CS_FIXER_IGNORE_ENV=1 composer cs-fix`
|
||||||
- [ ] If you changed the behavior of static-php-cli, update docs in `./docs/`.
|
- [ ] `composer analyse`
|
||||||
- [ ] If you updated `config/xxx.json` content, run `bin/spc dev:sort-config xxx`.
|
- [ ] `composer test`
|
||||||
|
- [ ] `bin/spc dev:sort-config`
|
||||||
|
- If it's an extension or dependency update, please ensure the following:
|
||||||
|
- [ ] Add your test combination to `src/globals/test-extensions.php`.
|
||||||
|
- [ ] If adding new or fixing bugs, add commit message containing `extension test` or `test extensions` to trigger full test suite.
|
||||||
|
|||||||
8
.github/workflows/ext-matrix-tests.yml
vendored
8
.github/workflows/ext-matrix-tests.yml
vendored
@@ -1,16 +1,14 @@
|
|||||||
name: "Extension matrix tests"
|
name: "Extension Matrix Tests"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
pull_request:
|
push:
|
||||||
branches: [ "main" ]
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/ext-matrix-tests.yml'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: "${{ matrix.extension }} (PHP ${{ matrix.php-version }} on ${{ matrix.operating-system }})"
|
name: "${{ matrix.extension }} (PHP ${{ matrix.php-version }} on ${{ matrix.operating-system }})"
|
||||||
runs-on: ${{ matrix.operating-system }}
|
runs-on: ${{ matrix.operating-system }}
|
||||||
|
if: contains(github.event.head_commit.message, 'extension test') || contains(github.event.head_commit.message, 'test extensions')
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -190,7 +190,7 @@ jobs:
|
|||||||
echo "UPX_CMD=$(php src/globals/test-extensions.php upx)" >> $GITHUB_ENV
|
echo "UPX_CMD=$(php src/globals/test-extensions.php upx)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: "Run Build Tests (download)"
|
- name: "Run Build Tests (download)"
|
||||||
run: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} php src/globals/test-extensions.php download_cmd ${{ matrix.os }} ${{ matrix.php }}
|
run: php src/globals/test-extensions.php download_cmd ${{ matrix.os }} ${{ matrix.php }}
|
||||||
|
|
||||||
- name: "Run Build Tests (build)"
|
- name: "Run Build Tests (build)"
|
||||||
run: php src/globals/test-extensions.php build_cmd ${{ matrix.os }} ${{ matrix.php }}
|
run: php src/globals/test-extensions.php build_cmd ${{ matrix.os }} ${{ matrix.php }}
|
||||||
|
|||||||
@@ -69,4 +69,5 @@ return (new PhpCsFixer\Config())
|
|||||||
])
|
])
|
||||||
->setFinder(
|
->setFinder(
|
||||||
PhpCsFixer\Finder::create()->in([__DIR__ . '/src', __DIR__ . '/tests/SPC'])
|
PhpCsFixer\Finder::create()->in([__DIR__ . '/src', __DIR__ . '/tests/SPC'])
|
||||||
);
|
)
|
||||||
|
->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect());
|
||||||
|
|||||||
10
README-zh.md
10
README-zh.md
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
**static-php-cli**是一个用于静态编译、构建 PHP 解释器的工具,支持众多流行扩展。
|
**static-php-cli**是一个用于静态编译、构建 PHP 解释器的工具,支持众多流行扩展。
|
||||||
|
|
||||||
目前 static-php-cli 支持 `cli`、`fpm`、`embed` 和 `micro` SAPI。
|
目前 static-php-cli 支持 `cli`、`fpm`、`embed`、`micro` 和 `frankenphp` SAPI。
|
||||||
|
|
||||||
**static-php-cli**也支持将 PHP 代码和 PHP 运行时打包为一个文件并运行。
|
**static-php-cli**也支持将 PHP 代码和 PHP 运行时打包为一个文件并运行。
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ bin/spc micro:combine my-app.phar -I "memory_limit=4G" -I "disable_functions=sys
|
|||||||
|
|
||||||
如果你知道 [embed SAPI](https://github.com/php/php-src/tree/master/sapi/embed),你应该知道如何使用它。对于有可能编译用到引入其他库的问题,你可以使用 `buildroot/bin/php-config` 来获取编译时的配置。
|
如果你知道 [embed SAPI](https://github.com/php/php-src/tree/master/sapi/embed),你应该知道如何使用它。对于有可能编译用到引入其他库的问题,你可以使用 `buildroot/bin/php-config` 来获取编译时的配置。
|
||||||
|
|
||||||
另外,有关如何使用此功能的高级示例,请查看[如何使用它构建 FrankenPHP 的静态版本](https://github.com/dunglas/frankenphp/blob/main/docs/static.md)。
|
另外,有关如何使用此功能的高级示例,请查看[如何使用它构建 FrankenPHP 的静态版本](https://github.com/php/frankenphp/blob/main/docs/static.md)。
|
||||||
|
|
||||||
## 贡献
|
## 贡献
|
||||||
|
|
||||||
@@ -292,6 +292,12 @@ bin/spc micro:combine my-app.phar -I "memory_limit=4G" -I "disable_functions=sys
|
|||||||
|
|
||||||
你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。你捐赠的一部分将会被用于维护 **static-php.dev** 服务器。
|
你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。你捐赠的一部分将会被用于维护 **static-php.dev** 服务器。
|
||||||
|
|
||||||
|
**特别赞助商**:
|
||||||
|
|
||||||
|
<a href="https://beyondco.de/"><img src="/docs/public/images/beyondcode-seeklogo.png" width="300" alt="Beyond Code Logo" /></a>
|
||||||
|
|
||||||
|
<a href="https://nativephp.com/"><img src="/docs/public/images/nativephp-logo.svg" width="300" alt="NativePHP Logo" /></a>
|
||||||
|
|
||||||
## 开源协议
|
## 开源协议
|
||||||
|
|
||||||
本项目采用 MIT License 许可开源,下面是类似的项目:
|
本项目采用 MIT License 许可开源,下面是类似的项目:
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -10,7 +10,7 @@
|
|||||||
**static-php-cli** is a powerful tool designed for building static, standalone PHP runtime
|
**static-php-cli** is a powerful tool designed for building static, standalone PHP runtime
|
||||||
with popular extensions.
|
with popular extensions.
|
||||||
|
|
||||||
Static PHP built by **static-php-cli** supports `cli`, `fpm`, `embed` and `micro` SAPI.
|
Static PHP built by **static-php-cli** supports `cli`, `fpm`, `embed`, `micro` and `frankenphp` SAPI.
|
||||||
|
|
||||||
**static-php-cli** also has the ability to package PHP projects
|
**static-php-cli** also has the ability to package PHP projects
|
||||||
along with the PHP interpreter into one single executable file.
|
along with the PHP interpreter into one single executable file.
|
||||||
@@ -302,7 +302,7 @@ If you know [embed SAPI](https://github.com/php/php-src/tree/master/sapi/embed),
|
|||||||
You may require the introduction of other libraries during compilation,
|
You may require the introduction of other libraries during compilation,
|
||||||
you can use `buildroot/bin/php-config` to obtain the compile-time configuration.
|
you can use `buildroot/bin/php-config` to obtain the compile-time configuration.
|
||||||
|
|
||||||
For an advanced example of how to use this feature, take a look at [how to use it to build a static version of FrankenPHP](https://github.com/dunglas/frankenphp/blob/main/docs/static.md).
|
For an advanced example of how to use this feature, take a look at [how to use it to build a static version of FrankenPHP](https://github.com/php/frankenphp/blob/main/docs/static.md).
|
||||||
|
|
||||||
## Contribution
|
## Contribution
|
||||||
|
|
||||||
@@ -317,6 +317,12 @@ Now there is a [static-php](https://github.com/static-php) organization, which i
|
|||||||
|
|
||||||
You can sponsor me or my project from [GitHub Sponsor](https://github.com/crazywhalecc). A portion of your donation will be used to maintain the **static-php.dev** server.
|
You can sponsor me or my project from [GitHub Sponsor](https://github.com/crazywhalecc). A portion of your donation will be used to maintain the **static-php.dev** server.
|
||||||
|
|
||||||
|
**Special thanks to sponsors below**:
|
||||||
|
|
||||||
|
<a href="https://beyondco.de/"><img src="/docs/public/images/beyondcode-seeklogo.png" width="300" alt="Beyond Code Logo" /></a>
|
||||||
|
|
||||||
|
<a href="https://nativephp.com/"><img src="/docs/public/images/nativephp-logo.svg" width="300" alt="NativePHP Logo" /></a>
|
||||||
|
|
||||||
## Open-Source License
|
## Open-Source License
|
||||||
|
|
||||||
This project itself is based on MIT License,
|
This project itself is based on MIT License,
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
# This file is using docker to run commands
|
# This file is using docker to run commands
|
||||||
SPC_DOCKER_VERSION=v3
|
SPC_DOCKER_VERSION=v4
|
||||||
|
|
||||||
# Detect docker can run
|
# Detect docker can run
|
||||||
if ! which docker >/dev/null; then
|
if ! which docker >/dev/null; then
|
||||||
echo "Docker is not installed, please install docker first !"
|
echo "Docker is not installed, please install docker first !"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
DOCKER_EXECUTABLE="docker"
|
DOCKER_EXECUTABLE="docker"
|
||||||
# shellcheck disable=SC2046
|
# shellcheck disable=SC2046
|
||||||
@@ -22,27 +24,48 @@ if [ $(id -u) -ne 0 ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Convert uname to gnu arch
|
||||||
|
CURRENT_ARCH=$(uname -m)
|
||||||
# to check if qemu-docker run
|
if [ "$CURRENT_ARCH" = "arm64" ]; then
|
||||||
if [ "$SPC_USE_ARCH" = "" ]; then
|
CURRENT_ARCH=aarch64
|
||||||
SPC_USE_ARCH=x86_64
|
|
||||||
fi
|
fi
|
||||||
|
if [ -z "$SPC_USE_ARCH" ]; then
|
||||||
|
SPC_USE_ARCH=$CURRENT_ARCH
|
||||||
|
fi
|
||||||
|
# parse SPC_USE_ARCH
|
||||||
case $SPC_USE_ARCH in
|
case $SPC_USE_ARCH in
|
||||||
x86_64)
|
x86_64|amd64)
|
||||||
ALPINE_FROM=alpine:edge
|
SPC_USE_ARCH=x86_64
|
||||||
|
if [ "$CURRENT_ARCH" != "x86_64" ]; then
|
||||||
|
PLATFORM_ARG="--platform linux/amd64"
|
||||||
|
ALPINE_FROM=multiarch/alpine:x86_64-edge
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
aarch64)
|
aarch64|arm64)
|
||||||
ALPINE_FROM=multiarch/alpine:aarch64-edge
|
SPC_USE_ARCH=aarch64
|
||||||
# shellcheck disable=SC2039
|
if [ "$CURRENT_ARCH" != "aarch64" ]; then
|
||||||
echo -e "\e[033m* Using different arch needs to setup qemu-static for docker !\e[0m"
|
PLATFORM_ARG="--platform linux/arm64"
|
||||||
$DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null
|
ALPINE_FROM=multiarch/alpine:aarch64-edge
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Current arch is not supported to run in docker: $SPC_USE_ARCH"
|
echo "Current arch is not supported to run in docker: $SPC_USE_ARCH"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
# if ALPINE_FROM is not set, use alpine:edge
|
||||||
|
if [ -z "$ALPINE_FROM" ]; then
|
||||||
|
ALPINE_FROM=alpine:edge
|
||||||
|
fi
|
||||||
|
if [ "$SPC_USE_ARCH" != "$CURRENT_ARCH" ]; then
|
||||||
|
echo "* Using different arch needs to setup qemu-static for docker !"
|
||||||
|
ALPINE_FROM=multiarch/alpine:$SPC_USE_ARCH-edge
|
||||||
|
if [ "$(uname -s)" = "Linux" ]; then
|
||||||
|
$DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
ALPINE_FROM=alpine:edge
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$SPC_USE_MIRROR" = "yes" ]; then
|
if [ "$SPC_USE_MIRROR" = "yes" ]; then
|
||||||
SPC_USE_MIRROR="RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories"
|
SPC_USE_MIRROR="RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories"
|
||||||
@@ -53,7 +76,7 @@ fi
|
|||||||
# Detect docker env is setup
|
# Detect docker env is setup
|
||||||
if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION; then
|
if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION; then
|
||||||
echo "Docker container does not exist. Building docker image ..."
|
echo "Docker container does not exist. Building docker image ..."
|
||||||
$DOCKER_EXECUTABLE build -t cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION -f- . <<EOF
|
$DOCKER_EXECUTABLE build $PLATFORM_ARG -t cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION -f- . <<EOF
|
||||||
FROM $ALPINE_FROM
|
FROM $ALPINE_FROM
|
||||||
$SPC_USE_MIRROR
|
$SPC_USE_MIRROR
|
||||||
RUN apk update; \
|
RUN apk update; \
|
||||||
@@ -84,7 +107,8 @@ RUN apk update; \
|
|||||||
wget \
|
wget \
|
||||||
xz \
|
xz \
|
||||||
gettext-dev \
|
gettext-dev \
|
||||||
binutils-gold
|
binutils-gold \
|
||||||
|
patchelf
|
||||||
|
|
||||||
RUN curl -#fSL https://dl.static-php.dev/static-php-cli/bulk/php-8.4.4-cli-linux-\$(uname -m).tar.gz | tar -xz -C /usr/local/bin && \
|
RUN curl -#fSL https://dl.static-php.dev/static-php-cli/bulk/php-8.4.4-cli-linux-\$(uname -m).tar.gz | tar -xz -C /usr/local/bin && \
|
||||||
chmod +x /usr/local/bin/php
|
chmod +x /usr/local/bin/php
|
||||||
@@ -147,7 +171,7 @@ if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then
|
|||||||
echo "* ./pkgroot: $(pwd)/pkgroot"
|
echo "* ./pkgroot: $(pwd)/pkgroot"
|
||||||
echo "*"
|
echo "*"
|
||||||
set -ex
|
set -ex
|
||||||
$DOCKER_EXECUTABLE run --rm $INTERACT $ENV_LIST $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION
|
$DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION /bin/bash
|
||||||
else
|
else
|
||||||
$DOCKER_EXECUTABLE run --rm $INTERACT $ENV_LIST $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION bin/spc $@
|
$DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION bin/spc $@
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# This file is using docker to run commands
|
||||||
|
SPC_DOCKER_VERSION=v4
|
||||||
|
|
||||||
# Detect docker can run
|
# Detect docker can run
|
||||||
if ! which docker >/dev/null; then
|
if ! which docker >/dev/null; then
|
||||||
echo "Docker is not installed, please install docker first !"
|
echo "Docker is not installed, please install docker first !"
|
||||||
@@ -19,35 +24,47 @@ if [ $(id -u) -ne 0 ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Convert uname to gnu arch
|
||||||
|
CURRENT_ARCH=$(uname -m)
|
||||||
# to check if qemu-docker run
|
if [ "$CURRENT_ARCH" = "arm64" ]; then
|
||||||
if [ "$SPC_USE_ARCH" = "" ]; then
|
CURRENT_ARCH=aarch64
|
||||||
SPC_USE_ARCH=current
|
|
||||||
fi
|
fi
|
||||||
|
if [ -z "$SPC_USE_ARCH" ]; then
|
||||||
|
SPC_USE_ARCH=$CURRENT_ARCH
|
||||||
|
fi
|
||||||
|
# parse SPC_USE_ARCH
|
||||||
case $SPC_USE_ARCH in
|
case $SPC_USE_ARCH in
|
||||||
current)
|
x86_64|amd64)
|
||||||
BASE_ARCH=$(uname -m)
|
SPC_USE_ARCH=x86_64
|
||||||
if [ "$BASE_ARCH" = "arm64" ]; then
|
SPC_USE_ARCH_DOCKER=amd64
|
||||||
BASE_ARCH=aarch64
|
if [ "$CURRENT_ARCH" != "x86_64" ]; then
|
||||||
|
PLATFORM_ARG="--platform linux/amd64"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
aarch64)
|
aarch64|arm64)
|
||||||
BASE_ARCH=aarch64
|
SPC_USE_ARCH=aarch64
|
||||||
# shellcheck disable=SC2039
|
SPC_USE_ARCH_DOCKER=arm64
|
||||||
echo -e "\e[033m* Using different arch needs to setup qemu-static for docker !\e[0m"
|
if [ "$CURRENT_ARCH" != "aarch64" ]; then
|
||||||
$DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null
|
PLATFORM_ARG="--platform linux/arm64"
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Current arch is not supported to run in docker: $SPC_USE_ARCH"
|
echo "Current arch is not supported to run in docker: $SPC_USE_ARCH"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
# detect if we need to use qemu-static
|
||||||
|
if [ "$SPC_USE_ARCH" != "$CURRENT_ARCH" ]; then
|
||||||
|
if [ "$(uname -s)" = "Linux" ]; then
|
||||||
|
echo "* Using different arch needs to setup qemu-static for docker !"
|
||||||
|
$DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static --reset -p yes > /dev/null
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Detect docker env is setup
|
# Detect docker env is setup
|
||||||
if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-gnu-$SPC_USE_ARCH; then
|
if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION; then
|
||||||
echo "Docker container does not exist. Building docker image ..."
|
echo "Docker container does not exist. Building docker image ..."
|
||||||
$DOCKER_EXECUTABLE build -t cwcc-spc-gnu-$SPC_USE_ARCH -f- . <<EOF
|
$DOCKER_EXECUTABLE buildx build $PLATFORM_ARG -t cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION -f- . <<EOF
|
||||||
FROM centos:7
|
FROM centos:7
|
||||||
RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/*.repo && \
|
RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/*.repo && \
|
||||||
sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/*.repo && \
|
sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/*.repo && \
|
||||||
@@ -59,7 +76,7 @@ RUN yum clean all && \
|
|||||||
|
|
||||||
RUN yum install -y centos-release-scl
|
RUN yum install -y centos-release-scl
|
||||||
|
|
||||||
RUN if [ "$BASE_ARCH" = "aarch64" ]; then \
|
RUN if [ "$SPC_USE_ARCH" = "aarch64" ]; then \
|
||||||
sed -i 's|mirror.centos.org/centos|vault.centos.org/altarch|g' /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo ; \
|
sed -i 's|mirror.centos.org/centos|vault.centos.org/altarch|g' /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo ; \
|
||||||
sed -i 's|mirror.centos.org/centos|vault.centos.org/altarch|g' /etc/yum.repos.d/CentOS-SCLo-scl.repo ; \
|
sed -i 's|mirror.centos.org/centos|vault.centos.org/altarch|g' /etc/yum.repos.d/CentOS-SCLo-scl.repo ; \
|
||||||
else \
|
else \
|
||||||
@@ -74,7 +91,12 @@ RUN echo "source scl_source enable devtoolset-10" >> /etc/bashrc
|
|||||||
RUN source /etc/bashrc
|
RUN source /etc/bashrc
|
||||||
RUN yum install -y which
|
RUN yum install -y which
|
||||||
|
|
||||||
RUN curl -o cmake.tgz -fsSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$BASE_ARCH.tar.gz && \
|
RUN curl -fsSL -o patchelf.tgz https://github.com/NixOS/patchelf/releases/download/0.18.0/patchelf-0.18.0-$SPC_USE_ARCH.tar.gz && \
|
||||||
|
mkdir -p /patchelf && \
|
||||||
|
tar -xzf patchelf.tgz -C /patchelf --strip-components=1 && \
|
||||||
|
cp /patchelf/bin/patchelf /usr/bin/
|
||||||
|
|
||||||
|
RUN curl -o cmake.tgz -#fSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$SPC_USE_ARCH.tar.gz && \
|
||||||
mkdir /cmake && \
|
mkdir /cmake && \
|
||||||
tar -xzf cmake.tgz -C /cmake --strip-components 1
|
tar -xzf cmake.tgz -C /cmake --strip-components 1
|
||||||
|
|
||||||
@@ -85,11 +107,11 @@ ADD ./bin/setup-runtime /app/bin/setup-runtime
|
|||||||
ADD ./bin/spc /app/bin/spc
|
ADD ./bin/spc /app/bin/spc
|
||||||
RUN /app/bin/setup-runtime
|
RUN /app/bin/setup-runtime
|
||||||
RUN /app/bin/php /app/bin/composer install --no-dev
|
RUN /app/bin/php /app/bin/composer install --no-dev
|
||||||
ENV PATH="/app/bin:/cmake/bin:$PATH"
|
|
||||||
ENV SPC_LIBC=glibc
|
ENV SPC_LIBC=glibc
|
||||||
|
ENV PATH="/app/bin:/cmake/bin:/opt/rh/devtoolset-10/root/usr/bin:\$PATH"
|
||||||
|
|
||||||
ADD ./config/env.ini /app/config/env.ini
|
ADD ./config/env.ini /app/config/env.ini
|
||||||
RUN bin/spc doctor --auto-fix --debug
|
RUN CC=gcc bin/spc doctor --auto-fix --debug
|
||||||
|
|
||||||
RUN curl -o make.tgz -fsSL https://ftp.gnu.org/gnu/make/make-4.4.tar.gz && \
|
RUN curl -o make.tgz -fsSL https://ftp.gnu.org/gnu/make/make-4.4.tar.gz && \
|
||||||
tar -zxvf make.tgz && \
|
tar -zxvf make.tgz && \
|
||||||
@@ -106,7 +128,6 @@ RUN curl -o automake.tgz -fsSL https://ftp.gnu.org/gnu/automake/automake-1.17.ta
|
|||||||
make && \
|
make && \
|
||||||
make install && \
|
make install && \
|
||||||
ln -sf /usr/local/bin/automake /usr/bin/automake
|
ln -sf /usr/local/bin/automake /usr/bin/automake
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -132,11 +153,7 @@ if [ -f "$(pwd)/craft.yml" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Apply env in temp env file
|
# Apply env in temp env file
|
||||||
echo 'CC=/opt/rh/devtoolset-10/root/usr/bin/gcc' > /tmp/spc-gnu-docker.env
|
echo 'SPC_DEFAULT_C_FLAGS=-fPIC' > /tmp/spc-gnu-docker.env
|
||||||
echo 'CXX=/opt/rh/devtoolset-10/root/usr/bin/g++' >> /tmp/spc-gnu-docker.env
|
|
||||||
echo 'AR=/opt/rh/devtoolset-10/root/usr/bin/ar' >> /tmp/spc-gnu-docker.env
|
|
||||||
echo 'LD=/opt/rh/devtoolset-10/root/usr/bin/ld' >> /tmp/spc-gnu-docker.env
|
|
||||||
echo 'SPC_DEFAULT_C_FLAGS=-fPIE -fPIC' >> /tmp/spc-gnu-docker.env
|
|
||||||
echo 'SPC_LIBC=glibc' >> /tmp/spc-gnu-docker.env
|
echo 'SPC_LIBC=glibc' >> /tmp/spc-gnu-docker.env
|
||||||
echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"' >> /tmp/spc-gnu-docker.env
|
echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"' >> /tmp/spc-gnu-docker.env
|
||||||
echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env
|
echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env
|
||||||
@@ -168,7 +185,7 @@ if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then
|
|||||||
echo "* ./pkgroot: $(pwd)/pkgroot"
|
echo "* ./pkgroot: $(pwd)/pkgroot"
|
||||||
echo "*"
|
echo "*"
|
||||||
set -ex
|
set -ex
|
||||||
$DOCKER_EXECUTABLE run --rm -it --privileged $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH
|
$DOCKER_EXECUTABLE run $PLATFORM_ARG --privileged --rm -it $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION /bin/bash
|
||||||
else
|
else
|
||||||
$DOCKER_EXECUTABLE run --rm $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH bin/spc $@
|
$DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION bin/spc $@
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ SPC_CONCURRENCY=${CPU_COUNT}
|
|||||||
SPC_SKIP_PHP_VERSION_CHECK="no"
|
SPC_SKIP_PHP_VERSION_CHECK="no"
|
||||||
; Ignore some check item for bin/spc doctor command, comma separated (e.g. SPC_SKIP_DOCTOR_CHECK_ITEMS="if homebrew has installed")
|
; Ignore some check item for bin/spc doctor command, comma separated (e.g. SPC_SKIP_DOCTOR_CHECK_ITEMS="if homebrew has installed")
|
||||||
SPC_SKIP_DOCTOR_CHECK_ITEMS=""
|
SPC_SKIP_DOCTOR_CHECK_ITEMS=""
|
||||||
|
; extra modules that xcaddy will include in the FrankenPHP build
|
||||||
|
SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/frankenphp/caddy --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy --with github.com/dunglas/caddy-cbrotli"
|
||||||
|
|
||||||
; EXTENSION_DIR where the built php will look for extension when a .ini instructs to load them
|
; EXTENSION_DIR where the built php will look for extension when a .ini instructs to load them
|
||||||
; only useful for builds targeting not pure-static linking
|
; only useful for builds targeting not pure-static linking
|
||||||
; default paths
|
; default paths
|
||||||
@@ -52,6 +55,8 @@ SPC_SKIP_DOCTOR_CHECK_ITEMS=""
|
|||||||
; EXTENSION_DIR=
|
; EXTENSION_DIR=
|
||||||
|
|
||||||
[windows]
|
[windows]
|
||||||
|
; build target: win7-static
|
||||||
|
SPC_TARGET=native-windows
|
||||||
; php-sdk-binary-tools path
|
; php-sdk-binary-tools path
|
||||||
PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools"
|
PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools"
|
||||||
; upx executable path
|
; upx executable path
|
||||||
@@ -60,22 +65,37 @@ UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe"
|
|||||||
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static
|
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static
|
||||||
|
|
||||||
[linux]
|
[linux]
|
||||||
; include PATH for musl libc.
|
; Linux can use different build toolchains.
|
||||||
|
; - musl (default, when SPC_LIBC=musl): used for general linux distros, can build `musl` (statically linked) only.
|
||||||
|
; - zig (will become default): usable on all Linux distros, can build `-musl`, `arch-linux-musl -dynamic` and `arch-linux-gnu` targets. Can specify version such as `x86_64-linux-gnu.2.17`.
|
||||||
|
; - 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, switch to SPC_TARGET with zig toolchain instead
|
||||||
SPC_LIBC=musl
|
SPC_LIBC=musl
|
||||||
|
|
||||||
|
; Recommended: specify your target here. Zig toolchain will be used.
|
||||||
|
; examples:
|
||||||
|
; `native-native-gnu` - links against glibc, current OS version
|
||||||
|
; `native-native-gnu.2.17` - links against glibc, version 2.17
|
||||||
|
; `native-native` - static linking against musl
|
||||||
|
; `native-native-musl -dynamic` - links against musl libc
|
||||||
|
; SPC_TARGET=
|
||||||
|
|
||||||
; compiler environments
|
; compiler environments
|
||||||
CC=${SPC_LINUX_DEFAULT_CC}
|
CC=${SPC_LINUX_DEFAULT_CC}
|
||||||
CXX=${SPC_LINUX_DEFAULT_CXX}
|
CXX=${SPC_LINUX_DEFAULT_CXX}
|
||||||
AR=${SPC_LINUX_DEFAULT_AR}
|
AR=${SPC_LINUX_DEFAULT_AR}
|
||||||
LD=ld.gold
|
LD=${SPC_LINUX_DEFAULT_LD}
|
||||||
; default compiler flags, used in CMake toolchain file, openssl and pkg-config build
|
; default compiler flags, used in CMake toolchain file, openssl and pkg-config build
|
||||||
SPC_DEFAULT_C_FLAGS="-fpic -Os"
|
SPC_DEFAULT_C_FLAGS="-fPIC -Os -Wno-error=date-time"
|
||||||
SPC_DEFAULT_CXX_FLAGS="-fpic -Os"
|
SPC_DEFAULT_CXX_FLAGS="-fPIC -Os -Wno-error=date-time"
|
||||||
; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated)
|
; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated)
|
||||||
SPC_EXTRA_LIBS=
|
SPC_EXTRA_LIBS=
|
||||||
; upx executable path
|
; upx executable path
|
||||||
UPX_EXEC=${PKG_ROOT_PATH}/bin/upx
|
UPX_EXEC=${PKG_ROOT_PATH}/bin/upx
|
||||||
; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches
|
; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches
|
||||||
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream
|
SPC_MICRO_PATCHES=cli_checks,disable_huge_page
|
||||||
|
|
||||||
; *** default build command for building php ***
|
; *** default build command for building php ***
|
||||||
; buildconf command
|
; buildconf command
|
||||||
@@ -89,23 +109,26 @@ SPC_CMD_VAR_PHP_EMBED_TYPE="static"
|
|||||||
|
|
||||||
; *** default build vars for building php ***
|
; *** default build vars for building php ***
|
||||||
; CFLAGS for configuring php
|
; CFLAGS for configuring php
|
||||||
SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -fpie"
|
SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -fPIE"
|
||||||
; CPPFLAGS for configuring php
|
; CPPFLAGS for configuring php
|
||||||
SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS="-I${BUILD_INCLUDE_PATH}"
|
SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS="-I${BUILD_INCLUDE_PATH}"
|
||||||
; LDFLAGS for configuring php
|
; LDFLAGS for configuring php
|
||||||
SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}"
|
SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}"
|
||||||
; LIBS for configuring php
|
; LIBS for configuring php
|
||||||
SPC_CMD_VAR_PHP_CONFIGURE_LIBS="-ldl -lpthread -lm"
|
SPC_CMD_VAR_PHP_CONFIGURE_LIBS="-ldl -lrt -lpthread -lm -lresolv -lutil"
|
||||||
; EXTRA_CFLAGS for `make` php
|
; EXTRA_CFLAGS for `make` php
|
||||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fpie ${SPC_DEFAULT_C_FLAGS}"
|
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE ${SPC_DEFAULT_C_FLAGS}"
|
||||||
; EXTRA_LIBS for `make` php
|
; EXTRA_LIBS for `make` php
|
||||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm"
|
SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lrt -lpthread -lm -lresolv -lutil"
|
||||||
; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so
|
; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so
|
||||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS=""
|
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS=""
|
||||||
; EXTRA_LDFLAGS_PROGRAM for `make` php
|
; EXTRA_LDFLAGS_PROGRAM for `make` php
|
||||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie"
|
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -pie"
|
||||||
|
|
||||||
[macos]
|
[macos]
|
||||||
|
; 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
|
; compiler environments
|
||||||
CC=clang
|
CC=clang
|
||||||
CXX=clang++
|
CXX=clang++
|
||||||
@@ -115,7 +138,7 @@ SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os"
|
|||||||
; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated)
|
; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated)
|
||||||
SPC_EXTRA_LIBS=
|
SPC_EXTRA_LIBS=
|
||||||
; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches
|
; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches
|
||||||
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,macos_iconv
|
SPC_MICRO_PATCHES=cli_checks,macos_iconv
|
||||||
|
|
||||||
; *** default build command for building php ***
|
; *** default build command for building php ***
|
||||||
; buildconf command
|
; buildconf command
|
||||||
|
|||||||
@@ -304,12 +304,14 @@
|
|||||||
},
|
},
|
||||||
"intl": {
|
"intl": {
|
||||||
"support": {
|
"support": {
|
||||||
"Windows": "no",
|
|
||||||
"BSD": "wip"
|
"BSD": "wip"
|
||||||
},
|
},
|
||||||
"type": "builtin",
|
"type": "builtin",
|
||||||
"lib-depends": [
|
"lib-depends-unix": [
|
||||||
"icu"
|
"icu"
|
||||||
|
],
|
||||||
|
"lib-depends-windows": [
|
||||||
|
"icu-static-win"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
@@ -336,6 +338,9 @@
|
|||||||
},
|
},
|
||||||
"type": "builtin",
|
"type": "builtin",
|
||||||
"arg-type": "none",
|
"arg-type": "none",
|
||||||
|
"ext-depends": [
|
||||||
|
"xml"
|
||||||
|
],
|
||||||
"target": [
|
"target": [
|
||||||
"static"
|
"static"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -207,6 +207,18 @@
|
|||||||
"libicudata.a"
|
"libicudata.a"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"icu-static-win": {
|
||||||
|
"source": "icu-static-win",
|
||||||
|
"static-libs-windows": [
|
||||||
|
"icudt.lib",
|
||||||
|
"icuin.lib",
|
||||||
|
"icuio.lib",
|
||||||
|
"icuuc.lib"
|
||||||
|
],
|
||||||
|
"headers-windows": [
|
||||||
|
"unicode"
|
||||||
|
]
|
||||||
|
},
|
||||||
"imagemagick": {
|
"imagemagick": {
|
||||||
"source": "imagemagick",
|
"source": "imagemagick",
|
||||||
"static-libs-unix": [
|
"static-libs-unix": [
|
||||||
@@ -854,5 +866,14 @@
|
|||||||
"zstd.h",
|
"zstd.h",
|
||||||
"zstd_errors.h"
|
"zstd_errors.h"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"watcher": {
|
||||||
|
"source": "watcher",
|
||||||
|
"static-libs-unix": [
|
||||||
|
"libwatcher-c.a"
|
||||||
|
],
|
||||||
|
"headers": [
|
||||||
|
"wtr/watcher-c.h"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,5 +42,32 @@
|
|||||||
"extract-files": {
|
"extract-files": {
|
||||||
"upx-*-win64/upx.exe": "{pkg_root_path}/bin/upx.exe"
|
"upx-*-win64/upx.exe": "{pkg_root_path}/bin/upx.exe"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"go-xcaddy-x86_64-linux": {
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
"go-xcaddy-aarch64-linux": {
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
"go-xcaddy-x86_64-macos": {
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
"go-xcaddy-aarch64-macos": {
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
"zig-x86_64-linux": {
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
"zig-aarch64-linux": {
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
"zig-x86_64-macos": {
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
"zig-aarch64-macos": {
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
"zig-x86_64-win": {
|
||||||
|
"type": "custom"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,6 +342,14 @@
|
|||||||
"path": "LICENSE"
|
"path": "LICENSE"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"icu-static-win": {
|
||||||
|
"type": "url",
|
||||||
|
"url": "https://dl.static-php.dev/static-php-cli/deps/icu-static-windows-x64/icu-static-windows-x64.zip",
|
||||||
|
"license": {
|
||||||
|
"type": "text",
|
||||||
|
"text": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
"igbinary": {
|
"igbinary": {
|
||||||
"type": "url",
|
"type": "url",
|
||||||
"url": "https://pecl.php.net/get/igbinary",
|
"url": "https://pecl.php.net/get/igbinary",
|
||||||
@@ -1070,5 +1078,14 @@
|
|||||||
"type": "file",
|
"type": "file",
|
||||||
"path": "LICENSE"
|
"path": "LICENSE"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"watcher": {
|
||||||
|
"type": "ghtar",
|
||||||
|
"repo": "e-dant/watcher",
|
||||||
|
"prefer-stable": true,
|
||||||
|
"license": {
|
||||||
|
"type": "file",
|
||||||
|
"path": "license"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ export default {
|
|||||||
nav: [],
|
nav: [],
|
||||||
socialLinks: [
|
socialLinks: [
|
||||||
{icon: 'github', link: 'https://github.com/crazywhalecc/static-php-cli'}
|
{icon: 'github', link: 'https://github.com/crazywhalecc/static-php-cli'}
|
||||||
]
|
],
|
||||||
|
footer: {
|
||||||
|
message: 'Released under the MIT License.',
|
||||||
|
copyright: 'Copyright © 2023-present crazywhalecc'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import DefaultTheme from 'vitepress/theme'
|
import DefaultTheme from 'vitepress/theme'
|
||||||
import {inBrowser, useData} from "vitepress";
|
import {inBrowser, useData} from "vitepress";
|
||||||
import {watchEffect} from "vue";
|
import {watchEffect} from "vue";
|
||||||
|
import './style.css';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
...DefaultTheme,
|
...DefaultTheme,
|
||||||
@@ -13,4 +14,4 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
docs/.vitepress/theme/style.css
Normal file
6
docs/.vitepress/theme/style.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/** override default styles */
|
||||||
|
.vp-sponsor-grid-image {
|
||||||
|
max-height:36px !important;
|
||||||
|
max-width: 1000px !important;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -42,6 +42,9 @@ build-options:
|
|||||||
# Set micro SAPI as win32 mode, without this, micro SAPI will be compiled as a console application (only for Windows, default: false)
|
# Set micro SAPI as win32 mode, without this, micro SAPI will be compiled as a console application (only for Windows, default: false)
|
||||||
enable-micro-win32: false
|
enable-micro-win32: false
|
||||||
|
|
||||||
|
# Build options for shared extensions (list or comma-separated are both accepted)
|
||||||
|
shared-extensions: [ ]
|
||||||
|
|
||||||
# Download options
|
# Download options
|
||||||
download-options:
|
download-options:
|
||||||
# Use custom url for specified sources, format: "{source-name}:{url}" (e.g. "php-src:https://example.com/php-8.4.0.tar.gz")
|
# Use custom url for specified sources, format: "{source-name}:{url}" (e.g. "php-src:https://example.com/php-8.4.0.tar.gz")
|
||||||
|
|||||||
@@ -38,17 +38,11 @@ buildroot/bin/php -d "zend_extension=/path/to/php{PHP_VER}-{ts/nts}/xdebug.so" -
|
|||||||
```
|
```
|
||||||
|
|
||||||
For macOS platform, almost all binaries under macOS cannot be truly purely statically linked, and almost all binaries will link macOS system libraries: `/usr/lib/libresolv.9.dylib` and `/usr/lib/libSystem.B.dylib`.
|
For macOS platform, almost all binaries under macOS cannot be truly purely statically linked, and almost all binaries will link macOS system libraries: `/usr/lib/libresolv.9.dylib` and `/usr/lib/libSystem.B.dylib`.
|
||||||
So on macOS, you can use statically compiled PHP binaries under certain compilation conditions, and dynamically linked extensions:
|
So on macOS, you can **directly** use SPC to build statically compiled PHP binaries with dynamically linked extensions:
|
||||||
|
|
||||||
1. Using the `--no-strip` parameter will not strip information such as debugging symbols from the binary file for use with external Zend extensions such as `Xdebug`.
|
1. Build shared extension `xxx.so` using: `--build-shared=XXX` option. e.g. `bin/spc build bcmath,zlib --build-shared=xdebug --build-cli`
|
||||||
2. If you want to compile some Zend extensions, use Homebrew, MacPorts, source code compilation, and install a normal version of PHP on your operating system.
|
2. You will get `buildroot/modules/xdebug.so` and `buildroot/bin/php`.
|
||||||
3. Use the `phpize && ./configure && make` command to compile the extensions you want to use.
|
3. The `xdebug.so` file could be used for php that version and thread-safe are the same.
|
||||||
4. Copy the extension file `xxxx.so` to the outside, use the statically compiled PHP binary, for example to use the Xdebug extension: `cd buildroot/bin/ && ./php -d "zend_extension=/path/to/xdebug.so"`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# build statically linked php-cli but not stripped
|
|
||||||
bin/spc build ffi --build-cli --no-strip
|
|
||||||
```
|
|
||||||
|
|
||||||
## Can it support Oracle database extension?
|
## Can it support Oracle database extension?
|
||||||
|
|
||||||
|
|||||||
@@ -48,17 +48,17 @@ This extension contains an implementation of the coroutine environment for `pdo_
|
|||||||
|
|
||||||
## swow
|
## swow
|
||||||
|
|
||||||
1. Only PHP 8.0 ~ 8.4 is supported.
|
1. Only PHP 8.0+ is supported.
|
||||||
|
|
||||||
## imagick
|
## imagick
|
||||||
|
|
||||||
1. The imagick extension currently only has openmp support on musl libc. This means that multithreading is disabled on glibc or other operating systems. The extension is still fully functional.
|
1. OpenMP support is disabled, this is recommended by the maintainers and also the case system packages.
|
||||||
|
|
||||||
## imap
|
## imap
|
||||||
|
|
||||||
1. Kerberos is not supported
|
1. Kerberos is not supported
|
||||||
2. ext-imap is not thread safe due to the underlying c-client. It's not possible to use it in --enable-zts builds.
|
2. ext-imap is not thread safe due to the underlying c-client. It's not possible to use it in `--enable-zts` builds.
|
||||||
3. Because the extension may be dropped from php, we recommend you look for an alternative implementation, such as [Webklex/php-imap](https://github.com/Webklex/php-imap)
|
3. The extension was dropped from php 8.4, we recommend you look for an alternative implementation, such as [Webklex/php-imap](https://github.com/Webklex/php-imap)
|
||||||
|
|
||||||
## gd
|
## gd
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ and this extension cannot be compiled into php by static linking, so it cannot b
|
|||||||
|
|
||||||
## xdebug
|
## xdebug
|
||||||
|
|
||||||
1. Xdebug is only buildable as a shared extension. On Linux, you need to use static-php-cli with SPC_LIBC=glibc.
|
1. Xdebug is only buildable as a shared extension. On Linux, you'll need to use a SPC_TARGET like `native-native -dynamic` or `native-native-gnu`.
|
||||||
2. When using Linux/glibc or macOS, you can compile Xdebug as a shared extension using --build-shared="xdebug".
|
2. When using Linux/glibc or macOS, you can compile Xdebug as a shared extension using --build-shared="xdebug".
|
||||||
The compiled `./php` binary can be configured and run by specifying the INI, eg `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`.
|
The compiled `./php` binary can be configured and run by specifying the INI, eg `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`.
|
||||||
|
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ If the build is successful, you will see the `buildroot/bin` directory in the cu
|
|||||||
- fpm: The build result is `buildroot/bin/php-fpm`.
|
- fpm: The build result is `buildroot/bin/php-fpm`.
|
||||||
- micro: The build result is `buildroot/bin/micro.sfx`. If you need to further package it with PHP code, please refer to [Packaging micro binary](./manual-build#command-micro-combine).
|
- micro: The build result is `buildroot/bin/micro.sfx`. If you need to further package it with PHP code, please refer to [Packaging micro binary](./manual-build#command-micro-combine).
|
||||||
- embed: See [Using embed](./manual-build#embed-usage).
|
- embed: See [Using embed](./manual-build#embed-usage).
|
||||||
|
- frankenphp: The build result is `buildroot/bin/frankenphp`.
|
||||||
|
|
||||||
If the build fails, you can use the `--debug` parameter to view detailed error information,
|
If the build fails, you can use the `--debug` parameter to view detailed error information,
|
||||||
or use the `--with-clean` to clear the old compilation results and recompile.
|
or use the `--with-clean` to clear the old compilation results and recompile.
|
||||||
@@ -290,6 +291,7 @@ You need to specify a compilation target, choose from the following parameters:
|
|||||||
- `--build-fpm`: Build a fpm sapi (php-fpm, used in conjunction with other traditional fpm architecture software such as nginx)
|
- `--build-fpm`: Build a fpm sapi (php-fpm, used in conjunction with other traditional fpm architecture software such as nginx)
|
||||||
- `--build-micro`: Build a micro sapi (used to build a standalone executable binary containing PHP code)
|
- `--build-micro`: Build a micro sapi (used to build a standalone executable binary containing PHP code)
|
||||||
- `--build-embed`: Build an embed sapi (used to embed into other C language programs)
|
- `--build-embed`: Build an embed sapi (used to embed into other C language programs)
|
||||||
|
- `--build-frankenphp`: Build a [FrankenPHP](https://github.com/php/frankenphp) executable
|
||||||
- `--build-all`: build all above sapi
|
- `--build-all`: build all above sapi
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -337,7 +339,7 @@ You can try to use the following commands:
|
|||||||
- `--cxx=XXX`: Specifies the execution command of the C++ language compiler (Linux defaults to `g++`, macOS defaults to `clang++`)
|
- `--cxx=XXX`: Specifies the execution command of the C++ language compiler (Linux defaults to `g++`, macOS defaults to `clang++`)
|
||||||
- `--with-clean`: clean up old make files before compiling PHP
|
- `--with-clean`: clean up old make files before compiling PHP
|
||||||
- `--enable-zts`: Make compiled PHP thread-safe version (default is NTS version)
|
- `--enable-zts`: Make compiled PHP thread-safe version (default is NTS version)
|
||||||
- `--no-strip`: Do not run `strip` after compiling the PHP library to trim the binary file to reduce its size (the macOS binary file without trim can use dynamically linked third-party extensions)
|
- `--no-strip`: Do not run `strip` after compiling the PHP library to trim the binary file to reduce its size
|
||||||
- `--with-libs=XXX,YYY`: Compile the specified dependent library before compiling PHP, and activate some extended optional functions (such as libavif of the gd library, etc.)
|
- `--with-libs=XXX,YYY`: Compile the specified dependent library before compiling PHP, and activate some extended optional functions (such as libavif of the gd library, etc.)
|
||||||
- `--with-config-file-path=XXX`: Set the path in which to look for `php.ini` (Check [here](../faq/index.html#what-is-the-path-of-php-ini) for default paths)
|
- `--with-config-file-path=XXX`: Set the path in which to look for `php.ini` (Check [here](../faq/index.html#what-is-the-path-of-php-ini) for default paths)
|
||||||
- `--with-config-file-scan-dir=XXX`: Set the directory to scan for `.ini` files after reading `php.ini` (Check [here](../faq/index.html#what-is-the-path-of-php-ini) for default paths)
|
- `--with-config-file-scan-dir=XXX`: Set the directory to scan for `.ini` files after reading `php.ini` (Check [here](../faq/index.html#what-is-the-path-of-php-ini) for default paths)
|
||||||
@@ -509,6 +511,8 @@ When `bin/spc doctor` automatically repairs the Windows environment, tools such
|
|||||||
Here is an example of installing the tool:
|
Here is an example of installing the tool:
|
||||||
|
|
||||||
- Download and install UPX (Linux and Windows only): `bin/spc install-pkg upx`
|
- Download and install UPX (Linux and Windows only): `bin/spc install-pkg upx`
|
||||||
|
- Download and install nasm (Windows only): `bin/spc install-pkg nasm`
|
||||||
|
- Download and install go-xcaddy: `bin/spc install-pkg go-xcaddy`
|
||||||
|
|
||||||
## Command - del-download
|
## Command - del-download
|
||||||
|
|
||||||
|
|||||||
@@ -21,3 +21,16 @@ features:
|
|||||||
- title: Dependency Management
|
- title: Dependency Management
|
||||||
details: static-php-cli comes with dependency management and supports installation of different types of PHP extensions.
|
details: static-php-cli comes with dependency management and supports installation of different types of PHP extensions.
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {VPSponsors} from "vitepress/theme";
|
||||||
|
const sponsors = [
|
||||||
|
{ name: 'Beyond Code', img: '/images/beyondcode-seeklogo.png', url: 'https://beyondco.de/' },
|
||||||
|
{ name: 'NativePHP', img: '/images/nativephp-logo.svg', url: 'https://nativephp.com/' },
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
## Special Sponsors
|
||||||
|
|
||||||
|
<VPSponsors :data="sponsors"/>
|
||||||
|
|
||||||
|
|||||||
BIN
docs/public/images/beyondcode-seeklogo.png
Normal file
BIN
docs/public/images/beyondcode-seeklogo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
6
docs/public/images/nativephp-logo.svg
Normal file
6
docs/public/images/nativephp-logo.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 764 100">
|
||||||
|
<path fill="#505b93" d="M29.5,98.4H0V1.2h29.5V98.4z"/>
|
||||||
|
<path fill="#00aaa6" d="M96.7,98.4H67.2V1.2h29.5V98.4z"/>
|
||||||
|
<path fill="#272d48" d="M96.7,98.4H29.5V1.2L96.7,98.4z"/>
|
||||||
|
<path fill="#272d48" d="M102.6,54.5c0-6.2,1.3-11.8,4.1-16.7c2.7-5,6.7-8.8,11.9-11.6c5.2-2.8,11.4-4.2,18.6-4.2c1.1,0,2.8,0.1,5.2,0.3 c4.9,0.4,9.1,1.3,12.5,2.8c3.4,1.5,6.4,3.4,9.1,5.9v-7.2h25.8v74.7h-25.8v-7.2c-2.5,2.6-5.6,4.6-9.4,6.1c-3.8,1.5-7.9,2.4-12.1,2.6 c-0.9,0.1-2.3,0.1-4.1,0.1c-7.2,0-13.4-1.4-18.8-4.1c-5.4-2.7-9.5-6.6-12.4-11.5c-2.9-4.9-4.3-10.5-4.3-16.6V54.5z M128.4,63.9 c0,9.2,5.9,13.8,17.7,13.8c11.8,0,17.7-4.6,17.7-13.8v-5.6c0-9.3-5.9-14-17.7-14c-11.8,0-17.7,4.7-17.7,14V63.9z M208.3,98.4V47h-12.8V23.7l12.8-0.2V1.2h25.9l0.1,22.4h18.3V47h-18.4v51.4 M287.3,98.4h-25.9V23.6h25.9V98.4z M351,98.4H325l-31-74.8H320L338,68l18-44.5h25.9L351,98.4z M454.9,87.6c-4.1,4.2-8.8,7.3-14.1,9.3c-5.3,2-11.8,2.9-19.5,2.9c-8,0-15.1-1.4-21.3-4.2 c-6.2-2.8-11.1-6.7-14.7-11.6c-3.5-5-5.3-10.6-5.3-17V54.6c0-6.5,1.7-12.2,5.2-17.1c3.4-4.9,8.2-8.7,14.2-11.3c6-2.7,12.7-4,20-4 c8,0,15,1.5,21.2,4.3c6.1,2.9,10.9,7,14.4,12.4c3.4,5.4,5.2,11.6,5.2,18.7c0,1.7-0.2,4.2-0.7,7.7l-54.2,0.1v2.1 c0,4.1,1.5,7.3,4.4,9.4c2.9,2.2,6.9,3.2,11.8,3.2c4.6,0,8.5-0.6,11.8-1.8c3.2-1.2,6.4-3.5,9.4-6.8L454.9,87.6z M434.4,51.4 c0-3.1-1.4-5.5-4.2-7.1c-2.8-1.6-6.4-2.4-10.8-2.4s-7.9,0.8-10.5,2.4c-2.6,1.6-3.8,4-3.8,7.1H434.4z M466,98.4V1.2h56c9.9,0,17.8,1.9,23.7,5.6c5.9,3.7,10,8.2,12.2,13.3c2.3,5.1,3.4,10.1,3.4,15 c0,4.8-1.2,9.7-3.5,14.7c-2.3,5-6.4,9.2-12.2,12.8c-5.8,3.6-13.7,5.4-23.7,5.4h-30.2v30.5H466z M522,44.6c9,0,13.6-3.3,13.6-9.9 c0-6.9-4.6-10.3-13.7-10.3h-30.1v20.2H522z M593,98.4h-25.8V1.2H593v38h43.9v-38h25.8v97.2h-25.8V59.9H593V98.4z M668.6,98.4V1.2h56c9.9,0,17.8,1.9,23.7,5.6c5.9,3.7,10,8.2,12.2,13.3c2.3,5.1,3.4,10.1,3.4,15 c0,4.8-1.2,9.7-3.5,14.7c-2.3,5-6.4,9.2-12.2,12.8c-5.8,3.6-13.7,5.4-23.7,5.4h-30.2v30.5H668.6z M724.6,44.6c9,0,13.6-3.3,13.6-9.9 c0-6.9-4.6-10.3-13.7-10.3h-30.1v20.2H724.6z M291.1,20.2h-33.5V0h33.5V20.2z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -35,17 +35,11 @@ buildroot/bin/php -d "zend_extension=/path/to/php{PHP_VER}-{ts/nts}/xdebug.so" -
|
|||||||
```
|
```
|
||||||
|
|
||||||
对于 macOS 平台来说,macOS 下的几乎所有二进制文件都无法真正纯静态链接,几乎所有二进制文件都会链接 macOS 的系统库:`/usr/lib/libresolv.9.dylib` 和 `/usr/lib/libSystem.B.dylib`。
|
对于 macOS 平台来说,macOS 下的几乎所有二进制文件都无法真正纯静态链接,几乎所有二进制文件都会链接 macOS 的系统库:`/usr/lib/libresolv.9.dylib` 和 `/usr/lib/libSystem.B.dylib`。
|
||||||
所以在 macOS 系统下,在特定的编译条件下可以使用静态编译的 php 二进制文件,可使用动态链接的扩展:
|
因此,在 macOS 上,您可以直接构建出使用静态编译的 PHP 二进制文件和动态链接的扩展:
|
||||||
|
|
||||||
1. 使用 `--no-strip` 参数,将不会对二进制文件去除调试符号等信息,以供使用 `Xdebug` 等外部 Zend 扩展。
|
1. 使用 `--build-shared=XXX` 选项构建共享扩展 `xxx.so`。例如:`bin/spc build bcmath,zlib --build-shared=xdebug --build-cli`
|
||||||
2. 如果要编译某些 Zend 扩展,使用 Homebrew、MacPorts、源码编译的形式,在所在的操作系统安装一个普通版本的 PHP。
|
2. 您将获得 `buildroot/modules/xdebug.so` 和 `buildroot/bin/php`。
|
||||||
3. 使用 `phpize && ./configure && make` 命令编译想要使用的扩展。
|
3. `xdebug.so` 文件可用于版本和线程安全相同的 php。
|
||||||
4. 将扩展文件 `xxxx.so` 拷贝到外部,使用静态编译的 PHP 二进制,例如使用 Xdebug 扩展:`cd buildroot/bin/ && ./php -d "zend_extension=/path/to/xdebug.so"`。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 构建静态 php-cli
|
|
||||||
bin/spc build ffi --build-cli --no-strip
|
|
||||||
```
|
|
||||||
|
|
||||||
## 可以支持 Oracle 数据库扩展吗
|
## 可以支持 Oracle 数据库扩展吗
|
||||||
|
|
||||||
|
|||||||
@@ -45,17 +45,17 @@ swoole-hook-sqlite 与 `pdo_sqlite` 扩展冲突。如需使用 Swoole 和 `pdo_
|
|||||||
|
|
||||||
## swow
|
## swow
|
||||||
|
|
||||||
1. swow 仅支持 PHP 8.0 ~ 8.4 版本。
|
1. swow 仅支持 PHP 8.0+ 版本。
|
||||||
|
|
||||||
## imagick
|
## imagick
|
||||||
|
|
||||||
imagick 扩展目前仅在 musl libc 上支持 OpenMP(libgomp)。使用 glibc 方式构建的 imagick 扩展无法支持多线程特性。
|
1. OpenMP 支持已被禁用,这是维护者推荐的做法,系统软件包也是如此配置。
|
||||||
|
|
||||||
## imap
|
## imap
|
||||||
|
|
||||||
1. 该扩展目前不支持 Kerberos。
|
1. 该扩展目前不支持 Kerberos。
|
||||||
2. 由于底层的 c-client、ext-imap 不是线程安全的。 无法在 `--enable-zts` 构建中使用它。
|
2. 由于底层的 c-client、ext-imap 不是线程安全的。 无法在 `--enable-zts` 构建中使用它。
|
||||||
3. 由于该扩展可能会从未来的 PHP 中删除,因此我们建议您寻找替代实现,例如 [Webklex/php-imap](https://github.com/Webklex/php-imap)。
|
3. 该扩展已在 PHP 8.4 中被移除,因此我们建议您寻找替代实现,例如 [Webklex/php-imap](https://github.com/Webklex/php-imap)。
|
||||||
|
|
||||||
## gd
|
## gd
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ bin/spc build gd --with-libs=freetype,libjpeg,libavif,libwebp --build-cli
|
|||||||
|
|
||||||
## xdebug
|
## xdebug
|
||||||
|
|
||||||
1. Xdebug 只能作为共享扩展进行构建。在 Linux 上,您需要使用 static-php-cli 并设置 SPC_LIBC=glibc。
|
1. Xdebug 只能作为共享扩展进行构建。您需要使用除了 `musl-static` 外的其他 `SPC_TARGET` 构建目标。
|
||||||
2. 使用 Linux/glibc 或 macOS 时,您可以使用 `--build-shared=xdebug` 将 Xdebug 编译为共享扩展。
|
2. 使用 Linux/glibc 或 macOS 时,您可以使用 `--build-shared=xdebug` 将 Xdebug 编译为共享扩展。
|
||||||
编译后的 `./php` 二进制文件可以通过指定 INI 文件进行配置和运行,例如 `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`。
|
编译后的 `./php` 二进制文件可以通过指定 INI 文件进行配置和运行,例如 `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`。
|
||||||
|
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ bin/spc craft --debug
|
|||||||
- fpm: 构建结果为 `buildroot/bin/php-fpm`。
|
- fpm: 构建结果为 `buildroot/bin/php-fpm`。
|
||||||
- micro: 构建结果为 `buildroot/bin/micro.sfx`,如需进一步与 PHP 代码打包,请查看 [打包 micro 二进制](./manual-build#命令-micro-combine-打包-micro-二进制)。
|
- micro: 构建结果为 `buildroot/bin/micro.sfx`,如需进一步与 PHP 代码打包,请查看 [打包 micro 二进制](./manual-build#命令-micro-combine-打包-micro-二进制)。
|
||||||
- embed: 参见 [embed 使用](./manual-build#embed-使用)。
|
- embed: 参见 [embed 使用](./manual-build#embed-使用)。
|
||||||
|
- frankenphp: 构建结果为 `buildroot/bin/frankenphp`。
|
||||||
|
|
||||||
如果中途构建出错,你可以使用 `--debug` 参数查看详细的错误信息,或者使用 `--with-clean` 参数清除旧的编译结果,重新编译。
|
如果中途构建出错,你可以使用 `--debug` 参数查看详细的错误信息,或者使用 `--with-clean` 参数清除旧的编译结果,重新编译。
|
||||||
|
|
||||||
@@ -250,6 +251,7 @@ bin/spc doctor --auto-fix
|
|||||||
- `--build-fpm`: 构建一个 fpm sapi(php-fpm,用于和其他传统的 fpm 架构的软件如 nginx 配合使用)
|
- `--build-fpm`: 构建一个 fpm sapi(php-fpm,用于和其他传统的 fpm 架构的软件如 nginx 配合使用)
|
||||||
- `--build-micro`: 构建一个 micro sapi(用于构建一个包含 PHP 代码的独立可执行二进制)
|
- `--build-micro`: 构建一个 micro sapi(用于构建一个包含 PHP 代码的独立可执行二进制)
|
||||||
- `--build-embed`: 构建一个 embed sapi(用于嵌入到其他 C 语言程序中)
|
- `--build-embed`: 构建一个 embed sapi(用于嵌入到其他 C 语言程序中)
|
||||||
|
- `--build-frankenphp`: 构建一个 [frankenphp](https://github.com/php/frankenphp) 二进制
|
||||||
- `--build-all`: 构建以上所有 sapi
|
- `--build-all`: 构建以上所有 sapi
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -294,7 +296,7 @@ bin/spc build bcmath,curl,openssl,ftp,posix,pcntl --build-cli
|
|||||||
- `--cxx=XXX`: 指定 C++ 语言编译器的执行命令(Linux 默认 `g++`,macOS 默认 `clang++`)
|
- `--cxx=XXX`: 指定 C++ 语言编译器的执行命令(Linux 默认 `g++`,macOS 默认 `clang++`)
|
||||||
- `--with-clean`: 编译 PHP 前先清理旧的 make 产生的文件
|
- `--with-clean`: 编译 PHP 前先清理旧的 make 产生的文件
|
||||||
- `--enable-zts`: 让编译的 PHP 为线程安全版本(默认为 NTS 版本)
|
- `--enable-zts`: 让编译的 PHP 为线程安全版本(默认为 NTS 版本)
|
||||||
- `--no-strip`: 编译 PHP 库后不运行 `strip` 裁剪二进制文件缩小体积(不裁剪的 macOS 二进制文件可使用动态链接的第三方扩展)
|
- `--no-strip`: 编译 PHP 库后不运行 `strip` 裁剪二进制文件缩小体积
|
||||||
- `--with-libs=XXX,YYY`: 编译 PHP 前先编译指定的依赖库,激活部分扩展的可选功能(例如 gd 库的 libavif 等)
|
- `--with-libs=XXX,YYY`: 编译 PHP 前先编译指定的依赖库,激活部分扩展的可选功能(例如 gd 库的 libavif 等)
|
||||||
- `--with-config-file-path=XXX`: 查找 `php.ini` 的路径(在 [这里](../faq/index.html#php-ini-的路径是什么) 查看默认路径)
|
- `--with-config-file-path=XXX`: 查找 `php.ini` 的路径(在 [这里](../faq/index.html#php-ini-的路径是什么) 查看默认路径)
|
||||||
- `--with-config-file-scan-dir=XXX`: 读取 `php.ini` 后扫描 `.ini` 文件的目录(在 [这里](../faq/index.html#php-ini-的路径是什么) 查看默认路径)
|
- `--with-config-file-scan-dir=XXX`: 读取 `php.ini` 后扫描 `.ini` 文件的目录(在 [这里](../faq/index.html#php-ini-的路径是什么) 查看默认路径)
|
||||||
@@ -457,6 +459,8 @@ bin/spc dev:sort-config ext
|
|||||||
下面是安装工具的示例:
|
下面是安装工具的示例:
|
||||||
|
|
||||||
- 下载安装 UPX(仅限 Linux 和 Windows): `bin/spc install-pkg upx`
|
- 下载安装 UPX(仅限 Linux 和 Windows): `bin/spc install-pkg upx`
|
||||||
|
- 下载安装 nasm(仅限 Windows): `bin/spc install-pkg nasm`
|
||||||
|
- 下载安装 go-xcaddy: `bin/spc install-pkg go-xcaddy`
|
||||||
|
|
||||||
## 命令 del-download - 删除已下载的资源
|
## 命令 del-download - 删除已下载的资源
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,8 @@
|
|||||||
<phpunit
|
<phpunit
|
||||||
bootstrap="tests/bootstrap.php"
|
bootstrap="tests/bootstrap.php"
|
||||||
>
|
>
|
||||||
</phpunit>
|
<php>
|
||||||
|
<env name="SPC_IGNORE_BAD_HASH" value="yes" force="true" />
|
||||||
|
<env name="SPC_SKIP_TOOLCHAIN_CHECK" value="yes" force="true" />
|
||||||
|
</php>
|
||||||
|
</phpunit>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use SPC\command\BuildPHPCommand;
|
|||||||
use SPC\command\CraftCommand;
|
use SPC\command\CraftCommand;
|
||||||
use SPC\command\DeleteDownloadCommand;
|
use SPC\command\DeleteDownloadCommand;
|
||||||
use SPC\command\dev\AllExtCommand;
|
use SPC\command\dev\AllExtCommand;
|
||||||
|
use SPC\command\dev\EnvCommand;
|
||||||
use SPC\command\dev\ExtVerCommand;
|
use SPC\command\dev\ExtVerCommand;
|
||||||
use SPC\command\dev\GenerateExtDepDocsCommand;
|
use SPC\command\dev\GenerateExtDepDocsCommand;
|
||||||
use SPC\command\dev\GenerateExtDocCommand;
|
use SPC\command\dev\GenerateExtDocCommand;
|
||||||
@@ -33,7 +34,7 @@ use Symfony\Component\Console\Application;
|
|||||||
*/
|
*/
|
||||||
final class ConsoleApplication extends Application
|
final class ConsoleApplication extends Application
|
||||||
{
|
{
|
||||||
public const VERSION = '2.6.0';
|
public const VERSION = '2.6.1';
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
@@ -70,6 +71,7 @@ final class ConsoleApplication extends Application
|
|||||||
new GenerateExtDepDocsCommand(),
|
new GenerateExtDepDocsCommand(),
|
||||||
new GenerateLibDepDocsCommand(),
|
new GenerateLibDepDocsCommand(),
|
||||||
new PackLibCommand(),
|
new PackLibCommand(),
|
||||||
|
new EnvCommand(),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ use SPC\exception\RuntimeException;
|
|||||||
use SPC\exception\WrongUsageException;
|
use SPC\exception\WrongUsageException;
|
||||||
use SPC\store\Config;
|
use SPC\store\Config;
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
|
use SPC\store\LockFile;
|
||||||
use SPC\store\SourceManager;
|
use SPC\store\SourceManager;
|
||||||
|
use SPC\store\SourcePatcher;
|
||||||
use SPC\util\CustomExt;
|
use SPC\util\CustomExt;
|
||||||
|
|
||||||
abstract class BuilderBase
|
abstract class BuilderBase
|
||||||
@@ -202,6 +204,8 @@ abstract class BuilderBase
|
|||||||
$this->emitPatchPoint('before-exts-extract');
|
$this->emitPatchPoint('before-exts-extract');
|
||||||
SourceManager::initSource(exts: [...$static_extensions, ...$shared_extensions]);
|
SourceManager::initSource(exts: [...$static_extensions, ...$shared_extensions]);
|
||||||
$this->emitPatchPoint('after-exts-extract');
|
$this->emitPatchPoint('after-exts-extract');
|
||||||
|
// patch micro
|
||||||
|
SourcePatcher::patchMicro();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ([...$static_extensions, ...$shared_extensions] as $extension) {
|
foreach ([...$static_extensions, ...$shared_extensions] as $extension) {
|
||||||
@@ -262,17 +266,6 @@ abstract class BuilderBase
|
|||||||
if (!$ext->isBuildShared()) {
|
if (!$ext->isBuildShared()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (Config::getExt($ext->getName(), 'type') === 'builtin' || Config::getExt($ext->getName(), 'build-with-php') === true) {
|
|
||||||
if (file_exists(BUILD_MODULES_PATH . '/' . $ext->getName() . '.so')) {
|
|
||||||
logger()->info('Shared extension [' . $ext->getName() . '] was already built by php-src/configure (' . $ext->getName() . '.so)');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (Config::getExt($ext->getName(), 'build-with-php') === true) {
|
|
||||||
logger()->warning('Shared extension [' . $ext->getName() . '] did not build with php-src/configure (' . $ext->getName() . '.so)');
|
|
||||||
logger()->warning('Try deleting your build and source folders and running `spc build`` again.');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$ext->buildShared();
|
$ext->buildShared();
|
||||||
}
|
}
|
||||||
} catch (RuntimeException $e) {
|
} catch (RuntimeException $e) {
|
||||||
@@ -361,15 +354,11 @@ abstract class BuilderBase
|
|||||||
public function getPHPVersionFromArchive(?string $file = null): false|string
|
public function getPHPVersionFromArchive(?string $file = null): false|string
|
||||||
{
|
{
|
||||||
if ($file === null) {
|
if ($file === null) {
|
||||||
$lock = file_exists(DOWNLOAD_PATH . '/.lock.json') ? file_get_contents(DOWNLOAD_PATH . '/.lock.json') : false;
|
$lock = LockFile::get('php-src');
|
||||||
if ($lock === false) {
|
if ($lock === null) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$lock = json_decode($lock, true);
|
|
||||||
$file = $lock['php-src']['filename'] ?? null;
|
|
||||||
if ($file === null) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
$file = LockFile::getLockFullPath($lock);
|
||||||
}
|
}
|
||||||
if (preg_match('/php-(\d+\.\d+\.\d+(?:RC\d+)?)\.tar\.(?:gz|bz2|xz)/', $file, $match)) {
|
if (preg_match('/php-(\d+\.\d+\.\d+(?:RC\d+)?)\.tar\.(?:gz|bz2|xz)/', $file, $match)) {
|
||||||
return $match[1];
|
return $match[1];
|
||||||
@@ -415,6 +404,9 @@ abstract class BuilderBase
|
|||||||
if (($type & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED) {
|
if (($type & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED) {
|
||||||
$ls[] = 'embed';
|
$ls[] = 'embed';
|
||||||
}
|
}
|
||||||
|
if (($type & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP) {
|
||||||
|
$ls[] = 'frankenphp';
|
||||||
|
}
|
||||||
return implode(', ', $ls);
|
return implode(', ', $ls);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,6 +513,29 @@ abstract class BuilderBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function checkBeforeBuildPHP(int $rule): void
|
||||||
|
{
|
||||||
|
if (($rule & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP) {
|
||||||
|
if (!$this->getOption('enable-zts')) {
|
||||||
|
throw new WrongUsageException('FrankenPHP SAPI requires ZTS enabled PHP, build with `--enable-zts`!');
|
||||||
|
}
|
||||||
|
// frankenphp doesn't support windows, BSD is currently not supported by static-php-cli
|
||||||
|
if (!in_array(PHP_OS_FAMILY, ['Linux', 'Darwin'])) {
|
||||||
|
throw new WrongUsageException('FrankenPHP SAPI is only available on Linux and macOS!');
|
||||||
|
}
|
||||||
|
// frankenphp needs package go-xcaddy installed
|
||||||
|
$pkg_dir = PKG_ROOT_PATH . '/go-xcaddy-' . arch2gnu(php_uname('m')) . '-' . osfamily2shortname();
|
||||||
|
if (!file_exists("{$pkg_dir}/bin/go") || !file_exists("{$pkg_dir}/bin/xcaddy")) {
|
||||||
|
global $argv;
|
||||||
|
throw new WrongUsageException("FrankenPHP SAPI requires the go-xcaddy package, please install it first: {$argv[0]} install-pkg go-xcaddy");
|
||||||
|
}
|
||||||
|
// frankenphp needs libxml2 lib on macos, see: https://github.com/php/frankenphp/blob/main/frankenphp.go#L17
|
||||||
|
if (PHP_OS_FAMILY === 'Darwin' && !$this->getLib('libxml2')) {
|
||||||
|
throw new WrongUsageException('FrankenPHP SAPI for macOS requires libxml2 library, please include the `xml` extension in your build.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate micro extension test php code.
|
* Generate micro extension test php code.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace SPC\builder;
|
namespace SPC\builder;
|
||||||
|
|
||||||
|
use SPC\builder\linux\SystemUtil;
|
||||||
use SPC\exception\FileSystemException;
|
use SPC\exception\FileSystemException;
|
||||||
use SPC\exception\RuntimeException;
|
use SPC\exception\RuntimeException;
|
||||||
use SPC\exception\WrongUsageException;
|
use SPC\exception\WrongUsageException;
|
||||||
@@ -193,7 +194,7 @@ class Extension
|
|||||||
* If you need to patch some code, overwrite this
|
* If you need to patch some code, overwrite this
|
||||||
* return true if you patched something, false if not
|
* return true if you patched something, false if not
|
||||||
*/
|
*/
|
||||||
public function patchBeforeSharedBuild(): bool
|
public function patchBeforeSharedPhpize(): bool
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -208,6 +209,25 @@ class Extension
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patch code before shared extension make
|
||||||
|
* If you need to patch some code, overwrite this
|
||||||
|
* return true if you patched something, false if not
|
||||||
|
*/
|
||||||
|
public function patchBeforeSharedMake(): bool
|
||||||
|
{
|
||||||
|
$extra = SystemUtil::getExtraRuntimeObjects();
|
||||||
|
if (!$extra) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FileSystem::replaceFileRegex(
|
||||||
|
$this->source_dir . '/Makefile',
|
||||||
|
"/^(shared_objects_{$this->getName()}\\s*=.*)$/m",
|
||||||
|
"$1 {$extra}",
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
* returns a command line string with all required shared extensions to load
|
* returns a command line string with all required shared extensions to load
|
||||||
@@ -264,9 +284,12 @@ class Extension
|
|||||||
// If you need to run some check, overwrite this or add your assert in src/globals/ext-tests/{extension_name}.php
|
// If you need to run some check, overwrite this or add your assert in src/globals/ext-tests/{extension_name}.php
|
||||||
// If check failed, throw RuntimeException
|
// If check failed, throw RuntimeException
|
||||||
$sharedExtensions = $this->getSharedExtensionLoadString();
|
$sharedExtensions = $this->getSharedExtensionLoadString();
|
||||||
[$ret] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"');
|
[$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"');
|
||||||
if ($ret !== 0) {
|
if ($ret !== 0) {
|
||||||
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret);
|
throw new RuntimeException(
|
||||||
|
'extension ' . $this->getName() . ' failed runtime check: php-cli returned ' . $ret . "\n" .
|
||||||
|
join("\n", $out)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_exists(ROOT_DIR . '/src/globals/ext-tests/' . $this->getName() . '.php')) {
|
if (file_exists(ROOT_DIR . '/src/globals/ext-tests/' . $this->getName() . '.php')) {
|
||||||
@@ -328,11 +351,21 @@ class Extension
|
|||||||
*/
|
*/
|
||||||
public function buildShared(): void
|
public function buildShared(): void
|
||||||
{
|
{
|
||||||
logger()->info('Building extension [' . $this->getName() . '] as shared extension (' . $this->getName() . '.so)');
|
if (Config::getExt($this->getName(), 'type') === 'builtin' || Config::getExt($this->getName(), 'build-with-php') === true) {
|
||||||
if (file_exists(BUILD_MODULES_PATH . '/' . $this->getName() . '.so')) {
|
if (file_exists(BUILD_MODULES_PATH . '/' . $this->getName() . '.so')) {
|
||||||
logger()->info('extension ' . $this->getName() . ' already built, skipping');
|
logger()->info('Shared extension [' . $this->getName() . '] was already built by php-src/configure (' . $this->getName() . '.so)');
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
if (Config::getExt($this->getName(), 'build-with-php') === true) {
|
||||||
|
logger()->warning('Shared extension [' . $this->getName() . '] did not build with php-src/configure (' . $this->getName() . '.so)');
|
||||||
|
logger()->warning('Try deleting your build and source folders and running `spc build`` again.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (file_exists(BUILD_MODULES_PATH . '/' . $this->getName() . '.so')) {
|
||||||
|
logger()->info('Shared extension [' . $this->getName() . '] was already built, skipping (' . $this->getName() . '.so)');
|
||||||
|
}
|
||||||
|
logger()->info('Building extension [' . $this->getName() . '] as shared extension (' . $this->getName() . '.so)');
|
||||||
foreach ($this->dependencies as $dependency) {
|
foreach ($this->dependencies as $dependency) {
|
||||||
if (!$dependency instanceof Extension) {
|
if (!$dependency instanceof Extension) {
|
||||||
continue;
|
continue;
|
||||||
@@ -361,39 +394,56 @@ class Extension
|
|||||||
{
|
{
|
||||||
$config = (new SPCConfigUtil($this->builder))->config([$this->getName()], with_dependencies: true);
|
$config = (new SPCConfigUtil($this->builder))->config([$this->getName()], with_dependencies: true);
|
||||||
[$staticLibString, $sharedLibString] = $this->getStaticAndSharedLibs();
|
[$staticLibString, $sharedLibString] = $this->getStaticAndSharedLibs();
|
||||||
|
|
||||||
|
// macOS ld64 doesn't understand these, while Linux and BSD do
|
||||||
|
// use them to make sure that all symbols are picked up, even if a library has already been visited before
|
||||||
|
$preStatic = PHP_OS_FAMILY !== 'Darwin' ? '-Wl,--start-group ' : '';
|
||||||
|
$postStatic = PHP_OS_FAMILY !== 'Darwin' ? ' -Wl,--end-group ' : ' ';
|
||||||
$env = [
|
$env = [
|
||||||
'CFLAGS' => $config['cflags'],
|
'CFLAGS' => $config['cflags'],
|
||||||
'CXXFLAGS' => $config['cflags'],
|
'CXXFLAGS' => $config['cflags'],
|
||||||
'LDFLAGS' => $config['ldflags'],
|
'LDFLAGS' => $config['ldflags'],
|
||||||
'LIBS' => '-Wl,-Bstatic -Wl,--start-group ' . $staticLibString . ' -Wl,--end-group -Wl,-Bdynamic ' . $sharedLibString,
|
'LIBS' => $preStatic . $staticLibString . $postStatic . $sharedLibString,
|
||||||
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
|
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ($this->patchBeforeSharedPhpize()) {
|
||||||
|
logger()->info("Extension [{$this->getName()}] patched before shared phpize");
|
||||||
|
}
|
||||||
|
|
||||||
// prepare configure args
|
// prepare configure args
|
||||||
shell()->cd($this->source_dir)
|
shell()->cd($this->source_dir)
|
||||||
->setEnv($env)
|
->setEnv($env)
|
||||||
|
->appendEnv($this->getExtraEnv())
|
||||||
->exec(BUILD_BIN_PATH . '/phpize');
|
->exec(BUILD_BIN_PATH . '/phpize');
|
||||||
|
|
||||||
if ($this->patchBeforeSharedConfigure()) {
|
if ($this->patchBeforeSharedConfigure()) {
|
||||||
logger()->info('ext [ . ' . $this->getName() . '] patching before shared configure');
|
logger()->info("Extension [{$this->getName()}] patched before shared configure");
|
||||||
}
|
}
|
||||||
|
|
||||||
shell()->cd($this->source_dir)
|
shell()->cd($this->source_dir)
|
||||||
->setEnv($env)
|
->setEnv($env)
|
||||||
|
->appendEnv($this->getExtraEnv())
|
||||||
->exec(
|
->exec(
|
||||||
'./configure ' . $this->getUnixConfigureArg(true) .
|
'./configure ' . $this->getUnixConfigureArg(true) .
|
||||||
' --with-php-config=' . BUILD_BIN_PATH . '/php-config ' .
|
' --with-php-config=' . BUILD_BIN_PATH . '/php-config ' .
|
||||||
'--enable-shared --disable-static'
|
'--enable-shared --disable-static'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// some extensions don't define their dependencies well, this patch is only needed for a few
|
||||||
FileSystem::replaceFileRegex(
|
FileSystem::replaceFileRegex(
|
||||||
$this->source_dir . '/Makefile',
|
$this->source_dir . '/Makefile',
|
||||||
'/^(.*_SHARED_LIBADD\s*=.*)$/m',
|
'/^(.*_SHARED_LIBADD\s*=.*)$/m',
|
||||||
'$1 ' . $staticLibString
|
'$1 ' . $staticLibString
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($this->patchBeforeSharedMake()) {
|
||||||
|
logger()->info("Extension [{$this->getName()}] patched before shared make");
|
||||||
|
}
|
||||||
|
|
||||||
shell()->cd($this->source_dir)
|
shell()->cd($this->source_dir)
|
||||||
->setEnv($env)
|
->setEnv($env)
|
||||||
|
->appendEnv($this->getExtraEnv())
|
||||||
->exec('make clean')
|
->exec('make clean')
|
||||||
->exec('make -j' . $this->builder->concurrency)
|
->exec('make -j' . $this->builder->concurrency)
|
||||||
->exec('make install');
|
->exec('make install');
|
||||||
@@ -472,14 +522,13 @@ class Extension
|
|||||||
*
|
*
|
||||||
* @return array [staticLibString, sharedLibString]
|
* @return array [staticLibString, sharedLibString]
|
||||||
*/
|
*/
|
||||||
private function getStaticAndSharedLibs(): array
|
protected function getStaticAndSharedLibs(): array
|
||||||
{
|
{
|
||||||
$config = (new SPCConfigUtil($this->builder))->config([$this->getName()], with_dependencies: true);
|
$config = (new SPCConfigUtil($this->builder))->config([$this->getName()], with_dependencies: true);
|
||||||
$sharedLibString = '';
|
$sharedLibString = '';
|
||||||
$staticLibString = '';
|
$staticLibString = '';
|
||||||
$staticLibs = $this->getLibFilesString();
|
$staticLibs = $this->getLibFilesString();
|
||||||
$staticLibs = str_replace(BUILD_LIB_PATH . '/lib', '-l', $staticLibs);
|
$staticLibs = str_replace([BUILD_LIB_PATH . '/lib', '.a'], ['-l', ''], $staticLibs);
|
||||||
$staticLibs = str_replace('.a', '', $staticLibs);
|
|
||||||
$staticLibs = explode('-l', $staticLibs . ' ' . $config['libs']);
|
$staticLibs = explode('-l', $staticLibs . ' ' . $config['libs']);
|
||||||
foreach ($staticLibs as $lib) {
|
foreach ($staticLibs as $lib) {
|
||||||
$lib = trim($lib);
|
$lib = trim($lib);
|
||||||
@@ -487,7 +536,7 @@ class Extension
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$static_lib = 'lib' . $lib . '.a';
|
$static_lib = 'lib' . $lib . '.a';
|
||||||
if (file_exists(BUILD_LIB_PATH . '/' . $static_lib)) {
|
if (file_exists(BUILD_LIB_PATH . '/' . $static_lib) && !str_contains($static_lib, 'libphp')) {
|
||||||
if (!str_contains($staticLibString, '-l' . $lib . ' ')) {
|
if (!str_contains($staticLibString, '-l' . $lib . ' ')) {
|
||||||
$staticLibString .= '-l' . $lib . ' ';
|
$staticLibString .= '-l' . $lib . ' ';
|
||||||
}
|
}
|
||||||
@@ -498,6 +547,11 @@ class Extension
|
|||||||
return [trim($staticLibString), trim($sharedLibString)];
|
return [trim($staticLibString), trim($sharedLibString)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getExtraEnv(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
private function getLibraryDependencies(bool $recursive = false): array
|
private function getLibraryDependencies(bool $recursive = false): array
|
||||||
{
|
{
|
||||||
$ret = array_filter($this->dependencies, fn ($x) => $x instanceof LibraryBase);
|
$ret = array_filter($this->dependencies, fn ($x) => $x instanceof LibraryBase);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use SPC\exception\WrongUsageException;
|
|||||||
use SPC\store\Config;
|
use SPC\store\Config;
|
||||||
use SPC\store\Downloader;
|
use SPC\store\Downloader;
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
|
use SPC\store\LockFile;
|
||||||
use SPC\store\SourceManager;
|
use SPC\store\SourceManager;
|
||||||
use SPC\util\GlobalValueTrait;
|
use SPC\util\GlobalValueTrait;
|
||||||
|
|
||||||
@@ -46,12 +47,11 @@ abstract class LibraryBase
|
|||||||
*/
|
*/
|
||||||
public function setup(bool $force = false): int
|
public function setup(bool $force = false): int
|
||||||
{
|
{
|
||||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
|
|
||||||
$source = Config::getLib(static::NAME, 'source');
|
$source = Config::getLib(static::NAME, 'source');
|
||||||
// if source is locked as pre-built, we just tryInstall it
|
// if source is locked as pre-built, we just tryInstall it
|
||||||
$pre_built_name = Downloader::getPreBuiltLockName($source);
|
$pre_built_name = Downloader::getPreBuiltLockName($source);
|
||||||
if (isset($lock[$pre_built_name]) && ($lock[$pre_built_name]['lock_as'] ?? SPC_DOWNLOAD_SOURCE) === SPC_DOWNLOAD_PRE_BUILT) {
|
if (($lock = LockFile::get($pre_built_name)) && $lock['lock_as'] === SPC_DOWNLOAD_PRE_BUILT) {
|
||||||
return $this->tryInstall($lock[$pre_built_name]['filename'], $force);
|
return $this->tryInstall($lock, $force);
|
||||||
}
|
}
|
||||||
return $this->tryBuild($force);
|
return $this->tryBuild($force);
|
||||||
}
|
}
|
||||||
@@ -166,14 +166,15 @@ abstract class LibraryBase
|
|||||||
* @throws WrongUsageException
|
* @throws WrongUsageException
|
||||||
* @throws FileSystemException
|
* @throws FileSystemException
|
||||||
*/
|
*/
|
||||||
public function tryInstall(string $install_file, bool $force_install = false): int
|
public function tryInstall(array $lock, bool $force_install = false): int
|
||||||
{
|
{
|
||||||
|
$install_file = $lock['filename'];
|
||||||
if ($force_install) {
|
if ($force_install) {
|
||||||
logger()->info('Installing required library [' . static::NAME . '] from pre-built binaries');
|
logger()->info('Installing required library [' . static::NAME . '] from pre-built binaries');
|
||||||
|
|
||||||
// Extract files
|
// Extract files
|
||||||
try {
|
try {
|
||||||
FileSystem::extractPackage($install_file, DOWNLOAD_PATH . '/' . $install_file, BUILD_ROOT_PATH);
|
FileSystem::extractPackage($install_file, $lock['source_type'], DOWNLOAD_PATH . '/' . $install_file, BUILD_ROOT_PATH);
|
||||||
$this->install();
|
$this->install();
|
||||||
return LIB_STATUS_OK;
|
return LIB_STATUS_OK;
|
||||||
} catch (FileSystemException|RuntimeException $e) {
|
} catch (FileSystemException|RuntimeException $e) {
|
||||||
@@ -183,19 +184,19 @@ abstract class LibraryBase
|
|||||||
}
|
}
|
||||||
foreach ($this->getStaticLibs() as $name) {
|
foreach ($this->getStaticLibs() as $name) {
|
||||||
if (!file_exists(BUILD_LIB_PATH . "/{$name}")) {
|
if (!file_exists(BUILD_LIB_PATH . "/{$name}")) {
|
||||||
$this->tryInstall($install_file, true);
|
$this->tryInstall($lock, true);
|
||||||
return LIB_STATUS_OK;
|
return LIB_STATUS_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($this->getHeaders() as $name) {
|
foreach ($this->getHeaders() as $name) {
|
||||||
if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) {
|
if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) {
|
||||||
$this->tryInstall($install_file, true);
|
$this->tryInstall($lock, true);
|
||||||
return LIB_STATUS_OK;
|
return LIB_STATUS_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// pkg-config is treated specially. If it is pkg-config, check if the pkg-config binary exists
|
// pkg-config is treated specially. If it is pkg-config, check if the pkg-config binary exists
|
||||||
if (static::NAME === 'pkg-config' && !file_exists(BUILD_ROOT_PATH . '/bin/pkg-config')) {
|
if (static::NAME === 'pkg-config' && !file_exists(BUILD_ROOT_PATH . '/bin/pkg-config')) {
|
||||||
$this->tryInstall($install_file, true);
|
$this->tryInstall($lock, true);
|
||||||
return LIB_STATUS_OK;
|
return LIB_STATUS_OK;
|
||||||
}
|
}
|
||||||
return LIB_STATUS_ALREADY;
|
return LIB_STATUS_ALREADY;
|
||||||
|
|||||||
@@ -5,28 +5,25 @@ declare(strict_types=1);
|
|||||||
namespace SPC\builder\extension;
|
namespace SPC\builder\extension;
|
||||||
|
|
||||||
use SPC\builder\Extension;
|
use SPC\builder\Extension;
|
||||||
|
use SPC\builder\linux\SystemUtil;
|
||||||
use SPC\util\CustomExt;
|
use SPC\util\CustomExt;
|
||||||
|
|
||||||
#[CustomExt('imagick')]
|
#[CustomExt('imagick')]
|
||||||
class imagick extends Extension
|
class imagick extends Extension
|
||||||
{
|
{
|
||||||
public function patchBeforeMake(): bool
|
|
||||||
{
|
|
||||||
if (PHP_OS_FAMILY !== 'Linux') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// imagick with calls omp_pause_all, which requires openmp, on non-musl we build imagick without openmp
|
|
||||||
$extra_libs = trim(getenv('SPC_EXTRA_LIBS') . ' -lgomp');
|
|
||||||
f_putenv('SPC_EXTRA_LIBS=' . $extra_libs);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getUnixConfigureArg(bool $shared = false): string
|
public function getUnixConfigureArg(bool $shared = false): string
|
||||||
{
|
{
|
||||||
$disable_omp = !(getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) ? '' : ' ac_cv_func_omp_pause_resource_all=no';
|
$disable_omp = ' ac_cv_func_omp_pause_resource_all=no';
|
||||||
return '--with-imagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $disable_omp;
|
return '--with-imagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $disable_omp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getStaticAndSharedLibs(): array
|
||||||
|
{
|
||||||
|
[$static, $shared] = parent::getStaticAndSharedLibs();
|
||||||
|
if (SystemUtil::getLibcVersionIfExists('glibc') && SystemUtil::getLibcVersionIfExists('glibc') <= '2.17') {
|
||||||
|
$static .= ' -lstdc++';
|
||||||
|
$shared = str_replace('-lstdc++', '', $shared);
|
||||||
|
}
|
||||||
|
return [$static, $shared];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace SPC\builder\extension;
|
namespace SPC\builder\extension;
|
||||||
|
|
||||||
use SPC\builder\Extension;
|
use SPC\builder\Extension;
|
||||||
|
use SPC\builder\windows\WindowsBuilder;
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
use SPC\util\CustomExt;
|
use SPC\util\CustomExt;
|
||||||
|
|
||||||
@@ -13,16 +14,18 @@ class intl extends Extension
|
|||||||
{
|
{
|
||||||
public function patchBeforeBuildconf(): bool
|
public function patchBeforeBuildconf(): bool
|
||||||
{
|
{
|
||||||
// TODO: remove the following line when https://github.com/php/php-src/pull/14002 will be released
|
if ($this->builder instanceof WindowsBuilder) {
|
||||||
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/intl/config.m4', 'PHP_CXX_COMPILE_STDCXX(11', 'PHP_CXX_COMPILE_STDCXX(17');
|
FileSystem::replaceFileStr(
|
||||||
// Also need to use clang++ -std=c++17 to force override the default C++ standard
|
SOURCE_PATH . '/php-src/ext/intl/config.w32',
|
||||||
if (is_string($env = getenv('CXX')) && !str_contains($env, 'std=c++17')) {
|
'EXTENSION("intl", "php_intl.c intl_convert.c intl_convertcpp.cpp intl_error.c ", true,',
|
||||||
f_putenv('CXX=' . $env . ' -std=c++17');
|
'EXTENSION("intl", "php_intl.c intl_convert.c intl_convertcpp.cpp intl_error.c ", PHP_INTL_SHARED,'
|
||||||
|
);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function patchBeforeSharedBuild(): bool
|
public function patchBeforeSharedPhpize(): bool
|
||||||
{
|
{
|
||||||
return $this->patchBeforeBuildconf();
|
return $this->patchBeforeBuildconf();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,4 +57,11 @@ class pgsql extends Extension
|
|||||||
}
|
}
|
||||||
return '--with-pgsql=' . BUILD_ROOT_PATH;
|
return '--with-pgsql=' . BUILD_ROOT_PATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getExtraEnv(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'CFLAGS' => '-Wno-int-conversion',
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ class protobuf extends Extension
|
|||||||
if ($this->builder->getPHPVersionID() < 80000 && getenv('SPC_SKIP_PHP_VERSION_CHECK') !== 'yes') {
|
if ($this->builder->getPHPVersionID() < 80000 && getenv('SPC_SKIP_PHP_VERSION_CHECK') !== 'yes') {
|
||||||
throw new \RuntimeException('The latest protobuf extension requires PHP 8.0 or later');
|
throw new \RuntimeException('The latest protobuf extension requires PHP 8.0 or later');
|
||||||
}
|
}
|
||||||
|
$grpc = $this->builder->getExt('grpc');
|
||||||
// protobuf conflicts with grpc
|
// protobuf conflicts with grpc
|
||||||
if ($this->builder->getExt('grpc') !== null) {
|
if ($grpc?->isBuildStatic()) {
|
||||||
throw new \RuntimeException('protobuf conflicts with grpc, please remove grpc or protobuf extension');
|
throw new \RuntimeException('protobuf conflicts with grpc, please remove grpc or protobuf extension');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,13 @@ class redis extends Extension
|
|||||||
public function getUnixConfigureArg(bool $shared = false): string
|
public function getUnixConfigureArg(bool $shared = false): string
|
||||||
{
|
{
|
||||||
$arg = '--enable-redis';
|
$arg = '--enable-redis';
|
||||||
$arg .= $this->builder->getExt('session') ? ' --enable-redis-session' : ' --disable-redis-session';
|
if ($this->isBuildStatic()) {
|
||||||
$arg .= $this->builder->getExt('igbinary') ? ' --enable-redis-igbinary' : ' --disable-redis-igbinary';
|
$arg .= $this->builder->getExt('session')?->isBuildStatic() ? ' --enable-redis-session' : ' --disable-redis-session';
|
||||||
|
$arg .= $this->builder->getExt('igbinary')?->isBuildStatic() ? ' --enable-redis-igbinary' : ' --disable-redis-igbinary';
|
||||||
|
} else {
|
||||||
|
$arg .= $this->builder->getExt('session') ? ' --enable-redis-session' : ' --disable-redis-session';
|
||||||
|
$arg .= $this->builder->getExt('igbinary') ? ' --enable-redis-igbinary' : ' --disable-redis-igbinary';
|
||||||
|
}
|
||||||
if ($this->builder->getLib('zstd')) {
|
if ($this->builder->getLib('zstd')) {
|
||||||
$arg .= ' --enable-redis-zstd --with-libzstd="' . BUILD_ROOT_PATH . '"';
|
$arg .= ' --enable-redis-zstd --with-libzstd="' . BUILD_ROOT_PATH . '"';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,14 @@ class swoole extends Extension
|
|||||||
{
|
{
|
||||||
public function patchBeforeMake(): bool
|
public function patchBeforeMake(): bool
|
||||||
{
|
{
|
||||||
|
$patched = false;
|
||||||
if ($this->builder instanceof MacOSBuilder) {
|
if ($this->builder instanceof MacOSBuilder) {
|
||||||
// Fix swoole with event extension <util.h> conflict bug
|
// Fix swoole with event extension <util.h> conflict bug
|
||||||
$util_path = shell()->execWithResult('xcrun --show-sdk-path', false)[1][0] . '/usr/include/util.h';
|
$util_path = shell()->execWithResult('xcrun --show-sdk-path', false)[1][0] . '/usr/include/util.h';
|
||||||
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/swoole/thirdparty/php/standard/proc_open.cc', 'include <util.h>', 'include "' . $util_path . '"');
|
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/swoole/thirdparty/php/standard/proc_open.cc', 'include <util.h>', 'include "' . $util_path . '"');
|
||||||
return true;
|
$patched = true;
|
||||||
}
|
}
|
||||||
return false;
|
return $patched;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getExtVersion(): ?string
|
public function getExtVersion(): ?string
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace SPC\builder\extension;
|
namespace SPC\builder\extension;
|
||||||
|
|
||||||
use SPC\builder\Extension;
|
use SPC\builder\Extension;
|
||||||
|
use SPC\store\FileSystem;
|
||||||
use SPC\util\CustomExt;
|
use SPC\util\CustomExt;
|
||||||
|
|
||||||
#[CustomExt('uv')]
|
#[CustomExt('uv')]
|
||||||
@@ -16,4 +17,14 @@ class uv extends Extension
|
|||||||
throw new \RuntimeException('The latest uv extension requires PHP 8.0 or later');
|
throw new \RuntimeException('The latest uv extension requires PHP 8.0 or later');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function patchBeforeSharedMake(): bool
|
||||||
|
{
|
||||||
|
parent::patchBeforeSharedMake();
|
||||||
|
if (PHP_OS_FAMILY !== 'Linux' || arch2gnu(php_uname('m')) !== 'aarch64') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FileSystem::replaceFileRegex($this->source_dir . '/Makefile', '/^(LDFLAGS =.*)$/m', '$1 -luv -ldl -lrt -pthread');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,6 @@ class yac extends Extension
|
|||||||
|
|
||||||
public function getUnixConfigureArg(bool $shared = false): string
|
public function getUnixConfigureArg(bool $shared = false): string
|
||||||
{
|
{
|
||||||
return '--enable-yac ' . ($shared ? '=shared' : '') . ' --enable-igbinary --enable-json --with-system-fastlz';
|
return '--enable-yac' . ($shared ? '=shared' : '') . ' --enable-igbinary --enable-json --with-system-fastlz';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ class BSDBuilder extends UnixBuilderBase
|
|||||||
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
|
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
|
||||||
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
|
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
|
||||||
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
|
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
|
||||||
|
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
|
||||||
|
|
||||||
shell()->cd(SOURCE_PATH . '/php-src')
|
shell()->cd(SOURCE_PATH . '/php-src')
|
||||||
->exec(
|
->exec(
|
||||||
@@ -143,6 +144,10 @@ class BSDBuilder extends UnixBuilderBase
|
|||||||
}
|
}
|
||||||
$this->buildEmbed();
|
$this->buildEmbed();
|
||||||
}
|
}
|
||||||
|
if ($enableFrankenphp) {
|
||||||
|
logger()->info('building frankenphp');
|
||||||
|
$this->buildFrankenphp();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPHP(int $build_target = BUILD_TARGET_NONE)
|
public function testPHP(int $build_target = BUILD_TARGET_NONE)
|
||||||
|
|||||||
12
src/SPC/builder/freebsd/library/watcher.php
Normal file
12
src/SPC/builder/freebsd/library/watcher.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\builder\freebsd\library;
|
||||||
|
|
||||||
|
class watcher extends BSDLibraryBase
|
||||||
|
{
|
||||||
|
use \SPC\builder\unix\library\watcher;
|
||||||
|
|
||||||
|
public const NAME = 'watcher';
|
||||||
|
}
|
||||||
@@ -10,7 +10,10 @@ use SPC\exception\RuntimeException;
|
|||||||
use SPC\exception\WrongUsageException;
|
use SPC\exception\WrongUsageException;
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
use SPC\store\SourcePatcher;
|
use SPC\store\SourcePatcher;
|
||||||
|
use SPC\toolchain\ToolchainManager;
|
||||||
|
use SPC\toolchain\ZigToolchain;
|
||||||
use SPC\util\GlobalEnvManager;
|
use SPC\util\GlobalEnvManager;
|
||||||
|
use SPC\util\SPCTarget;
|
||||||
|
|
||||||
class LinuxBuilder extends UnixBuilderBase
|
class LinuxBuilder extends UnixBuilderBase
|
||||||
{
|
{
|
||||||
@@ -25,23 +28,8 @@ class LinuxBuilder extends UnixBuilderBase
|
|||||||
{
|
{
|
||||||
$this->options = $options;
|
$this->options = $options;
|
||||||
|
|
||||||
// check musl-cross make installed if we use musl-cross-make
|
GlobalEnvManager::init();
|
||||||
$arch = arch2gnu(php_uname('m'));
|
GlobalEnvManager::afterInit();
|
||||||
|
|
||||||
GlobalEnvManager::init($this);
|
|
||||||
|
|
||||||
if (getenv('SPC_LIBC') === 'musl' && !SystemUtil::isMuslDist()) {
|
|
||||||
$this->setOptionIfNotExist('library_path', "LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\"");
|
|
||||||
$this->setOptionIfNotExist('ld_library_path', "LD_LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\"");
|
|
||||||
GlobalEnvManager::putenv("PATH=/usr/local/musl/bin:/usr/local/musl/{$arch}-linux-musl/bin:" . getenv('PATH'));
|
|
||||||
$configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
|
|
||||||
$configure = "LD_LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\" " . $configure;
|
|
||||||
GlobalEnvManager::putenv("SPC_CMD_PREFIX_PHP_CONFIGURE={$configure}");
|
|
||||||
|
|
||||||
if (!file_exists("/usr/local/musl/{$arch}-linux-musl/lib/libc.a")) {
|
|
||||||
throw new WrongUsageException('You are building with musl-libc target in glibc distro, but musl-toolchain is not installed, please install musl-toolchain first. (You can use `doctor` command to install it)');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// concurrency
|
// concurrency
|
||||||
$this->concurrency = intval(getenv('SPC_CONCURRENCY'));
|
$this->concurrency = intval(getenv('SPC_CONCURRENCY'));
|
||||||
@@ -81,6 +69,7 @@ class LinuxBuilder extends UnixBuilderBase
|
|||||||
}
|
}
|
||||||
// add libstdc++, some extensions or libraries need it
|
// add libstdc++, some extensions or libraries need it
|
||||||
$extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCpp() ? '-lstdc++ ' : '');
|
$extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCpp() ? '-lstdc++ ' : '');
|
||||||
|
$extra_libs .= (ToolchainManager::getToolchainClass() === ZigToolchain::class ? ' -lunwind' : '');
|
||||||
f_putenv('SPC_EXTRA_LIBS=' . $extra_libs);
|
f_putenv('SPC_EXTRA_LIBS=' . $extra_libs);
|
||||||
$cflags = $this->arch_c_flags;
|
$cflags = $this->arch_c_flags;
|
||||||
f_putenv('CFLAGS=' . $cflags);
|
f_putenv('CFLAGS=' . $cflags);
|
||||||
@@ -104,16 +93,19 @@ class LinuxBuilder extends UnixBuilderBase
|
|||||||
$zts = '';
|
$zts = '';
|
||||||
}
|
}
|
||||||
$disable_jit = $this->getOption('disable-opcache-jit', false) ? '--disable-opcache-jit ' : '';
|
$disable_jit = $this->getOption('disable-opcache-jit', false) ? '--disable-opcache-jit ' : '';
|
||||||
|
if (!$disable_jit && $this->getExt('opcache')) {
|
||||||
|
f_putenv('COMPILER_EXTRA=-fno-sanitize=undefined');
|
||||||
|
}
|
||||||
$config_file_path = $this->getOption('with-config-file-path', false) ?
|
$config_file_path = $this->getOption('with-config-file-path', false) ?
|
||||||
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
|
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
|
||||||
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
|
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
|
||||||
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
|
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
|
||||||
|
|
||||||
$enable_cli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
|
$enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
|
||||||
$enable_fpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
|
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
|
||||||
$enable_micro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
|
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
|
||||||
$enable_embed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
|
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
|
||||||
|
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
|
||||||
|
|
||||||
$mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : '';
|
$mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : '';
|
||||||
// prepare build php envs
|
// prepare build php envs
|
||||||
@@ -125,7 +117,7 @@ class LinuxBuilder extends UnixBuilderBase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// process micro upx patch if micro sapi enabled
|
// process micro upx patch if micro sapi enabled
|
||||||
if ($enable_micro) {
|
if ($enableMicro) {
|
||||||
if (version_compare($this->getMicroVersion(), '0.2.0') < 0) {
|
if (version_compare($this->getMicroVersion(), '0.2.0') < 0) {
|
||||||
// for phpmicro 0.1.x
|
// for phpmicro 0.1.x
|
||||||
$this->processMicroUPXLegacy();
|
$this->processMicroUPXLegacy();
|
||||||
@@ -134,13 +126,19 @@ class LinuxBuilder extends UnixBuilderBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
$embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
|
$embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
|
||||||
|
if ($embed_type !== 'static' && SPCTarget::isStatic()) {
|
||||||
|
throw new WrongUsageException(
|
||||||
|
'Linux does not support loading shared libraries when linking libc statically. ' .
|
||||||
|
'Change SPC_CMD_VAR_PHP_EMBED_TYPE to static.'
|
||||||
|
);
|
||||||
|
}
|
||||||
shell()->cd(SOURCE_PATH . '/php-src')
|
shell()->cd(SOURCE_PATH . '/php-src')
|
||||||
->exec(
|
->exec(
|
||||||
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
|
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
|
||||||
($enable_cli ? '--enable-cli ' : '--disable-cli ') .
|
($enableCli ? '--enable-cli ' : '--disable-cli ') .
|
||||||
($enable_fpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') .
|
($enableFpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') .
|
||||||
($enable_embed ? "--enable-embed={$embed_type} " : '--disable-embed ') .
|
($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') .
|
||||||
($enable_micro ? '--enable-micro=all-static ' : '--disable-micro ') .
|
($enableMicro ? '--enable-micro=all-static ' : '--disable-micro ') .
|
||||||
$config_file_path .
|
$config_file_path .
|
||||||
$config_file_scan_dir .
|
$config_file_scan_dir .
|
||||||
$disable_jit .
|
$disable_jit .
|
||||||
@@ -156,25 +154,29 @@ class LinuxBuilder extends UnixBuilderBase
|
|||||||
|
|
||||||
$this->cleanMake();
|
$this->cleanMake();
|
||||||
|
|
||||||
if ($enable_cli) {
|
if ($enableCli) {
|
||||||
logger()->info('building cli');
|
logger()->info('building cli');
|
||||||
$this->buildCli();
|
$this->buildCli();
|
||||||
}
|
}
|
||||||
if ($enable_fpm) {
|
if ($enableFpm) {
|
||||||
logger()->info('building fpm');
|
logger()->info('building fpm');
|
||||||
$this->buildFpm();
|
$this->buildFpm();
|
||||||
}
|
}
|
||||||
if ($enable_micro) {
|
if ($enableMicro) {
|
||||||
logger()->info('building micro');
|
logger()->info('building micro');
|
||||||
$this->buildMicro();
|
$this->buildMicro();
|
||||||
}
|
}
|
||||||
if ($enable_embed) {
|
if ($enableEmbed) {
|
||||||
logger()->info('building embed');
|
logger()->info('building embed');
|
||||||
if ($enable_micro) {
|
if ($enableMicro) {
|
||||||
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'OVERALL_TARGET =', 'OVERALL_TARGET = libphp.la');
|
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'OVERALL_TARGET =', 'OVERALL_TARGET = libphp.la');
|
||||||
}
|
}
|
||||||
$this->buildEmbed();
|
$this->buildEmbed();
|
||||||
}
|
}
|
||||||
|
if ($enableFrankenphp) {
|
||||||
|
logger()->info('building frankenphp');
|
||||||
|
$this->buildFrankenphp();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPHP(int $build_target = BUILD_TARGET_NONE)
|
public function testPHP(int $build_target = BUILD_TARGET_NONE)
|
||||||
@@ -285,16 +287,48 @@ class LinuxBuilder extends UnixBuilderBase
|
|||||||
->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install");
|
->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install");
|
||||||
|
|
||||||
$ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS');
|
$ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS');
|
||||||
|
$realLibName = 'libphp.so';
|
||||||
if (preg_match('/-release\s+(\S+)/', $ldflags, $matches)) {
|
if (preg_match('/-release\s+(\S+)/', $ldflags, $matches)) {
|
||||||
$release = $matches[1];
|
$release = $matches[1];
|
||||||
$realLibName = 'libphp-' . $release . '.so';
|
$realLibName = 'libphp-' . $release . '.so';
|
||||||
$realLib = BUILD_LIB_PATH . '/' . $realLibName;
|
|
||||||
rename(BUILD_LIB_PATH . '/libphp.so', $realLib);
|
|
||||||
$cwd = getcwd();
|
$cwd = getcwd();
|
||||||
chdir(BUILD_LIB_PATH);
|
$libphpPath = BUILD_LIB_PATH . '/libphp.so';
|
||||||
symlink($realLibName, 'libphp.so');
|
$libphpRelease = BUILD_LIB_PATH . '/' . $realLibName;
|
||||||
|
if (!file_exists($libphpRelease) && file_exists($libphpPath)) {
|
||||||
|
rename($libphpPath, $libphpRelease);
|
||||||
|
}
|
||||||
|
if (file_exists($libphpRelease)) {
|
||||||
|
chdir(BUILD_LIB_PATH);
|
||||||
|
if (file_exists($libphpPath)) {
|
||||||
|
unlink($libphpPath);
|
||||||
|
}
|
||||||
|
symlink($realLibName, 'libphp.so');
|
||||||
|
}
|
||||||
|
if (is_dir(BUILD_MODULES_PATH)) {
|
||||||
|
chdir(BUILD_MODULES_PATH);
|
||||||
|
foreach ($this->getExts() as $ext) {
|
||||||
|
if (!$ext->isBuildShared()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = $ext->getName();
|
||||||
|
$versioned = "{$name}-{$release}.so";
|
||||||
|
$unversioned = "{$name}.so";
|
||||||
|
if (is_file(BUILD_MODULES_PATH . "/{$versioned}")) {
|
||||||
|
rename(BUILD_MODULES_PATH . "/{$versioned}", BUILD_MODULES_PATH . "/{$unversioned}");
|
||||||
|
shell()->cd(BUILD_MODULES_PATH)
|
||||||
|
->exec(sprintf(
|
||||||
|
'patchelf --set-soname %s %s',
|
||||||
|
escapeshellarg($unversioned),
|
||||||
|
escapeshellarg($unversioned)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
chdir($cwd);
|
chdir($cwd);
|
||||||
}
|
}
|
||||||
|
if (!$this->getOption('no-strip', false) && file_exists(BUILD_LIB_PATH . '/' . $realLibName)) {
|
||||||
|
shell()->cd(BUILD_LIB_PATH)->exec("strip --strip-all {$realLibName}");
|
||||||
|
}
|
||||||
$this->patchPhpScripts();
|
$this->patchPhpScripts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ namespace SPC\builder\linux;
|
|||||||
|
|
||||||
use SPC\builder\traits\UnixSystemUtilTrait;
|
use SPC\builder\traits\UnixSystemUtilTrait;
|
||||||
use SPC\exception\RuntimeException;
|
use SPC\exception\RuntimeException;
|
||||||
|
use SPC\toolchain\ToolchainManager;
|
||||||
|
use SPC\toolchain\ZigToolchain;
|
||||||
|
|
||||||
class SystemUtil
|
class SystemUtil
|
||||||
{
|
{
|
||||||
@@ -13,6 +15,8 @@ class SystemUtil
|
|||||||
|
|
||||||
public static ?string $libc_version = null;
|
public static ?string $libc_version = null;
|
||||||
|
|
||||||
|
private static ?string $extra_runtime_objects = null;
|
||||||
|
|
||||||
/** @noinspection PhpMissingBreakStatementInspection */
|
/** @noinspection PhpMissingBreakStatementInspection */
|
||||||
public static function getOSRelease(): array
|
public static function getOSRelease(): array
|
||||||
{
|
{
|
||||||
@@ -78,9 +82,11 @@ class SystemUtil
|
|||||||
/**
|
/**
|
||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
*/
|
*/
|
||||||
public static function getCCType(string $cc): string
|
public static function getCCType(?string $cc = null): string
|
||||||
{
|
{
|
||||||
|
$cc ??= getenv('CC');
|
||||||
return match (true) {
|
return match (true) {
|
||||||
|
str_contains($cc, 'zig') => 'clang',
|
||||||
str_ends_with($cc, 'c++'), str_ends_with($cc, 'cc'), str_ends_with($cc, 'g++'), str_ends_with($cc, 'gcc') => 'gcc',
|
str_ends_with($cc, 'c++'), str_ends_with($cc, 'cc'), str_ends_with($cc, 'g++'), str_ends_with($cc, 'gcc') => 'gcc',
|
||||||
$cc === 'clang++', $cc === 'clang', str_starts_with($cc, 'musl-clang') => 'clang',
|
$cc === 'clang++', $cc === 'clang', str_starts_with($cc, 'musl-clang') => 'clang',
|
||||||
default => throw new RuntimeException("unknown cc type: {$cc}"),
|
default => throw new RuntimeException("unknown cc type: {$cc}"),
|
||||||
@@ -188,12 +194,12 @@ class SystemUtil
|
|||||||
/**
|
/**
|
||||||
* Get libc version string from ldd
|
* Get libc version string from ldd
|
||||||
*/
|
*/
|
||||||
public static function getLibcVersionIfExists(): ?string
|
public static function getLibcVersionIfExists(?string $libc = null): ?string
|
||||||
{
|
{
|
||||||
if (self::$libc_version !== null) {
|
if (self::$libc_version !== null) {
|
||||||
return self::$libc_version;
|
return self::$libc_version;
|
||||||
}
|
}
|
||||||
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'glibc') {
|
if ($libc === 'glibc') {
|
||||||
$result = shell()->execWithResult('ldd --version', false);
|
$result = shell()->execWithResult('ldd --version', false);
|
||||||
if ($result[0] !== 0) {
|
if ($result[0] !== 0) {
|
||||||
return null;
|
return null;
|
||||||
@@ -208,7 +214,7 @@ class SystemUtil
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') {
|
if ($libc === 'musl') {
|
||||||
if (self::isMuslDist()) {
|
if (self::isMuslDist()) {
|
||||||
$result = shell()->execWithResult('ldd 2>&1', false);
|
$result = shell()->execWithResult('ldd 2>&1', false);
|
||||||
} else {
|
} else {
|
||||||
@@ -224,4 +230,37 @@ class SystemUtil
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getExtraRuntimeObjects(): string
|
||||||
|
{
|
||||||
|
if (ToolchainManager::getToolchainClass() !== ZigToolchain::class) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::$extra_runtime_objects !== null) {
|
||||||
|
return self::$extra_runtime_objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
$paths = ['/usr/lib/gcc', '/usr/local/lib/gcc'];
|
||||||
|
$objects = ['crtbeginS.o', 'crtendS.o'];
|
||||||
|
$found = [];
|
||||||
|
|
||||||
|
foreach ($objects as $obj) {
|
||||||
|
$located = null;
|
||||||
|
foreach ($paths as $base) {
|
||||||
|
$output = shell_exec("find {$base} -name {$obj} -print -quit 2>/dev/null");
|
||||||
|
$line = trim((string) $output);
|
||||||
|
if ($line !== '') {
|
||||||
|
$located = $line;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($located) {
|
||||||
|
$found[] = $located;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$extra_runtime_objects = implode(' ', $found);
|
||||||
|
return implode(' ', $found);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace SPC\builder\linux\library;
|
namespace SPC\builder\linux\library;
|
||||||
|
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
|
use SPC\util\SPCTarget;
|
||||||
|
|
||||||
class icu extends LinuxLibraryBase
|
class icu extends LinuxLibraryBase
|
||||||
{
|
{
|
||||||
@@ -16,7 +17,7 @@ class icu extends LinuxLibraryBase
|
|||||||
{
|
{
|
||||||
$cppflags = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1 -DPIC -fPIC"';
|
$cppflags = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1 -DPIC -fPIC"';
|
||||||
$cxxflags = 'CXXFLAGS="-std=c++17 -DPIC -fPIC -fno-ident"';
|
$cxxflags = 'CXXFLAGS="-std=c++17 -DPIC -fPIC -fno-ident"';
|
||||||
$ldflags = getenv('SPC_LIBC') !== 'glibc' ? 'LDFLAGS="-static"' : '';
|
$ldflags = SPCTarget::isStatic() ? 'LDFLAGS="-static"' : '';
|
||||||
shell()->cd($this->source_dir . '/source')->initializeEnv($this)
|
shell()->cd($this->source_dir . '/source')->initializeEnv($this)
|
||||||
->exec(
|
->exec(
|
||||||
"{$cppflags} {$cxxflags} {$ldflags} " .
|
"{$cppflags} {$cxxflags} {$ldflags} " .
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ class libmemcached extends LinuxLibraryBase
|
|||||||
|
|
||||||
public function build(): void
|
public function build(): void
|
||||||
{
|
{
|
||||||
UnixCMakeExecutor::create($this)->build();
|
UnixCMakeExecutor::create($this)
|
||||||
|
->addConfigureArgs('-DCMAKE_INSTALL_RPATH=""')
|
||||||
|
->build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ class openssl extends LinuxLibraryBase
|
|||||||
|
|
||||||
$ex_lib = trim($ex_lib);
|
$ex_lib = trim($ex_lib);
|
||||||
|
|
||||||
$clang_postfix = SystemUtil::getCCType(getenv('CC')) === 'clang' ? '-clang' : '';
|
/* @phpstan-ignore-next-line */
|
||||||
|
$clang_postfix = SystemUtil::getCCType() === 'clang' && (GNU_ARCH === 'x86_64' || PHP_OS_FAMILY === 'Darwin') ? '-clang' : '';
|
||||||
|
|
||||||
shell()->cd($this->source_dir)->initializeEnv($this)
|
shell()->cd($this->source_dir)->initializeEnv($this)
|
||||||
->exec(
|
->exec(
|
||||||
|
|||||||
12
src/SPC/builder/linux/library/watcher.php
Normal file
12
src/SPC/builder/linux/library/watcher.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\builder\linux\library;
|
||||||
|
|
||||||
|
class watcher extends LinuxLibraryBase
|
||||||
|
{
|
||||||
|
use \SPC\builder\unix\library\watcher;
|
||||||
|
|
||||||
|
public const NAME = 'watcher';
|
||||||
|
}
|
||||||
@@ -28,7 +28,8 @@ class MacOSBuilder extends UnixBuilderBase
|
|||||||
$this->options = $options;
|
$this->options = $options;
|
||||||
|
|
||||||
// apply global environment variables
|
// apply global environment variables
|
||||||
GlobalEnvManager::init($this);
|
GlobalEnvManager::init();
|
||||||
|
GlobalEnvManager::afterInit();
|
||||||
|
|
||||||
// ---------- set necessary compile vars ----------
|
// ---------- set necessary compile vars ----------
|
||||||
// concurrency
|
// concurrency
|
||||||
@@ -122,6 +123,7 @@ class MacOSBuilder extends UnixBuilderBase
|
|||||||
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
|
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
|
||||||
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
|
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
|
||||||
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
|
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
|
||||||
|
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
|
||||||
|
|
||||||
// prepare build php envs
|
// prepare build php envs
|
||||||
$mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : '';
|
$mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : '';
|
||||||
@@ -180,9 +182,10 @@ class MacOSBuilder extends UnixBuilderBase
|
|||||||
}
|
}
|
||||||
$this->buildEmbed();
|
$this->buildEmbed();
|
||||||
}
|
}
|
||||||
|
if ($enableFrankenphp) {
|
||||||
$this->emitPatchPoint('before-sanity-check');
|
logger()->info('building frankenphp');
|
||||||
$this->sanityCheck($build_target);
|
$this->buildFrankenphp();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPHP(int $build_target = BUILD_TARGET_NONE)
|
public function testPHP(int $build_target = BUILD_TARGET_NONE)
|
||||||
@@ -205,7 +208,7 @@ class MacOSBuilder extends UnixBuilderBase
|
|||||||
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make';
|
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make';
|
||||||
$shell->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} cli");
|
$shell->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} cli");
|
||||||
if (!$this->getOption('no-strip', false)) {
|
if (!$this->getOption('no-strip', false)) {
|
||||||
$shell->exec('dsymutil -f sapi/cli/php')->exec('strip sapi/cli/php');
|
$shell->exec('dsymutil -f sapi/cli/php')->exec('strip -S sapi/cli/php');
|
||||||
}
|
}
|
||||||
$this->deployBinary(BUILD_TARGET_CLI);
|
$this->deployBinary(BUILD_TARGET_CLI);
|
||||||
}
|
}
|
||||||
@@ -232,12 +235,15 @@ class MacOSBuilder extends UnixBuilderBase
|
|||||||
|
|
||||||
// patch fake cli for micro
|
// patch fake cli for micro
|
||||||
$vars['EXTRA_CFLAGS'] .= $enable_fake_cli;
|
$vars['EXTRA_CFLAGS'] .= $enable_fake_cli;
|
||||||
if ($this->getOption('no-strip', false)) {
|
|
||||||
$vars['STRIP'] = 'dsymutil -f ';
|
|
||||||
}
|
|
||||||
$vars = SystemUtil::makeEnvVarString($vars);
|
$vars = SystemUtil::makeEnvVarString($vars);
|
||||||
|
|
||||||
shell()->cd(SOURCE_PATH . '/php-src')->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . " {$vars} micro");
|
$shell = shell()->cd(SOURCE_PATH . '/php-src');
|
||||||
|
// build
|
||||||
|
$shell->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . " {$vars} micro");
|
||||||
|
// strip
|
||||||
|
if (!$this->getOption('no-strip', false)) {
|
||||||
|
$shell->exec('dsymutil -f sapi/micro/micro.sfx')->exec('strip -S sapi/micro/micro.sfx');
|
||||||
|
}
|
||||||
|
|
||||||
$this->deployBinary(BUILD_TARGET_MICRO);
|
$this->deployBinary(BUILD_TARGET_MICRO);
|
||||||
|
|
||||||
@@ -259,7 +265,7 @@ class MacOSBuilder extends UnixBuilderBase
|
|||||||
$shell = shell()->cd(SOURCE_PATH . '/php-src');
|
$shell = shell()->cd(SOURCE_PATH . '/php-src');
|
||||||
$shell->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . " {$vars} fpm");
|
$shell->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . " {$vars} fpm");
|
||||||
if (!$this->getOption('no-strip', false)) {
|
if (!$this->getOption('no-strip', false)) {
|
||||||
$shell->exec('dsymutil -f sapi/fpm/php-fpm')->exec('strip sapi/fpm/php-fpm');
|
$shell->exec('dsymutil -f sapi/fpm/php-fpm')->exec('strip -S sapi/fpm/php-fpm');
|
||||||
}
|
}
|
||||||
$this->deployBinary(BUILD_TARGET_FPM);
|
$this->deployBinary(BUILD_TARGET_FPM);
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/SPC/builder/macos/library/watcher.php
Normal file
12
src/SPC/builder/macos/library/watcher.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\builder\macos\library;
|
||||||
|
|
||||||
|
class watcher extends MacOSLibraryBase
|
||||||
|
{
|
||||||
|
use \SPC\builder\unix\library\watcher;
|
||||||
|
|
||||||
|
public const NAME = 'watcher';
|
||||||
|
}
|
||||||
@@ -119,9 +119,9 @@ trait UnixLibraryTrait
|
|||||||
{
|
{
|
||||||
$env = getenv($this->getSnakeCaseName() . '_CFLAGS') ?: '';
|
$env = getenv($this->getSnakeCaseName() . '_CFLAGS') ?: '';
|
||||||
if (!str_contains($env, $this->builder->arch_c_flags)) {
|
if (!str_contains($env, $this->builder->arch_c_flags)) {
|
||||||
$env .= $this->builder->arch_c_flags;
|
$env .= ' ' . $this->builder->arch_c_flags;
|
||||||
}
|
}
|
||||||
return $env;
|
return trim($env);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLibExtraLdFlags(): string
|
public function getLibExtraLdFlags(): string
|
||||||
@@ -138,8 +138,8 @@ trait UnixLibraryTrait
|
|||||||
{
|
{
|
||||||
$env = getenv($this->getSnakeCaseName() . '_CXXFLAGS') ?: '';
|
$env = getenv($this->getSnakeCaseName() . '_CXXFLAGS') ?: '';
|
||||||
if (!str_contains($env, $this->builder->arch_cxx_flags)) {
|
if (!str_contains($env, $this->builder->arch_cxx_flags)) {
|
||||||
$env .= $this->builder->arch_cxx_flags;
|
$env .= ' ' . $this->builder->arch_cxx_flags;
|
||||||
}
|
}
|
||||||
return $env;
|
return trim($env);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,14 @@ use SPC\exception\FileSystemException;
|
|||||||
use SPC\exception\RuntimeException;
|
use SPC\exception\RuntimeException;
|
||||||
use SPC\exception\WrongUsageException;
|
use SPC\exception\WrongUsageException;
|
||||||
use SPC\store\Config;
|
use SPC\store\Config;
|
||||||
|
use SPC\store\CurlHook;
|
||||||
|
use SPC\store\Downloader;
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
|
use SPC\store\pkg\GoXcaddy;
|
||||||
use SPC\util\DependencyUtil;
|
use SPC\util\DependencyUtil;
|
||||||
|
use SPC\util\GlobalEnvManager;
|
||||||
use SPC\util\SPCConfigUtil;
|
use SPC\util\SPCConfigUtil;
|
||||||
|
use SPC\util\SPCTarget;
|
||||||
|
|
||||||
abstract class UnixBuilderBase extends BuilderBase
|
abstract class UnixBuilderBase extends BuilderBase
|
||||||
{
|
{
|
||||||
@@ -198,26 +203,43 @@ abstract class UnixBuilderBase extends BuilderBase
|
|||||||
$util = new SPCConfigUtil($this);
|
$util = new SPCConfigUtil($this);
|
||||||
$config = $util->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs'));
|
$config = $util->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs'));
|
||||||
$lens = "{$config['cflags']} {$config['ldflags']} {$config['libs']}";
|
$lens = "{$config['cflags']} {$config['ldflags']} {$config['libs']}";
|
||||||
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') {
|
if (SPCTarget::isStatic()) {
|
||||||
$lens .= ' -static';
|
$lens .= ' -static';
|
||||||
}
|
}
|
||||||
|
// if someone changed to EMBED_TYPE=shared, we need to add LD_LIBRARY_PATH
|
||||||
|
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
|
||||||
|
$ext_path = 'LD_LIBRARY_PATH=' . BUILD_LIB_PATH . ':$LD_LIBRARY_PATH ';
|
||||||
|
FileSystem::removeFileIfExists(BUILD_LIB_PATH . '/libphp.a');
|
||||||
|
} else {
|
||||||
|
$ext_path = '';
|
||||||
|
foreach (glob(BUILD_LIB_PATH . '/libphp*.so') as $file) {
|
||||||
|
unlink($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens);
|
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens);
|
||||||
if ($ret !== 0) {
|
if ($ret !== 0) {
|
||||||
throw new RuntimeException('embed failed sanity check: build failed. Error message: ' . implode("\n", $out));
|
throw new RuntimeException('embed failed sanity check: build failed. Error message: ' . implode("\n", $out));
|
||||||
}
|
}
|
||||||
// if someone changed to --enable-embed=shared, we need to add LD_LIBRARY_PATH
|
|
||||||
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
|
|
||||||
$ext_path = 'LD_LIBRARY_PATH=' . BUILD_ROOT_PATH . '/lib:$LD_LIBRARY_PATH ';
|
|
||||||
FileSystem::removeFileIfExists(BUILD_ROOT_PATH . '/lib/libphp.a');
|
|
||||||
} else {
|
|
||||||
$ext_path = '';
|
|
||||||
FileSystem::removeFileIfExists(BUILD_ROOT_PATH . '/lib/libphp.so');
|
|
||||||
}
|
|
||||||
[$ret, $output] = shell()->cd($sample_file_path)->execWithResult($ext_path . './embed');
|
[$ret, $output] = shell()->cd($sample_file_path)->execWithResult($ext_path . './embed');
|
||||||
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
|
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
|
||||||
throw new RuntimeException('embed failed sanity check: run failed. Error message: ' . implode("\n", $output));
|
throw new RuntimeException('embed failed sanity check: run failed. Error message: ' . implode("\n", $output));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sanity check for frankenphp
|
||||||
|
if (($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP) {
|
||||||
|
logger()->info('running frankenphp sanity check');
|
||||||
|
$frankenphp = BUILD_BIN_PATH . '/frankenphp';
|
||||||
|
if (!file_exists($frankenphp)) {
|
||||||
|
throw new RuntimeException('FrankenPHP binary not found: ' . $frankenphp);
|
||||||
|
}
|
||||||
|
[$ret, $output] = shell()
|
||||||
|
->setEnv(['LD_LIBRARY_PATH' => BUILD_LIB_PATH])
|
||||||
|
->execWithResult("{$frankenphp} version");
|
||||||
|
if ($ret !== 0 || !str_contains(implode('', $output), 'FrankenPHP')) {
|
||||||
|
throw new RuntimeException('FrankenPHP failed sanity check: ret[' . $ret . ']. out[' . implode('', $output) . ']');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -277,4 +299,70 @@ abstract class UnixBuilderBase extends BuilderBase
|
|||||||
FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str);
|
FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws WrongUsageException
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
protected function buildFrankenphp(): void
|
||||||
|
{
|
||||||
|
$nobrotli = $this->getLib('brotli') === null ? ',nobrotli' : '';
|
||||||
|
$nowatcher = $this->getLib('watcher') === null ? ',nowatcher' : '';
|
||||||
|
$xcaddyModules = getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES');
|
||||||
|
// make it possible to build from a different frankenphp directory!
|
||||||
|
if (!str_contains($xcaddyModules, '--with github.com/dunglas/frankenphp')) {
|
||||||
|
$xcaddyModules = '--with github.com/dunglas/frankenphp ' . $xcaddyModules;
|
||||||
|
}
|
||||||
|
if ($this->getLib('brotli') === null && str_contains($xcaddyModules, '--with github.com/dunglas/caddy-cbrotli')) {
|
||||||
|
logger()->warning('caddy-cbrotli module is enabled, but brotli library is not built. Disabling caddy-cbrotli.');
|
||||||
|
$xcaddyModules = str_replace('--with github.com/dunglas/caddy-cbrotli', '', $xcaddyModules);
|
||||||
|
}
|
||||||
|
$lrt = PHP_OS_FAMILY === 'Linux' ? '-lrt' : '';
|
||||||
|
$releaseInfo = json_decode(Downloader::curlExec(
|
||||||
|
'https://api.github.com/repos/php/frankenphp/releases/latest',
|
||||||
|
hooks: [[CurlHook::class, 'setupGithubToken']],
|
||||||
|
), true);
|
||||||
|
$frankenPhpVersion = $releaseInfo['tag_name'];
|
||||||
|
$libphpVersion = $this->getPHPVersion();
|
||||||
|
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
|
||||||
|
$libphpVersion = preg_replace('/\.\d$/', '', $libphpVersion);
|
||||||
|
}
|
||||||
|
$debugFlags = $this->getOption('no-strip') ? "'-w -s' " : '';
|
||||||
|
$extLdFlags = "-extldflags '-pie'";
|
||||||
|
$muslTags = '';
|
||||||
|
$staticFlags = '';
|
||||||
|
if (SPCTarget::isStatic()) {
|
||||||
|
$extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000'";
|
||||||
|
$muslTags = 'static_build,';
|
||||||
|
$staticFlags = '-static -static-pie';
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = (new SPCConfigUtil($this))->config($this->ext_list, $this->lib_list, with_dependencies: true);
|
||||||
|
|
||||||
|
$env = [
|
||||||
|
'CGO_ENABLED' => '1',
|
||||||
|
'CGO_CFLAGS' => $config['cflags'],
|
||||||
|
'CGO_LDFLAGS' => "{$staticFlags} {$config['ldflags']} {$config['libs']} {$lrt}",
|
||||||
|
'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' .
|
||||||
|
'-ldflags \"-linkmode=external ' . $extLdFlags . ' ' . $debugFlags .
|
||||||
|
'-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' .
|
||||||
|
"{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " .
|
||||||
|
"-tags={$muslTags}nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}",
|
||||||
|
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
|
||||||
|
];
|
||||||
|
foreach (GoXcaddy::getEnvironment() as $key => $value) {
|
||||||
|
if ($key === 'PATH') {
|
||||||
|
GlobalEnvManager::addPathIfNotExists($value);
|
||||||
|
} else {
|
||||||
|
$env[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shell()->cd(BUILD_BIN_PATH)
|
||||||
|
->setEnv($env)
|
||||||
|
->exec("xcaddy build --output frankenphp {$xcaddyModules}");
|
||||||
|
|
||||||
|
if (!$this->getOption('no-strip', false) && file_exists(BUILD_BIN_PATH . '/frankenphp')) {
|
||||||
|
shell()->cd(BUILD_BIN_PATH)->exec('strip --strip-all frankenphp');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ trait attr
|
|||||||
protected function build(): void
|
protected function build(): void
|
||||||
{
|
{
|
||||||
UnixAutoconfExecutor::create($this)
|
UnixAutoconfExecutor::create($this)
|
||||||
|
->appendEnv([
|
||||||
|
'CFLAGS' => '-Wno-int-conversion -Wno-implicit-function-declaration',
|
||||||
|
])
|
||||||
->exec('libtoolize --force --copy')
|
->exec('libtoolize --force --copy')
|
||||||
->exec('./autogen.sh || autoreconf -if')
|
->exec('./autogen.sh || autoreconf -if')
|
||||||
->configure('--disable-nls')
|
->configure('--disable-nls')
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ trait curl
|
|||||||
->optionalLib('libssh2', fn ($lib) => "-DLIBSSH2_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DLIBSSH2_INCLUDE_DIR={$lib->getIncludeDir()}", '-DCURL_USE_LIBSSH2=OFF')
|
->optionalLib('libssh2', fn ($lib) => "-DLIBSSH2_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DLIBSSH2_INCLUDE_DIR={$lib->getIncludeDir()}", '-DCURL_USE_LIBSSH2=OFF')
|
||||||
->optionalLib('nghttp2', fn ($lib) => "-DUSE_NGHTTP2=ON -DNGHTTP2_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DNGHTTP2_INCLUDE_DIR={$lib->getIncludeDir()}", '-DUSE_NGHTTP2=OFF')
|
->optionalLib('nghttp2', fn ($lib) => "-DUSE_NGHTTP2=ON -DNGHTTP2_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DNGHTTP2_INCLUDE_DIR={$lib->getIncludeDir()}", '-DUSE_NGHTTP2=OFF')
|
||||||
->optionalLib('nghttp3', fn ($lib) => "-DUSE_NGHTTP3=ON -DNGHTTP3_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DNGHTTP3_INCLUDE_DIR={$lib->getIncludeDir()}", '-DUSE_NGHTTP3=OFF')
|
->optionalLib('nghttp3', fn ($lib) => "-DUSE_NGHTTP3=ON -DNGHTTP3_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DNGHTTP3_INCLUDE_DIR={$lib->getIncludeDir()}", '-DUSE_NGHTTP3=OFF')
|
||||||
|
->optionalLib('ngtcp2', fn ($lib) => "-DUSE_NGTCP2=ON -DNGNGTCP2_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DNGNGTCP2_INCLUDE_DIR={$lib->getIncludeDir()}", '-DUSE_NGTCP2=OFF')
|
||||||
->optionalLib('ldap', ...cmake_boolean_args('CURL_DISABLE_LDAP', true))
|
->optionalLib('ldap', ...cmake_boolean_args('CURL_DISABLE_LDAP', true))
|
||||||
->optionalLib('zstd', ...cmake_boolean_args('CURL_ZSTD'))
|
->optionalLib('zstd', ...cmake_boolean_args('CURL_ZSTD'))
|
||||||
->optionalLib('idn2', ...cmake_boolean_args('USE_LIBIDN2'))
|
->optionalLib('idn2', ...cmake_boolean_args('USE_LIBIDN2'))
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ trait gettext
|
|||||||
$autoconf->addConfigureArgs('--disable-threads');
|
$autoconf->addConfigureArgs('--disable-threads');
|
||||||
}
|
}
|
||||||
|
|
||||||
$autoconf->configure()->make(with_clean: true);
|
$autoconf->configure()->make();
|
||||||
$this->patchLaDependencyPrefix();
|
$this->patchLaDependencyPrefix();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,19 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace SPC\builder\unix\library;
|
namespace SPC\builder\unix\library;
|
||||||
|
|
||||||
use SPC\builder\linux\library\LinuxLibraryBase;
|
|
||||||
use SPC\builder\macos\library\MacOSLibraryBase;
|
|
||||||
use SPC\exception\FileSystemException;
|
use SPC\exception\FileSystemException;
|
||||||
use SPC\exception\RuntimeException;
|
use SPC\exception\RuntimeException;
|
||||||
|
use SPC\exception\WrongUsageException;
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
use SPC\util\executor\UnixAutoconfExecutor;
|
use SPC\util\executor\UnixAutoconfExecutor;
|
||||||
|
use SPC\util\SPCTarget;
|
||||||
|
|
||||||
trait imagemagick
|
trait imagemagick
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
* @throws FileSystemException
|
* @throws FileSystemException
|
||||||
|
* @throws WrongUsageException
|
||||||
*/
|
*/
|
||||||
protected function build(): void
|
protected function build(): void
|
||||||
{
|
{
|
||||||
@@ -32,17 +33,16 @@ trait imagemagick
|
|||||||
->optionalLib('freetype', ...ac_with_args('freetype'))
|
->optionalLib('freetype', ...ac_with_args('freetype'))
|
||||||
->optionalLib('bzip2', ...ac_with_args('bzlib'))
|
->optionalLib('bzip2', ...ac_with_args('bzlib'))
|
||||||
->addConfigureArgs(
|
->addConfigureArgs(
|
||||||
// TODO: glibc rh 10 toolset's libgomp.a was built without -fPIC so we can't use openmp without depending on libgomp.so
|
'--disable-openmp',
|
||||||
getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10') ? '--disable-openmp' : '--enable-openmp',
|
|
||||||
'--without-jxl',
|
'--without-jxl',
|
||||||
'--without-x',
|
'--without-x',
|
||||||
);
|
);
|
||||||
|
|
||||||
// special: linux musl needs `-static`
|
// special: linux-static target needs `-static`
|
||||||
$ldflags = ($this instanceof LinuxLibraryBase) && getenv('SPC_LIBC') !== 'glibc' ? ('-static -ldl') : '-ldl';
|
$ldflags = SPCTarget::isStatic() ? ('-static -ldl') : '-ldl';
|
||||||
|
|
||||||
// special: macOS needs -iconv
|
// special: macOS needs -iconv
|
||||||
$libs = $this instanceof MacOSLibraryBase ? '-liconv' : '';
|
$libs = SPCTarget::getTargetOS() === 'Darwin' ? '-liconv' : '';
|
||||||
|
|
||||||
$ac->appendEnv([
|
$ac->appendEnv([
|
||||||
'LDFLAGS' => $ldflags,
|
'LDFLAGS' => $ldflags,
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ namespace SPC\builder\unix\library;
|
|||||||
|
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
use SPC\util\executor\UnixAutoconfExecutor;
|
use SPC\util\executor\UnixAutoconfExecutor;
|
||||||
|
use SPC\util\SPCTarget;
|
||||||
|
|
||||||
trait ldap
|
trait ldap
|
||||||
{
|
{
|
||||||
public function patchBeforeBuild(): bool
|
public function patchBeforeBuild(): bool
|
||||||
{
|
{
|
||||||
$extra = getenv('SPC_LIBC') === 'glibc' ? '-ldl -lpthread -lm -lresolv -lutil' : '';
|
$extra = SPCTarget::getLibc() === 'glibc' ? '-ldl -lpthread -lm -lresolv -lutil' : '';
|
||||||
FileSystem::replaceFileStr($this->source_dir . '/configure', '"-lssl -lcrypto', '"-lssl -lcrypto -lz ' . $extra);
|
FileSystem::replaceFileStr($this->source_dir . '/configure', '"-lssl -lcrypto', '"-lssl -lcrypto -lz ' . $extra);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -26,8 +27,10 @@ trait ldap
|
|||||||
'--disable-slapd',
|
'--disable-slapd',
|
||||||
'--without-systemd',
|
'--without-systemd',
|
||||||
'--without-cyrus-sasl',
|
'--without-cyrus-sasl',
|
||||||
|
'ac_cv_func_pthread_kill_other_threads_np=no'
|
||||||
)
|
)
|
||||||
->appendEnv([
|
->appendEnv([
|
||||||
|
'CFLAGS' => '-Wno-date-time',
|
||||||
'LDFLAGS' => "-L{$this->getLibDir()}",
|
'LDFLAGS' => "-L{$this->getLibDir()}",
|
||||||
'CPPFLAGS' => "-I{$this->getIncludeDir()}",
|
'CPPFLAGS' => "-I{$this->getIncludeDir()}",
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -4,22 +4,24 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace SPC\builder\unix\library;
|
namespace SPC\builder\unix\library;
|
||||||
|
|
||||||
use SPC\exception\FileSystemException;
|
use SPC\toolchain\ToolchainManager;
|
||||||
use SPC\exception\RuntimeException;
|
use SPC\toolchain\ZigToolchain;
|
||||||
use SPC\util\executor\UnixCMakeExecutor;
|
use SPC\util\executor\UnixCMakeExecutor;
|
||||||
|
|
||||||
trait libaom
|
trait libaom
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @throws RuntimeException
|
|
||||||
* @throws FileSystemException
|
|
||||||
*/
|
|
||||||
protected function build(): void
|
protected function build(): void
|
||||||
{
|
{
|
||||||
|
$extra = getenv('COMPILER_EXTRA');
|
||||||
|
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
|
||||||
|
$new = trim($extra . ' -D_GNU_SOURCE');
|
||||||
|
f_putenv("COMPILER_EXTRA={$new}");
|
||||||
|
}
|
||||||
UnixCMakeExecutor::create($this)
|
UnixCMakeExecutor::create($this)
|
||||||
->setBuildDir("{$this->source_dir}/builddir")
|
->setBuildDir("{$this->source_dir}/builddir")
|
||||||
->addConfigureArgs('-DAOM_TARGET_CPU=generic')
|
->addConfigureArgs('-DAOM_TARGET_CPU=generic')
|
||||||
->build();
|
->build();
|
||||||
|
f_putenv("COMPILER_EXTRA={$extra}");
|
||||||
$this->patchPkgconfPrefix(['aom.pc']);
|
$this->patchPkgconfPrefix(['aom.pc']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ trait liblz4
|
|||||||
{
|
{
|
||||||
shell()->cd($this->source_dir)->initializeEnv($this)
|
shell()->cd($this->source_dir)->initializeEnv($this)
|
||||||
->exec("make PREFIX='' clean")
|
->exec("make PREFIX='' clean")
|
||||||
->exec("make -j{$this->builder->concurrency} PREFIX=''")
|
->exec("make lib -j{$this->builder->concurrency} PREFIX=''");
|
||||||
|
|
||||||
|
FileSystem::replaceFileStr($this->source_dir . '/Makefile', '$(MAKE) -C $(PRGDIR) $@', '');
|
||||||
|
|
||||||
|
shell()->cd($this->source_dir)
|
||||||
->exec("make install PREFIX='' DESTDIR=" . BUILD_ROOT_PATH);
|
->exec("make install PREFIX='' DESTDIR=" . BUILD_ROOT_PATH);
|
||||||
|
|
||||||
$this->patchPkgconfPrefix(['liblz4.pc']);
|
$this->patchPkgconfPrefix(['liblz4.pc']);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ namespace SPC\builder\unix\library;
|
|||||||
|
|
||||||
use SPC\exception\FileSystemException;
|
use SPC\exception\FileSystemException;
|
||||||
use SPC\exception\RuntimeException;
|
use SPC\exception\RuntimeException;
|
||||||
|
use SPC\store\FileSystem;
|
||||||
use SPC\util\executor\UnixAutoconfExecutor;
|
use SPC\util\executor\UnixAutoconfExecutor;
|
||||||
|
|
||||||
trait librdkafka
|
trait librdkafka
|
||||||
@@ -16,7 +17,18 @@ trait librdkafka
|
|||||||
*/
|
*/
|
||||||
protected function build(): void
|
protected function build(): void
|
||||||
{
|
{
|
||||||
|
FileSystem::replaceFileStr(
|
||||||
|
$this->source_dir . '/lds-gen.py',
|
||||||
|
"funcs.append('rd_ut_coverage_check')",
|
||||||
|
''
|
||||||
|
);
|
||||||
|
FileSystem::replaceFileStr(
|
||||||
|
$this->source_dir . '/src/rd.h',
|
||||||
|
'#error "IOV_MAX not defined"',
|
||||||
|
"#define IOV_MAX 1024\n#define __GNU__"
|
||||||
|
);
|
||||||
UnixAutoconfExecutor::create($this)
|
UnixAutoconfExecutor::create($this)
|
||||||
|
->appendEnv(['CFLAGS' => '-Wno-int-conversion -Wno-unused-but-set-variable -Wno-unused-variable'])
|
||||||
->optionalLib(
|
->optionalLib(
|
||||||
'zstd',
|
'zstd',
|
||||||
function ($lib) {
|
function ($lib) {
|
||||||
|
|||||||
@@ -33,7 +33,13 @@ trait libxslt
|
|||||||
'--without-debugger',
|
'--without-debugger',
|
||||||
"--with-libxml-prefix={$this->getBuildRootPath()}",
|
"--with-libxml-prefix={$this->getBuildRootPath()}",
|
||||||
);
|
);
|
||||||
$ac->exec("{$this->builder->getOption('library_path')} {$this->builder->getOption('ld_library_path')} ./configure {$ac->getConfigureArgsString()}")->make();
|
if (getenv('SPC_LD_LIBRARY_PATH') && getenv('SPC_LIBRARY_PATH')) {
|
||||||
|
$ac->appendEnv([
|
||||||
|
'LD_LIBRARY_PATH' => getenv('SPC_LD_LIBRARY_PATH'),
|
||||||
|
'LIBRARY_PATH' => getenv('SPC_LIBRARY_PATH'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$ac->configure()->make();
|
||||||
|
|
||||||
$this->patchPkgconfPrefix(['libexslt.pc']);
|
$this->patchPkgconfPrefix(['libexslt.pc']);
|
||||||
$this->patchLaDependencyPrefix();
|
$this->patchLaDependencyPrefix();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace SPC\builder\unix\library;
|
namespace SPC\builder\unix\library;
|
||||||
|
|
||||||
use SPC\util\executor\UnixCMakeExecutor;
|
use SPC\util\executor\UnixCMakeExecutor;
|
||||||
|
use SPC\util\SPCTarget;
|
||||||
|
|
||||||
trait mimalloc
|
trait mimalloc
|
||||||
{
|
{
|
||||||
@@ -13,9 +14,9 @@ trait mimalloc
|
|||||||
$cmake = UnixCMakeExecutor::create($this)
|
$cmake = UnixCMakeExecutor::create($this)
|
||||||
->addConfigureArgs(
|
->addConfigureArgs(
|
||||||
'-DMI_BUILD_SHARED=OFF',
|
'-DMI_BUILD_SHARED=OFF',
|
||||||
'-DMI_INSTALL_TOPLEVEL=ON'
|
'-DMI_INSTALL_TOPLEVEL=ON',
|
||||||
);
|
);
|
||||||
if (getenv('SPC_LIBC') === 'musl') {
|
if (SPCTarget::getLibc() === 'musl') {
|
||||||
$cmake->addConfigureArgs('-DMI_LIBC_MUSL=ON');
|
$cmake->addConfigureArgs('-DMI_LIBC_MUSL=ON');
|
||||||
}
|
}
|
||||||
$cmake->build();
|
$cmake->build();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ namespace SPC\builder\unix\library;
|
|||||||
|
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
use SPC\util\executor\UnixAutoconfExecutor;
|
use SPC\util\executor\UnixAutoconfExecutor;
|
||||||
|
use SPC\util\SPCTarget;
|
||||||
|
|
||||||
trait ncurses
|
trait ncurses
|
||||||
{
|
{
|
||||||
@@ -14,6 +15,9 @@ trait ncurses
|
|||||||
$filelist = FileSystem::scanDirFiles(BUILD_BIN_PATH, relative: true);
|
$filelist = FileSystem::scanDirFiles(BUILD_BIN_PATH, relative: true);
|
||||||
|
|
||||||
UnixAutoconfExecutor::create($this)
|
UnixAutoconfExecutor::create($this)
|
||||||
|
->appendEnv([
|
||||||
|
'LDFLAGS' => SPCTarget::isStatic() ? '-static' : '',
|
||||||
|
])
|
||||||
->configure(
|
->configure(
|
||||||
'--enable-overwrite',
|
'--enable-overwrite',
|
||||||
'--with-curses-h',
|
'--with-curses-h',
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace SPC\builder\unix\library;
|
namespace SPC\builder\unix\library;
|
||||||
|
|
||||||
use SPC\builder\linux\library\LinuxLibraryBase;
|
|
||||||
use SPC\util\executor\UnixAutoconfExecutor;
|
use SPC\util\executor\UnixAutoconfExecutor;
|
||||||
|
use SPC\util\SPCTarget;
|
||||||
|
|
||||||
trait pkgconfig
|
trait pkgconfig
|
||||||
{
|
{
|
||||||
@@ -13,8 +13,8 @@ trait pkgconfig
|
|||||||
{
|
{
|
||||||
UnixAutoconfExecutor::create($this)
|
UnixAutoconfExecutor::create($this)
|
||||||
->appendEnv([
|
->appendEnv([
|
||||||
'CFLAGS' => PHP_OS_FAMILY !== 'Linux' ? '-Wimplicit-function-declaration -Wno-int-conversion' : '',
|
'CFLAGS' => '-Wimplicit-function-declaration -Wno-int-conversion',
|
||||||
'LDFLAGS' => !($this instanceof LinuxLibraryBase) || getenv('SPC_LIBC') === 'glibc' ? '' : '--static',
|
'LDFLAGS' => SPCTarget::isStatic() ? '--static' : '',
|
||||||
])
|
])
|
||||||
->configure(
|
->configure(
|
||||||
'--with-internal-glib',
|
'--with-internal-glib',
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use SPC\builder\linux\library\LinuxLibraryBase;
|
|||||||
use SPC\exception\FileSystemException;
|
use SPC\exception\FileSystemException;
|
||||||
use SPC\exception\RuntimeException;
|
use SPC\exception\RuntimeException;
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
|
use SPC\util\SPCTarget;
|
||||||
|
|
||||||
trait postgresql
|
trait postgresql
|
||||||
{
|
{
|
||||||
@@ -50,7 +51,7 @@ trait postgresql
|
|||||||
$error_exec_cnt += $output[0] === 0 ? 0 : 1;
|
$error_exec_cnt += $output[0] === 0 ? 0 : 1;
|
||||||
if (!empty($output[1][0])) {
|
if (!empty($output[1][0])) {
|
||||||
$ldflags = $output[1][0];
|
$ldflags = $output[1][0];
|
||||||
$envs .= !($this instanceof LinuxLibraryBase) || getenv('SPC_LIBC') === 'glibc' ? " LDFLAGS=\"{$ldflags}\" " : " LDFLAGS=\"{$ldflags} -static\" ";
|
$envs .= SPCTarget::isStatic() ? " LDFLAGS=\"{$ldflags} -static\" " : " LDFLAGS=\"{$ldflags}\" ";
|
||||||
}
|
}
|
||||||
$output = shell()->execWithResult("pkg-config --libs-only-l --static {$packages}");
|
$output = shell()->execWithResult("pkg-config --libs-only-l --static {$packages}");
|
||||||
$error_exec_cnt += $output[0] === 0 ? 0 : 1;
|
$error_exec_cnt += $output[0] === 0 ? 0 : 1;
|
||||||
|
|||||||
28
src/SPC/builder/unix/library/watcher.php
Normal file
28
src/SPC/builder/unix/library/watcher.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\builder\unix\library;
|
||||||
|
|
||||||
|
use SPC\exception\FileSystemException;
|
||||||
|
use SPC\exception\RuntimeException;
|
||||||
|
use SPC\store\FileSystem;
|
||||||
|
|
||||||
|
trait watcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @throws RuntimeException
|
||||||
|
* @throws FileSystemException
|
||||||
|
*/
|
||||||
|
protected function build(): void
|
||||||
|
{
|
||||||
|
shell()->cd($this->source_dir . '/watcher-c')
|
||||||
|
->initializeEnv($this)
|
||||||
|
->exec(getenv('CXX') . ' -c -o libwatcher-c.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra -fPIC')
|
||||||
|
->exec(getenv('AR') . ' rcs libwatcher-c.a libwatcher-c.o');
|
||||||
|
|
||||||
|
copy($this->source_dir . '/watcher-c/libwatcher-c.a', BUILD_LIB_PATH . '/libwatcher-c.a');
|
||||||
|
FileSystem::createDir(BUILD_INCLUDE_PATH . '/wtr');
|
||||||
|
copy($this->source_dir . '/watcher-c/include/wtr/watcher-c.h', BUILD_INCLUDE_PATH . '/wtr/watcher-c.h');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,8 @@ class WindowsBuilder extends BuilderBase
|
|||||||
{
|
{
|
||||||
$this->options = $options;
|
$this->options = $options;
|
||||||
|
|
||||||
GlobalEnvManager::init($this);
|
GlobalEnvManager::init();
|
||||||
|
GlobalEnvManager::afterInit();
|
||||||
|
|
||||||
// ---------- set necessary options ----------
|
// ---------- set necessary options ----------
|
||||||
// set sdk (require visual studio 16 or 17)
|
// set sdk (require visual studio 16 or 17)
|
||||||
|
|||||||
27
src/SPC/builder/windows/library/icu_static_win.php
Normal file
27
src/SPC/builder/windows/library/icu_static_win.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\builder\windows\library;
|
||||||
|
|
||||||
|
use SPC\store\FileSystem;
|
||||||
|
|
||||||
|
class icu_static_win extends WindowsLibraryBase
|
||||||
|
{
|
||||||
|
public const NAME = 'icu-static-win';
|
||||||
|
|
||||||
|
protected function build(): void
|
||||||
|
{
|
||||||
|
copy("{$this->source_dir}\\x64-windows-static\\lib\\icudt.lib", "{$this->getLibDir()}\\icudt.lib");
|
||||||
|
copy("{$this->source_dir}\\x64-windows-static\\lib\\icuin.lib", "{$this->getLibDir()}\\icuin.lib");
|
||||||
|
copy("{$this->source_dir}\\x64-windows-static\\lib\\icuio.lib", "{$this->getLibDir()}\\icuio.lib");
|
||||||
|
copy("{$this->source_dir}\\x64-windows-static\\lib\\icuuc.lib", "{$this->getLibDir()}\\icuuc.lib");
|
||||||
|
|
||||||
|
// create libpq folder in buildroot/includes/libpq
|
||||||
|
if (!file_exists("{$this->getIncludeDir()}\\unicode")) {
|
||||||
|
mkdir("{$this->getIncludeDir()}\\unicode");
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystem::copyDir("{$this->source_dir}\\x64-windows-static\\include\\unicode", "{$this->getIncludeDir()}\\unicode");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -99,6 +99,7 @@ abstract class BaseCommand extends Command
|
|||||||
// init GlobalEnv
|
// init GlobalEnv
|
||||||
if (!$this instanceof BuildCommand) {
|
if (!$this instanceof BuildCommand) {
|
||||||
GlobalEnvManager::init();
|
GlobalEnvManager::init();
|
||||||
|
f_putenv('SPC_SKIP_TOOLCHAIN_CHECK=yes');
|
||||||
}
|
}
|
||||||
if ($this->shouldExecute()) {
|
if ($this->shouldExecute()) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use SPC\store\SourcePatcher;
|
|||||||
use SPC\util\DependencyUtil;
|
use SPC\util\DependencyUtil;
|
||||||
use SPC\util\GlobalEnvManager;
|
use SPC\util\GlobalEnvManager;
|
||||||
use SPC\util\LicenseDumper;
|
use SPC\util\LicenseDumper;
|
||||||
|
use SPC\util\SPCTarget;
|
||||||
use Symfony\Component\Console\Attribute\AsCommand;
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
@@ -32,8 +33,9 @@ class BuildPHPCommand extends BuildCommand
|
|||||||
$this->addOption('build-cli', null, null, 'Build cli SAPI');
|
$this->addOption('build-cli', null, null, 'Build cli SAPI');
|
||||||
$this->addOption('build-fpm', null, null, 'Build fpm SAPI (not available on Windows)');
|
$this->addOption('build-fpm', null, null, 'Build fpm SAPI (not available on Windows)');
|
||||||
$this->addOption('build-embed', null, null, 'Build embed SAPI (not available on Windows)');
|
$this->addOption('build-embed', null, null, 'Build embed SAPI (not available on Windows)');
|
||||||
|
$this->addOption('build-frankenphp', null, null, 'Build FrankenPHP SAPI (not available on Windows)');
|
||||||
$this->addOption('build-all', null, null, 'Build all SAPI');
|
$this->addOption('build-all', null, null, 'Build all SAPI');
|
||||||
$this->addOption('no-strip', null, null, 'build without strip, in order to debug and load external extensions');
|
$this->addOption('no-strip', null, null, 'build without strip, keep symbols to debug');
|
||||||
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');
|
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');
|
||||||
$this->addOption('with-config-file-path', null, InputOption::VALUE_REQUIRED, 'Set the path in which to look for php.ini', $isWindows ? null : '/usr/local/etc/php');
|
$this->addOption('with-config-file-path', null, InputOption::VALUE_REQUIRED, 'Set the path in which to look for php.ini', $isWindows ? null : '/usr/local/etc/php');
|
||||||
$this->addOption('with-config-file-scan-dir', null, InputOption::VALUE_REQUIRED, 'Set the directory to scan for .ini files after reading php.ini', $isWindows ? null : '/usr/local/etc/php/conf.d');
|
$this->addOption('with-config-file-scan-dir', null, InputOption::VALUE_REQUIRED, 'Set the directory to scan for .ini files after reading php.ini', $isWindows ? null : '/usr/local/etc/php/conf.d');
|
||||||
@@ -61,14 +63,9 @@ class BuildPHPCommand extends BuildCommand
|
|||||||
$rule = $this->parseRules($shared_extensions);
|
$rule = $this->parseRules($shared_extensions);
|
||||||
|
|
||||||
// check dynamic extension build env
|
// check dynamic extension build env
|
||||||
// macOS must use --no-strip option
|
|
||||||
if (!empty($shared_extensions) && PHP_OS_FAMILY === 'Darwin' && !$this->getOption('no-strip')) {
|
|
||||||
$this->output->writeln('MacOS does not support dynamic extension loading with stripped binary, please use --no-strip option!');
|
|
||||||
return static::FAILURE;
|
|
||||||
}
|
|
||||||
// linux must build with glibc
|
// linux must build with glibc
|
||||||
if (!empty($shared_extensions) && PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') !== 'glibc') {
|
if (!empty($shared_extensions) && SPCTarget::isStatic()) {
|
||||||
$this->output->writeln('Linux does not support dynamic extension loading with musl-libc full-static build, please build with glibc!');
|
$this->output->writeln('Linux does not support dynamic extension loading with fully static builds, please build with a shared C runtime target!');
|
||||||
return static::FAILURE;
|
return static::FAILURE;
|
||||||
}
|
}
|
||||||
$static_and_shared = array_intersect($static_extensions, $shared_extensions);
|
$static_and_shared = array_intersect($static_extensions, $shared_extensions);
|
||||||
@@ -78,11 +75,12 @@ class BuildPHPCommand extends BuildCommand
|
|||||||
|
|
||||||
if ($rule === BUILD_TARGET_NONE) {
|
if ($rule === BUILD_TARGET_NONE) {
|
||||||
$this->output->writeln('<error>Please add at least one build SAPI!</error>');
|
$this->output->writeln('<error>Please add at least one build SAPI!</error>');
|
||||||
$this->output->writeln("<comment>\t--build-cli\tBuild php-cli SAPI</comment>");
|
$this->output->writeln("<comment>\t--build-cli\t\tBuild php-cli SAPI</comment>");
|
||||||
$this->output->writeln("<comment>\t--build-micro\tBuild phpmicro SAPI</comment>");
|
$this->output->writeln("<comment>\t--build-micro\t\tBuild phpmicro SAPI</comment>");
|
||||||
$this->output->writeln("<comment>\t--build-fpm\tBuild php-fpm SAPI</comment>");
|
$this->output->writeln("<comment>\t--build-fpm\t\tBuild php-fpm SAPI</comment>");
|
||||||
$this->output->writeln("<comment>\t--build-embed\tBuild embed SAPI/libphp</comment>");
|
$this->output->writeln("<comment>\t--build-embed\t\tBuild embed SAPI/libphp</comment>");
|
||||||
$this->output->writeln("<comment>\t--build-all\tBuild all SAPI: cli, micro, fpm, embed</comment>");
|
$this->output->writeln("<comment>\t--build-frankenphp\tBuild FrankenPHP SAPI/libphp</comment>");
|
||||||
|
$this->output->writeln("<comment>\t--build-all\t\tBuild all SAPI: cli, micro, fpm, embed, frankenphp</comment>");
|
||||||
return static::FAILURE;
|
return static::FAILURE;
|
||||||
}
|
}
|
||||||
if ($rule === BUILD_TARGET_ALL) {
|
if ($rule === BUILD_TARGET_ALL) {
|
||||||
@@ -136,6 +134,8 @@ class BuildPHPCommand extends BuildCommand
|
|||||||
// print info
|
// print info
|
||||||
$indent_texts = [
|
$indent_texts = [
|
||||||
'Build OS' => PHP_OS_FAMILY . ' (' . php_uname('m') . ')',
|
'Build OS' => PHP_OS_FAMILY . ' (' . php_uname('m') . ')',
|
||||||
|
'Build Target' => getenv('SPC_TARGET'),
|
||||||
|
'Build Toolchain' => getenv('SPC_TOOLCHAIN'),
|
||||||
'Build SAPI' => $builder->getBuildTypeName($rule),
|
'Build SAPI' => $builder->getBuildTypeName($rule),
|
||||||
'Static Extensions (' . count($static_extensions) . ')' => implode(',', $static_extensions),
|
'Static Extensions (' . count($static_extensions) . ')' => implode(',', $static_extensions),
|
||||||
'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions),
|
'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions),
|
||||||
@@ -158,14 +158,9 @@ class BuildPHPCommand extends BuildCommand
|
|||||||
if ($this->input->getOption('with-upx-pack') && in_array(PHP_OS_FAMILY, ['Linux', 'Windows'])) {
|
if ($this->input->getOption('with-upx-pack') && in_array(PHP_OS_FAMILY, ['Linux', 'Windows'])) {
|
||||||
$indent_texts['UPX Pack'] = 'enabled';
|
$indent_texts['UPX Pack'] = 'enabled';
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
$ver = $builder->getPHPVersion();
|
$ver = $builder->getPHPVersionFromArchive() ?: $builder->getPHPVersion();
|
||||||
$indent_texts['PHP Version'] = $ver;
|
$indent_texts['PHP Version'] = $ver;
|
||||||
} catch (\Throwable) {
|
|
||||||
if (($ver = $builder->getPHPVersionFromArchive()) !== false) {
|
|
||||||
$indent_texts['PHP Version'] = $ver;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($not_included)) {
|
if (!empty($not_included)) {
|
||||||
$indent_texts['Extra Exts (' . count($not_included) . ')'] = implode(', ', $not_included);
|
$indent_texts['Extra Exts (' . count($not_included) . ')'] = implode(', ', $not_included);
|
||||||
@@ -183,6 +178,9 @@ class BuildPHPCommand extends BuildCommand
|
|||||||
// validate libs and extensions
|
// validate libs and extensions
|
||||||
$builder->validateLibsAndExts();
|
$builder->validateLibsAndExts();
|
||||||
|
|
||||||
|
// check some things before building all the things
|
||||||
|
$builder->checkBeforeBuildPHP($rule);
|
||||||
|
|
||||||
// clean builds and sources
|
// clean builds and sources
|
||||||
if ($this->input->getOption('with-clean')) {
|
if ($this->input->getOption('with-clean')) {
|
||||||
logger()->info('Cleaning source and previous build dir...');
|
logger()->info('Cleaning source and previous build dir...');
|
||||||
@@ -207,11 +205,11 @@ class BuildPHPCommand extends BuildCommand
|
|||||||
// add static-php-cli.version to main.c, in order to debug php failure more easily
|
// add static-php-cli.version to main.c, in order to debug php failure more easily
|
||||||
SourcePatcher::patchSPCVersionToPHP($this->getApplication()->getVersion());
|
SourcePatcher::patchSPCVersionToPHP($this->getApplication()->getVersion());
|
||||||
|
|
||||||
|
// clean old modules that may conflict with the new php build
|
||||||
|
FileSystem::removeDir(BUILD_MODULES_PATH);
|
||||||
// start to build
|
// start to build
|
||||||
$builder->buildPHP($rule);
|
$builder->buildPHP($rule);
|
||||||
|
|
||||||
SourcePatcher::patchBeforeSharedBuild($builder);
|
|
||||||
|
|
||||||
// build dynamic extensions if needed
|
// build dynamic extensions if needed
|
||||||
if (!empty($shared_extensions)) {
|
if (!empty($shared_extensions)) {
|
||||||
logger()->info('Building shared extensions ...');
|
logger()->info('Building shared extensions ...');
|
||||||
@@ -292,7 +290,8 @@ class BuildPHPCommand extends BuildCommand
|
|||||||
$rule |= ($this->getOption('build-cli') ? BUILD_TARGET_CLI : BUILD_TARGET_NONE);
|
$rule |= ($this->getOption('build-cli') ? BUILD_TARGET_CLI : BUILD_TARGET_NONE);
|
||||||
$rule |= ($this->getOption('build-micro') ? BUILD_TARGET_MICRO : BUILD_TARGET_NONE);
|
$rule |= ($this->getOption('build-micro') ? BUILD_TARGET_MICRO : BUILD_TARGET_NONE);
|
||||||
$rule |= ($this->getOption('build-fpm') ? BUILD_TARGET_FPM : BUILD_TARGET_NONE);
|
$rule |= ($this->getOption('build-fpm') ? BUILD_TARGET_FPM : BUILD_TARGET_NONE);
|
||||||
$rule |= ($this->getOption('build-embed') || !empty($shared_extensions) ? BUILD_TARGET_EMBED : BUILD_TARGET_NONE);
|
$rule |= $this->getOption('build-embed') || !empty($shared_extensions) ? BUILD_TARGET_EMBED : BUILD_TARGET_NONE;
|
||||||
|
$rule |= ($this->getOption('build-frankenphp') ? (BUILD_TARGET_FRANKENPHP | BUILD_TARGET_EMBED) : BUILD_TARGET_NONE);
|
||||||
$rule |= ($this->getOption('build-all') ? BUILD_TARGET_ALL : BUILD_TARGET_NONE);
|
$rule |= ($this->getOption('build-all') ? BUILD_TARGET_ALL : BUILD_TARGET_NONE);
|
||||||
return $rule;
|
return $rule;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ declare(strict_types=1);
|
|||||||
namespace SPC\command;
|
namespace SPC\command;
|
||||||
|
|
||||||
use SPC\exception\ValidationException;
|
use SPC\exception\ValidationException;
|
||||||
|
use SPC\toolchain\ToolchainManager;
|
||||||
|
use SPC\toolchain\ZigToolchain;
|
||||||
use SPC\util\ConfigValidator;
|
use SPC\util\ConfigValidator;
|
||||||
use Symfony\Component\Console\Attribute\AsCommand;
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Process\Process;
|
use Symfony\Component\Process\Process;
|
||||||
|
|
||||||
#[AsCommand('craft', 'Build static-php from craft.yml')]
|
#[AsCommand('craft', 'Build static-php from craft.yml')]
|
||||||
class CraftCommand extends BaseCommand
|
class CraftCommand extends BuildCommand
|
||||||
{
|
{
|
||||||
public function configure(): void
|
public function configure(): void
|
||||||
{
|
{
|
||||||
@@ -49,7 +51,7 @@ class CraftCommand extends BaseCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
$static_extensions = implode(',', $craft['extensions']);
|
$static_extensions = implode(',', $craft['extensions']);
|
||||||
$shared_extensions = implode(',', $craft['shared-extensions']);
|
$shared_extensions = implode(',', $craft['shared-extensions'] ?? []);
|
||||||
$libs = implode(',', $craft['libs']);
|
$libs = implode(',', $craft['libs']);
|
||||||
|
|
||||||
// init log
|
// init log
|
||||||
@@ -66,6 +68,24 @@ class CraftCommand extends BaseCommand
|
|||||||
return static::FAILURE;
|
return static::FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// install go and xcaddy for frankenphp
|
||||||
|
if (in_array('frankenphp', $craft['sapi'])) {
|
||||||
|
$retcode = $this->runCommand('install-pkg', 'go-xcaddy');
|
||||||
|
if ($retcode !== 0) {
|
||||||
|
$this->output->writeln('<error>craft go-xcaddy failed</error>');
|
||||||
|
$this->log("craft: spc install-pkg go-xcaddy failed with code: {$retcode}", true);
|
||||||
|
return static::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// install zig if requested
|
||||||
|
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
|
||||||
|
$retcode = $this->runCommand('install-pkg', 'zig');
|
||||||
|
if ($retcode !== 0) {
|
||||||
|
$this->output->writeln('<error>craft zig failed</error>');
|
||||||
|
$this->log("craft: spc install-pkg zig failed with code: {$retcode}", true);
|
||||||
|
return static::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
// craft download
|
// craft download
|
||||||
if ($craft['craft-options']['download']) {
|
if ($craft['craft-options']['download']) {
|
||||||
$sharedAppend = $shared_extensions ? ',' . $shared_extensions : '';
|
$sharedAppend = $shared_extensions ? ',' . $shared_extensions : '';
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use SPC\exception\FileSystemException;
|
|||||||
use SPC\exception\WrongUsageException;
|
use SPC\exception\WrongUsageException;
|
||||||
use SPC\store\Downloader;
|
use SPC\store\Downloader;
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
|
use SPC\store\LockFile;
|
||||||
use Symfony\Component\Console\Attribute\AsCommand;
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
@@ -21,6 +22,8 @@ class DeleteDownloadCommand extends BaseCommand
|
|||||||
{
|
{
|
||||||
$this->addArgument('sources', InputArgument::REQUIRED, 'The sources/packages will be deleted, comma separated');
|
$this->addArgument('sources', InputArgument::REQUIRED, 'The sources/packages will be deleted, comma separated');
|
||||||
$this->addOption('all', 'A', null, 'Delete all downloaded and locked sources/packages');
|
$this->addOption('all', 'A', null, 'Delete all downloaded and locked sources/packages');
|
||||||
|
$this->addOption('pre-built-only', 'W', null, 'Delete only pre-built sources/packages, not the original ones');
|
||||||
|
$this->addOption('source-only', 'S', null, 'Delete only sources, not the pre-built packages');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function initialize(InputInterface $input, OutputInterface $output): void
|
public function initialize(InputInterface $input, OutputInterface $output): void
|
||||||
@@ -46,29 +49,30 @@ class DeleteDownloadCommand extends BaseCommand
|
|||||||
return static::SUCCESS;
|
return static::SUCCESS;
|
||||||
}
|
}
|
||||||
$chosen_sources = $sources;
|
$chosen_sources = $sources;
|
||||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
|
|
||||||
|
|
||||||
$deleted_sources = [];
|
$deleted_sources = [];
|
||||||
foreach ($chosen_sources as $source) {
|
foreach ($chosen_sources as $source) {
|
||||||
$source = trim($source);
|
$source = trim($source);
|
||||||
foreach ([$source, Downloader::getPreBuiltLockName($source)] as $name) {
|
if (LockFile::get($source) && !$this->getOption('pre-built-only')) {
|
||||||
if (isset($lock[$name])) {
|
$deleted_sources[] = $source;
|
||||||
$deleted_sources[] = $name;
|
}
|
||||||
}
|
if (LockFile::get(Downloader::getPreBuiltLockName($source)) && !$this->getOption('source-only')) {
|
||||||
|
$deleted_sources[] = Downloader::getPreBuiltLockName($source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($deleted_sources as $lock_name) {
|
foreach ($deleted_sources as $lock_name) {
|
||||||
|
$lock = LockFile::get($lock_name);
|
||||||
// remove download file/dir if exists
|
// remove download file/dir if exists
|
||||||
if ($lock[$lock_name]['source_type'] === 'archive') {
|
if ($lock['source_type'] === SPC_SOURCE_ARCHIVE) {
|
||||||
if (file_exists($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock[$lock_name]['filename']))) {
|
if (file_exists($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock['filename']))) {
|
||||||
logger()->info('Deleting file ' . $path);
|
logger()->info('Deleting file ' . $path);
|
||||||
unlink($path);
|
unlink($path);
|
||||||
} else {
|
} else {
|
||||||
logger()->warning("Source/Package [{$lock_name}] file not found, skip deleting file.");
|
logger()->warning("Source/Package [{$lock_name}] file not found, skip deleting file.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (is_dir($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock[$lock_name]['dirname']))) {
|
if (is_dir($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock['dirname']))) {
|
||||||
logger()->info('Deleting dir ' . $path);
|
logger()->info('Deleting dir ' . $path);
|
||||||
FileSystem::removeDir($path);
|
FileSystem::removeDir($path);
|
||||||
} else {
|
} else {
|
||||||
@@ -76,9 +80,8 @@ class DeleteDownloadCommand extends BaseCommand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// remove locked sources
|
// remove locked sources
|
||||||
unset($lock[$lock_name]);
|
LockFile::put($lock_name, null);
|
||||||
}
|
}
|
||||||
FileSystem::writeFile(DOWNLOAD_PATH . '/.lock.json', json_encode($lock, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
|
||||||
logger()->info('Delete success!');
|
logger()->info('Delete success!');
|
||||||
return static::SUCCESS;
|
return static::SUCCESS;
|
||||||
} catch (DownloaderException $e) {
|
} catch (DownloaderException $e) {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace SPC\command;
|
namespace SPC\command;
|
||||||
|
|
||||||
use SPC\builder\linux\SystemUtil;
|
|
||||||
use SPC\builder\traits\UnixSystemUtilTrait;
|
use SPC\builder\traits\UnixSystemUtilTrait;
|
||||||
use SPC\exception\DownloaderException;
|
use SPC\exception\DownloaderException;
|
||||||
use SPC\exception\FileSystemException;
|
use SPC\exception\FileSystemException;
|
||||||
@@ -12,7 +11,9 @@ use SPC\exception\RuntimeException;
|
|||||||
use SPC\exception\WrongUsageException;
|
use SPC\exception\WrongUsageException;
|
||||||
use SPC\store\Config;
|
use SPC\store\Config;
|
||||||
use SPC\store\Downloader;
|
use SPC\store\Downloader;
|
||||||
|
use SPC\store\LockFile;
|
||||||
use SPC\util\DependencyUtil;
|
use SPC\util\DependencyUtil;
|
||||||
|
use SPC\util\SPCTarget;
|
||||||
use Symfony\Component\Console\Attribute\AsCommand;
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
@@ -222,8 +223,8 @@ class DownloadCommand extends BaseCommand
|
|||||||
'{name}' => $source,
|
'{name}' => $source,
|
||||||
'{arch}' => arch2gnu(php_uname('m')),
|
'{arch}' => arch2gnu(php_uname('m')),
|
||||||
'{os}' => strtolower(PHP_OS_FAMILY),
|
'{os}' => strtolower(PHP_OS_FAMILY),
|
||||||
'{libc}' => getenv('SPC_LIBC') ?: 'default',
|
'{libc}' => SPCTarget::getLibc() ?? 'default',
|
||||||
'{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default',
|
'{libcver}' => SPCTarget::getLibcVersion() ?? 'default',
|
||||||
];
|
];
|
||||||
$find = str_replace(array_keys($replace), array_values($replace), Config::getPreBuilt('match-pattern'));
|
$find = str_replace(array_keys($replace), array_values($replace), Config::getPreBuilt('match-pattern'));
|
||||||
// find filename in asset list
|
// find filename in asset list
|
||||||
@@ -301,7 +302,7 @@ class DownloadCommand extends BaseCommand
|
|||||||
throw new WrongUsageException('Windows currently does not support --from-zip !');
|
throw new WrongUsageException('Windows currently does not support --from-zip !');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
|
if (!file_exists(LockFile::LOCK_FILE)) {
|
||||||
throw new RuntimeException('.lock.json not exist in "downloads/"');
|
throw new RuntimeException('.lock.json not exist in "downloads/"');
|
||||||
}
|
}
|
||||||
} catch (RuntimeException $e) {
|
} catch (RuntimeException $e) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use Symfony\Component\Console\Input\InputArgument;
|
|||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
|
||||||
#[AsCommand('spc-config', 'Build dependencies')]
|
#[AsCommand('spc-config', 'Build dependencies')]
|
||||||
class SPCConfigCommand extends BuildCommand
|
class SPCConfigCommand extends BaseCommand
|
||||||
{
|
{
|
||||||
protected bool $no_motd = true;
|
protected bool $no_motd = true;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace SPC\command;
|
|||||||
use SPC\store\Config;
|
use SPC\store\Config;
|
||||||
use SPC\store\Downloader;
|
use SPC\store\Downloader;
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
|
use SPC\store\LockFile;
|
||||||
use Symfony\Component\Console\Attribute\AsCommand;
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
@@ -40,16 +41,9 @@ class SwitchPhpVersionCommand extends BaseCommand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// detect if downloads/.lock.json exists
|
if (LockFile::isLockFileExists('php-src')) {
|
||||||
$lock_file = DOWNLOAD_PATH . '/.lock.json';
|
|
||||||
// parse php-src part of lock file
|
|
||||||
$lock_data = json_decode(file_get_contents($lock_file), true);
|
|
||||||
// get php-src downloaded file name
|
|
||||||
$php_src = $lock_data['php-src'];
|
|
||||||
$file = DOWNLOAD_PATH . '/' . ($php_src['filename'] ?? '.donot.delete.me');
|
|
||||||
if (file_exists($file)) {
|
|
||||||
$this->output->writeln('<info>Removing old PHP source...</info>');
|
$this->output->writeln('<info>Removing old PHP source...</info>');
|
||||||
unlink($file);
|
LockFile::put('php-src', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download new PHP source
|
// Download new PHP source
|
||||||
|
|||||||
37
src/SPC/command/dev/EnvCommand.php
Normal file
37
src/SPC/command/dev/EnvCommand.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\command\dev;
|
||||||
|
|
||||||
|
use SPC\command\BaseCommand;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
#[AsCommand('dev:env', 'Returns the internally defined environment variables')]
|
||||||
|
class EnvCommand extends BaseCommand
|
||||||
|
{
|
||||||
|
public function configure()
|
||||||
|
{
|
||||||
|
$this->addArgument('env', InputArgument::REQUIRED, 'The environment variable to show, if not set, all will be shown');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initialize(InputInterface $input, OutputInterface $output): void
|
||||||
|
{
|
||||||
|
$this->no_motd = true;
|
||||||
|
parent::initialize($input, $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(): int
|
||||||
|
{
|
||||||
|
$env = $this->getArgument('env');
|
||||||
|
if (($val = getenv($env)) === false) {
|
||||||
|
$this->output->writeln("<error>Environment variable '{$env}' is not set.</error>");
|
||||||
|
return static::FAILURE;
|
||||||
|
}
|
||||||
|
$this->output->writeln("<info>{$val}</info>");
|
||||||
|
return static::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@ namespace SPC\command\dev;
|
|||||||
|
|
||||||
use SPC\builder\BuilderProvider;
|
use SPC\builder\BuilderProvider;
|
||||||
use SPC\builder\LibraryBase;
|
use SPC\builder\LibraryBase;
|
||||||
use SPC\builder\linux\SystemUtil;
|
|
||||||
use SPC\command\BuildCommand;
|
use SPC\command\BuildCommand;
|
||||||
use SPC\exception\ExceptionHandler;
|
use SPC\exception\ExceptionHandler;
|
||||||
use SPC\exception\FileSystemException;
|
use SPC\exception\FileSystemException;
|
||||||
@@ -14,7 +13,9 @@ use SPC\exception\RuntimeException;
|
|||||||
use SPC\exception\WrongUsageException;
|
use SPC\exception\WrongUsageException;
|
||||||
use SPC\store\Config;
|
use SPC\store\Config;
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
|
use SPC\store\LockFile;
|
||||||
use SPC\util\DependencyUtil;
|
use SPC\util\DependencyUtil;
|
||||||
|
use SPC\util\SPCTarget;
|
||||||
use Symfony\Component\Console\Attribute\AsCommand;
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
|
||||||
@@ -47,9 +48,8 @@ class PackLibCommand extends BuildCommand
|
|||||||
$lib->setup();
|
$lib->setup();
|
||||||
} else {
|
} else {
|
||||||
// Get lock info
|
// Get lock info
|
||||||
$lock = json_decode(file_get_contents(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
|
|
||||||
$source = Config::getLib($lib->getName(), 'source');
|
$source = Config::getLib($lib->getName(), 'source');
|
||||||
if (!isset($lock[$source]) || ($lock[$source]['lock_as'] ?? SPC_DOWNLOAD_SOURCE) === SPC_DOWNLOAD_PRE_BUILT) {
|
if (($lock = LockFile::get($source)) === null || ($lock['lock_as'] === SPC_DOWNLOAD_PRE_BUILT)) {
|
||||||
logger()->critical("The library {$lib->getName()} is downloaded as pre-built, we need to build it instead of installing pre-built.");
|
logger()->critical("The library {$lib->getName()} is downloaded as pre-built, we need to build it instead of installing pre-built.");
|
||||||
return static::FAILURE;
|
return static::FAILURE;
|
||||||
}
|
}
|
||||||
@@ -76,8 +76,8 @@ class PackLibCommand extends BuildCommand
|
|||||||
'{name}' => $lib->getName(),
|
'{name}' => $lib->getName(),
|
||||||
'{arch}' => arch2gnu(php_uname('m')),
|
'{arch}' => arch2gnu(php_uname('m')),
|
||||||
'{os}' => strtolower(PHP_OS_FAMILY),
|
'{os}' => strtolower(PHP_OS_FAMILY),
|
||||||
'{libc}' => getenv('SPC_LIBC') ?: 'default',
|
'{libc}' => SPCTarget::getLibc() ?? 'default',
|
||||||
'{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default',
|
'{libcver}' => SPCTarget::getLibcVersion() ?? 'default',
|
||||||
];
|
];
|
||||||
// detect suffix, for proper tar option
|
// detect suffix, for proper tar option
|
||||||
$tar_option = $this->getTarOptionFromSuffix(Config::getPreBuilt('match-pattern'));
|
$tar_option = $this->getTarOptionFromSuffix(Config::getPreBuilt('match-pattern'));
|
||||||
|
|||||||
@@ -72,6 +72,14 @@ final class CheckListHandler
|
|||||||
{
|
{
|
||||||
foreach (FileSystem::getClassesPsr4(__DIR__ . '/item', 'SPC\doctor\item') as $class) {
|
foreach (FileSystem::getClassesPsr4(__DIR__ . '/item', 'SPC\doctor\item') as $class) {
|
||||||
$ref = new \ReflectionClass($class);
|
$ref = new \ReflectionClass($class);
|
||||||
|
$optional = $ref->getAttributes(OptionalCheck::class)[0] ?? null;
|
||||||
|
if ($optional !== null) {
|
||||||
|
/** @var OptionalCheck $instance */
|
||||||
|
$instance = $optional->newInstance();
|
||||||
|
if (is_callable($instance->check) && !call_user_func($instance->check)) {
|
||||||
|
continue; // skip this class if optional check is false
|
||||||
|
}
|
||||||
|
}
|
||||||
foreach ($ref->getMethods() as $method) {
|
foreach ($ref->getMethods() as $method) {
|
||||||
foreach ($method->getAttributes() as $a) {
|
foreach ($method->getAttributes() as $a) {
|
||||||
if (is_a($a->getName(), AsCheckItem::class, true)) {
|
if (is_a($a->getName(), AsCheckItem::class, true)) {
|
||||||
|
|||||||
11
src/SPC/doctor/OptionalCheck.php
Normal file
11
src/SPC/doctor/OptionalCheck.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\doctor;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
|
||||||
|
class OptionalCheck
|
||||||
|
{
|
||||||
|
public function __construct(public array $check) {}
|
||||||
|
}
|
||||||
@@ -4,10 +4,10 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace SPC\doctor\item;
|
namespace SPC\doctor\item;
|
||||||
|
|
||||||
use SPC\builder\linux\SystemUtil;
|
|
||||||
use SPC\doctor\AsCheckItem;
|
use SPC\doctor\AsCheckItem;
|
||||||
use SPC\doctor\AsFixItem;
|
use SPC\doctor\AsFixItem;
|
||||||
use SPC\doctor\CheckResult;
|
use SPC\doctor\CheckResult;
|
||||||
|
use SPC\doctor\OptionalCheck;
|
||||||
use SPC\exception\DownloaderException;
|
use SPC\exception\DownloaderException;
|
||||||
use SPC\exception\FileSystemException;
|
use SPC\exception\FileSystemException;
|
||||||
use SPC\exception\RuntimeException;
|
use SPC\exception\RuntimeException;
|
||||||
@@ -16,20 +16,20 @@ use SPC\store\Downloader;
|
|||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
use SPC\store\PackageManager;
|
use SPC\store\PackageManager;
|
||||||
use SPC\store\SourcePatcher;
|
use SPC\store\SourcePatcher;
|
||||||
|
use SPC\toolchain\MuslToolchain;
|
||||||
|
|
||||||
|
#[OptionalCheck([self::class, 'optionalCheck'])]
|
||||||
class LinuxMuslCheck
|
class LinuxMuslCheck
|
||||||
{
|
{
|
||||||
|
public static function optionalCheck(): bool
|
||||||
|
{
|
||||||
|
return getenv('SPC_TOOLCHAIN') === MuslToolchain::class;
|
||||||
|
}
|
||||||
|
|
||||||
/** @noinspection PhpUnused */
|
/** @noinspection PhpUnused */
|
||||||
#[AsCheckItem('if musl-wrapper is installed', limit_os: 'Linux', level: 800)]
|
#[AsCheckItem('if musl-wrapper is installed', limit_os: 'Linux', level: 800)]
|
||||||
public function checkMusl(): CheckResult
|
public function checkMusl(): CheckResult
|
||||||
{
|
{
|
||||||
if (SystemUtil::isMuslDist()) {
|
|
||||||
return CheckResult::ok('musl-based distro, skipped');
|
|
||||||
}
|
|
||||||
if (getenv('SPC_LIBC') === 'glibc') {
|
|
||||||
return CheckResult::ok('Building with glibc, skipped');
|
|
||||||
}
|
|
||||||
|
|
||||||
$musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m'));
|
$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')) {
|
if (file_exists($musl_wrapper_lib) && file_exists('/usr/local/musl/lib/libc.a')) {
|
||||||
return CheckResult::ok();
|
return CheckResult::ok();
|
||||||
@@ -40,13 +40,6 @@ class LinuxMuslCheck
|
|||||||
#[AsCheckItem('if musl-cross-make is installed', limit_os: 'Linux', level: 799)]
|
#[AsCheckItem('if musl-cross-make is installed', limit_os: 'Linux', level: 799)]
|
||||||
public function checkMuslCrossMake(): CheckResult
|
public function checkMuslCrossMake(): CheckResult
|
||||||
{
|
{
|
||||||
if (SystemUtil::isMuslDist()) {
|
|
||||||
return CheckResult::ok('musl-based distro, skipped');
|
|
||||||
}
|
|
||||||
if (getenv('SPC_LIBC') === 'glibc') {
|
|
||||||
return CheckResult::ok('Building with glibc, skipped');
|
|
||||||
}
|
|
||||||
|
|
||||||
$arch = arch2gnu(php_uname('m'));
|
$arch = arch2gnu(php_uname('m'));
|
||||||
$cross_compile_lib = "/usr/local/musl/{$arch}-linux-musl/lib/libc.a";
|
$cross_compile_lib = "/usr/local/musl/{$arch}-linux-musl/lib/libc.a";
|
||||||
$cross_compile_gcc = "/usr/local/musl/bin/{$arch}-linux-musl-gcc";
|
$cross_compile_gcc = "/usr/local/musl/bin/{$arch}-linux-musl-gcc";
|
||||||
@@ -78,7 +71,7 @@ class LinuxMuslCheck
|
|||||||
];
|
];
|
||||||
logger()->info('Downloading ' . $musl_source['url']);
|
logger()->info('Downloading ' . $musl_source['url']);
|
||||||
Downloader::downloadSource($musl_version_name, $musl_source);
|
Downloader::downloadSource($musl_version_name, $musl_source);
|
||||||
FileSystem::extractSource($musl_version_name, DOWNLOAD_PATH . "/{$musl_version_name}.tar.gz");
|
FileSystem::extractSource($musl_version_name, SPC_SOURCE_ARCHIVE, DOWNLOAD_PATH . "/{$musl_version_name}.tar.gz");
|
||||||
|
|
||||||
// Apply CVE-2025-26519 patch
|
// Apply CVE-2025-26519 patch
|
||||||
SourcePatcher::patchFile('musl-1.2.5_CVE-2025-26519_0001.patch', SOURCE_PATH . "/{$musl_version_name}");
|
SourcePatcher::patchFile('musl-1.2.5_CVE-2025-26519_0001.patch', SOURCE_PATH . "/{$musl_version_name}");
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class LinuxToolCheckList
|
|||||||
'bzip2', 'cmake', 'gcc',
|
'bzip2', 'cmake', 'gcc',
|
||||||
'g++', 'patch', 'binutils-gold',
|
'g++', 'patch', 'binutils-gold',
|
||||||
'libtoolize', 'which',
|
'libtoolize', 'which',
|
||||||
|
'patchelf',
|
||||||
];
|
];
|
||||||
|
|
||||||
public const TOOLS_DEBIAN = [
|
public const TOOLS_DEBIAN = [
|
||||||
@@ -30,6 +31,7 @@ class LinuxToolCheckList
|
|||||||
'tar', 'unzip', 'gzip',
|
'tar', 'unzip', 'gzip',
|
||||||
'bzip2', 'cmake', 'patch',
|
'bzip2', 'cmake', 'patch',
|
||||||
'xz', 'libtoolize', 'which',
|
'xz', 'libtoolize', 'which',
|
||||||
|
'patchelf',
|
||||||
];
|
];
|
||||||
|
|
||||||
public const TOOLS_RHEL = [
|
public const TOOLS_RHEL = [
|
||||||
@@ -38,6 +40,7 @@ class LinuxToolCheckList
|
|||||||
'tar', 'unzip', 'gzip', 'gcc',
|
'tar', 'unzip', 'gzip', 'gcc',
|
||||||
'bzip2', 'cmake', 'patch', 'which',
|
'bzip2', 'cmake', 'patch', 'which',
|
||||||
'xz', 'libtool', 'gettext-devel',
|
'xz', 'libtool', 'gettext-devel',
|
||||||
|
'perl', 'patchelf',
|
||||||
];
|
];
|
||||||
|
|
||||||
public const TOOLS_ARCH = [
|
public const TOOLS_ARCH = [
|
||||||
@@ -48,6 +51,7 @@ class LinuxToolCheckList
|
|||||||
'binutils-gold' => 'ld.gold',
|
'binutils-gold' => 'ld.gold',
|
||||||
'base-devel' => 'automake',
|
'base-devel' => 'automake',
|
||||||
'gettext-devel' => 'gettextize',
|
'gettext-devel' => 'gettextize',
|
||||||
|
'gettext-dev' => 'gettextize',
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @noinspection PhpUnused */
|
/** @noinspection PhpUnused */
|
||||||
|
|||||||
53
src/SPC/doctor/item/ZigCheck.php
Normal file
53
src/SPC/doctor/item/ZigCheck.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\doctor\item;
|
||||||
|
|
||||||
|
use SPC\doctor\AsCheckItem;
|
||||||
|
use SPC\doctor\AsFixItem;
|
||||||
|
use SPC\doctor\CheckResult;
|
||||||
|
use SPC\doctor\OptionalCheck;
|
||||||
|
use SPC\exception\FileSystemException;
|
||||||
|
use SPC\exception\WrongUsageException;
|
||||||
|
use SPC\store\PackageManager;
|
||||||
|
use SPC\store\pkg\Zig;
|
||||||
|
use SPC\toolchain\ZigToolchain;
|
||||||
|
|
||||||
|
#[OptionalCheck([self::class, 'optionalCheck'])]
|
||||||
|
class ZigCheck
|
||||||
|
{
|
||||||
|
public static function optionalCheck(): bool
|
||||||
|
{
|
||||||
|
return getenv('SPC_TOOLCHAIN') === ZigToolchain::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @noinspection PhpUnused */
|
||||||
|
#[AsCheckItem('if zig is installed', level: 800)]
|
||||||
|
public function checkZig(): CheckResult
|
||||||
|
{
|
||||||
|
if (Zig::isInstalled()) {
|
||||||
|
return CheckResult::ok();
|
||||||
|
}
|
||||||
|
return CheckResult::fail('zig is not installed', 'install-zig');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @noinspection PhpUnused */
|
||||||
|
/**
|
||||||
|
* @throws FileSystemException
|
||||||
|
* @throws WrongUsageException
|
||||||
|
*/
|
||||||
|
#[AsFixItem('install-zig')]
|
||||||
|
public function fixMuslCrossMake(): bool
|
||||||
|
{
|
||||||
|
$arch = arch2gnu(php_uname('m'));
|
||||||
|
$os = match (PHP_OS_FAMILY) {
|
||||||
|
'Windows' => 'win',
|
||||||
|
'Darwin' => 'macos',
|
||||||
|
'BSD' => 'freebsd',
|
||||||
|
default => 'linux',
|
||||||
|
};
|
||||||
|
PackageManager::installPackage("zig-{$arch}-{$os}");
|
||||||
|
return Zig::isInstalled();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,12 +4,13 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace SPC\store;
|
namespace SPC\store;
|
||||||
|
|
||||||
use SPC\builder\linux\SystemUtil;
|
|
||||||
use SPC\exception\DownloaderException;
|
use SPC\exception\DownloaderException;
|
||||||
use SPC\exception\FileSystemException;
|
use SPC\exception\FileSystemException;
|
||||||
use SPC\exception\RuntimeException;
|
use SPC\exception\RuntimeException;
|
||||||
use SPC\exception\WrongUsageException;
|
use SPC\exception\WrongUsageException;
|
||||||
|
use SPC\store\pkg\CustomPackage;
|
||||||
use SPC\store\source\CustomSourceBase;
|
use SPC\store\source\CustomSourceBase;
|
||||||
|
use SPC\util\SPCTarget;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Source Downloader.
|
* Source Downloader.
|
||||||
@@ -208,31 +209,7 @@ class Downloader
|
|||||||
if ($download_as === SPC_DOWNLOAD_PRE_BUILT) {
|
if ($download_as === SPC_DOWNLOAD_PRE_BUILT) {
|
||||||
$name = self::getPreBuiltLockName($name);
|
$name = self::getPreBuiltLockName($name);
|
||||||
}
|
}
|
||||||
self::lockSource($name, ['source_type' => 'archive', 'filename' => $filename, 'move_path' => $move_path, 'lock_as' => $download_as]);
|
LockFile::lockSource($name, ['source_type' => SPC_SOURCE_ARCHIVE, 'filename' => $filename, 'move_path' => $move_path, 'lock_as' => $download_as]);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try to lock source.
|
|
||||||
*
|
|
||||||
* @param string $name Source name
|
|
||||||
* @param array{
|
|
||||||
* source_type: string,
|
|
||||||
* dirname: ?string,
|
|
||||||
* filename: ?string,
|
|
||||||
* move_path: ?string,
|
|
||||||
* lock_as: int
|
|
||||||
* } $data Source data
|
|
||||||
* @throws FileSystemException
|
|
||||||
*/
|
|
||||||
public static function lockSource(string $name, array $data): void
|
|
||||||
{
|
|
||||||
if (!file_exists(FileSystem::convertPath(DOWNLOAD_PATH . '/.lock.json'))) {
|
|
||||||
$lock = [];
|
|
||||||
} else {
|
|
||||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
|
|
||||||
}
|
|
||||||
$lock[$name] = $data;
|
|
||||||
FileSystem::writeFile(DOWNLOAD_PATH . '/.lock.json', json_encode($lock, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,7 +255,7 @@ class Downloader
|
|||||||
}
|
}
|
||||||
// Lock
|
// Lock
|
||||||
logger()->debug("Locking git source {$name}");
|
logger()->debug("Locking git source {$name}");
|
||||||
self::lockSource($name, ['source_type' => 'dir', 'dirname' => $name, 'move_path' => $move_path, 'lock_as' => $lock_as]);
|
LockFile::lockSource($name, ['source_type' => SPC_SOURCE_GIT, 'dirname' => $name, 'move_path' => $move_path, 'lock_as' => $lock_as]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// 复制目录过去
|
// 复制目录过去
|
||||||
@@ -371,12 +348,25 @@ class Downloader
|
|||||||
SPC_DOWNLOAD_PRE_BUILT
|
SPC_DOWNLOAD_PRE_BUILT
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case 'local':
|
||||||
|
// Local directory, do nothing, just lock it
|
||||||
|
logger()->debug("Locking local source {$name}");
|
||||||
|
LockFile::lockSource($name, [
|
||||||
|
'source_type' => SPC_SOURCE_LOCAL,
|
||||||
|
'dirname' => $pkg['dirname'],
|
||||||
|
'move_path' => $pkg['extract'] ?? null,
|
||||||
|
'lock_as' => SPC_DOWNLOAD_PACKAGE,
|
||||||
|
]);
|
||||||
|
break;
|
||||||
case 'custom': // Custom download method, like API-based download or other
|
case 'custom': // Custom download method, like API-based download or other
|
||||||
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/source', 'SPC\store\source');
|
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg');
|
||||||
foreach ($classes as $class) {
|
foreach ($classes as $class) {
|
||||||
if (is_a($class, CustomSourceBase::class, true) && $class::NAME === $name) {
|
if (is_a($class, CustomPackage::class, true) && $class !== CustomPackage::class) {
|
||||||
(new $class())->fetch($force);
|
$cls = new $class();
|
||||||
break;
|
if (in_array($name, $cls->getSupportName())) {
|
||||||
|
(new $class())->fetch($name, $force, $pkg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -477,6 +467,16 @@ class Downloader
|
|||||||
$download_as
|
$download_as
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case 'local':
|
||||||
|
// Local directory, do nothing, just lock it
|
||||||
|
logger()->debug("Locking local source {$name}");
|
||||||
|
LockFile::lockSource($name, [
|
||||||
|
'source_type' => SPC_SOURCE_LOCAL,
|
||||||
|
'dirname' => $source['dirname'],
|
||||||
|
'move_path' => $source['extract'] ?? null,
|
||||||
|
'lock_as' => $download_as,
|
||||||
|
]);
|
||||||
|
break;
|
||||||
case 'custom': // Custom download method, like API-based download or other
|
case 'custom': // Custom download method, like API-based download or other
|
||||||
if (isset($source['func']) && is_callable($source['func'])) {
|
if (isset($source['func']) && is_callable($source['func'])) {
|
||||||
$source['name'] = $name;
|
$source['name'] = $name;
|
||||||
@@ -591,7 +591,12 @@ class Downloader
|
|||||||
|
|
||||||
public static function getPreBuiltLockName(string $source): string
|
public static function getPreBuiltLockName(string $source): string
|
||||||
{
|
{
|
||||||
return "{$source}-" . PHP_OS_FAMILY . '-' . getenv('GNU_ARCH') . '-' . (getenv('SPC_LIBC') ?: 'default') . '-' . (SystemUtil::getLibcVersionIfExists() ?? 'default');
|
$os_family = PHP_OS_FAMILY;
|
||||||
|
$gnu_arch = getenv('GNU_ARCH') ?: 'unknown';
|
||||||
|
$libc = SPCTarget::getLibc();
|
||||||
|
$libc_version = SPCTarget::getLibcVersion() ?? 'default';
|
||||||
|
|
||||||
|
return "{$source}-{$os_family}-{$gnu_arch}-{$libc}-{$libc_version}";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -629,33 +634,30 @@ class Downloader
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws FileSystemException
|
* @throws FileSystemException
|
||||||
|
* @throws WrongUsageException
|
||||||
*/
|
*/
|
||||||
private static function isAlreadyDownloaded(string $name, bool $force, int $download_as = SPC_DOWNLOAD_SOURCE): bool
|
private static function isAlreadyDownloaded(string $name, bool $force, int $download_as = SPC_DOWNLOAD_SOURCE): bool
|
||||||
{
|
{
|
||||||
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
|
// If the lock file exists, skip downloading for source mode
|
||||||
$lock = [];
|
$lock_item = LockFile::get($name);
|
||||||
} else {
|
if (!$force && $download_as === SPC_DOWNLOAD_SOURCE && $lock_item !== null) {
|
||||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
|
if (file_exists($path = LockFile::getLockFullPath($lock_item))) {
|
||||||
}
|
logger()->notice("Source [{$name}] already downloaded: {$path}");
|
||||||
// If lock file exists, skip downloading for source mode
|
|
||||||
if (!$force && $download_as === SPC_DOWNLOAD_SOURCE && isset($lock[$name])) {
|
|
||||||
if (
|
|
||||||
$lock[$name]['source_type'] === 'archive' && file_exists(DOWNLOAD_PATH . '/' . $lock[$name]['filename']) ||
|
|
||||||
$lock[$name]['source_type'] === 'dir' && is_dir(DOWNLOAD_PATH . '/' . $lock[$name]['dirname'])
|
|
||||||
) {
|
|
||||||
logger()->notice("Source [{$name}] already downloaded: " . ($lock[$name]['filename'] ?? $lock[$name]['dirname']));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If lock file exists for current arch and glibc target, skip downloading
|
$lock_name = self::getPreBuiltLockName($name);
|
||||||
|
$lock_item = LockFile::get($lock_name);
|
||||||
if (!$force && $download_as === SPC_DOWNLOAD_PRE_BUILT && isset($lock[$lock_name = self::getPreBuiltLockName($name)])) {
|
if (!$force && $download_as === SPC_DOWNLOAD_PRE_BUILT && $lock_item !== null) {
|
||||||
// lock name with env
|
// lock name with env
|
||||||
if (
|
if (file_exists($path = LockFile::getLockFullPath($lock_item))) {
|
||||||
$lock[$lock_name]['source_type'] === 'archive' && file_exists(DOWNLOAD_PATH . '/' . $lock[$lock_name]['filename']) ||
|
logger()->notice("Pre-built content [{$name}] already downloaded: {$path}");
|
||||||
$lock[$lock_name]['source_type'] === 'dir' && is_dir(DOWNLOAD_PATH . '/' . $lock[$lock_name]['dirname'])
|
return true;
|
||||||
) {
|
}
|
||||||
logger()->notice("Pre-built content [{$name}] already downloaded: " . ($lock[$lock_name]['filename'] ?? $lock[$lock_name]['dirname']));
|
}
|
||||||
|
if (!$force && $download_as === SPC_DOWNLOAD_PACKAGE && $lock_item !== null) {
|
||||||
|
if (file_exists($path = LockFile::getLockFullPath($lock_item))) {
|
||||||
|
logger()->notice("Source [{$name}] already downloaded: {$path}");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ class FileSystem
|
|||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
* @throws FileSystemException
|
* @throws FileSystemException
|
||||||
*/
|
*/
|
||||||
public static function extractPackage(string $name, string $filename, ?string $extract_path = null): void
|
public static function extractPackage(string $name, string $source_type, string $filename, ?string $extract_path = null): void
|
||||||
{
|
{
|
||||||
if ($extract_path !== null) {
|
if ($extract_path !== null) {
|
||||||
// replace
|
// replace
|
||||||
@@ -151,14 +151,15 @@ class FileSystem
|
|||||||
} else {
|
} else {
|
||||||
$extract_path = PKG_ROOT_PATH . '/' . $name;
|
$extract_path = PKG_ROOT_PATH . '/' . $name;
|
||||||
}
|
}
|
||||||
logger()->info("extracting {$name} package to {$extract_path} ...");
|
logger()->info("Extracting {$name} package to {$extract_path} ...");
|
||||||
$target = self::convertPath($extract_path);
|
$target = self::convertPath($extract_path);
|
||||||
|
|
||||||
if (!is_dir($dir = dirname($target))) {
|
if (!is_dir($dir = dirname($target))) {
|
||||||
self::createDir($dir);
|
self::createDir($dir);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
self::extractArchive($filename, $target);
|
// extract wrapper command
|
||||||
|
self::extractWithType($source_type, $filename, $extract_path);
|
||||||
} catch (RuntimeException $e) {
|
} catch (RuntimeException $e) {
|
||||||
if (PHP_OS_FAMILY === 'Windows') {
|
if (PHP_OS_FAMILY === 'Windows') {
|
||||||
f_passthru('rmdir /s /q ' . $target);
|
f_passthru('rmdir /s /q ' . $target);
|
||||||
@@ -177,24 +178,23 @@ class FileSystem
|
|||||||
* @throws FileSystemException
|
* @throws FileSystemException
|
||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
*/
|
*/
|
||||||
public static function extractSource(string $name, string $filename, ?string $move_path = null): void
|
public static function extractSource(string $name, string $source_type, string $filename, ?string $move_path = null): void
|
||||||
{
|
{
|
||||||
// if source hook is empty, load it
|
// if source hook is empty, load it
|
||||||
if (self::$_extract_hook === []) {
|
if (self::$_extract_hook === []) {
|
||||||
SourcePatcher::init();
|
SourcePatcher::init();
|
||||||
}
|
}
|
||||||
if ($move_path !== null) {
|
$move_path = match ($move_path) {
|
||||||
$move_path = SOURCE_PATH . '/' . $move_path;
|
null => SOURCE_PATH . '/' . $name,
|
||||||
} else {
|
default => self::isRelativePath($move_path) ? (SOURCE_PATH . '/' . $move_path) : $move_path,
|
||||||
$move_path = SOURCE_PATH . "/{$name}";
|
};
|
||||||
}
|
|
||||||
$target = self::convertPath($move_path);
|
$target = self::convertPath($move_path);
|
||||||
logger()->info("extracting {$name} source to {$target}" . ' ...');
|
logger()->info("Extracting {$name} source to {$target}" . ' ...');
|
||||||
if (!is_dir($dir = dirname($target))) {
|
if (!is_dir($dir = dirname($target))) {
|
||||||
self::createDir($dir);
|
self::createDir($dir);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
self::extractArchive($filename, $target);
|
self::extractWithType($source_type, $filename, $move_path);
|
||||||
self::emitSourceExtractHook($name, $target);
|
self::emitSourceExtractHook($name, $target);
|
||||||
} catch (RuntimeException $e) {
|
} catch (RuntimeException $e) {
|
||||||
if (PHP_OS_FAMILY === 'Windows') {
|
if (PHP_OS_FAMILY === 'Windows') {
|
||||||
@@ -484,11 +484,6 @@ class FileSystem
|
|||||||
*/
|
*/
|
||||||
private static function extractArchive(string $filename, string $target): void
|
private static function extractArchive(string $filename, string $target): void
|
||||||
{
|
{
|
||||||
// Git source, just move
|
|
||||||
if (is_dir(self::convertPath($filename))) {
|
|
||||||
self::copyDir(self::convertPath($filename), $target);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Create base dir
|
// Create base dir
|
||||||
if (f_mkdir(directory: $target, recursive: true) !== true) {
|
if (f_mkdir(directory: $target, recursive: true) !== true) {
|
||||||
throw new FileSystemException('create ' . $target . ' dir failed');
|
throw new FileSystemException('create ' . $target . ' dir failed');
|
||||||
@@ -553,4 +548,16 @@ class FileSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function extractWithType(string $source_type, string $filename, string $extract_path): void
|
||||||
|
{
|
||||||
|
logger()->debug('Extracting source [' . $source_type . ']: ' . $filename);
|
||||||
|
/* @phpstan-ignore-next-line */
|
||||||
|
match ($source_type) {
|
||||||
|
SPC_SOURCE_ARCHIVE => self::extractArchive($filename, $extract_path),
|
||||||
|
SPC_SOURCE_GIT => self::copyDir(self::convertPath($filename), $extract_path),
|
||||||
|
// soft link to the local source
|
||||||
|
SPC_SOURCE_LOCAL => symlink(self::convertPath($filename), $extract_path),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
227
src/SPC/store/LockFile.php
Normal file
227
src/SPC/store/LockFile.php
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\store;
|
||||||
|
|
||||||
|
use SPC\exception\FileSystemException;
|
||||||
|
use SPC\exception\RuntimeException;
|
||||||
|
use SPC\exception\WrongUsageException;
|
||||||
|
|
||||||
|
class LockFile
|
||||||
|
{
|
||||||
|
public const string LOCK_FILE = DOWNLOAD_PATH . '/.lock.json';
|
||||||
|
|
||||||
|
private static ?array $lock_file_content = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a lock entry by its name.
|
||||||
|
*
|
||||||
|
* @param string $lock_name Lock name to retrieve
|
||||||
|
* @return null|array{
|
||||||
|
* source_type: string,
|
||||||
|
* filename: ?string,
|
||||||
|
* dirname: ?string,
|
||||||
|
* move_path: ?string,
|
||||||
|
* lock_as: int,
|
||||||
|
* hash: string
|
||||||
|
* } Returns the lock entry as an associative array if it exists, or null if it does not
|
||||||
|
*/
|
||||||
|
public static function get(string $lock_name): ?array
|
||||||
|
{
|
||||||
|
self::init();
|
||||||
|
|
||||||
|
// Return the specific lock entry if it exists, otherwise return an empty array
|
||||||
|
$result = self::$lock_file_content[$lock_name] ?? null;
|
||||||
|
|
||||||
|
// Add old `dir` compatibility
|
||||||
|
if (($result['source_type'] ?? null) === 'dir') {
|
||||||
|
logger()->warning("Lock entry for '{$lock_name}' has 'source_type' set to 'dir', which is deprecated. Please re-download your dependencies.");
|
||||||
|
$result['source_type'] = SPC_SOURCE_GIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a lock file exists for a given lock name.
|
||||||
|
*
|
||||||
|
* @param string $lock_name Lock name to check
|
||||||
|
*/
|
||||||
|
public static function isLockFileExists(string $lock_name): bool
|
||||||
|
{
|
||||||
|
return match (self::get($lock_name)['source_type'] ?? null) {
|
||||||
|
SPC_SOURCE_ARCHIVE => file_exists(DOWNLOAD_PATH . '/' . (self::get($lock_name)['filename'] ?? '.never-exist-file')),
|
||||||
|
SPC_SOURCE_GIT, SPC_SOURCE_LOCAL => is_dir(DOWNLOAD_PATH . '/' . (self::get($lock_name)['dirname'] ?? '.never-exist-dir')),
|
||||||
|
default => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put a lock entry into the lock file.
|
||||||
|
*
|
||||||
|
* @param string $lock_name Lock name to set or remove
|
||||||
|
* @param null|array $lock_content lock content to set, or null to remove the lock entry
|
||||||
|
* @throws FileSystemException
|
||||||
|
* @throws WrongUsageException
|
||||||
|
*/
|
||||||
|
public static function put(string $lock_name, ?array $lock_content): void
|
||||||
|
{
|
||||||
|
self::init();
|
||||||
|
|
||||||
|
if ($lock_content === null && isset(self::$lock_file_content[$lock_name])) {
|
||||||
|
self::removeLockFileIfExists(self::$lock_file_content[$lock_name]);
|
||||||
|
unset(self::$lock_file_content[$lock_name]);
|
||||||
|
} else {
|
||||||
|
self::$lock_file_content[$lock_name] = $lock_content;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the updated lock data back to the file
|
||||||
|
FileSystem::createDir(dirname(self::LOCK_FILE));
|
||||||
|
file_put_contents(self::LOCK_FILE, json_encode(self::$lock_file_content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the full path of a lock file or directory based on the lock options.
|
||||||
|
*
|
||||||
|
* @param array $lock_options lock item options, must contain 'source_type', 'filename' or 'dirname'
|
||||||
|
* @return string the absolute path to the lock file or directory
|
||||||
|
* @throws WrongUsageException
|
||||||
|
*/
|
||||||
|
public static function getLockFullPath(array $lock_options): string
|
||||||
|
{
|
||||||
|
return match ($lock_options['source_type']) {
|
||||||
|
SPC_SOURCE_ARCHIVE => FileSystem::isRelativePath($lock_options['filename']) ? (DOWNLOAD_PATH . '/' . $lock_options['filename']) : $lock_options['filename'],
|
||||||
|
SPC_SOURCE_GIT, SPC_SOURCE_LOCAL => FileSystem::isRelativePath($lock_options['dirname']) ? (DOWNLOAD_PATH . '/' . $lock_options['dirname']) : $lock_options['dirname'],
|
||||||
|
default => throw new WrongUsageException("Unknown source type: {$lock_options['source_type']}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getExtractPath(string $lock_name, string $default_path): ?string
|
||||||
|
{
|
||||||
|
$lock = self::get($lock_name);
|
||||||
|
if ($lock === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If move_path is set, use it; otherwise, use the default extract directory
|
||||||
|
if (isset($lock['move_path'])) {
|
||||||
|
if (FileSystem::isRelativePath($lock['move_path'])) {
|
||||||
|
// If move_path is relative, prepend the default extract directory
|
||||||
|
return match ($lock['lock_as']) {
|
||||||
|
SPC_DOWNLOAD_SOURCE, SPC_DOWNLOAD_PRE_BUILT => FileSystem::convertPath(SOURCE_PATH . '/' . $lock['move_path']),
|
||||||
|
SPC_DOWNLOAD_PACKAGE => FileSystem::convertPath(PKG_ROOT_PATH . '/' . $lock['move_path']),
|
||||||
|
default => throw new WrongUsageException("Unknown lock type: {$lock['lock_as']}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return FileSystem::convertPath($lock['move_path']);
|
||||||
|
}
|
||||||
|
return FileSystem::convertPath($default_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hash of the lock source based on the lock options.
|
||||||
|
*
|
||||||
|
* @param array $lock_options Lock options
|
||||||
|
* @return string Hash of the lock source
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
public static function getLockSourceHash(array $lock_options): string
|
||||||
|
{
|
||||||
|
$result = match ($lock_options['source_type']) {
|
||||||
|
SPC_SOURCE_ARCHIVE => sha1_file(DOWNLOAD_PATH . '/' . $lock_options['filename']),
|
||||||
|
SPC_SOURCE_GIT => exec('cd ' . escapeshellarg(DOWNLOAD_PATH . '/' . $lock_options['dirname']) . ' && ' . SPC_GIT_EXEC . ' rev-parse HEAD'),
|
||||||
|
SPC_SOURCE_LOCAL => 'LOCAL HASH IS ALWAYS DIFFERENT',
|
||||||
|
default => filter_var(getenv('SPC_IGNORE_BAD_HASH'), FILTER_VALIDATE_BOOLEAN) ? '' : throw new RuntimeException("Unknown source type: {$lock_options['source_type']}"),
|
||||||
|
};
|
||||||
|
if ($result === false && !filter_var(getenv('SPC_IGNORE_BAD_HASH'), FILTER_VALIDATE_BOOLEAN)) {
|
||||||
|
throw new RuntimeException("Failed to get hash for source: {$lock_options['source_type']}");
|
||||||
|
}
|
||||||
|
return $result ?: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $lock_options Lock options
|
||||||
|
* @param string $destination Target directory
|
||||||
|
* @throws FileSystemException
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
public static function putLockSourceHash(array $lock_options, string $destination): void
|
||||||
|
{
|
||||||
|
$hash = LockFile::getLockSourceHash($lock_options);
|
||||||
|
if ($lock_options['source_type'] === SPC_SOURCE_LOCAL) {
|
||||||
|
logger()->debug("Source [{$lock_options['dirname']}] is local, no hash will be written.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FileSystem::writeFile("{$destination}/.spc-hash", $hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to lock source with hash.
|
||||||
|
*
|
||||||
|
* @param string $name Source name
|
||||||
|
* @param array{
|
||||||
|
* source_type: string,
|
||||||
|
* dirname: ?string,
|
||||||
|
* filename: ?string,
|
||||||
|
* move_path: ?string,
|
||||||
|
* lock_as: int
|
||||||
|
* } $data Source data
|
||||||
|
* @throws FileSystemException
|
||||||
|
* @throws RuntimeException
|
||||||
|
* @throws WrongUsageException
|
||||||
|
*/
|
||||||
|
public static function lockSource(string $name, array $data): void
|
||||||
|
{
|
||||||
|
// calculate hash
|
||||||
|
$hash = LockFile::getLockSourceHash($data);
|
||||||
|
$data['hash'] = $hash;
|
||||||
|
self::put($name, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function init(): void
|
||||||
|
{
|
||||||
|
if (self::$lock_file_content === null) {
|
||||||
|
// Initialize the lock file content if it hasn't been loaded yet
|
||||||
|
if (!file_exists(self::LOCK_FILE)) {
|
||||||
|
logger()->debug('Lock file does not exist: ' . self::LOCK_FILE . ', initializing empty lock file.');
|
||||||
|
self::$lock_file_content = [];
|
||||||
|
file_put_contents(self::LOCK_FILE, json_encode(self::$lock_file_content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||||
|
} else {
|
||||||
|
$file_content = file_get_contents(self::LOCK_FILE);
|
||||||
|
self::$lock_file_content = json_decode($file_content, true);
|
||||||
|
if (self::$lock_file_content === null) {
|
||||||
|
throw new \RuntimeException('Failed to decode lock file: ' . self::LOCK_FILE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the lock file or directory if it exists.
|
||||||
|
*
|
||||||
|
* @param array $lock_options lock item options, must contain 'source_type', 'filename' or 'dirname'
|
||||||
|
* @throws WrongUsageException
|
||||||
|
* @throws FileSystemException
|
||||||
|
*/
|
||||||
|
private static function removeLockFileIfExists(array $lock_options): void
|
||||||
|
{
|
||||||
|
if ($lock_options['source_type'] === SPC_SOURCE_ARCHIVE) {
|
||||||
|
$path = self::getLockFullPath($lock_options);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
logger()->info('Removing file ' . $path);
|
||||||
|
unlink($path);
|
||||||
|
} else {
|
||||||
|
logger()->debug("Lock file [{$lock_options['filename']}] not found, skip removing file.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$path = self::getLockFullPath($lock_options);
|
||||||
|
if (is_dir($path)) {
|
||||||
|
logger()->info('Removing directory ' . $path);
|
||||||
|
FileSystem::removeDir($path);
|
||||||
|
} else {
|
||||||
|
logger()->debug("Lock directory [{$lock_options['dirname']}] not found, skip removing directory.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ namespace SPC\store;
|
|||||||
|
|
||||||
use SPC\exception\FileSystemException;
|
use SPC\exception\FileSystemException;
|
||||||
use SPC\exception\WrongUsageException;
|
use SPC\exception\WrongUsageException;
|
||||||
|
use SPC\store\pkg\CustomPackage;
|
||||||
|
|
||||||
class PackageManager
|
class PackageManager
|
||||||
{
|
{
|
||||||
@@ -32,11 +33,27 @@ class PackageManager
|
|||||||
|
|
||||||
// Download package
|
// Download package
|
||||||
Downloader::downloadPackage($pkg_name, $config, $force);
|
Downloader::downloadPackage($pkg_name, $config, $force);
|
||||||
|
if (Config::getPkg($pkg_name)['type'] === 'custom') {
|
||||||
|
// Custom extract function
|
||||||
|
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg');
|
||||||
|
foreach ($classes as $class) {
|
||||||
|
if (is_a($class, CustomPackage::class, true) && $class !== CustomPackage::class) {
|
||||||
|
$cls = new $class();
|
||||||
|
if (in_array($pkg_name, $cls->getSupportName())) {
|
||||||
|
(new $class())->extract($pkg_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
// After download, read lock file name
|
// After download, read lock file name
|
||||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true);
|
$lock = LockFile::get($pkg_name);
|
||||||
$filename = DOWNLOAD_PATH . '/' . ($lock[$pkg_name]['filename'] ?? $lock[$pkg_name]['dirname']);
|
$source_type = $lock['source_type'];
|
||||||
$extract = $lock[$pkg_name]['move_path'] === null ? (PKG_ROOT_PATH . '/' . $pkg_name) : $lock[$pkg_name]['move_path'];
|
$filename = LockFile::getLockFullPath($lock);
|
||||||
FileSystem::extractPackage($pkg_name, $filename, $extract);
|
$extract = LockFile::getExtractPath($pkg_name, PKG_ROOT_PATH . '/' . $pkg_name);
|
||||||
|
|
||||||
|
FileSystem::extractPackage($pkg_name, $source_type, $filename, $extract);
|
||||||
|
|
||||||
// if contains extract-files, we just move this file to destination, and remove extract dir
|
// if contains extract-files, we just move this file to destination, and remove extract dir
|
||||||
if (is_array($config['extract-files'] ?? null) && is_assoc_array($config['extract-files'])) {
|
if (is_array($config['extract-files'] ?? null) && is_assoc_array($config['extract-files'])) {
|
||||||
|
|||||||
@@ -17,11 +17,6 @@ class SourceManager
|
|||||||
*/
|
*/
|
||||||
public static function initSource(?array $sources = null, ?array $libs = null, ?array $exts = null, bool $source_only = false): void
|
public static function initSource(?array $sources = null, ?array $libs = null, ?array $exts = null, bool $source_only = false): void
|
||||||
{
|
{
|
||||||
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
|
|
||||||
throw new WrongUsageException('Download lock file "downloads/.lock.json" not found, maybe you need to download sources first ?');
|
|
||||||
}
|
|
||||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true);
|
|
||||||
|
|
||||||
$sources_extracted = [];
|
$sources_extracted = [];
|
||||||
// source check exist
|
// source check exist
|
||||||
if (is_array($sources)) {
|
if (is_array($sources)) {
|
||||||
@@ -56,8 +51,8 @@ class SourceManager
|
|||||||
}
|
}
|
||||||
// check source downloaded
|
// check source downloaded
|
||||||
$pre_built_name = Downloader::getPreBuiltLockName($source);
|
$pre_built_name = Downloader::getPreBuiltLockName($source);
|
||||||
if ($source_only || !isset($lock[$pre_built_name])) {
|
if ($source_only || LockFile::get($pre_built_name) === null) {
|
||||||
if (!isset($lock[$source])) {
|
if (LockFile::get($source) === null) {
|
||||||
throw new WrongUsageException("Source [{$source}] not downloaded or not locked, you should download it first !");
|
throw new WrongUsageException("Source [{$source}] not downloaded or not locked, you should download it first !");
|
||||||
}
|
}
|
||||||
$lock_name = $source;
|
$lock_name = $source;
|
||||||
@@ -65,14 +60,38 @@ class SourceManager
|
|||||||
$lock_name = $pre_built_name;
|
$lock_name = $pre_built_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$lock_content = LockFile::get($lock_name);
|
||||||
|
|
||||||
// check source dir exist
|
// check source dir exist
|
||||||
$check = $lock[$lock_name]['move_path'] === null ? (SOURCE_PATH . '/' . $source) : (SOURCE_PATH . '/' . $lock[$lock_name]['move_path']);
|
$check = LockFile::getExtractPath($lock_name, SOURCE_PATH . '/' . $source);
|
||||||
|
// $check = $lock[$lock_name]['move_path'] === null ? (SOURCE_PATH . '/' . $source) : (SOURCE_PATH . '/' . $lock[$lock_name]['move_path']);
|
||||||
if (!is_dir($check)) {
|
if (!is_dir($check)) {
|
||||||
logger()->debug('Extracting source [' . $source . '] to ' . $check . ' ...');
|
logger()->debug('Extracting source [' . $source . '] to ' . $check . ' ...');
|
||||||
FileSystem::extractSource($source, DOWNLOAD_PATH . '/' . ($lock[$lock_name]['filename'] ?? $lock[$lock_name]['dirname']), $lock[$lock_name]['move_path']);
|
$filename = LockFile::getLockFullPath($lock_content);
|
||||||
} else {
|
FileSystem::extractSource($source, $lock_content['source_type'], $filename, $check);
|
||||||
logger()->debug('Source [' . $source . '] already extracted in ' . $check . ', skip !');
|
LockFile::putLockSourceHash($lock_content, $check);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
// if a lock file does not have hash, calculate with the current source (backward compatibility)
|
||||||
|
if (!isset($lock_content['hash'])) {
|
||||||
|
$hash = LockFile::getLockSourceHash($lock_content);
|
||||||
|
} else {
|
||||||
|
$hash = $lock_content['hash'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// when source already extracted, detect if the extracted source hash is the same as the lock file one
|
||||||
|
if (file_exists("{$check}/.spc-hash") && FileSystem::readFile("{$check}/.spc-hash") === $hash) {
|
||||||
|
logger()->debug('Source [' . $source . '] already extracted in ' . $check . ', skip !');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not, remove the source dir and extract again
|
||||||
|
logger()->notice("Source [{$source}] hash mismatch, removing old source dir and extracting again ...");
|
||||||
|
FileSystem::removeDir($check);
|
||||||
|
$filename = LockFile::getLockFullPath($lock_content);
|
||||||
|
$move_path = LockFile::getExtractPath($lock_name, SOURCE_PATH . '/' . $source);
|
||||||
|
FileSystem::extractSource($source, $lock_content['source_type'], $filename, $move_path);
|
||||||
|
LockFile::putLockSourceHash($lock_content, $check);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,27 +11,27 @@ use SPC\builder\unix\UnixBuilderBase;
|
|||||||
use SPC\exception\FileSystemException;
|
use SPC\exception\FileSystemException;
|
||||||
use SPC\exception\RuntimeException;
|
use SPC\exception\RuntimeException;
|
||||||
use SPC\exception\WrongUsageException;
|
use SPC\exception\WrongUsageException;
|
||||||
|
use SPC\util\SPCTarget;
|
||||||
|
|
||||||
class SourcePatcher
|
class SourcePatcher
|
||||||
{
|
{
|
||||||
public static function init(): void
|
public static function init(): void
|
||||||
{
|
{
|
||||||
// FileSystem::addSourceExtractHook('swow', [SourcePatcher::class, 'patchSwow']);
|
// FileSystem::addSourceExtractHook('swow', [__CLASS__, 'patchSwow']);
|
||||||
FileSystem::addSourceExtractHook('micro', [SourcePatcher::class, 'patchMicro']);
|
FileSystem::addSourceExtractHook('openssl', [__CLASS__, 'patchOpenssl11Darwin']);
|
||||||
FileSystem::addSourceExtractHook('openssl', [SourcePatcher::class, 'patchOpenssl11Darwin']);
|
FileSystem::addSourceExtractHook('swoole', [__CLASS__, 'patchSwoole']);
|
||||||
FileSystem::addSourceExtractHook('swoole', [SourcePatcher::class, 'patchSwoole']);
|
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchPhpLibxml212']);
|
||||||
FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchPhpLibxml212']);
|
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchGDWin32']);
|
||||||
FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchGDWin32']);
|
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchFfiCentos7FixO3strncmp']);
|
||||||
FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchFfiCentos7FixO3strncmp']);
|
FileSystem::addSourceExtractHook('sqlsrv', [__CLASS__, 'patchSQLSRVWin32']);
|
||||||
FileSystem::addSourceExtractHook('sqlsrv', [SourcePatcher::class, 'patchSQLSRVWin32']);
|
FileSystem::addSourceExtractHook('pdo_sqlsrv', [__CLASS__, 'patchSQLSRVWin32']);
|
||||||
FileSystem::addSourceExtractHook('pdo_sqlsrv', [SourcePatcher::class, 'patchSQLSRVWin32']);
|
FileSystem::addSourceExtractHook('yaml', [__CLASS__, 'patchYamlWin32']);
|
||||||
FileSystem::addSourceExtractHook('yaml', [SourcePatcher::class, 'patchYamlWin32']);
|
FileSystem::addSourceExtractHook('libyaml', [__CLASS__, 'patchLibYaml']);
|
||||||
FileSystem::addSourceExtractHook('libyaml', [SourcePatcher::class, 'patchLibYaml']);
|
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchImapLicense']);
|
||||||
FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchImapLicense']);
|
FileSystem::addSourceExtractHook('ext-imagick', [__CLASS__, 'patchImagickWith84']);
|
||||||
FileSystem::addSourceExtractHook('ext-imagick', [SourcePatcher::class, 'patchImagickWith84']);
|
FileSystem::addSourceExtractHook('libaom', [__CLASS__, 'patchLibaomForAlpine']);
|
||||||
FileSystem::addSourceExtractHook('libaom', [SourcePatcher::class, 'patchLibaomForAlpine']);
|
FileSystem::addSourceExtractHook('attr', [__CLASS__, 'patchAttrForAlpine']);
|
||||||
FileSystem::addSourceExtractHook('attr', [SourcePatcher::class, 'patchAttrForAlpine']);
|
FileSystem::addSourceExtractHook('gmssl', [__CLASS__, 'patchGMSSL']);
|
||||||
FileSystem::addSourceExtractHook('gmssl', [SourcePatcher::class, 'patchGMSSL']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,12 +46,12 @@ class SourcePatcher
|
|||||||
{
|
{
|
||||||
foreach ($builder->getExts() as $ext) {
|
foreach ($builder->getExts() as $ext) {
|
||||||
if ($ext->patchBeforeBuildconf() === true) {
|
if ($ext->patchBeforeBuildconf() === true) {
|
||||||
logger()->info('Extension [' . $ext->getName() . '] patched before buildconf');
|
logger()->info("Extension [{$ext->getName()}] patched before buildconf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($builder->getLibs() as $lib) {
|
foreach ($builder->getLibs() as $lib) {
|
||||||
if ($lib->patchBeforeBuildconf() === true) {
|
if ($lib->patchBeforeBuildconf() === true) {
|
||||||
logger()->info('Library [' . $lib->getName() . '] patched before buildconf');
|
logger()->info("Library [{$lib->getName()}]patched before buildconf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// patch windows php 8.1 bug
|
// patch windows php 8.1 bug
|
||||||
@@ -73,18 +73,9 @@ class SourcePatcher
|
|||||||
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/build/php.m4', 'PKG_CHECK_MODULES(', 'PKG_CHECK_MODULES_STATIC(');
|
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/build/php.m4', 'PKG_CHECK_MODULES(', 'PKG_CHECK_MODULES_STATIC(');
|
||||||
|
|
||||||
if ($builder->getOption('enable-micro-win32')) {
|
if ($builder->getOption('enable-micro-win32')) {
|
||||||
SourcePatcher::patchMicroWin32();
|
self::patchMicroWin32();
|
||||||
} else {
|
} else {
|
||||||
SourcePatcher::unpatchMicroWin32();
|
self::unpatchMicroWin32();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function patchBeforeSharedBuild(BuilderBase $builder): void
|
|
||||||
{
|
|
||||||
foreach ($builder->getExts() as $ext) {
|
|
||||||
if ($ext->patchBeforeSharedBuild() === true) {
|
|
||||||
logger()->info('Extension [' . $ext->getName() . '] patched before shared build');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,26 +89,23 @@ class SourcePatcher
|
|||||||
{
|
{
|
||||||
foreach ($builder->getExts() as $ext) {
|
foreach ($builder->getExts() as $ext) {
|
||||||
if ($ext->patchBeforeConfigure() === true) {
|
if ($ext->patchBeforeConfigure() === true) {
|
||||||
logger()->info('Extension [' . $ext->getName() . '] patched before configure');
|
logger()->info("Extension [{$ext->getName()}] patched before configure");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($builder->getLibs() as $lib) {
|
foreach ($builder->getLibs() as $lib) {
|
||||||
if ($lib->patchBeforeConfigure() === true) {
|
if ($lib->patchBeforeConfigure() === true) {
|
||||||
logger()->info('Library [' . $lib->getName() . '] patched before configure');
|
logger()->info("Library [{$lib->getName()}] patched before configure");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// patch capstone
|
// patch capstone
|
||||||
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/have_capstone="yes"/', 'have_capstone="no"');
|
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/have_capstone="yes"/', 'have_capstone="no"');
|
||||||
if ($builder instanceof LinuxBuilder && getenv('SPC_LIBC') === 'glibc') {
|
|
||||||
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Zend/zend_operators.h', '# define ZEND_USE_ASM_ARITHMETIC 1', '# define ZEND_USE_ASM_ARITHMETIC 0');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
* @throws FileSystemException
|
* @throws FileSystemException
|
||||||
*/
|
*/
|
||||||
public static function patchMicro(string $name = '', string $target = '', ?array $items = null): bool
|
public static function patchMicro(?array $items = null): bool
|
||||||
{
|
{
|
||||||
if (!file_exists(SOURCE_PATH . '/php-src/sapi/micro/php_micro.c')) {
|
if (!file_exists(SOURCE_PATH . '/php-src/sapi/micro/php_micro.c')) {
|
||||||
return false;
|
return false;
|
||||||
@@ -164,11 +152,7 @@ class SourcePatcher
|
|||||||
|
|
||||||
foreach ($patches as $patch) {
|
foreach ($patches as $patch) {
|
||||||
logger()->info("Patching micro with {$patch}");
|
logger()->info("Patching micro with {$patch}");
|
||||||
$patchesStr = str_replace('/', DIRECTORY_SEPARATOR, $patch);
|
self::patchFile(SOURCE_PATH . "/php-src/{$patch}", SOURCE_PATH . '/php-src');
|
||||||
f_passthru(
|
|
||||||
'cd ' . SOURCE_PATH . '/php-src && ' .
|
|
||||||
(PHP_OS_FAMILY === 'Windows' ? 'type' : 'cat') . ' ' . $patchesStr . ' | patch -p1 '
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -177,24 +161,32 @@ class SourcePatcher
|
|||||||
/**
|
/**
|
||||||
* Use existing patch file for patching
|
* Use existing patch file for patching
|
||||||
*
|
*
|
||||||
* @param string $patch_name Patch file name in src/globals/patch/
|
* @param string $patch_name Patch file name in src/globals/patch/ or absolute path
|
||||||
* @param string $cwd Working directory for patch command
|
* @param string $cwd Working directory for patch command
|
||||||
* @param bool $reverse Reverse patches (default: False)
|
* @param bool $reverse Reverse patches (default: False)
|
||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
*/
|
*/
|
||||||
public static function patchFile(string $patch_name, string $cwd, bool $reverse = false): bool
|
public static function patchFile(string $patch_name, string $cwd, bool $reverse = false): bool
|
||||||
{
|
{
|
||||||
if (!file_exists(ROOT_DIR . "/src/globals/patch/{$patch_name}")) {
|
if (FileSystem::isRelativePath($patch_name)) {
|
||||||
|
$patch_file = ROOT_DIR . "/src/globals/patch/{$patch_name}";
|
||||||
|
} else {
|
||||||
|
$patch_file = $patch_name;
|
||||||
|
}
|
||||||
|
if (!file_exists($patch_file)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$patch_file = ROOT_DIR . "/src/globals/patch/{$patch_name}";
|
$patch_str = FileSystem::convertPath($patch_file);
|
||||||
$patch_str = str_replace('/', DIRECTORY_SEPARATOR, $patch_file);
|
if (!file_exists($patch_str)) {
|
||||||
|
throw new RuntimeException("Patch file [{$patch_str}] does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
// Copy patch from phar
|
// Copy patch from phar
|
||||||
if (\Phar::running() !== '') {
|
if (str_starts_with($patch_str, 'phar://')) {
|
||||||
file_put_contents(SOURCE_PATH . '/' . $patch_name, file_get_contents($patch_file));
|
$filename = pathinfo($patch_file, PATHINFO_BASENAME);
|
||||||
$patch_str = str_replace('/', DIRECTORY_SEPARATOR, SOURCE_PATH . '/' . $patch_name);
|
file_put_contents(SOURCE_PATH . "/{$filename}", file_get_contents($patch_file));
|
||||||
|
$patch_str = FileSystem::convertPath(SOURCE_PATH . "/{$filename}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// detect
|
// detect
|
||||||
@@ -241,6 +233,7 @@ class SourcePatcher
|
|||||||
'PHP_ADD_INCLUDE([$ext_srcdir])',
|
'PHP_ADD_INCLUDE([$ext_srcdir])',
|
||||||
"PHP_ADD_INCLUDE( [\$ext_srcdir] )\n PHP_ADD_INCLUDE([\$abs_srcdir/ext])"
|
"PHP_ADD_INCLUDE( [\$ext_srcdir] )\n PHP_ADD_INCLUDE([\$abs_srcdir/ext])"
|
||||||
);
|
);
|
||||||
|
|
||||||
// swoole 5.1.3 build fix
|
// swoole 5.1.3 build fix
|
||||||
// get swoole version first
|
// get swoole version first
|
||||||
$file = SOURCE_PATH . '/php-src/ext/swoole/include/swoole_version.h';
|
$file = SOURCE_PATH . '/php-src/ext/swoole/include/swoole_version.h';
|
||||||
@@ -254,6 +247,7 @@ class SourcePatcher
|
|||||||
if ($version === '5.1.3') {
|
if ($version === '5.1.3') {
|
||||||
self::patchFile('spc_fix_swoole_50513.patch', SOURCE_PATH . '/php-src/ext/swoole');
|
self::patchFile('spc_fix_swoole_50513.patch', SOURCE_PATH . '/php-src/ext/swoole');
|
||||||
}
|
}
|
||||||
|
self::patchFile('swoole_fix_date_time.patch', SOURCE_PATH . '/php-src/ext/swoole');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,14 +273,28 @@ class SourcePatcher
|
|||||||
// call extension patch before make
|
// call extension patch before make
|
||||||
foreach ($builder->getExts(false) as $ext) {
|
foreach ($builder->getExts(false) as $ext) {
|
||||||
if ($ext->patchBeforeMake() === true) {
|
if ($ext->patchBeforeMake() === true) {
|
||||||
logger()->info('Extension [' . $ext->getName() . '] patched before make');
|
logger()->info("Extension [{$ext->getName()}] patched before make");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($builder->getLibs() as $lib) {
|
foreach ($builder->getLibs() as $lib) {
|
||||||
if ($lib->patchBeforeMake() === true) {
|
if ($lib->patchBeforeMake() === true) {
|
||||||
logger()->info('Library [' . $lib->getName() . '] patched before make');
|
logger()->info("Library [{$lib->getName()}] patched before make");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (str_contains((string) getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), '-release')) {
|
||||||
|
FileSystem::replaceFileLineContainsString(
|
||||||
|
SOURCE_PATH . '/php-src/ext/standard/info.c',
|
||||||
|
'#ifdef CONFIGURE_COMMAND',
|
||||||
|
'#ifdef NO_CONFIGURE_COMMAND',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
FileSystem::replaceFileLineContainsString(
|
||||||
|
SOURCE_PATH . '/php-src/ext/standard/info.c',
|
||||||
|
'#ifdef NO_CONFIGURE_COMMAND',
|
||||||
|
'#ifdef CONFIGURE_COMMAND',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -446,7 +454,7 @@ class SourcePatcher
|
|||||||
}
|
}
|
||||||
$extnum = intval($match[1]);
|
$extnum = intval($match[1]);
|
||||||
if ($extnum < 30800) {
|
if ($extnum < 30800) {
|
||||||
SourcePatcher::patchFile('imagick_php84_before_30800.patch', SOURCE_PATH . '/php-src/ext/imagick');
|
self::patchFile('imagick_php84_before_30800.patch', SOURCE_PATH . '/php-src/ext/imagick');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -454,7 +462,7 @@ class SourcePatcher
|
|||||||
|
|
||||||
public static function patchFfiCentos7FixO3strncmp(): bool
|
public static function patchFfiCentos7FixO3strncmp(): bool
|
||||||
{
|
{
|
||||||
if (PHP_OS_FAMILY !== 'Linux' || SystemUtil::getLibcVersionIfExists() >= '2.17') {
|
if (!($ver = SPCTarget::getLibcVersion()) || version_compare($ver, '2.17', '>')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!file_exists(SOURCE_PATH . '/php-src/main/php_version.h')) {
|
if (!file_exists(SOURCE_PATH . '/php-src/main/php_version.h')) {
|
||||||
@@ -464,14 +472,14 @@ class SourcePatcher
|
|||||||
if (preg_match('/PHP_VERSION_ID (\d+)/', $file, $match) !== 0 && intval($match[1]) < 80316) {
|
if (preg_match('/PHP_VERSION_ID (\d+)/', $file, $match) !== 0 && intval($match[1]) < 80316) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SourcePatcher::patchFile('ffi_centos7_fix_O3_strncmp.patch', SOURCE_PATH . '/php-src');
|
self::patchFile('ffi_centos7_fix_O3_strncmp.patch', SOURCE_PATH . '/php-src');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function patchLibaomForAlpine(): bool
|
public static function patchLibaomForAlpine(): bool
|
||||||
{
|
{
|
||||||
if (PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist()) {
|
if (PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist()) {
|
||||||
SourcePatcher::patchFile('libaom_posix_implict.patch', SOURCE_PATH . '/libaom');
|
self::patchFile('libaom_posix_implict.patch', SOURCE_PATH . '/libaom');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -480,7 +488,7 @@ class SourcePatcher
|
|||||||
public static function patchAttrForAlpine(): bool
|
public static function patchAttrForAlpine(): bool
|
||||||
{
|
{
|
||||||
if (PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist() || PHP_OS_FAMILY === 'Darwin') {
|
if (PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist() || PHP_OS_FAMILY === 'Darwin') {
|
||||||
SourcePatcher::patchFile('attr_alpine_gethostname.patch', SOURCE_PATH . '/attr');
|
self::patchFile('attr_alpine_gethostname.patch', SOURCE_PATH . '/attr');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
18
src/SPC/store/pkg/CustomPackage.php
Normal file
18
src/SPC/store/pkg/CustomPackage.php
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\store\pkg;
|
||||||
|
|
||||||
|
abstract class CustomPackage
|
||||||
|
{
|
||||||
|
abstract public function getSupportName(): array;
|
||||||
|
|
||||||
|
abstract public function fetch(string $name, bool $force = false, ?array $config = null): void;
|
||||||
|
|
||||||
|
abstract public function extract(string $name): void;
|
||||||
|
|
||||||
|
abstract public static function getEnvironment(): array;
|
||||||
|
|
||||||
|
abstract public static function isInstalled(): bool;
|
||||||
|
}
|
||||||
115
src/SPC/store/pkg/GoXcaddy.php
Normal file
115
src/SPC/store/pkg/GoXcaddy.php
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\store\pkg;
|
||||||
|
|
||||||
|
use SPC\store\Downloader;
|
||||||
|
use SPC\store\FileSystem;
|
||||||
|
use SPC\store\LockFile;
|
||||||
|
|
||||||
|
class GoXcaddy extends CustomPackage
|
||||||
|
{
|
||||||
|
public static function isInstalled(): bool
|
||||||
|
{
|
||||||
|
$arch = arch2gnu(php_uname('m'));
|
||||||
|
$os = match (PHP_OS_FAMILY) {
|
||||||
|
'Windows' => 'win',
|
||||||
|
'Darwin' => 'macos',
|
||||||
|
'BSD' => 'freebsd',
|
||||||
|
default => 'linux',
|
||||||
|
};
|
||||||
|
|
||||||
|
$packageName = "go-xcaddy-{$arch}-{$os}";
|
||||||
|
$pkgroot = PKG_ROOT_PATH;
|
||||||
|
$folder = "{$pkgroot}/{$packageName}";
|
||||||
|
|
||||||
|
return is_dir($folder) && is_file("{$folder}/bin/go") && is_file("{$folder}/bin/xcaddy");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSupportName(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'go-xcaddy-x86_64-linux',
|
||||||
|
'go-xcaddy-x86_64-macos',
|
||||||
|
'go-xcaddy-aarch64-linux',
|
||||||
|
'go-xcaddy-aarch64-macos',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetch(string $name, bool $force = false, ?array $config = null): void
|
||||||
|
{
|
||||||
|
$pkgroot = PKG_ROOT_PATH;
|
||||||
|
$go_exec = "{$pkgroot}/{$name}/bin/go";
|
||||||
|
$xcaddy_exec = "{$pkgroot}/{$name}/bin/xcaddy";
|
||||||
|
if ($force) {
|
||||||
|
FileSystem::removeDir("{$pkgroot}/{$name}");
|
||||||
|
}
|
||||||
|
if (file_exists($go_exec) && file_exists($xcaddy_exec)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$arch = match (explode('-', $name)[2]) {
|
||||||
|
'x86_64' => 'amd64',
|
||||||
|
'aarch64' => 'arm64',
|
||||||
|
default => throw new \InvalidArgumentException('Unsupported architecture: ' . $name),
|
||||||
|
};
|
||||||
|
$os = match (explode('-', $name)[3]) {
|
||||||
|
'linux' => 'linux',
|
||||||
|
'macos' => 'darwin',
|
||||||
|
default => throw new \InvalidArgumentException('Unsupported OS: ' . $name),
|
||||||
|
};
|
||||||
|
$go_version = '1.24.4';
|
||||||
|
$config = [
|
||||||
|
'type' => 'url',
|
||||||
|
'url' => "https://go.dev/dl/go{$go_version}.{$os}-{$arch}.tar.gz",
|
||||||
|
];
|
||||||
|
Downloader::downloadPackage($name, $config, $force);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function extract(string $name): void
|
||||||
|
{
|
||||||
|
$pkgroot = PKG_ROOT_PATH;
|
||||||
|
$go_exec = "{$pkgroot}/{$name}/bin/go";
|
||||||
|
$xcaddy_exec = "{$pkgroot}/{$name}/bin/xcaddy";
|
||||||
|
if (file_exists($go_exec) && file_exists($xcaddy_exec)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$lock = json_decode(FileSystem::readFile(LockFile::LOCK_FILE), true);
|
||||||
|
$source_type = $lock[$name]['source_type'];
|
||||||
|
$filename = DOWNLOAD_PATH . '/' . ($lock[$name]['filename'] ?? $lock[$name]['dirname']);
|
||||||
|
$extract = $lock[$name]['move_path'] === null ? "{$pkgroot}/{$name}" : $lock[$name]['move_path'];
|
||||||
|
|
||||||
|
FileSystem::extractPackage($name, $source_type, $filename, $extract);
|
||||||
|
|
||||||
|
// install xcaddy
|
||||||
|
shell()
|
||||||
|
->appendEnv([
|
||||||
|
'PATH' => "{$pkgroot}/{$name}/bin:" . getenv('PATH'),
|
||||||
|
'GOROOT' => "{$pkgroot}/{$name}",
|
||||||
|
'GOBIN' => "{$pkgroot}/{$name}/bin",
|
||||||
|
'GOPATH' => "{$pkgroot}/go",
|
||||||
|
])
|
||||||
|
->exec("CC=cc {$go_exec} install github.com/caddyserver/xcaddy/cmd/xcaddy@latest");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getEnvironment(): array
|
||||||
|
{
|
||||||
|
$arch = arch2gnu(php_uname('m'));
|
||||||
|
$os = match (PHP_OS_FAMILY) {
|
||||||
|
'Windows' => 'win',
|
||||||
|
'Darwin' => 'macos',
|
||||||
|
'BSD' => 'freebsd',
|
||||||
|
default => 'linux',
|
||||||
|
};
|
||||||
|
|
||||||
|
$packageName = "go-xcaddy-{$arch}-{$os}";
|
||||||
|
$pkgroot = PKG_ROOT_PATH;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'PATH' => "{$pkgroot}/{$packageName}/bin",
|
||||||
|
'GOROOT' => "{$pkgroot}/{$packageName}",
|
||||||
|
'GOBIN' => "{$pkgroot}/{$packageName}/bin",
|
||||||
|
'GOPATH' => "{$pkgroot}/go",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
164
src/SPC/store/pkg/Zig.php
Normal file
164
src/SPC/store/pkg/Zig.php
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\store\pkg;
|
||||||
|
|
||||||
|
use SPC\store\CurlHook;
|
||||||
|
use SPC\store\Downloader;
|
||||||
|
use SPC\store\FileSystem;
|
||||||
|
use SPC\store\LockFile;
|
||||||
|
|
||||||
|
class Zig extends CustomPackage
|
||||||
|
{
|
||||||
|
public static function isInstalled(): bool
|
||||||
|
{
|
||||||
|
$path = self::getPath();
|
||||||
|
return file_exists("{$path}/zig") && file_exists("{$path}/zig-cc") && file_exists("{$path}/zig-c++");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSupportName(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'zig-x86_64-linux',
|
||||||
|
'zig-aarch64-linux',
|
||||||
|
'zig-x86_64-macos',
|
||||||
|
'zig-aarch64-macos',
|
||||||
|
'zig-x86_64-win',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetch(string $name, bool $force = false, ?array $config = null): void
|
||||||
|
{
|
||||||
|
$pkgroot = PKG_ROOT_PATH;
|
||||||
|
$zig_exec = match (PHP_OS_FAMILY) {
|
||||||
|
'Windows' => "{$pkgroot}/{$name}/zig.exe",
|
||||||
|
default => "{$pkgroot}/{$name}/zig",
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($force) {
|
||||||
|
FileSystem::removeDir("{$pkgroot}/{$name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_exists($zig_exec)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = explode('-', $name);
|
||||||
|
$arch = $parts[1];
|
||||||
|
$os = $parts[2];
|
||||||
|
|
||||||
|
$zig_arch = match ($arch) {
|
||||||
|
'x86_64', 'aarch64' => $arch,
|
||||||
|
default => throw new \InvalidArgumentException('Unsupported architecture: ' . $arch),
|
||||||
|
};
|
||||||
|
|
||||||
|
$zig_os = match ($os) {
|
||||||
|
'linux' => 'linux',
|
||||||
|
'macos' => 'macos',
|
||||||
|
'win' => 'windows',
|
||||||
|
default => throw new \InvalidArgumentException('Unsupported OS: ' . $os),
|
||||||
|
};
|
||||||
|
|
||||||
|
$index_json = json_decode(Downloader::curlExec('https://ziglang.org/download/index.json', hooks: [[CurlHook::class, 'setupGithubToken']]), true);
|
||||||
|
|
||||||
|
$latest_version = null;
|
||||||
|
foreach ($index_json as $version => $data) {
|
||||||
|
$latest_version = $version;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$latest_version) {
|
||||||
|
throw new \RuntimeException('Could not determine latest Zig version');
|
||||||
|
}
|
||||||
|
|
||||||
|
logger()->info("Installing Zig version {$latest_version}");
|
||||||
|
|
||||||
|
$platform_key = "{$zig_arch}-{$zig_os}";
|
||||||
|
if (!isset($index_json[$latest_version][$platform_key])) {
|
||||||
|
throw new \RuntimeException("No download available for {$platform_key} in Zig version {$latest_version}");
|
||||||
|
}
|
||||||
|
|
||||||
|
$download_info = $index_json[$latest_version][$platform_key];
|
||||||
|
$url = $download_info['tarball'];
|
||||||
|
$filename = basename($url);
|
||||||
|
|
||||||
|
$pkg = [
|
||||||
|
'type' => 'url',
|
||||||
|
'url' => $url,
|
||||||
|
'filename' => $filename,
|
||||||
|
];
|
||||||
|
|
||||||
|
Downloader::downloadPackage($name, $pkg, $force);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function extract(string $name): void
|
||||||
|
{
|
||||||
|
$pkgroot = PKG_ROOT_PATH;
|
||||||
|
$zig_bin_dir = "{$pkgroot}/{$name}";
|
||||||
|
$zig_exec = match (PHP_OS_FAMILY) {
|
||||||
|
'Windows' => "{$zig_bin_dir}/zig.exe",
|
||||||
|
default => "{$zig_bin_dir}/zig",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (file_exists($zig_exec) && file_exists("{$zig_bin_dir}/zig-cc")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$lock = json_decode(FileSystem::readFile(LockFile::LOCK_FILE), true);
|
||||||
|
$source_type = $lock[$name]['source_type'];
|
||||||
|
$filename = DOWNLOAD_PATH . '/' . ($lock[$name]['filename'] ?? $lock[$name]['dirname']);
|
||||||
|
$extract = "{$pkgroot}/{$name}";
|
||||||
|
|
||||||
|
FileSystem::extractPackage($name, $source_type, $filename, $extract);
|
||||||
|
|
||||||
|
$this->createZigCcScript($zig_bin_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getEnvironment(): array
|
||||||
|
{
|
||||||
|
$arch = arch2gnu(php_uname('m'));
|
||||||
|
$os = match (PHP_OS_FAMILY) {
|
||||||
|
'Linux' => 'linux',
|
||||||
|
'Windows' => 'win',
|
||||||
|
'Darwin' => 'macos',
|
||||||
|
'BSD' => 'freebsd',
|
||||||
|
default => 'linux',
|
||||||
|
};
|
||||||
|
|
||||||
|
$packageName = "zig-{$arch}-{$os}";
|
||||||
|
$path = PKG_ROOT_PATH . "/{$packageName}";
|
||||||
|
|
||||||
|
return [
|
||||||
|
'PATH' => $path,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getPath(): string
|
||||||
|
{
|
||||||
|
$arch = arch2gnu(php_uname('m'));
|
||||||
|
$os = match (PHP_OS_FAMILY) {
|
||||||
|
'Linux' => 'linux',
|
||||||
|
'Windows' => 'win',
|
||||||
|
'Darwin' => 'macos',
|
||||||
|
'BSD' => 'freebsd',
|
||||||
|
default => 'linux',
|
||||||
|
};
|
||||||
|
|
||||||
|
$packageName = "zig-{$arch}-{$os}";
|
||||||
|
return PKG_ROOT_PATH . "/{$packageName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createZigCcScript(string $bin_dir): void
|
||||||
|
{
|
||||||
|
$script_path = __DIR__ . '/../scripts/zig-cc.sh';
|
||||||
|
$script_content = file_get_contents($script_path);
|
||||||
|
|
||||||
|
file_put_contents("{$bin_dir}/zig-cc", $script_content);
|
||||||
|
chmod("{$bin_dir}/zig-cc", 0755);
|
||||||
|
|
||||||
|
$script_content = str_replace('zig cc', 'zig c++', $script_content);
|
||||||
|
file_put_contents("{$bin_dir}/zig-c++", $script_content);
|
||||||
|
chmod("{$bin_dir}/zig-c++", 0755);
|
||||||
|
}
|
||||||
|
}
|
||||||
55
src/SPC/store/scripts/zig-cc.sh
Normal file
55
src/SPC/store/scripts/zig-cc.sh
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
|
||||||
|
BUILDROOT_ABS="$(realpath "$SCRIPT_DIR/../../buildroot/include" 2>/dev/null || echo "")"
|
||||||
|
PARSED_ARGS=()
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-isystem)
|
||||||
|
shift
|
||||||
|
ARG="$1"
|
||||||
|
[[ -n "$ARG" ]] && shift || break
|
||||||
|
ARG_ABS="$(realpath "$ARG" 2>/dev/null || echo "")"
|
||||||
|
if [[ -n "$ARG_ABS" && "$ARG_ABS" == "$BUILDROOT_ABS" ]]; then
|
||||||
|
PARSED_ARGS+=("-I$ARG")
|
||||||
|
else
|
||||||
|
PARSED_ARGS+=("-isystem" "$ARG")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
-isystem*)
|
||||||
|
ARG="${1#-isystem}"
|
||||||
|
shift
|
||||||
|
ARG_ABS="$(realpath "$ARG" 2>/dev/null || echo "")"
|
||||||
|
if [[ -n "$ARG_ABS" && "$ARG_ABS" == "$BUILDROOT_ABS" ]]; then
|
||||||
|
PARSED_ARGS+=("-I$ARG")
|
||||||
|
else
|
||||||
|
PARSED_ARGS+=("-isystem$ARG")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
PARSED_ARGS+=("$1")
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
TARGET=""
|
||||||
|
if [ -n "$SPC_TARGET" ]; then
|
||||||
|
TARGET="-target $SPC_TARGET"
|
||||||
|
fi
|
||||||
|
|
||||||
|
output=$(zig cc $TARGET -lstdc++ $COMPILER_EXTRA "${PARSED_ARGS[@]}" 2>&1)
|
||||||
|
status=$?
|
||||||
|
|
||||||
|
if [ $status -eq 0 ]; then
|
||||||
|
echo "$output"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$output" | grep -q "version '.*' in target triple"; then
|
||||||
|
echo "$output" | grep -v "version '.*' in target triple"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
exec zig cc $TARGET $COMPILER_EXTRA "${PARSED_ARGS[@]}"
|
||||||
|
fi
|
||||||
33
src/SPC/toolchain/ClangNativeToolchain.php
Normal file
33
src/SPC/toolchain/ClangNativeToolchain.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\toolchain;
|
||||||
|
|
||||||
|
use SPC\builder\freebsd\SystemUtil as FreeBSDSystemUtil;
|
||||||
|
use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
|
||||||
|
use SPC\builder\macos\SystemUtil as MacOSSystemUtil;
|
||||||
|
use SPC\exception\WrongUsageException;
|
||||||
|
use SPC\util\GlobalEnvManager;
|
||||||
|
|
||||||
|
class ClangNativeToolchain implements ToolchainInterface
|
||||||
|
{
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function afterInit(): void
|
||||||
|
{
|
||||||
|
// check clang exists
|
||||||
|
match (PHP_OS_FAMILY) {
|
||||||
|
'Linux' => LinuxSystemUtil::findCommand('clang++') ?? throw new WrongUsageException('Clang++ not found, please install it or manually set CC/CXX to a valid path.'),
|
||||||
|
'Darwin' => MacOSSystemUtil::findCommand('clang++') ?? throw new WrongUsageException('Clang++ not found, please install it or set CC/CXX to a valid path.'),
|
||||||
|
'BSD' => FreeBSDSystemUtil::findCommand('clang++') ?? throw new WrongUsageException('Clang++ not found, please install it or set CC/CXX to a valid path.'),
|
||||||
|
default => throw new WrongUsageException('Clang is not supported on ' . PHP_OS_FAMILY . '.'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/SPC/toolchain/GccNativeToolchain.php
Normal file
33
src/SPC/toolchain/GccNativeToolchain.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\toolchain;
|
||||||
|
|
||||||
|
use SPC\builder\freebsd\SystemUtil as FreeBSDSystemUtil;
|
||||||
|
use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
|
||||||
|
use SPC\builder\macos\SystemUtil as MacOSSystemUtil;
|
||||||
|
use SPC\exception\WrongUsageException;
|
||||||
|
use SPC\util\GlobalEnvManager;
|
||||||
|
|
||||||
|
class GccNativeToolchain implements ToolchainInterface
|
||||||
|
{
|
||||||
|
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.gold');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function afterInit(): void
|
||||||
|
{
|
||||||
|
// check gcc exists
|
||||||
|
match (PHP_OS_FAMILY) {
|
||||||
|
'Linux' => LinuxSystemUtil::findCommand('g++') ?? throw new WrongUsageException('g++ not found, please install it or set CC/CXX to a valid path.'),
|
||||||
|
'Darwin' => MacOSSystemUtil::findCommand('g++') ?? throw new WrongUsageException('g++ not found, please install it or set CC/CXX to a valid path.'),
|
||||||
|
'BSD' => FreeBSDSystemUtil::findCommand('g++') ?? throw new WrongUsageException('g++ not found, please install it or set CC/CXX to a valid path.'),
|
||||||
|
default => throw new \RuntimeException('GCC is not supported on ' . PHP_OS_FAMILY . '.'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/SPC/toolchain/MSVCToolchain.php
Normal file
12
src/SPC/toolchain/MSVCToolchain.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\toolchain;
|
||||||
|
|
||||||
|
class MSVCToolchain implements ToolchainInterface
|
||||||
|
{
|
||||||
|
public function initEnv(): void {}
|
||||||
|
|
||||||
|
public function afterInit(): void {}
|
||||||
|
}
|
||||||
39
src/SPC/toolchain/MuslToolchain.php
Normal file
39
src/SPC/toolchain/MuslToolchain.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\toolchain;
|
||||||
|
|
||||||
|
use SPC\exception\WrongUsageException;
|
||||||
|
use SPC\util\GlobalEnvManager;
|
||||||
|
|
||||||
|
class MuslToolchain implements ToolchainInterface
|
||||||
|
{
|
||||||
|
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::addPathIfNotExists('/usr/local/musl/bin');
|
||||||
|
GlobalEnvManager::addPathIfNotExists("/usr/local/musl/{$arch}-linux-musl/bin");
|
||||||
|
|
||||||
|
GlobalEnvManager::putenv("SPC_LD_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib");
|
||||||
|
GlobalEnvManager::putenv("SPC_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function afterInit(): void
|
||||||
|
{
|
||||||
|
$arch = getenv('GNU_ARCH');
|
||||||
|
// append LD_LIBRARY_PATH to $configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
|
||||||
|
$configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
|
||||||
|
$ld_library_path = getenv('SPC_LD_LIBRARY_PATH');
|
||||||
|
GlobalEnvManager::putenv("SPC_CMD_PREFIX_PHP_CONFIGURE=LD_LIBRARY_PATH=\"{$ld_library_path}\" {$configure}");
|
||||||
|
|
||||||
|
if (!file_exists("/usr/local/musl/{$arch}-linux-musl/lib/libc.a")) {
|
||||||
|
throw new WrongUsageException('You are building with musl-libc target in glibc distro, but musl-toolchain is not installed, please install musl-toolchain first. (You can use `doctor` command to install it)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/SPC/toolchain/ToolchainInterface.php
Normal file
18
src/SPC/toolchain/ToolchainInterface.php
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\toolchain;
|
||||||
|
|
||||||
|
interface ToolchainInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Initialize the environment for the given target.
|
||||||
|
*/
|
||||||
|
public function initEnv(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform actions after the environment has been initialized for the given target.
|
||||||
|
*/
|
||||||
|
public function afterInit(): void;
|
||||||
|
}
|
||||||
56
src/SPC/toolchain/ToolchainManager.php
Normal file
56
src/SPC/toolchain/ToolchainManager.php
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\toolchain;
|
||||||
|
|
||||||
|
use SPC\builder\linux\SystemUtil;
|
||||||
|
use SPC\exception\WrongUsageException;
|
||||||
|
use SPC\util\GlobalEnvManager;
|
||||||
|
|
||||||
|
class ToolchainManager
|
||||||
|
{
|
||||||
|
public const array OS_DEFAULT_TOOLCHAIN = [
|
||||||
|
'Linux' => ZigToolchain::class,
|
||||||
|
'Windows' => MSVCToolchain::class,
|
||||||
|
'Darwin' => ClangNativeToolchain::class,
|
||||||
|
'BSD' => ClangNativeToolchain::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
public static function getToolchainClass(): string
|
||||||
|
{
|
||||||
|
$libc = getenv('SPC_LIBC');
|
||||||
|
if ($libc && !getenv('SPC_TARGET')) {
|
||||||
|
// TODO: @crazywhalecc this breaks tests
|
||||||
|
// logger()->warning('SPC_LIBC is deprecated, please use SPC_TARGET instead.');
|
||||||
|
return match ($libc) {
|
||||||
|
'musl' => SystemUtil::isMuslDist() ? GccNativeToolchain::class : MuslToolchain::class,
|
||||||
|
'glibc' => !SystemUtil::isMuslDist() ? GccNativeToolchain::class : throw new WrongUsageException('SPC_LIBC must be musl for musl dist.'),
|
||||||
|
default => throw new WrongUsageException('Unsupported SPC_LIBC value: ' . $libc),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::OS_DEFAULT_TOOLCHAIN[PHP_OS_FAMILY];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws WrongUsageException
|
||||||
|
*/
|
||||||
|
public static function initToolchain(): void
|
||||||
|
{
|
||||||
|
$toolchainClass = self::getToolchainClass();
|
||||||
|
/* @var ToolchainInterface $toolchainClass */
|
||||||
|
(new $toolchainClass())->initEnv();
|
||||||
|
GlobalEnvManager::putenv("SPC_TOOLCHAIN={$toolchainClass}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function afterInitToolchain(): void
|
||||||
|
{
|
||||||
|
if (!getenv('SPC_TOOLCHAIN')) {
|
||||||
|
throw new WrongUsageException('SPC_TOOLCHAIN not set');
|
||||||
|
}
|
||||||
|
$toolchain = getenv('SPC_TOOLCHAIN');
|
||||||
|
/* @var ToolchainInterface $toolchain */
|
||||||
|
(new $toolchain())->afterInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/SPC/toolchain/ZigToolchain.php
Normal file
29
src/SPC/toolchain/ZigToolchain.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\toolchain;
|
||||||
|
|
||||||
|
use SPC\exception\WrongUsageException;
|
||||||
|
use SPC\store\pkg\Zig;
|
||||||
|
use SPC\util\GlobalEnvManager;
|
||||||
|
|
||||||
|
class ZigToolchain implements ToolchainInterface
|
||||||
|
{
|
||||||
|
public function initEnv(): void
|
||||||
|
{
|
||||||
|
// Set environment variables for zig toolchain
|
||||||
|
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=zig-cc');
|
||||||
|
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=zig-c++');
|
||||||
|
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=ar');
|
||||||
|
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function afterInit(): void
|
||||||
|
{
|
||||||
|
if (!is_dir(Zig::getEnvironment()['PATH'])) {
|
||||||
|
throw new WrongUsageException('You are building with zig, but zig is not installed, please install zig first. (You can use `doctor` command to install it)');
|
||||||
|
}
|
||||||
|
GlobalEnvManager::addPathIfNotExists(Zig::getEnvironment()['PATH']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -164,6 +164,12 @@ class ConfigValidator
|
|||||||
if (is_string($craft['extensions'])) {
|
if (is_string($craft['extensions'])) {
|
||||||
$craft['extensions'] = array_filter(array_map(fn ($x) => trim($x), explode(',', $craft['extensions'])));
|
$craft['extensions'] = array_filter(array_map(fn ($x) => trim($x), explode(',', $craft['extensions'])));
|
||||||
}
|
}
|
||||||
|
if (!isset($craft['shared-extensions'])) {
|
||||||
|
$craft['shared-extensions'] = [];
|
||||||
|
}
|
||||||
|
if (is_string($craft['shared-extensions'] ?? [])) {
|
||||||
|
$craft['shared-extensions'] = array_filter(array_map(fn ($x) => trim($x), explode(',', $craft['shared-extensions'])));
|
||||||
|
}
|
||||||
// check libs
|
// check libs
|
||||||
if (isset($craft['libs']) && is_string($craft['libs'])) {
|
if (isset($craft['libs']) && is_string($craft['libs'])) {
|
||||||
$craft['libs'] = array_filter(array_map(fn ($x) => trim($x), explode(',', $craft['libs'])));
|
$craft['libs'] = array_filter(array_map(fn ($x) => trim($x), explode(',', $craft['libs'])));
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace SPC\util;
|
namespace SPC\util;
|
||||||
|
|
||||||
use SPC\builder\BuilderBase;
|
|
||||||
use SPC\builder\linux\SystemUtil;
|
|
||||||
use SPC\exception\RuntimeException;
|
use SPC\exception\RuntimeException;
|
||||||
use SPC\exception\WrongUsageException;
|
use SPC\exception\WrongUsageException;
|
||||||
|
use SPC\toolchain\ToolchainManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Environment variable manager
|
* Environment variable manager
|
||||||
@@ -24,11 +23,10 @@ class GlobalEnvManager
|
|||||||
/**
|
/**
|
||||||
* Initialize the environment variables
|
* Initialize the environment variables
|
||||||
*
|
*
|
||||||
* @param null|BuilderBase $builder Builder
|
|
||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
* @throws WrongUsageException
|
* @throws WrongUsageException
|
||||||
*/
|
*/
|
||||||
public static function init(?BuilderBase $builder = null): void
|
public static function init(): void
|
||||||
{
|
{
|
||||||
// Check pre-defined env vars exists
|
// Check pre-defined env vars exists
|
||||||
if (getenv('BUILD_ROOT_PATH') === false) {
|
if (getenv('BUILD_ROOT_PATH') === false) {
|
||||||
@@ -37,25 +35,89 @@ class GlobalEnvManager
|
|||||||
|
|
||||||
// Define env vars for unix
|
// Define env vars for unix
|
||||||
if (is_unix()) {
|
if (is_unix()) {
|
||||||
self::putenv('PATH=' . BUILD_ROOT_PATH . '/bin:' . getenv('PATH'));
|
self::addPathIfNotExists(BUILD_BIN_PATH);
|
||||||
self::putenv('PKG_CONFIG=' . BUILD_BIN_PATH . '/pkg-config');
|
self::putenv('PKG_CONFIG=' . BUILD_BIN_PATH . '/pkg-config');
|
||||||
self::putenv('PKG_CONFIG_PATH=' . BUILD_ROOT_PATH . '/lib/pkgconfig');
|
self::putenv('PKG_CONFIG_PATH=' . BUILD_ROOT_PATH . '/lib/pkgconfig');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define env vars for linux
|
$ini = self::readIniFile();
|
||||||
if (PHP_OS_FAMILY === 'Linux') {
|
|
||||||
$arch = getenv('GNU_ARCH');
|
$default_put_list = [];
|
||||||
if (SystemUtil::isMuslDist()) {
|
foreach ($ini['global'] as $k => $v) {
|
||||||
self::putenv('SPC_LINUX_DEFAULT_CC=gcc');
|
if (getenv($k) === false) {
|
||||||
self::putenv('SPC_LINUX_DEFAULT_CXX=g++');
|
$default_put_list[$k] = $v;
|
||||||
self::putenv('SPC_LINUX_DEFAULT_AR=ar');
|
self::putenv("{$k}={$v}");
|
||||||
} else {
|
}
|
||||||
self::putenv("SPC_LINUX_DEFAULT_CC={$arch}-linux-musl-gcc");
|
}
|
||||||
self::putenv("SPC_LINUX_DEFAULT_CXX={$arch}-linux-musl-g++");
|
$os_ini = match (PHP_OS_FAMILY) {
|
||||||
self::putenv("SPC_LINUX_DEFAULT_AR={$arch}-linux-musl-ar");
|
'Windows' => $ini['windows'] ?? [],
|
||||||
|
'Darwin' => $ini['macos'] ?? [],
|
||||||
|
'Linux' => $ini['linux'] ?? [],
|
||||||
|
'BSD' => $ini['freebsd'] ?? [],
|
||||||
|
default => [],
|
||||||
|
};
|
||||||
|
foreach ($os_ini as $k => $v) {
|
||||||
|
if (getenv($k) === false) {
|
||||||
|
$default_put_list[$k] = $v;
|
||||||
|
self::putenv("{$k}={$v}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ToolchainManager::initToolchain();
|
||||||
|
|
||||||
|
// apply second time
|
||||||
|
$ini2 = self::readIniFile();
|
||||||
|
|
||||||
|
foreach ($ini2['global'] as $k => $v) {
|
||||||
|
if (isset($default_put_list[$k]) && $default_put_list[$k] !== $v) {
|
||||||
|
self::putenv("{$k}={$v}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$os_ini2 = match (PHP_OS_FAMILY) {
|
||||||
|
'Windows' => $ini2['windows'] ?? [],
|
||||||
|
'Darwin' => $ini2['macos'] ?? [],
|
||||||
|
'Linux' => $ini2['linux'] ?? [],
|
||||||
|
'BSD' => $ini2['freebsd'] ?? [],
|
||||||
|
default => [],
|
||||||
|
};
|
||||||
|
foreach ($os_ini2 as $k => $v) {
|
||||||
|
if (isset($default_put_list[$k]) && $default_put_list[$k] !== $v) {
|
||||||
|
self::putenv("{$k}={$v}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function putenv(string $val): void
|
||||||
|
{
|
||||||
|
f_putenv($val);
|
||||||
|
self::$env_cache[] = $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function addPathIfNotExists(string $path): void
|
||||||
|
{
|
||||||
|
if (is_unix() && !str_contains(getenv('PATH'), $path)) {
|
||||||
|
self::putenv("PATH={$path}:" . getenv('PATH'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the toolchain after the environment variables are set.
|
||||||
|
* The toolchain or environment availability check is done here.
|
||||||
|
*
|
||||||
|
* @throws WrongUsageException
|
||||||
|
*/
|
||||||
|
public static function afterInit(): void
|
||||||
|
{
|
||||||
|
if (!filter_var(getenv('SPC_SKIP_TOOLCHAIN_CHECK'), FILTER_VALIDATE_BOOL)) {
|
||||||
|
ToolchainManager::afterInitToolchain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws WrongUsageException
|
||||||
|
*/
|
||||||
|
private static function readIniFile(): array
|
||||||
|
{
|
||||||
// Init env.ini file, read order:
|
// Init env.ini file, read order:
|
||||||
// WORKING_DIR/config/env.ini
|
// WORKING_DIR/config/env.ini
|
||||||
// ROOT_DIR/config/env.ini
|
// ROOT_DIR/config/env.ini
|
||||||
@@ -97,28 +159,6 @@ class GlobalEnvManager
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self::applyConfig($ini['global']);
|
return $ini;
|
||||||
match (PHP_OS_FAMILY) {
|
|
||||||
'Windows' => self::applyConfig($ini['windows']),
|
|
||||||
'Darwin' => self::applyConfig($ini['macos']),
|
|
||||||
'Linux' => self::applyConfig($ini['linux']),
|
|
||||||
'BSD' => self::applyConfig($ini['freebsd']),
|
|
||||||
default => null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function putenv(string $val): void
|
|
||||||
{
|
|
||||||
f_putenv($val);
|
|
||||||
self::$env_cache[] = $val;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function applyConfig(array $ini): void
|
|
||||||
{
|
|
||||||
foreach ($ini as $k => $v) {
|
|
||||||
if (getenv($k) === false) {
|
|
||||||
self::putenv($k . '=' . $v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user