mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-03 06:45:39 +08:00
Compare commits
291 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90981e3877 | ||
|
|
d69826eb4a | ||
|
|
dc83282019 | ||
|
|
23c0d6f4aa | ||
|
|
8e4d4b7be5 | ||
|
|
ee906aaff9 | ||
|
|
e2b80e7f03 | ||
|
|
09073c5517 | ||
|
|
00050f4d0e | ||
|
|
cff6ec17ea | ||
|
|
64079d9331 | ||
|
|
541889d17b | ||
|
|
c91128995d | ||
|
|
fd2b7af1dc | ||
|
|
081e2d2846 | ||
|
|
9edb9417a1 | ||
|
|
4ae4165ba2 | ||
|
|
e441a575ea | ||
|
|
1575016885 | ||
|
|
987ad4b846 | ||
|
|
f4b03ae835 | ||
|
|
d6de01d05c | ||
|
|
09b7159119 | ||
|
|
4198ddd5d1 | ||
|
|
8332ed87e0 | ||
|
|
f6b091498f | ||
|
|
a45f314447 | ||
|
|
8c8cb70174 | ||
|
|
f09c18e78f | ||
|
|
f5d93d2f54 | ||
|
|
7402fbf7c1 | ||
|
|
2c590e5895 | ||
|
|
463a98b1bf | ||
|
|
6d1c6d7f61 | ||
|
|
08362fb6e5 | ||
|
|
4d5641f6ec | ||
|
|
f34ecf9468 | ||
|
|
aa5c829fae | ||
|
|
fc118d709e | ||
|
|
589a4a9803 | ||
|
|
944e314bab | ||
|
|
0b17ce9e61 | ||
|
|
ed4978bb89 | ||
|
|
7a4f28e939 | ||
|
|
5cb6a75e7d | ||
|
|
757af25d8f | ||
|
|
5e3e7eccbf | ||
|
|
9738fcd6cd | ||
|
|
5a6a33303c | ||
|
|
ae15d6c5f5 | ||
|
|
ff15973a25 | ||
|
|
b88a68dab8 | ||
|
|
b05bdcd83d | ||
|
|
6a4ad34324 | ||
|
|
9a2d94cc33 | ||
|
|
f1d1d4fe10 | ||
|
|
fa6fa1c425 | ||
|
|
f8c8300c9c | ||
|
|
09198b431f | ||
|
|
f426ced789 | ||
|
|
bab330b64e | ||
|
|
b59a06face | ||
|
|
bb44e88c3b | ||
|
|
8649068159 | ||
|
|
5476385553 | ||
|
|
56bac35768 | ||
|
|
4cdc6a07ae | ||
|
|
c30b34ae5c | ||
|
|
da5c6fd084 | ||
|
|
a4b6499530 | ||
|
|
eb4445ea59 | ||
|
|
be51bcfdfc | ||
|
|
7bc4131c02 | ||
|
|
da8debdade | ||
|
|
ef5e664981 | ||
|
|
8e50af3a7e | ||
|
|
8e96c64918 | ||
|
|
8a9c8a279d | ||
|
|
72ca0cecd1 | ||
|
|
3564f6d0a7 | ||
|
|
d3e2b4b5b2 | ||
|
|
c6de6e7056 | ||
|
|
55322a282c | ||
|
|
6789ea81ff | ||
|
|
ac8a9af89c | ||
|
|
553b817b2a | ||
|
|
96592bce3e | ||
|
|
4e393886aa | ||
|
|
2e13be2a7a | ||
|
|
41fb29eba4 | ||
|
|
b519291fa2 | ||
|
|
dd752cd5be | ||
|
|
6a153f9aa0 | ||
|
|
bba390dbcc | ||
|
|
32efeb970c | ||
|
|
310335813f | ||
|
|
487980c9a8 | ||
|
|
f8801e224f | ||
|
|
5e229a0b01 | ||
|
|
49cfcbe92d | ||
|
|
4e4ce282db | ||
|
|
e2fd3e18d6 | ||
|
|
605c06f85c | ||
|
|
4cdefeab81 | ||
|
|
8ab09898f0 | ||
|
|
9c8b4d627c | ||
|
|
8923077120 | ||
|
|
29c31d90e5 | ||
|
|
e281d26a3a | ||
|
|
8ed68f481a | ||
|
|
b1abff61a5 | ||
|
|
15638cea4c | ||
|
|
9ed77c10e0 | ||
|
|
ccf262d202 | ||
|
|
4be894bc10 | ||
|
|
6440863ce4 | ||
|
|
25d7c72b6e | ||
|
|
dc4dd6ffa4 | ||
|
|
bf79134405 | ||
|
|
4f8b9d0f81 | ||
|
|
70bda268e5 | ||
|
|
e559dce9d5 | ||
|
|
6a98a6bf5e | ||
|
|
9b53133ba4 | ||
|
|
5b319b0df1 | ||
|
|
572bf919aa | ||
|
|
1d960a9084 | ||
|
|
4b28d1c2df | ||
|
|
9c8fd4d45d | ||
|
|
b62f029da7 | ||
|
|
c711a3666e | ||
|
|
487c6da4ac | ||
|
|
e942b13d73 | ||
|
|
c5ae719b9c | ||
|
|
7b6e707e24 | ||
|
|
0114700dad | ||
|
|
ec9364db69 | ||
|
|
033e29985e | ||
|
|
61cba2342d | ||
|
|
a3acad4ef3 | ||
|
|
1e13eb4abe | ||
|
|
1bac06fe3c | ||
|
|
762a768969 | ||
|
|
d0a6e3a860 | ||
|
|
995187d258 | ||
|
|
5333a04e1c | ||
|
|
19be5263b5 | ||
|
|
6e79401ab5 | ||
|
|
903036f1c0 | ||
|
|
6bd3eea0fe | ||
|
|
f28a3cf5a3 | ||
|
|
c828c2c6e4 | ||
|
|
26ccaa4449 | ||
|
|
ae1193ab16 | ||
|
|
6d6a29368e | ||
|
|
897cb00351 | ||
|
|
8a1689b79d | ||
|
|
018dfae15e | ||
|
|
c83e803c85 | ||
|
|
fa4d33671d | ||
|
|
f8d77b9b50 | ||
|
|
6594811536 | ||
|
|
cc7eb7cd84 | ||
|
|
e6408b4693 | ||
|
|
330c3486af | ||
|
|
b9dfb5afe3 | ||
|
|
565ac87b65 | ||
|
|
f0c39c1770 | ||
|
|
896cf889e4 | ||
|
|
6b91570054 | ||
|
|
31906b36e5 | ||
|
|
4e2d4f3f05 | ||
|
|
ffdc2dc85a | ||
|
|
911bc74bf4 | ||
|
|
3467c9d291 | ||
|
|
15cd8543f1 | ||
|
|
864db0ebc5 | ||
|
|
9ee623112d | ||
|
|
d789b1a472 | ||
|
|
17a25b44e2 | ||
|
|
781260f3fc | ||
|
|
6c43fa55b1 | ||
|
|
c4440668bb | ||
|
|
e437bf2ffe | ||
|
|
0f2d2d5734 | ||
|
|
a49ae05599 | ||
|
|
8ae2755dbe | ||
|
|
3966bd5f0a | ||
|
|
384ba54c79 | ||
|
|
26dbc922eb | ||
|
|
31a4498056 | ||
|
|
044275d909 | ||
|
|
c5316f9231 | ||
|
|
2d1a61d184 | ||
|
|
f7744188f4 | ||
|
|
5b6c923eef | ||
|
|
b0b031afb2 | ||
|
|
fe09d8c03e | ||
|
|
c2813d5736 | ||
|
|
8b2b658ced | ||
|
|
e862bacc8a | ||
|
|
cac57dbf79 | ||
|
|
345fe783cc | ||
|
|
55efe6d65b | ||
|
|
dccbc30972 | ||
|
|
f914a82379 | ||
|
|
18979d8fb6 | ||
|
|
21149f6807 | ||
|
|
93e6dcab1d | ||
|
|
59dedbea2c | ||
|
|
8bda4fd31e | ||
|
|
8039ea49f9 | ||
|
|
fc90b4ae23 | ||
|
|
e02be69e40 | ||
|
|
35a90f2769 | ||
|
|
fa87149631 | ||
|
|
8dfe722e14 | ||
|
|
dcbfe1e0ab | ||
|
|
e11946fd10 | ||
|
|
44399cd185 | ||
|
|
927d7f55ba | ||
|
|
e323d7b155 | ||
|
|
d0b253c346 | ||
|
|
81430e6853 | ||
|
|
83696e92b7 | ||
|
|
b0538c09bf | ||
|
|
40f89d1dca | ||
|
|
fa2e041cc9 | ||
|
|
3c614663a3 | ||
|
|
b5c7185374 | ||
|
|
d0a9a3a594 | ||
|
|
2e6329bb86 | ||
|
|
d3ba04fc5b | ||
|
|
7a78ea0185 | ||
|
|
36b04f9eba | ||
|
|
79ab6490fd | ||
|
|
191d345250 | ||
|
|
311ee40ae4 | ||
|
|
808d224b08 | ||
|
|
4247883664 | ||
|
|
3198cc40c1 | ||
|
|
8680e83af3 | ||
|
|
0156f33a20 | ||
|
|
df8b2dbf16 | ||
|
|
30e174ac95 | ||
|
|
b9f8f02d98 | ||
|
|
0b0ae270da | ||
|
|
893ce31b17 | ||
|
|
e35d6c2651 | ||
|
|
8dd0512335 | ||
|
|
5c1194ea92 | ||
|
|
953ed83df5 | ||
|
|
c330d02e78 | ||
|
|
af6a23011c | ||
|
|
882ae07deb | ||
|
|
be7c002869 | ||
|
|
e621e4a5a1 | ||
|
|
bd815d4ea2 | ||
|
|
4bc30b0b6f | ||
|
|
4d3501118e | ||
|
|
8d303348d9 | ||
|
|
2a2f4120b9 | ||
|
|
15f8887f14 | ||
|
|
fe945ab3ea | ||
|
|
e2b6f4cedd | ||
|
|
d82c86cf62 | ||
|
|
9df2867175 | ||
|
|
5c803d1553 | ||
|
|
d45b1853f8 | ||
|
|
1ac621fb9c | ||
|
|
4e74ac1937 | ||
|
|
959734ac31 | ||
|
|
166f3de52f | ||
|
|
e3adfff16f | ||
|
|
7cbc374278 | ||
|
|
129041402d | ||
|
|
9ac578dfd6 | ||
|
|
ef709a169b | ||
|
|
2eeba33f94 | ||
|
|
f4b2b9ae7d | ||
|
|
e44efb2a54 | ||
|
|
598f6d55c5 | ||
|
|
a2c5c7e6e6 | ||
|
|
4f6e646812 | ||
|
|
a79564f685 | ||
|
|
a77e49cbc9 | ||
|
|
cefb737fd2 | ||
|
|
8c8aba2dd5 | ||
|
|
97b18e9121 | ||
|
|
ddc9cc2237 | ||
|
|
aff803f334 |
4
.github/workflows/build-unix.yml
vendored
4
.github/workflows/build-unix.yml
vendored
@@ -136,12 +136,12 @@ jobs:
|
||||
macos-x86_64)
|
||||
DOWN_CMD="composer update --no-dev --classmap-authoritative && ./bin/spc doctor --auto-fix && ./bin/spc download"
|
||||
BUILD_CMD="./bin/spc build"
|
||||
RUNS_ON="macos-13"
|
||||
RUNS_ON="macos-15-intel"
|
||||
;;
|
||||
macos-aarch64)
|
||||
DOWN_CMD="composer update --no-dev --classmap-authoritative && ./bin/spc doctor --auto-fix && ./bin/spc download"
|
||||
BUILD_CMD="./bin/spc build"
|
||||
RUNS_ON="macos-14"
|
||||
RUNS_ON="macos-15"
|
||||
;;
|
||||
esac
|
||||
DOWN_CMD="$DOWN_CMD --with-php=${{ inputs.php-version }} --for-extensions=${{ inputs.extensions }} --ignore-cache-sources=php-src"
|
||||
|
||||
8
.github/workflows/ext-matrix-tests.yml
vendored
8
.github/workflows/ext-matrix-tests.yml
vendored
@@ -85,9 +85,9 @@ jobs:
|
||||
- "8.4"
|
||||
operating-system:
|
||||
- "ubuntu-latest"
|
||||
#- "macos-13"
|
||||
#- "macos-15-intel"
|
||||
#- "debian-arm64-self-hosted"
|
||||
- "macos-14"
|
||||
- "macos-15"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
@@ -99,11 +99,11 @@ jobs:
|
||||
OS=""
|
||||
if [ "${{ matrix.operating-system }}" = "ubuntu-latest" ]; then
|
||||
OS="linux-x86_64"
|
||||
elif [ "${{ matrix.operating-system }}" = "macos-13" ]; then
|
||||
elif [ "${{ matrix.operating-system }}" = "macos-15-intel" ]; then
|
||||
OS="macos-x86_64"
|
||||
elif [ "${{ matrix.operating-system }}" = "debian-arm64-self-hosted" ]; then
|
||||
OS="linux-aarch64"
|
||||
elif [ "${{ matrix.operating-system }}" = "macos-14" ]; then
|
||||
elif [ "${{ matrix.operating-system }}" = "macos-15" ]; then
|
||||
OS="macos-aarch64"
|
||||
fi
|
||||
echo "OS=$OS" >> $GITHUB_ENV
|
||||
|
||||
6
.github/workflows/release-build.yml
vendored
6
.github/workflows/release-build.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
os: "ubuntu-latest"
|
||||
filename: "spc-linux-x86_64.tar.gz"
|
||||
- name: "macos-x86_64"
|
||||
os: "macos-13"
|
||||
os: "macos-15-intel"
|
||||
filename: "spc-macos-x86_64.tar.gz"
|
||||
- name: "linux-aarch64"
|
||||
os: "ubuntu-latest"
|
||||
@@ -147,11 +147,11 @@ jobs:
|
||||
- name: "linux-x86_64"
|
||||
os: "ubuntu-latest"
|
||||
- name: "macos-x86_64"
|
||||
os: "macos-13"
|
||||
os: "macos-15-intel"
|
||||
- name: "linux-aarch64"
|
||||
os: "ubuntu-24.04-arm"
|
||||
- name: "macos-aarch64"
|
||||
os: "macos-latest"
|
||||
os: "macos-15"
|
||||
- name: "windows-x64"
|
||||
os: "windows-latest"
|
||||
steps:
|
||||
|
||||
8
.github/workflows/tests.yml
vendored
8
.github/workflows/tests.yml
vendored
@@ -1,13 +1,9 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'src/globals/test-extensions.php'
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
types: [ opened, synchronize, reopened ]
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'config/**'
|
||||
@@ -179,7 +175,7 @@ jobs:
|
||||
key: php-dependencies-${{ matrix.os }}
|
||||
|
||||
- name: "Install Dependencies"
|
||||
run: composer update -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
|
||||
run: composer update -vvv --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist --no-plugins
|
||||
|
||||
- name: "Run Build Tests (doctor)"
|
||||
run: php src/globals/test-extensions.php doctor_cmd ${{ matrix.os }} ${{ matrix.php }}
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -22,6 +22,9 @@ docker/source/
|
||||
# default package root directory
|
||||
/pkgroot/**
|
||||
|
||||
# Windows PHP SDK binary tools
|
||||
/php-sdk-binary-tools/**
|
||||
|
||||
# default pack:lib and release directory
|
||||
/dist/**
|
||||
packlib_files.txt
|
||||
@@ -34,6 +37,7 @@ packlib_files.txt
|
||||
/bin/*
|
||||
!/bin/spc*
|
||||
!/bin/setup-runtime*
|
||||
!/bin/docker-entrypoint.sh
|
||||
|
||||
# exclude windows build tools
|
||||
/php-sdk-binary-tools/
|
||||
|
||||
343
README-zh.md
343
README-zh.md
@@ -5,309 +5,168 @@
|
||||
[](https://github.com/crazywhalecc/static-php-cli/releases)
|
||||
[](https://github.com/crazywhalecc/static-php-cli/actions/workflows/tests.yml)
|
||||
[](https://github.com/crazywhalecc/static-php-cli/blob/main/LICENSE)
|
||||
[](https://static-php.dev/zh/guide/extensions.html)
|
||||
|
||||
**static-php-cli**是一个用于静态编译、构建 PHP 解释器的工具,支持众多流行扩展。
|
||||
|
||||
目前 static-php-cli 支持 `cli`、`fpm`、`embed`、`micro` 和 `frankenphp` SAPI。
|
||||
|
||||
**static-php-cli**也支持将 PHP 代码和 PHP 运行时打包为一个文件并运行。
|
||||
**static-php-cli** 是一个用于构建静态、独立 PHP 运行时的强大工具,支持众多流行扩展。
|
||||
|
||||
## 特性
|
||||
|
||||
static-php-cli(简称 `spc`)有许多特性:
|
||||
- :elephant: **支持多 PHP 版本** - 支持 PHP 8.1, 8.2, 8.3, 8.4, 8.5
|
||||
- :handbag: **单文件 PHP 可执行文件** - 构建零依赖的独立 PHP
|
||||
- :hamburger: **phpmicro 集成** - 构建 **[phpmicro](https://github.com/dixyes/phpmicro)** 自解压可执行文件(将 PHP 二进制文件和源代码合并为一个文件)
|
||||
- :pill: **智能环境检查器** - 自动构建环境检查器,具备自动修复功能
|
||||
- :zap: **跨平台支持** - 支持 Linux、macOS、FreeBSD 和 Windows
|
||||
- :wrench: **可配置补丁** - 可自定义的源代码补丁系统
|
||||
- :books: **智能依赖管理** - 自动处理构建依赖
|
||||
- 📦 **自包含工具** - 提供使用 [box](https://github.com/box-project/box) 构建的 `spc` 可执行文件
|
||||
- :fire: **广泛的扩展支持** - 支持 75+ 流行 [扩展](https://static-php.dev/zh/guide/extensions.html)
|
||||
- :floppy_disk: **UPX 压缩** - 减小二进制文件大小 30-50%(仅 Linux/Windows)
|
||||
|
||||
- :handbag: 构建独立的单文件 PHP 解释器,无需任何依赖
|
||||
- :hamburger: 构建 **[phpmicro](https://github.com/dixyes/phpmicro)** 自执行二进制(将 PHP 代码和 PHP 解释器打包为一个文件)
|
||||
- :pill: 提供一键检查和修复编译环境的 Doctor 模块
|
||||
- :zap: 支持多个系统:`Linux`、`macOS`、`FreeBSD`、`Windows`
|
||||
- :wrench: 高度自定义的代码 patch 功能
|
||||
- :books: 自带编译依赖管理
|
||||
- 📦 提供由自身编译的独立 `spc` 二进制(使用 spc 和 [box](https://github.com/box-project/box) 构建)
|
||||
- :fire: 支持大量 [扩展](https://static-php.dev/zh/guide/extensions.html)
|
||||
- :floppy_disk: 整合 UPX 工具(减小二进制文件体积)
|
||||
|
||||
**静态 php-cli:**
|
||||
**单文件独立 php-cli:**
|
||||
|
||||
<img width="700" alt="out1" src="https://github.com/crazywhalecc/static-php-cli/assets/20330940/01a2e60f-13b0-4242-a645-f7afa4936396">
|
||||
|
||||
**使用 phpmicro 打包 PHP 代码:**
|
||||
**使用 phpmicro 将 PHP 代码与 PHP 解释器结合:**
|
||||
|
||||
<img width="700" alt="out2" src="https://github.com/crazywhalecc/static-php-cli/assets/20330940/46b7128d-fb72-4169-957e-48564c3ff3e2">
|
||||
|
||||
## 文档
|
||||
## 快速开始
|
||||
|
||||
目前 README 编写了基本用法。有关 static-php-cli 所有的功能,请点击这里查看文档:<https://static-php.dev>。
|
||||
|
||||
## 直接下载
|
||||
|
||||
如果你不想自行编译 PHP,可以从本项目现有的示例 Action 下载 Artifact,也可以从自托管的服务器下载。
|
||||
|
||||
| 组合名称 | 组合扩展数 | 系统 | 备注 |
|
||||
|---------------------------------------------------------------------|----------------------------------------------------------------------------|-------------|--------------|
|
||||
| [common](https://dl.static-php.dev/static-php-cli/common/) | [30+](https://dl.static-php.dev/static-php-cli/common/README.txt) | Linux/macOS | 体积为 7.5MB 左右 |
|
||||
| [bulk](https://dl.static-php.dev/static-php-cli/bulk/) | [50+](https://dl.static-php.dev/static-php-cli/bulk/README.txt) | Linux/macOS | 体积为 25MB 左右 |
|
||||
| [minimal](https://dl.static-php.dev/static-php-cli/minimal/) | [5](https://dl.static-php.dev/static-php-cli/minimal/README.txt) | Linux/macOS | 体积为 3MB 左右 |
|
||||
| [spc-min](https://dl.static-php.dev/static-php-cli/windows/spc-min) | [5](https://dl.static-php.dev/static-php-cli/windows/spc-min/README.txt) | Windows | 体积为 3MB 左右 |
|
||||
| [spc-max](https://dl.static-php.dev/static-php-cli/windows/spc-max) | [40+](https://dl.static-php.dev/static-php-cli/windows/spc-max/README.txt) | Windows | 体积为 8.5MB 左右 |
|
||||
|
||||
> Linux 和 Windows 默认启用了 UPX 压缩,可减小 30~50% 的 PHP 二进制体积。
|
||||
> macOS 当前不支持 UPX,所以上述预编译的 macOS 版本体积可能较大。
|
||||
|
||||
## 使用 static-php-cli 构建 PHP
|
||||
|
||||
### 编译环境需求
|
||||
|
||||
- PHP >= 8.4(这是 spc 自身需要的版本,不是支持的构建版本)
|
||||
- 扩展:`mbstring,tokenizer,phar`
|
||||
- 系统安装了 `curl` 和 `git`
|
||||
|
||||
是的,本项目采用 PHP 编写,编译前需要一个 PHP 环境,比较滑稽。
|
||||
但本项目默认可通过自身构建的 micro 和 static-php 二进制运行,其他只需要包含上面提到的扩展和 PHP 版本大于等于 8.1 即可。
|
||||
|
||||
下面是架构支持情况,:octocat: 代表支持 GitHub Action 构建,:computer: 代表支持本地构建,空 代表暂不支持。
|
||||
|
||||
| | x86_64 | aarch64 |
|
||||
|---------|----------------------|----------------------|
|
||||
| macOS | :octocat: :computer: | :octocat: :computer: |
|
||||
| Linux | :octocat: :computer: | :octocat: :computer: |
|
||||
| Windows | :octocat: :computer: | |
|
||||
| FreeBSD | :computer: | :computer: |
|
||||
|
||||
当前支持编译的 PHP 版本:
|
||||
|
||||
> :warning: 部分支持,对于新的测试版和旧版本可能存在问题。
|
||||
>
|
||||
> :heavy_check_mark: 支持
|
||||
>
|
||||
> :x: 不支持
|
||||
|
||||
| PHP Version | Status | Comment |
|
||||
|-------------|--------------------|---------------------------------------------------------|
|
||||
| 7.2 | :x: | |
|
||||
| 7.3 | :x: | phpmicro 和许多扩展不支持 7.3、7.4 版本 |
|
||||
| 7.4 | :x: | phpmicro 和许多扩展不支持 7.3、7.4 版本 |
|
||||
| 8.0 | :warning: | PHP 官方已停止 8.0 的维护,我们不再处理 8.0 相关的 backport 支持 |
|
||||
| 8.1 | :heavy_check_mark: | PHP 官方仅对 8.1 提供安全更新,在 8.5 发布后我们不再处理 8.1 相关的 backport 支持 |
|
||||
| 8.2 | :heavy_check_mark: | |
|
||||
| 8.3 | :heavy_check_mark: | |
|
||||
| 8.4 | :heavy_check_mark: | |
|
||||
| 8.5 (alpha) | :warning: | PHP 8.5 目前处于 alpha 阶段 |
|
||||
|
||||
> 这个表格的支持状态是 static-php-cli 对构建对应版本的支持情况,不是 PHP 官方对该版本的支持情况。
|
||||
|
||||
### 支持的扩展
|
||||
|
||||
请先根据下方扩展列表选择你要编译的扩展。
|
||||
|
||||
- [扩展支持列表](https://static-php.dev/zh/guide/extensions.html)
|
||||
- [编译命令生成器](https://static-php.dev/zh/guide/cli-generator.html)
|
||||
|
||||
> 如果这里没有你需要的扩展,可以提交 Issue。
|
||||
|
||||
### 在线构建(使用 GitHub Actions)
|
||||
|
||||
使用 GitHub Action 可以方便地构建一个静态编译的 PHP,同时可以自行定义要编译的扩展。
|
||||
|
||||
1. Fork 本项目。
|
||||
2. 进入项目的 Actions,选择 CI。
|
||||
3. 选择 `Run workflow`,填入你要编译的 PHP 版本、目标类型、扩展列表。(扩展列表使用英文逗号分割,例如 `bcmath,curl,mbstring`)
|
||||
4. 等待大约一段时间后,进入对应的任务中,获取 `Artifacts`。
|
||||
|
||||
如果你选择了 `debug`,则会在构建时输出所有日志,包括编译的日志,以供排查错误。
|
||||
|
||||
### 本地构建(使用 spc 二进制,推荐)
|
||||
|
||||
该项目提供了 static-php-cli 的二进制文件:`spc`。
|
||||
您可以使用 `spc` 二进制文件,无需安装任何运行时(用起来就像 golang 程序)。
|
||||
目前,`spc` 二进制文件提供的平台有 Linux 和 macOS。
|
||||
|
||||
使用以下命令从自托管服务器下载:
|
||||
### 1. 下载 spc 二进制文件
|
||||
|
||||
```bash
|
||||
# Download from self-hosted nightly builds (sync with main branch)
|
||||
# For Linux x86_64
|
||||
# Linux x86_64
|
||||
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-x86_64
|
||||
# For Linux aarch64
|
||||
# Linux aarch64
|
||||
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-aarch64
|
||||
# macOS x86_64 (Intel)
|
||||
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-x86_64
|
||||
# macOS aarch64 (Apple)
|
||||
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-aarch64
|
||||
# Windows (x86_64, win10 build 17063 or later)
|
||||
# Windows (x86_64, win10 build 17063 或更高版本,请先安装 VS2022)
|
||||
curl.exe -fsSL -o spc.exe https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-windows-x64.exe
|
||||
```
|
||||
|
||||
# Add execute perm (Linux and macOS only)
|
||||
对于 macOS 和 Linux,请先添加执行权限:
|
||||
|
||||
```bash
|
||||
chmod +x ./spc
|
||||
|
||||
# Run (Linux and macOS)
|
||||
./spc --version
|
||||
# Run (Windows powershell)
|
||||
.\spc.exe --version
|
||||
```
|
||||
|
||||
自托管 `spc` 由 GitHub Actions 构建,你也可以从 Actions 直接下载:[此处](https://github.com/crazywhalecc/static-php-cli/actions/workflows/release-build.yml)。
|
||||
### 2. 构建静态 PHP
|
||||
|
||||
### 本地构建(使用 git 源码)
|
||||
首先,创建一个 `craft.yml` 文件,并从 [扩展列表](https://static-php.dev/zh/guide/extensions.html) 或 [命令生成器](https://static-php.dev/zh/guide/cli-generator.html) 中指定要包含的扩展:
|
||||
|
||||
如果你需要修改 static-php-cli 源码,或者使用 spc 二进制构建有问题,你可以使用 git 源码下载 static-php-cli。
|
||||
```yml
|
||||
# PHP 版本支持:8.1, 8.2, 8.3, 8.4, 8.5
|
||||
php-version: 8.4
|
||||
# 在此处放置您的扩展列表
|
||||
extensions: "apcu,bcmath,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,gd,iconv,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,pcntl,pdo,pdo_mysql,pdo_sqlite,phar,posix,readline,redis,session,simplexml,sockets,sodium,sqlite3,tokenizer,xml,xmlreader,xmlwriter,xsl,zip,zlib"
|
||||
sapi:
|
||||
- cli
|
||||
- micro
|
||||
- fpm
|
||||
download-options:
|
||||
prefer-pre-built: true
|
||||
```
|
||||
|
||||
运行命令:
|
||||
|
||||
```bash
|
||||
# clone 仓库即可
|
||||
git clone https://github.com/crazywhalecc/static-php-cli.git
|
||||
./spc craft
|
||||
|
||||
# 输出完整控制台日志
|
||||
./spc craft --debug
|
||||
```
|
||||
|
||||
如果您的系统上尚未安装 php,我们建议你使用内置的 setup-runtime 自动安装 PHP 和 Composer。
|
||||
### 3. 静态 PHP 使用
|
||||
|
||||
```bash
|
||||
cd static-php-cli
|
||||
chmod +x bin/setup-runtime
|
||||
# it will download static php (from self-hosted server) and composer (from getcomposer)
|
||||
bin/setup-runtime
|
||||
# initialize composer deps
|
||||
bin/composer install
|
||||
# chmod
|
||||
chmod +x bin/spc
|
||||
bin/spc --version
|
||||
现在您可以将 static-php-cli 构建的二进制文件复制到另一台机器上,无需依赖即可运行:
|
||||
|
||||
```
|
||||
# php-cli
|
||||
buildroot/bin/php -v
|
||||
|
||||
# phpmicro
|
||||
echo '<?php echo "Hello world!\n";' > a.php
|
||||
./spc micro:combine a.php -O my-app
|
||||
./my-app
|
||||
|
||||
# php-fpm
|
||||
buildroot/bin/php-fpm -v
|
||||
```
|
||||
|
||||
### 开始构建 PHP
|
||||
## 文档
|
||||
|
||||
下面是使用 static-php-cli 的基础用法:
|
||||
当前 README 包含基本用法。有关 static-php-cli 的所有功能,
|
||||
请访问 <https://static-php.dev>。
|
||||
|
||||
> 如果你使用的是打包好的 `spc` 二进制,你需要将下列命令的 `./bin/spc` 替换为 `./spc`。
|
||||
## 直接下载
|
||||
|
||||
```bash
|
||||
# 检查环境依赖,并根据尝试自动安装缺失的编译工具
|
||||
./bin/spc doctor --auto-fix
|
||||
如果您不想构建或想先测试,可以从 [Actions](https://github.com/static-php/static-php-cli-hosted/actions/workflows/build-php-bulk.yml) 下载示例预编译工件,或从自托管服务器下载。
|
||||
|
||||
# 输出目标项目依赖的扩展列表
|
||||
./bin/spc dump-extensions /path/to/your/project --format=text
|
||||
以下是几个具有不同扩展组合的预编译静态 PHP 二进制文件,
|
||||
您可以根据需要直接下载。
|
||||
|
||||
# 拉取所有依赖库
|
||||
./bin/spc download --all
|
||||
# 只拉取编译指定扩展需要的所有依赖(推荐)
|
||||
./bin/spc download --for-extensions="openssl,pcntl,mbstring,pdo_sqlite"
|
||||
# 下载依赖时,优先下载有预编译的库(节省编译依赖的时间)
|
||||
./bin/spc download --for-extensions="openssl,curl,mbstring,mbregex" --prefer-pre-built
|
||||
# 下载编译不同版本的 PHP (--with-php=x.y 或 --with-php=x.y.z,推荐 8.1 ~ 8.3)
|
||||
./bin/spc download --for-extensions="openssl,curl,mbstring" --with-php=8.1
|
||||
| 组合名称 | 扩展数量 | 系统 | 备注 |
|
||||
|----------------------------------------------------------------------|----------------------------------------------------------------------------|--------------|--------------------|
|
||||
| [common](https://dl.static-php.dev/static-php-cli/common/) | [30+](https://dl.static-php.dev/static-php-cli/common/README.txt) | Linux, macOS | 二进制文件大小约为 7.5MB |
|
||||
| [bulk](https://dl.static-php.dev/static-php-cli/bulk/) | [50+](https://dl.static-php.dev/static-php-cli/bulk/README.txt) | Linux, macOS | 二进制文件大小约为 25MB |
|
||||
| [gnu-bulk](https://dl.static-php.dev/static-php-cli/gnu-bulk/) | [50+](https://dl.static-php.dev/static-php-cli/bulk/README.txt) | Linux, macOS | 使用 glibc 的 bulk 组合 |
|
||||
| [minimal](https://dl.static-php.dev/static-php-cli/minimal/) | [5](https://dl.static-php.dev/static-php-cli/minimal/README.txt) | Linux, macOS | 二进制文件大小约为 3MB |
|
||||
| [spc-min](https://dl.static-php.dev/static-php-cli/windows/spc-min/) | [5](https://dl.static-php.dev/static-php-cli/windows/spc-min/README.txt) | Windows | 二进制文件大小约为 3MB |
|
||||
| [spc-max](https://dl.static-php.dev/static-php-cli/windows/spc-max/) | [40+](https://dl.static-php.dev/static-php-cli/windows/spc-max/README.txt) | Windows | 二进制文件大小约为 8.5MB |
|
||||
|
||||
# 构建包含 bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl 扩展的 php-cli 和 micro.sfx
|
||||
./bin/spc build "bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl" --build-cli --build-micro
|
||||
# 编译线程安全版本 (--enable-zts)
|
||||
./bin/spc build "curl,phar" --enable-zts --build-cli
|
||||
# 编译后使用 UPX 减小可执行文件体积 (仅 Linux、Windows 可用) (至少压缩至原来的 30~50%)
|
||||
./bin/spc build "curl,phar" --enable-zts --build-cli --with-upx-pack
|
||||
```
|
||||
> Linux 和 Windows 支持对二进制文件进行 UPX 压缩,可以将二进制文件大小减少 30% 到 50%。
|
||||
> macOS 不支持 UPX 压缩,因此 mac 的预构建二进制文件大小较大。
|
||||
|
||||
其中,目前支持构建 cli,micro,fpm 和 embed,使用以下参数的一个或多个来指定编译的 SAPI:
|
||||
### 在线构建(使用 GitHub Actions)
|
||||
|
||||
- `--build-cli`:构建 cli 二进制
|
||||
- `--build-micro`:构建 phpmicro 自执行二进制
|
||||
- `--build-fpm`:构建 fpm
|
||||
- `--build-embed`:构建 embed(libphp)
|
||||
- `--build-all`:构建所有
|
||||
上方直接下载的二进制不能满足需求时,可使用 GitHub Action 可以轻松构建静态编译的 PHP,
|
||||
同时自行定义要编译的扩展。
|
||||
|
||||
如果出现了任何错误,可以使用 `--debug` 参数来展示完整的输出日志,以供排查错误:
|
||||
1. Fork 本项目。
|
||||
2. 进入项目的 Actions 并选择 `CI`。
|
||||
3. 选择 `Run workflow`,填入您要编译的 PHP 版本、目标类型和扩展列表。(扩展用逗号分隔,例如 `bcmath,curl,mbstring`)
|
||||
4. 等待一段时间后,进入相应的任务并获取 `Artifacts`。
|
||||
|
||||
```bash
|
||||
./bin/spc build "openssl,pcntl,mbstring" --debug --build-all
|
||||
./bin/spc download --all --debug
|
||||
```
|
||||
|
||||
## 不同 SAPI 的使用
|
||||
|
||||
### 使用 cli
|
||||
|
||||
> php-cli 是一个静态的二进制文件,类似 Go、Rust 语言编译后的单个可移植的二进制文件。
|
||||
|
||||
采用参数 `--build-cli` 或`--build-all` 参数时,最后编译结果会输出一个 `./php` 的二进制文件,此文件可分发、可直接使用。
|
||||
该文件编译后会存放在 `buildroot/bin/` 目录中,名称为 `php`,拷贝出来即可。
|
||||
|
||||
```bash
|
||||
cd buildroot/bin/
|
||||
./php -v # 检查版本
|
||||
./php -m # 检查编译的扩展
|
||||
./php your_code.php # 运行代码
|
||||
./php your_project.phar # 运行打包为 phar 单文件的项目
|
||||
```
|
||||
|
||||
### 使用 micro
|
||||
|
||||
> phpmicro 是一个提供自执行二进制 PHP 的项目,本项目依赖 phpmicro 进行编译自执行二进制。详见 [dixyes/phpmicro](https://github.com/dixyes/phpmicro)。
|
||||
|
||||
采用项目参数 `--build-micro` 或 `--build-all` 时,最后编译结果会输出一个 `./micro.sfx` 的文件,此文件需要配合你的 PHP 源码使用。
|
||||
该文件编译后会存放在 `buildroot/bin/` 目录中,拷贝出来即可。
|
||||
|
||||
使用时应准备好你的项目源码文件,可以是单个 PHP 文件,也可以是 Phar 文件。
|
||||
|
||||
```bash
|
||||
echo "<?php echo 'Hello world' . PHP_EOL;" > code.php
|
||||
cat micro.sfx code.php > single-app && chmod +x single-app
|
||||
./single-app
|
||||
```
|
||||
|
||||
如果打包 PHAR 文件,仅需把 code.php 更换为 phar 文件路径即可。
|
||||
你可以使用 [box-project/box](https://github.com/box-project/box) 将你的 CLI 项目打包为 Phar,
|
||||
然后将它与 phpmicro 结合,生成独立可执行的二进制文件。
|
||||
|
||||
```bash
|
||||
# 使用 static-php-cli 生成的 micro.sfx 结合,也可以直接使用 cat 命令结合它们
|
||||
bin/spc micro:combine my-app.phar
|
||||
cat buildroot/bin/micro.sfx my-app.phar > my-app && chmod +x my-app
|
||||
|
||||
# 使用 micro:combine 结合可以将 INI 选项注入到二进制中
|
||||
bin/spc micro:combine my-app.phar -I "memory_limit=4G" -I "disable_functions=system" --output my-app-2
|
||||
```
|
||||
|
||||
> 有些情况下的 phar 文件或 PHP 项目可能无法在 micro 环境下运行。
|
||||
|
||||
### 使用 fpm
|
||||
|
||||
采用项目参数 `--build-fpm` 或 `--build-all` 时,最后编译结果会输出一个 `./php-fpm` 的文件。
|
||||
该文件存放在 `buildroot/bin/` 目录,拷贝出来即可使用。
|
||||
|
||||
在正常的 Linux 发行版和 macOS 系统中,安装 php-fpm 后包管理会自动生成默认的 fpm 配置文件。
|
||||
因为 php-fpm 必须指定配置文件才可启动,本项目编译的 php-fpm 不会带任何配置文件,所以需自行编写 `php-fpm.conf` 和 `pool.conf` 配置文件。
|
||||
|
||||
指定 `php-fpm.conf` 可以使用命令参数 `-y`,例如:`./php-fpm -y php-fpm.conf`。
|
||||
|
||||
### 使用 embed
|
||||
|
||||
采用项目参数 `--build-embed` 或 `--build-all` 时,最后编译结果会输出一个 `libphp.a`、`php-config` 以及一系列头文件,存放在 `buildroot/`,你可以在你的其他代码中引入它们。
|
||||
|
||||
如果你知道 [embed SAPI](https://github.com/php/php-src/tree/master/sapi/embed),你应该知道如何使用它。对于有可能编译用到引入其他库的问题,你可以使用 `buildroot/bin/php-config` 来获取编译时的配置。
|
||||
|
||||
另外,有关如何使用此功能的高级示例,请查看[如何使用它构建 FrankenPHP 的静态版本](https://github.com/php/frankenphp/blob/main/docs/static.md)。
|
||||
如果您启用 `debug`,构建时将输出所有日志,包括编译日志,以便故障排除。
|
||||
|
||||
## 贡献
|
||||
|
||||
如果缺少你需要的扩展,可发起 Issue。如果你对本项目较熟悉,也欢迎为本项目发起 Pull Request。
|
||||
如果您需要的扩展缺失,可以创建 issue。
|
||||
如果您熟悉本项目,也欢迎发起 pull request。
|
||||
|
||||
另外,添加新扩展的贡献方式,可以参考下方 `进阶`。
|
||||
如果您想贡献文档,请直接编辑 `docs/` 目录。
|
||||
|
||||
如果你想贡献文档内容,请直接修改 `docs/` 目录。
|
||||
现在有一个 [static-php](https://github.com/static-php) 组织,用于存储与项目相关的仓库。
|
||||
|
||||
## 赞助本项目
|
||||
|
||||
你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。你捐赠的一部分将会被用于维护 **static-php.dev** 服务器。
|
||||
您可以从 [GitHub Sponsor](https://github.com/crazywhalecc) 赞助我或我的项目。您捐赠的一部分将用于维护 **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 许可证,
|
||||
一些新添加的扩展和依赖可能来自其他项目,
|
||||
这些代码文件的头部也会给出额外的许可证和作者说明。
|
||||
|
||||
这些是类似的项目:
|
||||
|
||||
- [dixyes/lwmbs](https://github.com/dixyes/lwmbs)
|
||||
- [swoole/swoole-cli](https://github.com/swoole/swoole-cli)
|
||||
|
||||
该项目使用了 [dixyes/lwmbs](https://github.com/dixyes/lwmbs) 中的一些代码,例如 Windows 静态构建目标和 libiconv 库支持。
|
||||
lwmbs 使用 [Mulan PSL 2](http://license.coscl.org.cn/MulanPSL2) 许可进行分发。对应文件有关于作者和许可的特殊说明,除此之外,均使用 MIT 授权许可。
|
||||
本项目使用了 [dixyes/lwmbs](https://github.com/dixyes/lwmbs) 的一些代码,例如 Windows 静态构建目标和 libiconv 支持。
|
||||
lwmbs 基于 [Mulan PSL 2](http://license.coscl.org.cn/MulanPSL2) 许可证。
|
||||
|
||||
因本项目的特殊性,使用项目编译过程中会使用很多其他开源项目,例如 curl、protobuf 等,它们都有各自的开源协议。
|
||||
请在编译完成后,使用命令 `bin/spc dump-license` 导出项目使用项目的开源协议,并遵守对应项目的 LICENSE。
|
||||
由于本项目的特殊性,
|
||||
项目编译过程中会使用许多其他开源项目,如 curl 和 protobuf,
|
||||
它们都有自己的开源许可证。
|
||||
|
||||
请在编译后使用 `bin/spc dump-license` 命令导出项目中使用的开源许可证,
|
||||
并遵守相应项目的 LICENSE。
|
||||
|
||||
328
README.md
328
README.md
@@ -5,29 +5,22 @@
|
||||
[](https://github.com/crazywhalecc/static-php-cli/releases)
|
||||
[](https://github.com/crazywhalecc/static-php-cli/actions/workflows/tests.yml)
|
||||
[](https://github.com/crazywhalecc/static-php-cli/blob/main/LICENSE)
|
||||
[](https://static-php.dev/en/guide/extensions.html)
|
||||
|
||||
**static-php-cli** is a powerful tool designed for building static, standalone PHP runtime
|
||||
with popular extensions.
|
||||
|
||||
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
|
||||
along with the PHP interpreter into one single executable file.
|
||||
|
||||
## Features
|
||||
|
||||
static-php-cli (you can call it `spc`) has a lot of features:
|
||||
|
||||
- :handbag: Build single-file php executable, without any dependencies
|
||||
- :hamburger: Build **[phpmicro](https://github.com/dixyes/phpmicro)** self-extracted executable (glue php binary and php source code into one file)
|
||||
- :pill: Automatic build environment checker (Doctor module)
|
||||
- :elephant: Support multiple PHP versions - PHP 8.1, 8.2, 8.3, 8.4, 8.5
|
||||
- :handbag: Build single-file PHP executable with zero dependencies
|
||||
- :hamburger:Build **[phpmicro](https://github.com/dixyes/phpmicro)** self-extracting executables (combines PHP binary and source code into one file)
|
||||
- :pill: Automatic build environment checker with auto-fix capabilities
|
||||
- :zap: `Linux`, `macOS`, `FreeBSD`, `Windows` support
|
||||
- :wrench: Configurable source code patches
|
||||
- :books: Build dependency management
|
||||
- 📦 Provide `spc` own standalone executable (built by spc and [box](https://github.com/box-project/box))
|
||||
- :fire: Support many popular [extensions](https://static-php.dev/en/guide/extensions.html)
|
||||
- :floppy_disk: UPX integration (significantly reduces binary size)
|
||||
- :wrench: Configurable source code patching
|
||||
- :books: Intelligent dependency management
|
||||
- 📦 Self-contained `spc` executable (built with [box](https://github.com/box-project/box))
|
||||
- :fire: Support 100+ popular [extensions](https://static-php.dev/en/guide/extensions.html)
|
||||
- :floppy_disk: UPX compression support (reduces binary size by 30-50%)
|
||||
|
||||
**Single-file standalone php-cli:**
|
||||
|
||||
@@ -37,6 +30,72 @@ static-php-cli (you can call it `spc`) has a lot of features:
|
||||
|
||||
<img width="700" alt="out2" src="https://github.com/crazywhalecc/static-php-cli/assets/20330940/46b7128d-fb72-4169-957e-48564c3ff3e2">
|
||||
|
||||
## Quickstart
|
||||
|
||||
### 1. Download spc binary
|
||||
|
||||
```bash
|
||||
# For Linux x86_64
|
||||
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-x86_64
|
||||
# For Linux aarch64
|
||||
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-aarch64
|
||||
# macOS x86_64 (Intel)
|
||||
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-x86_64
|
||||
# macOS aarch64 (Apple)
|
||||
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-aarch64
|
||||
# Windows (x86_64, win10 build 17063 or later, please install VS2022 first)
|
||||
curl.exe -fsSL -o spc.exe https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-windows-x64.exe
|
||||
```
|
||||
|
||||
For macOS and Linux, add execute permission first:
|
||||
|
||||
```bash
|
||||
chmod +x ./spc
|
||||
```
|
||||
|
||||
### 2. Build Static PHP
|
||||
|
||||
First, create a `craft.yml` file and specify which extensions you want to include from [extension list](https://static-php.dev/en/guide/extensions.html) or [command generator](https://static-php.dev/en/guide/cli-generator.html):
|
||||
|
||||
```yml
|
||||
# PHP version support: 8.1, 8.2, 8.3, 8.4, 8.5
|
||||
php-version: 8.4
|
||||
# Put your extension list here
|
||||
extensions: "apcu,bcmath,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,gd,iconv,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,pcntl,pdo,pdo_mysql,pdo_sqlite,phar,posix,readline,redis,session,simplexml,sockets,sodium,sqlite3,tokenizer,xml,xmlreader,xmlwriter,xsl,zip,zlib"
|
||||
sapi:
|
||||
- cli
|
||||
- micro
|
||||
- fpm
|
||||
download-options:
|
||||
prefer-pre-built: true
|
||||
```
|
||||
|
||||
Run command:
|
||||
|
||||
```bash
|
||||
./spc craft
|
||||
|
||||
# Output full console log
|
||||
./spc craft --debug
|
||||
```
|
||||
|
||||
### 3. Static PHP usage
|
||||
|
||||
Now you can copy binaries built by static-php-cli to another machine and run with no dependencies:
|
||||
|
||||
```
|
||||
# php-cli
|
||||
buildroot/bin/php -v
|
||||
|
||||
# phpmicro
|
||||
echo '<?php echo "Hello world!\n";' > a.php
|
||||
./spc micro:combine a.php -O my-app
|
||||
./my-app
|
||||
|
||||
# php-fpm
|
||||
buildroot/bin/php-fpm -v
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
The current README contains basic usage. For all the features of static-php-cli,
|
||||
@@ -53,6 +112,7 @@ which can be downloaded directly according to your needs.
|
||||
|----------------------------------------------------------------------|----------------------------------------------------------------------------|--------------|--------------------------------|
|
||||
| [common](https://dl.static-php.dev/static-php-cli/common/) | [30+](https://dl.static-php.dev/static-php-cli/common/README.txt) | Linux, macOS | The binary size is about 7.5MB |
|
||||
| [bulk](https://dl.static-php.dev/static-php-cli/bulk/) | [50+](https://dl.static-php.dev/static-php-cli/bulk/README.txt) | Linux, macOS | The binary size is about 25MB |
|
||||
| [gnu-bulk](https://dl.static-php.dev/static-php-cli/gnu-bulk/) | [50+](https://dl.static-php.dev/static-php-cli/bulk/README.txt) | Linux, macOS | Using shared glibc |
|
||||
| [minimal](https://dl.static-php.dev/static-php-cli/minimal/) | [5](https://dl.static-php.dev/static-php-cli/minimal/README.txt) | Linux, macOS | The binary size is about 3MB |
|
||||
| [spc-min](https://dl.static-php.dev/static-php-cli/windows/spc-min/) | [5](https://dl.static-php.dev/static-php-cli/windows/spc-min/README.txt) | Windows | The binary size is about 3MB |
|
||||
| [spc-max](https://dl.static-php.dev/static-php-cli/windows/spc-max/) | [40+](https://dl.static-php.dev/static-php-cli/windows/spc-max/README.txt) | Windows | The binary size is about 8.5MB |
|
||||
@@ -60,64 +120,10 @@ which can be downloaded directly according to your needs.
|
||||
> Linux and Windows supports UPX compression for binaries, which can reduce the size of the binary by 30% to 50%.
|
||||
> macOS does not support UPX compression, so the size of the pre-built binaries for mac is larger.
|
||||
|
||||
## Build
|
||||
|
||||
### Compilation Requirements
|
||||
|
||||
You can say I made a PHP builder written in PHP, pretty funny.
|
||||
But static-php-cli runtime only requires an environment above PHP 8.1 and extensions mentioned below.
|
||||
|
||||
- PHP >= 8.4 (This is the version required by spc itself, not the build version)
|
||||
- Extension: `mbstring,tokenizer,phar`
|
||||
- Supported OS with `curl` and `git` installed
|
||||
|
||||
Here is the supported OS and arch, where :octocat: represents support for GitHub Action builds,
|
||||
:computer: represents support for local manual builds, and blank represents not currently supported.
|
||||
|
||||
| | x86_64 | aarch64 |
|
||||
|---------|----------------------|----------------------|
|
||||
| macOS | :octocat: :computer: | :octocat: :computer: |
|
||||
| Linux | :octocat: :computer: | :octocat: :computer: |
|
||||
| Windows | :octocat: :computer: | |
|
||||
| FreeBSD | :computer: | :computer: |
|
||||
|
||||
Currently supported PHP versions for compilation:
|
||||
|
||||
> :warning: Partial support, there may be issues with newer test versions or older versions.
|
||||
>
|
||||
> :heavy_check_mark: supported
|
||||
>
|
||||
> :x: not supported
|
||||
|
||||
| PHP Version | Status | Comment |
|
||||
|-------------|--------------------|----------------------------------------------------------------------------------------------------|
|
||||
| 7.2 | :x: | |
|
||||
| 7.3 | :x: | phpmicro and some extensions not supported on 7.x |
|
||||
| 7.4 | :x: | phpmicro and some extensions not supported on 7.x |
|
||||
| 8.0 | :warning: | PHP official has stopped maintenance of 8.0, we no longer provide backport support for version 8.0 |
|
||||
| 8.1 | :heavy_check_mark: | PHP official has security fixes only, we no longer provide backport support when 8.5 released |
|
||||
| 8.2 | :heavy_check_mark: | |
|
||||
| 8.3 | :heavy_check_mark: | |
|
||||
| 8.4 | :heavy_check_mark: | |
|
||||
| 8.5 (alpha) | :warning: | PHP 8.5 is in alpha |
|
||||
|
||||
> This table shows the support status for static-php-cli in building the corresponding version,
|
||||
> not the official PHP support status for that version.
|
||||
|
||||
### Supported Extensions
|
||||
|
||||
Please first select the extension you want to compile based on the extension list below.
|
||||
|
||||
- [Supported Extension List](https://static-php.dev/en/guide/extensions.html)
|
||||
- [Command Generator](https://static-php.dev/en/guide/cli-generator.html)
|
||||
|
||||
> If an extension you need is missing, you can submit an issue.
|
||||
|
||||
Here is the current planned roadmap for extension support: [#152](https://github.com/crazywhalecc/static-php-cli/issues/152) .
|
||||
|
||||
### Build Online (using GitHub Actions)
|
||||
|
||||
Use GitHub Action to easily build a statically compiled PHP,
|
||||
When the above direct download binaries cannot meet your needs,
|
||||
you can use GitHub Action to easily build a statically compiled PHP,
|
||||
and at the same time define the extensions to be compiled by yourself.
|
||||
|
||||
1. Fork me.
|
||||
@@ -127,184 +133,6 @@ and at the same time define the extensions to be compiled by yourself.
|
||||
|
||||
If you enable `debug`, all logs will be output at build time, including compiled logs, for troubleshooting.
|
||||
|
||||
### Build Locally (using SPC binary, recommended)
|
||||
|
||||
This project provides a binary file of static-php-cli: `spc`.
|
||||
You can use `spc` binary instead of installing any runtime like golang app.
|
||||
Currently, the platforms supported by `spc` binary are Linux and macOS.
|
||||
|
||||
Download from self-hosted nightly builds using commands below:
|
||||
|
||||
```bash
|
||||
# Download from self-hosted nightly builds (sync with main branch)
|
||||
# For Linux x86_64
|
||||
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-x86_64
|
||||
# For Linux aarch64
|
||||
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-aarch64
|
||||
# macOS x86_64 (Intel)
|
||||
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-x86_64
|
||||
# macOS aarch64 (Apple)
|
||||
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-aarch64
|
||||
# Windows (x86_64, win10 build 17063 or later)
|
||||
curl.exe -fsSL -o spc.exe https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-windows-x64.exe
|
||||
|
||||
# Add execute perm (Linux and macOS only)
|
||||
chmod +x ./spc
|
||||
|
||||
# Run (Linux and macOS)
|
||||
./spc --version
|
||||
# Run (Windows powershell)
|
||||
.\spc.exe --version
|
||||
```
|
||||
|
||||
Self-hosted `spc` is built by GitHub Actions, you can also download from Actions artifacts [here](https://github.com/crazywhalecc/static-php-cli/actions/workflows/release-build.yml).
|
||||
|
||||
### Build Locally (using git source)
|
||||
|
||||
If you need to modify the static-php-cli source code, or have problems using the spc binary build,
|
||||
you can download static-php-cli using the git source code.
|
||||
|
||||
```bash
|
||||
# just clone me!
|
||||
git clone https://github.com/crazywhalecc/static-php-cli.git
|
||||
```
|
||||
|
||||
If you have not installed php on your system, we recommend that you use the built-in setup-runtime to install PHP and Composer automatically.
|
||||
|
||||
```bash
|
||||
cd static-php-cli
|
||||
chmod +x bin/setup-runtime
|
||||
# it will download static php (from self-hosted server) and composer (from getcomposer)
|
||||
bin/setup-runtime
|
||||
# initialize composer deps
|
||||
bin/composer install
|
||||
# chmod
|
||||
chmod +x bin/spc
|
||||
bin/spc --version
|
||||
```
|
||||
|
||||
### Start Building PHP
|
||||
|
||||
Basic usage for building php with some extensions:
|
||||
|
||||
> If you are using the packaged standalone `spc` binary, you need to replace `bin/spc` with `./spc` or `.\spc.exe` in the following commands.
|
||||
|
||||
```bash
|
||||
# Check system tool dependencies, auto-fix them if possible
|
||||
./bin/spc doctor --auto-fix
|
||||
|
||||
# fetch all libraries
|
||||
./bin/spc download --all
|
||||
# dump a list of extensions required by your project
|
||||
./bin/spc dump-extensions /path/to/your/project --format=text
|
||||
# only fetch necessary sources by needed extensions (recommended)
|
||||
./bin/spc download --for-extensions="openssl,pcntl,mbstring,pdo_sqlite"
|
||||
# download pre-built libraries first (save time for compiling dependencies)
|
||||
./bin/spc download --for-extensions="openssl,curl,mbstring,mbregex" --prefer-pre-built
|
||||
# download different PHP version (--with-php=x.y or --with-php=x.y.z, recommend 8.3 ~ 8.4)
|
||||
./bin/spc download --for-extensions="openssl,curl,mbstring" --with-php=8.1
|
||||
|
||||
# with bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl extension, build both CLI and phpmicro SAPI
|
||||
./bin/spc build "bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl" --build-cli --build-micro
|
||||
# build thread-safe (ZTS) version (--enable-zts)
|
||||
./bin/spc build "curl,phar" --enable-zts --build-cli
|
||||
# build, pack executable with UPX (linux and windows only) (reduce binary size for 30~50%)
|
||||
./bin/spc build "curl,phar" --enable-zts --build-cli --with-upx-pack
|
||||
```
|
||||
|
||||
Now we support `cli`, `micro`, `fpm` and `embed` SAPI. You can use one or more of the following parameters to specify the compiled SAPI:
|
||||
|
||||
- `--build-cli`: build static cli executable
|
||||
- `--build-micro`: build static phpmicro self-extracted executable
|
||||
- `--build-fpm`: build static fpm binary
|
||||
- `--build-embed`: build embed (libphp)
|
||||
- `--build-all`: build all
|
||||
|
||||
If anything goes wrong, use `--debug` option to display full terminal output:
|
||||
|
||||
```bash
|
||||
./bin/spc build "openssl,pcntl,mbstring" --debug --build-all
|
||||
./bin/spc download --all --debug
|
||||
```
|
||||
|
||||
## Different SAPI Usage
|
||||
|
||||
### Use cli
|
||||
|
||||
> php-cli is a single static binary, you can use it like normal php installed on your system.
|
||||
|
||||
When using the parameter `--build-cli` or `--build-all`,
|
||||
the final compilation result will output a binary file named `./php`,
|
||||
which can be distributed and used directly.
|
||||
This file will be located in the directory `buildroot/bin/`, copy it out for use.
|
||||
|
||||
```bash
|
||||
cd buildroot/bin/
|
||||
./php -v # check version
|
||||
./php -m # check extensions
|
||||
./php your_code.php # run your php code
|
||||
./php your_project.phar # run your phar (project archive)
|
||||
```
|
||||
|
||||
### Use micro
|
||||
|
||||
> phpmicro is a SelF-extracted eXecutable SAPI module,
|
||||
> provided by [phpmicro](https://github.com/dixyes/phpmicro) project.
|
||||
> But this project is using a [fork](https://github.com/static-php/phpmicro) of phpmicro, because we need to add some features to it.
|
||||
> It can put php runtime and your source code together.
|
||||
|
||||
When using the parameter `--build-all` or `--build-micro`,
|
||||
the final compilation result will output a file named `./micro.sfx`,
|
||||
which needs to be used with your PHP source code like `code.php`.
|
||||
This file will be located in the path `buildroot/bin/micro.sfx`, simply copy it out for use.
|
||||
|
||||
Prepare your project source code, which can be a single PHP file or a Phar file, for use.
|
||||
|
||||
```bash
|
||||
echo "<?php echo 'Hello world' . PHP_EOL;" > code.php
|
||||
cat micro.sfx code.php > single-app && chmod +x single-app
|
||||
./single-app
|
||||
```
|
||||
|
||||
If you package a PHAR file, just replace `code.php` with the phar file path.
|
||||
You can use [box-project/box](https://github.com/box-project/box) to package your CLI project as Phar,
|
||||
It is then combined with phpmicro to produce a standalone executable binary.
|
||||
|
||||
```bash
|
||||
# Use the micro.sfx generated by static-php-cli to combine,
|
||||
bin/spc micro:combine my-app.phar
|
||||
# or you can directly use the cat command to combine them.
|
||||
cat buildroot/bin/micro.sfx my-app.phar > my-app && chmod +x my-app
|
||||
|
||||
# Use micro:combine combination to inject INI options into the binary.
|
||||
bin/spc micro:combine my-app.phar -I "memory_limit=4G" -I "disable_functions=system" --output my-app-2
|
||||
```
|
||||
|
||||
> In some cases, PHAR files may not run in a micro environment. Overall, micro is not production ready.
|
||||
|
||||
### Use fpm
|
||||
|
||||
When using the parameter `--build-all` or `--build-fpm`,
|
||||
the final compilation result will output a file named `./php-fpm`,
|
||||
This file will be located in the path `buildroot/bin/`, simply copy it out for use.
|
||||
|
||||
In common Linux distributions and macOS systems, the package manager will automatically generate a default fpm configuration file after installing php-fpm.
|
||||
Because php-fpm must specify a configuration file before running, the php-fpm compiled by this project will not have any configuration files, so you need to write `php-fpm.conf` and `pool.conf` configuration files yourself.
|
||||
|
||||
Specifying `php-fpm.conf` can use the command parameter `-y`, for example: `./php-fpm -y php-fpm.conf`.
|
||||
|
||||
### Use embed
|
||||
|
||||
When using the project parameters `--build-embed` or `--build-all`,
|
||||
the final compilation result will output a `libphp.a`, `php-config` and a series of header files,
|
||||
stored in `buildroot/`. You can introduce them in your other projects.
|
||||
|
||||
If you know [embed SAPI](https://github.com/php/php-src/tree/master/sapi/embed), you should know how to use it.
|
||||
You may require the introduction of other libraries during compilation,
|
||||
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/php/frankenphp/blob/main/docs/static.md).
|
||||
|
||||
## Contribution
|
||||
|
||||
If the extension you need is missing, you can create an issue.
|
||||
|
||||
11
bin/docker-entrypoint.sh
Normal file
11
bin/docker-entrypoint.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
TARGET_DIR="/app/pkgroot/$(uname -m)-linux"
|
||||
BACKUP_DIR="/app/pkgroot-private"
|
||||
# copy private pkgroot to pkgroot if pkgroot is empty
|
||||
if [ ! -d "$TARGET_DIR" ] || [ -z "$(ls -A "$TARGET_DIR")" ]; then
|
||||
echo "* Copying private pkgroot to pkgroot ..."
|
||||
rm -rf "$TARGET_DIR"
|
||||
cp -r "$BACKUP_DIR" "$TARGET_DIR"
|
||||
fi
|
||||
exec "$@"
|
||||
@@ -25,7 +25,7 @@ __DIR__=$(cd "$(dirname "$0")" && pwd)
|
||||
__PROJECT__=$(cd "${__DIR__}"/../ && pwd)
|
||||
|
||||
# set download dir
|
||||
__PHP_RUNTIME_URL__="https://dl.static-php.dev/static-php-cli/bulk/php-8.4.4-cli-${__OS_FIXED__}-${__ARCH__}.tar.gz"
|
||||
__PHP_RUNTIME_URL__="https://dl.static-php.dev/static-php-cli/bulk/php-8.4.12-cli-${__OS_FIXED__}-${__ARCH__}.tar.gz"
|
||||
__COMPOSER_URL__="https://getcomposer.org/download/latest-stable/composer.phar"
|
||||
|
||||
# use china mirror
|
||||
@@ -45,7 +45,7 @@ done
|
||||
|
||||
case "$mirror" in
|
||||
china)
|
||||
__PHP_RUNTIME_URL__="https://dl.static-php.dev/static-php-cli/bulk/php-8.4.4-cli-${__OS_FIXED__}-${__ARCH__}.tar.gz"
|
||||
__PHP_RUNTIME_URL__="https://dl.static-php.dev/static-php-cli/bulk/php-8.4.12-cli-${__OS_FIXED__}-${__ARCH__}.tar.gz"
|
||||
__COMPOSER_URL__="https://mirrors.tencent.com/composer/composer.phar"
|
||||
;;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
set -e
|
||||
|
||||
# This file is using docker to run commands
|
||||
SPC_DOCKER_VERSION=v5
|
||||
SPC_DOCKER_VERSION=v6
|
||||
|
||||
# Detect docker can run
|
||||
if ! which docker >/dev/null; then
|
||||
@@ -122,6 +122,14 @@ ADD ./src /app/src
|
||||
COPY ./composer.* /app/
|
||||
ADD ./bin /app/bin
|
||||
RUN composer install --no-dev
|
||||
ADD ./config /app/config
|
||||
RUN bin/spc doctor --auto-fix
|
||||
RUN bin/spc install-pkg upx
|
||||
|
||||
RUN mv /app/pkgroot/\$(uname -m)-linux /app/pkgroot-private
|
||||
ADD bin/docker-entrypoint.sh /bin/docker-entrypoint.sh
|
||||
RUN chmod +x /bin/docker-entrypoint.sh
|
||||
ENTRYPOINT ["/bin/docker-entrypoint.sh"]
|
||||
EOF
|
||||
fi
|
||||
|
||||
@@ -142,6 +150,7 @@ MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/source:/app/source"
|
||||
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/dist:/app/dist"
|
||||
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/downloads:/app/downloads"
|
||||
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/pkgroot:/app/pkgroot"
|
||||
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/log:/app/log"
|
||||
if [ -f "$(pwd)/craft.yml" ]; then
|
||||
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/craft.yml:/app/craft.yml"
|
||||
fi
|
||||
@@ -153,6 +162,47 @@ if [ ! -z "$GITHUB_TOKEN" ]; then
|
||||
ENV_LIST="$ENV_LIST -e GITHUB_TOKEN=$GITHUB_TOKEN"
|
||||
fi
|
||||
|
||||
# Intercept and rewrite --with-frankenphp-app option, and mount host path to /app/app
|
||||
FRANKENPHP_APP_PATH=""
|
||||
NEW_ARGS=()
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--with-frankenphp-app=*)
|
||||
FRANKENPHP_APP_PATH="${1#*=}"
|
||||
NEW_ARGS+=("--with-frankenphp-app=/app/app")
|
||||
shift
|
||||
;;
|
||||
--with-frankenphp-app)
|
||||
if [ -n "${2:-}" ]; then
|
||||
FRANKENPHP_APP_PATH="$2"
|
||||
NEW_ARGS+=("--with-frankenphp-app=/app/app")
|
||||
shift 2
|
||||
else
|
||||
NEW_ARGS+=("$1")
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
NEW_ARGS+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Normalize the path and add mount if provided
|
||||
if [ -n "$FRANKENPHP_APP_PATH" ]; then
|
||||
# expand ~ to $HOME
|
||||
if [ "${FRANKENPHP_APP_PATH#~}" != "$FRANKENPHP_APP_PATH" ]; then
|
||||
FRANKENPHP_APP_PATH="$HOME${FRANKENPHP_APP_PATH#~}"
|
||||
fi
|
||||
# make absolute if relative
|
||||
case "$FRANKENPHP_APP_PATH" in
|
||||
/*) ABS_APP_PATH="$FRANKENPHP_APP_PATH" ;;
|
||||
*) ABS_APP_PATH="$(pwd)/$FRANKENPHP_APP_PATH" ;;
|
||||
esac
|
||||
MOUNT_LIST="$MOUNT_LIST -v $ABS_APP_PATH:/app/app"
|
||||
fi
|
||||
|
||||
# Run docker
|
||||
# shellcheck disable=SC2068
|
||||
# shellcheck disable=SC2086
|
||||
@@ -174,5 +224,5 @@ if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then
|
||||
set -ex
|
||||
$DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION /bin/bash
|
||||
else
|
||||
$DOCKER_EXECUTABLE run $PLATFORM_ARG --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 "${NEW_ARGS[@]}"
|
||||
fi
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
set -e
|
||||
|
||||
# This file is using docker to run commands
|
||||
SPC_DOCKER_VERSION=v5
|
||||
SPC_DOCKER_VERSION=v6
|
||||
|
||||
# Detect docker can run
|
||||
if ! which docker >/dev/null; then
|
||||
@@ -113,6 +113,7 @@ ENV PATH="/app/bin:/cmake/bin:/opt/rh/devtoolset-10/root/usr/bin:\$PATH"
|
||||
|
||||
ADD ./config /app/config
|
||||
RUN CC=gcc bin/spc doctor --auto-fix --debug
|
||||
RUN bin/spc install-pkg upx
|
||||
RUN if [ -f /app/buildroot/bin/re2c ]; then \
|
||||
cp /app/buildroot/bin/re2c /usr/local/bin/re2c ;\
|
||||
fi
|
||||
@@ -132,6 +133,11 @@ RUN curl -o automake.tgz -fsSL https://ftp.gnu.org/gnu/automake/automake-1.17.ta
|
||||
make && \
|
||||
make install && \
|
||||
ln -sf /usr/local/bin/automake /usr/bin/automake
|
||||
|
||||
RUN mv /app/pkgroot/\$(uname -m)-linux /app/pkgroot-private
|
||||
ADD bin/docker-entrypoint.sh /bin/docker-entrypoint.sh
|
||||
RUN chmod +x /bin/docker-entrypoint.sh
|
||||
ENTRYPOINT ["/bin/docker-entrypoint.sh"]
|
||||
EOF
|
||||
fi
|
||||
|
||||
@@ -152,6 +158,7 @@ MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/source:/app/source"
|
||||
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/dist:/app/dist"
|
||||
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/downloads:/app/downloads"
|
||||
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/pkgroot:/app/pkgroot"
|
||||
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/log:/app/log"
|
||||
if [ -f "$(pwd)/craft.yml" ]; then
|
||||
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/craft.yml:/app/craft.yml"
|
||||
fi
|
||||
@@ -167,6 +174,47 @@ if [ ! -z "$GITHUB_TOKEN" ]; then
|
||||
ENV_LIST="$ENV_LIST -e GITHUB_TOKEN=$GITHUB_TOKEN"
|
||||
fi
|
||||
|
||||
# Intercept and rewrite --with-frankenphp-app option, and mount host path to /app/app
|
||||
FRANKENPHP_APP_PATH=""
|
||||
NEW_ARGS=()
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--with-frankenphp-app=*)
|
||||
FRANKENPHP_APP_PATH="${1#*=}"
|
||||
NEW_ARGS+=("--with-frankenphp-app=/app/app")
|
||||
shift
|
||||
;;
|
||||
--with-frankenphp-app)
|
||||
if [ -n "${2:-}" ]; then
|
||||
FRANKENPHP_APP_PATH="$2"
|
||||
NEW_ARGS+=("--with-frankenphp-app=/app/app")
|
||||
shift 2
|
||||
else
|
||||
NEW_ARGS+=("$1")
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
NEW_ARGS+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Normalize the path and add mount if provided
|
||||
if [ -n "$FRANKENPHP_APP_PATH" ]; then
|
||||
# expand ~ to $HOME
|
||||
if [ "${FRANKENPHP_APP_PATH#~}" != "$FRANKENPHP_APP_PATH" ]; then
|
||||
FRANKENPHP_APP_PATH="$HOME${FRANKENPHP_APP_PATH#~}"
|
||||
fi
|
||||
# make absolute if relative
|
||||
case "$FRANKENPHP_APP_PATH" in
|
||||
/*) ABS_APP_PATH="$FRANKENPHP_APP_PATH" ;;
|
||||
*) ABS_APP_PATH="$(pwd)/$FRANKENPHP_APP_PATH" ;;
|
||||
esac
|
||||
MOUNT_LIST="$MOUNT_LIST -v $ABS_APP_PATH:/app/app"
|
||||
fi
|
||||
|
||||
# Run docker
|
||||
# shellcheck disable=SC2068
|
||||
# shellcheck disable=SC2086
|
||||
@@ -189,5 +237,5 @@ if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then
|
||||
set -ex
|
||||
$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
|
||||
$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 $@
|
||||
$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 "${NEW_ARGS[@]}"
|
||||
fi
|
||||
|
||||
577
composer.lock
generated
577
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,39 +1,41 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; static-php-cli (spc) env configuration
|
||||
;
|
||||
; This file is used to set default env vars for static-php-cli build.
|
||||
; As dynamic build process, some of these vars can be overwritten by CLI options.
|
||||
; And you can also overwrite these vars by setting them in your shell environment.
|
||||
; The value should be changed only if you know what you are doing. Otherwise, please leave them as default.
|
||||
;
|
||||
; We need to use some pre-defined internal env vars, like `BUILD_ROOT_PATH`, `DOWNLOAD_PATH`, etc.
|
||||
; Please note that these vars cannot be defined in this file, they are only be defined before static-php-cli running.
|
||||
; Please note that these vars cannot be defined in this file, they should only be defined before static-php-cli running.
|
||||
;
|
||||
; Here's a list of env vars, these value cannot be changed anywhere:
|
||||
; Here's a list of env vars, these variables will be defined if not defined:
|
||||
;
|
||||
; SPC_VERSION: the version of static-php-cli.
|
||||
; WORKING_DIR: the working directory of the build process. (default: `$(pwd)`)
|
||||
; ROOT_DIR: the root directory of static-php-cli. (default: `/path/to/static-php-cli`, when running in phar or micro mode: `phar://path/to/spc.phar`)
|
||||
; BUILD_ROOT_PATH: the root path of the build process. (default: `$(pwd)/buildroot`)
|
||||
; BUILD_INCLUDE_PATH: the path of the include files. (default: `$BUILD_ROOT_PATH/include`)
|
||||
; BUILD_LIB_PATH: the path of the lib files. (default: `$BUILD_ROOT_PATH/lib`)
|
||||
; BUILD_BIN_PATH: the path of the bin files. (default: `$BUILD_ROOT_PATH/bin`)
|
||||
; PKG_ROOT_PATH: the root path of the package files. (default: `$(pwd)/pkgroot`)
|
||||
; BUILD_MODULES_PATH: the path of the php modules (shared extensions) files. (default: `$BUILD_ROOT_PATH/modules`)
|
||||
; PKG_ROOT_PATH: the root path of the package files. (default: `$(pwd)/pkgroot/$GNU_ARCH-{darwin|linux|windows}`)
|
||||
; SOURCE_PATH: the path of the source files. (default: `$(pwd)/source`)
|
||||
; DOWNLOAD_PATH: the path of the download files. (default: `$(pwd)/downloads`)
|
||||
; CPU_COUNT: the count of the CPU cores. (default: `$(nproc)`)
|
||||
; SPC_ARCH: the arch of the current system, for some libraries needed `--host=XXX` args. (default: `$(uname -m)`, e.g. `x86_64`, `aarch64`, `arm64`)
|
||||
; GNU_ARCH: the GNU arch of the current system. (default: `$(uname -m)`, e.g. `x86_64`, `aarch64`)
|
||||
; MAC_ARCH: the MAC arch of the current system. (default: `$(uname -m)`, e.g. `x86_64`, `arm64`)
|
||||
; PATH: (*nix only) static-php-cli will add `$BUILD_BIN_PATH` to PATH.
|
||||
; PKG_CONFIG_PATH: (*nix only) static-php-cli will set `$BUILD_LIB_PATH/pkgconfig` to PKG_CONFIG_PATH.
|
||||
;
|
||||
; * These vars are only be defined in Unix (macOS, Linux, FreeBSD)Builder and cannot be changed anywhere:
|
||||
; PATH: static-php-cli will add `$BUILD_BIN_PATH` to PATH.
|
||||
; PKG_CONFIG: static-php-cli will set `$BUILD_BIN_PATH/pkg-config` to PKG_CONFIG.
|
||||
; PKG_CONFIG_PATH: static-php-cli will set `$BUILD_LIB_PATH/pkgconfig` to PKG_CONFIG_PATH.
|
||||
; Here's a list of env vars, these variables is defined in SPC and cannot be changed anywhere:
|
||||
;
|
||||
; * These vars are only be defined in LinuxBuilder and cannot be changed anywhere:
|
||||
; SPC_LINUX_DEFAULT_CC: the default compiler for linux. (For alpine linux: `gcc`, default: `$GNU_ARCH-linux-musl-gcc`)
|
||||
; SPC_LINUX_DEFAULT_CXX: the default c++ compiler for linux. (For alpine linux: `g++`, default: `$GNU_ARCH-linux-musl-g++`)
|
||||
; SPC_LINUX_DEFAULT_AR: the default archiver for linux. (For alpine linux: `ar`, default: `$GNU_ARCH-linux-musl-ar`)
|
||||
; SPC_VERSION: the version of static-php-cli.
|
||||
; WORKING_DIR: the working directory of the build process. (default: `$(pwd)`)
|
||||
; ROOT_DIR: the root directory of static-php-cli. (default: `/path/to/static-php-cli`, when running in phar or micro mode: `phar://path/to/spc.phar`)
|
||||
; CPU_COUNT: the count of the CPU cores. (default: `$(nproc)`)
|
||||
; SPC_ARCH: the arch of the current system, for some libraries needed `--host=XXX` args. (default: `$(uname -m)`, e.g. `x86_64`, `aarch64`, `arm64`)
|
||||
; GNU_ARCH: the GNU arch of the current system. (default: `$(uname -m)`, e.g. `x86_64`, `aarch64`)
|
||||
; MAC_ARCH: the MAC arch of the current system. (default: `$(uname -m)`, e.g. `x86_64`, `arm64`)
|
||||
; PKG_CONFIG: (*nix only) static-php-cli will set `$BUILD_BIN_PATH/pkg-config` to PKG_CONFIG.
|
||||
; SPC_LINUX_DEFAULT_CC: (linux only) the default compiler for linux. (For alpine linux: `gcc`, default: `$GNU_ARCH-linux-musl-gcc`)
|
||||
; SPC_LINUX_DEFAULT_CXX: (linux only) the default c++ compiler for linux. (For alpine linux: `g++`, default: `$GNU_ARCH-linux-musl-g++`)
|
||||
; SPC_LINUX_DEFAULT_AR: (linux only) the default archiver for linux. (For alpine linux: `ar`, default: `$GNU_ARCH-linux-musl-ar`)
|
||||
; SPC_EXTRA_PHP_VARS: (linux only) the extra vars for building php, used in `configure` and `make` command.
|
||||
|
||||
[global]
|
||||
; Build concurrency for make -jN, default is CPU_COUNT, this value are used in every libs.
|
||||
@@ -43,7 +45,7 @@ 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")
|
||||
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"
|
||||
SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy --with github.com/dunglas/caddy-cbrotli"
|
||||
; The display message for php version output (PHP >= 8.4 available)
|
||||
PHP_BUILD_PROVIDER="static-php-cli ${SPC_VERSION}"
|
||||
|
||||
@@ -64,7 +66,7 @@ PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools"
|
||||
; upx executable path
|
||||
UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe"
|
||||
; 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,cli_static
|
||||
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static,win32_api
|
||||
|
||||
[linux]
|
||||
; Linux can use different build toolchains.
|
||||
@@ -103,16 +105,12 @@ SPC_MICRO_PATCHES=cli_checks,disable_huge_page
|
||||
; buildconf command
|
||||
SPC_CMD_PREFIX_PHP_BUILDCONF="./buildconf --force"
|
||||
; configure command
|
||||
SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --disable-shared --enable-static --disable-all --disable-cgi --disable-phpdbg --with-pic"
|
||||
; make command
|
||||
SPC_CMD_PREFIX_PHP_MAKE="make -j${SPC_CONCURRENCY}"
|
||||
SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --disable-shared --enable-static --disable-all --disable-phpdbg --with-pic"
|
||||
|
||||
; *** default build vars for building php ***
|
||||
; embed type for php, static (libphp.a) or shared (libphp.so)
|
||||
SPC_CMD_VAR_PHP_EMBED_TYPE="static"
|
||||
; CFLAGS for configuring php
|
||||
SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -fPIE"
|
||||
; EXTRA_CFLAGS for `make` php
|
||||
; EXTRA_CFLAGS for `configure` and `make` php
|
||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE ${SPC_DEFAULT_C_FLAGS}"
|
||||
; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so
|
||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS=""
|
||||
@@ -137,17 +135,13 @@ SPC_MICRO_PATCHES=cli_checks,macos_iconv
|
||||
; buildconf command
|
||||
SPC_CMD_PREFIX_PHP_BUILDCONF="./buildconf --force"
|
||||
; configure command
|
||||
SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg"
|
||||
; make command
|
||||
SPC_CMD_PREFIX_PHP_MAKE="make -j${SPC_CONCURRENCY}"
|
||||
SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-phpdbg"
|
||||
|
||||
; *** default build vars for building php ***
|
||||
; embed type for php, static (libphp.a) or shared (libphp.dylib)
|
||||
SPC_CMD_VAR_PHP_EMBED_TYPE="static"
|
||||
; CFLAGS for configuring php
|
||||
SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -Werror=unknown-warning-option"
|
||||
; EXTRA_CFLAGS for `make` php
|
||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie ${SPC_DEFAULT_C_FLAGS}"
|
||||
; EXTRA_CFLAGS for `configure` and `make` php
|
||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -Werror=unknown-warning-option ${SPC_DEFAULT_C_FLAGS}"
|
||||
|
||||
[freebsd]
|
||||
; compiler environments
|
||||
|
||||
@@ -341,6 +341,7 @@
|
||||
"ext-depends": [
|
||||
"xml"
|
||||
],
|
||||
"build-with-php": true,
|
||||
"target": [
|
||||
"static"
|
||||
]
|
||||
@@ -414,9 +415,17 @@
|
||||
"libmemcached",
|
||||
"fastlz"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"zstd"
|
||||
],
|
||||
"ext-depends": [
|
||||
"session",
|
||||
"zlib"
|
||||
],
|
||||
"ext-suggests": [
|
||||
"igbinary",
|
||||
"msgpack",
|
||||
"session"
|
||||
]
|
||||
},
|
||||
"mongodb": {
|
||||
@@ -445,7 +454,7 @@
|
||||
"type": "external",
|
||||
"source": "msgpack",
|
||||
"arg-type-unix": "with",
|
||||
"arg-type-win": "enable",
|
||||
"arg-type-windows": "enable",
|
||||
"ext-depends": [
|
||||
"session"
|
||||
]
|
||||
@@ -453,6 +462,7 @@
|
||||
"mysqli": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"build-with-php": true,
|
||||
"ext-depends": [
|
||||
"mysqlnd"
|
||||
]
|
||||
@@ -460,6 +470,7 @@
|
||||
"mysqlnd": {
|
||||
"type": "builtin",
|
||||
"arg-type-windows": "with",
|
||||
"build-with-php": true,
|
||||
"lib-depends": [
|
||||
"zlib"
|
||||
]
|
||||
@@ -679,7 +690,7 @@
|
||||
"type": "builtin",
|
||||
"arg-type": "with-path",
|
||||
"lib-depends": [
|
||||
"readline"
|
||||
"libedit"
|
||||
],
|
||||
"target": [
|
||||
"static"
|
||||
@@ -745,17 +756,27 @@
|
||||
"apcu"
|
||||
]
|
||||
},
|
||||
"snmp": {
|
||||
"support": {
|
||||
"Windows": "wip",
|
||||
"BSD": "wip"
|
||||
},
|
||||
"type": "builtin",
|
||||
"arg-type-unix": "with",
|
||||
"arg-type-windows": "with",
|
||||
"lib-depends": [
|
||||
"net-snmp"
|
||||
]
|
||||
},
|
||||
"soap": {
|
||||
"support": {
|
||||
"BSD": "wip"
|
||||
},
|
||||
"type": "builtin",
|
||||
"arg-type": "custom",
|
||||
"lib-depends": [
|
||||
"libxml2"
|
||||
],
|
||||
"ext-depends-windows": [
|
||||
"xml"
|
||||
"ext-depends": [
|
||||
"libxml",
|
||||
"session"
|
||||
]
|
||||
},
|
||||
"sockets": {
|
||||
@@ -791,6 +812,7 @@
|
||||
"type": "builtin",
|
||||
"arg-type": "with-path",
|
||||
"arg-type-windows": "with",
|
||||
"build-with-php": true,
|
||||
"lib-depends": [
|
||||
"sqlite"
|
||||
]
|
||||
@@ -879,6 +901,22 @@
|
||||
"mysqli"
|
||||
]
|
||||
},
|
||||
"swoole-hook-odbc": {
|
||||
"support": {
|
||||
"Windows": "no",
|
||||
"BSD": "wip"
|
||||
},
|
||||
"notes": true,
|
||||
"type": "addon",
|
||||
"arg-type": "none",
|
||||
"ext-depends": [
|
||||
"pdo",
|
||||
"swoole"
|
||||
],
|
||||
"lib-depends": [
|
||||
"unixodbc"
|
||||
]
|
||||
},
|
||||
"swoole-hook-pgsql": {
|
||||
"support": {
|
||||
"Windows": "no",
|
||||
@@ -908,22 +946,6 @@
|
||||
"swoole"
|
||||
]
|
||||
},
|
||||
"swoole-hook-odbc": {
|
||||
"support": {
|
||||
"Windows": "no",
|
||||
"BSD": "wip"
|
||||
},
|
||||
"notes": true,
|
||||
"type": "addon",
|
||||
"arg-type": "none",
|
||||
"ext-depends": [
|
||||
"pdo",
|
||||
"swoole"
|
||||
],
|
||||
"lib-depends": [
|
||||
"unixodbc"
|
||||
]
|
||||
},
|
||||
"swow": {
|
||||
"support": {
|
||||
"BSD": "wip"
|
||||
@@ -978,6 +1000,14 @@
|
||||
"type": "builtin",
|
||||
"build-with-php": true
|
||||
},
|
||||
"trader": {
|
||||
"support": {
|
||||
"BSD": "wip",
|
||||
"Windows": "wip"
|
||||
},
|
||||
"type": "external",
|
||||
"source": "ext-trader"
|
||||
},
|
||||
"uuid": {
|
||||
"support": {
|
||||
"Windows": "wip",
|
||||
@@ -1146,8 +1176,9 @@
|
||||
"support": {
|
||||
"BSD": "wip"
|
||||
},
|
||||
"type": "builtin",
|
||||
"arg-type": "with-path",
|
||||
"type": "external",
|
||||
"source": "ext-zip",
|
||||
"arg-type": "custom",
|
||||
"arg-type-windows": "enable",
|
||||
"lib-depends-unix": [
|
||||
"libzip"
|
||||
@@ -1170,6 +1201,7 @@
|
||||
"lib-depends": [
|
||||
"zlib"
|
||||
],
|
||||
"build-with-php": true,
|
||||
"target": [
|
||||
"static"
|
||||
]
|
||||
|
||||
@@ -7,18 +7,29 @@
|
||||
"source": "php-src",
|
||||
"lib-depends": [
|
||||
"lib-base",
|
||||
"micro"
|
||||
"micro",
|
||||
"frankenphp"
|
||||
],
|
||||
"lib-depends-macos": [
|
||||
"lib-base",
|
||||
"micro",
|
||||
"libxml2",
|
||||
"frankenphp"
|
||||
],
|
||||
"lib-suggests-linux": [
|
||||
"libacl",
|
||||
"brotli",
|
||||
"watcher"
|
||||
],
|
||||
"lib-suggests-unix": [
|
||||
"lib-suggests-macos": [
|
||||
"brotli",
|
||||
"watcher"
|
||||
]
|
||||
},
|
||||
"frankenphp": {
|
||||
"source": "frankenphp",
|
||||
"type": "target"
|
||||
},
|
||||
"micro": {
|
||||
"type": "target",
|
||||
"source": "micro"
|
||||
@@ -197,7 +208,7 @@
|
||||
"openssl",
|
||||
"libcares"
|
||||
],
|
||||
"provide-pre-built": true,
|
||||
"cpp-library": true,
|
||||
"frameworks": [
|
||||
"CoreFoundation"
|
||||
]
|
||||
@@ -225,6 +236,7 @@
|
||||
},
|
||||
"imagemagick": {
|
||||
"source": "imagemagick",
|
||||
"cpp-library": true,
|
||||
"pkg-configs": [
|
||||
"Magick++-7.Q16HDRI",
|
||||
"MagickCore-7.Q16HDRI",
|
||||
@@ -338,6 +350,15 @@
|
||||
],
|
||||
"cpp-library": true
|
||||
},
|
||||
"libedit": {
|
||||
"source": "libedit",
|
||||
"static-libs-unix": [
|
||||
"libedit.a"
|
||||
],
|
||||
"lib-depends": [
|
||||
"ncurses"
|
||||
]
|
||||
},
|
||||
"libevent": {
|
||||
"source": "libevent",
|
||||
"static-libs-unix": [
|
||||
@@ -447,6 +468,7 @@
|
||||
},
|
||||
"libmemcached": {
|
||||
"source": "libmemcached",
|
||||
"cpp-library": true,
|
||||
"static-libs-unix": [
|
||||
"libmemcached.a",
|
||||
"libmemcachedprotocol.a",
|
||||
@@ -490,13 +512,16 @@
|
||||
},
|
||||
"librdkafka": {
|
||||
"source": "librdkafka",
|
||||
"static-libs-unix": [
|
||||
"librdkafka.a",
|
||||
"librdkafka++.a",
|
||||
"librdkafka-static.a"
|
||||
"pkg-configs": [
|
||||
"rdkafka++-static",
|
||||
"rdkafka-static"
|
||||
],
|
||||
"cpp-library": true,
|
||||
"lib-suggests": [
|
||||
"curl",
|
||||
"liblz4",
|
||||
"openssl",
|
||||
"zlib",
|
||||
"zstd"
|
||||
]
|
||||
},
|
||||
@@ -543,6 +568,21 @@
|
||||
"zstd"
|
||||
]
|
||||
},
|
||||
"liburing": {
|
||||
"source": "liburing",
|
||||
"pkg-configs": [
|
||||
"liburing",
|
||||
"liburing-ffi"
|
||||
],
|
||||
"static-libs-linux": [
|
||||
"liburing.a",
|
||||
"liburing-ffi.a"
|
||||
],
|
||||
"headers-linux": [
|
||||
"liburing/",
|
||||
"liburing.h"
|
||||
]
|
||||
},
|
||||
"libuuid": {
|
||||
"source": "libuuid",
|
||||
"static-libs-unix": [
|
||||
@@ -576,8 +616,8 @@
|
||||
},
|
||||
"libxml2": {
|
||||
"source": "libxml2",
|
||||
"static-libs-unix": [
|
||||
"libxml2.a"
|
||||
"pkg-configs": [
|
||||
"libxml-2.0"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
"libxml2s.lib",
|
||||
@@ -591,7 +631,6 @@
|
||||
],
|
||||
"lib-suggests-unix": [
|
||||
"xz",
|
||||
"icu",
|
||||
"zlib"
|
||||
],
|
||||
"lib-depends-windows": [
|
||||
@@ -667,6 +706,17 @@
|
||||
"libncurses.a"
|
||||
]
|
||||
},
|
||||
"net-snmp": {
|
||||
"source": "net-snmp",
|
||||
"pkg-configs": [
|
||||
"netsnmp",
|
||||
"netsnmp-agent"
|
||||
],
|
||||
"lib-depends": [
|
||||
"openssl",
|
||||
"zlib"
|
||||
]
|
||||
},
|
||||
"nghttp2": {
|
||||
"source": "nghttp2",
|
||||
"static-libs-unix": [
|
||||
@@ -765,7 +815,7 @@
|
||||
"libxml2",
|
||||
"openssl",
|
||||
"zlib",
|
||||
"readline"
|
||||
"libedit"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"icu",
|
||||
@@ -817,6 +867,7 @@
|
||||
},
|
||||
"snappy": {
|
||||
"source": "snappy",
|
||||
"cpp-library": true,
|
||||
"static-libs-unix": [
|
||||
"libsnappy.a"
|
||||
],
|
||||
@@ -862,6 +913,7 @@
|
||||
},
|
||||
"watcher": {
|
||||
"source": "watcher",
|
||||
"cpp-library": true,
|
||||
"static-libs-unix": [
|
||||
"libwatcher-c.a"
|
||||
],
|
||||
@@ -937,5 +989,9 @@
|
||||
"liburing/",
|
||||
"liburing.h"
|
||||
]
|
||||
},
|
||||
"frankenphp": {
|
||||
"source": "frankenphp",
|
||||
"type": "target"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
"type": "url",
|
||||
"url": "https://dl.static-php.dev/static-php-cli/deps/nasm/nasm-2.16.01-win64.zip",
|
||||
"extract-files": {
|
||||
"nasm-2.16.01/nasm.exe": "{php_sdk_path}/bin/nasm.exe",
|
||||
"nasm-2.16.01/ndisasm.exe": "{php_sdk_path}/bin/ndisasm.exe"
|
||||
"nasm.exe": "{php_sdk_path}/bin/nasm.exe",
|
||||
"ndisasm.exe": "{php_sdk_path}/bin/ndisasm.exe"
|
||||
}
|
||||
},
|
||||
"pkg-config-aarch64-linux": {
|
||||
@@ -84,7 +84,7 @@
|
||||
"repo": "upx/upx",
|
||||
"match": "upx.+-win64\\.zip",
|
||||
"extract-files": {
|
||||
"upx-*-win64/upx.exe": "{pkg_root_path}/bin/upx.exe"
|
||||
"upx.exe": "{pkg_root_path}/bin/upx.exe"
|
||||
}
|
||||
},
|
||||
"zig-aarch64-linux": {
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"provide-pre-built": true,
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "doc/COPYING"
|
||||
"path": "doc/COPYING.LGPL"
|
||||
}
|
||||
},
|
||||
"brotli": {
|
||||
@@ -233,6 +233,16 @@
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"ext-trader": {
|
||||
"type": "url",
|
||||
"url": "https://pecl.php.net/get/trader",
|
||||
"path": "php-src/ext/trader",
|
||||
"filename": "trader.tgz",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"ext-uuid": {
|
||||
"type": "url",
|
||||
"url": "https://pecl.php.net/get/uuid",
|
||||
@@ -263,6 +273,15 @@
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"ext-zip": {
|
||||
"type": "url",
|
||||
"url": "https://pecl.php.net/get/zip",
|
||||
"filename": "ext-zip.tgz",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"ext-zstd": {
|
||||
"type": "git",
|
||||
"path": "php-src/ext/zstd",
|
||||
@@ -282,10 +301,20 @@
|
||||
"path": "LICENSE.MIT"
|
||||
}
|
||||
},
|
||||
"frankenphp": {
|
||||
"type": "ghtar",
|
||||
"repo": "php/frankenphp",
|
||||
"prefer-stable": true,
|
||||
"provide-pre-built": false,
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"freetype": {
|
||||
"type": "git",
|
||||
"rev": "VER-2-13-2",
|
||||
"url": "https://github.com/freetype/freetype",
|
||||
"type": "ghtagtar",
|
||||
"repo": "freetype/freetype",
|
||||
"match": "VER-2-\\d+-\\d+",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "LICENSE.TXT"
|
||||
@@ -297,16 +326,17 @@
|
||||
"regex": "/href=\"(?<file>gettext-(?<version>[^\"]+)\\.tar\\.xz)\"/",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "COPYING"
|
||||
"path": "gettext-runtime/intl/COPYING.LIB"
|
||||
}
|
||||
},
|
||||
"gmp": {
|
||||
"type": "url",
|
||||
"url": "https://dl.static-php.dev/static-php-cli/deps/gmp/gmp-6.3.0.tar.xz",
|
||||
"type": "filelist",
|
||||
"url": "https://gmplib.org/download/gmp/",
|
||||
"regex": "/href=\"(?<file>gmp-(?<version>[^\"]+)\\.tar\\.xz)\"/",
|
||||
"provide-pre-built": true,
|
||||
"alt": {
|
||||
"type": "ghtagtar",
|
||||
"repo": "alisw/GMP"
|
||||
"type": "url",
|
||||
"url": "https://dl.static-php.dev/static-php-cli/deps/gmp/gmp-6.3.0.tar.xz"
|
||||
},
|
||||
"license": {
|
||||
"type": "text",
|
||||
@@ -324,7 +354,7 @@
|
||||
},
|
||||
"grpc": {
|
||||
"type": "git",
|
||||
"rev": "v1.68.x",
|
||||
"rev": "v1.75.x",
|
||||
"url": "https://github.com/grpc/grpc.git",
|
||||
"provide-pre-built": true,
|
||||
"license": {
|
||||
@@ -343,6 +373,16 @@
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"frankenphp": {
|
||||
"type": "ghtar",
|
||||
"repo": "php/frankenphp",
|
||||
"prefer-stable": true,
|
||||
"provide-pre-build": false,
|
||||
"license": {
|
||||
"type": "file",
|
||||
"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",
|
||||
@@ -430,7 +470,7 @@
|
||||
"provide-pre-built": true,
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "doc/COPYING"
|
||||
"path": "doc/COPYING.LGPL"
|
||||
}
|
||||
},
|
||||
"libaom": {
|
||||
@@ -489,6 +529,16 @@
|
||||
"path": "COPYING"
|
||||
}
|
||||
},
|
||||
"libedit": {
|
||||
"type": "filelist",
|
||||
"url": "https://thrysoee.dk/editline/",
|
||||
"regex": "/href=\"(?<file>libedit-(?<version>[^\"]+)\\.tar\\.gz)\"/",
|
||||
"provide-pre-built": true,
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "COPYING"
|
||||
}
|
||||
},
|
||||
"libevent": {
|
||||
"type": "ghrel",
|
||||
"repo": "libevent/libevent",
|
||||
@@ -537,7 +587,7 @@
|
||||
"provide-pre-built": true,
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "COPYING"
|
||||
"path": "COPYING.LIB"
|
||||
}
|
||||
},
|
||||
"libiconv-win": {
|
||||
@@ -650,6 +700,15 @@
|
||||
"path": "LICENSE.md"
|
||||
}
|
||||
},
|
||||
"liburing": {
|
||||
"type": "ghtar",
|
||||
"repo": "axboe/liburing",
|
||||
"prefer-stable": true,
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "COPYING"
|
||||
}
|
||||
},
|
||||
"libuuid": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/static-php/libuuid.git",
|
||||
@@ -733,7 +792,7 @@
|
||||
"micro": {
|
||||
"type": "git",
|
||||
"path": "php-src/sapi/micro",
|
||||
"rev": "php-85-win",
|
||||
"rev": "master",
|
||||
"url": "https://github.com/static-php/phpmicro",
|
||||
"license": {
|
||||
"type": "file",
|
||||
@@ -781,6 +840,14 @@
|
||||
"path": "COPYING"
|
||||
}
|
||||
},
|
||||
"net-snmp": {
|
||||
"type": "ghtagtar",
|
||||
"repo": "net-snmp/net-snmp",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "COPYING"
|
||||
}
|
||||
},
|
||||
"nghttp2": {
|
||||
"type": "ghrel",
|
||||
"repo": "nghttp2/nghttp2",
|
||||
@@ -880,7 +947,7 @@
|
||||
"postgresql": {
|
||||
"type": "ghtagtar",
|
||||
"repo": "postgres/postgres",
|
||||
"match": "REL_16_\\d+",
|
||||
"match": "REL_18_\\d+",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "COPYRIGHT"
|
||||
@@ -971,7 +1038,6 @@
|
||||
},
|
||||
"snappy": {
|
||||
"type": "git",
|
||||
"repo": "google/snappy",
|
||||
"rev": "main",
|
||||
"url": "https://github.com/google/snappy",
|
||||
"license": {
|
||||
@@ -980,9 +1046,8 @@
|
||||
}
|
||||
},
|
||||
"spx": {
|
||||
"type": "git",
|
||||
"rev": "master",
|
||||
"url": "https://github.com/static-php/php-spx.git",
|
||||
"type": "pie",
|
||||
"repo": "noisebynorthwest/php-spx",
|
||||
"path": "php-src/ext/spx",
|
||||
"license": {
|
||||
"type": "file",
|
||||
@@ -1136,14 +1201,5 @@
|
||||
"type": "file",
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"liburing": {
|
||||
"type": "ghtar",
|
||||
"repo": "axboe/liburing",
|
||||
"prefer-stable": true,
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "COPYING"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,6 +379,11 @@ const craftCommandString = computed(() => {
|
||||
str += 'debug: true\n';
|
||||
}
|
||||
|
||||
if (preBuilt.value) {
|
||||
str += 'download-options:\n';
|
||||
str += ' prefer-pre-built: true\n';
|
||||
}
|
||||
|
||||
str += '{{position_hold}}';
|
||||
|
||||
if (enableUPX.value) {
|
||||
@@ -387,9 +392,6 @@ const craftCommandString = computed(() => {
|
||||
if (zts.value) {
|
||||
str += ' enable-zts: true\n';
|
||||
}
|
||||
if (preBuilt.value) {
|
||||
str += ' prefer-pre-built: true\n';
|
||||
}
|
||||
|
||||
if (!str.endsWith('{{position_hold}}')) {
|
||||
str = str.replace('{{position_hold}}', 'build-options:\n');
|
||||
@@ -742,10 +744,26 @@ h2 {
|
||||
}
|
||||
|
||||
select {
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
padding: 0 4px;
|
||||
padding: 8px 12px;
|
||||
width: 300px;
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
select:hover {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
background-color: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
select:focus {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
box-shadow: 0 0 0 3px var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.my-btn {
|
||||
@@ -779,17 +797,160 @@ select {
|
||||
|
||||
.textarea {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
width: calc(100% - 12px);
|
||||
padding: 4px 8px;
|
||||
border-radius: 8px;
|
||||
width: calc(100% - 24px);
|
||||
padding: 12px;
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 14px;
|
||||
font-family: var(--vp-font-family-mono);
|
||||
line-height: 1.5;
|
||||
transition: all 0.2s ease;
|
||||
outline: none;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.textarea:hover {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
background-color: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.textarea:focus {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
box-shadow: 0 0 0 3px var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.input {
|
||||
display: block;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
padding: 4px 8px;
|
||||
padding: 10px 12px;
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.input:hover {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
background-color: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
box-shadow: 0 0 0 3px var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
/* Radio button styles */
|
||||
input[type="radio"] {
|
||||
appearance: none;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: 2px solid var(--vp-c-border);
|
||||
border-radius: 50%;
|
||||
background-color: var(--vp-c-bg);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
margin-right: 6px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
input[type="radio"]:hover {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
input[type="radio"]:checked {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
background-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
input[type="radio"]:checked::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
input[type="radio"]:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Checkbox styles */
|
||||
input[type="checkbox"] {
|
||||
appearance: none;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: 2px solid var(--vp-c-border);
|
||||
border-radius: 4px;
|
||||
background-color: var(--vp-c-bg);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
margin-right: 6px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
input[type="checkbox"]:hover {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
background-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 5px;
|
||||
width: 4px;
|
||||
height: 8px;
|
||||
border: solid var(--vp-c-bg);
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Label styles */
|
||||
label {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
label:hover {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
input[type="radio"]:disabled + label,
|
||||
input[type="checkbox"]:disabled + label {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
input[type="radio"]:disabled + label:hover,
|
||||
input[type="checkbox"]:disabled + label:hover {
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.command-container {
|
||||
|
||||
219
docs/.vitepress/components/Contributors.vue
Normal file
219
docs/.vitepress/components/Contributors.vue
Normal file
@@ -0,0 +1,219 @@
|
||||
<template>
|
||||
<div class="contributors-container">
|
||||
<div class="contributors-header">
|
||||
<h2>Contributors</h2>
|
||||
<p class="contributors-description">
|
||||
Thanks to all the amazing people who have contributed to this project!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="loading-state">
|
||||
<div class="spinner"></div>
|
||||
<p>Loading contributors...</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="error" class="error-state">
|
||||
<p>{{ error }}</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="contributors-grid">
|
||||
<a
|
||||
v-for="contributor in contributors"
|
||||
:key="contributor.id"
|
||||
:href="contributor.html_url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="contributor-card"
|
||||
:title="contributor.login"
|
||||
>
|
||||
<img
|
||||
:src="contributor.avatar_url"
|
||||
:alt="contributor.login"
|
||||
class="contributor-avatar"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div class="contributor-name">{{ contributor.login }}</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
interface Contributor {
|
||||
id: number;
|
||||
login: string;
|
||||
avatar_url: string;
|
||||
html_url: string;
|
||||
contributions: number;
|
||||
}
|
||||
|
||||
const contributors = ref<Contributor[]>([]);
|
||||
const loading = ref(true);
|
||||
const error = ref('');
|
||||
|
||||
const fetchContributors = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
error.value = '';
|
||||
|
||||
const response = await fetch(
|
||||
'https://api.github.com/repos/crazywhalecc/static-php-cli/contributors?per_page=24'
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch contributors');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
contributors.value = data;
|
||||
} catch (err) {
|
||||
error.value = 'Failed to load contributors. Please try again later.';
|
||||
console.error('Error fetching contributors:', err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchContributors();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.contributors-container {
|
||||
margin: 48px auto;
|
||||
padding: 32px 24px;
|
||||
max-width: 1152px;
|
||||
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-bg) 100%);
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.contributors-header {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.contributors-header h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 8px 0;
|
||||
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.contributors-description {
|
||||
font-size: 0.95rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.loading-state,
|
||||
.error-state {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 0 auto 16px;
|
||||
border: 4px solid var(--vp-c-divider);
|
||||
border-top-color: var(--vp-c-brand-1);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.contributors-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.contributor-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
transition: all 0.3s ease;
|
||||
text-decoration: none;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.contributor-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||
border-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.contributor-avatar {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
transition: all 0.3s ease;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.contributor-card:hover .contributor-avatar {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.contributor-name {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
word-break: break-word;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.contributors-container {
|
||||
margin: 32px 16px;
|
||||
padding: 24px 16px;
|
||||
}
|
||||
|
||||
.contributors-header h2 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.contributors-description {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.contributors-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.contributor-card {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.contributor-avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.contributor-name {
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -4,7 +4,7 @@ import sidebarZh from "./sidebar.zh";
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default {
|
||||
title: "static-php-cli",
|
||||
title: "Static PHP",
|
||||
description: "Build single static PHP binary, with PHP project together, with popular extensions included.",
|
||||
locales: {
|
||||
en: {
|
||||
@@ -44,6 +44,7 @@ export default {
|
||||
},
|
||||
themeConfig: {
|
||||
// https://vitepress.dev/reference/default-theme-config
|
||||
logo: '/images/static-php_nobg.png',
|
||||
nav: [],
|
||||
socialLinks: [
|
||||
{icon: 'github', link: 'https://github.com/crazywhalecc/static-php-cli'}
|
||||
|
||||
@@ -4,3 +4,21 @@
|
||||
max-width: 1000px !important;
|
||||
}
|
||||
|
||||
|
||||
.vp-doc .contributors-header h2 {
|
||||
padding-top: 0;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.vp-doc .sponsors-header h2 {
|
||||
padding-top: 0;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.dark .VPImage.logo {
|
||||
filter: contrast(0.7);
|
||||
}
|
||||
|
||||
.dark .VPImage.image-src {
|
||||
filter: contrast(0.7);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ The following is the source download configuration corresponding to the `libeven
|
||||
The most important field here is `type`. Currently, the types it supports are:
|
||||
|
||||
- `url`: Directly use URL to download, for example: `https://download.libsodium.org/libsodium/releases/libsodium-1.0.18.tar.gz`.
|
||||
- `pie`: Download PHP extensions from Packagist using the PIE (PHP Installer for Extensions) standard.
|
||||
- `ghrel`: Use the GitHub Release API to download, download the artifacts uploaded from the latest version released by maintainers.
|
||||
- `ghtar`: Use the GitHub Release API to download.
|
||||
Different from `ghrel`, `ghtar` is downloaded from the `source code (tar.gz)` in the latest Release of the project.
|
||||
@@ -89,6 +90,37 @@ Example (download the imagick extension and extract it to the extension storage
|
||||
}
|
||||
```
|
||||
|
||||
## Download type - pie
|
||||
|
||||
PIE (PHP Installer for Extensions) type sources refer to downloading PHP extensions from Packagist that follow the PIE standard.
|
||||
This method automatically fetches extension information from the Packagist repository and downloads the appropriate distribution file.
|
||||
|
||||
The parameters included are:
|
||||
|
||||
- `repo`: The Packagist vendor/package name, such as `vendor/package-name`
|
||||
|
||||
Example (download a PHP extension from Packagist using PIE):
|
||||
|
||||
```json
|
||||
{
|
||||
"ext-example": {
|
||||
"type": "pie",
|
||||
"repo": "vendor/example-extension",
|
||||
"path": "php-src/ext/example",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "LICENSE"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
::: tip
|
||||
The PIE download type will automatically detect the extension information from Packagist metadata,
|
||||
including the download URL, version, and distribution type.
|
||||
The extension must be marked as `type: php-ext` or contain `php-ext` metadata in its Packagist package definition.
|
||||
:::
|
||||
|
||||
## Download type - ghrel
|
||||
|
||||
ghrel will download files from Assets uploaded in GitHub Release.
|
||||
|
||||
@@ -44,6 +44,11 @@ So on macOS, you can **directly** use SPC to build statically compiled PHP binar
|
||||
2. You will get `buildroot/modules/xdebug.so` and `buildroot/bin/php`.
|
||||
3. The `xdebug.so` file could be used for php that version and thread-safe are the same.
|
||||
|
||||
For the Windows platform, since officially built extensions (such as `php_yaml.dll`) force the use of the `php8.dll` dynamic library as a link, and statically built PHP does not include any dynamic libraries other than system libraries,
|
||||
php.exe built by static-php cannot load officially built dynamic extensions. Since static-php-cli does not yet support building dynamic extensions, there is currently no way to load dynamic extensions with static-php.
|
||||
|
||||
However, Windows can normally use the `FFI` extension to load other dll files and call them.
|
||||
|
||||
## Can it support Oracle database extension?
|
||||
|
||||
Some extensions that rely on closed source libraries, such as `oci8`, `sourceguardian`, etc.,
|
||||
|
||||
@@ -21,19 +21,30 @@ The following is the architecture support situation, where :gear: represents sup
|
||||
| Windows | :gear: :computer: | |
|
||||
| FreeBSD | :computer: | :computer: |
|
||||
|
||||
Among them, Linux is currently only tested on Ubuntu, Debian, and Alpine distributions,
|
||||
and other distributions have not been tested, which cannot guarantee successful compilation.
|
||||
For untested distributions, local compilation can be done using methods such as Docker to avoid environmental issues.
|
||||
Current supported PHP versions for compilation:
|
||||
|
||||
There are two architectures for macOS: `x86_64` and `Arm`, but binaries compiled on one architecture cannot be directly used on the other architecture.
|
||||
Rosetta 2 cannot guarantee that programs compiled with `Arm` architecture can fully run on `x86_64` environment.
|
||||
> :warning: Partial support, there may be issues with new beta versions and old versions.
|
||||
>
|
||||
> :heavy_check_mark: Supported
|
||||
>
|
||||
> :x: Not supported
|
||||
|
||||
Windows currently only supports the x86_64 architecture, and does not support 32-bit x86 or arm64 architecture.
|
||||
| PHP Version | Status | Comment |
|
||||
|-------------|--------------------|-------------------------------------------------------------------------------------------------------------------------|
|
||||
| 7.2 | :x: | |
|
||||
| 7.3 | :x: | phpmicro and many extensions do not support 7.3, 7.4 versions |
|
||||
| 7.4 | :x: | phpmicro and many extensions do not support 7.3, 7.4 versions |
|
||||
| 8.0 | :warning: | PHP official has stopped maintaining 8.0, we no longer handle 8.0 related backport support |
|
||||
| 8.1 | :warning: | PHP official only provides security updates for 8.1, we no longer handle 8.1 related backport support after 8.5 release |
|
||||
| 8.2 | :heavy_check_mark: | |
|
||||
| 8.3 | :heavy_check_mark: | |
|
||||
| 8.4 | :heavy_check_mark: | |
|
||||
| 8.5 (beta) | :warning: | PHP 8.5 is currently in beta stage |
|
||||
|
||||
## Supported PHP Version
|
||||
> This table shows the support status of static-php-cli for building corresponding versions, not the PHP official support status for that version.
|
||||
|
||||
Currently, static php cli supports PHP versions 8.1 to 8.5, and theoretically supports PHP 8.0 and earlier versions.
|
||||
Simply select the earlier version when downloading.
|
||||
However, due to some extensions and special components that have stopped supporting earlier versions of PHP,
|
||||
static-php-cli will not explicitly support earlier versions.
|
||||
## PHP Support Versions
|
||||
|
||||
Currently, static-php-cli supports PHP versions 8.2 ~ 8.5, and theoretically supports PHP 8.1 and earlier versions, just select the earlier version when downloading.
|
||||
However, due to some extensions and special components that have stopped supporting earlier versions of PHP, static-php-cli will not explicitly support earlier versions.
|
||||
We recommend that you compile the latest PHP version possible for a better experience.
|
||||
|
||||
@@ -289,6 +289,7 @@ You need to specify a compilation target, choose from the following parameters:
|
||||
|
||||
- `--build-cli`: Build a cli sapi (command line interface, which can execute PHP code on the command line)
|
||||
- `--build-fpm`: Build a fpm sapi (php-fpm, used in conjunction with other traditional fpm architecture software such as nginx)
|
||||
- `--build-cgi`: Build a cgi sapi (cgi, rarely used)
|
||||
- `--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-frankenphp`: Build a [FrankenPHP](https://github.com/php/frankenphp) executable
|
||||
|
||||
125
docs/en/index.md
125
docs/en/index.md
@@ -3,11 +3,14 @@
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: "static-php-cli"
|
||||
name: "Static PHP"
|
||||
tagline: "Build standalone PHP binary on Linux, macOS, FreeBSD, Windows, with PHP project together, with popular extensions included."
|
||||
image:
|
||||
src: /images/static-php_nobg.png
|
||||
alt: Static PHP CLI Logo
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Guide
|
||||
text: Get Started
|
||||
link: ./guide/
|
||||
|
||||
features:
|
||||
@@ -19,3 +22,121 @@ features:
|
||||
details: static-php-cli comes with dependency management and supports installation of different types of PHP extensions.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import {VPSponsors} from "vitepress/theme";
|
||||
import Contributors from '../.vitepress/components/Contributors.vue';
|
||||
|
||||
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>
|
||||
|
||||
<div class="sponsors-section">
|
||||
<div class="sponsors-header">
|
||||
<h2>Special Sponsors</h2>
|
||||
<p class="sponsors-description">
|
||||
Thank you to our amazing sponsors for supporting this project!
|
||||
</p>
|
||||
</div>
|
||||
<VPSponsors :data="sponsors"/>
|
||||
</div>
|
||||
|
||||
<style scoped>
|
||||
.sponsors-section {
|
||||
margin: 48px auto;
|
||||
padding: 32px 24px;
|
||||
max-width: 1152px;
|
||||
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-bg) 100%);
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.sponsors-section:hover {
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.sponsors-header {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.sponsors-header h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 8px 0;
|
||||
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.sponsors-description {
|
||||
font-size: 0.95rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sponsors-section {
|
||||
margin: 32px 16px;
|
||||
padding: 24px 16px;
|
||||
}
|
||||
|
||||
.sponsors-header h2 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.sponsors-description {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hero logo styling */
|
||||
:deep(.VPImage.image-src) {
|
||||
border-radius: 20px;
|
||||
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-default-soft) 100%);
|
||||
padding: 40px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
:deep(.VPImage.image-src:hover) {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* Dark mode adjustments for logo */
|
||||
.dark :deep(.VPImage.image-src) {
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.dark :deep(.VPImage.image-src:hover) {
|
||||
opacity: 1;
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
/* Additional styling for the logo image itself */
|
||||
:deep(.VPImage.image-src img) {
|
||||
max-height: 280px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
:deep(.VPImage.image-src) {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
:deep(.VPImage.image-src img) {
|
||||
max-height: 200px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<Contributors />
|
||||
|
||||
117
docs/index.md
117
docs/index.md
@@ -3,8 +3,11 @@
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: "static-php-cli"
|
||||
name: "Static PHP"
|
||||
tagline: "Build standalone PHP binary on Linux, macOS, FreeBSD, Windows, with PHP project together, with popular extensions included."
|
||||
image:
|
||||
src: /images/static-php_nobg.png
|
||||
alt: Static PHP CLI Logo
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Get Started
|
||||
@@ -24,13 +27,121 @@ features:
|
||||
|
||||
<script setup>
|
||||
import {VPSponsors} from "vitepress/theme";
|
||||
import Contributors from './.vitepress/components/Contributors.vue';
|
||||
|
||||
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
|
||||
<div class="sponsors-section">
|
||||
<div class="sponsors-header">
|
||||
<h2>Special Sponsors</h2>
|
||||
<p class="sponsors-description">
|
||||
Thank you to our amazing sponsors for supporting this project!
|
||||
</p>
|
||||
</div>
|
||||
<VPSponsors :data="sponsors"/>
|
||||
</div>
|
||||
|
||||
<VPSponsors :data="sponsors"/>
|
||||
<style scoped>
|
||||
.sponsors-section {
|
||||
margin: 48px auto;
|
||||
padding: 32px 24px;
|
||||
max-width: 1152px;
|
||||
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-bg) 100%);
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.sponsors-section:hover {
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.sponsors-header {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.sponsors-header h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 8px 0;
|
||||
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.sponsors-description {
|
||||
font-size: 0.95rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sponsors-section {
|
||||
margin: 32px 16px;
|
||||
padding: 24px 16px;
|
||||
}
|
||||
|
||||
.sponsors-header h2 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.sponsors-description {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hero logo styling */
|
||||
:deep(.VPImage.image-src) {
|
||||
border-radius: 20px;
|
||||
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-default-soft) 100%);
|
||||
padding: 40px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
:deep(.VPImage.image-src:hover) {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* Dark mode adjustments for logo */
|
||||
.dark :deep(.VPImage.image-src) {
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.dark :deep(.VPImage.image-src:hover) {
|
||||
opacity: 1;
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
/* Additional styling for the logo image itself */
|
||||
:deep(.VPImage.image-src img) {
|
||||
max-height: 280px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
:deep(.VPImage.image-src) {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
:deep(.VPImage.image-src img) {
|
||||
max-height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<Contributors />
|
||||
|
||||
|
||||
BIN
docs/public/images/static-php_nobg.png
Normal file
BIN
docs/public/images/static-php_nobg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 965 KiB |
@@ -30,6 +30,7 @@ static-php-cli 的下载资源模块是一个主要的功能,它包含了所
|
||||
这里最主要的字段是 `type`,目前它支持的类型有:
|
||||
|
||||
- `url`: 直接使用 URL 下载,例如:`https://download.libsodium.org/libsodium/releases/libsodium-1.0.18.tar.gz`。
|
||||
- `pie`: 使用 PIE(PHP Installer for Extensions)标准从 Packagist 下载 PHP 扩展。
|
||||
- `ghrel`: 使用 GitHub Release API 下载,即从 GitHub 项目发布的最新版本中上传的附件下载。
|
||||
- `ghtar`: 使用 GitHub Release API 下载,与 `ghrel` 不同的是,`ghtar` 是从项目的最新 Release 中找 `source code (tar.gz)` 下载的。
|
||||
- `ghtagtar`: 使用 GitHub Release API 下载,与 `ghtar` 相比,`ghtagtar` 可以从 `tags` 列表找最新的,并下载 `tar.gz` 格式的源码(因为有些项目只使用了 `tag` 发布版本)。
|
||||
@@ -77,6 +78,36 @@ url 类型的资源指的是从 URL 直接下载文件。
|
||||
}
|
||||
```
|
||||
|
||||
## 下载类型 - pie
|
||||
|
||||
PIE(PHP Installer for Extensions)类型的资源是从 Packagist 下载遵循 PIE 标准的 PHP 扩展。
|
||||
该方法会自动从 Packagist 仓库获取扩展信息,并下载相应的分发文件。
|
||||
|
||||
包含的参数有:
|
||||
|
||||
- `repo`: Packagist 的 vendor/package 名称,如 `vendor/package-name`
|
||||
|
||||
例子(使用 PIE 从 Packagist 下载 PHP 扩展):
|
||||
|
||||
```json
|
||||
{
|
||||
"ext-example": {
|
||||
"type": "pie",
|
||||
"repo": "vendor/example-extension",
|
||||
"path": "php-src/ext/example",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "LICENSE"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
::: tip
|
||||
PIE 下载类型会自动从 Packagist 元数据中检测扩展信息,包括下载 URL、版本和分发类型。
|
||||
扩展必须在其 Packagist 包定义中标记为 `type: php-ext` 或包含 `php-ext` 元数据。
|
||||
:::
|
||||
|
||||
## 下载类型 - ghrel
|
||||
|
||||
ghrel 会从 GitHub Release 中上传的 Assets 下载文件。首先使用 GitHub Release API 获取最新版本,然后根据正则匹配方式下载相应的文件。
|
||||
|
||||
@@ -41,6 +41,11 @@ buildroot/bin/php -d "zend_extension=/path/to/php{PHP_VER}-{ts/nts}/xdebug.so" -
|
||||
2. 你将获得 `buildroot/modules/xdebug.so` 和 `buildroot/bin/php`。
|
||||
3. `xdebug.so` 文件可用于版本和线程安全相同的 php。
|
||||
|
||||
对于 Windows 平台,由于官方构建的扩展(如 `php_yaml.dll`)强制使用了 `php8.dll` 动态库作为链接,静态构建的 PHP 不包含任何系统库以外的动态库,
|
||||
所以 Windows 下无法加载官方构建的动态扩展。 由于 static-php-cli 还暂未支持构建动态扩展,所以目前还没有让 static-php 加载动态扩展的方法。
|
||||
|
||||
不过,Windows 可以正常使用 `FFI` 扩展加载其他的 dll 文件并调用。
|
||||
|
||||
## 可以支持 Oracle 数据库扩展吗?
|
||||
|
||||
部分依赖库闭源的扩展,如 `oci8`、`sourceguardian` 等,它们没有提供纯静态编译的依赖库文件(`.a`),仅提供了动态依赖库文件(`.so`),
|
||||
|
||||
@@ -19,16 +19,30 @@ static-php-cli 是一个用于构建静态编译的 PHP 二进制的工具,目
|
||||
| Windows | :gear: :computer: | |
|
||||
| FreeBSD | :computer: | :computer: |
|
||||
|
||||
其中,Linux 目前仅在 Ubuntu、Debian、Alpine 发行版测试通过,其他发行版未进行测试,不能保证编译成功。
|
||||
对于未经过测试的发行版,可以使用 Docker 等方式本地编译,避免环境导致的问题。
|
||||
当前支持编译的 PHP 版本:
|
||||
|
||||
macOS 下支持 x86_64 和 Arm 两种架构,但在其中一个架构上编译的二进制无法直接在另一个架构上使用。
|
||||
Rosetta 2 不能保证 Arm 架构编译的程序可以完全运行在 x86_64 环境下。
|
||||
> :warning: 部分支持,对于新的测试版和旧版本可能存在问题。
|
||||
>
|
||||
> :heavy_check_mark: 支持
|
||||
>
|
||||
> :x: 不支持
|
||||
|
||||
Windows 目前只支持 x86_64 架构,不支持 32 位 x86、不支持 arm64 架构。
|
||||
| PHP Version | Status | Comment |
|
||||
|-------------|--------------------|---------------------------------------------------------|
|
||||
| 7.2 | :x: | |
|
||||
| 7.3 | :x: | phpmicro 和许多扩展不支持 7.3、7.4 版本 |
|
||||
| 7.4 | :x: | phpmicro 和许多扩展不支持 7.3、7.4 版本 |
|
||||
| 8.0 | :warning: | PHP 官方已停止 8.0 的维护,我们不再处理 8.0 相关的 backport 支持 |
|
||||
| 8.1 | :warning: | PHP 官方仅对 8.1 提供安全更新,在 8.5 发布后我们不再处理 8.1 相关的 backport 支持 |
|
||||
| 8.2 | :heavy_check_mark: | |
|
||||
| 8.3 | :heavy_check_mark: | |
|
||||
| 8.4 | :heavy_check_mark: | |
|
||||
| 8.5 (beta) | :warning: | PHP 8.5 目前处于 beta 阶段 |
|
||||
|
||||
> 这个表格的支持状态是 static-php-cli 对构建对应版本的支持情况,不是 PHP 官方对该版本的支持情况。
|
||||
|
||||
## PHP 支持版本
|
||||
|
||||
目前,static-php-cli 对 PHP 8.1 ~ 8.5 版本是支持的,对于 PHP 8.0 及更早版本理论上支持,只需下载时选择早期版本即可。
|
||||
目前,static-php-cli 对 PHP 8.2 ~ 8.5 版本是支持的,对于 PHP 8.1 及更早版本理论上支持,只需下载时选择早期版本即可。
|
||||
但由于部分扩展和特殊组件已对早期版本的 PHP 停止了支持,所以 static-php-cli 不会明确支持早期版本。
|
||||
我们推荐你编译尽可能新的 PHP 版本,以获得更好的体验。
|
||||
|
||||
@@ -249,6 +249,7 @@ bin/spc doctor --auto-fix
|
||||
|
||||
- `--build-cli`: 构建一个 cli sapi(命令行界面,可在命令行执行 PHP 代码)
|
||||
- `--build-fpm`: 构建一个 fpm sapi(php-fpm,用于和其他传统的 fpm 架构的软件如 nginx 配合使用)
|
||||
- `--build-cgi`: 构建一个 cgi sapi(cgi,可用于传统的 cgi 架构的软件如 apache 配合使用)
|
||||
- `--build-micro`: 构建一个 micro sapi(用于构建一个包含 PHP 代码的独立可执行二进制)
|
||||
- `--build-embed`: 构建一个 embed sapi(用于嵌入到其他 C 语言程序中)
|
||||
- `--build-frankenphp`: 构建一个 [frankenphp](https://github.com/php/frankenphp) 二进制
|
||||
|
||||
138
docs/zh/index.md
138
docs/zh/index.md
@@ -3,18 +3,140 @@
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: "static-php-cli"
|
||||
name: "Static PHP"
|
||||
tagline: "在 Linux、macOS、FreeBSD、Windows 上与 PHP 项目一起构建独立的 PHP 二进制文件,并包含流行的扩展。"
|
||||
image:
|
||||
src: /images/static-php_nobg.png
|
||||
alt: Static PHP CLI Logo
|
||||
actions:
|
||||
- theme: brand
|
||||
text: 指南
|
||||
text: 开始使用
|
||||
link: ./guide/
|
||||
|
||||
features:
|
||||
- title: 静态二进制
|
||||
details: 您可以轻松地编译一个独立的 PHP 二进制文件以供嵌入程序使用。包括 cli、fpm、micro。
|
||||
- title: phpmicro 自执行二进制
|
||||
details: 您可以使用 micro SAPI 编译一个自解压的可执行文件,并将 PHP 代码与二进制文件打包为一个文件。
|
||||
- title: 依赖管理
|
||||
details: static-php-cli 附带依赖项管理,支持安装不同类型的 PHP 扩展和不同的依赖库。
|
||||
- title: 静态 CLI 二进制
|
||||
details: 您可以轻松地编译一个独立的 PHP 二进制文件以供通用使用,包括 CLI、FPM SAPI。
|
||||
- title: Micro 自解压可执行文件
|
||||
details: 您可以编译一个自解压的可执行文件,并将 PHP 源代码与二进制文件打包在一起。
|
||||
- title: 依赖管理
|
||||
details: static-php-cli 附带依赖项管理,支持安装不同类型的 PHP 扩展。
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import {VPSponsors} from "vitepress/theme";
|
||||
import Contributors from '../.vitepress/components/Contributors.vue';
|
||||
|
||||
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>
|
||||
|
||||
<div class="sponsors-section">
|
||||
<div class="sponsors-header">
|
||||
<h2>特别赞助商</h2>
|
||||
<p class="sponsors-description">
|
||||
感谢我们出色的赞助商对本项目的支持!
|
||||
</p>
|
||||
</div>
|
||||
<VPSponsors :data="sponsors"/>
|
||||
</div>
|
||||
|
||||
<style scoped>
|
||||
.sponsors-section {
|
||||
margin: 48px auto;
|
||||
padding: 32px 24px;
|
||||
max-width: 1152px;
|
||||
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-bg) 100%);
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.sponsors-section:hover {
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.sponsors-header {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.sponsors-header h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 8px 0;
|
||||
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.sponsors-description {
|
||||
font-size: 0.95rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sponsors-section {
|
||||
margin: 32px 16px;
|
||||
padding: 24px 16px;
|
||||
}
|
||||
|
||||
.sponsors-header h2 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.sponsors-description {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hero logo styling */
|
||||
:deep(.VPImage.image-src) {
|
||||
border-radius: 20px;
|
||||
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-default-soft) 100%);
|
||||
padding: 40px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
:deep(.VPImage.image-src:hover) {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* Dark mode adjustments for logo */
|
||||
.dark :deep(.VPImage.image-src) {
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.dark :deep(.VPImage.image-src:hover) {
|
||||
opacity: 1;
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
/* Additional styling for the logo image itself */
|
||||
:deep(.VPImage.image-src img) {
|
||||
max-height: 280px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
:deep(.VPImage.image-src) {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
:deep(.VPImage.image-src img) {
|
||||
max-height: 200px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<Contributors />
|
||||
|
||||
@@ -9,6 +9,7 @@ parameters:
|
||||
- '#Attribute class JetBrains\\PhpStorm\\ArrayShape does not exist#'
|
||||
- '#Function Swoole\\Coroutine\\run not found.#'
|
||||
- '#Static call to instance method ZM\\Logger\\ConsoleColor#'
|
||||
- '#Constant GNU_ARCH not found#'
|
||||
dynamicConstantNames:
|
||||
- PHP_OS_FAMILY
|
||||
excludePaths:
|
||||
|
||||
@@ -34,7 +34,7 @@ use Symfony\Component\Console\Application;
|
||||
*/
|
||||
final class ConsoleApplication extends Application
|
||||
{
|
||||
public const string VERSION = '2.7.2';
|
||||
public const string VERSION = '2.7.7';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\store\LockFile;
|
||||
use SPC\store\pkg\GoXcaddy;
|
||||
use SPC\store\SourceManager;
|
||||
use SPC\store\SourcePatcher;
|
||||
use SPC\util\AttributeMapper;
|
||||
@@ -127,27 +128,6 @@ abstract class BuilderBase
|
||||
return array_filter($this->exts, fn ($ext) => $ext->isBuildStatic());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is a cpp extensions or libraries.
|
||||
*/
|
||||
public function hasCpp(): bool
|
||||
{
|
||||
// judge cpp-extension
|
||||
$exts = array_keys($this->getExts(false));
|
||||
foreach ($exts as $ext) {
|
||||
if (Config::getExt($ext, 'cpp-extension', false) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$libs = array_keys($this->getLibs());
|
||||
foreach ($libs as $lib) {
|
||||
if (Config::getLib($lib, 'cpp-library', false) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set libs only mode.
|
||||
*
|
||||
@@ -392,6 +372,9 @@ abstract class BuilderBase
|
||||
if (($type & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP) {
|
||||
$ls[] = 'frankenphp';
|
||||
}
|
||||
if (($type & BUILD_TARGET_CGI) === BUILD_TARGET_CGI) {
|
||||
$ls[] = 'cgi';
|
||||
}
|
||||
return implode(', ', $ls);
|
||||
}
|
||||
|
||||
@@ -504,8 +487,7 @@ abstract class BuilderBase
|
||||
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")) {
|
||||
if (!GoXcaddy::isInstalled()) {
|
||||
global $argv;
|
||||
throw new WrongUsageException("FrankenPHP SAPI requires the go-xcaddy package, please install it first: {$argv[0]} install-pkg go-xcaddy");
|
||||
}
|
||||
|
||||
@@ -4,14 +4,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder;
|
||||
|
||||
use SPC\builder\unix\UnixBuilderBase;
|
||||
use SPC\exception\EnvironmentException;
|
||||
use SPC\exception\SPCException;
|
||||
use SPC\exception\ValidationException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\toolchain\ToolchainManager;
|
||||
use SPC\toolchain\ZigToolchain;
|
||||
use SPC\util\SPCConfigUtil;
|
||||
use SPC\util\SPCTarget;
|
||||
|
||||
@@ -222,13 +221,25 @@ class Extension
|
||||
*/
|
||||
public function patchBeforeSharedMake(): bool
|
||||
{
|
||||
$config = (new SPCConfigUtil($this->builder))->config([$this->getName()], array_map(fn ($l) => $l->getName(), $this->builder->getLibs()));
|
||||
[$staticLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
|
||||
FileSystem::replaceFileRegex(
|
||||
$this->source_dir . '/Makefile',
|
||||
'/^(.*_SHARED_LIBADD\s*=.*)$/m',
|
||||
'$1 ' . trim($staticLibs)
|
||||
);
|
||||
$config = (new SPCConfigUtil($this->builder))->getExtensionConfig($this);
|
||||
[$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
|
||||
$lstdcpp = str_contains($sharedLibs, '-l:libstdc++.a') ? '-l:libstdc++.a' : null;
|
||||
$lstdcpp ??= str_contains($sharedLibs, '-lstdc++') ? '-lstdc++' : '';
|
||||
|
||||
$makefileContent = file_get_contents($this->source_dir . '/Makefile');
|
||||
if (preg_match('/^(.*_SHARED_LIBADD\s*=\s*)(.*)$/m', $makefileContent, $matches)) {
|
||||
$prefix = $matches[1];
|
||||
$currentLibs = trim($matches[2]);
|
||||
$newLibs = trim("{$currentLibs} {$staticLibs} {$lstdcpp}");
|
||||
$deduplicatedLibs = deduplicate_flags($newLibs);
|
||||
|
||||
FileSystem::replaceFileRegex(
|
||||
$this->source_dir . '/Makefile',
|
||||
'/^(.*_SHARED_LIBADD\s*=.*)$/m',
|
||||
$prefix . $deduplicatedLibs
|
||||
);
|
||||
}
|
||||
|
||||
if ($objs = getenv('SPC_EXTRA_RUNTIME_OBJECTS')) {
|
||||
FileSystem::replaceFileRegex(
|
||||
$this->source_dir . '/Makefile',
|
||||
@@ -295,7 +306,7 @@ class Extension
|
||||
// Run compile check if build target is cli
|
||||
// If you need to run some check, overwrite this or add your assert in src/globals/ext-tests/{extension_name}.php
|
||||
$sharedExtensions = $this->getSharedExtensionLoadString();
|
||||
[$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"');
|
||||
[$ret] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"');
|
||||
if ($ret !== 0) {
|
||||
throw new ValidationException(
|
||||
"extension {$this->getName()} failed compile check: php-cli returned {$ret}",
|
||||
@@ -325,7 +336,7 @@ class Extension
|
||||
{
|
||||
// Run compile check if build target is cli
|
||||
// If you need to run some check, overwrite this or add your assert in src/globals/ext-tests/{extension_name}.php
|
||||
[$ret] = cmd()->execWithResult(BUILD_ROOT_PATH . '/bin/php.exe -n --ri "' . $this->getDistName() . '"', false);
|
||||
[$ret] = cmd()->execWithResult(BUILD_BIN_PATH . '/php.exe -n --ri "' . $this->getDistName() . '"', false);
|
||||
if ($ret !== 0) {
|
||||
throw new ValidationException("extension {$this->getName()} failed compile check: php-cli returned {$ret}", validation_module: "Extension {$this->getName()} sanity check");
|
||||
}
|
||||
@@ -402,26 +413,7 @@ class Extension
|
||||
*/
|
||||
public function buildUnixShared(): void
|
||||
{
|
||||
$config = (new SPCConfigUtil($this->builder))->config(
|
||||
[$this->getName()],
|
||||
array_map(fn ($l) => $l->getName(), $this->getLibraryDependencies(recursive: true)),
|
||||
$this->builder->getOption('with-suggested-exts'),
|
||||
$this->builder->getOption('with-suggested-libs'),
|
||||
);
|
||||
[$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
|
||||
$preStatic = PHP_OS_FAMILY === 'Darwin' ? '' : '-Wl,--start-group ';
|
||||
$postStatic = PHP_OS_FAMILY === 'Darwin' ? '' : ' -Wl,--end-group ';
|
||||
$env = [
|
||||
'CFLAGS' => $config['cflags'],
|
||||
'CXXFLAGS' => $config['cflags'],
|
||||
'LDFLAGS' => $config['ldflags'],
|
||||
'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"),
|
||||
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
|
||||
];
|
||||
if (ToolchainManager::getToolchainClass() === ZigToolchain::class && SPCTarget::getTargetOS() === 'Linux') {
|
||||
$env['SPC_COMPILER_EXTRA'] = '-lstdc++';
|
||||
}
|
||||
|
||||
$env = $this->getSharedExtensionEnv();
|
||||
if ($this->patchBeforeSharedPhpize()) {
|
||||
logger()->info("Extension [{$this->getName()}] patched before shared phpize");
|
||||
}
|
||||
@@ -436,13 +428,15 @@ class Extension
|
||||
logger()->info("Extension [{$this->getName()}] patched before shared configure");
|
||||
}
|
||||
|
||||
$phpvars = getenv('SPC_EXTRA_PHP_VARS') ?: '';
|
||||
|
||||
shell()->cd($this->source_dir)
|
||||
->setEnv($env)
|
||||
->appendEnv($this->getExtraEnv())
|
||||
->exec(
|
||||
'./configure ' . $this->getUnixConfigureArg(true) .
|
||||
' --with-php-config=' . BUILD_BIN_PATH . '/php-config ' .
|
||||
'--enable-shared --disable-static'
|
||||
"--enable-shared --disable-static {$phpvars}"
|
||||
);
|
||||
|
||||
if ($this->patchBeforeSharedMake()) {
|
||||
@@ -455,6 +449,15 @@ class Extension
|
||||
->exec('make clean')
|
||||
->exec('make -j' . $this->builder->concurrency)
|
||||
->exec('make install');
|
||||
|
||||
// process *.so file
|
||||
$soFile = BUILD_MODULES_PATH . '/' . $this->getName() . '.so';
|
||||
if (!file_exists($soFile)) {
|
||||
throw new ValidationException("extension {$this->getName()} build failed: {$soFile} not found", validation_module: "Extension {$this->getName()} build");
|
||||
}
|
||||
/** @var UnixBuilderBase $builder */
|
||||
$builder = $this->builder;
|
||||
$builder->deployBinary($soFile, $soFile, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -493,6 +496,58 @@ class Extension
|
||||
return $this->build_static;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the library dependencies that current extension depends on.
|
||||
*
|
||||
* @param bool $recursive Whether it includes dependencies recursively
|
||||
*/
|
||||
public function getLibraryDependencies(bool $recursive = false): array
|
||||
{
|
||||
$ret = array_filter($this->dependencies, fn ($x) => $x instanceof LibraryBase);
|
||||
if (!$recursive) {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$deps = [];
|
||||
|
||||
$added = 1;
|
||||
while ($added !== 0) {
|
||||
$added = 0;
|
||||
foreach ($ret as $depName => $dep) {
|
||||
foreach ($dep->getDependencies(true) as $depdepName => $depdep) {
|
||||
if (!array_key_exists($depdepName, $deps)) {
|
||||
$deps[$depdepName] = $depdep;
|
||||
++$added;
|
||||
}
|
||||
}
|
||||
if (!array_key_exists($depName, $deps)) {
|
||||
$deps[$depName] = $dep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $deps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the environment variables a shared extension needs to be built.
|
||||
* CFLAGS, CXXFLAGS, LDFLAGS and so on.
|
||||
*/
|
||||
protected function getSharedExtensionEnv(): array
|
||||
{
|
||||
$config = (new SPCConfigUtil($this->builder))->getExtensionConfig($this);
|
||||
[$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
|
||||
$preStatic = PHP_OS_FAMILY === 'Darwin' ? '' : '-Wl,--start-group ';
|
||||
$postStatic = PHP_OS_FAMILY === 'Darwin' ? '' : ' -Wl,--end-group ';
|
||||
return [
|
||||
'CFLAGS' => $config['cflags'],
|
||||
'CXXFLAGS' => $config['cflags'],
|
||||
'LDFLAGS' => $config['ldflags'],
|
||||
'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"),
|
||||
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
|
||||
];
|
||||
}
|
||||
|
||||
protected function addLibraryDependency(string $name, bool $optional = false): void
|
||||
{
|
||||
$depLib = $this->builder->getLib($name);
|
||||
@@ -502,7 +557,7 @@ class Extension
|
||||
}
|
||||
logger()->info("enabling {$this->name} without library {$name}");
|
||||
} else {
|
||||
$this->dependencies[] = $depLib;
|
||||
$this->dependencies[$name] = $depLib;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,7 +570,7 @@ class Extension
|
||||
}
|
||||
logger()->info("enabling {$this->name} without extension {$name}");
|
||||
} else {
|
||||
$this->dependencies[] = $depExt;
|
||||
$this->dependencies[$name] = $depExt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,37 +605,4 @@ class Extension
|
||||
}
|
||||
return [trim($staticLibString), trim($sharedLibString)];
|
||||
}
|
||||
|
||||
private function getLibraryDependencies(bool $recursive = false): array
|
||||
{
|
||||
$ret = array_filter($this->dependencies, fn ($x) => $x instanceof LibraryBase);
|
||||
if (!$recursive) {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$deps = [];
|
||||
|
||||
$added = 1;
|
||||
while ($added !== 0) {
|
||||
$added = 0;
|
||||
foreach ($ret as $depName => $dep) {
|
||||
foreach ($dep->getDependencies(true) as $depdepName => $depdep) {
|
||||
if (!in_array($depdepName, array_keys($deps), true)) {
|
||||
$deps[$depdepName] = $depdep;
|
||||
++$added;
|
||||
}
|
||||
}
|
||||
if (!in_array($depName, array_keys($deps), true)) {
|
||||
$deps[$depName] = $dep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists(0, $deps)) {
|
||||
$zero = [0 => $deps[0]];
|
||||
unset($deps[0]);
|
||||
return $zero + $deps;
|
||||
}
|
||||
return $deps;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class dba extends Extension
|
||||
{
|
||||
public function getUnixConfigureArg(bool $shared = false): string
|
||||
{
|
||||
$qdbm = $this->builder->getLib('qdbm') ? (' --with-qdbm=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH) : '';
|
||||
$qdbm = $this->builder->getLib('qdbm') ? (' --with-qdbm=' . BUILD_ROOT_PATH) : '';
|
||||
return '--enable-dba' . ($shared ? '=shared' : '') . $qdbm;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,11 @@ class gettext extends Extension
|
||||
public function patchBeforeBuildconf(): bool
|
||||
{
|
||||
if ($this->builder instanceof MacOSBuilder) {
|
||||
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/gettext/config.m4', 'AC_CHECK_LIB($GETTEXT_CHECK_IN_LIB', 'AC_CHECK_LIB(intl');
|
||||
FileSystem::replaceFileStr(
|
||||
SOURCE_PATH . '/php-src/ext/gettext/config.m4',
|
||||
['AC_CHECK_LIB($GETTEXT_CHECK_IN_LIB', 'AC_CHECK_LIB([$GETTEXT_CHECK_IN_LIB'],
|
||||
['AC_CHECK_LIB(intl', 'AC_CHECK_LIB([intl'] // new php versions use a bracket
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ class grpc extends Extension
|
||||
public function patchBeforeConfigure(): bool
|
||||
{
|
||||
$util = new SPCConfigUtil($this->builder, ['libs_only_deps' => true]);
|
||||
$config = $util->config(['grpc']);
|
||||
$config = $util->getExtensionConfig($this);
|
||||
$libs = $config['libs'];
|
||||
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/configure', '-lgrpc', $libs);
|
||||
return true;
|
||||
@@ -56,4 +56,11 @@ class grpc extends Extension
|
||||
GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes');
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getSharedExtensionEnv(): array
|
||||
{
|
||||
$env = parent::getSharedExtensionEnv();
|
||||
$env['CPPFLAGS'] = $env['CXXFLAGS'] . ' -Wno-attributes';
|
||||
return $env;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ class memcached extends Extension
|
||||
'--with-libmemcached-dir=' . BUILD_ROOT_PATH . ' ' .
|
||||
'--disable-memcached-sasl ' .
|
||||
'--enable-memcached-json ' .
|
||||
($this->builder->getLib('zstd') ? '--with-zstd ' : '') .
|
||||
($this->builder->getExt('igbinary') ? '--enable-memcached-igbinary ' : '') .
|
||||
($this->builder->getExt('session') ? '--enable-memcached-session ' : '') .
|
||||
($this->builder->getExt('msgpack') ? '--enable-memcached-msgpack ' : '') .
|
||||
'--with-system-fastlz';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace SPC\builder\extension;
|
||||
use SPC\builder\Extension;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\CustomExt;
|
||||
use SPC\util\SPCConfigUtil;
|
||||
|
||||
#[CustomExt('rdkafka')]
|
||||
class rdkafka extends Extension
|
||||
@@ -15,6 +16,7 @@ class rdkafka extends Extension
|
||||
{
|
||||
FileSystem::replaceFileStr("{$this->source_dir}/config.m4", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm\n", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm \$RDKAFKA_LIBS\n");
|
||||
FileSystem::replaceFileStr("{$this->source_dir}/config.m4", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm\"\n", '-L$RDKAFKA_DIR/$PHP_LIBDIR -lm $RDKAFKA_LIBS"');
|
||||
FileSystem::replaceFileStr("{$this->source_dir}/config.m4", 'PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,', 'AC_CHECK_LIB([$LIBNAME], [$LIBSYMBOL],');
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -37,8 +39,7 @@ class rdkafka extends Extension
|
||||
|
||||
public function getUnixConfigureArg(bool $shared = false): string
|
||||
{
|
||||
$pkgconf_libs = shell()->execWithResult('pkg-config --libs --static rdkafka')[1];
|
||||
$pkgconf_libs = trim(implode('', $pkgconf_libs));
|
||||
return '--with-rdkafka=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . ' RDKAFKA_LIBS="' . $pkgconf_libs . '"';
|
||||
$pkgconf_libs = (new SPCConfigUtil($this->builder, ['no_php' => true, 'libs_only_deps' => true]))->getExtensionConfig($this);
|
||||
return '--with-rdkafka=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . " RDKAFKA_LIBS=\"{$pkgconf_libs['libs']}\"";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\exception\ValidationException;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\CustomExt;
|
||||
|
||||
@@ -23,12 +24,7 @@ class readline extends Extension
|
||||
|
||||
public function getUnixConfigureArg(bool $shared = false): string
|
||||
{
|
||||
$enable = '--without-libedit --with-readline=' . BUILD_ROOT_PATH;
|
||||
if ($this->builder->getPHPVersionID() < 84000) {
|
||||
// the check uses `char rl_pending_input()` instead of `extern int rl_pending_input`, which makes LTO fail
|
||||
$enable .= ' ac_cv_lib_readline_rl_pending_input=yes';
|
||||
}
|
||||
return $enable;
|
||||
return '--with-libedit --without-readline';
|
||||
}
|
||||
|
||||
public function buildUnixShared(): void
|
||||
@@ -39,4 +35,13 @@ class readline extends Extension
|
||||
}
|
||||
parent::buildUnixShared();
|
||||
}
|
||||
|
||||
public function runCliCheckUnix(): void
|
||||
{
|
||||
parent::runCliCheckUnix();
|
||||
[$ret, $out] = shell()->execWithResult('printf "exit\n" | ' . BUILD_BIN_PATH . '/php -a');
|
||||
if ($ret !== 0 || !str_contains(implode("\n", $out), 'Interactive shell')) {
|
||||
throw new ValidationException("readline extension failed sanity check. Code: {$ret}, output: " . implode("\n", $out));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\toolchain\ToolchainManager;
|
||||
use SPC\toolchain\ZigToolchain;
|
||||
use SPC\util\CustomExt;
|
||||
|
||||
#[CustomExt('simdjson')]
|
||||
@@ -17,7 +19,7 @@ class simdjson extends Extension
|
||||
FileSystem::replaceFileRegex(
|
||||
SOURCE_PATH . '/php-src/ext/simdjson/config.m4',
|
||||
'/php_version=(`.*`)$/m',
|
||||
'php_version=' . strval($php_ver)
|
||||
'php_version=' . $php_ver
|
||||
);
|
||||
FileSystem::replaceFileStr(
|
||||
SOURCE_PATH . '/php-src/ext/simdjson/config.m4',
|
||||
@@ -31,4 +33,18 @@ class simdjson extends Extension
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSharedExtensionEnv(): array
|
||||
{
|
||||
$env = parent::getSharedExtensionEnv();
|
||||
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
|
||||
$extra = getenv('SPC_COMPILER_EXTRA');
|
||||
if (!str_contains((string) $extra, '-lstdc++')) {
|
||||
f_putenv('SPC_COMPILER_EXTRA=' . clean_spaces($extra . ' -lstdc++'));
|
||||
}
|
||||
$env['CFLAGS'] .= ' -Xclang -target-feature -Xclang +evex512';
|
||||
$env['CXXFLAGS'] .= ' -Xclang -target-feature -Xclang +evex512';
|
||||
}
|
||||
return $env;
|
||||
}
|
||||
}
|
||||
|
||||
29
src/SPC/builder/extension/snmp.php
Normal file
29
src/SPC/builder/extension/snmp.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\CustomExt;
|
||||
use SPC\util\PkgConfigUtil;
|
||||
|
||||
#[CustomExt('snmp')]
|
||||
class snmp extends Extension
|
||||
{
|
||||
public function patchBeforeBuildconf(): bool
|
||||
{
|
||||
// Overwrite m4 config using newer PHP version
|
||||
if ($this->builder->getPHPVersionID() < 80400) {
|
||||
FileSystem::copy(ROOT_DIR . '/src/globals/extra/snmp-ext-config-old.m4', "{$this->source_dir}/config.m4");
|
||||
}
|
||||
$libs = implode(' ', PkgConfigUtil::getLibsArray('netsnmp'));
|
||||
FileSystem::replaceFileStr(
|
||||
"{$this->source_dir}/config.m4",
|
||||
'PHP_EVAL_LIBLINE([$SNMP_LIBS], [SNMP_SHARED_LIBADD])',
|
||||
"SNMP_LIBS=\"{$libs}\"\nPHP_EVAL_LIBLINE([\$SNMP_LIBS], [SNMP_SHARED_LIBADD])"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ class spx extends Extension
|
||||
{
|
||||
public function getUnixConfigureArg(bool $shared = false): string
|
||||
{
|
||||
$arg = '--enable-spx' . ($shared ? '=shared' : '');
|
||||
$arg = '--enable-SPX' . ($shared ? '=shared' : '');
|
||||
if ($this->builder->getLib('zlib') !== null) {
|
||||
$arg .= ' --with-zlib-dir=' . BUILD_ROOT_PATH;
|
||||
}
|
||||
@@ -29,4 +29,20 @@ class spx extends Extension
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function patchBeforeBuildconf(): bool
|
||||
{
|
||||
FileSystem::replaceFileStr(
|
||||
$this->source_dir . '/config.m4',
|
||||
'CFLAGS="$CFLAGS -Werror -Wall -O3 -pthread -std=gnu90"',
|
||||
'CFLAGS="$CFLAGS -pthread"'
|
||||
);
|
||||
FileSystem::replaceFileStr(
|
||||
$this->source_dir . '/src/php_spx.h',
|
||||
"extern zend_module_entry spx_module_entry;\n",
|
||||
"extern zend_module_entry spx_module_entry;;\n#define phpext_spx_ptr &spx_module_entry\n"
|
||||
);
|
||||
FileSystem::copy($this->source_dir . '/src/php_spx.h', $this->source_dir . '/php_spx.h');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,12 +69,15 @@ class swoole extends Extension
|
||||
$arg .= $this->builder->getExt('swoole-hook-pgsql') ? ' --enable-swoole-pgsql' : ' --disable-swoole-pgsql';
|
||||
$arg .= $this->builder->getExt('swoole-hook-mysql') ? ' --enable-mysqlnd' : ' --disable-mysqlnd';
|
||||
$arg .= $this->builder->getExt('swoole-hook-sqlite') ? ' --enable-swoole-sqlite' : ' --disable-swoole-sqlite';
|
||||
|
||||
if ($this->builder->getExt('swoole-hook-odbc')) {
|
||||
$config = (new SPCConfigUtil($this->builder, ['libs_only_deps' => true]))->config([], ['unixodbc']);
|
||||
$config = (new SPCConfigUtil($this->builder))->getLibraryConfig($this->builder->getLib('unixodbc'));
|
||||
$arg .= ' --with-swoole-odbc=unixODBC,' . BUILD_ROOT_PATH . ' SWOOLE_ODBC_LIBS="' . $config['libs'] . '"';
|
||||
}
|
||||
|
||||
if ($this->getExtVersion() >= '6.1.0') {
|
||||
$arg .= ' --enable-swoole-stdext';
|
||||
}
|
||||
|
||||
if (SPCTarget::getTargetOS() === 'Darwin') {
|
||||
$arg .= ' ac_cv_lib_pthread_pthread_barrier_init=no';
|
||||
}
|
||||
|
||||
17
src/SPC/builder/extension/zip.php
Normal file
17
src/SPC/builder/extension/zip.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\util\CustomExt;
|
||||
|
||||
#[CustomExt('zip')]
|
||||
class zip extends Extension
|
||||
{
|
||||
public function getUnixConfigureArg(bool $shared = false): string
|
||||
{
|
||||
return !$shared ? '--with-zip=' . BUILD_ROOT_PATH : '--enable-zip=shared';
|
||||
}
|
||||
}
|
||||
@@ -153,7 +153,7 @@ class BSDBuilder extends UnixBuilderBase
|
||||
if (!$this->getOption('no-strip', false)) {
|
||||
$shell->exec('strip sapi/cli/php');
|
||||
}
|
||||
$this->deployBinary(BUILD_TARGET_CLI);
|
||||
$this->deploySAPIBinary(BUILD_TARGET_CLI);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,7 +184,7 @@ class BSDBuilder extends UnixBuilderBase
|
||||
if (!$this->getOption('no-strip', false)) {
|
||||
shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec('strip --strip-unneeded micro.sfx');
|
||||
}
|
||||
$this->deployBinary(BUILD_TARGET_MICRO);
|
||||
$this->deploySAPIBinary(BUILD_TARGET_MICRO);
|
||||
|
||||
if ($this->phar_patched) {
|
||||
SourcePatcher::unpatchMicroPhar();
|
||||
@@ -206,7 +206,7 @@ class BSDBuilder extends UnixBuilderBase
|
||||
if (!$this->getOption('no-strip', false)) {
|
||||
$shell->exec('strip sapi/fpm/php-fpm');
|
||||
}
|
||||
$this->deployBinary(BUILD_TARGET_FPM);
|
||||
$this->deploySAPIBinary(BUILD_TARGET_FPM);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,8 @@ namespace SPC\builder\linux;
|
||||
use SPC\builder\unix\UnixBuilderBase;
|
||||
use SPC\exception\PatchException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\DirDiff;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\store\SourcePatcher;
|
||||
use SPC\util\GlobalEnvManager;
|
||||
@@ -84,16 +86,19 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
|
||||
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
|
||||
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
|
||||
$enableCgi = ($build_target & BUILD_TARGET_CGI) === BUILD_TARGET_CGI;
|
||||
|
||||
// prepare build php envs
|
||||
// $musl_flag = SPCTarget::getLibc() === 'musl' ? ' -D__MUSL__' : ' -U__MUSL__';
|
||||
$php_configure_env = SystemUtil::makeEnvVarString([
|
||||
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'),
|
||||
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
|
||||
'CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH, // . ' -Dsomethinghere', // . $musl_flag,
|
||||
'LDFLAGS' => '-L' . BUILD_LIB_PATH,
|
||||
// 'LIBS' => SPCTarget::getRuntimeLibs(), // do not pass static libraries here yet, they may contain polyfills for libc functions!
|
||||
]);
|
||||
|
||||
$phpvars = getenv('SPC_EXTRA_PHP_VARS') ?: '';
|
||||
|
||||
$embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
|
||||
if ($embed_type !== 'static' && SPCTarget::isStatic()) {
|
||||
throw new WrongUsageException(
|
||||
@@ -102,21 +107,22 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
);
|
||||
}
|
||||
|
||||
shell()->cd(SOURCE_PATH . '/php-src')
|
||||
->exec(
|
||||
$php_configure_env . ' ' .
|
||||
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
|
||||
($enableCli ? '--enable-cli ' : '--disable-cli ') .
|
||||
($enableFpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') .
|
||||
($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') .
|
||||
($enableMicro ? '--enable-micro=all-static ' : '--disable-micro ') .
|
||||
$config_file_path .
|
||||
$config_file_scan_dir .
|
||||
$json_74 .
|
||||
$zts .
|
||||
$maxExecutionTimers .
|
||||
$this->makeStaticExtensionArgs() . ' '
|
||||
);
|
||||
$this->seekPhpSrcLogFileOnException(fn () => shell()->cd(SOURCE_PATH . '/php-src')->exec(
|
||||
$php_configure_env . ' ' .
|
||||
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
|
||||
($enableCli ? '--enable-cli ' : '--disable-cli ') .
|
||||
($enableFpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') .
|
||||
($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') .
|
||||
($enableMicro ? '--enable-micro=all-static ' : '--disable-micro ') .
|
||||
($enableCgi ? '--enable-cgi ' : '--disable-cgi ') .
|
||||
$config_file_path .
|
||||
$config_file_scan_dir .
|
||||
$json_74 .
|
||||
$zts .
|
||||
$maxExecutionTimers .
|
||||
$phpvars . ' ' .
|
||||
$this->makeStaticExtensionArgs() . ' '
|
||||
));
|
||||
|
||||
$this->emitPatchPoint('before-php-make');
|
||||
SourcePatcher::patchBeforeMake($this);
|
||||
@@ -131,6 +137,10 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
logger()->info('building fpm');
|
||||
$this->buildFpm();
|
||||
}
|
||||
if ($enableCgi) {
|
||||
logger()->info('building cgi');
|
||||
$this->buildCgi();
|
||||
}
|
||||
if ($enableMicro) {
|
||||
logger()->info('building micro');
|
||||
$this->buildMicro();
|
||||
@@ -142,16 +152,22 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
}
|
||||
$this->buildEmbed();
|
||||
}
|
||||
// build dynamic extensions if needed, must happen before building FrankenPHP to make sure we export all necessary, undefined symbols
|
||||
$shared_extensions = array_map('trim', array_filter(explode(',', $this->getOption('build-shared'))));
|
||||
if (!empty($shared_extensions)) {
|
||||
logger()->info('Building shared extensions ...');
|
||||
$this->buildSharedExts();
|
||||
}
|
||||
if ($enableFrankenphp) {
|
||||
logger()->info('building frankenphp');
|
||||
$this->buildFrankenphp();
|
||||
}
|
||||
$shared_extensions = array_map('trim', array_filter(explode(',', $this->getOption('build-shared'))));
|
||||
if (!empty($shared_extensions)) {
|
||||
if (SPCTarget::isStatic()) {
|
||||
throw new WrongUsageException(
|
||||
"You're building against musl libc statically (the default on Linux), but you're trying to build shared extensions.\n" .
|
||||
'Static musl libc does not implement `dlopen`, so your php binary is not able to load shared extensions.' . "\n" .
|
||||
'Either use SPC_LIBC=glibc to link against glibc on a glibc OS, or use SPC_TARGET="native-native-musl -dynamic" to link against musl libc dynamically using `zig cc`.'
|
||||
);
|
||||
}
|
||||
logger()->info('Building shared extensions...');
|
||||
$this->buildSharedExts();
|
||||
}
|
||||
}
|
||||
|
||||
public function testPHP(int $build_target = BUILD_TARGET_NONE)
|
||||
@@ -165,21 +181,32 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
*/
|
||||
protected function buildCli(): void
|
||||
{
|
||||
if ($this->getExt('readline') && SPCTarget::isStatic()) {
|
||||
SourcePatcher::patchFile('musl_static_readline.patch', SOURCE_PATH . '/php-src');
|
||||
}
|
||||
|
||||
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
|
||||
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make';
|
||||
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
|
||||
shell()->cd(SOURCE_PATH . '/php-src')
|
||||
->exec('sed -i "s|//lib|/lib|g" Makefile')
|
||||
->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} cli");
|
||||
->exec("make {$concurrency} {$vars} cli");
|
||||
|
||||
if (!$this->getOption('no-strip', false)) {
|
||||
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')->exec('strip --strip-unneeded php');
|
||||
}
|
||||
if ($this->getOption('with-upx-pack')) {
|
||||
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')
|
||||
->exec(getenv('UPX_EXEC') . ' --best php');
|
||||
if ($this->getExt('readline') && SPCTarget::isStatic()) {
|
||||
SourcePatcher::patchFile('musl_static_readline.patch', SOURCE_PATH . '/php-src', true);
|
||||
}
|
||||
|
||||
$this->deployBinary(BUILD_TARGET_CLI);
|
||||
$this->deploySAPIBinary(BUILD_TARGET_CLI);
|
||||
}
|
||||
|
||||
protected function buildCgi(): void
|
||||
{
|
||||
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
|
||||
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
|
||||
shell()->cd(SOURCE_PATH . '/php-src')
|
||||
->exec('sed -i "s|//lib|/lib|g" Makefile')
|
||||
->exec("make {$concurrency} {$vars} cgi");
|
||||
|
||||
$this->deploySAPIBinary(BUILD_TARGET_CGI);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,29 +217,33 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
if ($this->getPHPVersionID() < 80000) {
|
||||
throw new WrongUsageException('phpmicro only support PHP >= 8.0!');
|
||||
}
|
||||
if ($this->getExt('phar')) {
|
||||
$this->phar_patched = true;
|
||||
SourcePatcher::patchMicroPhar($this->getPHPVersionID());
|
||||
}
|
||||
try {
|
||||
if ($this->getExt('phar')) {
|
||||
$this->phar_patched = true;
|
||||
SourcePatcher::patchMicroPhar($this->getPHPVersionID());
|
||||
}
|
||||
|
||||
$enable_fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : '';
|
||||
$vars = $this->getMakeExtraVars();
|
||||
$enable_fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : '';
|
||||
$vars = $this->getMakeExtraVars();
|
||||
|
||||
// patch fake cli for micro
|
||||
$vars['EXTRA_CFLAGS'] .= $enable_fake_cli;
|
||||
$vars = SystemUtil::makeEnvVarString($vars);
|
||||
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make';
|
||||
// patch fake cli for micro
|
||||
$vars['EXTRA_CFLAGS'] .= $enable_fake_cli;
|
||||
$vars = SystemUtil::makeEnvVarString($vars);
|
||||
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
|
||||
|
||||
shell()->cd(SOURCE_PATH . '/php-src')
|
||||
->exec('sed -i "s|//lib|/lib|g" Makefile')
|
||||
->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} micro");
|
||||
shell()->cd(SOURCE_PATH . '/php-src')
|
||||
->exec('sed -i "s|//lib|/lib|g" Makefile')
|
||||
->exec("make {$concurrency} {$vars} micro");
|
||||
|
||||
$this->processMicroUPX();
|
||||
// deploy micro.sfx
|
||||
$dst = $this->deploySAPIBinary(BUILD_TARGET_MICRO);
|
||||
|
||||
$this->deployBinary(BUILD_TARGET_MICRO);
|
||||
|
||||
if ($this->phar_patched) {
|
||||
SourcePatcher::unpatchMicroPhar();
|
||||
// patch after UPX-ed micro.sfx
|
||||
$this->processUpxedMicroSfx($dst);
|
||||
} finally {
|
||||
if ($this->phar_patched) {
|
||||
SourcePatcher::unpatchMicroPhar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,19 +253,12 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
protected function buildFpm(): void
|
||||
{
|
||||
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
|
||||
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make';
|
||||
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
|
||||
shell()->cd(SOURCE_PATH . '/php-src')
|
||||
->exec('sed -i "s|//lib|/lib|g" Makefile')
|
||||
->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} fpm");
|
||||
->exec("make {$concurrency} {$vars} fpm");
|
||||
|
||||
if (!$this->getOption('no-strip', false)) {
|
||||
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')->exec('strip --strip-unneeded php-fpm');
|
||||
}
|
||||
if ($this->getOption('with-upx-pack')) {
|
||||
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')
|
||||
->exec(getenv('UPX_EXEC') . ' --best php-fpm');
|
||||
}
|
||||
$this->deployBinary(BUILD_TARGET_FPM);
|
||||
$this->deploySAPIBinary(BUILD_TARGET_FPM);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,17 +266,69 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
*/
|
||||
protected function buildEmbed(): void
|
||||
{
|
||||
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
|
||||
$sharedExts = array_filter($this->exts, static fn ($ext) => $ext->isBuildShared());
|
||||
$sharedExts = array_filter($sharedExts, static function ($ext) {
|
||||
return Config::getExt($ext->getName(), 'build-with-php') === true;
|
||||
});
|
||||
$install_modules = $sharedExts ? 'install-modules' : '';
|
||||
|
||||
// detect changes in module path
|
||||
$diff = new DirDiff(BUILD_MODULES_PATH, true);
|
||||
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
|
||||
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
|
||||
shell()->cd(SOURCE_PATH . '/php-src')
|
||||
->exec('sed -i "s|//lib|/lib|g" Makefile')
|
||||
->exec('sed -i "s|^EXTENSION_DIR = .*|EXTENSION_DIR = /' . basename(BUILD_MODULES_PATH) . '|" Makefile')
|
||||
->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install");
|
||||
->exec("make {$concurrency} INSTALL_ROOT=" . BUILD_ROOT_PATH . " {$vars} install-sapi {$install_modules} install-build install-headers install-programs");
|
||||
|
||||
// process libphp.so for shared embed
|
||||
$libphpSo = BUILD_LIB_PATH . '/libphp.so';
|
||||
if (file_exists($libphpSo)) {
|
||||
// post actions: rename libphp.so to libphp-<release>.so if -release is set in LDFLAGS
|
||||
$this->processLibphpSoFile($libphpSo);
|
||||
// deploy libphp.so
|
||||
$this->deployBinary($libphpSo, $libphpSo, false);
|
||||
}
|
||||
|
||||
// process shared extensions build-with-php
|
||||
$increment_files = $diff->getChangedFiles();
|
||||
foreach ($increment_files as $increment_file) {
|
||||
$this->deployBinary($increment_file, $increment_file, false);
|
||||
}
|
||||
|
||||
// process libphp.a for static embed
|
||||
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') {
|
||||
$AR = getenv('AR') ?: 'ar';
|
||||
f_passthru("{$AR} -t " . BUILD_LIB_PATH . "/libphp.a | grep '\\.a$' | xargs -n1 {$AR} d " . BUILD_LIB_PATH . '/libphp.a');
|
||||
// export dynamic symbols
|
||||
SystemUtil::exportDynamicSymbols(BUILD_LIB_PATH . '/libphp.a');
|
||||
}
|
||||
|
||||
// patch embed php scripts
|
||||
$this->patchPhpScripts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return extra variables for php make command.
|
||||
*/
|
||||
private function getMakeExtraVars(): array
|
||||
{
|
||||
$config = (new SPCConfigUtil($this, ['libs_only_deps' => true, 'absolute_libs' => true]))->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs'));
|
||||
$static = SPCTarget::isStatic() ? '-all-static' : '';
|
||||
$lib = BUILD_LIB_PATH;
|
||||
return array_filter([
|
||||
'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
|
||||
'EXTRA_LIBS' => $config['libs'],
|
||||
'EXTRA_LDFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'),
|
||||
'EXTRA_LDFLAGS_PROGRAM' => "-L{$lib} {$static} -pie",
|
||||
]);
|
||||
}
|
||||
|
||||
private function processLibphpSoFile(string $libphpSo): void
|
||||
{
|
||||
$ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS') ?: '';
|
||||
$libDir = BUILD_LIB_PATH;
|
||||
$modulesDir = BUILD_MODULES_PATH;
|
||||
$libphpSo = "{$libDir}/libphp.so";
|
||||
$realLibName = 'libphp.so';
|
||||
$cwd = getcwd();
|
||||
|
||||
@@ -302,7 +378,7 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
$target = "{$libDir}/{$realLibName}";
|
||||
if (file_exists($target)) {
|
||||
[, $output] = shell()->execWithResult('readelf -d ' . escapeshellarg($target));
|
||||
$output = join("\n", $output);
|
||||
$output = implode("\n", $output);
|
||||
if (preg_match('/SONAME.*\[(.+)]/', $output, $sonameMatch)) {
|
||||
$currentSoname = $sonameMatch[1];
|
||||
if ($currentSoname !== basename($target)) {
|
||||
@@ -314,60 +390,29 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') {
|
||||
$AR = getenv('AR') ?: 'ar';
|
||||
f_passthru("{$AR} -t " . BUILD_LIB_PATH . "/libphp.a | grep '\\.a$' | xargs -n1 {$AR} d " . BUILD_LIB_PATH . '/libphp.a');
|
||||
// export dynamic symbols
|
||||
SystemUtil::exportDynamicSymbols(BUILD_LIB_PATH . '/libphp.a');
|
||||
}
|
||||
|
||||
if (!$this->getOption('no-strip', false) && file_exists(BUILD_LIB_PATH . '/' . $realLibName)) {
|
||||
shell()->cd(BUILD_LIB_PATH)->exec("strip --strip-unneeded {$realLibName}");
|
||||
}
|
||||
$this->patchPhpScripts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return extra variables for php make command.
|
||||
* Patch micro.sfx after UPX compression.
|
||||
* micro needs special section handling in LinuxBuilder.
|
||||
* The micro.sfx does not support UPX directly, but we can remove UPX
|
||||
* info segment to adapt.
|
||||
* This will also make micro.sfx with upx-packed more like a malware fore antivirus
|
||||
*/
|
||||
private function getMakeExtraVars(): array
|
||||
private function processUpxedMicroSfx(string $dst): void
|
||||
{
|
||||
$config = (new SPCConfigUtil($this, ['libs_only_deps' => true, 'absolute_libs' => true]))->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs'));
|
||||
$static = SPCTarget::isStatic() ? '-all-static' : '';
|
||||
$lib = BUILD_LIB_PATH;
|
||||
return [
|
||||
'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
|
||||
'EXTRA_LIBS' => $config['libs'],
|
||||
'EXTRA_LDFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'),
|
||||
'EXTRA_LDFLAGS_PROGRAM' => "-L{$lib} {$static} -pie",
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip micro.sfx for Linux.
|
||||
* The micro.sfx does not support UPX directly, but we can remove UPX-info segment to adapt.
|
||||
* This will also make micro.sfx with upx-packed more like a malware fore antivirus :(
|
||||
*/
|
||||
private function processMicroUPX(): void
|
||||
{
|
||||
if (version_compare($this->getMicroVersion(), '0.2.0') >= 0 && !$this->getOption('no-strip', false)) {
|
||||
shell()->exec('strip --strip-unneeded ' . SOURCE_PATH . '/php-src/sapi/micro/micro.sfx');
|
||||
|
||||
if ($this->getOption('with-upx-pack')) {
|
||||
// strip first
|
||||
shell()->exec(getenv('UPX_EXEC') . ' --best ' . SOURCE_PATH . '/php-src/sapi/micro/micro.sfx');
|
||||
// cut binary with readelf
|
||||
[$ret, $out] = shell()->execWithResult('readelf -l ' . SOURCE_PATH . '/php-src/sapi/micro/micro.sfx | awk \'/LOAD|GNU_STACK/ {getline; print $1, $2, $3, $4, $6, $7}\'');
|
||||
$out[1] = explode(' ', $out[1]);
|
||||
$offset = $out[1][0];
|
||||
if ($ret !== 0 || !str_starts_with($offset, '0x')) {
|
||||
throw new PatchException('phpmicro UPX patcher', 'Cannot find offset in readelf output');
|
||||
}
|
||||
$offset = hexdec($offset);
|
||||
// remove upx extra wastes
|
||||
file_put_contents(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx', substr(file_get_contents(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx'), 0, $offset));
|
||||
if ($this->getOption('with-upx-pack') && version_compare($this->getMicroVersion(), '0.2.0') >= 0) {
|
||||
// strip first
|
||||
// cut binary with readelf
|
||||
[$ret, $out] = shell()->execWithResult("readelf -l {$dst} | awk '/LOAD|GNU_STACK/ {getline; print \$1, \$2, \$3, \$4, \$6, \$7}'");
|
||||
$out[1] = explode(' ', $out[1]);
|
||||
$offset = $out[1][0];
|
||||
if ($ret !== 0 || !str_starts_with($offset, '0x')) {
|
||||
throw new PatchException('phpmicro UPX patcher', 'Cannot find offset in readelf output');
|
||||
}
|
||||
$offset = hexdec($offset);
|
||||
// remove upx extra wastes
|
||||
file_put_contents($dst, substr(file_get_contents($dst), 0, $offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
src/SPC/builder/linux/library/libedit.php
Normal file
12
src/SPC/builder/linux/library/libedit.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\linux\library;
|
||||
|
||||
class libedit extends LinuxLibraryBase
|
||||
{
|
||||
use \SPC\builder\unix\library\libedit;
|
||||
|
||||
public const NAME = 'libedit';
|
||||
}
|
||||
12
src/SPC/builder/linux/library/net_snmp.php
Normal file
12
src/SPC/builder/linux/library/net_snmp.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\linux\library;
|
||||
|
||||
class net_snmp extends LinuxLibraryBase
|
||||
{
|
||||
use \SPC\builder\unix\library\net_snmp;
|
||||
|
||||
public const NAME = 'net-snmp';
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class openssl extends LinuxLibraryBase
|
||||
if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc'), 'prefix=')) {
|
||||
FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file);
|
||||
}
|
||||
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Libs.private: ${libdir}/libz.a');
|
||||
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Requires.private: zlib');
|
||||
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\linux\library;
|
||||
|
||||
/**
|
||||
* gmp is a template library class for unix
|
||||
*/
|
||||
class readline extends LinuxLibraryBase
|
||||
{
|
||||
use \SPC\builder\unix\library\readline;
|
||||
|
||||
@@ -7,6 +7,8 @@ namespace SPC\builder\macos;
|
||||
use SPC\builder\macos\library\MacOSLibraryBase;
|
||||
use SPC\builder\unix\UnixBuilderBase;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\DirDiff;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\store\SourcePatcher;
|
||||
use SPC\util\GlobalEnvManager;
|
||||
@@ -100,10 +102,11 @@ class MacOSBuilder extends UnixBuilderBase
|
||||
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
|
||||
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
|
||||
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
|
||||
$enableCgi = ($build_target & BUILD_TARGET_CGI) === BUILD_TARGET_CGI;
|
||||
|
||||
// prepare build php envs
|
||||
$envs_build_php = SystemUtil::makeEnvVarString([
|
||||
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'),
|
||||
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
|
||||
'CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH,
|
||||
'LDFLAGS' => '-L' . BUILD_LIB_PATH,
|
||||
]);
|
||||
@@ -117,20 +120,20 @@ class MacOSBuilder extends UnixBuilderBase
|
||||
}
|
||||
|
||||
$embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
|
||||
shell()->cd(SOURCE_PATH . '/php-src')
|
||||
->exec(
|
||||
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
|
||||
$this->seekPhpSrcLogFileOnException(fn () => shell()->cd(SOURCE_PATH . '/php-src')->exec(
|
||||
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
|
||||
($enableCli ? '--enable-cli ' : '--disable-cli ') .
|
||||
($enableFpm ? '--enable-fpm ' : '--disable-fpm ') .
|
||||
($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') .
|
||||
($enableMicro ? '--enable-micro ' : '--disable-micro ') .
|
||||
($enableCgi ? '--enable-cgi ' : '--disable-cgi ') .
|
||||
$config_file_path .
|
||||
$config_file_scan_dir .
|
||||
$json_74 .
|
||||
$zts .
|
||||
$this->makeStaticExtensionArgs() . ' ' .
|
||||
$envs_build_php
|
||||
);
|
||||
));
|
||||
|
||||
$this->emitPatchPoint('before-php-make');
|
||||
SourcePatcher::patchBeforeMake($this);
|
||||
@@ -145,6 +148,10 @@ class MacOSBuilder extends UnixBuilderBase
|
||||
logger()->info('building fpm');
|
||||
$this->buildFpm();
|
||||
}
|
||||
if ($enableCgi) {
|
||||
logger()->info('building cgi');
|
||||
$this->buildCgi();
|
||||
}
|
||||
if ($enableMicro) {
|
||||
logger()->info('building micro');
|
||||
$this->buildMicro();
|
||||
@@ -156,15 +163,15 @@ class MacOSBuilder extends UnixBuilderBase
|
||||
}
|
||||
$this->buildEmbed();
|
||||
}
|
||||
if ($enableFrankenphp) {
|
||||
logger()->info('building frankenphp');
|
||||
$this->buildFrankenphp();
|
||||
}
|
||||
$shared_extensions = array_map('trim', array_filter(explode(',', $this->getOption('build-shared'))));
|
||||
if (!empty($shared_extensions)) {
|
||||
logger()->info('Building shared extensions ...');
|
||||
$this->buildSharedExts();
|
||||
}
|
||||
if ($enableFrankenphp) {
|
||||
logger()->info('building frankenphp');
|
||||
$this->buildFrankenphp();
|
||||
}
|
||||
}
|
||||
|
||||
public function testPHP(int $build_target = BUILD_TARGET_NONE)
|
||||
@@ -181,12 +188,19 @@ class MacOSBuilder extends UnixBuilderBase
|
||||
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
|
||||
|
||||
$shell = shell()->cd(SOURCE_PATH . '/php-src');
|
||||
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make';
|
||||
$shell->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} cli");
|
||||
if (!$this->getOption('no-strip', false)) {
|
||||
$shell->exec('dsymutil -f sapi/cli/php')->exec('strip -S sapi/cli/php');
|
||||
}
|
||||
$this->deployBinary(BUILD_TARGET_CLI);
|
||||
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
|
||||
$shell->exec("make {$concurrency} {$vars} cli");
|
||||
$this->deploySAPIBinary(BUILD_TARGET_CLI);
|
||||
}
|
||||
|
||||
protected function buildCgi(): void
|
||||
{
|
||||
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
|
||||
|
||||
$shell = shell()->cd(SOURCE_PATH . '/php-src');
|
||||
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
|
||||
$shell->exec("make {$concurrency} {$vars} cgi");
|
||||
$this->deploySAPIBinary(BUILD_TARGET_CGI);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,30 +211,30 @@ class MacOSBuilder extends UnixBuilderBase
|
||||
if ($this->getPHPVersionID() < 80000) {
|
||||
throw new WrongUsageException('phpmicro only support PHP >= 8.0!');
|
||||
}
|
||||
if ($this->getExt('phar')) {
|
||||
$this->phar_patched = true;
|
||||
SourcePatcher::patchMicroPhar($this->getPHPVersionID());
|
||||
}
|
||||
|
||||
$enable_fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : '';
|
||||
$vars = $this->getMakeExtraVars();
|
||||
try {
|
||||
if ($this->getExt('phar')) {
|
||||
$this->phar_patched = true;
|
||||
SourcePatcher::patchMicroPhar($this->getPHPVersionID());
|
||||
}
|
||||
|
||||
// patch fake cli for micro
|
||||
$vars['EXTRA_CFLAGS'] .= $enable_fake_cli;
|
||||
$vars = SystemUtil::makeEnvVarString($vars);
|
||||
$enable_fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : '';
|
||||
$vars = $this->getMakeExtraVars();
|
||||
|
||||
$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');
|
||||
}
|
||||
// patch fake cli for micro
|
||||
$vars['EXTRA_CFLAGS'] .= $enable_fake_cli;
|
||||
$vars = SystemUtil::makeEnvVarString($vars);
|
||||
|
||||
$this->deployBinary(BUILD_TARGET_MICRO);
|
||||
$shell = shell()->cd(SOURCE_PATH . '/php-src');
|
||||
// build
|
||||
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
|
||||
$shell->exec("make {$concurrency} {$vars} micro");
|
||||
|
||||
if ($this->phar_patched) {
|
||||
SourcePatcher::unpatchMicroPhar();
|
||||
$this->deploySAPIBinary(BUILD_TARGET_MICRO);
|
||||
} finally {
|
||||
if ($this->phar_patched) {
|
||||
SourcePatcher::unpatchMicroPhar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,11 +246,9 @@ class MacOSBuilder extends UnixBuilderBase
|
||||
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
|
||||
|
||||
$shell = shell()->cd(SOURCE_PATH . '/php-src');
|
||||
$shell->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . " {$vars} fpm");
|
||||
if (!$this->getOption('no-strip', false)) {
|
||||
$shell->exec('dsymutil -f sapi/fpm/php-fpm')->exec('strip -S sapi/fpm/php-fpm');
|
||||
}
|
||||
$this->deployBinary(BUILD_TARGET_FPM);
|
||||
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
|
||||
$shell->exec("make {$concurrency} {$vars} fpm");
|
||||
$this->deploySAPIBinary(BUILD_TARGET_FPM);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,10 +256,31 @@ class MacOSBuilder extends UnixBuilderBase
|
||||
*/
|
||||
protected function buildEmbed(): void
|
||||
{
|
||||
$sharedExts = array_filter($this->exts, static fn ($ext) => $ext->isBuildShared());
|
||||
$sharedExts = array_filter($sharedExts, static function ($ext) {
|
||||
return Config::getExt($ext->getName(), 'build-with-php') === true;
|
||||
});
|
||||
$install_modules = $sharedExts ? 'install-modules' : '';
|
||||
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
|
||||
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
|
||||
|
||||
$diff = new DirDiff(BUILD_MODULES_PATH, true);
|
||||
|
||||
shell()->cd(SOURCE_PATH . '/php-src')
|
||||
->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install");
|
||||
->exec('sed -i "" "s|^EXTENSION_DIR = .*|EXTENSION_DIR = /' . basename(BUILD_MODULES_PATH) . '|" Makefile')
|
||||
->exec("make {$concurrency} INSTALL_ROOT=" . BUILD_ROOT_PATH . " {$vars} install-sapi {$install_modules} install-build install-headers install-programs");
|
||||
|
||||
$libphp = BUILD_LIB_PATH . '/libphp.dylib';
|
||||
if (file_exists($libphp)) {
|
||||
$this->deployBinary($libphp, $libphp, false);
|
||||
// macOS currently have no -release option for dylib, so we just rename it here
|
||||
}
|
||||
|
||||
// process shared extensions build-with-php
|
||||
$increment_files = $diff->getChangedFiles();
|
||||
foreach ($increment_files as $increment_file) {
|
||||
$this->deployBinary($increment_file, $increment_file, false);
|
||||
}
|
||||
|
||||
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') {
|
||||
$AR = getenv('AR') ?: 'ar';
|
||||
@@ -261,10 +294,10 @@ class MacOSBuilder extends UnixBuilderBase
|
||||
private function getMakeExtraVars(): array
|
||||
{
|
||||
$config = (new SPCConfigUtil($this, ['libs_only_deps' => true]))->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs'));
|
||||
return [
|
||||
return array_filter([
|
||||
'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
|
||||
'EXTRA_LDFLAGS_PROGRAM' => '-L' . BUILD_LIB_PATH,
|
||||
'EXTRA_LIBS' => $config['libs'],
|
||||
];
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
12
src/SPC/builder/macos/library/libedit.php
Normal file
12
src/SPC/builder/macos/library/libedit.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class libedit extends MacOSLibraryBase
|
||||
{
|
||||
use \SPC\builder\unix\library\libedit;
|
||||
|
||||
public const NAME = 'libedit';
|
||||
}
|
||||
12
src/SPC/builder/macos/library/net_snmp.php
Normal file
12
src/SPC/builder/macos/library/net_snmp.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class net_snmp extends MacOSLibraryBase
|
||||
{
|
||||
use \SPC\builder\unix\library\net_snmp;
|
||||
|
||||
public const NAME = 'net-snmp';
|
||||
}
|
||||
@@ -63,7 +63,7 @@ class openssl extends MacOSLibraryBase
|
||||
if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc'), 'prefix=')) {
|
||||
FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file);
|
||||
}
|
||||
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Libs.private: ${libdir}/libz.a');
|
||||
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Requires.private: zlib');
|
||||
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
/**
|
||||
* gmp is a template library class for unix
|
||||
*/
|
||||
class readline extends MacOSLibraryBase
|
||||
{
|
||||
use \SPC\builder\unix\library\readline;
|
||||
|
||||
@@ -5,15 +5,16 @@ declare(strict_types=1);
|
||||
namespace SPC\builder\unix;
|
||||
|
||||
use SPC\builder\BuilderBase;
|
||||
use SPC\builder\linux\SystemUtil;
|
||||
use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
|
||||
use SPC\exception\SPCException;
|
||||
use SPC\exception\SPCInternalException;
|
||||
use SPC\exception\ValidationException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\CurlHook;
|
||||
use SPC\store\Downloader;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\store\pkg\GoXcaddy;
|
||||
use SPC\store\SourceManager;
|
||||
use SPC\toolchain\GccNativeToolchain;
|
||||
use SPC\toolchain\ToolchainManager;
|
||||
use SPC\util\DependencyUtil;
|
||||
@@ -79,6 +80,92 @@ abstract class UnixBuilderBase extends BuilderBase
|
||||
$this->lib_list = $sorted_libraries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip unneeded symbols from binary file.
|
||||
*/
|
||||
public function stripBinary(string $binary_path): void
|
||||
{
|
||||
shell()->exec(match (PHP_OS_FAMILY) {
|
||||
'Darwin' => "strip -S {$binary_path}",
|
||||
'Linux' => "strip --strip-unneeded {$binary_path}",
|
||||
default => throw new SPCInternalException('stripBinary is only supported on Linux and macOS'),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract debug information from binary file.
|
||||
*
|
||||
* @param string $binary_path the path to the binary file, including executables, shared libraries, etc
|
||||
*/
|
||||
public function extractDebugInfo(string $binary_path): string
|
||||
{
|
||||
$target_dir = BUILD_ROOT_PATH . '/debug';
|
||||
FileSystem::createDir($target_dir);
|
||||
$basename = basename($binary_path);
|
||||
$debug_file = "{$target_dir}/{$basename}" . (PHP_OS_FAMILY === 'Darwin' ? '.dwarf' : '.debug');
|
||||
if (PHP_OS_FAMILY === 'Darwin') {
|
||||
shell()->exec("dsymutil -f {$binary_path} -o {$debug_file}");
|
||||
} elseif (PHP_OS_FAMILY === 'Linux') {
|
||||
if ($eu_strip = SystemUtil::findCommand('eu-strip')) {
|
||||
shell()
|
||||
->exec("{$eu_strip} -f {$debug_file} {$binary_path}")
|
||||
->exec("objcopy --add-gnu-debuglink={$debug_file} {$binary_path}");
|
||||
} else {
|
||||
shell()
|
||||
->exec("objcopy --only-keep-debug {$binary_path} {$debug_file}")
|
||||
->exec("objcopy --add-gnu-debuglink={$debug_file} {$binary_path}");
|
||||
}
|
||||
} else {
|
||||
throw new SPCInternalException('extractDebugInfo is only supported on Linux and macOS');
|
||||
}
|
||||
return $debug_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy the binary file from src to dst.
|
||||
*/
|
||||
public function deployBinary(string $src, string $dst, bool $executable = true): string
|
||||
{
|
||||
logger()->debug('Deploying binary from ' . $src . ' to ' . $dst);
|
||||
|
||||
// file must exists
|
||||
if (!file_exists($src)) {
|
||||
throw new SPCInternalException("Deploy failed. Cannot find file: {$src}");
|
||||
}
|
||||
// dst dir must exists
|
||||
FileSystem::createDir(dirname($dst));
|
||||
|
||||
// ignore copy to self
|
||||
if (realpath($src) !== realpath($dst)) {
|
||||
shell()->exec('cp ' . escapeshellarg($src) . ' ' . escapeshellarg($dst));
|
||||
}
|
||||
|
||||
// file exist
|
||||
if (!file_exists($dst)) {
|
||||
throw new SPCInternalException("Deploy failed. Cannot find file after copy: {$dst}");
|
||||
}
|
||||
|
||||
// extract debug info
|
||||
$this->extractDebugInfo($dst);
|
||||
|
||||
// strip
|
||||
if (!$this->getOption('no-strip')) {
|
||||
$this->stripBinary($dst);
|
||||
}
|
||||
|
||||
// UPX for linux
|
||||
$upx_option = $this->getOption('with-upx-pack');
|
||||
if ($upx_option && PHP_OS_FAMILY === 'Linux' && $executable) {
|
||||
if ($this->getOption('no-strip')) {
|
||||
logger()->warning('UPX compression is not recommended when --no-strip is enabled.');
|
||||
}
|
||||
logger()->info("Compressing {$dst} with UPX");
|
||||
shell()->exec(getenv('UPX_EXEC') . " --best {$dst}");
|
||||
}
|
||||
|
||||
return $dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanity check after build complete.
|
||||
*/
|
||||
@@ -122,6 +209,16 @@ abstract class UnixBuilderBase extends BuilderBase
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check for php-cgi
|
||||
if (($build_target & BUILD_TARGET_CGI) === BUILD_TARGET_CGI) {
|
||||
logger()->info('running cgi sanity check');
|
||||
[$ret, $output] = shell()->execWithResult("echo '<?php echo \"<h1>Hello, World!</h1>\";' | " . BUILD_BIN_PATH . '/php-cgi -n');
|
||||
$raw_output = implode('', $output);
|
||||
if ($ret !== 0 || !str_contains($raw_output, 'Hello, World!') || !str_contains($raw_output, 'text/html')) {
|
||||
throw new ValidationException("cgi failed sanity check. code: {$ret}, output: {$raw_output}", validation_module: 'php-cgi sanity check');
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check for embed
|
||||
if (($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED) {
|
||||
logger()->info('running embed sanity check');
|
||||
@@ -199,22 +296,20 @@ abstract class UnixBuilderBase extends BuilderBase
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy the binary file to the build bin path.
|
||||
*
|
||||
* @param int $type Type integer, one of BUILD_TARGET_CLI, BUILD_TARGET_MICRO, BUILD_TARGET_FPM
|
||||
* Deploy binaries that produces executable SAPI
|
||||
*/
|
||||
protected function deployBinary(int $type): bool
|
||||
protected function deploySAPIBinary(int $type): string
|
||||
{
|
||||
$src = match ($type) {
|
||||
BUILD_TARGET_CLI => SOURCE_PATH . '/php-src/sapi/cli/php',
|
||||
BUILD_TARGET_MICRO => SOURCE_PATH . '/php-src/sapi/micro/micro.sfx',
|
||||
BUILD_TARGET_FPM => SOURCE_PATH . '/php-src/sapi/fpm/php-fpm',
|
||||
BUILD_TARGET_CGI => SOURCE_PATH . '/php-src/sapi/cgi/php-cgi',
|
||||
BUILD_TARGET_FRANKENPHP => BUILD_BIN_PATH . '/frankenphp',
|
||||
default => throw new SPCInternalException("Deployment does not accept type {$type}"),
|
||||
};
|
||||
logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file');
|
||||
FileSystem::createDir(BUILD_BIN_PATH);
|
||||
shell()->exec('cp ' . escapeshellarg($src) . ' ' . escapeshellarg(BUILD_BIN_PATH));
|
||||
return true;
|
||||
$dst = BUILD_BIN_PATH . '/' . basename($src);
|
||||
return $this->deployBinary($src, $dst);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,24 +350,75 @@ abstract class UnixBuilderBase extends BuilderBase
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the --with-frankenphp-app option
|
||||
* Creates app.tar and app.checksum in source/frankenphp directory
|
||||
*/
|
||||
protected function processFrankenphpApp(): void
|
||||
{
|
||||
$frankenphpSourceDir = getenv('FRANKENPHP_SOURCE_PATH') ?: SOURCE_PATH . '/frankenphp';
|
||||
if (!is_dir($frankenphpSourceDir)) {
|
||||
SourceManager::initSource(['frankenphp'], ['frankenphp']);
|
||||
}
|
||||
$frankenphpAppPath = $this->getOption('with-frankenphp-app');
|
||||
|
||||
if ($frankenphpAppPath) {
|
||||
if (!is_dir($frankenphpAppPath)) {
|
||||
throw new WrongUsageException("The path provided to --with-frankenphp-app is not a valid directory: {$frankenphpAppPath}");
|
||||
}
|
||||
$appTarPath = $frankenphpSourceDir . '/app.tar';
|
||||
logger()->info("Creating app.tar from {$frankenphpAppPath}");
|
||||
|
||||
shell()->exec('tar -cf ' . escapeshellarg($appTarPath) . ' -C ' . escapeshellarg($frankenphpAppPath) . ' .');
|
||||
|
||||
$checksum = hash_file('md5', $appTarPath);
|
||||
file_put_contents($frankenphpSourceDir . '/app_checksum.txt', $checksum);
|
||||
} else {
|
||||
FileSystem::removeFileIfExists($frankenphpSourceDir . '/app.tar');
|
||||
FileSystem::removeFileIfExists($frankenphpSourceDir . '/app_checksum.txt');
|
||||
file_put_contents($frankenphpSourceDir . '/app.tar', '');
|
||||
file_put_contents($frankenphpSourceDir . '/app_checksum.txt', '');
|
||||
}
|
||||
}
|
||||
|
||||
protected function getFrankenPHPVersion(): string
|
||||
{
|
||||
if ($version = getenv('FRANKENPHP_VERSION')) {
|
||||
return $version;
|
||||
}
|
||||
$frankenphpSourceDir = getenv('FRANKENPHP_SOURCE_PATH') ?: SOURCE_PATH . '/frankenphp';
|
||||
$goModPath = $frankenphpSourceDir . '/caddy/go.mod';
|
||||
|
||||
if (!file_exists($goModPath)) {
|
||||
throw new SPCInternalException("FrankenPHP caddy/go.mod file not found at {$goModPath}, why did we not download FrankenPHP?");
|
||||
}
|
||||
|
||||
$content = file_get_contents($goModPath);
|
||||
if (preg_match('/github\.com\/dunglas\/frankenphp\s+v?(\d+\.\d+\.\d+)/', $content, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
throw new SPCInternalException('Could not find FrankenPHP version in caddy/go.mod');
|
||||
}
|
||||
|
||||
protected function buildFrankenphp(): void
|
||||
{
|
||||
GlobalEnvManager::addPathIfNotExists(GoXcaddy::getPath());
|
||||
$this->processFrankenphpApp();
|
||||
$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;
|
||||
}
|
||||
$frankenphpSourceDir = getenv('FRANKENPHP_SOURCE_PATH') ?: SOURCE_PATH . '/frankenphp';
|
||||
|
||||
$xcaddyModules = preg_replace('#--with github.com/dunglas/frankenphp\S*#', '', $xcaddyModules);
|
||||
$xcaddyModules = "--with github.com/dunglas/frankenphp={$frankenphpSourceDir} " .
|
||||
"--with github.com/dunglas/frankenphp/caddy={$frankenphpSourceDir}/caddy {$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);
|
||||
}
|
||||
$releaseInfo = json_decode(Downloader::curlExec(
|
||||
'https://api.github.com/repos/php/frankenphp/releases/latest',
|
||||
hooks: [[CurlHook::class, 'setupGithubToken']],
|
||||
), true);
|
||||
$frankenPhpVersion = $releaseInfo['tag_name'];
|
||||
|
||||
$frankenPhpVersion = $this->getFrankenPHPVersion();
|
||||
$libphpVersion = $this->getPHPVersion();
|
||||
$dynamic_exports = '';
|
||||
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
|
||||
@@ -282,20 +428,18 @@ abstract class UnixBuilderBase extends BuilderBase
|
||||
$dynamic_exports = ' ' . $dynamicSymbolsArgument;
|
||||
}
|
||||
}
|
||||
$debugFlags = $this->getOption('no-strip') ? '-w -s ' : '';
|
||||
$extLdFlags = "-extldflags '-pie{$dynamic_exports}'";
|
||||
$extLdFlags = "-extldflags '-pie{$dynamic_exports} {$this->arch_ld_flags}'";
|
||||
$muslTags = '';
|
||||
$staticFlags = '';
|
||||
if (SPCTarget::isStatic()) {
|
||||
$extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000{$dynamic_exports}'";
|
||||
$extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000{$dynamic_exports} {$this->arch_ld_flags}'";
|
||||
$muslTags = 'static_build,';
|
||||
$staticFlags = '-static-pie';
|
||||
}
|
||||
|
||||
$config = (new SPCConfigUtil($this))->config($this->ext_list, $this->lib_list);
|
||||
$cflags = "{$this->arch_c_flags} {$config['cflags']} " . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS');
|
||||
$cflags = "{$this->arch_c_flags} {$config['cflags']} " . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -DFRANKENPHP_VERSION=' . $frankenPhpVersion;
|
||||
$libs = $config['libs'];
|
||||
$libs .= PHP_OS_FAMILY === 'Linux' ? ' -lrt' : '';
|
||||
// Go's gcc driver doesn't automatically link against -lgcov or -lrt. Ugly, but necessary fix.
|
||||
if ((str_contains((string) getenv('SPC_DEFAULT_C_FLAGS'), '-fprofile') ||
|
||||
str_contains((string) getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), '-fprofile')) &&
|
||||
@@ -303,34 +447,37 @@ abstract class UnixBuilderBase extends BuilderBase
|
||||
$cflags .= ' -Wno-error=missing-profile';
|
||||
$libs .= ' -lgcov';
|
||||
}
|
||||
$env = [
|
||||
$env = [...[
|
||||
'CGO_ENABLED' => '1',
|
||||
'CGO_CFLAGS' => clean_spaces($cflags),
|
||||
'CGO_LDFLAGS' => "{$this->arch_ld_flags} {$staticFlags} {$config['ldflags']} {$libs}",
|
||||
'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' .
|
||||
'-ldflags \"-linkmode=external ' . $extLdFlags . ' ' . $debugFlags .
|
||||
'-ldflags \"-linkmode=external ' . $extLdFlags . ' ' .
|
||||
'-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' .
|
||||
"{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " .
|
||||
"v{$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;
|
||||
}
|
||||
}
|
||||
], ...GoXcaddy::getEnvironment()];
|
||||
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')) {
|
||||
if (PHP_OS_FAMILY === 'Linux') {
|
||||
shell()->cd(BUILD_BIN_PATH)->exec('strip --strip-unneeded frankenphp');
|
||||
} else { // macOS doesn't understand strip-unneeded
|
||||
shell()->cd(BUILD_BIN_PATH)->exec('strip -S frankenphp');
|
||||
$this->deploySAPIBinary(BUILD_TARGET_FRANKENPHP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek php-src/config.log when building PHP, add it to exception.
|
||||
*/
|
||||
protected function seekPhpSrcLogFileOnException(callable $callback): void
|
||||
{
|
||||
try {
|
||||
$callback();
|
||||
} catch (SPCException $e) {
|
||||
if (file_exists(SOURCE_PATH . '/php-src/config.log')) {
|
||||
$e->addExtraLogFile('php-src config.log', 'php-src.config.log');
|
||||
copy(SOURCE_PATH . '/php-src/config.log', SPC_LOGS_DIR . '/php-src.config.log');
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ trait attr
|
||||
->exec('libtoolize --force --copy')
|
||||
->exec('./autogen.sh || autoreconf -if')
|
||||
->configure('--disable-nls')
|
||||
->make();
|
||||
->make('install-attributes_h install-data install-libattr_h install-libLTLIBRARIES install-pkgincludeHEADERS install-pkgconfDATA', with_install: false);
|
||||
$this->patchPkgconfPrefix(['libattr.pc'], PKGCONF_PATCH_PREFIX);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ trait gettext
|
||||
$autoconf->addConfigureArgs('--disable-threads');
|
||||
}
|
||||
|
||||
$autoconf->configure()->make();
|
||||
$autoconf->configure()->make(dir: $this->getSourceDir() . '/gettext-runtime/intl');
|
||||
$this->patchLaDependencyPrefix();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,11 @@ trait imagemagick
|
||||
{
|
||||
protected function build(): void
|
||||
{
|
||||
$original_ldflags = $this->builder->arch_ld_flags;
|
||||
if (str_contains($this->builder->arch_ld_flags, '-Wl,--as-needed')) {
|
||||
$this->builder->arch_ld_flags = str_replace('-Wl,--as-needed', '', $original_ldflags);
|
||||
}
|
||||
|
||||
$ac = UnixAutoconfExecutor::create($this)
|
||||
->optionalLib('libzip', ...ac_with_args('zip'))
|
||||
->optionalLib('libjpeg', ...ac_with_args('jpeg'))
|
||||
@@ -32,7 +37,7 @@ trait imagemagick
|
||||
);
|
||||
|
||||
// special: linux-static target needs `-static`
|
||||
$ldflags = SPCTarget::isStatic() ? ('-static -ldl') : '-ldl';
|
||||
$ldflags = SPCTarget::isStatic() ? '-static -ldl' : '-ldl';
|
||||
|
||||
// special: macOS needs -iconv
|
||||
$libs = SPCTarget::getTargetOS() === 'Darwin' ? '-liconv' : '';
|
||||
@@ -45,6 +50,8 @@ trait imagemagick
|
||||
|
||||
$ac->configure()->make();
|
||||
|
||||
$this->builder->arch_ld_flags = $original_ldflags;
|
||||
|
||||
$filelist = [
|
||||
'ImageMagick.pc',
|
||||
'ImageMagick-7.Q16HDRI.pc',
|
||||
|
||||
@@ -26,7 +26,7 @@ trait libacl
|
||||
->exec('libtoolize --force --copy')
|
||||
->exec('./autogen.sh || autoreconf -if')
|
||||
->configure('--disable-nls', '--disable-tests')
|
||||
->make();
|
||||
->make('install-acl_h install-libacl_h install-data install-libLTLIBRARIES install-pkgincludeHEADERS install-sysincludeHEADERS install-pkgconfDATA', with_install: false);
|
||||
$this->patchPkgconfPrefix(['libacl.pc'], PKGCONF_PATCH_PREFIX);
|
||||
}
|
||||
}
|
||||
|
||||
30
src/SPC/builder/unix/library/libedit.php
Normal file
30
src/SPC/builder/unix/library/libedit.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\unix\library;
|
||||
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\executor\UnixAutoconfExecutor;
|
||||
|
||||
trait libedit
|
||||
{
|
||||
public function patchBeforeBuild(): bool
|
||||
{
|
||||
FileSystem::replaceFileRegex(
|
||||
$this->source_dir . '/src/sys.h',
|
||||
'|//#define\s+strl|',
|
||||
'#define strl'
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function build(): void
|
||||
{
|
||||
UnixAutoconfExecutor::create($this)
|
||||
->appendEnv(['CFLAGS' => '-D__STDC_ISO_10646__=201103L'])
|
||||
->configure()
|
||||
->make();
|
||||
$this->patchPkgconfPrefix(['libedit.pc']);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,13 @@ trait libiconv
|
||||
{
|
||||
protected function build(): void
|
||||
{
|
||||
UnixAutoconfExecutor::create($this)->configure('--enable-extra-encodings')->make();
|
||||
UnixAutoconfExecutor::create($this)
|
||||
->configure(
|
||||
'--enable-extra-encodings',
|
||||
'--enable-year2038',
|
||||
)
|
||||
->make('install-lib', with_install: false)
|
||||
->make('install-lib', with_install: false, dir: $this->getSourceDir() . '/libcharset');
|
||||
$this->patchLaDependencyPrefix();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace SPC\builder\unix\library;
|
||||
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\executor\UnixAutoconfExecutor;
|
||||
use SPC\util\executor\UnixCMakeExecutor;
|
||||
|
||||
trait librdkafka
|
||||
{
|
||||
@@ -26,34 +26,18 @@ trait librdkafka
|
||||
|
||||
protected function build(): void
|
||||
{
|
||||
UnixAutoconfExecutor::create($this)
|
||||
->appendEnv(['CFLAGS' => '-Wno-int-conversion -Wno-unused-but-set-variable -Wno-unused-variable'])
|
||||
->optionalLib(
|
||||
'zstd',
|
||||
function ($lib) {
|
||||
putenv("STATIC_LIB_libzstd={$lib->getLibDir()}/libzstd.a");
|
||||
return '';
|
||||
},
|
||||
'--disable-zstd'
|
||||
UnixCMakeExecutor::create($this)
|
||||
->optionalLib('zstd', ...cmake_boolean_args('WITH_ZSTD'))
|
||||
->optionalLib('curl', ...cmake_boolean_args('WITH_CURL'))
|
||||
->optionalLib('openssl', ...cmake_boolean_args('WITH_SSL'))
|
||||
->optionalLib('zlib', ...cmake_boolean_args('WITH_ZLIB'))
|
||||
->optionalLib('liblz4', ...cmake_boolean_args('ENABLE_LZ4_EXT'))
|
||||
->addConfigureArgs(
|
||||
'-DWITH_SASL=OFF',
|
||||
'-DRDKAFKA_BUILD_STATIC=ON',
|
||||
'-DRDKAFKA_BUILD_EXAMPLES=OFF',
|
||||
'-DRDKAFKA_BUILD_TESTS=OFF',
|
||||
)
|
||||
->removeConfigureArgs(
|
||||
'--with-pic',
|
||||
'--enable-pic',
|
||||
)
|
||||
->configure(
|
||||
'--disable-curl',
|
||||
'--disable-sasl',
|
||||
'--disable-valgrind',
|
||||
'--disable-zlib',
|
||||
'--disable-ssl',
|
||||
)
|
||||
->make();
|
||||
|
||||
$this->patchPkgconfPrefix(['rdkafka.pc', 'rdkafka-static.pc', 'rdkafka++.pc', 'rdkafka++-static.pc']);
|
||||
// remove dynamic libs
|
||||
shell()
|
||||
->exec("rm -rf {$this->getLibDir()}/*.so.*")
|
||||
->exec("rm -rf {$this->getLibDir()}/*.so")
|
||||
->exec("rm -rf {$this->getLibDir()}/*.dylib");
|
||||
->build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,17 +20,17 @@ trait libxml2
|
||||
"-DZLIB_INCLUDE_DIR={$this->getIncludeDir()}",
|
||||
'-DLIBXML2_WITH_ZLIB=OFF',
|
||||
)
|
||||
->optionalLib('icu', ...cmake_boolean_args('LIBXML2_WITH_ICU'))
|
||||
->optionalLib('xz', ...cmake_boolean_args('LIBXML2_WITH_LZMA'))
|
||||
->addConfigureArgs(
|
||||
'-DLIBXML2_WITH_ICONV=ON',
|
||||
'-DLIBXML2_WITH_ICU=OFF', // optional, but discouraged: https://gitlab.gnome.org/GNOME/libxml2/-/blob/master/README.md
|
||||
'-DLIBXML2_WITH_PYTHON=OFF',
|
||||
'-DLIBXML2_WITH_PROGRAMS=OFF',
|
||||
'-DLIBXML2_WITH_TESTS=OFF',
|
||||
);
|
||||
|
||||
if ($this instanceof LinuxLibraryBase) {
|
||||
$cmake->addConfigureArgs('-DIconv_IS_BUILD_IN=OFF');
|
||||
$cmake->addConfigureArgs('-DIconv_IS_BUILT_IN=OFF');
|
||||
}
|
||||
|
||||
$cmake->build();
|
||||
|
||||
@@ -22,6 +22,7 @@ trait libzip
|
||||
'-DBUILD_EXAMPLES=OFF',
|
||||
'-DBUILD_REGRESS=OFF',
|
||||
'-DBUILD_TOOLS=OFF',
|
||||
'-DBUILD_OSSFUZZ=OFF',
|
||||
)
|
||||
->build();
|
||||
$this->patchPkgconfPrefix(['libzip.pc'], PKGCONF_PATCH_PREFIX);
|
||||
|
||||
@@ -38,7 +38,7 @@ trait ncurses
|
||||
->make();
|
||||
$final = FileSystem::scanDirFiles(BUILD_BIN_PATH, relative: true);
|
||||
// Remove the new files
|
||||
$new_files = array_diff($final, $filelist);
|
||||
$new_files = array_diff($final, $filelist ?: []);
|
||||
foreach ($new_files as $file) {
|
||||
@unlink(BUILD_BIN_PATH . '/' . $file);
|
||||
}
|
||||
|
||||
49
src/SPC/builder/unix/library/net_snmp.php
Normal file
49
src/SPC/builder/unix/library/net_snmp.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\unix\library;
|
||||
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\executor\UnixAutoconfExecutor;
|
||||
|
||||
trait net_snmp
|
||||
{
|
||||
public function patchBeforeBuild(): bool
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Linux') {
|
||||
FileSystem::replaceFileStr("{$this->source_dir}/configure", 'LIBS="-lssl ${OPENSSL_LIBS}"', 'LIBS="-lssl ${OPENSSL_LIBS} -lpthread -ldl"');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function build(): void
|
||||
{
|
||||
// use --static for PKG_CONFIG
|
||||
UnixAutoconfExecutor::create($this)
|
||||
->setEnv(['PKG_CONFIG' => getenv('PKG_CONFIG') . ' --static'])
|
||||
->configure(
|
||||
'--disable-mibs',
|
||||
'--without-nl',
|
||||
'--disable-agent',
|
||||
'--disable-applications',
|
||||
'--disable-manuals',
|
||||
'--disable-scripts',
|
||||
'--disable-embedded-perl',
|
||||
'--without-perl-modules',
|
||||
'--with-out-mib-modules="if-mib host disman/event-mib ucd-snmp/diskio mibII"',
|
||||
'--with-out-transports="Unix"',
|
||||
'--with-mib-modules=""',
|
||||
'--enable-mini-agent',
|
||||
'--with-default-snmp-version="3"',
|
||||
'--with-sys-contact="@@no.where"',
|
||||
'--with-sys-location="Unknown"',
|
||||
'--with-logfile="/var/log/snmpd.log"',
|
||||
'--with-persistent-directory="/var/lib/net-snmp"',
|
||||
'--with-openssl=' . BUILD_ROOT_PATH,
|
||||
'--with-zlib=' . BUILD_ROOT_PATH,
|
||||
)->make(with_install: 'installheaders installlibs install_pkgconfig');
|
||||
$this->patchPkgconfPrefix();
|
||||
}
|
||||
}
|
||||
@@ -4,93 +4,83 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\unix\library;
|
||||
|
||||
use SPC\builder\linux\library\LinuxLibraryBase;
|
||||
use SPC\exception\BuildFailureException;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\PkgConfigUtil;
|
||||
use SPC\util\SPCConfigUtil;
|
||||
use SPC\util\SPCTarget;
|
||||
|
||||
trait postgresql
|
||||
{
|
||||
public function patchBeforeBuild(): bool
|
||||
{
|
||||
// fix aarch64 build on glibc 2.17 (e.g. CentOS 7)
|
||||
if (SPCTarget::getLibcVersion() === '2.17' && GNU_ARCH === 'aarch64') {
|
||||
try {
|
||||
FileSystem::replaceFileStr("{$this->source_dir}/src/port/pg_popcount_aarch64.c", 'HWCAP_SVE', '0');
|
||||
FileSystem::replaceFileStr(
|
||||
"{$this->source_dir}/src/port/pg_crc32c_armv8_choose.c",
|
||||
'#if defined(__linux__) && !defined(__aarch64__) && !defined(HWCAP2_CRC32)',
|
||||
'#if defined(__linux__) && !defined(HWCAP_CRC32)'
|
||||
);
|
||||
} catch (FileSystemException) {
|
||||
// allow file not-existence to make it compatible with old and new version
|
||||
}
|
||||
}
|
||||
// skip the test on platforms where libpq infrastructure may be provided by statically-linked libraries
|
||||
FileSystem::replaceFileStr("{$this->source_dir}/src/interfaces/libpq/Makefile", 'invokes exit\'; exit 1;', 'invokes exit\';');
|
||||
// disable shared libs build
|
||||
FileSystem::replaceFileStr(
|
||||
"{$this->source_dir}/src/Makefile.shlib",
|
||||
[
|
||||
'$(LINK.shared) -o $@ $(OBJS) $(LDFLAGS) $(LDFLAGS_SL) $(SHLIB_LINK)',
|
||||
'$(INSTALL_SHLIB) $< \'$(DESTDIR)$(pkglibdir)/$(shlib)\'',
|
||||
'$(INSTALL_SHLIB) $< \'$(DESTDIR)$(libdir)/$(shlib)\'',
|
||||
'$(INSTALL_SHLIB) $< \'$(DESTDIR)$(bindir)/$(shlib)\'',
|
||||
],
|
||||
''
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function build(): void
|
||||
{
|
||||
$builddir = BUILD_ROOT_PATH;
|
||||
$envs = '';
|
||||
$packages = 'zlib openssl readline libxml-2.0';
|
||||
$optional_packages = [
|
||||
'zstd' => 'libzstd',
|
||||
'ldap' => 'ldap',
|
||||
'libxslt' => 'libxslt',
|
||||
'icu' => 'icu-i18n',
|
||||
$libs = array_map(fn ($x) => $x->getName(), $this->getDependencies(true));
|
||||
$spc = new SPCConfigUtil($this->builder, ['no_php' => true, 'libs_only_deps' => true]);
|
||||
$config = $spc->config(libraries: $libs, include_suggest_lib: $this->builder->getOption('with-suggested-libs', false));
|
||||
|
||||
$env_vars = [
|
||||
'CFLAGS' => $config['cflags'],
|
||||
'CPPFLAGS' => '-DPIC',
|
||||
'LDFLAGS' => $config['ldflags'],
|
||||
'LIBS' => $config['libs'],
|
||||
];
|
||||
$error_exec_cnt = 0;
|
||||
|
||||
foreach ($optional_packages as $lib => $pkg) {
|
||||
if ($this->getBuilder()->getLib($lib)) {
|
||||
$packages .= ' ' . $pkg;
|
||||
$output = shell()->execWithResult("pkg-config --static {$pkg}");
|
||||
$error_exec_cnt += $output[0] === 0 ? 0 : 1;
|
||||
logger()->info(var_export($output[1], true));
|
||||
}
|
||||
}
|
||||
|
||||
$output = shell()->execWithResult("pkg-config --cflags-only-I --static {$packages}");
|
||||
$error_exec_cnt += $output[0] === 0 ? 0 : 1;
|
||||
$macos_15_bug_cflags = PHP_OS_FAMILY === 'Darwin' ? ' -Wno-unguarded-availability-new' : '';
|
||||
$cflags = '';
|
||||
if (!empty($output[1][0])) {
|
||||
$cflags = $output[1][0];
|
||||
$envs .= ' CPPFLAGS="-DPIC"';
|
||||
$cflags = "{$cflags} -fno-ident{$macos_15_bug_cflags}";
|
||||
}
|
||||
$output = shell()->execWithResult("pkg-config --libs-only-L --static {$packages}");
|
||||
$error_exec_cnt += $output[0] === 0 ? 0 : 1;
|
||||
if (!empty($output[1][0])) {
|
||||
$ldflags = $output[1][0];
|
||||
$envs .= SPCTarget::isStatic() ? " LDFLAGS=\"{$ldflags} -static\" " : " LDFLAGS=\"{$ldflags}\" ";
|
||||
}
|
||||
$output = shell()->execWithResult("pkg-config --libs-only-l --static {$packages}");
|
||||
$error_exec_cnt += $output[0] === 0 ? 0 : 1;
|
||||
if (!empty($output[1][0])) {
|
||||
$libs = $output[1][0];
|
||||
$libcpp = '';
|
||||
if ($this->builder->getLib('icu')) {
|
||||
$libcpp = $this instanceof LinuxLibraryBase ? ' -lstdc++' : ' -lc++';
|
||||
}
|
||||
$envs .= " LIBS=\"{$libs}{$libcpp}\" ";
|
||||
}
|
||||
if ($error_exec_cnt > 0) {
|
||||
throw new BuildFailureException('Failed to get pkg-config information!');
|
||||
if ($ldLibraryPath = getenv('SPC_LD_LIBRARY_PATH')) {
|
||||
$env_vars['LD_LIBRARY_PATH'] = $ldLibraryPath;
|
||||
}
|
||||
|
||||
FileSystem::resetDir($this->source_dir . '/build');
|
||||
|
||||
$version = $this->getVersion();
|
||||
// 16.1 workaround
|
||||
if (version_compare($version, '16.1') >= 0) {
|
||||
# 有静态链接配置 参考文件: src/interfaces/libpq/Makefile
|
||||
shell()->cd($this->source_dir . '/build')
|
||||
->exec('sed -i.backup "s/invokes exit\'; exit 1;/invokes exit\';/" ../src/interfaces/libpq/Makefile')
|
||||
->exec('sed -i.backup "278 s/^/# /" ../src/Makefile.shlib')
|
||||
->exec('sed -i.backup "402 s/^/# /" ../src/Makefile.shlib');
|
||||
} else {
|
||||
throw new BuildFailureException('Unsupported version for postgresql: ' . $version . ' !');
|
||||
}
|
||||
// php source relies on the non-private encoding functions in libpgcommon.a
|
||||
FileSystem::replaceFileStr(
|
||||
"{$this->source_dir}/src/common/Makefile",
|
||||
'$(OBJS_FRONTEND): CPPFLAGS += -DUSE_PRIVATE_ENCODING_FUNCS',
|
||||
'$(OBJS_FRONTEND): CPPFLAGS += -UUSE_PRIVATE_ENCODING_FUNCS -DFRONTEND',
|
||||
);
|
||||
|
||||
// configure
|
||||
shell()->cd($this->source_dir . '/build')->initializeEnv($this)
|
||||
->appendEnv(['CFLAGS' => $cflags])
|
||||
$shell = shell()->cd("{$this->source_dir}/build")->initializeEnv($this)
|
||||
->appendEnv($env_vars)
|
||||
->exec(
|
||||
"{$envs} ../configure " .
|
||||
"--prefix={$builddir} " .
|
||||
($this->builder->getOption('enable-zts') ? '--enable-thread-safety ' : '--disable-thread-safety ') .
|
||||
'../configure ' .
|
||||
"--prefix={$this->getBuildRootPath()} " .
|
||||
'--enable-coverage=no ' .
|
||||
'--with-ssl=openssl ' .
|
||||
'--with-readline ' .
|
||||
'--with-libxml ' .
|
||||
($this->builder->getLib('icu') ? '--with-icu ' : '--without-icu ') .
|
||||
($this->builder->getLib('ldap') ? '--with-ldap ' : '--without-ldap ') .
|
||||
// '--without-ldap ' .
|
||||
($this->builder->getLib('libxslt') ? '--with-libxslt ' : '--without-libxslt ') .
|
||||
($this->builder->getLib('zstd') ? '--with-zstd ' : '--without-zstd ') .
|
||||
'--without-lz4 ' .
|
||||
@@ -99,32 +89,29 @@ trait postgresql
|
||||
'--without-pam ' .
|
||||
'--without-bonjour ' .
|
||||
'--without-tcl '
|
||||
)
|
||||
->exec($envs . ' make -C src/bin/pg_config install')
|
||||
->exec($envs . ' make -C src/include install')
|
||||
->exec($envs . ' make -C src/common install')
|
||||
->exec($envs . ' make -C src/port install')
|
||||
->exec($envs . ' make -C src/interfaces/libpq install');
|
||||
);
|
||||
|
||||
// patch ldap lib
|
||||
if ($this->builder->getLib('ldap')) {
|
||||
$libs = PkgConfigUtil::getLibsArray('ldap');
|
||||
$libs = clean_spaces(implode(' ', $libs));
|
||||
FileSystem::replaceFileStr($this->source_dir . '/build/config.status', '-lldap', $libs);
|
||||
FileSystem::replaceFileStr($this->source_dir . '/build/src/Makefile.global', '-lldap', $libs);
|
||||
}
|
||||
|
||||
$shell
|
||||
->exec('make -C src/bin/pg_config install')
|
||||
->exec('make -C src/include install')
|
||||
->exec('make -C src/common install')
|
||||
->exec('make -C src/port install')
|
||||
->exec('make -C src/interfaces/libpq install');
|
||||
|
||||
// remove dynamic libs
|
||||
shell()->cd($this->source_dir . '/build')
|
||||
->exec("rm -rf {$builddir}/lib/*.so.*")
|
||||
->exec("rm -rf {$builddir}/lib/*.so")
|
||||
->exec("rm -rf {$builddir}/lib/*.dylib");
|
||||
->exec("rm -rf {$this->getBuildRootPath()}/lib/*.so.*")
|
||||
->exec("rm -rf {$this->getBuildRootPath()}/lib/*.so")
|
||||
->exec("rm -rf {$this->getBuildRootPath()}/lib/*.dylib");
|
||||
|
||||
FileSystem::replaceFileStr(BUILD_LIB_PATH . '/pkgconfig/libpq.pc', '-lldap', '-lldap -llber');
|
||||
}
|
||||
|
||||
private function getVersion(): string
|
||||
{
|
||||
try {
|
||||
$file = FileSystem::readFile($this->source_dir . '/meson.build');
|
||||
if (preg_match("/^\\s+version:\\s?'(.*)'/m", $file, $match)) {
|
||||
return $match[1];
|
||||
}
|
||||
return 'unknown';
|
||||
} catch (FileSystemException) {
|
||||
return 'unknown';
|
||||
}
|
||||
FileSystem::replaceFileStr("{$this->getLibDir()}/pkgconfig/libpq.pc", '-lldap', '-lldap -llber');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,18 +4,29 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\unix\library;
|
||||
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\util\executor\UnixAutoconfExecutor;
|
||||
|
||||
trait unixodbc
|
||||
{
|
||||
protected function build(): void
|
||||
{
|
||||
$sysconf_selector = match (PHP_OS_FAMILY) {
|
||||
'Darwin' => match (GNU_ARCH) {
|
||||
'x86_64' => '/usr/local/etc',
|
||||
'aarch64' => '/opt/homebrew/etc',
|
||||
default => throw new WrongUsageException('Unsupported architecture: ' . GNU_ARCH),
|
||||
},
|
||||
'Linux' => '/etc',
|
||||
default => throw new WrongUsageException('Unsupported OS: ' . PHP_OS_FAMILY),
|
||||
};
|
||||
UnixAutoconfExecutor::create($this)
|
||||
->configure(
|
||||
'--disable-debug',
|
||||
'--disable-dependency-tracking',
|
||||
"--with-libiconv-prefix={$this->getBuildRootPath()}",
|
||||
'--with-included-ltdl',
|
||||
"--sysconfdir={$sysconf_selector}",
|
||||
'--enable-gui=no',
|
||||
)
|
||||
->make();
|
||||
|
||||
@@ -57,6 +57,7 @@ class WindowsBuilder extends BuilderBase
|
||||
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
|
||||
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
|
||||
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
|
||||
$enableCgi = ($build_target & BUILD_TARGET_CGI) === BUILD_TARGET_CGI;
|
||||
|
||||
SourcePatcher::patchBeforeBuildconf($this);
|
||||
|
||||
@@ -66,21 +67,6 @@ class WindowsBuilder extends BuilderBase
|
||||
|
||||
$zts = $this->zts ? '--enable-zts=yes ' : '--enable-zts=no ';
|
||||
|
||||
// with-upx-pack for phpmicro
|
||||
if ($enableMicro && version_compare($this->getMicroVersion(), '0.2.0') < 0) {
|
||||
$makefile = FileSystem::convertPath(SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag.w32');
|
||||
if ($this->getOption('with-upx-pack', false)) {
|
||||
if (!file_exists($makefile . '.originfile')) {
|
||||
copy($makefile, $makefile . '.originfile');
|
||||
FileSystem::replaceFileStr($makefile, '$(MICRO_SFX):', '_MICRO_UPX = ' . getenv('UPX_EXEC') . " --best $(MICRO_SFX)\n$(MICRO_SFX):");
|
||||
FileSystem::replaceFileStr($makefile, '@$(_MICRO_MT)', "@$(_MICRO_MT)\n\t@$(_MICRO_UPX)");
|
||||
}
|
||||
} elseif (file_exists($makefile . '.originfile')) {
|
||||
copy($makefile . '.originfile', $makefile);
|
||||
unlink($makefile . '.originfile');
|
||||
}
|
||||
}
|
||||
|
||||
$opcache_jit = !$this->getOption('disable-opcache-jit', false);
|
||||
$opcache_jit_arg = $opcache_jit ? '--enable-opcache-jit=yes ' : '--enable-opcache-jit=no ';
|
||||
|
||||
@@ -102,13 +88,13 @@ class WindowsBuilder extends BuilderBase
|
||||
->exec(
|
||||
"{$this->sdk_prefix} configure.bat --task-args \"" .
|
||||
'--disable-all ' .
|
||||
'--disable-cgi ' .
|
||||
'--with-php-build=' . BUILD_ROOT_PATH . ' ' .
|
||||
'--with-extra-includes=' . BUILD_INCLUDE_PATH . ' ' .
|
||||
'--with-extra-libs=' . BUILD_LIB_PATH . ' ' .
|
||||
($enableCli ? '--enable-cli=yes ' : '--enable-cli=no ') .
|
||||
($enableMicro ? ('--enable-micro=yes ' . $micro_logo . $micro_w32) : '--enable-micro=no ') .
|
||||
($enableEmbed ? '--enable-embed=yes ' : '--enable-embed=no ') .
|
||||
($enableCli ? '--enable-cli ' : '--disable-cli ') .
|
||||
($enableMicro ? ('--enable-micro ' . $micro_logo . $micro_w32) : '--disable-micro ') .
|
||||
($enableEmbed ? '--enable-embed ' : '--disable-embed ') .
|
||||
($enableCgi ? '--enable-cgi ' : '--disable-cgi ') .
|
||||
$config_file_scan_dir .
|
||||
$opcache_jit_arg .
|
||||
"{$this->makeStaticExtensionArgs()} " .
|
||||
@@ -127,6 +113,10 @@ class WindowsBuilder extends BuilderBase
|
||||
if ($enableFpm) {
|
||||
logger()->warning('Windows does not support fpm SAPI, I will skip it.');
|
||||
}
|
||||
if ($enableCgi) {
|
||||
logger()->info('building cgi');
|
||||
$this->buildCgi();
|
||||
}
|
||||
if ($enableMicro) {
|
||||
logger()->info('building micro');
|
||||
$this->buildMicro();
|
||||
@@ -140,7 +130,7 @@ class WindowsBuilder extends BuilderBase
|
||||
}
|
||||
}
|
||||
|
||||
public function testPHP(int $build_target = BUILD_TARGET_NONE)
|
||||
public function testPHP(int $build_target = BUILD_TARGET_NONE): void
|
||||
{
|
||||
$this->sanityCheck($build_target);
|
||||
}
|
||||
@@ -151,12 +141,52 @@ class WindowsBuilder extends BuilderBase
|
||||
|
||||
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
|
||||
|
||||
// Add debug symbols for release build if --no-strip is specified
|
||||
// We need to modify CFLAGS to replace /Ox with /Zi and add /DEBUG to LDFLAGS
|
||||
$debug_overrides = '';
|
||||
if ($this->getOption('no-strip', false)) {
|
||||
// Read current CFLAGS from Makefile and replace optimization flags
|
||||
$makefile_content = file_get_contents(SOURCE_PATH . '\php-src\Makefile');
|
||||
if (preg_match('/^CFLAGS=(.+?)$/m', $makefile_content, $matches)) {
|
||||
$cflags = $matches[1];
|
||||
// Replace /Ox (full optimization) with /Zi (debug info) and /Od (disable optimization)
|
||||
// Keep optimization for speed: /O2 /Zi instead of /Od /Zi
|
||||
$cflags = str_replace('/Ox ', '/O2 /Zi ', $cflags);
|
||||
$debug_overrides = '"CFLAGS=' . $cflags . '" "LDFLAGS=/DEBUG /LTCG /INCREMENTAL:NO" "LDFLAGS_CLI=/DEBUG" ';
|
||||
}
|
||||
}
|
||||
|
||||
// add nmake wrapper
|
||||
FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_cli_wrapper.bat', "nmake /nologo LIBS_CLI=\"ws2_32.lib shell32.lib {$extra_libs}\" EXTRA_LD_FLAGS_PROGRAM= %*");
|
||||
FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_cli_wrapper.bat', "nmake /nologo {$debug_overrides}LIBS_CLI=\"ws2_32.lib shell32.lib {$extra_libs}\" EXTRA_LD_FLAGS_PROGRAM= %*");
|
||||
|
||||
cmd()->cd(SOURCE_PATH . '\php-src')->exec("{$this->sdk_prefix} nmake_cli_wrapper.bat --task-args php.exe");
|
||||
|
||||
$this->deployBinary(BUILD_TARGET_CLI);
|
||||
$this->deploySAPIBinary(BUILD_TARGET_CLI);
|
||||
}
|
||||
|
||||
public function buildCgi(): void
|
||||
{
|
||||
SourcePatcher::patchWindowsCGITarget();
|
||||
|
||||
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
|
||||
|
||||
// Add debug symbols for release build if --no-strip is specified
|
||||
$debug_overrides = '';
|
||||
if ($this->getOption('no-strip', false)) {
|
||||
$makefile_content = file_get_contents(SOURCE_PATH . '\php-src\Makefile');
|
||||
if (preg_match('/^CFLAGS=(.+?)$/m', $makefile_content, $matches)) {
|
||||
$cflags = $matches[1];
|
||||
$cflags = str_replace('/Ox ', '/O2 /Zi ', $cflags);
|
||||
$debug_overrides = '"CFLAGS=' . $cflags . '" "LDFLAGS=/DEBUG /LTCG /INCREMENTAL:NO" "LDFLAGS_CGI=/DEBUG" ';
|
||||
}
|
||||
}
|
||||
|
||||
// add nmake wrapper
|
||||
FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_cgi_wrapper.bat', "nmake /nologo {$debug_overrides}LIBS_CGI=\"ws2_32.lib kernel32.lib advapi32.lib {$extra_libs}\" EXTRA_LD_FLAGS_PROGRAM= %*");
|
||||
|
||||
cmd()->cd(SOURCE_PATH . '\php-src')->exec("{$this->sdk_prefix} nmake_cgi_wrapper.bat --task-args php-cgi.exe");
|
||||
|
||||
$this->deploySAPIBinary(BUILD_TARGET_CGI);
|
||||
}
|
||||
|
||||
public function buildEmbed(): void
|
||||
@@ -183,9 +213,20 @@ class WindowsBuilder extends BuilderBase
|
||||
|
||||
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
|
||||
|
||||
// Add debug symbols for release build if --no-strip is specified
|
||||
$debug_overrides = '';
|
||||
if ($this->getOption('no-strip', false) && !$this->getOption('debug', false)) {
|
||||
$makefile_content = file_get_contents(SOURCE_PATH . '\php-src\Makefile');
|
||||
if (preg_match('/^CFLAGS=(.+?)$/m', $makefile_content, $matches)) {
|
||||
$cflags = $matches[1];
|
||||
$cflags = str_replace('/Ox ', '/O2 /Zi ', $cflags);
|
||||
$debug_overrides = '"CFLAGS=' . $cflags . '" "LDFLAGS=/DEBUG /LTCG /INCREMENTAL:NO" "LDFLAGS_MICRO=/DEBUG" ';
|
||||
}
|
||||
}
|
||||
|
||||
// add nmake wrapper
|
||||
$fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' /DPHP_MICRO_FAKE_CLI" ' : '';
|
||||
$wrapper = "nmake /nologo LIBS_MICRO=\"ws2_32.lib shell32.lib {$extra_libs}\" CFLAGS_MICRO=\"/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1{$fake_cli}\" %*";
|
||||
$wrapper = "nmake /nologo {$debug_overrides}LIBS_MICRO=\"ws2_32.lib shell32.lib {$extra_libs}\" CFLAGS_MICRO=\"/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1{$fake_cli}\" %*";
|
||||
FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_micro_wrapper.bat', $wrapper);
|
||||
|
||||
// phar patch for micro
|
||||
@@ -202,7 +243,7 @@ class WindowsBuilder extends BuilderBase
|
||||
}
|
||||
}
|
||||
|
||||
$this->deployBinary(BUILD_TARGET_MICRO);
|
||||
$this->deploySAPIBinary(BUILD_TARGET_MICRO);
|
||||
}
|
||||
|
||||
public function proveLibs(array $sorted_libraries): void
|
||||
@@ -265,7 +306,7 @@ class WindowsBuilder extends BuilderBase
|
||||
// sanity check for php-cli
|
||||
if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
|
||||
logger()->info('running cli sanity check');
|
||||
[$ret, $output] = cmd()->execWithResult(BUILD_ROOT_PATH . '\bin\php.exe -n -r "echo \"hello\";"');
|
||||
[$ret, $output] = cmd()->execWithResult(BUILD_BIN_PATH . '\php.exe -n -r "echo \"hello\";"');
|
||||
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
|
||||
throw new ValidationException('cli failed sanity check', validation_module: 'php-cli function check');
|
||||
}
|
||||
@@ -284,7 +325,7 @@ class WindowsBuilder extends BuilderBase
|
||||
if (file_exists($test_file)) {
|
||||
@unlink($test_file);
|
||||
}
|
||||
file_put_contents($test_file, file_get_contents(BUILD_ROOT_PATH . '\bin\micro.sfx') . $task['content']);
|
||||
file_put_contents($test_file, file_get_contents(BUILD_BIN_PATH . '\micro.sfx') . $task['content']);
|
||||
chmod($test_file, 0755);
|
||||
[$ret, $out] = cmd()->execWithResult($test_file);
|
||||
foreach ($task['conditions'] as $condition => $closure) {
|
||||
@@ -298,6 +339,17 @@ class WindowsBuilder extends BuilderBase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check for php-cgi
|
||||
if (($build_target & BUILD_TARGET_CGI) === BUILD_TARGET_CGI) {
|
||||
logger()->info('running cgi sanity check');
|
||||
FileSystem::writeFile(SOURCE_PATH . '\php-cgi-test.php', '<?php echo "<h1>Hello, World!</h1>"; ?>');
|
||||
[$ret, $output] = cmd()->execWithResult(BUILD_BIN_PATH . '\php-cgi.exe -n -f ' . SOURCE_PATH . '\php-cgi-test.php');
|
||||
$raw_output = implode("\n", $output);
|
||||
if ($ret !== 0 || !str_contains($raw_output, 'Hello, World!')) {
|
||||
throw new ValidationException("cgi failed sanity check. code: {$ret}, output: {$raw_output}", validation_module: 'php-cgi sanity check');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,27 +357,53 @@ class WindowsBuilder extends BuilderBase
|
||||
*
|
||||
* @param int $type Deploy type
|
||||
*/
|
||||
public function deployBinary(int $type): bool
|
||||
public function deploySAPIBinary(int $type): void
|
||||
{
|
||||
logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file');
|
||||
|
||||
$debug_dir = BUILD_ROOT_PATH . '\debug';
|
||||
$bin_path = BUILD_BIN_PATH;
|
||||
|
||||
// create dirs
|
||||
FileSystem::createDir($debug_dir);
|
||||
FileSystem::createDir($bin_path);
|
||||
|
||||
// determine source path for different SAPI
|
||||
$rel_type = 'Release'; // TODO: Debug build support
|
||||
$ts = $this->zts ? '_TS' : '';
|
||||
$src = match ($type) {
|
||||
BUILD_TARGET_CLI => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\php.exe",
|
||||
BUILD_TARGET_MICRO => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\micro.sfx",
|
||||
BUILD_TARGET_CLI => [SOURCE_PATH . "\\php-src\\x64\\{$rel_type}{$ts}", 'php.exe', 'php.pdb'],
|
||||
BUILD_TARGET_MICRO => [SOURCE_PATH . "\\php-src\\x64\\{$rel_type}{$ts}", 'micro.sfx', 'micro.pdb'],
|
||||
BUILD_TARGET_CGI => [SOURCE_PATH . "\\php-src\\x64\\{$rel_type}{$ts}", 'php-cgi.exe', 'php-cgi.pdb'],
|
||||
default => throw new SPCInternalException("Deployment does not accept type {$type}"),
|
||||
};
|
||||
|
||||
// with-upx-pack for cli and micro
|
||||
if ($this->getOption('with-upx-pack', false)) {
|
||||
if ($type === BUILD_TARGET_CLI || ($type === BUILD_TARGET_MICRO && version_compare($this->getMicroVersion(), '0.2.0') >= 0)) {
|
||||
cmd()->exec(getenv('UPX_EXEC') . ' --best ' . escapeshellarg($src));
|
||||
}
|
||||
$src = "{$src[0]}\\{$src[1]}";
|
||||
$dst = BUILD_BIN_PATH . '\\' . basename($src);
|
||||
|
||||
// file must exists
|
||||
if (!file_exists($src)) {
|
||||
throw new SPCInternalException("Deploy failed. Cannot find file: {$src}");
|
||||
}
|
||||
// dst dir must exists
|
||||
FileSystem::createDir(dirname($dst));
|
||||
|
||||
// copy file
|
||||
if (realpath($src) !== realpath($dst)) {
|
||||
cmd()->exec('copy ' . escapeshellarg($src) . ' ' . escapeshellarg($dst));
|
||||
}
|
||||
|
||||
logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file');
|
||||
FileSystem::createDir(BUILD_ROOT_PATH . '\bin');
|
||||
// extract debug info in buildroot/debug
|
||||
if ($this->getOption('no-strip', false) && file_exists("{$src[0]}\\{$src[2]}")) {
|
||||
cmd()->exec('copy ' . escapeshellarg("{$src[0]}\\{$src[2]}") . ' ' . escapeshellarg($debug_dir));
|
||||
}
|
||||
|
||||
cmd()->exec('copy ' . escapeshellarg($src) . ' ' . escapeshellarg(BUILD_ROOT_PATH . '\bin\\'));
|
||||
return true;
|
||||
// with-upx-pack for cli and micro
|
||||
if ($this->getOption('with-upx-pack', false)) {
|
||||
if ($type === BUILD_TARGET_CLI || $type === BUILD_TARGET_CGI || ($type === BUILD_TARGET_MICRO && version_compare($this->getMicroVersion(), '0.2.0') >= 0)) {
|
||||
cmd()->exec(getenv('UPX_EXEC') . ' --best ' . escapeshellarg($dst));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,7 @@ class BuildPHPCommand extends BuildCommand
|
||||
$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-frankenphp', null, null, 'Build FrankenPHP SAPI (not available on Windows)');
|
||||
$this->addOption('build-cgi', null, null, 'Build cgi SAPI');
|
||||
$this->addOption('build-all', null, null, 'Build all SAPI');
|
||||
$this->addOption('no-strip', null, null, 'build without strip, keep symbols to debug');
|
||||
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');
|
||||
@@ -47,6 +48,7 @@ class BuildPHPCommand extends BuildCommand
|
||||
$this->addOption('with-upx-pack', null, null, 'Compress / pack binary using UPX tool (linux/windows only)');
|
||||
$this->addOption('with-micro-logo', null, InputOption::VALUE_REQUIRED, 'Use custom .ico for micro.sfx (windows only)');
|
||||
$this->addOption('enable-micro-win32', null, null, 'Enable win32 mode for phpmicro (Windows only)');
|
||||
$this->addOption('with-frankenphp-app', null, InputOption::VALUE_REQUIRED, 'Path to a folder to be embedded in FrankenPHP');
|
||||
}
|
||||
|
||||
public function handle(): int
|
||||
@@ -221,11 +223,9 @@ class BuildPHPCommand extends BuildCommand
|
||||
|
||||
// ---------- When using bin/spc-alpine-docker, the build root path is different from the host system ----------
|
||||
$build_root_path = BUILD_ROOT_PATH;
|
||||
$cwd = getcwd();
|
||||
$fixed = '';
|
||||
$build_root_path = get_display_path($build_root_path);
|
||||
if (!empty(getenv('SPC_FIX_DEPLOY_ROOT'))) {
|
||||
str_replace($cwd, '', $build_root_path);
|
||||
$build_root_path = getenv('SPC_FIX_DEPLOY_ROOT') . '/' . basename($build_root_path);
|
||||
$fixed = ' (host system)';
|
||||
}
|
||||
if (($rule & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
|
||||
@@ -274,6 +274,7 @@ class BuildPHPCommand extends BuildCommand
|
||||
$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-frankenphp') ? (BUILD_TARGET_FRANKENPHP | BUILD_TARGET_EMBED) : BUILD_TARGET_NONE);
|
||||
$rule |= ($this->getOption('build-cgi') ? BUILD_TARGET_CGI : BUILD_TARGET_NONE);
|
||||
$rule |= ($this->getOption('build-all') ? BUILD_TARGET_ALL : BUILD_TARGET_NONE);
|
||||
return $rule;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ declare(strict_types=1);
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\exception\ValidationException;
|
||||
use SPC\store\pkg\GoXcaddy;
|
||||
use SPC\store\pkg\Zig;
|
||||
use SPC\toolchain\ToolchainManager;
|
||||
use SPC\toolchain\ZigToolchain;
|
||||
use SPC\util\ConfigValidator;
|
||||
@@ -63,7 +65,7 @@ class CraftCommand extends BuildCommand
|
||||
}
|
||||
}
|
||||
// install go and xcaddy for frankenphp
|
||||
if (in_array('frankenphp', $craft['sapi'])) {
|
||||
if (in_array('frankenphp', $craft['sapi']) && !GoXcaddy::isInstalled()) {
|
||||
$retcode = $this->runCommand('install-pkg', 'go-xcaddy');
|
||||
if ($retcode !== 0) {
|
||||
$this->output->writeln('<error>craft go-xcaddy failed</error>');
|
||||
@@ -71,7 +73,7 @@ class CraftCommand extends BuildCommand
|
||||
}
|
||||
}
|
||||
// install zig if requested
|
||||
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
|
||||
if (ToolchainManager::getToolchainClass() === ZigToolchain::class && !Zig::isInstalled()) {
|
||||
$retcode = $this->runCommand('install-pkg', 'zig');
|
||||
if ($retcode !== 0) {
|
||||
$this->output->writeln('<error>craft zig failed</error>');
|
||||
|
||||
@@ -106,7 +106,7 @@ class DownloadCommand extends BaseCommand
|
||||
}
|
||||
|
||||
// retry
|
||||
$retry = intval($this->getOption('retry'));
|
||||
$retry = (int) $this->getOption('retry');
|
||||
f_putenv('SPC_DOWNLOAD_RETRIES=' . $retry);
|
||||
|
||||
// Use shallow-clone can reduce git resource download
|
||||
@@ -265,7 +265,7 @@ class DownloadCommand extends BaseCommand
|
||||
f_passthru((PHP_OS_FAMILY === 'Windows' ? 'rmdir /s /q ' : 'rm -rf ') . DOWNLOAD_PATH);
|
||||
}
|
||||
// unzip command check
|
||||
if (PHP_OS_FAMILY !== 'Windows' && !$this->findCommand('unzip')) {
|
||||
if (PHP_OS_FAMILY !== 'Windows' && !self::findCommand('unzip')) {
|
||||
$this->output->writeln('Missing unzip command, you need to install it first !');
|
||||
$this->output->writeln('You can use "bin/spc doctor" command to check and install required tools');
|
||||
return static::FAILURE;
|
||||
|
||||
@@ -28,7 +28,7 @@ class LinuxToolCheckList
|
||||
public const TOOLS_DEBIAN = [
|
||||
'make', 'bison', 're2c', 'flex',
|
||||
'git', 'autoconf', 'automake', 'autopoint',
|
||||
'tar', 'unzip', 'gzip',
|
||||
'tar', 'unzip', 'gzip', 'gcc', 'g++',
|
||||
'bzip2', 'cmake', 'patch',
|
||||
'xz', 'libtoolize', 'which',
|
||||
'patchelf',
|
||||
@@ -37,10 +37,10 @@ class LinuxToolCheckList
|
||||
public const TOOLS_RHEL = [
|
||||
'perl', 'make', 'bison', 're2c', 'flex',
|
||||
'git', 'autoconf', 'automake',
|
||||
'tar', 'unzip', 'gzip', 'gcc',
|
||||
'tar', 'unzip', 'gzip', 'gcc', 'g++',
|
||||
'bzip2', 'cmake', 'patch', 'which',
|
||||
'xz', 'libtool', 'gettext-devel',
|
||||
'patchelf',
|
||||
'patchelf', 'file',
|
||||
];
|
||||
|
||||
public const TOOLS_ARCH = [
|
||||
@@ -53,7 +53,8 @@ class LinuxToolCheckList
|
||||
'base-devel' => 'automake',
|
||||
'gettext-devel' => 'gettextize',
|
||||
'gettext-dev' => 'gettextize',
|
||||
'perl-IPC-Cmd' => '/usr/share/doc/perl-IPC-Cmd',
|
||||
'perl-IPC-Cmd' => '/usr/share/perl5/vendor_perl/IPC/Cmd.pm',
|
||||
'perl-Time-Piece' => '/usr/lib64/perl5/Time/Piece.pm',
|
||||
];
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
@@ -65,7 +66,7 @@ class LinuxToolCheckList
|
||||
$required = match ($distro['dist']) {
|
||||
'alpine' => self::TOOLS_ALPINE,
|
||||
'redhat' => self::TOOLS_RHEL,
|
||||
'centos' => array_merge(self::TOOLS_RHEL, ['perl-IPC-Cmd']),
|
||||
'centos' => array_merge(self::TOOLS_RHEL, ['perl-IPC-Cmd', 'perl-Time-Piece']),
|
||||
'arch' => self::TOOLS_ARCH,
|
||||
default => self::TOOLS_DEBIAN,
|
||||
};
|
||||
@@ -81,14 +82,14 @@ class LinuxToolCheckList
|
||||
return CheckResult::ok();
|
||||
}
|
||||
|
||||
#[AsCheckItem('if cmake version >= 3.18', limit_os: 'Linux')]
|
||||
#[AsCheckItem('if cmake version >= 3.22', limit_os: 'Linux')]
|
||||
public function checkCMakeVersion(): ?CheckResult
|
||||
{
|
||||
$ver = get_cmake_version();
|
||||
if ($ver === null) {
|
||||
return CheckResult::fail('Failed to get cmake version');
|
||||
}
|
||||
if (version_compare($ver, '3.18.0') < 0) {
|
||||
if (version_compare($ver, '3.22.0') < 0) {
|
||||
return CheckResult::fail('cmake version is too low (' . $ver . '), please update it manually!');
|
||||
}
|
||||
return CheckResult::ok($ver);
|
||||
|
||||
@@ -29,6 +29,18 @@ class PkgConfigCheck
|
||||
return CheckResult::ok($pkgconf);
|
||||
}
|
||||
|
||||
#[AsCheckItem('if pkg-config is working', level: 799)]
|
||||
public function checkPkgConfigFunctional(): CheckResult
|
||||
{
|
||||
$pkgconf = PkgConfigUtil::findPkgConfig();
|
||||
// check if pkg-config is functional
|
||||
[$ret, $output] = shell()->execWithResult("{$pkgconf} --version", false);
|
||||
if ($ret === 0) {
|
||||
return CheckResult::ok(implode(' ', $output));
|
||||
}
|
||||
return CheckResult::fail('pkg-config is not functional', 'install-pkgconfig');
|
||||
}
|
||||
|
||||
#[AsFixItem('install-pkgconfig')]
|
||||
public function installPkgConfig(): bool
|
||||
{
|
||||
|
||||
@@ -137,13 +137,18 @@ class ExceptionHandler
|
||||
|
||||
self::logError("\n----------------------------------------\n");
|
||||
|
||||
self::logError('⚠ The ' . ConsoleColor::cyan('console output log') . ConsoleColor::red(' is saved in ') . ConsoleColor::none(SPC_OUTPUT_LOG));
|
||||
// convert log file path if in docker
|
||||
$spc_log_convert = get_display_path(SPC_OUTPUT_LOG);
|
||||
$shell_log_convert = get_display_path(SPC_SHELL_LOG);
|
||||
$spc_logs_dir_convert = get_display_path(SPC_LOGS_DIR);
|
||||
|
||||
self::logError('⚠ The ' . ConsoleColor::cyan('console output log') . ConsoleColor::red(' is saved in ') . ConsoleColor::none($spc_log_convert));
|
||||
if (file_exists(SPC_SHELL_LOG)) {
|
||||
self::logError('⚠ The ' . ConsoleColor::cyan('shell output log') . ConsoleColor::red(' is saved in ') . ConsoleColor::none(SPC_SHELL_LOG));
|
||||
self::logError('⚠ The ' . ConsoleColor::cyan('shell output log') . ConsoleColor::red(' is saved in ') . ConsoleColor::none($shell_log_convert));
|
||||
}
|
||||
if ($e->getExtraLogFiles() !== []) {
|
||||
foreach ($e->getExtraLogFiles() as $key => $file) {
|
||||
self::logError("⚠ Log file [{$key}] is saved in: " . ConsoleColor::none(SPC_LOGS_DIR . "/{$file}"));
|
||||
self::logError("⚠ Log file [{$key}] is saved in: " . ConsoleColor::none("{$spc_logs_dir_convert}/{$file}"));
|
||||
}
|
||||
}
|
||||
if (!defined('DEBUG_MODE')) {
|
||||
@@ -154,7 +159,9 @@ class ExceptionHandler
|
||||
public static function handleDefaultException(\Throwable $e): void
|
||||
{
|
||||
$class = get_class($e);
|
||||
self::logError("✗ Unhandled exception {$class}:\n\t{$e->getMessage()}\n");
|
||||
$file = $e->getFile();
|
||||
$line = $e->getLine();
|
||||
self::logError("✗ Unhandled exception {$class} on {$file} line {$line}:\n\t{$e->getMessage()}\n");
|
||||
self::logError('Stack trace:');
|
||||
self::logError(ConsoleColor::gray($e->getTraceAsString()) . PHP_EOL, 4);
|
||||
self::logError('⚠ Please report this exception to: https://github.com/crazywhalecc/static-php-cli/issues');
|
||||
|
||||
@@ -14,9 +14,9 @@ use SPC\builder\Extension;
|
||||
*/
|
||||
class ValidationException extends SPCException
|
||||
{
|
||||
private null|array|string $validation_module = null;
|
||||
private array|string|null $validation_module = null;
|
||||
|
||||
public function __construct(string $message = '', int $code = 0, ?\Throwable $previous = null, null|array|string $validation_module = null)
|
||||
public function __construct(string $message = '', int $code = 0, ?\Throwable $previous = null, array|string|null $validation_module = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
|
||||
|
||||
95
src/SPC/store/DirDiff.php
Normal file
95
src/SPC/store/DirDiff.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\store;
|
||||
|
||||
/**
|
||||
* A util class to diff directory file increments.
|
||||
*/
|
||||
class DirDiff
|
||||
{
|
||||
protected array $before = [];
|
||||
|
||||
protected array $before_file_hashes = [];
|
||||
|
||||
public function __construct(protected string $dir, protected bool $track_content_changes = false)
|
||||
{
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the baseline to current state.
|
||||
*/
|
||||
public function reset(): void
|
||||
{
|
||||
$this->before = FileSystem::scanDirFiles($this->dir, relative: true) ?: [];
|
||||
|
||||
if ($this->track_content_changes) {
|
||||
$this->before_file_hashes = [];
|
||||
foreach ($this->before as $file) {
|
||||
$this->before_file_hashes[$file] = md5_file($this->dir . DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of incremented files.
|
||||
*
|
||||
* @param bool $relative Return relative paths or absolute paths
|
||||
* @return array<string> List of incremented files
|
||||
*/
|
||||
public function getIncrementFiles(bool $relative = false): array
|
||||
{
|
||||
$after = FileSystem::scanDirFiles($this->dir, relative: true) ?: [];
|
||||
$diff = array_diff($after, $this->before);
|
||||
if ($relative) {
|
||||
return $diff;
|
||||
}
|
||||
return array_map(fn ($f) => $this->dir . DIRECTORY_SEPARATOR . $f, $diff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of changed files (including new files).
|
||||
*
|
||||
* @param bool $relative Return relative paths or absolute paths
|
||||
* @param bool $include_new_files Include new files as changed files
|
||||
* @return array<string> List of changed files
|
||||
*/
|
||||
public function getChangedFiles(bool $relative = false, bool $include_new_files = true): array
|
||||
{
|
||||
$after = FileSystem::scanDirFiles($this->dir, relative: true) ?: [];
|
||||
$changed = [];
|
||||
foreach ($after as $file) {
|
||||
if (isset($this->before_file_hashes[$file])) {
|
||||
$after_hash = md5_file($this->dir . DIRECTORY_SEPARATOR . $file);
|
||||
if ($after_hash !== $this->before_file_hashes[$file]) {
|
||||
$changed[] = $file;
|
||||
}
|
||||
} elseif ($include_new_files) {
|
||||
// New file, consider as changed
|
||||
$changed[] = $file;
|
||||
}
|
||||
}
|
||||
if ($relative) {
|
||||
return $changed;
|
||||
}
|
||||
return array_map(fn ($f) => $this->dir . DIRECTORY_SEPARATOR . $f, $changed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of removed files.
|
||||
*
|
||||
* @param bool $relative Return relative paths or absolute paths
|
||||
* @return array<string> List of removed files
|
||||
*/
|
||||
public function getRemovedFiles(bool $relative = false): array
|
||||
{
|
||||
$after = FileSystem::scanDirFiles($this->dir, relative: true) ?: [];
|
||||
$removed = array_diff($this->before, $after);
|
||||
if ($relative) {
|
||||
return $removed;
|
||||
}
|
||||
return array_map(fn ($f) => $this->dir . DIRECTORY_SEPARATOR . $f, $removed);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,42 @@ use SPC\util\SPCTarget;
|
||||
*/
|
||||
class Downloader
|
||||
{
|
||||
/**
|
||||
* Get latest version from PIE config (Packagist)
|
||||
*
|
||||
* @param string $name Source name
|
||||
* @param array $source Source meta info: [repo]
|
||||
* @return array<int, string> [url, filename]
|
||||
*/
|
||||
public static function getPIEInfo(string $name, array $source): array
|
||||
{
|
||||
$packagist_url = "https://repo.packagist.org/p2/{$source['repo']}.json";
|
||||
logger()->debug("Fetching {$name} source from packagist index: {$packagist_url}");
|
||||
$data = json_decode(self::curlExec(
|
||||
url: $packagist_url,
|
||||
retries: self::getRetryAttempts()
|
||||
), true);
|
||||
if (!isset($data['packages'][$source['repo']]) || !is_array($data['packages'][$source['repo']])) {
|
||||
throw new DownloaderException("failed to find {$name} repo info from packagist");
|
||||
}
|
||||
// get the first version
|
||||
$first = $data['packages'][$source['repo']][0] ?? [];
|
||||
// check 'type' => 'php-ext' or contains 'php-ext' key
|
||||
if (!isset($first['php-ext'])) {
|
||||
throw new DownloaderException("failed to find {$name} php-ext info from packagist, maybe not a php extension package");
|
||||
}
|
||||
// get download link from dist
|
||||
$dist_url = $first['dist']['url'] ?? null;
|
||||
$dist_type = $first['dist']['type'] ?? null;
|
||||
if (!$dist_url || !$dist_type) {
|
||||
throw new DownloaderException("failed to find {$name} dist info from packagist");
|
||||
}
|
||||
$name = str_replace('/', '_', $source['repo']);
|
||||
$version = $first['version'] ?? 'unknown';
|
||||
// file name use: $name-$version.$dist_type
|
||||
return [$dist_url, "{$name}-{$version}.{$dist_type}"];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get latest version from BitBucket tag
|
||||
*
|
||||
@@ -65,19 +101,19 @@ class Downloader
|
||||
url: "https://api.github.com/repos/{$source['repo']}/{$type}",
|
||||
hooks: [[CurlHook::class, 'setupGithubToken']],
|
||||
retries: self::getRetryAttempts()
|
||||
), true);
|
||||
), true, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
$url = null;
|
||||
for ($i = 0; $i < count($data); ++$i) {
|
||||
if (($data[$i]['prerelease'] ?? false) === true && ($source['prefer-stable'] ?? false)) {
|
||||
foreach ($data as $rel) {
|
||||
if (($rel['prerelease'] ?? false) === true && ($source['prefer-stable'] ?? false)) {
|
||||
continue;
|
||||
}
|
||||
if (!($source['match'] ?? null)) {
|
||||
$url = $data[$i]['tarball_url'] ?? null;
|
||||
$url = $rel['tarball_url'] ?? null;
|
||||
break;
|
||||
}
|
||||
if (preg_match('|' . $source['match'] . '|', $data[$i]['tarball_url'])) {
|
||||
$url = $data[$i]['tarball_url'];
|
||||
if (preg_match('|' . $source['match'] . '|', $rel['tarball_url'])) {
|
||||
$url = $rel['tarball_url'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -232,7 +268,8 @@ class Downloader
|
||||
$quiet = !defined('DEBUG_MODE') ? '-q --quiet' : '';
|
||||
$git = SPC_GIT_EXEC;
|
||||
$shallow = defined('GIT_SHALLOW_CLONE') ? '--depth 1 --single-branch' : '';
|
||||
$recursive = ($submodules === null) ? '--recursive' : '';
|
||||
$recursive = ($submodules === null && defined('GIT_SHALLOW_CLONE')) ? '--recursive --shallow-submodules' : null;
|
||||
$recursive ??= $submodules === null ? '--recursive' : '';
|
||||
|
||||
try {
|
||||
self::registerCancelEvent(function () use ($download_path) {
|
||||
@@ -243,8 +280,9 @@ class Downloader
|
||||
});
|
||||
f_passthru("{$git} clone {$quiet} --config core.autocrlf=false --branch \"{$branch}\" {$shallow} {$recursive} \"{$url}\" \"{$download_path}\"");
|
||||
if ($submodules !== null) {
|
||||
$depth_flag = defined('GIT_SHALLOW_CLONE') ? '--depth 1' : '';
|
||||
foreach ($submodules as $submodule) {
|
||||
f_passthru("cd \"{$download_path}\" && {$git} submodule update --init " . escapeshellarg($submodule));
|
||||
f_passthru("cd \"{$download_path}\" && {$git} submodule update --init {$depth_flag} " . escapeshellarg($submodule));
|
||||
}
|
||||
}
|
||||
} catch (SPCException $e) {
|
||||
@@ -315,91 +353,14 @@ class Downloader
|
||||
if (self::isAlreadyDownloaded($name, $force, SPC_DOWNLOAD_PACKAGE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
switch ($pkg['type']) {
|
||||
case 'bitbuckettag': // BitBucket Tag
|
||||
[$url, $filename] = self::getLatestBitbucketTag($name, $pkg);
|
||||
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE);
|
||||
break;
|
||||
case 'ghtar': // GitHub Release (tar)
|
||||
[$url, $filename] = self::getLatestGithubTarball($name, $pkg);
|
||||
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE, hooks: [[CurlHook::class, 'setupGithubToken']]);
|
||||
break;
|
||||
case 'ghtagtar': // GitHub Tag (tar)
|
||||
[$url, $filename] = self::getLatestGithubTarball($name, $pkg, 'tags');
|
||||
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE, hooks: [[CurlHook::class, 'setupGithubToken']]);
|
||||
break;
|
||||
case 'ghrel': // GitHub Release (uploaded)
|
||||
[$url, $filename] = self::getLatestGithubRelease($name, $pkg);
|
||||
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE, ['Accept: application/octet-stream'], [[CurlHook::class, 'setupGithubToken']]);
|
||||
break;
|
||||
case 'filelist': // Basic File List (regex based crawler)
|
||||
[$url, $filename] = self::getFromFileList($name, $pkg);
|
||||
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE);
|
||||
break;
|
||||
case 'url': // Direct download URL
|
||||
$url = $pkg['url'];
|
||||
$filename = $pkg['filename'] ?? basename($pkg['url']);
|
||||
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE);
|
||||
break;
|
||||
case 'git': // Git repo
|
||||
self::downloadGit(
|
||||
$name,
|
||||
$pkg['url'],
|
||||
$pkg['rev'],
|
||||
$pkg['submodules'] ?? null,
|
||||
$pkg['extract'] ?? null,
|
||||
self::getRetryAttempts(),
|
||||
SPC_DOWNLOAD_PRE_BUILT
|
||||
);
|
||||
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
|
||||
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg');
|
||||
if (isset($pkg['func']) && is_callable($pkg['func'])) {
|
||||
$pkg['name'] = $name;
|
||||
$pkg['func']($force, $pkg, SPC_DOWNLOAD_PACKAGE);
|
||||
break;
|
||||
}
|
||||
foreach ($classes as $class) {
|
||||
if (is_a($class, CustomPackage::class, true) && $class !== CustomPackage::class) {
|
||||
$cls = new $class();
|
||||
if (in_array($name, $cls->getSupportName())) {
|
||||
(new $class())->fetch($name, $force, $pkg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new DownloaderException('unknown source type: ' . $pkg['type']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Because sometimes files downloaded through the command line are not automatically deleted after a failure.
|
||||
// Here we need to manually delete the file if it is detected to exist.
|
||||
if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) {
|
||||
logger()->warning('Deleting download file: ' . $filename);
|
||||
unlink(DOWNLOAD_PATH . '/' . $filename);
|
||||
}
|
||||
throw new DownloaderException('Download failed! ' . $e->getMessage());
|
||||
}
|
||||
self::downloadByType($pkg['type'], $name, $pkg, $force, SPC_DOWNLOAD_PACKAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download source
|
||||
*
|
||||
* @param string $name source name
|
||||
* @param null|array{
|
||||
* @param null|array{
|
||||
* type: string,
|
||||
* repo: ?string,
|
||||
* url: ?string,
|
||||
@@ -414,7 +375,7 @@ class Downloader
|
||||
* path: ?string,
|
||||
* text: ?string
|
||||
* }
|
||||
* } $source source meta info: [type, path, rev, url, filename, regex, license]
|
||||
* } $source source meta info: [type, path, rev, url, filename, regex, license]
|
||||
* @param bool $force Whether to force download (default: false)
|
||||
* @param int $download_as Lock source type (default: SPC_LOCK_SOURCE)
|
||||
*/
|
||||
@@ -437,80 +398,7 @@ class Downloader
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
switch ($source['type']) {
|
||||
case 'bitbuckettag': // BitBucket Tag
|
||||
[$url, $filename] = self::getLatestBitbucketTag($name, $source);
|
||||
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as);
|
||||
break;
|
||||
case 'ghtar': // GitHub Release (tar)
|
||||
[$url, $filename] = self::getLatestGithubTarball($name, $source);
|
||||
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
|
||||
break;
|
||||
case 'ghtagtar': // GitHub Tag (tar)
|
||||
[$url, $filename] = self::getLatestGithubTarball($name, $source, 'tags');
|
||||
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
|
||||
break;
|
||||
case 'ghrel': // GitHub Release (uploaded)
|
||||
[$url, $filename] = self::getLatestGithubRelease($name, $source);
|
||||
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as, ['Accept: application/octet-stream'], [[CurlHook::class, 'setupGithubToken']]);
|
||||
break;
|
||||
case 'filelist': // Basic File List (regex based crawler)
|
||||
[$url, $filename] = self::getFromFileList($name, $source);
|
||||
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as);
|
||||
break;
|
||||
case 'url': // Direct download URL
|
||||
$url = $source['url'];
|
||||
$filename = $source['filename'] ?? basename($source['url']);
|
||||
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as);
|
||||
break;
|
||||
case 'git': // Git repo
|
||||
self::downloadGit(
|
||||
$name,
|
||||
$source['url'],
|
||||
$source['rev'],
|
||||
$source['submodules'] ?? null,
|
||||
$source['path'] ?? null,
|
||||
self::getRetryAttempts(),
|
||||
$download_as
|
||||
);
|
||||
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
|
||||
if (isset($source['func']) && is_callable($source['func'])) {
|
||||
$source['name'] = $name;
|
||||
$source['func']($force, $source, $download_as);
|
||||
break;
|
||||
}
|
||||
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/source', 'SPC\store\source');
|
||||
foreach ($classes as $class) {
|
||||
if (is_a($class, CustomSourceBase::class, true) && $class::NAME === $name) {
|
||||
(new $class())->fetch($force, $source, $download_as);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new DownloaderException('unknown source type: ' . $source['type']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Because sometimes files downloaded through the command line are not automatically deleted after a failure.
|
||||
// Here we need to manually delete the file if it is detected to exist.
|
||||
if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) {
|
||||
logger()->warning('Deleting download file: ' . $filename);
|
||||
unlink(DOWNLOAD_PATH . '/' . $filename);
|
||||
}
|
||||
throw new DownloaderException('Download failed! ' . $e->getMessage());
|
||||
}
|
||||
self::downloadByType($source['type'], $name, $source, $force, $download_as);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -711,4 +599,109 @@ class Downloader
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download by type.
|
||||
*
|
||||
* @param string $type Types
|
||||
* @param string $name Download item name
|
||||
* @param array{
|
||||
* url?: string,
|
||||
* repo?: string,
|
||||
* rev?: string,
|
||||
* path?: string,
|
||||
* filename?: string,
|
||||
* dirname?: string,
|
||||
* match?: string,
|
||||
* prefer-stable?: bool,
|
||||
* extract?: string,
|
||||
* submodules?: array<string>,
|
||||
* provide-pre-built?: bool,
|
||||
* func?: ?callable,
|
||||
* license?: array
|
||||
* } $conf Download item config
|
||||
* @param bool $force Force download
|
||||
* @param int $download_as Lock source type
|
||||
*/
|
||||
private static function downloadByType(string $type, string $name, array $conf, bool $force, int $download_as): void
|
||||
{
|
||||
try {
|
||||
switch ($type) {
|
||||
case 'pie': // Packagist
|
||||
[$url, $filename] = self::getPIEInfo($name, $conf);
|
||||
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
|
||||
break;
|
||||
case 'bitbuckettag': // BitBucket Tag
|
||||
[$url, $filename] = self::getLatestBitbucketTag($name, $conf);
|
||||
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as);
|
||||
break;
|
||||
case 'ghtar': // GitHub Release (tar)
|
||||
[$url, $filename] = self::getLatestGithubTarball($name, $conf);
|
||||
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
|
||||
break;
|
||||
case 'ghtagtar': // GitHub Tag (tar)
|
||||
[$url, $filename] = self::getLatestGithubTarball($name, $conf, 'tags');
|
||||
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
|
||||
break;
|
||||
case 'ghrel': // GitHub Release (uploaded)
|
||||
[$url, $filename] = self::getLatestGithubRelease($name, $conf);
|
||||
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as, ['Accept: application/octet-stream'], [[CurlHook::class, 'setupGithubToken']]);
|
||||
break;
|
||||
case 'filelist': // Basic File List (regex based crawler)
|
||||
[$url, $filename] = self::getFromFileList($name, $conf);
|
||||
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as);
|
||||
break;
|
||||
case 'url': // Direct download URL
|
||||
$url = $conf['url'];
|
||||
$filename = $conf['filename'] ?? basename($conf['url']);
|
||||
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as);
|
||||
break;
|
||||
case 'git': // Git repo
|
||||
self::downloadGit($name, $conf['url'], $conf['rev'], $conf['submodules'] ?? null, $conf['path'] ?? $conf['extract'] ?? null, self::getRetryAttempts(), $download_as);
|
||||
break;
|
||||
case 'local': // Local directory, do nothing, just lock it
|
||||
LockFile::lockSource($name, [
|
||||
'source_type' => SPC_SOURCE_LOCAL,
|
||||
'dirname' => $conf['dirname'],
|
||||
'move_path' => $conf['path'] ?? $conf['extract'] ?? null,
|
||||
'lock_as' => $download_as,
|
||||
]);
|
||||
break;
|
||||
case 'custom': // Custom download method, like API-based download or other
|
||||
if (isset($conf['func'])) {
|
||||
$conf['name'] = $name;
|
||||
$conf['func']($force, $conf, $download_as);
|
||||
break;
|
||||
}
|
||||
$classes = [
|
||||
...FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/source', 'SPC\store\source'),
|
||||
...FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg'),
|
||||
];
|
||||
foreach ($classes as $class) {
|
||||
if (is_a($class, CustomSourceBase::class, true) && $class::NAME === $name) {
|
||||
(new $class())->fetch($force, $conf, $download_as);
|
||||
break;
|
||||
}
|
||||
if (is_a($class, CustomPackage::class, true) && $class !== CustomPackage::class) {
|
||||
$cls = new $class();
|
||||
if (in_array($name, $cls->getSupportName())) {
|
||||
(new $class())->fetch($name, $force, $conf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new DownloaderException("Unknown download type: {$type}");
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Because sometimes files downloaded through the command line are not automatically deleted after a failure.
|
||||
// Here we need to manually delete the file if it is detected to exist.
|
||||
if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) {
|
||||
logger()->warning("Deleting download file: {$filename}");
|
||||
unlink(DOWNLOAD_PATH . '/' . $filename);
|
||||
}
|
||||
throw new DownloaderException("Download failed: {$e->getMessage()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +174,9 @@ class FileSystem
|
||||
logger()->debug("Copying file from {$from} to {$to}");
|
||||
$dst_path = FileSystem::convertPath($to);
|
||||
$src_path = FileSystem::convertPath($from);
|
||||
if ($src_path === $dst_path) {
|
||||
return;
|
||||
}
|
||||
if (!copy($src_path, $dst_path)) {
|
||||
throw new FileSystemException('Cannot copy file from ' . $src_path . ' to ' . $dst_path);
|
||||
}
|
||||
@@ -274,7 +277,7 @@ class FileSystem
|
||||
public static function convertWinPathToMinGW(string $path): string
|
||||
{
|
||||
if (preg_match('/^[A-Za-z]:/', $path)) {
|
||||
$path = '/' . strtolower(substr($path, 0, 1)) . '/' . str_replace('\\', '/', substr($path, 2));
|
||||
$path = '/' . strtolower($path[0]) . '/' . str_replace('\\', '/', substr($path, 2));
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
@@ -314,8 +317,13 @@ class FileSystem
|
||||
$sub_file = self::convertPath($dir . '/' . $v);
|
||||
if (is_dir($sub_file) && $recursive) {
|
||||
# 如果是 目录 且 递推 , 则递推添加下级文件
|
||||
$list = array_merge($list, self::scanDirFiles($sub_file, $recursive, $relative));
|
||||
} elseif (is_file($sub_file) || is_dir($sub_file) && !$recursive && $include_dir) {
|
||||
$sub_list = self::scanDirFiles($sub_file, $recursive, $relative);
|
||||
if (is_array($sub_list)) {
|
||||
foreach ($sub_list as $item) {
|
||||
$list[] = $item;
|
||||
}
|
||||
}
|
||||
} elseif (is_file($sub_file) || (is_dir($sub_file) && !$recursive && $include_dir)) {
|
||||
# 如果是 文件 或 (是 目录 且 不递推 且 包含目录)
|
||||
if (is_string($relative) && mb_strpos($sub_file, $relative) === 0) {
|
||||
$list[] = ltrim(mb_substr($sub_file, mb_strlen($relative)), '/\\');
|
||||
@@ -440,7 +448,7 @@ class FileSystem
|
||||
public static function writeFile(string $path, mixed $content, ...$args): bool|int|string
|
||||
{
|
||||
$dir = pathinfo(self::convertPath($path), PATHINFO_DIRNAME);
|
||||
if (!is_dir($dir) && !mkdir($dir, 0755, true)) {
|
||||
if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) {
|
||||
throw new FileSystemException('Write file failed, cannot create parent directory: ' . $dir);
|
||||
}
|
||||
return file_put_contents($path, $content, ...$args);
|
||||
@@ -579,7 +587,7 @@ class FileSystem
|
||||
'tar', 'xz', 'txz' => f_passthru("tar -xf {$filename} -C {$target} --strip-components 1"),
|
||||
'tgz', 'gz' => f_passthru("tar -xzf {$filename} -C {$target} --strip-components 1"),
|
||||
'bz2' => f_passthru("tar -xjf {$filename} -C {$target} --strip-components 1"),
|
||||
'zip' => f_passthru("unzip {$filename} -d {$target}"),
|
||||
'zip' => self::unzipWithStrip($filename, $target),
|
||||
default => throw new FileSystemException('unknown archive format: ' . $filename),
|
||||
};
|
||||
} elseif (PHP_OS_FAMILY === 'Windows') {
|
||||
@@ -594,7 +602,7 @@ class FileSystem
|
||||
match (self::extname($filename)) {
|
||||
'tar' => f_passthru("tar -xf {$filename} -C {$target} --strip-components 1"),
|
||||
'xz', 'txz', 'gz', 'tgz', 'bz2' => cmd()->execWithResult("\"{$_7z}\" x -so {$filename} | tar -f - -x -C \"{$target}\" --strip-components 1"),
|
||||
'zip' => f_passthru("\"{$_7z}\" x {$filename} -o{$target} -y"),
|
||||
'zip' => self::unzipWithStrip($filename, $target),
|
||||
default => throw new FileSystemException("unknown archive format: {$filename}"),
|
||||
};
|
||||
}
|
||||
@@ -630,7 +638,7 @@ class FileSystem
|
||||
|
||||
private static function extractWithType(string $source_type, string $filename, string $extract_path): void
|
||||
{
|
||||
logger()->debug('Extracting source [' . $source_type . ']: ' . $filename);
|
||||
logger()->debug("Extracting source [{$source_type}]: {$filename}");
|
||||
/* @phpstan-ignore-next-line */
|
||||
match ($source_type) {
|
||||
SPC_SOURCE_ARCHIVE => self::extractArchive($filename, $extract_path),
|
||||
@@ -639,4 +647,107 @@ class FileSystem
|
||||
SPC_SOURCE_LOCAL => symlink(self::convertPath($filename), $extract_path),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Move file or directory, handling cross-device scenarios
|
||||
* Uses rename() if possible, falls back to copy+delete for cross-device moves
|
||||
*
|
||||
* @param string $source Source path
|
||||
* @param string $dest Destination path
|
||||
*/
|
||||
private static function moveFileOrDir(string $source, string $dest): void
|
||||
{
|
||||
$source = self::convertPath($source);
|
||||
$dest = self::convertPath($dest);
|
||||
|
||||
// Try rename first (fast, atomic)
|
||||
if (@rename($source, $dest)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_dir($source)) {
|
||||
self::copyDir($source, $dest);
|
||||
self::removeDir($source);
|
||||
} else {
|
||||
if (!copy($source, $dest)) {
|
||||
throw new FileSystemException("Failed to copy file from {$source} to {$dest}");
|
||||
}
|
||||
if (!unlink($source)) {
|
||||
throw new FileSystemException("Failed to remove source file: {$source}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unzip file with stripping top-level directory
|
||||
*/
|
||||
private static function unzipWithStrip(string $zip_file, string $extract_path): void
|
||||
{
|
||||
$temp_dir = self::convertPath(sys_get_temp_dir() . '/spc_unzip_' . bin2hex(random_bytes(16)));
|
||||
$zip_file = self::convertPath($zip_file);
|
||||
$extract_path = self::convertPath($extract_path);
|
||||
|
||||
// extract to temp dir
|
||||
self::createDir($temp_dir);
|
||||
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$mute = defined('DEBUG_MODE') ? '' : ' > NUL';
|
||||
// use php-sdk-binary-tools/bin/7za.exe
|
||||
$_7z = self::convertPath(getenv('PHP_SDK_PATH') . '/bin/7za.exe');
|
||||
f_passthru("\"{$_7z}\" x {$zip_file} -o{$temp_dir} -y{$mute}");
|
||||
} else {
|
||||
$mute = defined('DEBUG_MODE') ? '' : ' > /dev/null';
|
||||
f_passthru("unzip \"{$zip_file}\" -d \"{$temp_dir}\"{$mute}");
|
||||
}
|
||||
// scan first level dirs (relative, not recursive, include dirs)
|
||||
$contents = self::scanDirFiles($temp_dir, false, true, true);
|
||||
if ($contents === false) {
|
||||
throw new FileSystemException('Cannot scan unzip temp dir: ' . $temp_dir);
|
||||
}
|
||||
// if extract path already exists, remove it
|
||||
if (is_dir($extract_path)) {
|
||||
self::removeDir($extract_path);
|
||||
}
|
||||
// if only one dir, move its contents to extract_path
|
||||
$subdir = self::convertPath("{$temp_dir}/{$contents[0]}");
|
||||
if (count($contents) === 1 && is_dir($subdir)) {
|
||||
self::moveFileOrDir($subdir, $extract_path);
|
||||
} else {
|
||||
// else, if it contains only one dir, strip dir and copy other files
|
||||
$dircount = 0;
|
||||
$dir = [];
|
||||
$top_files = [];
|
||||
foreach ($contents as $item) {
|
||||
if (is_dir(self::convertPath("{$temp_dir}/{$item}"))) {
|
||||
++$dircount;
|
||||
$dir[] = $item;
|
||||
} else {
|
||||
$top_files[] = $item;
|
||||
}
|
||||
}
|
||||
// extract dir contents to extract_path
|
||||
self::createDir($extract_path);
|
||||
// extract move dir
|
||||
if ($dircount === 1) {
|
||||
$sub_contents = self::scanDirFiles("{$temp_dir}/{$dir[0]}", false, true, true);
|
||||
if ($sub_contents === false) {
|
||||
throw new FileSystemException("Cannot scan unzip temp sub-dir: {$dir[0]}");
|
||||
}
|
||||
foreach ($sub_contents as $sub_item) {
|
||||
self::moveFileOrDir(self::convertPath("{$temp_dir}/{$dir[0]}/{$sub_item}"), self::convertPath("{$extract_path}/{$sub_item}"));
|
||||
}
|
||||
} else {
|
||||
foreach ($dir as $item) {
|
||||
self::moveFileOrDir(self::convertPath("{$temp_dir}/{$item}"), self::convertPath("{$extract_path}/{$item}"));
|
||||
}
|
||||
}
|
||||
// move top-level files to extract_path
|
||||
foreach ($top_files as $top_file) {
|
||||
self::moveFileOrDir(self::convertPath("{$temp_dir}/{$top_file}"), self::convertPath("{$extract_path}/{$top_file}"));
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up temp directory
|
||||
self::removeDir($temp_dir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,8 +155,8 @@ class LockFile
|
||||
* @param string $name Source name
|
||||
* @param array{
|
||||
* source_type: string,
|
||||
* dirname: ?string,
|
||||
* filename: ?string,
|
||||
* dirname?: ?string,
|
||||
* filename?: ?string,
|
||||
* move_path: ?string,
|
||||
* lock_as: int
|
||||
* } $data Source data
|
||||
|
||||
@@ -44,7 +44,7 @@ class SourcePatcher
|
||||
}
|
||||
foreach ($builder->getLibs() as $lib) {
|
||||
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
|
||||
@@ -549,6 +549,39 @@ class SourcePatcher
|
||||
FileSystem::writeFile(SOURCE_PATH . '/php-src/Makefile', implode("\r\n", $lines));
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch cgi SAPI Makefile for Windows.
|
||||
*/
|
||||
public static function patchWindowsCGITarget(): void
|
||||
{
|
||||
// search Makefile code line contains "$(BUILD_DIR)\php-cgi.exe:"
|
||||
$content = FileSystem::readFile(SOURCE_PATH . '/php-src/Makefile');
|
||||
$lines = explode("\r\n", $content);
|
||||
$line_num = 0;
|
||||
$found = false;
|
||||
foreach ($lines as $v) {
|
||||
if (str_contains($v, '$(BUILD_DIR)\php-cgi.exe:')) {
|
||||
$found = $line_num;
|
||||
break;
|
||||
}
|
||||
++$line_num;
|
||||
}
|
||||
if ($found === false) {
|
||||
throw new PatchException('Windows Makefile patching for php-cgi.exe target', 'Cannot patch windows CGI Makefile, Makefile does not contain "$(BUILD_DIR)\php-cgi.exe:" line');
|
||||
}
|
||||
// cli: $(BUILD_DIR)\php.exe: $(DEPS_CLI) $(CLI_GLOBAL_OBJS) $(BUILD_DIR)\$(PHPLIB) $(BUILD_DIR)\php.exe.res $(BUILD_DIR)\php.exe.manifest
|
||||
// $lines[$line_num] = '$(BUILD_DIR)\php.exe: generated_files $(DEPS_CLI) $(CLI_GLOBAL_OBJS) $(PHP_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(ASM_OBJS) $(BUILD_DIR)\php.exe.res $(BUILD_DIR)\php.exe.manifest';
|
||||
// cgi: $(BUILD_DIR)\php-cgi.exe: $(DEPS_CGI) $(CGI_GLOBAL_OBJS) $(BUILD_DIR)\$(PHPLIB) $(BUILD_DIR)\php-cgi.exe.res $(BUILD_DIR)\php-cgi.exe.manifest
|
||||
$lines[$line_num] = '$(BUILD_DIR)\php-cgi.exe: $(DEPS_CGI) $(CGI_GLOBAL_OBJS) $(PHP_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(ASM_OBJS) $(BUILD_DIR)\php-cgi.exe.res $(BUILD_DIR)\php-cgi.exe.manifest';
|
||||
|
||||
// cli: @"$(LINK)" /nologo $(CGI_GLOBAL_OBJS_RESP) $(BUILD_DIR)\$(PHPLIB) $(LIBS_CGI) $(BUILD_DIR)\php-cgi.exe.res /out:$(BUILD_DIR)\php-cgi.exe $(LDFLAGS) $(LDFLAGS_CGI)
|
||||
$lines[$line_num + 1] = "\t" . '@"$(LINK)" /nologo $(PHP_GLOBAL_OBJS_RESP) $(CGI_GLOBAL_OBJS_RESP) $(STATIC_EXT_OBJS_RESP) $(STATIC_EXT_LIBS) $(ASM_OBJS) $(LIBS) $(LIBS_CGI) $(BUILD_DIR)\php-cgi.exe.res /out:$(BUILD_DIR)\php-cgi.exe $(LDFLAGS) $(LDFLAGS_CGI) /ltcg /nodefaultlib:msvcrt /nodefaultlib:msvcrtd /ignore:4286';
|
||||
FileSystem::writeFile(SOURCE_PATH . '/php-src/Makefile', implode("\r\n", $lines));
|
||||
|
||||
// Patch cgi-static, comment ZEND_TSRMLS_CACHE_DEFINE()
|
||||
FileSystem::replaceFileRegex(SOURCE_PATH . '\php-src\sapi\cgi\cgi_main.c', '/^ZEND_TSRMLS_CACHE_DEFINE\(\)/m', '// ZEND_TSRMLS_CACHE_DEFINE()');
|
||||
}
|
||||
|
||||
public static function patchPhpLibxml212(): bool
|
||||
{
|
||||
$file = file_get_contents(SOURCE_PATH . '/php-src/main/php_version.h');
|
||||
|
||||
@@ -30,10 +30,14 @@ abstract class CustomPackage
|
||||
|
||||
/**
|
||||
* Get the environment variables this package needs to be usable.
|
||||
* PATH needs to be appended, rather than replaced.
|
||||
*/
|
||||
abstract public static function getEnvironment(): array;
|
||||
|
||||
/**
|
||||
* Get the PATH required to use this package.
|
||||
*/
|
||||
abstract public static function getPath(): ?string;
|
||||
|
||||
abstract public static function isInstalled(): bool;
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,18 +13,7 @@ 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}";
|
||||
|
||||
$folder = PKG_ROOT_PATH . '/go-xcaddy';
|
||||
return is_dir($folder) && is_file("{$folder}/bin/go") && is_file("{$folder}/bin/xcaddy");
|
||||
}
|
||||
|
||||
@@ -59,7 +48,7 @@ class GoXcaddy extends CustomPackage
|
||||
'macos' => 'darwin',
|
||||
default => throw new \InvalidArgumentException('Unsupported OS: ' . $name),
|
||||
};
|
||||
$go_version = '1.24.4';
|
||||
$go_version = '1.25.0';
|
||||
$config = [
|
||||
'type' => 'url',
|
||||
'url' => "https://go.dev/dl/go{$go_version}.{$os}-{$arch}.tar.gz",
|
||||
@@ -70,15 +59,15 @@ class GoXcaddy extends CustomPackage
|
||||
public function extract(string $name): void
|
||||
{
|
||||
$pkgroot = PKG_ROOT_PATH;
|
||||
$go_exec = "{$pkgroot}/{$name}/bin/go";
|
||||
$xcaddy_exec = "{$pkgroot}/{$name}/bin/xcaddy";
|
||||
$go_exec = "{$pkgroot}/go-xcaddy/bin/go";
|
||||
$xcaddy_exec = "{$pkgroot}/go-xcaddy/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'] ?? "{$pkgroot}/{$name}";
|
||||
$extract = $lock[$name]['move_path'] ?? "{$pkgroot}/go-xcaddy";
|
||||
|
||||
FileSystem::extractPackage($name, $source_type, $filename, $extract);
|
||||
|
||||
@@ -91,9 +80,9 @@ class GoXcaddy extends CustomPackage
|
||||
// install xcaddy without using musl tools, xcaddy build requires dynamic linking
|
||||
shell()
|
||||
->appendEnv([
|
||||
'PATH' => "{$pkgroot}/{$name}/bin:" . $sanitizedPath,
|
||||
'GOROOT' => "{$pkgroot}/{$name}",
|
||||
'GOBIN' => "{$pkgroot}/{$name}/bin",
|
||||
'PATH' => "{$pkgroot}/go-xcaddy/bin:" . $sanitizedPath,
|
||||
'GOROOT' => "{$pkgroot}/go-xcaddy",
|
||||
'GOBIN' => "{$pkgroot}/go-xcaddy/bin",
|
||||
'GOPATH' => "{$pkgroot}/go",
|
||||
])
|
||||
->exec('CC=cc go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest');
|
||||
@@ -101,22 +90,17 @@ class GoXcaddy extends CustomPackage
|
||||
|
||||
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}";
|
||||
$packageName = 'go-xcaddy';
|
||||
$pkgroot = PKG_ROOT_PATH;
|
||||
|
||||
return [
|
||||
'PATH' => "{$pkgroot}/{$packageName}/bin",
|
||||
'GOROOT' => "{$pkgroot}/{$packageName}",
|
||||
'GOBIN' => "{$pkgroot}/{$packageName}/bin",
|
||||
'GOPATH' => "{$pkgroot}/go",
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPath(): ?string
|
||||
{
|
||||
return PKG_ROOT_PATH . '/go-xcaddy/bin';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ class Zig extends CustomPackage
|
||||
public function extract(string $name): void
|
||||
{
|
||||
$pkgroot = PKG_ROOT_PATH;
|
||||
$zig_bin_dir = "{$pkgroot}/{$name}";
|
||||
$zig_bin_dir = "{$pkgroot}/zig";
|
||||
|
||||
$files = ['zig', 'zig-cc', 'zig-c++', 'zig-ar', 'zig-ld.lld', 'zig-ranlib', 'zig-objcopy'];
|
||||
$all_exist = true;
|
||||
@@ -120,7 +120,7 @@ class Zig extends CustomPackage
|
||||
$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}";
|
||||
$extract = "{$pkgroot}/zig";
|
||||
|
||||
FileSystem::extractPackage($name, $source_type, $filename, $extract);
|
||||
|
||||
@@ -129,34 +129,12 @@ class Zig extends CustomPackage
|
||||
|
||||
public static function getEnvironment(): array
|
||||
{
|
||||
$arch = arch2gnu(php_uname('m'));
|
||||
$os = match (PHP_OS_FAMILY) {
|
||||
'Windows' => 'win',
|
||||
'Darwin' => 'macos',
|
||||
'BSD' => 'freebsd',
|
||||
default => 'linux',
|
||||
};
|
||||
|
||||
$packageName = "zig-{$arch}-{$os}";
|
||||
$path = PKG_ROOT_PATH . "/{$packageName}";
|
||||
|
||||
return [
|
||||
'PATH' => $path,
|
||||
];
|
||||
return [];
|
||||
}
|
||||
|
||||
private static function getPath(): string
|
||||
public static function getPath(): ?string
|
||||
{
|
||||
$arch = arch2gnu(php_uname('m'));
|
||||
$os = match (PHP_OS_FAMILY) {
|
||||
'Windows' => 'win',
|
||||
'Darwin' => 'macos',
|
||||
'BSD' => 'freebsd',
|
||||
default => 'linux',
|
||||
};
|
||||
|
||||
$packageName = "zig-{$arch}-{$os}";
|
||||
return PKG_ROOT_PATH . "/{$packageName}";
|
||||
return PKG_ROOT_PATH . '/zig';
|
||||
}
|
||||
|
||||
private function createZigCcScript(string $bin_dir): void
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
|
||||
BUILDROOT_ABS="$(realpath "$SCRIPT_DIR/../../buildroot/include" 2>/dev/null || true)"
|
||||
BUILDROOT_ABS="$(realpath "$SCRIPT_DIR/../../../buildroot/include" 2>/dev/null || true)"
|
||||
PARSED_ARGS=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
@@ -19,9 +19,15 @@ while [[ $# -gt 0 ]]; do
|
||||
ARG_ABS="$(realpath "$ARG" 2>/dev/null || true)"
|
||||
[[ "$ARG_ABS" == "$BUILDROOT_ABS" ]] && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem$ARG")
|
||||
;;
|
||||
-march=*|-mcpu=*) # replace -march=x86-64 with -march=x86_64
|
||||
-march=*|-mcpu=*)
|
||||
OPT_NAME="${1%%=*}"
|
||||
OPT_VALUE="${1#*=}"
|
||||
# Skip armv8- flags entirely as Zig doesn't support them
|
||||
if [[ "$OPT_VALUE" == armv8-* ]]; then
|
||||
shift
|
||||
continue
|
||||
fi
|
||||
# replace -march=x86-64 with -march=x86_64
|
||||
OPT_VALUE="${OPT_VALUE//-/_}"
|
||||
PARSED_ARGS+=("${OPT_NAME}=${OPT_VALUE}")
|
||||
shift
|
||||
|
||||
@@ -16,11 +16,11 @@ class PhpSource extends CustomSourceBase
|
||||
{
|
||||
$major = defined('SPC_BUILD_PHP_VERSION') ? SPC_BUILD_PHP_VERSION : '8.4';
|
||||
if ($major === '8.5') {
|
||||
Downloader::downloadSource('php-src', ['type' => 'url', 'url' => 'https://downloads.php.net/~edorian/php-8.5.0beta1.tar.xz'], $force);
|
||||
Downloader::downloadSource('php-src', ['type' => 'url', 'url' => 'https://downloads.php.net/~daniels/php-8.5.0RC5.tar.xz'], $force);
|
||||
} elseif ($major === 'git') {
|
||||
Downloader::downloadSource('php-src', ['type' => 'git', 'url' => 'https://github.com/php/php-src.git', 'rev' => 'master'], $force);
|
||||
} else {
|
||||
Downloader::downloadSource('php-src', self::getLatestPHPInfo($major), $force);
|
||||
Downloader::downloadSource('php-src', $this->getLatestPHPInfo($major), $force);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class PhpSource extends CustomSourceBase
|
||||
// 查找最新的小版本号
|
||||
$info = json_decode(Downloader::curlExec(
|
||||
url: "https://www.php.net/releases/index.php?json&version={$major_version}",
|
||||
retries: intval(getenv('SPC_DOWNLOAD_RETRIES') ?: 0)
|
||||
retries: (int) getenv('SPC_DOWNLOAD_RETRIES') ?: 0
|
||||
), true);
|
||||
if (!isset($info['version'])) {
|
||||
throw new DownloaderException("Version {$major_version} not found.");
|
||||
|
||||
@@ -42,10 +42,10 @@ class ZigToolchain implements ToolchainInterface
|
||||
|
||||
public function afterInit(): void
|
||||
{
|
||||
if (!is_dir(Zig::getEnvironment()['PATH'])) {
|
||||
if (!Zig::isInstalled()) {
|
||||
throw new EnvironmentException('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']);
|
||||
GlobalEnvManager::addPathIfNotExists(Zig::getPath());
|
||||
f_passthru('ulimit -n 2048'); // zig opens extra file descriptors, so when a lot of extensions are built statically, 1024 is not enough
|
||||
$cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: '';
|
||||
$cxxflags = getenv('SPC_DEFAULT_CXX_FLAGS') ?: '';
|
||||
@@ -64,6 +64,11 @@ class ZigToolchain implements ToolchainInterface
|
||||
$extra_libs = trim($extra_libs . ' -lunwind');
|
||||
GlobalEnvManager::putenv("SPC_EXTRA_LIBS={$extra_libs}");
|
||||
}
|
||||
$cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: getenv('CFLAGS') ?: '';
|
||||
$has_avx512 = str_contains($cflags, '-mavx512') || str_contains($cflags, '-march=x86-64-v4');
|
||||
if (!$has_avx512) {
|
||||
GlobalEnvManager::putenv('SPC_EXTRA_PHP_VARS=php_cv_have_avx512=no php_cv_have_avx512vbmi=no');
|
||||
}
|
||||
}
|
||||
|
||||
public function getCompilerInfo(): ?string
|
||||
|
||||
@@ -11,6 +11,150 @@ use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class ConfigValidator
|
||||
{
|
||||
/**
|
||||
* Global field type definitions
|
||||
* Maps field names to their expected types and validation rules
|
||||
* Note: This only includes fields used in config files (source.json, lib.json, ext.json, pkg.json, pre-built.json)
|
||||
*/
|
||||
private const array FIELD_TYPES = [
|
||||
// String fields
|
||||
'url' => 'string', // url
|
||||
'regex' => 'string', // regex pattern
|
||||
'rev' => 'string', // revision/branch
|
||||
'repo' => 'string', // repository name
|
||||
'match' => 'string', // match pattern (aaa*bbb)
|
||||
'filename' => 'string', // filename
|
||||
'path' => 'string', // copy path
|
||||
'extract' => 'string', // copy path (alias of path)
|
||||
'dirname' => 'string', // directory name for local source
|
||||
'source' => 'string', // the source name that this item uses
|
||||
'match-pattern-linux' => 'string', // pre-built match pattern for linux
|
||||
'match-pattern-macos' => 'string', // pre-built match pattern for macos
|
||||
'match-pattern-windows' => 'string', // pre-built match pattern for windows
|
||||
|
||||
// Boolean fields
|
||||
'prefer-stable' => 'bool', // prefer stable releases
|
||||
'provide-pre-built' => 'bool', // provide pre-built binaries
|
||||
'notes' => 'bool', // whether to show notes in docs
|
||||
'cpp-library' => 'bool', // whether this is a C++ library
|
||||
'cpp-extension' => 'bool', // whether this is a C++ extension
|
||||
'build-with-php' => 'bool', // whether if this extension can be built to shared with PHP source together
|
||||
'zend-extension' => 'bool', // whether this is a zend extension
|
||||
'unix-only' => 'bool', // whether this extension is only for unix-like systems
|
||||
|
||||
// Array fields
|
||||
'submodules' => 'array', // git submodules list (for git source type)
|
||||
'lib-depends' => 'list',
|
||||
'lib-suggests' => 'list',
|
||||
'ext-depends' => 'list',
|
||||
'ext-suggests' => 'list',
|
||||
'static-libs' => 'list',
|
||||
'pkg-configs' => 'list', // required pkg-config files without suffix (e.g. [libwebp])
|
||||
'headers' => 'list', // required header files
|
||||
'bin' => 'list', // required binary files
|
||||
'frameworks' => 'list', // shared library frameworks (macOS)
|
||||
|
||||
// Object/assoc array fields
|
||||
'support' => 'object', // extension OS support docs
|
||||
'extract-files' => 'object', // pkg.json extract files mapping with match pattern
|
||||
'alt' => 'object|bool', // alternative source/package
|
||||
'license' => 'object|array', // license information
|
||||
'target' => 'array', // extension build targets (default: [static], alternate: [shared] or both)
|
||||
|
||||
// Special/mixed fields
|
||||
'func' => 'callable', // custom download function for custom source/package type
|
||||
'type' => 'string', // type field (validated separately)
|
||||
];
|
||||
|
||||
/**
|
||||
* Source/Package download type validation rules
|
||||
* Maps type names to [required_props, optional_props]
|
||||
*/
|
||||
private const array SOURCE_TYPE_FIELDS = [
|
||||
'filelist' => [['url', 'regex'], []],
|
||||
'git' => [['url', 'rev'], ['path', 'extract', 'submodules']],
|
||||
'ghtagtar' => [['repo'], ['path', 'extract', 'prefer-stable', 'match']],
|
||||
'ghtar' => [['repo'], ['path', 'extract', 'prefer-stable', 'match']],
|
||||
'ghrel' => [['repo', 'match'], ['path', 'extract', 'prefer-stable']],
|
||||
'url' => [['url'], ['filename', 'path', 'extract']],
|
||||
'bitbuckettag' => [['repo'], ['path', 'extract']],
|
||||
'local' => [['dirname'], ['path', 'extract']],
|
||||
'pie' => [['repo'], ['path']],
|
||||
'custom' => [[], ['func']],
|
||||
];
|
||||
|
||||
/**
|
||||
* Source.json specific fields [field_name => required]
|
||||
* Note: 'type' is validated separately in validateSourceTypeConfig
|
||||
* Field types are defined in FIELD_TYPES constant
|
||||
*/
|
||||
private const array SOURCE_FIELDS = [
|
||||
'type' => true, // source type (must be SOURCE_TYPE_FIELDS key)
|
||||
'provide-pre-built' => false, // whether to provide pre-built binaries
|
||||
'alt' => false, // alternative source configuration
|
||||
'license' => false, // license information for source
|
||||
// ... other fields are validated based on source type
|
||||
];
|
||||
|
||||
/**
|
||||
* Lib.json specific fields [field_name => required]
|
||||
* Field types are defined in FIELD_TYPES constant
|
||||
*/
|
||||
private const array LIB_FIELDS = [
|
||||
'type' => false, // lib type (lib/package/target/root)
|
||||
'source' => false, // the source name that this lib uses
|
||||
'lib-depends' => false, // required libraries
|
||||
'lib-suggests' => false, // suggested libraries
|
||||
'static-libs' => false, // Generated static libraries
|
||||
'pkg-configs' => false, // Generated pkg-config files
|
||||
'cpp-library' => false, // whether this is a C++ library
|
||||
'headers' => false, // Generated header files
|
||||
'bin' => false, // Generated binary files
|
||||
'frameworks' => false, // Used shared library frameworks (macOS)
|
||||
];
|
||||
|
||||
/**
|
||||
* Ext.json specific fields [field_name => required]
|
||||
* Field types are defined in FIELD_TYPES constant
|
||||
*/
|
||||
private const array EXT_FIELDS = [
|
||||
'type' => true, // extension type (builtin/external/addon/wip)
|
||||
'source' => false, // the source name that this extension uses
|
||||
'support' => false, // extension OS support docs
|
||||
'notes' => false, // whether to show notes in docs
|
||||
'cpp-extension' => false, // whether this is a C++ extension
|
||||
'build-with-php' => false, // whether if this extension can be built to shared with PHP source together
|
||||
'target' => false, // extension build targets (default: [static], alternate: [shared] or both)
|
||||
'lib-depends' => false,
|
||||
'lib-suggests' => false,
|
||||
'ext-depends' => false,
|
||||
'ext-suggests' => false,
|
||||
'frameworks' => false,
|
||||
'zend-extension' => false, // whether this is a zend extension
|
||||
'unix-only' => false, // whether this extension is only for unix-like systems
|
||||
];
|
||||
|
||||
/**
|
||||
* Pkg.json specific fields [field_name => required]
|
||||
* Field types are defined in FIELD_TYPES constant
|
||||
*/
|
||||
private const array PKG_FIELDS = [
|
||||
'type' => true, // package type (same as source type)
|
||||
'extract-files' => false, // files to extract mapping (source pattern => target path)
|
||||
];
|
||||
|
||||
/**
|
||||
* Pre-built.json specific fields [field_name => required]
|
||||
* Field types are defined in FIELD_TYPES constant
|
||||
*/
|
||||
private const array PRE_BUILT_FIELDS = [
|
||||
'repo' => true, // repository name for pre-built binaries
|
||||
'prefer-stable' => false, // prefer stable releases
|
||||
'match-pattern-linux' => false, // pre-built match pattern for linux
|
||||
'match-pattern-macos' => false, // pre-built match pattern for macos
|
||||
'match-pattern-windows' => false, // pre-built match pattern for windows
|
||||
];
|
||||
|
||||
/**
|
||||
* Validate source.json
|
||||
*
|
||||
@@ -22,33 +166,20 @@ class ConfigValidator
|
||||
// Validate basic source type configuration
|
||||
self::validateSourceTypeConfig($src, $name, 'source');
|
||||
|
||||
// Check source-specific fields
|
||||
// Validate all source-specific fields using unified method
|
||||
self::validateConfigFields($src, $name, 'source', self::SOURCE_FIELDS);
|
||||
|
||||
// Check for unknown fields
|
||||
self::validateAllowedFields($src, $name, 'source', self::SOURCE_FIELDS);
|
||||
|
||||
// check if alt is valid
|
||||
if (isset($src['alt'])) {
|
||||
if (!is_assoc_array($src['alt']) && !is_bool($src['alt'])) {
|
||||
throw new ValidationException("source {$name} alt must be object or boolean");
|
||||
}
|
||||
if (is_assoc_array($src['alt'])) {
|
||||
// validate alt source recursively
|
||||
self::validateSource([$name . '_alt' => $src['alt']]);
|
||||
}
|
||||
}
|
||||
|
||||
// check if provide-pre-built is boolean
|
||||
if (isset($src['provide-pre-built']) && !is_bool($src['provide-pre-built'])) {
|
||||
throw new ValidationException("source {$name} provide-pre-built must be boolean");
|
||||
}
|
||||
|
||||
// check if prefer-stable is boolean
|
||||
if (isset($src['prefer-stable']) && !is_bool($src['prefer-stable'])) {
|
||||
throw new ValidationException("source {$name} prefer-stable must be boolean");
|
||||
if (isset($src['alt']) && is_assoc_array($src['alt'])) {
|
||||
// validate alt source recursively
|
||||
self::validateSource([$name . '_alt' => $src['alt']]);
|
||||
}
|
||||
|
||||
// check if license is valid
|
||||
if (isset($src['license'])) {
|
||||
if (!is_array($src['license'])) {
|
||||
throw new ValidationException("source {$name} license must be an object or array");
|
||||
}
|
||||
if (is_assoc_array($src['license'])) {
|
||||
self::checkSingleLicense($src['license'], $name);
|
||||
} elseif (is_list_array($src['license'])) {
|
||||
@@ -58,8 +189,6 @@ class ConfigValidator
|
||||
}
|
||||
self::checkSingleLicense($license, $name);
|
||||
}
|
||||
} else {
|
||||
throw new ValidationException("source {$name} license must be an object or array");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,9 +200,8 @@ class ConfigValidator
|
||||
if (!is_array($data)) {
|
||||
throw new ValidationException('lib.json is broken');
|
||||
}
|
||||
// check each lib
|
||||
|
||||
foreach ($data as $name => $lib) {
|
||||
// check if lib is an assoc array
|
||||
if (!is_assoc_array($lib)) {
|
||||
throw new ValidationException("lib {$name} is not an object");
|
||||
}
|
||||
@@ -89,36 +217,22 @@ class ConfigValidator
|
||||
if (isset($lib['source']) && !empty($source_data) && !isset($source_data[$lib['source']])) {
|
||||
throw new ValidationException("lib {$name} assigns an invalid source: {$lib['source']}");
|
||||
}
|
||||
// check if source is string
|
||||
if (isset($lib['source']) && !is_string($lib['source'])) {
|
||||
throw new ValidationException("lib {$name} source must be string");
|
||||
}
|
||||
// check if [lib-depends|lib-suggests|static-libs|headers|bin][-windows|-unix|-macos|-linux] are valid list array
|
||||
|
||||
// Validate basic fields using unified method
|
||||
self::validateConfigFields($lib, $name, 'lib', self::LIB_FIELDS);
|
||||
|
||||
// Validate list array fields with suffixes
|
||||
$suffixes = ['', '-windows', '-unix', '-macos', '-linux'];
|
||||
foreach ($suffixes as $suffix) {
|
||||
if (isset($lib['lib-depends' . $suffix]) && !is_list_array($lib['lib-depends' . $suffix])) {
|
||||
throw new ValidationException("lib {$name} lib-depends must be a list");
|
||||
}
|
||||
if (isset($lib['lib-suggests' . $suffix]) && !is_list_array($lib['lib-suggests' . $suffix])) {
|
||||
throw new ValidationException("lib {$name} lib-suggests must be a list");
|
||||
}
|
||||
if (isset($lib['static-libs' . $suffix]) && !is_list_array($lib['static-libs' . $suffix])) {
|
||||
throw new ValidationException("lib {$name} static-libs must be a list");
|
||||
}
|
||||
if (isset($lib['pkg-configs' . $suffix]) && !is_list_array($lib['pkg-configs' . $suffix])) {
|
||||
throw new ValidationException("lib {$name} pkg-configs must be a list");
|
||||
}
|
||||
if (isset($lib['headers' . $suffix]) && !is_list_array($lib['headers' . $suffix])) {
|
||||
throw new ValidationException("lib {$name} headers must be a list");
|
||||
}
|
||||
if (isset($lib['bin' . $suffix]) && !is_list_array($lib['bin' . $suffix])) {
|
||||
throw new ValidationException("lib {$name} bin must be a list");
|
||||
}
|
||||
}
|
||||
// check if frameworks is a list array
|
||||
if (isset($lib['frameworks']) && !is_list_array($lib['frameworks'])) {
|
||||
throw new ValidationException("lib {$name} frameworks must be a list");
|
||||
$fields = ['lib-depends', 'lib-suggests', 'static-libs', 'pkg-configs', 'headers', 'bin'];
|
||||
self::validateListArrayFields($lib, $name, 'lib', $fields, $suffixes);
|
||||
|
||||
// Validate frameworks (special case without suffix)
|
||||
if (isset($lib['frameworks'])) {
|
||||
self::validateFieldType('frameworks', $lib['frameworks'], $name, 'lib');
|
||||
}
|
||||
|
||||
// Check for unknown fields
|
||||
self::validateAllowedFields($lib, $name, 'lib', self::LIB_FIELDS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,61 +241,34 @@ class ConfigValidator
|
||||
if (!is_array($data)) {
|
||||
throw new ValidationException('ext.json is broken');
|
||||
}
|
||||
// check each extension
|
||||
|
||||
foreach ($data as $name => $ext) {
|
||||
// check if ext is an assoc array
|
||||
if (!is_assoc_array($ext)) {
|
||||
throw new ValidationException("ext {$name} is not an object");
|
||||
}
|
||||
// check if ext has valid type
|
||||
|
||||
if (!in_array($ext['type'] ?? '', ['builtin', 'external', 'addon', 'wip'])) {
|
||||
throw new ValidationException("ext {$name} type is invalid");
|
||||
}
|
||||
// check if external ext has source
|
||||
|
||||
// Check source field requirement
|
||||
if (($ext['type'] ?? '') === 'external' && !isset($ext['source'])) {
|
||||
throw new ValidationException("ext {$name} does not assign any source");
|
||||
}
|
||||
// check if source is string
|
||||
if (isset($ext['source']) && !is_string($ext['source'])) {
|
||||
throw new ValidationException("ext {$name} source must be string");
|
||||
}
|
||||
// check if support is valid
|
||||
if (isset($ext['support']) && !is_assoc_array($ext['support'])) {
|
||||
throw new ValidationException("ext {$name} support must be an object");
|
||||
}
|
||||
// check if notes is boolean
|
||||
if (isset($ext['notes']) && !is_bool($ext['notes'])) {
|
||||
throw new ValidationException("ext {$name} notes must be boolean");
|
||||
}
|
||||
// check if [lib-depends|lib-suggests|ext-depends][-windows|-unix|-macos|-linux] are valid list array
|
||||
|
||||
// Validate basic fields using unified method
|
||||
self::validateConfigFields($ext, $name, 'ext', self::EXT_FIELDS);
|
||||
|
||||
// Validate list array fields with suffixes
|
||||
$suffixes = ['', '-windows', '-unix', '-macos', '-linux'];
|
||||
foreach ($suffixes as $suffix) {
|
||||
if (isset($ext['lib-depends' . $suffix]) && !is_list_array($ext['lib-depends' . $suffix])) {
|
||||
throw new ValidationException("ext {$name} lib-depends must be a list");
|
||||
}
|
||||
if (isset($ext['lib-suggests' . $suffix]) && !is_list_array($ext['lib-suggests' . $suffix])) {
|
||||
throw new ValidationException("ext {$name} lib-suggests must be a list");
|
||||
}
|
||||
if (isset($ext['ext-depends' . $suffix]) && !is_list_array($ext['ext-depends' . $suffix])) {
|
||||
throw new ValidationException("ext {$name} ext-depends must be a list");
|
||||
}
|
||||
}
|
||||
// check if arg-type is valid
|
||||
if (isset($ext['arg-type'])) {
|
||||
$valid_arg_types = ['enable', 'with', 'with-path', 'custom', 'none', 'enable-path'];
|
||||
if (!in_array($ext['arg-type'], $valid_arg_types)) {
|
||||
throw new ValidationException("ext {$name} arg-type is invalid");
|
||||
}
|
||||
}
|
||||
// check if arg-type with suffix is valid
|
||||
foreach ($suffixes as $suffix) {
|
||||
if (isset($ext['arg-type' . $suffix])) {
|
||||
$valid_arg_types = ['enable', 'with', 'with-path', 'custom', 'none', 'enable-path'];
|
||||
if (!in_array($ext['arg-type' . $suffix], $valid_arg_types)) {
|
||||
throw new ValidationException("ext {$name} arg-type{$suffix} is invalid");
|
||||
}
|
||||
}
|
||||
}
|
||||
$fields = ['lib-depends', 'lib-suggests', 'ext-depends', 'ext-suggests'];
|
||||
self::validateListArrayFields($ext, $name, 'ext', $fields, $suffixes);
|
||||
|
||||
// Validate arg-type fields
|
||||
self::validateArgTypeFields($ext, $name, $suffixes);
|
||||
|
||||
// Check for unknown fields
|
||||
self::validateAllowedFields($ext, $name, 'ext', self::EXT_FIELDS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,12 +287,11 @@ class ConfigValidator
|
||||
// Validate basic source type configuration (reuse from source validation)
|
||||
self::validateSourceTypeConfig($pkg, $name, 'pkg');
|
||||
|
||||
// Check pkg-specific fields
|
||||
// check if extract-files is valid
|
||||
// Validate all pkg-specific fields using unified method
|
||||
self::validateConfigFields($pkg, $name, 'pkg', self::PKG_FIELDS);
|
||||
|
||||
// Validate extract-files content (object validation is done by validateFieldType)
|
||||
if (isset($pkg['extract-files'])) {
|
||||
if (!is_assoc_array($pkg['extract-files'])) {
|
||||
throw new ValidationException("pkg {$name} extract-files must be an object");
|
||||
}
|
||||
// check each extract file mapping
|
||||
foreach ($pkg['extract-files'] as $source => $target) {
|
||||
if (!is_string($source) || !is_string($target)) {
|
||||
@@ -213,6 +299,9 @@ class ConfigValidator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for unknown fields
|
||||
self::validateAllowedFields($pkg, $name, 'pkg', self::PKG_FIELDS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,18 +316,11 @@ class ConfigValidator
|
||||
throw new ValidationException('pre-built.json is broken');
|
||||
}
|
||||
|
||||
// Check required fields
|
||||
if (!isset($data['repo'])) {
|
||||
throw new ValidationException('pre-built.json must have [repo] field');
|
||||
}
|
||||
if (!is_string($data['repo'])) {
|
||||
throw new ValidationException('pre-built.json [repo] must be string');
|
||||
}
|
||||
// Validate all fields using unified method
|
||||
self::validateConfigFields($data, 'pre-built', 'pre-built', self::PRE_BUILT_FIELDS);
|
||||
|
||||
// Check optional prefer-stable field
|
||||
if (isset($data['prefer-stable']) && !is_bool($data['prefer-stable'])) {
|
||||
throw new ValidationException('pre-built.json [prefer-stable] must be boolean');
|
||||
}
|
||||
// Check for unknown fields
|
||||
self::validateAllowedFields($data, 'pre-built', 'pre-built', self::PRE_BUILT_FIELDS);
|
||||
|
||||
// Check match pattern fields (at least one must exist)
|
||||
$pattern_fields = ['match-pattern-linux', 'match-pattern-macos', 'match-pattern-windows'];
|
||||
@@ -247,9 +329,6 @@ class ConfigValidator
|
||||
foreach ($pattern_fields as $field) {
|
||||
if (isset($data[$field])) {
|
||||
$has_pattern = true;
|
||||
if (!is_string($data[$field])) {
|
||||
throw new ValidationException("pre-built.json [{$field}] must be string");
|
||||
}
|
||||
// Validate pattern contains required placeholders
|
||||
if (!str_contains($data[$field], '{name}')) {
|
||||
throw new ValidationException("pre-built.json [{$field}] must contain {name} placeholder");
|
||||
@@ -403,6 +482,52 @@ class ConfigValidator
|
||||
return $craft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a field based on its global type definition
|
||||
*
|
||||
* @param string $field Field name
|
||||
* @param mixed $value Field value
|
||||
* @param string $name Item name (for error messages)
|
||||
* @param string $type Item type (for error messages)
|
||||
* @return bool Returns true if validation passes
|
||||
*/
|
||||
private static function validateFieldType(string $field, mixed $value, string $name, string $type): bool
|
||||
{
|
||||
// Check if field exists in FIELD_TYPES
|
||||
if (!isset(self::FIELD_TYPES[$field])) {
|
||||
// Try to strip suffix and check base field name
|
||||
$suffixes = ['-windows', '-unix', '-macos', '-linux'];
|
||||
$base_field = $field;
|
||||
foreach ($suffixes as $suffix) {
|
||||
if (str_ends_with($field, $suffix)) {
|
||||
$base_field = substr($field, 0, -strlen($suffix));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset(self::FIELD_TYPES[$base_field])) {
|
||||
// Unknown field is not allowed - strict validation
|
||||
throw new ValidationException("{$type} {$name} has unknown field [{$field}]");
|
||||
}
|
||||
|
||||
// Use base field type for validation
|
||||
$expected_type = self::FIELD_TYPES[$base_field];
|
||||
} else {
|
||||
$expected_type = self::FIELD_TYPES[$field];
|
||||
}
|
||||
|
||||
return match ($expected_type) {
|
||||
'string' => is_string($value) ?: throw new ValidationException("{$type} {$name} [{$field}] must be string"),
|
||||
'bool' => is_bool($value) ?: throw new ValidationException("{$type} {$name} [{$field}] must be boolean"),
|
||||
'array' => is_array($value) ?: throw new ValidationException("{$type} {$name} [{$field}] must be array"),
|
||||
'list' => is_list_array($value) ?: throw new ValidationException("{$type} {$name} [{$field}] must be a list"),
|
||||
'object' => is_assoc_array($value) ?: throw new ValidationException("{$type} {$name} [{$field}] must be an object"),
|
||||
'object|bool' => (is_assoc_array($value) || is_bool($value)) ?: throw new ValidationException("{$type} {$name} [{$field}] must be object or boolean"),
|
||||
'object|array' => is_array($value) ?: throw new ValidationException("{$type} {$name} [{$field}] must be an object or array"),
|
||||
'callable' => true, // Skip validation for callable
|
||||
};
|
||||
}
|
||||
|
||||
private static function checkSingleLicense(array $license, string $name): void
|
||||
{
|
||||
if (!is_assoc_array($license)) {
|
||||
@@ -414,9 +539,6 @@ class ConfigValidator
|
||||
if (!in_array($license['type'], ['file', 'text'])) {
|
||||
throw new ValidationException("source {$name} license type is invalid");
|
||||
}
|
||||
if (!in_array($license['type'], ['file', 'text'])) {
|
||||
throw new ValidationException("source {$name} license type is invalid");
|
||||
}
|
||||
if ($license['type'] === 'file' && !isset($license['path'])) {
|
||||
throw new ValidationException("source {$name} license file must have path");
|
||||
}
|
||||
@@ -440,68 +562,127 @@ class ConfigValidator
|
||||
if (!is_string($item['type'])) {
|
||||
throw new ValidationException("{$config_type} {$name} type prop must be string");
|
||||
}
|
||||
if (!in_array($item['type'], ['filelist', 'git', 'ghtagtar', 'ghtar', 'ghrel', 'url', 'custom'])) {
|
||||
|
||||
if (!isset(self::SOURCE_TYPE_FIELDS[$item['type']])) {
|
||||
throw new ValidationException("{$config_type} {$name} type [{$item['type']}] is invalid");
|
||||
}
|
||||
|
||||
// Validate type-specific requirements
|
||||
switch ($item['type']) {
|
||||
case 'filelist':
|
||||
if (!isset($item['url'], $item['regex'])) {
|
||||
throw new ValidationException("{$config_type} {$name} needs [url] and [regex] props");
|
||||
[$required, $optional] = self::SOURCE_TYPE_FIELDS[$item['type']];
|
||||
|
||||
// Check required fields exist
|
||||
foreach ($required as $prop) {
|
||||
if (!isset($item[$prop])) {
|
||||
$props = implode('] and [', $required);
|
||||
throw new ValidationException("{$config_type} {$name} needs [{$props}] props");
|
||||
}
|
||||
}
|
||||
|
||||
// Validate field types using global field type definitions
|
||||
foreach (array_merge($required, $optional) as $prop) {
|
||||
if (isset($item[$prop])) {
|
||||
self::validateFieldType($prop, $item[$prop], $name, $config_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that fields with suffixes are list arrays
|
||||
*/
|
||||
private static function validateListArrayFields(array $item, string $name, string $type, array $fields, array $suffixes): void
|
||||
{
|
||||
foreach ($fields as $field) {
|
||||
foreach ($suffixes as $suffix) {
|
||||
$key = $field . $suffix;
|
||||
if (isset($item[$key])) {
|
||||
self::validateFieldType($key, $item[$key], $name, $type);
|
||||
}
|
||||
if (!is_string($item['url']) || !is_string($item['regex'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [url] and [regex] must be string");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate arg-type fields with suffixes
|
||||
*/
|
||||
private static function validateArgTypeFields(array $item, string $name, array $suffixes): void
|
||||
{
|
||||
$valid_arg_types = ['enable', 'with', 'with-path', 'custom', 'none', 'enable-path'];
|
||||
|
||||
foreach (array_merge([''], $suffixes) as $suffix) {
|
||||
$key = 'arg-type' . $suffix;
|
||||
if (isset($item[$key]) && !in_array($item[$key], $valid_arg_types)) {
|
||||
throw new ValidationException("ext {$name} {$key} is invalid");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unified method to validate config fields based on field definitions
|
||||
*
|
||||
* @param array $item Item data to validate
|
||||
* @param string $name Item name for error messages
|
||||
* @param string $type Config type (source, lib, ext, pkg, pre-built)
|
||||
* @param array $field_definitions Field definitions [field_name => required (bool)]
|
||||
*/
|
||||
private static function validateConfigFields(array $item, string $name, string $type, array $field_definitions): void
|
||||
{
|
||||
foreach ($field_definitions as $field => $required) {
|
||||
if ($required && !isset($item[$field])) {
|
||||
throw new ValidationException("{$type} {$name} must have [{$field}] field");
|
||||
}
|
||||
|
||||
if (isset($item[$field])) {
|
||||
self::validateFieldType($field, $item[$field], $name, $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that item only contains allowed fields
|
||||
* This method checks for unknown fields based on the config type
|
||||
*
|
||||
* @param array $item Item data to validate
|
||||
* @param string $name Item name for error messages
|
||||
* @param string $type Config type (source, lib, ext, pkg, pre-built)
|
||||
* @param array $field_definitions Field definitions [field_name => required (bool)]
|
||||
*/
|
||||
private static function validateAllowedFields(array $item, string $name, string $type, array $field_definitions): void
|
||||
{
|
||||
// For source and pkg types, we need to check SOURCE_TYPE_FIELDS as well
|
||||
$allowed_fields = array_keys($field_definitions);
|
||||
|
||||
// For source/pkg, add allowed fields from SOURCE_TYPE_FIELDS based on the type
|
||||
if (in_array($type, ['source', 'pkg']) && isset($item['type'], self::SOURCE_TYPE_FIELDS[$item['type']])) {
|
||||
[$required, $optional] = self::SOURCE_TYPE_FIELDS[$item['type']];
|
||||
$allowed_fields = array_merge($allowed_fields, $required, $optional);
|
||||
}
|
||||
|
||||
// For lib and ext types, add fields with suffixes
|
||||
if (in_array($type, ['lib', 'ext'])) {
|
||||
$suffixes = ['-windows', '-unix', '-macos', '-linux'];
|
||||
$base_fields = ['lib-depends', 'lib-suggests', 'static-libs', 'pkg-configs', 'headers', 'bin'];
|
||||
if ($type === 'ext') {
|
||||
$base_fields = ['lib-depends', 'lib-suggests', 'ext-depends', 'ext-suggests'];
|
||||
// Add arg-type fields
|
||||
foreach (array_merge([''], $suffixes) as $suffix) {
|
||||
$allowed_fields[] = 'arg-type' . $suffix;
|
||||
}
|
||||
break;
|
||||
case 'git':
|
||||
if (!isset($item['url'], $item['rev'])) {
|
||||
throw new ValidationException("{$config_type} {$name} needs [url] and [rev] props");
|
||||
}
|
||||
foreach ($base_fields as $field) {
|
||||
foreach ($suffixes as $suffix) {
|
||||
$allowed_fields[] = $field . $suffix;
|
||||
}
|
||||
if (!is_string($item['url']) || !is_string($item['rev'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [url] and [rev] must be string");
|
||||
}
|
||||
if (isset($item['path']) && !is_string($item['path'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [path] must be string");
|
||||
}
|
||||
break;
|
||||
case 'ghtagtar':
|
||||
case 'ghtar':
|
||||
if (!isset($item['repo'])) {
|
||||
throw new ValidationException("{$config_type} {$name} needs [repo] prop");
|
||||
}
|
||||
if (!is_string($item['repo'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [repo] must be string");
|
||||
}
|
||||
if (isset($item['path']) && !is_string($item['path'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [path] must be string");
|
||||
}
|
||||
break;
|
||||
case 'ghrel':
|
||||
if (!isset($item['repo'], $item['match'])) {
|
||||
throw new ValidationException("{$config_type} {$name} needs [repo] and [match] props");
|
||||
}
|
||||
if (!is_string($item['repo']) || !is_string($item['match'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [repo] and [match] must be string");
|
||||
}
|
||||
break;
|
||||
case 'url':
|
||||
if (!isset($item['url'])) {
|
||||
throw new ValidationException("{$config_type} {$name} needs [url] prop");
|
||||
}
|
||||
if (!is_string($item['url'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [url] must be string");
|
||||
}
|
||||
if (isset($item['filename']) && !is_string($item['filename'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [filename] must be string");
|
||||
}
|
||||
if (isset($item['path']) && !is_string($item['path'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [path] must be string");
|
||||
}
|
||||
break;
|
||||
case 'custom':
|
||||
// custom type has no specific requirements
|
||||
break;
|
||||
}
|
||||
// frameworks is lib-only
|
||||
if ($type === 'lib') {
|
||||
$allowed_fields[] = 'frameworks';
|
||||
}
|
||||
}
|
||||
|
||||
// Check each field in item
|
||||
foreach (array_keys($item) as $field) {
|
||||
if (!in_array($field, $allowed_fields)) {
|
||||
throw new ValidationException("{$type} {$name} has unknown field [{$field}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,12 @@ class GlobalEnvManager
|
||||
if (is_unix()) {
|
||||
self::addPathIfNotExists(BUILD_BIN_PATH);
|
||||
self::addPathIfNotExists(PKG_ROOT_PATH . '/bin');
|
||||
self::putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . '/pkgconfig');
|
||||
$pkgConfigPath = getenv('PKG_CONFIG_PATH');
|
||||
if ($pkgConfigPath !== false) {
|
||||
self::putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . "/pkgconfig:{$pkgConfigPath}");
|
||||
} else {
|
||||
self::putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . '/pkgconfig');
|
||||
}
|
||||
}
|
||||
|
||||
$ini = self::readIniFile();
|
||||
|
||||
@@ -117,7 +117,7 @@ class LicenseDumper
|
||||
/**
|
||||
* Loads a source license file from the specified path.
|
||||
*/
|
||||
private function loadSourceFile(string $source_name, int $index, null|array|string $in_path, ?string $custom_base_path = null): string
|
||||
private function loadSourceFile(string $source_name, int $index, array|string|null $in_path, ?string $custom_base_path = null): string
|
||||
{
|
||||
if (is_null($in_path)) {
|
||||
throw new SPCInternalException("source [{$source_name}] license file is not set, please check config/source.json");
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace SPC\util;
|
||||
|
||||
use SPC\builder\BuilderBase;
|
||||
use SPC\builder\BuilderProvider;
|
||||
use SPC\builder\Extension;
|
||||
use SPC\builder\LibraryBase;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
@@ -52,6 +54,9 @@ class SPCConfigUtil
|
||||
*/
|
||||
public function config(array $extensions = [], array $libraries = [], bool $include_suggest_ext = false, bool $include_suggest_lib = false): array
|
||||
{
|
||||
logger()->debug('config extensions: ' . implode(',', $extensions));
|
||||
logger()->debug('config libs: ' . implode(',', $libraries));
|
||||
logger()->debug('config suggest for [ext, lib]: ' . ($include_suggest_ext ? 'true' : 'false') . ',' . ($include_suggest_lib ? 'true' : 'false'));
|
||||
$extra_exts = [];
|
||||
foreach ($extensions as $ext) {
|
||||
$extra_exts = array_merge($extra_exts, Config::getExt($ext, 'ext-suggests', []));
|
||||
@@ -87,7 +92,7 @@ class SPCConfigUtil
|
||||
if (SPCTarget::getTargetOS() === 'Darwin') {
|
||||
$libs .= " {$this->getFrameworksString($extensions)}";
|
||||
}
|
||||
if ($this->builder->hasCpp()) {
|
||||
if ($this->hasCpp($extensions, $libraries)) {
|
||||
$libcpp = SPCTarget::getTargetOS() === 'Darwin' ? '-lc++' : '-lstdc++';
|
||||
$libs = str_replace($libcpp, '', $libs) . " {$libcpp}";
|
||||
}
|
||||
@@ -123,6 +128,84 @@ class SPCConfigUtil
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* [Helper function]
|
||||
* Get configuration for a specific extension(s) dependencies.
|
||||
*
|
||||
* @param Extension|Extension[] $extension Extension instance or list
|
||||
* @param bool $include_suggest_ext Whether to include suggested extensions
|
||||
* @param bool $include_suggest_lib Whether to include suggested libraries
|
||||
* @return array{
|
||||
* cflags: string,
|
||||
* ldflags: string,
|
||||
* libs: string
|
||||
* }
|
||||
*/
|
||||
public function getExtensionConfig(array|Extension $extension, bool $include_suggest_ext = false, bool $include_suggest_lib = false): array
|
||||
{
|
||||
if (!is_array($extension)) {
|
||||
$extension = [$extension];
|
||||
}
|
||||
$libs = array_map(fn ($y) => $y->getName(), array_merge(...array_map(fn ($x) => $x->getLibraryDependencies(true), $extension)));
|
||||
return $this->config(
|
||||
extensions: array_map(fn ($x) => $x->getName(), $extension),
|
||||
libraries: $libs,
|
||||
include_suggest_ext: $include_suggest_ext ?: $this->builder?->getOption('with-suggested-exts') ?? false,
|
||||
include_suggest_lib: $include_suggest_lib ?: $this->builder?->getOption('with-suggested-libs') ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* [Helper function]
|
||||
* Get configuration for a specific library(s) dependencies.
|
||||
*
|
||||
* @param LibraryBase|LibraryBase[] $lib Library instance or list
|
||||
* @param bool $include_suggest_lib Whether to include suggested libraries
|
||||
* @return array{
|
||||
* cflags: string,
|
||||
* ldflags: string,
|
||||
* libs: string
|
||||
* }
|
||||
*/
|
||||
public function getLibraryConfig(array|LibraryBase $lib, bool $include_suggest_lib = false): array
|
||||
{
|
||||
if (!is_array($lib)) {
|
||||
$lib = [$lib];
|
||||
}
|
||||
$save_no_php = $this->no_php;
|
||||
$this->no_php = true;
|
||||
$save_libs_only_deps = $this->libs_only_deps;
|
||||
$this->libs_only_deps = true;
|
||||
$ret = $this->config(
|
||||
libraries: array_map(fn ($x) => $x->getName(), $lib),
|
||||
include_suggest_lib: $include_suggest_lib ?: $this->builder?->getOption('with-suggested-libs') ?? false,
|
||||
);
|
||||
$this->no_php = $save_no_php;
|
||||
$this->libs_only_deps = $save_libs_only_deps;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
private function hasCpp(array $extensions, array $libraries): bool
|
||||
{
|
||||
// judge cpp-extension
|
||||
$builderExtNames = array_keys($this->builder->getExts(false));
|
||||
$exts = array_unique([...$builderExtNames, ...$extensions]);
|
||||
|
||||
foreach ($exts as $ext) {
|
||||
if (Config::getExt($ext, 'cpp-extension', false) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$builderLibNames = array_keys($this->builder->getLibs());
|
||||
$libs = array_unique([...$builderLibNames, ...$libraries]);
|
||||
foreach ($libs as $lib) {
|
||||
if (Config::getLib($lib, 'cpp-library', false) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getIncludesString(array $libraries): string
|
||||
{
|
||||
$base = BUILD_INCLUDE_PATH;
|
||||
|
||||
@@ -50,18 +50,22 @@ class UnixAutoconfExecutor extends Executor
|
||||
* @param bool $with_clean Whether to clean before building
|
||||
* @param array $after_env_vars Environment variables postfix
|
||||
*/
|
||||
public function make(string $target = '', false|string $with_install = 'install', bool $with_clean = true, array $after_env_vars = []): static
|
||||
public function make(string $target = '', false|string $with_install = 'install', bool $with_clean = true, array $after_env_vars = [], ?string $dir = null): static
|
||||
{
|
||||
return $this->seekLogFileOnException(function () use ($target, $with_install, $with_clean, $after_env_vars) {
|
||||
return $this->seekLogFileOnException(function () use ($target, $with_install, $with_clean, $after_env_vars, $dir) {
|
||||
$shell = $this->shell;
|
||||
if ($dir) {
|
||||
$shell = $shell->cd($dir);
|
||||
}
|
||||
if ($with_clean) {
|
||||
$this->shell->exec('make clean');
|
||||
$shell->exec('make clean');
|
||||
}
|
||||
$after_env_vars_str = $after_env_vars !== [] ? shell()->setEnv($after_env_vars)->getEnvString() : '';
|
||||
$this->shell->exec("make -j{$this->library->getBuilder()->concurrency} {$target} {$after_env_vars_str}");
|
||||
$shell->exec("make -j{$this->library->getBuilder()->concurrency} {$target} {$after_env_vars_str}");
|
||||
if ($with_install !== false) {
|
||||
$this->shell->exec("make {$with_install}");
|
||||
$shell->exec("make {$with_install}");
|
||||
}
|
||||
return $this->shell;
|
||||
return $shell;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -111,6 +115,12 @@ class UnixAutoconfExecutor extends Executor
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setEnv(array $env): static
|
||||
{
|
||||
$this->shell->setEnv($env);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function appendEnv(array $env): static
|
||||
{
|
||||
$this->shell->appendEnv($env);
|
||||
|
||||
@@ -205,7 +205,7 @@ SET(CMAKE_INSTALL_PREFIX "{$root}")
|
||||
SET(CMAKE_INSTALL_LIBDIR "lib")
|
||||
|
||||
set(PKG_CONFIG_EXECUTABLE "{$pkgConfigExecutable}")
|
||||
list(APPEND PKG_CONFIG_EXECUTABLE "--static")
|
||||
set(PKG_CONFIG_ARGN "--static" CACHE STRING "Extra arguments for pkg-config" FORCE)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
|
||||
@@ -8,6 +8,7 @@ use SPC\builder\freebsd\library\BSDLibraryBase;
|
||||
use SPC\builder\linux\library\LinuxLibraryBase;
|
||||
use SPC\builder\macos\library\MacOSLibraryBase;
|
||||
use SPC\exception\SPCInternalException;
|
||||
use SPC\util\SPCTarget;
|
||||
use ZM\Logger\ConsoleColor;
|
||||
|
||||
/**
|
||||
@@ -28,6 +29,7 @@ class UnixShell extends Shell
|
||||
|
||||
public function exec(string $cmd): static
|
||||
{
|
||||
$cmd = clean_spaces($cmd);
|
||||
/* @phpstan-ignore-next-line */
|
||||
logger()->info(ConsoleColor::yellow('[EXEC] ') . ConsoleColor::green($cmd));
|
||||
$original_command = $cmd;
|
||||
@@ -48,7 +50,7 @@ class UnixShell extends Shell
|
||||
'CFLAGS' => $library->getLibExtraCFlags(),
|
||||
'CXXFLAGS' => $library->getLibExtraCXXFlags(),
|
||||
'LDFLAGS' => $library->getLibExtraLdFlags(),
|
||||
'LIBS' => $library->getLibExtraLibs(),
|
||||
'LIBS' => $library->getLibExtraLibs() . SPCTarget::getRuntimeLibs(),
|
||||
]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,8 @@ const BUILD_TARGET_MICRO = 2; // build micro
|
||||
const BUILD_TARGET_FPM = 4; // build fpm
|
||||
const BUILD_TARGET_EMBED = 8; // build embed
|
||||
const BUILD_TARGET_FRANKENPHP = 16; // build frankenphp
|
||||
const BUILD_TARGET_ALL = BUILD_TARGET_CLI | BUILD_TARGET_MICRO | BUILD_TARGET_FPM | BUILD_TARGET_EMBED | BUILD_TARGET_FRANKENPHP; // build all
|
||||
const BUILD_TARGET_CGI = 32; // build cgi
|
||||
const BUILD_TARGET_ALL = BUILD_TARGET_CLI | BUILD_TARGET_MICRO | BUILD_TARGET_FPM | BUILD_TARGET_EMBED | BUILD_TARGET_FRANKENPHP | BUILD_TARGET_CGI; // build all
|
||||
|
||||
// doctor error fix policy
|
||||
const FIX_POLICY_DIE = 1; // die directly
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user