mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-02 22:35:43 +08:00
Compare commits
65 Commits
2.1.0-beta
...
2.1.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9664709f21 | ||
|
|
b46655ecfe | ||
|
|
842e0add29 | ||
|
|
254764761d | ||
|
|
5f6c1a0f40 | ||
|
|
d5dcd193cf | ||
|
|
5012da96be | ||
|
|
df0e37cd0f | ||
|
|
4ab7b6bfdc | ||
|
|
f498250001 | ||
|
|
3945ac037b | ||
|
|
b0d8b00fcc | ||
|
|
62b0bf8af0 | ||
|
|
905e080770 | ||
|
|
097ecd3fb0 | ||
|
|
b9359759dd | ||
|
|
50fe366c42 | ||
|
|
9dd89e6b02 | ||
|
|
c03220d1ee | ||
|
|
9db843ab66 | ||
|
|
304973d9bc | ||
|
|
ab386f820c | ||
|
|
4adf1f5e2e | ||
|
|
b4ae87585c | ||
|
|
ae3298472d | ||
|
|
d241cb993e | ||
|
|
8376122634 | ||
|
|
a30e054d7d | ||
|
|
78f4317660 | ||
|
|
3ac3dab6c8 | ||
|
|
1a7e436ee1 | ||
|
|
f11b36ab3c | ||
|
|
e2ef195a84 | ||
|
|
645e2a9fc2 | ||
|
|
d59b8457c6 | ||
|
|
a2d1262cbf | ||
|
|
2f9a1e8601 | ||
|
|
b239f60fe4 | ||
|
|
983521e225 | ||
|
|
aeed04a5ec | ||
|
|
faed569e8a | ||
|
|
49ddb3ec13 | ||
|
|
71c0387ab0 | ||
|
|
e5d2d5e689 | ||
|
|
be2394b39b | ||
|
|
158298b96c | ||
|
|
ffa84f8b91 | ||
|
|
0954ddcc96 | ||
|
|
d9bd96af71 | ||
|
|
2649dcd05c | ||
|
|
939db75268 | ||
|
|
227bf73870 | ||
|
|
05e3898e7a | ||
|
|
9777c9aa93 | ||
|
|
b8d8461e61 | ||
|
|
b977543c72 | ||
|
|
4d87cd11cc | ||
|
|
957daf0547 | ||
|
|
3dae904122 | ||
|
|
e835196972 | ||
|
|
52ed0e2cee | ||
|
|
1e898d271d | ||
|
|
39754cde59 | ||
|
|
a6f7b938e1 | ||
|
|
0ad501af9a |
53
.github/workflows/release-build.yml
vendored
53
.github/workflows/release-build.yml
vendored
@@ -15,14 +15,15 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.1"
|
||||
- "8.2"
|
||||
micro-version:
|
||||
- "8.1.26"
|
||||
- "8.2.16"
|
||||
operating-system:
|
||||
- "linux-x86_64"
|
||||
- "macos-x86_64"
|
||||
- "linux-aarch64"
|
||||
- "macos-aarch64"
|
||||
- "windows-x64"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
@@ -59,31 +60,51 @@ jobs:
|
||||
|
||||
- name: "Download minimal combination"
|
||||
run: |
|
||||
curl https://dl.static-php.dev/static-php-cli/minimal/php-${{ matrix.micro-version }}-micro-${{ matrix.operating-system }}.tar.gz -o tmp.tgz
|
||||
tar -zxvf tmp.tgz
|
||||
|
||||
if [ "${{ matrix.operating-system }}" = "windows-x64" ]; then
|
||||
curl https://dl.static-php.dev/static-php-cli/windows/spc-min/php-${{ matrix.micro-version }}-micro-win.zip -o tmp.zip
|
||||
unzip tmp.zip
|
||||
else
|
||||
curl https://dl.static-php.dev/static-php-cli/minimal/php-${{ matrix.micro-version }}-micro-${{ matrix.operating-system }}.tar.gz -o tmp.tgz
|
||||
tar -zxvf tmp.tgz
|
||||
fi
|
||||
|
||||
- name: "Generate Executable"
|
||||
run: |
|
||||
cat micro.sfx spc.phar > spc
|
||||
chmod +x spc
|
||||
bin/spc micro:combine spc.phar -M micro.sfx -O spc -I "memory_limit=2G"
|
||||
if [ "${{ matrix.operating-system }}" = "windows-x64" ]; then
|
||||
mv spc spc.exe
|
||||
else
|
||||
chmod +x spc
|
||||
fi
|
||||
|
||||
- name: "Archive Executable"
|
||||
run: |
|
||||
tar -czf spc-${{ matrix.operating-system }}.tar.gz spc
|
||||
echo "filename=spc-${{ matrix.operating-system }}.tar.gz" >> $GITHUB_ENV
|
||||
echo "OS=${{ matrix.operating-system }}" >> $GITHUB_ENV
|
||||
if [ "${{ matrix.operating-system }}" == "linux-x86_64" ]; then
|
||||
./spc dev:extensions
|
||||
if [ "${{ matrix.operating-system }}" != "windows-x64" ]; then
|
||||
tar -czf spc-${{ matrix.operating-system }}.tar.gz spc
|
||||
echo "filename=spc-${{ matrix.operating-system }}.tar.gz" >> $GITHUB_ENV
|
||||
echo "OS=${{ matrix.operating-system }}" >> $GITHUB_ENV
|
||||
if [ "${{ matrix.operating-system }}" == "linux-x86_64" ]; then
|
||||
./spc dev:extensions
|
||||
fi
|
||||
else
|
||||
echo "filename=spc-${{ matrix.operating-system }}.exe" >> $GITHUB_ENV
|
||||
echo "OS=${{ matrix.operating-system }}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: "Copy file"
|
||||
run: "mkdir dist/ && cp ${{ env.filename }} dist/ && cp spc dist/spc-$OS"
|
||||
run: |
|
||||
if [ "${{ matrix.operating-system }}" != "windows-x64" ]; then
|
||||
mkdir dist/ && cp ${{ env.filename }} dist/ && cp spc dist/spc-$OS
|
||||
else
|
||||
mkdir dist/ && cp spc.exe dist/${{ env.filename }}
|
||||
echo "SUFFIX=.exe" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: upload binaries to release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: ${{startsWith(github.ref, 'refs/tags/') }}
|
||||
with:
|
||||
files: ${{ env.filename }}
|
||||
files: dist/${{ env.filename }}
|
||||
|
||||
- name: "Deploy to Self-Hosted Server"
|
||||
if: github.repository == 'crazywhalecc/static-php-cli'
|
||||
@@ -100,5 +121,5 @@ jobs:
|
||||
- name: "Upload Artifact"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: spc
|
||||
name: spc-${{ matrix.operating-system }}
|
||||
path: spc${{ env.SUFFIX }}
|
||||
name: spc-${{ matrix.operating-system }}${{ env.SUFFIX }}
|
||||
|
||||
14
.github/workflows/tests.yml
vendored
14
.github/workflows/tests.yml
vendored
@@ -129,6 +129,13 @@ jobs:
|
||||
extensions: curl, openssl, mbstring
|
||||
ini-values: memory_limit=-1
|
||||
|
||||
- name: "Use test token if exists"
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: |
|
||||
if [ "${{ secrets.TEST_GH_TOKEN }}" != "" ]; then
|
||||
echo "GITHUB_TOKEN=${{ secrets.TEST_GH_TOKEN }}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: "Cache Composer packages"
|
||||
id: composer-cache
|
||||
uses: actions/cache@v3
|
||||
@@ -152,7 +159,12 @@ jobs:
|
||||
run: bin/spc doctor --auto-fix
|
||||
|
||||
- name: "Run Build Tests (download)"
|
||||
run: bin/spc download --for-extensions="$(php src/globals/test-extensions.php extensions)" --with-php=${{ matrix.php }} --debug
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
retry_on: error
|
||||
command: bin/spc download --for-extensions="$(php src/globals/test-extensions.php extensions)" --with-php=${{ matrix.php }} --debug
|
||||
|
||||
- name: "Run Build Tests (build)"
|
||||
run: bin/spc build "$(php src/globals/test-extensions.php extensions)" $(php src/globals/test-extensions.php libs_cmd) --build-cli --build-micro --build-fpm --debug
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,6 +16,9 @@ docker/source/
|
||||
# default source build root directory
|
||||
/buildroot/
|
||||
|
||||
# default package root directory
|
||||
/pkgroot/
|
||||
|
||||
# tools cache files
|
||||
.php-cs-fixer.cache
|
||||
.phpunit.result.cache
|
||||
|
||||
63
README-zh.md
63
README-zh.md
@@ -1,10 +1,11 @@
|
||||
# static-php-cli
|
||||
|
||||
[]()
|
||||
[](https://github.com/crazywhalecc/static-php-cli/actions/workflows/tests.yml)
|
||||
[]()
|
||||
[]([https://static-php.dev/](https://static-php.dev/en/guide/extensions.html))
|
||||
[](https://discord.gg/RNpegEYW)
|
||||
[](README.md)
|
||||
[](README-zh.md)
|
||||
[](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 解释器的工具,支持众多流行扩展。
|
||||
|
||||
@@ -12,9 +13,6 @@
|
||||
|
||||
**static-php-cli**也支持将 PHP 代码和 PHP 运行时打包为一个文件并运行。
|
||||
|
||||
- [README - English](./README.md)
|
||||
- [README - 中文](./README-zh.md)
|
||||
|
||||
## 特性
|
||||
|
||||
static-php-cli(简称 `spc`)有许多特性:
|
||||
@@ -22,11 +20,12 @@ static-php-cli(简称 `spc`)有许多特性:
|
||||
- :handbag: 构建独立的单文件 PHP 解释器,无需任何依赖
|
||||
- :hamburger: 构建 **[phpmicro](https://github.com/dixyes/phpmicro)** 自执行二进制(将 PHP 代码和 PHP 解释器打包为一个文件)
|
||||
- :pill: 提供一键检查和修复编译环境的 Doctor 模块
|
||||
- :zap: 支持多个系统:`Linux`、`macOS`、`FreeBSD`、[`Windows (WIP)`](https://github.com/crazywhalecc/static-php-cli/pull/301)
|
||||
- :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:**
|
||||
|
||||
@@ -48,16 +47,18 @@ static-php-cli(简称 `spc`)有许多特性:
|
||||
- [扩展组合 - bulk](https://dl.static-php.dev/static-php-cli/bulk/):bulk 组合包含了 [50+](https://dl.static-php.dev/static-php-cli/bulk/README.txt) 个扩展,体积为 70MB 左右。
|
||||
- [扩展组合 - minimal](https://dl.static-php.dev/static-php-cli/minimal/):minimal 组合包含了 [5](https://dl.static-php.dev/static-php-cli/minimal/README.txt) 个扩展,体积为 6MB 左右。
|
||||
|
||||
对于 Windows 系统,目前支持的扩展较少,故仅提供 SPC 自身运行的最小扩展组合的 `cli` 和 `micro`:[扩展组合 - spc-min](https://dl.static-php.dev/static-php-cli/windows/spc-min/)。
|
||||
|
||||
## 使用 static-php-cli 构建 PHP
|
||||
|
||||
### 编译环境需求
|
||||
|
||||
- PHP >= 8.1(这是 spc 自身需要的版本,不是支持的构建版本)
|
||||
- 扩展:`mbstring,pcntl,posix,tokenizer,phar`
|
||||
- 扩展:`mbstring,tokenizer,phar`
|
||||
- 系统安装了 `curl` 和 `git`
|
||||
|
||||
是的,本项目采用 PHP 编写,编译前需要一个 PHP 环境,比较滑稽。
|
||||
但本项目默认可通过自身构建的 micro 和 static-php 二进制运行,其他只需要包含 mbstring、pcntl 扩展和 PHP 版本大于等于 8.1 即可。
|
||||
但本项目默认可通过自身构建的 micro 和 static-php 二进制运行,其他只需要包含上面提到的扩展和 PHP 版本大于等于 8.1 即可。
|
||||
|
||||
下面是架构支持情况,:octocat: 代表支持 GitHub Action 构建,:computer: 代表支持本地构建,空 代表暂不支持。
|
||||
|
||||
@@ -65,10 +66,26 @@ static-php-cli(简称 `spc`)有许多特性:
|
||||
|---------|----------------------|----------------------|
|
||||
| macOS | :octocat: :computer: | :octocat: :computer: |
|
||||
| Linux | :octocat: :computer: | :octocat: :computer: |
|
||||
| Windows | | |
|
||||
| Windows | :computer: | |
|
||||
| FreeBSD | :computer: | :computer: |
|
||||
|
||||
目前支持编译的 PHP 版本为:`7.3`,`7.4`,`8.0`,`8.1`,`8.2`,`8.3`。
|
||||
当前支持编译的 PHP 版本:
|
||||
|
||||
> :warning: 支持,但可能不再提供修复
|
||||
>
|
||||
> :heavy_check_mark: 支持
|
||||
>
|
||||
> :x: 不支持
|
||||
|
||||
| PHP Version | Status | Comment |
|
||||
|-------------|--------------------|------------------------------|
|
||||
| 7.2 | :x: | |
|
||||
| 7.3 | :warning: | phpmicro 和许多扩展不支持 7.3、7.4 版本 |
|
||||
| 7.4 | :warning: | phpmicro 和许多扩展不支持 7.3、7.4 版本 |
|
||||
| 8.0 | :heavy_check_mark: | PHP 官方已停止 8.0 的维护 |
|
||||
| 8.1 | :heavy_check_mark: | |
|
||||
| 8.2 | :heavy_check_mark: | |
|
||||
| 8.3 | :heavy_check_mark: | |
|
||||
|
||||
### 支持的扩展
|
||||
|
||||
@@ -108,10 +125,16 @@ curl -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-a
|
||||
curl -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-x86_64
|
||||
# macOS aarch64 (Apple)
|
||||
curl -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 -o spc.exe https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-windows-x64.exe
|
||||
|
||||
# add x perm
|
||||
# Add execute perm (Linux and macOS only)
|
||||
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)。
|
||||
@@ -150,14 +173,16 @@ bin/spc --version
|
||||
# 拉取所有依赖库
|
||||
./bin/spc download --all
|
||||
# 只拉取编译指定扩展需要的所有依赖(推荐)
|
||||
./bin/spc download --for-extensions=openssl,pcntl,mbstring,pdo_sqlite
|
||||
./bin/spc download --for-extensions="openssl,pcntl,mbstring,pdo_sqlite"
|
||||
# 下载编译不同版本的 PHP (--with-php=x.y,推荐 7.3 ~ 8.3)
|
||||
./bin/spc download --for-extensions=openssl,curl,mbstring --with-php=8.1
|
||||
./bin/spc download --for-extensions="openssl,curl,mbstring" --with-php=8.1
|
||||
|
||||
# 构建包含 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
|
||||
./bin/spc build "curl,phar" --enable-zts --build-cli
|
||||
# 编译后使用 UPX 减小可执行文件体积 (--with-upx-pack) (至少压缩至原来的 30~50%)
|
||||
./bin/spc build "curl,phar" --enable-zts --build-cli --with-upx-pack
|
||||
```
|
||||
|
||||
其中,目前支持构建 cli,micro,fpm 和 embed,使用以下参数的一个或多个来指定编译的 SAPI:
|
||||
@@ -171,7 +196,7 @@ bin/spc --version
|
||||
如果出现了任何错误,可以使用 `--debug` 参数来展示完整的输出日志,以供排查错误:
|
||||
|
||||
```bash
|
||||
./bin/spc build openssl,pcntl,mbstring --debug --build-all
|
||||
./bin/spc build "openssl,pcntl,mbstring" --debug --build-all
|
||||
./bin/spc download --all --debug
|
||||
```
|
||||
|
||||
@@ -250,7 +275,7 @@ bin/spc micro:combine my-app.phar -I "memory_limit=4G" -I "disable_functions=sys
|
||||
|
||||
## 赞助本项目
|
||||
|
||||
你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。
|
||||
你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。你捐赠的一部分将会被用于维护 **static-php.dev** 服务器。
|
||||
|
||||
## 开源协议
|
||||
|
||||
|
||||
70
README.md
70
README.md
@@ -1,10 +1,11 @@
|
||||
# static-php-cli
|
||||
|
||||
[]()
|
||||
[](https://github.com/crazywhalecc/static-php-cli/actions/workflows/tests.yml)
|
||||
[]()
|
||||
[]([https://static-php.dev/](https://static-php.dev/en/guide/extensions.html))
|
||||
[](https://discord.gg/RNpegEYW)
|
||||
[](README-zh.md)
|
||||
[](README.md)
|
||||
[](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.
|
||||
@@ -14,9 +15,6 @@ Static PHP built by **static-php-cli** supports `cli`, `fpm`, `embed` and `micro
|
||||
**static-php-cli** also has the ability to package PHP projects
|
||||
along with the PHP interpreter into one single executable file.
|
||||
|
||||
- [README - English](./README.md)
|
||||
- [README - 中文](./README-zh.md)
|
||||
|
||||
## Features
|
||||
|
||||
static-php-cli (you can call it `spc`) has a lot of features:
|
||||
@@ -29,6 +27,7 @@ static-php-cli (you can call it `spc`) has a lot of features:
|
||||
- :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)
|
||||
|
||||
**Single-file standalone php-cli:**
|
||||
|
||||
@@ -54,16 +53,19 @@ which can be downloaded directly according to your needs.
|
||||
- [Extension-Combination - bulk](https://dl.static-php.dev/static-php-cli/bulk/): `bulk` contains [50+](https://dl.static-php.dev/static-php-cli/bulk/README.txt) extensions and is about 70MB in size.
|
||||
- [Extension-Combination - minimal](https://dl.static-php.dev/static-php-cli/minimal/): `minimal` contains [5](https://dl.static-php.dev/static-php-cli/minimal/README.txt) extensions and is about 6MB in size.
|
||||
|
||||
For Windows systems, there are currently fewer extensions supported,
|
||||
so only `cli` and `micro` that run the minimum extension combination of SPC itself are provided: [Extension-Combination - spc-min](https://dl.static-php.dev/static-php-cli/windows/spc-min/).
|
||||
|
||||
## Build
|
||||
|
||||
### Compilation Requirements
|
||||
|
||||
- PHP >= 8.1 (This is the version required by spc itself, not the build version)
|
||||
- Extension: `mbstring,pcntl,posix,tokenizer,phar`
|
||||
- Supported OS with `curl` and `git` installed
|
||||
|
||||
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 `mbstring`, `pcntl` extension.
|
||||
But static-php-cli runtime only requires an environment above PHP 8.1 and extensions mentioned below.
|
||||
|
||||
- PHP >= 8.1 (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.
|
||||
@@ -75,7 +77,23 @@ Here is the supported OS and arch, where :octocat: represents support for GitHub
|
||||
| Windows | :computer: | |
|
||||
| FreeBSD | :computer: | :computer: |
|
||||
|
||||
Currently supported PHP versions for compilation are: `7.3`, `7.4`, `8.0`, `8.1`, `8.2`, `8.3`.
|
||||
Currently supported PHP versions for compilation:
|
||||
|
||||
> :warning: supported but not maintained
|
||||
>
|
||||
> :heavy_check_mark: supported
|
||||
>
|
||||
> :x: not supported
|
||||
|
||||
| PHP Version | Status | Comment |
|
||||
|-------------|--------------------|---------------------------------------------------|
|
||||
| 7.2 | :x: | |
|
||||
| 7.3 | :warning: | phpmicro and some extensions not supported on 7.x |
|
||||
| 7.4 | :warning: | phpmicro and some extensions not supported on 7.x |
|
||||
| 8.0 | :heavy_check_mark: | PHP official has stopped maintenance of 8.0 |
|
||||
| 8.1 | :heavy_check_mark: | |
|
||||
| 8.2 | :heavy_check_mark: | |
|
||||
| 8.3 | :heavy_check_mark: | |
|
||||
|
||||
### Supported Extensions
|
||||
|
||||
@@ -118,10 +136,16 @@ curl -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-a
|
||||
curl -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-x86_64
|
||||
# macOS aarch64 (Apple)
|
||||
curl -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 -o spc.exe https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-windows-x64.exe
|
||||
|
||||
# add x perm
|
||||
# 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).
|
||||
@@ -151,7 +175,7 @@ bin/spc --version
|
||||
|
||||
Basic usage for building php with some extensions:
|
||||
|
||||
> If you are using the packaged `spc` binary, you need to replace `bin/spc` with `./spc` in the following commands.
|
||||
> 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
|
||||
@@ -160,14 +184,16 @@ Basic usage for building php with some extensions:
|
||||
# fetch all libraries
|
||||
./bin/spc download --all
|
||||
# only fetch necessary sources by needed extensions (recommended)
|
||||
./bin/spc download --for-extensions=openssl,pcntl,mbstring,pdo_sqlite
|
||||
./bin/spc download --for-extensions="openssl,pcntl,mbstring,pdo_sqlite"
|
||||
# download different PHP version (--with-php=x.y, recommend 7.3 ~ 8.3)
|
||||
./bin/spc download --for-extensions=openssl,curl,mbstring --with-php=8.1
|
||||
./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
|
||||
./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
|
||||
./bin/spc build "curl,phar" --enable-zts --build-cli
|
||||
# build, pack executable with UPX (--with-upx-pack) (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:
|
||||
@@ -181,7 +207,7 @@ Now we support `cli`, `micro`, `fpm` and `embed` SAPI. You can use one or more o
|
||||
If anything goes wrong, use `--debug` option to display full terminal output:
|
||||
|
||||
```bash
|
||||
./bin/spc build openssl,pcntl,mbstring --debug --build-all
|
||||
./bin/spc build "openssl,pcntl,mbstring" --debug --build-all
|
||||
./bin/spc download --all --debug
|
||||
```
|
||||
|
||||
@@ -274,7 +300,7 @@ Now there is a [static-php](https://github.com/static-php) organization, which i
|
||||
|
||||
## Sponsor this project
|
||||
|
||||
You can sponsor my project on [this page](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md).
|
||||
You can sponsor my project on [this page](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md). A portion of your donation will be used to maintain the **static-php.dev** server.
|
||||
|
||||
## Open-Source License
|
||||
|
||||
|
||||
1
box.json
1
box.json
@@ -14,6 +14,5 @@
|
||||
"vendor/zhamao"
|
||||
],
|
||||
"git-commit-short": "git_commit_short",
|
||||
"metadata": "ConsoleApplication::VERSION",
|
||||
"output": "spc.phar"
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
"captainhook/captainhook": "^5.10",
|
||||
"captainhook/plugin-composer": "^5.3",
|
||||
"friendsofphp/php-cs-fixer": "^3.25",
|
||||
"humbug/box": "^4.3",
|
||||
"humbug/box": "^4.5",
|
||||
"nunomaduro/collision": "^7.8",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpunit/phpunit": "^10.3"
|
||||
"phpunit/phpunit": "^10.3 || ^9"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
||||
940
composer.lock
generated
940
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,10 @@
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"curl"
|
||||
],
|
||||
"ext-depends-windows": [
|
||||
"zlib",
|
||||
"openssl"
|
||||
]
|
||||
},
|
||||
"dba": {
|
||||
@@ -59,8 +63,11 @@
|
||||
"ffi": {
|
||||
"arg-type": "custom",
|
||||
"type": "builtin",
|
||||
"lib-depends": [
|
||||
"lib-depends-unix": [
|
||||
"libffi"
|
||||
],
|
||||
"lib-depends-windows": [
|
||||
"libffi-win"
|
||||
]
|
||||
},
|
||||
"fileinfo": {
|
||||
@@ -95,7 +102,7 @@
|
||||
},
|
||||
"gettext": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"arg-type": "with-prefix",
|
||||
"lib-depends": [
|
||||
"gettext"
|
||||
]
|
||||
@@ -119,8 +126,12 @@
|
||||
"iconv": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with-prefix",
|
||||
"lib-depends": [
|
||||
"arg-type-windows": "with",
|
||||
"lib-depends-unix": [
|
||||
"libiconv"
|
||||
],
|
||||
"lib-depends-windows": [
|
||||
"libiconv-win"
|
||||
]
|
||||
},
|
||||
"igbinary": {
|
||||
@@ -313,13 +324,6 @@
|
||||
"type": "external",
|
||||
"source": "protobuf"
|
||||
},
|
||||
"pspell": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"aspell"
|
||||
]
|
||||
},
|
||||
"rar": {
|
||||
"type": "external",
|
||||
"source": "rar",
|
||||
@@ -354,9 +358,11 @@
|
||||
"simplexml": {
|
||||
"type": "builtin",
|
||||
"arg-type": "custom",
|
||||
"arg-type-windows": "with",
|
||||
"lib-depends": [
|
||||
"libxml2"
|
||||
],
|
||||
"ext-depends-windows": [
|
||||
"xml"
|
||||
]
|
||||
},
|
||||
"snappy": {
|
||||
@@ -371,18 +377,14 @@
|
||||
"apcu"
|
||||
]
|
||||
},
|
||||
"snmp": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"net-snmp"
|
||||
]
|
||||
},
|
||||
"soap": {
|
||||
"type": "builtin",
|
||||
"arg-type": "custom",
|
||||
"lib-depends": [
|
||||
"libxml2"
|
||||
],
|
||||
"ext-depends-windows": [
|
||||
"xml"
|
||||
]
|
||||
},
|
||||
"sockets": {
|
||||
@@ -398,6 +400,7 @@
|
||||
"sqlite3": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with-prefix",
|
||||
"arg-type-windows": "with",
|
||||
"lib-depends": [
|
||||
"sqlite"
|
||||
]
|
||||
@@ -417,8 +420,13 @@
|
||||
"type": "external",
|
||||
"source": "ext-ssh2",
|
||||
"arg-type": "with-prefix",
|
||||
"arg-type-windows": "with",
|
||||
"lib-depends": [
|
||||
"libssh2"
|
||||
],
|
||||
"ext-depends-windows": [
|
||||
"openssl",
|
||||
"zlib"
|
||||
]
|
||||
},
|
||||
"swoole": {
|
||||
@@ -493,8 +501,7 @@
|
||||
"unix-only": true
|
||||
},
|
||||
"sysvshm": {
|
||||
"type": "builtin",
|
||||
"unix-only": true
|
||||
"type": "builtin"
|
||||
},
|
||||
"tidy": {
|
||||
"type": "builtin",
|
||||
@@ -539,6 +546,9 @@
|
||||
"arg-type-windows": "with",
|
||||
"lib-depends": [
|
||||
"libxml2"
|
||||
],
|
||||
"ext-depends-windows": [
|
||||
"iconv"
|
||||
]
|
||||
},
|
||||
"xmlreader": {
|
||||
@@ -546,6 +556,10 @@
|
||||
"arg-type": "custom",
|
||||
"lib-depends": [
|
||||
"libxml2"
|
||||
],
|
||||
"ext-depends-windows": [
|
||||
"xml",
|
||||
"dom"
|
||||
]
|
||||
},
|
||||
"xmlwriter": {
|
||||
@@ -553,6 +567,9 @@
|
||||
"arg-type": "custom",
|
||||
"lib-depends": [
|
||||
"libxml2"
|
||||
],
|
||||
"ext-depends-windows": [
|
||||
"xml"
|
||||
]
|
||||
},
|
||||
"xsl": {
|
||||
|
||||
@@ -45,19 +45,21 @@
|
||||
"openssl",
|
||||
"zlib"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"lib-depends-windows": [
|
||||
"openssl",
|
||||
"zlib",
|
||||
"libssh2",
|
||||
"nghttp2"
|
||||
],
|
||||
"lib-suggests-unix": [
|
||||
"libssh2",
|
||||
"brotli",
|
||||
"nghttp2",
|
||||
"zstd"
|
||||
],
|
||||
"lib-suggests-windows": [
|
||||
"zlib",
|
||||
"libssh2",
|
||||
"brotli",
|
||||
"nghttp2",
|
||||
"zstd",
|
||||
"openssl"
|
||||
"zstd"
|
||||
],
|
||||
"frameworks": [
|
||||
"CoreFoundation",
|
||||
@@ -83,6 +85,22 @@
|
||||
"brotli"
|
||||
]
|
||||
},
|
||||
"gettext": {
|
||||
"source": "gettext",
|
||||
"static-libs-unix": [
|
||||
"libintl.a"
|
||||
],
|
||||
"lib-depends": [
|
||||
"libiconv"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"ncurses",
|
||||
"libxml2"
|
||||
],
|
||||
"frameworks": [
|
||||
"CoreFoundation"
|
||||
]
|
||||
},
|
||||
"glfw": {
|
||||
"source": "ext-glfw",
|
||||
"static-libs-unix": [
|
||||
@@ -129,7 +147,8 @@
|
||||
"libpng",
|
||||
"libjpeg",
|
||||
"libwebp",
|
||||
"freetype"
|
||||
"freetype",
|
||||
"libtiff"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"zstd",
|
||||
@@ -215,6 +234,17 @@
|
||||
"ffitarget.h"
|
||||
]
|
||||
},
|
||||
"libffi-win": {
|
||||
"source": "libffi-win",
|
||||
"static-libs-windows": [
|
||||
"libffi.lib"
|
||||
],
|
||||
"headers-windows": [
|
||||
"ffi.h",
|
||||
"ffitarget.h",
|
||||
"fficonfig.h"
|
||||
]
|
||||
},
|
||||
"libiconv": {
|
||||
"source": "libiconv",
|
||||
"static-libs-unix": [
|
||||
@@ -227,6 +257,13 @@
|
||||
"localcharset.h"
|
||||
]
|
||||
},
|
||||
"libiconv-win": {
|
||||
"source": "libiconv-win",
|
||||
"static-libs-windows": [
|
||||
"libiconv.lib",
|
||||
"libiconv_a.lib"
|
||||
]
|
||||
},
|
||||
"libjpeg": {
|
||||
"source": "libjpeg",
|
||||
"static-libs-unix": [
|
||||
@@ -294,6 +331,12 @@
|
||||
"zlib"
|
||||
]
|
||||
},
|
||||
"libtiff": {
|
||||
"source": "libtiff",
|
||||
"static-libs-unix": [
|
||||
"libtiff.a"
|
||||
]
|
||||
},
|
||||
"libuv": {
|
||||
"source": "libuv",
|
||||
"static-libs-unix": [
|
||||
@@ -316,21 +359,25 @@
|
||||
"libxml2.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
[
|
||||
"libxml2s.lib",
|
||||
"libxml2_a.lib"
|
||||
]
|
||||
"libxml2s.lib",
|
||||
"libxml2_a.lib"
|
||||
],
|
||||
"headers": [
|
||||
"libxml2"
|
||||
],
|
||||
"lib-depends": [
|
||||
"lib-depends-unix": [
|
||||
"libiconv"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"lib-suggests-unix": [
|
||||
"xz",
|
||||
"icu",
|
||||
"zlib"
|
||||
],
|
||||
"lib-depends-windows": [
|
||||
"libiconv-win"
|
||||
],
|
||||
"lib-suggests-windows": [
|
||||
"zlib"
|
||||
]
|
||||
},
|
||||
"libxslt": {
|
||||
@@ -411,10 +458,8 @@
|
||||
"libonig.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
[
|
||||
"onig.lib",
|
||||
"onig_a.lib"
|
||||
]
|
||||
"onig.lib",
|
||||
"onig_a.lib"
|
||||
],
|
||||
"headers": [
|
||||
"oniggnu.h",
|
||||
@@ -491,7 +536,10 @@
|
||||
"static-libs-unix": [
|
||||
"libsqlite3.a"
|
||||
],
|
||||
"headers-unix": [
|
||||
"static-libs-windows": [
|
||||
"libsqlite3_a.lib"
|
||||
],
|
||||
"headers": [
|
||||
"sqlite3.h",
|
||||
"sqlite3ext.h"
|
||||
]
|
||||
|
||||
46
config/pkg.json
Normal file
46
config/pkg.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"musl-toolchain-aarch64-linux": {
|
||||
"type": "url",
|
||||
"url": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/aarch64-musl-toolchain.tgz"
|
||||
},
|
||||
"musl-toolchain-x86_64-linux": {
|
||||
"type": "url",
|
||||
"url": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/x86_64-musl-toolchain.tgz"
|
||||
},
|
||||
"nasm-x86_64-win": {
|
||||
"type": "url",
|
||||
"url": "https://www.nasm.us/pub/nasm/releasebuilds/2.16.01/win64/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"
|
||||
}
|
||||
},
|
||||
"strawberry-perl-x86_64-win": {
|
||||
"type": "url",
|
||||
"url": "https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/download/SP_5380_5361/strawberry-perl-5.38.0.1-64bit-portable.zip"
|
||||
},
|
||||
"upx-aarch64-linux": {
|
||||
"type": "ghrel",
|
||||
"repo": "upx/upx",
|
||||
"match": "upx.+-arm64_linux\\.tar\\.xz",
|
||||
"extract-files": {
|
||||
"upx": "{pkg_root_path}/bin/upx"
|
||||
}
|
||||
},
|
||||
"upx-x86_64-linux": {
|
||||
"type": "ghrel",
|
||||
"repo": "upx/upx",
|
||||
"match": "upx.+-amd64_linux\\.tar\\.xz",
|
||||
"extract-files": {
|
||||
"upx": "{pkg_root_path}/bin/upx"
|
||||
}
|
||||
},
|
||||
"upx-x86_64-win": {
|
||||
"type": "ghrel",
|
||||
"repo": "upx/upx",
|
||||
"match": "upx.+-win64\\.zip",
|
||||
"extract-files": {
|
||||
"upx-*-win64/upx.exe": "{pkg_root_path}/bin/upx.exe"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,6 +129,15 @@
|
||||
"path": "LICENSE.TXT"
|
||||
}
|
||||
},
|
||||
"gettext": {
|
||||
"type": "filelist",
|
||||
"url": "https://ftp.gnu.org/pub/gnu/gettext/",
|
||||
"regex": "/href=\"(?<file>gettext-(?<version>[^\"]+)\\.tar\\.xz)\"/",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "COPYING"
|
||||
}
|
||||
},
|
||||
"gmp": {
|
||||
"type": "ghtagtar",
|
||||
"repo": "alisw/GMP",
|
||||
@@ -236,6 +245,15 @@
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"libffi-win": {
|
||||
"type": "git",
|
||||
"rev": "master",
|
||||
"url": "https://github.com/static-php/libffi-win.git",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"libiconv": {
|
||||
"type": "filelist",
|
||||
"url": "https://ftp.gnu.org/gnu/libiconv/",
|
||||
@@ -245,6 +263,15 @@
|
||||
"path": "COPYING"
|
||||
}
|
||||
},
|
||||
"libiconv-win": {
|
||||
"type": "git",
|
||||
"rev": "master",
|
||||
"url": "https://github.com/static-php/libiconv-win.git",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "source/COPYING"
|
||||
}
|
||||
},
|
||||
"libjpeg": {
|
||||
"type": "ghtar",
|
||||
"repo": "libjpeg-turbo/libjpeg-turbo",
|
||||
@@ -298,6 +325,15 @@
|
||||
"path": "COPYING"
|
||||
}
|
||||
},
|
||||
"libtiff": {
|
||||
"type": "filelist",
|
||||
"url": "https://download.osgeo.org/libtiff/",
|
||||
"regex": "/href=\"(?<file>tiff-(?<version>[^\"]+)\\.tar\\.xz)\"/",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "LICENSE.md"
|
||||
}
|
||||
},
|
||||
"libuv": {
|
||||
"type": "ghtar",
|
||||
"repo": "libuv/libuv",
|
||||
@@ -322,7 +358,7 @@
|
||||
},
|
||||
"libxml2": {
|
||||
"type": "url",
|
||||
"url": "https://github.com/GNOME/libxml2/archive/refs/tags/v2.9.14.tar.gz",
|
||||
"url": "https://github.com/GNOME/libxml2/archive/refs/tags/v2.12.5.tar.gz",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "Copyright"
|
||||
@@ -431,7 +467,7 @@
|
||||
},
|
||||
"postgresql": {
|
||||
"type": "url",
|
||||
"url": "https://ftp.postgresql.org/pub/source/v16.1/postgresql-16.1.tar.gz",
|
||||
"url": "https://ftp.postgresql.org/pub/source/v16.2/postgresql-16.2.tar.bz2",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "COPYRIGHT"
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace SPC;
|
||||
|
||||
use SPC\command\BuildCliCommand;
|
||||
use SPC\command\BuildLibsCommand;
|
||||
use SPC\command\DeleteDownloadCommand;
|
||||
use SPC\command\dev\AllExtCommand;
|
||||
use SPC\command\dev\PhpVerCommand;
|
||||
use SPC\command\dev\SortConfigCommand;
|
||||
@@ -13,6 +14,7 @@ use SPC\command\DoctorCommand;
|
||||
use SPC\command\DownloadCommand;
|
||||
use SPC\command\DumpLicenseCommand;
|
||||
use SPC\command\ExtractCommand;
|
||||
use SPC\command\InstallPkgCommand;
|
||||
use SPC\command\MicroCombineCommand;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Command\HelpCommand;
|
||||
@@ -23,7 +25,7 @@ use Symfony\Component\Console\Command\ListCommand;
|
||||
*/
|
||||
final class ConsoleApplication extends Application
|
||||
{
|
||||
public const VERSION = '2.1.0-beta.2';
|
||||
public const VERSION = '2.1.2';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -35,6 +37,8 @@ final class ConsoleApplication extends Application
|
||||
new BuildLibsCommand(),
|
||||
new DoctorCommand(),
|
||||
new DownloadCommand(),
|
||||
new InstallPkgCommand(),
|
||||
new DeleteDownloadCommand(),
|
||||
new DumpLicenseCommand(),
|
||||
new ExtractCommand(),
|
||||
new MicroCombineCommand(),
|
||||
|
||||
@@ -9,7 +9,7 @@ use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\SourceExtractor;
|
||||
use SPC\store\SourceManager;
|
||||
use SPC\util\CustomExt;
|
||||
|
||||
abstract class BuilderBase
|
||||
@@ -144,15 +144,15 @@ abstract class BuilderBase
|
||||
{
|
||||
CustomExt::loadCustomExt();
|
||||
$this->emitPatchPoint('before-php-extract');
|
||||
SourceExtractor::initSource(sources: ['php-src']);
|
||||
SourceManager::initSource(sources: ['php-src']);
|
||||
$this->emitPatchPoint('after-php-extract');
|
||||
if ($this->getPHPVersionID() >= 80000) {
|
||||
$this->emitPatchPoint('before-micro-extract');
|
||||
SourceExtractor::initSource(sources: ['micro']);
|
||||
SourceManager::initSource(sources: ['micro']);
|
||||
$this->emitPatchPoint('after-micro-extract');
|
||||
}
|
||||
$this->emitPatchPoint('before-exts-extract');
|
||||
SourceExtractor::initSource(exts: $extensions);
|
||||
SourceManager::initSource(exts: $extensions);
|
||||
$this->emitPatchPoint('after-exts-extract');
|
||||
foreach ($extensions as $extension) {
|
||||
$class = CustomExt::getExtClass($extension);
|
||||
@@ -231,6 +231,30 @@ abstract class BuilderBase
|
||||
throw new RuntimeException('PHP version file format is malformed, please remove it and download again');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PHP version from archive file name.
|
||||
*
|
||||
* @param null|string $file php-*.*.*.tar.gz filename, read from lockfile if empty
|
||||
*/
|
||||
public function getPHPVersionFromArchive(?string $file = null): false|string
|
||||
{
|
||||
if ($file === null) {
|
||||
$lock = file_exists(DOWNLOAD_PATH . '/.lock.json') ? file_get_contents(DOWNLOAD_PATH . '/.lock.json') : false;
|
||||
if ($lock === false) {
|
||||
return false;
|
||||
}
|
||||
$lock = json_decode($lock, true);
|
||||
$file = $lock['php-src']['filename'] ?? null;
|
||||
if ($file === null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (preg_match('/php-(\d+\.\d+\.\d+)/', $file, $match)) {
|
||||
return $match[1];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get build type name string to display.
|
||||
*
|
||||
|
||||
@@ -185,8 +185,11 @@ class Extension
|
||||
file_get_contents(ROOT_DIR . '/src/globals/tests/' . $this->getName() . '.php')
|
||||
);
|
||||
|
||||
[$ret] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -r "' . trim($test) . '"');
|
||||
[$ret, $out] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -r "' . trim($test) . '"');
|
||||
if ($ret !== 0) {
|
||||
if ($this->builder->getOption('debug')) {
|
||||
var_dump($out);
|
||||
}
|
||||
throw new RuntimeException('extension ' . $this->getName() . ' failed sanity check');
|
||||
}
|
||||
}
|
||||
@@ -209,7 +212,7 @@ class Extension
|
||||
// Trim additional content & escape special characters to allow inline usage
|
||||
$test = str_replace(
|
||||
['<?php', 'declare(strict_types=1);', "\n", '"', '$'],
|
||||
['', '', '', '\"', '\$'],
|
||||
['', '', '', '\"', '$'],
|
||||
file_get_contents(FileSystem::convertPath(ROOT_DIR . '/src/globals/tests/' . $this->getName() . '.php'))
|
||||
);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\builder\macos\MacOSBuilder;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\CustomExt;
|
||||
@@ -34,4 +35,16 @@ class event extends Extension
|
||||
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/-levent_openssl/', $this->getLibFilesString());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function patchBeforeMake(): bool
|
||||
{
|
||||
// Prevent event extension compile error on macOS
|
||||
if ($this->builder instanceof MacOSBuilder) {
|
||||
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_OPENPTY 1$/m', '');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,9 @@ class ffi extends Extension
|
||||
{
|
||||
return '--with-ffi --enable-zend-signals';
|
||||
}
|
||||
|
||||
public function getWindowsConfigureArg(): string
|
||||
{
|
||||
return '--with-ffi';
|
||||
}
|
||||
}
|
||||
|
||||
40
src/SPC/builder/extension/gettext.php
Normal file
40
src/SPC/builder/extension/gettext.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\builder\macos\MacOSBuilder;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\CustomExt;
|
||||
|
||||
#[CustomExt('gettext')]
|
||||
class gettext extends Extension
|
||||
{
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
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');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws WrongUsageException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function patchBeforeConfigure(): bool
|
||||
{
|
||||
if ($this->builder instanceof MacOSBuilder) {
|
||||
$frameworks = ' ' . $this->builder->getFrameworks(true) . ' ';
|
||||
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/configure', '-lintl', $this->getLibFilesString() . $frameworks);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -31,4 +31,16 @@ class mbregex extends Extension
|
||||
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: compiled php-cli mbstring extension does not contain regex !');
|
||||
}
|
||||
}
|
||||
|
||||
public function runCliCheckWindows(): void
|
||||
{
|
||||
[$ret, $out] = cmd()->execWithResult(BUILD_ROOT_PATH . '/bin/php --ri "mbstring"', false);
|
||||
if ($ret !== 0) {
|
||||
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: compiled php-cli does not contain mbstring !');
|
||||
}
|
||||
$out = implode("\n", $out);
|
||||
if (!str_contains($out, 'regex')) {
|
||||
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: compiled php-cli mbstring extension does not contain regex !');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ class mbstring extends Extension
|
||||
$arg = '--enable-mbstring';
|
||||
if ($this->builder->getExt('mbregex') === null) {
|
||||
$arg .= ' --disable-mbregex';
|
||||
} else {
|
||||
$arg .= ' --enable-mbregex';
|
||||
}
|
||||
return $arg;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\CustomExt;
|
||||
|
||||
#[CustomExt('xml')]
|
||||
@@ -33,4 +34,25 @@ class xml extends Extension
|
||||
$arg .= ' --with-libxml="' . BUILD_ROOT_PATH . '"';
|
||||
return $arg;
|
||||
}
|
||||
|
||||
public function patchBeforeBuildconf(): bool
|
||||
{
|
||||
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/win32/build/config.w32', 'dllmain.c ', '');
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getWindowsConfigureArg(): string
|
||||
{
|
||||
$arg = match ($this->name) {
|
||||
'xml' => '--with-xml',
|
||||
'soap' => '--enable-soap',
|
||||
'xmlreader' => '--enable-xmlreader',
|
||||
'xmlwriter' => '--enable-xmlwriter',
|
||||
'dom' => '--with-dom',
|
||||
'simplexml' => '--with-simplexml',
|
||||
default => throw new RuntimeException('Not accept non-xml extension'),
|
||||
};
|
||||
$arg .= ' --with-libxml';
|
||||
return $arg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +169,27 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
|
||||
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
|
||||
|
||||
// upx pack and strip for micro
|
||||
if ($this->getOption('with-upx-pack', false)) {
|
||||
FileSystem::replaceFileRegex(
|
||||
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
|
||||
'/POST_MICRO_BUILD_COMMANDS=.*/',
|
||||
'POST_MICRO_BUILD_COMMANDS=\$(STRIP) \$(MICRO_STRIP_FLAGS) \$(SAPI_MICRO_PATH) && ' . $this->getOption('upx-exec') . ' --best \$(SAPI_MICRO_PATH)',
|
||||
);
|
||||
} elseif (!$this->getOption('no-strip', false)) {
|
||||
FileSystem::replaceFileRegex(
|
||||
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
|
||||
'/POST_MICRO_BUILD_COMMANDS=.*/',
|
||||
'POST_MICRO_BUILD_COMMANDS=\$(STRIP) \$(MICRO_STRIP_FLAGS) \$(SAPI_MICRO_PATH)',
|
||||
);
|
||||
} else {
|
||||
FileSystem::replaceFileRegex(
|
||||
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
|
||||
'/POST_MICRO_BUILD_COMMANDS=.*/',
|
||||
'POST_MICRO_BUILD_COMMANDS=true',
|
||||
);
|
||||
}
|
||||
|
||||
shell()->cd(SOURCE_PATH . '/php-src')
|
||||
->exec(
|
||||
"{$this->getOption('ld_library_path')} " .
|
||||
@@ -236,7 +257,11 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
->exec('sed -i "s|//lib|/lib|g" Makefile')
|
||||
->exec("make -j{$this->concurrency} {$vars} cli");
|
||||
|
||||
if (!$this->getOption('no-strip', false)) {
|
||||
if ($this->getOption('with-upx-pack')) {
|
||||
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')
|
||||
->exec('strip --strip-all php')
|
||||
->exec($this->getOption('upx-exec') . ' --best php');
|
||||
} elseif (!$this->getOption('no-strip', false)) {
|
||||
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')->exec('strip --strip-all php');
|
||||
}
|
||||
|
||||
@@ -267,10 +292,6 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
->exec('sed -i "s|//lib|/lib|g" Makefile')
|
||||
->exec("make -j{$this->concurrency} {$vars} micro");
|
||||
|
||||
if (!$this->getOption('no-strip', false)) {
|
||||
shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec('strip --strip-all micro.sfx');
|
||||
}
|
||||
|
||||
$this->deployBinary(BUILD_TARGET_MICRO);
|
||||
|
||||
if ($this->phar_patched) {
|
||||
@@ -291,10 +312,13 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
->exec('sed -i "s|//lib|/lib|g" Makefile')
|
||||
->exec("make -j{$this->concurrency} {$vars} fpm");
|
||||
|
||||
if (!$this->getOption('no-strip', false)) {
|
||||
if ($this->getOption('with-upx-pack')) {
|
||||
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')
|
||||
->exec('strip --strip-all php-fpm')
|
||||
->exec($this->getOption('upx-exec') . ' --best php-fpm');
|
||||
} elseif (!$this->getOption('no-strip', false)) {
|
||||
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')->exec('strip --strip-all php-fpm');
|
||||
}
|
||||
|
||||
$this->deployBinary(BUILD_TARGET_FPM);
|
||||
}
|
||||
|
||||
|
||||
@@ -110,11 +110,7 @@ class SystemUtil
|
||||
public static function getTuneCFlags(string $arch): array
|
||||
{
|
||||
return match ($arch) {
|
||||
'x86_64' => [
|
||||
'-march=corei7',
|
||||
'-mtune=core-avx2',
|
||||
],
|
||||
'arm64', 'aarch64' => [],
|
||||
'x86_64', 'arm64', 'aarch64' => [],
|
||||
default => throw new RuntimeException('unsupported arch: ' . $arch),
|
||||
};
|
||||
}
|
||||
|
||||
12
src/SPC/builder/linux/library/gettext.php
Normal file
12
src/SPC/builder/linux/library/gettext.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\linux\library;
|
||||
|
||||
class gettext extends LinuxLibraryBase
|
||||
{
|
||||
use \SPC\builder\unix\library\gettext;
|
||||
|
||||
public const NAME = 'gettext';
|
||||
}
|
||||
12
src/SPC/builder/linux/library/libtiff.php
Normal file
12
src/SPC/builder/linux/library/libtiff.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\linux\library;
|
||||
|
||||
class libtiff extends LinuxLibraryBase
|
||||
{
|
||||
use \SPC\builder\unix\library\libtiff;
|
||||
|
||||
public const NAME = 'libtiff';
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class libxml2 extends LinuxLibraryBase
|
||||
*/
|
||||
public function build(): void
|
||||
{
|
||||
$enable_zlib = $this->builder->getLib('zlib') ? 'ON' : 'OFF';
|
||||
$enable_zlib = $this->builder->getLib('zlib') ? ('ON -DZLIB_LIBRARY=' . BUILD_LIB_PATH . '/libz.a -DZLIB_INCLUDE_DIR=' . BUILD_INCLUDE_PATH) : 'OFF';
|
||||
$enable_icu = $this->builder->getLib('icu') ? 'ON' : 'OFF';
|
||||
$enable_xz = $this->builder->getLib('xz') ? 'ON' : 'OFF';
|
||||
|
||||
|
||||
12
src/SPC/builder/macos/library/gettext.php
Normal file
12
src/SPC/builder/macos/library/gettext.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class gettext extends MacOSLibraryBase
|
||||
{
|
||||
use \SPC\builder\unix\library\gettext;
|
||||
|
||||
public const NAME = 'gettext';
|
||||
}
|
||||
12
src/SPC/builder/macos/library/libtiff.php
Normal file
12
src/SPC/builder/macos/library/libtiff.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class libtiff extends MacOSLibraryBase
|
||||
{
|
||||
use \SPC\builder\unix\library\libtiff;
|
||||
|
||||
public const NAME = 'libtiff';
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class libxml2 extends MacOSLibraryBase
|
||||
*/
|
||||
protected function build(): void
|
||||
{
|
||||
$enable_zlib = $this->builder->getLib('zlib') ? 'ON' : 'OFF';
|
||||
$enable_zlib = $this->builder->getLib('zlib') ? ('ON -DZLIB_LIBRARY=' . BUILD_LIB_PATH . '/libz.a -DZLIB_INCLUDE_DIR=' . BUILD_INCLUDE_PATH) : 'OFF';
|
||||
$enable_icu = $this->builder->getLib('icu') ? 'ON' : 'OFF';
|
||||
$enable_xz = $this->builder->getLib('xz') ? 'ON' : 'OFF';
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\store\SourceExtractor;
|
||||
use SPC\store\SourceManager;
|
||||
use SPC\util\DependencyUtil;
|
||||
|
||||
abstract class UnixBuilderBase extends BuilderBase
|
||||
@@ -107,7 +107,7 @@ abstract class UnixBuilderBase extends BuilderBase
|
||||
// if no libs specified, compile all supported libs
|
||||
if ($sorted_libraries === [] && $this->isLibsOnly()) {
|
||||
$libraries = array_keys($support_lib_list);
|
||||
$sorted_libraries = DependencyUtil::getLibsByDeps($libraries);
|
||||
$sorted_libraries = DependencyUtil::getLibs($libraries);
|
||||
}
|
||||
|
||||
// pkg-config must be compiled first, whether it is specified or not
|
||||
@@ -134,7 +134,7 @@ abstract class UnixBuilderBase extends BuilderBase
|
||||
$this->emitPatchPoint('before-libs-extract');
|
||||
|
||||
// extract sources
|
||||
SourceExtractor::initSource(libs: $sorted_libraries);
|
||||
SourceManager::initSource(libs: $sorted_libraries);
|
||||
|
||||
$this->emitPatchPoint('after-libs-extract');
|
||||
|
||||
|
||||
28
src/SPC/builder/unix/library/gettext.php
Normal file
28
src/SPC/builder/unix/library/gettext.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\unix\library;
|
||||
|
||||
trait gettext
|
||||
{
|
||||
protected function build(): void
|
||||
{
|
||||
$extra = $this->builder->getLib('ncurses') ? ('--with-libncurses-prefix=' . BUILD_ROOT_PATH . ' ') : '';
|
||||
$extra .= $this->builder->getLib('libxml2') ? ('--with-libxml2-prefix=' . BUILD_ROOT_PATH . ' ') : '';
|
||||
shell()->cd($this->source_dir)
|
||||
->exec(
|
||||
'./configure ' .
|
||||
'--enable-static ' .
|
||||
'--disable-shared ' .
|
||||
'--disable-java ' .
|
||||
'--disable-c+ ' .
|
||||
$extra .
|
||||
'--with-libiconv-prefix=' . BUILD_ROOT_PATH . ' ' .
|
||||
'--prefix=' . BUILD_ROOT_PATH
|
||||
)
|
||||
->exec('make clean')
|
||||
->exec("make -j{$this->builder->concurrency}")
|
||||
->exec('make install');
|
||||
}
|
||||
}
|
||||
30
src/SPC/builder/unix/library/libtiff.php
Normal file
30
src/SPC/builder/unix/library/libtiff.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\unix\library;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
|
||||
trait libtiff
|
||||
{
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function build(): void
|
||||
{
|
||||
shell()->cd($this->source_dir)
|
||||
->exec(
|
||||
'./configure ' .
|
||||
'--enable-static --disable-shared ' .
|
||||
'--disable-cxx ' .
|
||||
'--prefix='
|
||||
)
|
||||
->exec('make clean')
|
||||
->exec("make -j{$this->builder->concurrency}")
|
||||
->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
|
||||
$this->patchPkgconfPrefix(['libtiff-4.pc']);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ trait pkgconfig
|
||||
'--disable-shared ' .
|
||||
'--enable-static ' .
|
||||
'--with-internal-glib ' .
|
||||
'--disable-host-tool ' .
|
||||
'--with-pic ' .
|
||||
'--prefix=' . BUILD_ROOT_PATH . ' ' .
|
||||
'--without-sysroot ' .
|
||||
'--without-system-include-path ' .
|
||||
@@ -29,6 +31,7 @@ trait pkgconfig
|
||||
)
|
||||
->exec('make clean')
|
||||
->exec("make -j{$this->builder->concurrency}")
|
||||
->exec('make install');
|
||||
->exec('make install-exec');
|
||||
shell()->exec('strip ' . BUILD_ROOT_PATH . '/bin/pkg-config');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,11 +58,17 @@ trait postgresql
|
||||
|
||||
FileSystem::resetDir($this->source_dir . '/build');
|
||||
|
||||
# 有静态链接配置 参考文件: 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');
|
||||
$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 RuntimeException('Unsupported version for postgresql: ' . $version . ' !');
|
||||
}
|
||||
|
||||
// configure
|
||||
shell()->cd($this->source_dir . '/build')
|
||||
@@ -101,4 +107,17 @@ trait postgresql
|
||||
->exec("rm -rf {$builddir}/lib/*.so")
|
||||
->exec("rm -rf {$builddir}/lib/*.dylib");
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\store\SourceExtractor;
|
||||
use SPC\store\SourceManager;
|
||||
use SPC\store\SourcePatcher;
|
||||
use SPC\util\DependencyUtil;
|
||||
|
||||
@@ -72,6 +72,27 @@ class WindowsBuilder extends BuilderBase
|
||||
|
||||
$zts = $this->zts ? '--enable-zts=yes ' : '--enable-zts=no ';
|
||||
|
||||
// with-upx-pack for phpmicro
|
||||
$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 = {$this->getOption('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');
|
||||
}
|
||||
|
||||
if (($logo = $this->getOption('with-micro-logo')) !== null) {
|
||||
// realpath
|
||||
$logo = realpath($logo);
|
||||
$micro_logo = '--enable-micro-logo=' . escapeshellarg($logo) . ' ';
|
||||
} else {
|
||||
$micro_logo = '';
|
||||
}
|
||||
|
||||
cmd()->cd(SOURCE_PATH . '\php-src')
|
||||
->exec(
|
||||
"{$this->sdk_prefix} configure.bat --task-args \"" .
|
||||
@@ -81,7 +102,7 @@ class WindowsBuilder extends BuilderBase
|
||||
'--with-extra-includes=' . BUILD_INCLUDE_PATH . ' ' .
|
||||
'--with-extra-libs=' . BUILD_LIB_PATH . ' ' .
|
||||
($enableCli ? '--enable-cli=yes ' : '--enable-cli=no ') .
|
||||
($enableMicro ? '--enable-micro=yes ' : '--enable-micro=no ') .
|
||||
($enableMicro ? ('--enable-micro=yes ' . $micro_logo) : '--enable-micro=no ') .
|
||||
($enableEmbed ? '--enable-embed=yes ' : '--enable-embed=no ') .
|
||||
"{$this->makeExtensionArgs()} " .
|
||||
$zts .
|
||||
@@ -150,6 +171,8 @@ class WindowsBuilder extends BuilderBase
|
||||
$makefile = FileSystem::readFile(SOURCE_PATH . '\php-src\Makefile');
|
||||
if ($this->getPHPVersionID() >= 80200 && str_contains($makefile, 'FIBER_ASM_ARCH')) {
|
||||
$makefile .= "\r\n" . '$(MICRO_SFX): $(BUILD_DIR)\Zend\jump_$(FIBER_ASM_ARCH)_ms_pe_masm.obj $(BUILD_DIR)\Zend\make_$(FIBER_ASM_ARCH)_ms_pe_masm.obj' . "\r\n\r\n";
|
||||
} elseif ($this->getPHPVersionID() >= 80400 && str_contains($makefile, 'FIBER_ASM_ABI')) {
|
||||
$makefile .= "\r\n" . '$(MICRO_SFX): $(BUILD_DIR)\Zend\jump_$(FIBER_ASM_ABI).obj $(BUILD_DIR)\Zend\make_$(FIBER_ASM_ABI).obj' . "\r\n\r\n";
|
||||
}
|
||||
FileSystem::writeFile(SOURCE_PATH . '\php-src\Makefile', $makefile);
|
||||
|
||||
@@ -190,7 +213,7 @@ class WindowsBuilder extends BuilderBase
|
||||
// if no libs specified, compile all supported libs
|
||||
if ($sorted_libraries === [] && $this->isLibsOnly()) {
|
||||
$libraries = array_keys($support_lib_list);
|
||||
$sorted_libraries = DependencyUtil::getLibsByDeps($libraries);
|
||||
$sorted_libraries = DependencyUtil::getLibs($libraries);
|
||||
}
|
||||
|
||||
// add lib object for builder
|
||||
@@ -209,7 +232,7 @@ class WindowsBuilder extends BuilderBase
|
||||
}
|
||||
|
||||
// extract sources
|
||||
SourceExtractor::initSource(libs: $sorted_libraries);
|
||||
SourceManager::initSource(libs: $sorted_libraries);
|
||||
|
||||
// build all libs
|
||||
foreach ($this->libs as $lib) {
|
||||
@@ -291,6 +314,12 @@ class WindowsBuilder extends BuilderBase
|
||||
BUILD_TARGET_MICRO => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\micro.sfx",
|
||||
default => throw new RuntimeException('Deployment does not accept type ' . $type),
|
||||
};
|
||||
|
||||
// with-upx-pack for cli
|
||||
if ($this->getOption('with-upx-pack', false) && $type === BUILD_TARGET_CLI) {
|
||||
cmd()->exec($this->getOption('upx-exec') . ' --best ' . escapeshellarg($src));
|
||||
}
|
||||
|
||||
logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file');
|
||||
FileSystem::createDir(BUILD_ROOT_PATH . '\bin');
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ use SPC\builder\BuilderBase;
|
||||
use SPC\builder\LibraryBase;
|
||||
use SPC\builder\windows\WindowsBuilder;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
abstract class WindowsLibraryBase extends LibraryBase
|
||||
@@ -22,6 +24,36 @@ abstract class WindowsLibraryBase extends LibraryBase
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true): string
|
||||
{
|
||||
$libs = [$this];
|
||||
if ($recursive) {
|
||||
array_unshift($libs, ...array_values($this->getDependencies(recursive: true)));
|
||||
}
|
||||
|
||||
$sep = match ($style) {
|
||||
'autoconf' => ' ',
|
||||
'cmake' => ';',
|
||||
default => throw new RuntimeException('style only support autoconf and cmake'),
|
||||
};
|
||||
$ret = [];
|
||||
foreach ($libs as $lib) {
|
||||
$libFiles = [];
|
||||
foreach ($lib->getStaticLibs() as $name) {
|
||||
$name = str_replace(' ', '\ ', FileSystem::convertPath(BUILD_LIB_PATH . "/{$name}"));
|
||||
$name = str_replace('"', '\"', $name);
|
||||
$libFiles[] = $name;
|
||||
}
|
||||
array_unshift($ret, implode($sep, $libFiles));
|
||||
}
|
||||
return implode($sep, $ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a nmake wrapper file.
|
||||
*
|
||||
|
||||
40
src/SPC/builder/windows/library/curl.php
Normal file
40
src/SPC/builder/windows/library/curl.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\windows\library;
|
||||
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
class curl extends WindowsLibraryBase
|
||||
{
|
||||
public const NAME = 'curl';
|
||||
|
||||
protected function build(): void
|
||||
{
|
||||
// reset cmake
|
||||
FileSystem::resetDir($this->source_dir . '\build');
|
||||
|
||||
// start build
|
||||
cmd()->cd($this->source_dir)
|
||||
->execWithWrapper(
|
||||
$this->builder->makeSimpleWrapper('cmake'),
|
||||
'-B build ' .
|
||||
'-A x64 ' .
|
||||
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
|
||||
'-DCMAKE_BUILD_TYPE=Release ' .
|
||||
'-DBUILD_SHARED_LIBS=OFF ' .
|
||||
'-DBUILD_STATIC_LIBS=ON ' .
|
||||
'-DBUILD_CURL_EXE=OFF ' .
|
||||
'-DUSE_ZLIB=ON ' .
|
||||
'-DCURL_USE_OPENSSL=ON ' .
|
||||
'-DCURL_USE_LIBLSSH2=ON ' .
|
||||
'-DUSE_NGHTTP2=ON ' . // php-src with curl needs nghttp2
|
||||
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
|
||||
)
|
||||
->execWithWrapper(
|
||||
$this->builder->makeSimpleWrapper('cmake'),
|
||||
"--build build --config Release --target install -j{$this->builder->concurrency}"
|
||||
);
|
||||
}
|
||||
}
|
||||
46
src/SPC/builder/windows/library/libffi_win.php
Normal file
46
src/SPC/builder/windows/library/libffi_win.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\windows\library;
|
||||
|
||||
use SPC\builder\windows\SystemUtil;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
class libffi_win extends WindowsLibraryBase
|
||||
{
|
||||
public const NAME = 'libffi-win';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
$vs_ver_dir = match (SystemUtil::findVisualStudio()['version']) {
|
||||
'vs17' => '/win32/vs17_x64',
|
||||
'vs16' => '/win32/vs16_x64',
|
||||
default => throw new RuntimeException('Current VS version is not supported yet!'),
|
||||
};
|
||||
|
||||
// start build
|
||||
cmd()->cd($this->source_dir . $vs_ver_dir)
|
||||
->execWithWrapper(
|
||||
$this->builder->makeSimpleWrapper('msbuild'),
|
||||
'libffi-msvc.sln /t:Rebuild /p:Configuration=Release /p:Platform=x64'
|
||||
);
|
||||
FileSystem::createDir(BUILD_LIB_PATH);
|
||||
FileSystem::createDir(BUILD_INCLUDE_PATH);
|
||||
copy($this->source_dir . $vs_ver_dir . '\x64\Release\libffi.lib', BUILD_LIB_PATH . '\libffi.lib');
|
||||
copy($this->source_dir . $vs_ver_dir . '\x64\Release\libffi.pdb', BUILD_LIB_PATH . '\libffi.pdb');
|
||||
copy($this->source_dir . '\include\ffi.h', BUILD_INCLUDE_PATH . '\ffi.h');
|
||||
|
||||
FileSystem::replaceFileStr(BUILD_INCLUDE_PATH . '\ffi.h', '#define LIBFFI_H', "#define LIBFFI_H\n#define FFI_BUILDING");
|
||||
copy($this->source_dir . '\src\x86\ffitarget.h', BUILD_INCLUDE_PATH . '\ffitarget.h');
|
||||
copy($this->source_dir . '\fficonfig.h', BUILD_INCLUDE_PATH . '\fficonfig.h');
|
||||
|
||||
// copy($this->source_dir . '\msvc_build\out\static-Release\X64\libffi.lib', BUILD_LIB_PATH . '\libffi.lib');
|
||||
// copy($this->source_dir . '\msvc_build\include\ffi.h', BUILD_INCLUDE_PATH . '\ffi.h');
|
||||
// copy($this->source_dir . '\msvc_build\include\fficonfig.h', BUILD_INCLUDE_PATH . '\fficonfig.h');
|
||||
// copy($this->source_dir . '\src\x86\ffitarget.h', BUILD_INCLUDE_PATH . '\ffitarget.h');
|
||||
|
||||
// FileSystem::replaceFileStr(BUILD_INCLUDE_PATH . '\ffi.h', '..\..\src\x86\ffitarget.h', 'ffitarget.h');
|
||||
}
|
||||
}
|
||||
35
src/SPC/builder/windows/library/libiconv_win.php
Normal file
35
src/SPC/builder/windows/library/libiconv_win.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\windows\library;
|
||||
|
||||
use SPC\builder\windows\SystemUtil;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
class libiconv_win extends WindowsLibraryBase
|
||||
{
|
||||
public const NAME = 'libiconv-win';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
$vs_ver_dir = match (SystemUtil::findVisualStudio()['version']) {
|
||||
'vs17' => '/MSVC17',
|
||||
'vs16' => '/MSVC16',
|
||||
default => throw new RuntimeException('Current VS version is not supported yet!'),
|
||||
};
|
||||
|
||||
// start build
|
||||
cmd()->cd($this->source_dir . $vs_ver_dir)
|
||||
->execWithWrapper(
|
||||
$this->builder->makeSimpleWrapper('msbuild'),
|
||||
'libiconv.sln /t:Rebuild /p:Configuration=Release /p:Platform=x64'
|
||||
);
|
||||
FileSystem::createDir(BUILD_LIB_PATH);
|
||||
FileSystem::createDir(BUILD_INCLUDE_PATH);
|
||||
copy($this->source_dir . $vs_ver_dir . '\x64\lib\libiconv.lib', BUILD_LIB_PATH . '\libiconv.lib');
|
||||
copy($this->source_dir . $vs_ver_dir . '\x64\lib\libiconv_a.lib', BUILD_LIB_PATH . '\libiconv_a.lib');
|
||||
copy($this->source_dir . '\source\include\iconv.h', BUILD_INCLUDE_PATH . '\iconv.h');
|
||||
}
|
||||
}
|
||||
38
src/SPC/builder/windows/library/libssh2.php
Normal file
38
src/SPC/builder/windows/library/libssh2.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\windows\library;
|
||||
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
class libssh2 extends WindowsLibraryBase
|
||||
{
|
||||
public const NAME = 'libssh2';
|
||||
|
||||
protected function build(): void
|
||||
{
|
||||
$zlib = $this->builder->getLib('zlib') ? 'ON' : 'OFF';
|
||||
// reset cmake
|
||||
FileSystem::resetDir($this->source_dir . '\build');
|
||||
|
||||
// start build
|
||||
cmd()->cd($this->source_dir)
|
||||
->execWithWrapper(
|
||||
$this->builder->makeSimpleWrapper('cmake'),
|
||||
'-B build ' .
|
||||
'-A x64 ' .
|
||||
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
|
||||
'-DCMAKE_BUILD_TYPE=Release ' .
|
||||
'-DBUILD_SHARED_LIBS=OFF ' .
|
||||
'-DBUILD_STATIC_LIBS=ON ' .
|
||||
'-DBUILD_TESTING=OFF ' .
|
||||
"-DENABLE_ZLIB_COMPRESSION={$zlib} " .
|
||||
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
|
||||
)
|
||||
->execWithWrapper(
|
||||
$this->builder->makeSimpleWrapper('cmake'),
|
||||
"--build build --config Release --target install -j{$this->builder->concurrency}"
|
||||
);
|
||||
}
|
||||
}
|
||||
44
src/SPC/builder/windows/library/libxml2.php
Normal file
44
src/SPC/builder/windows/library/libxml2.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\windows\library;
|
||||
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
class libxml2 extends WindowsLibraryBase
|
||||
{
|
||||
public const NAME = 'libxml2';
|
||||
|
||||
protected function build(): void
|
||||
{
|
||||
$zlib = $this->builder->getLib('zlib') ? 'ON' : 'OFF';
|
||||
// reset cmake
|
||||
FileSystem::resetDir($this->source_dir . '\build');
|
||||
|
||||
// start build
|
||||
cmd()->cd($this->source_dir)
|
||||
->execWithWrapper(
|
||||
$this->builder->makeSimpleWrapper('cmake'),
|
||||
'-B build ' .
|
||||
'-A x64 ' .
|
||||
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
|
||||
'-DBUILD_SHARED_LIBS=OFF ' .
|
||||
'-DBUILD_STATIC_LIBS=ON ' .
|
||||
"-DLIBXML2_WITH_ZLIB={$zlib} " .
|
||||
'-DLIBXML2_WITH_PYTHON=OFF ' .
|
||||
'-DLIBXML2_WITH_ICONV=ON ' .
|
||||
'-DIconv_LIBRARY=' . BUILD_LIB_PATH . ' ' .
|
||||
'-DIconv_INCLUDE_DIR=' . BUILD_INCLUDE_PATH . ' ' .
|
||||
'-DLIBXML2_WITH_LZMA=OFF ' . // xz not supported yet
|
||||
'-DLIBXML2_WITH_PROGRAMS=OFF ' .
|
||||
'-DLIBXML2_WITH_TESTS=OFF ' .
|
||||
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
|
||||
)
|
||||
->execWithWrapper(
|
||||
$this->builder->makeSimpleWrapper('cmake'),
|
||||
"--build build --config Release --target install -j{$this->builder->concurrency}"
|
||||
);
|
||||
copy(BUILD_LIB_PATH . '\libxml2s.lib', BUILD_LIB_PATH . '\libxml2_a.lib');
|
||||
}
|
||||
}
|
||||
37
src/SPC/builder/windows/library/nghttp2.php
Normal file
37
src/SPC/builder/windows/library/nghttp2.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\windows\library;
|
||||
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
class nghttp2 extends WindowsLibraryBase
|
||||
{
|
||||
public const NAME = 'nghttp2';
|
||||
|
||||
protected function build(): void
|
||||
{
|
||||
// reset cmake
|
||||
FileSystem::resetDir($this->source_dir . '\build');
|
||||
|
||||
// start build
|
||||
cmd()->cd($this->source_dir)
|
||||
->execWithWrapper(
|
||||
$this->builder->makeSimpleWrapper('cmake'),
|
||||
'-B build ' .
|
||||
'-A x64 ' .
|
||||
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
|
||||
'-DCMAKE_BUILD_TYPE=Release ' .
|
||||
'-DBUILD_SHARED_LIBS=OFF ' .
|
||||
'-DBUILD_STATIC_LIBS=ON ' .
|
||||
'-DENABLE_STATIC_CRT=ON ' .
|
||||
'-DENABLE_LIB_ONLY=ON ' .
|
||||
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
|
||||
)
|
||||
->execWithWrapper(
|
||||
$this->builder->makeSimpleWrapper('cmake'),
|
||||
"--build build --config Release --target install -j{$this->builder->concurrency}"
|
||||
);
|
||||
}
|
||||
}
|
||||
37
src/SPC/builder/windows/library/onig.php
Normal file
37
src/SPC/builder/windows/library/onig.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\windows\library;
|
||||
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
class onig extends WindowsLibraryBase
|
||||
{
|
||||
public const NAME = 'onig';
|
||||
|
||||
protected function build(): void
|
||||
{
|
||||
// reset cmake
|
||||
FileSystem::resetDir($this->source_dir . '\build');
|
||||
|
||||
// start build
|
||||
cmd()->cd($this->source_dir)
|
||||
->execWithWrapper(
|
||||
$this->builder->makeSimpleWrapper('cmake'),
|
||||
'-B build ' .
|
||||
'-A x64 ' .
|
||||
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
|
||||
'-DCMAKE_BUILD_TYPE=Release ' .
|
||||
'-DBUILD_SHARED_LIBS=OFF ' .
|
||||
'-DBUILD_STATIC_LIBS=ON ' .
|
||||
'-DMSVC_STATIC_RUNTIME=ON ' .
|
||||
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
|
||||
)
|
||||
->execWithWrapper(
|
||||
$this->builder->makeSimpleWrapper('cmake'),
|
||||
"--build build --config Release --target install -j{$this->builder->concurrency}"
|
||||
);
|
||||
copy(BUILD_LIB_PATH . '/onig.lib', BUILD_LIB_PATH . '/onig_a.lib');
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,8 @@ class openssl extends WindowsLibraryBase
|
||||
|
||||
protected function build(): void
|
||||
{
|
||||
$perl = file_exists(BUILD_ROOT_PATH . '\perl\perl\bin\perl.exe') ? (BUILD_ROOT_PATH . '\perl\perl\bin\perl.exe') : SystemUtil::findCommand('perl.exe');
|
||||
$perl_path_native = PKG_ROOT_PATH . '\strawberry-perl-' . arch2gnu(php_uname('m')) . '-win\perl\bin\perl.exe';
|
||||
$perl = file_exists($perl_path_native) ? ($perl_path_native) : SystemUtil::findCommand('perl.exe');
|
||||
if ($perl === null) {
|
||||
throw new RuntimeException('You need to install perl first! (easiest way is using static-php-cli command "doctor")');
|
||||
}
|
||||
|
||||
21
src/SPC/builder/windows/library/sqlite.php
Normal file
21
src/SPC/builder/windows/library/sqlite.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\windows\library;
|
||||
|
||||
class sqlite extends WindowsLibraryBase
|
||||
{
|
||||
public const NAME = 'sqlite';
|
||||
|
||||
public function patchBeforeBuild(): bool
|
||||
{
|
||||
copy(ROOT_DIR . '/src/globals/extra/Makefile-sqlite', $this->source_dir . '/Makefile');
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function build(): void
|
||||
{
|
||||
cmd()->cd($this->source_dir)->execWithWrapper($this->builder->makeSimpleWrapper('nmake'), 'PREFIX=' . BUILD_ROOT_PATH . ' install-static');
|
||||
}
|
||||
}
|
||||
@@ -56,11 +56,6 @@ abstract class BaseCommand extends Command
|
||||
// 如果 return false 则错误会继续递交给 PHP 标准错误处理
|
||||
return true;
|
||||
}, E_ALL | E_STRICT);
|
||||
if ($input->getOption('debug')) {
|
||||
global $ob_logger;
|
||||
$ob_logger = new ConsoleLogger(LogLevel::DEBUG);
|
||||
define('DEBUG_MODE', true);
|
||||
}
|
||||
$version = ConsoleApplication::VERSION;
|
||||
if (!$this->no_motd) {
|
||||
echo " _ _ _ _
|
||||
@@ -83,6 +78,14 @@ abstract class BaseCommand extends Command
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
|
||||
global $ob_logger;
|
||||
if ($input->getOption('debug')) {
|
||||
$ob_logger = new ConsoleLogger(LogLevel::DEBUG, decorated: !$input->getOption('no-ansi'));
|
||||
define('DEBUG_MODE', true);
|
||||
} else {
|
||||
$ob_logger = new ConsoleLogger(decorated: !$input->getOption('no-ansi'));
|
||||
}
|
||||
|
||||
// windows fallback
|
||||
Prompt::fallbackWhen(PHP_OS_FAMILY === 'Windows');
|
||||
ConfirmPrompt::fallbackUsing(function (ConfirmPrompt $prompt) use ($input, $output) {
|
||||
|
||||
@@ -23,20 +23,22 @@ class BuildCliCommand extends BuildCommand
|
||||
{
|
||||
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated');
|
||||
$this->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', '');
|
||||
$this->addOption('build-micro', null, null, 'build micro');
|
||||
$this->addOption('build-cli', null, null, 'build cli');
|
||||
$this->addOption('build-fpm', null, null, 'build fpm');
|
||||
$this->addOption('build-embed', null, null, 'build embed');
|
||||
$this->addOption('build-all', null, null, 'build cli, micro, fpm, embed');
|
||||
$this->addOption('build-micro', null, null, 'Build micro SAPI');
|
||||
$this->addOption('build-cli', null, null, 'Build cli SAPI');
|
||||
$this->addOption('build-fpm', null, null, 'Build fpm SAPI');
|
||||
$this->addOption('build-embed', null, null, 'Build embed SAPI');
|
||||
$this->addOption('build-all', null, null, 'Build all SAPI');
|
||||
$this->addOption('no-strip', null, null, 'build without strip, in order to debug and load external extensions');
|
||||
$this->addOption('enable-zts', null, null, 'enable ZTS support');
|
||||
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');
|
||||
$this->addOption('with-hardcoded-ini', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Patch PHP source code, inject hardcoded INI');
|
||||
$this->addOption('with-micro-fake-cli', null, null, 'Enable phpmicro fake cli');
|
||||
$this->addOption('with-micro-fake-cli', null, null, 'Let phpmicro\'s PHP_SAPI use "cli" instead of "micro"');
|
||||
$this->addOption('with-suggested-libs', 'L', null, 'Build with suggested libs for selected exts and libs');
|
||||
$this->addOption('with-suggested-exts', 'E', null, 'Build with suggested extensions for selected exts');
|
||||
$this->addOption('with-added-patch', 'P', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Inject patch script outside');
|
||||
$this->addOption('without-micro-ext-test', null, null, 'Disable phpmicro with extension test code');
|
||||
$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)');
|
||||
}
|
||||
|
||||
public function handle(): int
|
||||
@@ -58,11 +60,45 @@ class BuildCliCommand extends BuildCommand
|
||||
$this->output->writeln("<comment>\t--build-all\tBuild all SAPI: cli, micro, fpm, embed</comment>");
|
||||
return static::FAILURE;
|
||||
}
|
||||
if ($rule === BUILD_TARGET_ALL) {
|
||||
logger()->warning('--build-all option makes `--no-strip` always true, be aware!');
|
||||
}
|
||||
if (($rule & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO && $this->getOption('with-micro-logo')) {
|
||||
$logo = $this->getOption('with-micro-logo');
|
||||
if (!file_exists($logo)) {
|
||||
logger()->error('Logo file ' . $logo . ' not exist !');
|
||||
return static::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// Check upx
|
||||
$suffix = PHP_OS_FAMILY === 'Windows' ? '.exe' : '';
|
||||
if ($this->getOption('with-upx-pack')) {
|
||||
// only available for linux for now
|
||||
if (!in_array(PHP_OS_FAMILY, ['Linux', 'Windows'])) {
|
||||
logger()->error('UPX is only available on Linux and Windows!');
|
||||
return static::FAILURE;
|
||||
}
|
||||
// need to install this manually
|
||||
if (!file_exists(PKG_ROOT_PATH . '/bin/upx' . $suffix)) {
|
||||
global $argv;
|
||||
logger()->error('upx does not exist, please install it first:');
|
||||
logger()->error('');
|
||||
logger()->error("\t" . $argv[0] . ' install-pkg upx');
|
||||
logger()->error('');
|
||||
return static::FAILURE;
|
||||
}
|
||||
// exclusive with no-strip
|
||||
if ($this->getOption('no-strip')) {
|
||||
logger()->warning('--with-upx-pack conflicts with --no-strip, --no-strip won\'t work!');
|
||||
}
|
||||
}
|
||||
try {
|
||||
// create builder
|
||||
$builder = BuilderProvider::makeBuilderByInput($this->input);
|
||||
// calculate dependencies
|
||||
[$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions, $libraries);
|
||||
$include_suggest_ext = $this->getOption('with-suggested-exts');
|
||||
$include_suggest_lib = $this->getOption('with-suggested-libs');
|
||||
[$extensions, $libraries, $not_included] = DependencyUtil::getExtsAndLibs($extensions, $libraries, $include_suggest_ext, $include_suggest_lib);
|
||||
|
||||
// print info
|
||||
$indent_texts = [
|
||||
@@ -76,12 +112,27 @@ class BuildCliCommand extends BuildCommand
|
||||
if (!empty($this->input->getOption('with-hardcoded-ini'))) {
|
||||
$indent_texts['Hardcoded INI'] = $this->input->getOption('with-hardcoded-ini');
|
||||
}
|
||||
$this->printFormatInfo($indent_texts);
|
||||
if ($this->input->getOption('disable-opcache-jit')) {
|
||||
$indent_texts['Opcache JIT'] = 'disabled';
|
||||
}
|
||||
if ($this->input->getOption('with-upx-pack') && in_array(PHP_OS_FAMILY, ['Linux', 'Windows'])) {
|
||||
$indent_texts['UPX Pack'] = 'enabled';
|
||||
$builder->setOption('upx-exec', FileSystem::convertPath(PKG_ROOT_PATH . '/bin/upx' . $suffix));
|
||||
}
|
||||
try {
|
||||
$ver = $builder->getPHPVersion();
|
||||
$indent_texts['PHP Version'] = $ver;
|
||||
} catch (\Throwable) {
|
||||
if (($ver = $builder->getPHPVersionFromArchive()) !== false) {
|
||||
$indent_texts['PHP Version'] = $ver;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($not_included)) {
|
||||
logger()->warning('Some extensions will be enabled due to dependencies: ' . implode(',', $not_included));
|
||||
$indent_texts['Extra Exts (' . count($not_included) . ')'] = implode(', ', $not_included);
|
||||
}
|
||||
logger()->info('Build will start after 2s ...');
|
||||
$this->printFormatInfo($indent_texts);
|
||||
logger()->notice('Build will start after 2s ...');
|
||||
sleep(2);
|
||||
|
||||
if ($this->input->getOption('with-clean')) {
|
||||
|
||||
@@ -60,7 +60,7 @@ class BuildLibsCommand extends BuildCommand
|
||||
// 只编译 library 的情况下,标记
|
||||
$builder->setLibsOnly();
|
||||
// 编译和检查库完整
|
||||
$libraries = DependencyUtil::getLibsByDeps($libraries);
|
||||
$libraries = DependencyUtil::getLibs($libraries);
|
||||
$builder->buildLibs($libraries);
|
||||
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
|
||||
86
src/SPC/command/DeleteDownloadCommand.php
Normal file
86
src/SPC/command/DeleteDownloadCommand.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\exception\DownloaderException;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\FileSystem;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
#[AsCommand('del-download', 'Remove locked download source or package using name', ['delete-download', 'del-down'])]
|
||||
class DeleteDownloadCommand extends BaseCommand
|
||||
{
|
||||
public function configure(): void
|
||||
{
|
||||
$this->addArgument('sources', InputArgument::REQUIRED, 'The sources/packages will be deleted, comma separated');
|
||||
$this->addOption('all', 'A', null, 'Delete all downloaded and locked sources/packages');
|
||||
}
|
||||
|
||||
public function initialize(InputInterface $input, OutputInterface $output): void
|
||||
{
|
||||
if ($input->getOption('all')) {
|
||||
$input->setArgument('sources', '');
|
||||
}
|
||||
parent::initialize($input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
try {
|
||||
// get source list that will be downloaded
|
||||
$sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources'))));
|
||||
if (empty($sources)) {
|
||||
logger()->notice('Removing downloads/ directory ...');
|
||||
FileSystem::removeDir(DOWNLOAD_PATH);
|
||||
logger()->info('Removed downloads/ dir!');
|
||||
return static::SUCCESS;
|
||||
}
|
||||
$chosen_sources = $sources;
|
||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
|
||||
|
||||
foreach ($chosen_sources as $source) {
|
||||
$source = trim($source);
|
||||
if (!isset($lock[$source])) {
|
||||
logger()->warning("Source/Package [{$source}] not locked or not downloaded, skipped.");
|
||||
continue;
|
||||
}
|
||||
// remove download file/dir if exists
|
||||
if ($lock[$source]['source_type'] === 'archive') {
|
||||
if (file_exists($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock[$source]['filename']))) {
|
||||
logger()->info('Deleting file ' . $path);
|
||||
unlink($path);
|
||||
} else {
|
||||
logger()->warning("Source/Package [{$source}] file not found, skip deleting file.");
|
||||
}
|
||||
} else {
|
||||
if (is_dir($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock[$source]['dirname']))) {
|
||||
logger()->info('Deleting dir ' . $path);
|
||||
FileSystem::removeDir($path);
|
||||
} else {
|
||||
logger()->warning("Source/Package [{$source}] directory not found, skip deleting dir.");
|
||||
}
|
||||
}
|
||||
// remove locked sources
|
||||
unset($lock[$source]);
|
||||
}
|
||||
FileSystem::writeFile(DOWNLOAD_PATH . '/.lock.json', json_encode($lock, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
||||
logger()->info('Delete success!');
|
||||
return static::SUCCESS;
|
||||
} catch (DownloaderException $e) {
|
||||
logger()->error($e->getMessage());
|
||||
return static::FAILURE;
|
||||
} catch (WrongUsageException $e) {
|
||||
logger()->critical($e->getMessage());
|
||||
return static::FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,7 +214,7 @@ class DownloadCommand extends BaseCommand
|
||||
*/
|
||||
private function calculateSourcesByExt(array $extensions, bool $include_suggests = true): array
|
||||
{
|
||||
[$extensions, $libraries] = $include_suggests ? DependencyUtil::getAllExtLibsByDeps($extensions) : DependencyUtil::getExtLibsByDeps($extensions);
|
||||
[$extensions, $libraries] = $include_suggests ? DependencyUtil::getExtsAndLibs($extensions, [], true, true) : DependencyUtil::getExtsAndLibs($extensions);
|
||||
$sources = [];
|
||||
foreach ($extensions as $extension) {
|
||||
if (Config::getExt($extension, 'type') === 'external') {
|
||||
|
||||
@@ -22,8 +22,8 @@ class DumpLicenseCommand extends BaseCommand
|
||||
{
|
||||
$this->addOption('for-extensions', null, InputOption::VALUE_REQUIRED, 'Dump by extensions and related libraries', null);
|
||||
$this->addOption('without-php', null, InputOption::VALUE_NONE, 'Dump without php-src');
|
||||
$this->addOption('by-libs', null, InputOption::VALUE_REQUIRED, 'Dump by libraries', null);
|
||||
$this->addOption('by-sources', null, InputOption::VALUE_REQUIRED, 'Dump by original sources (source.json)', null);
|
||||
$this->addOption('for-libs', null, InputOption::VALUE_REQUIRED, 'Dump by libraries', null);
|
||||
$this->addOption('for-sources', null, InputOption::VALUE_REQUIRED, 'Dump by original sources (source.json)', null);
|
||||
$this->addOption('dump-dir', null, InputOption::VALUE_REQUIRED, 'Change dump directory', BUILD_ROOT_PATH . '/license');
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class DumpLicenseCommand extends BaseCommand
|
||||
// 从参数中获取要编译的 extensions,并转换为数组
|
||||
$extensions = array_map('trim', array_filter(explode(',', $this->getOption('for-extensions'))));
|
||||
// 根据提供的扩展列表获取依赖库列表并编译
|
||||
[$extensions, $libraries] = DependencyUtil::getExtLibsByDeps($extensions);
|
||||
[$extensions, $libraries] = DependencyUtil::getExtsAndLibs($extensions);
|
||||
$dumper->addExts($extensions);
|
||||
$dumper->addLibs($libraries);
|
||||
if (!$this->getOption('without-php')) {
|
||||
@@ -52,22 +52,22 @@ class DumpLicenseCommand extends BaseCommand
|
||||
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||
return static::SUCCESS;
|
||||
}
|
||||
if ($this->getOption('by-libs') !== null) {
|
||||
$libraries = array_map('trim', array_filter(explode(',', $this->getOption('by-libs'))));
|
||||
$libraries = DependencyUtil::getLibsByDeps($libraries);
|
||||
if ($this->getOption('for-libs') !== null) {
|
||||
$libraries = array_map('trim', array_filter(explode(',', $this->getOption('for-libs'))));
|
||||
$libraries = DependencyUtil::getLibs($libraries);
|
||||
$dumper->addLibs($libraries);
|
||||
$dumper->dump($this->getOption('dump-dir'));
|
||||
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||
return static::SUCCESS;
|
||||
}
|
||||
if ($this->getOption('by-sources') !== null) {
|
||||
$sources = array_map('trim', array_filter(explode(',', $this->getOption('by-sources'))));
|
||||
if ($this->getOption('for-sources') !== null) {
|
||||
$sources = array_map('trim', array_filter(explode(',', $this->getOption('for-sources'))));
|
||||
$dumper->addSources($sources);
|
||||
$dumper->dump($this->getOption('dump-dir'));
|
||||
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||
return static::SUCCESS;
|
||||
}
|
||||
$this->output->writeln('You must use one of "--for-extensions=", "--by-libs=", "--by-sources=" to dump');
|
||||
$this->output->writeln('You must use one of "--for-extensions=", "--for-libs=", "--for-sources=" to dump');
|
||||
return static::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ use SPC\builder\traits\UnixSystemUtilTrait;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\SourceExtractor;
|
||||
use SPC\store\SourceManager;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
#[AsCommand('extract', 'Extract required sources')]
|
||||
#[AsCommand('extract', 'Extract required sources', ['extract-source'])]
|
||||
class ExtractCommand extends BaseCommand
|
||||
{
|
||||
use UnixSystemUtilTrait;
|
||||
@@ -34,7 +34,7 @@ class ExtractCommand extends BaseCommand
|
||||
$this->output->writeln('<error>sources cannot be empty, at least contain one !</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
SourceExtractor::initSource(sources: $sources);
|
||||
SourceManager::initSource(sources: $sources);
|
||||
logger()->info('Extract done !');
|
||||
return static::SUCCESS;
|
||||
}
|
||||
|
||||
86
src/SPC/command/InstallPkgCommand.php
Normal file
86
src/SPC/command/InstallPkgCommand.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\builder\traits\UnixSystemUtilTrait;
|
||||
use SPC\exception\DownloaderException;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\PackageManager;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand('install-pkg', 'Install additional packages', ['i', 'install-package'])]
|
||||
class InstallPkgCommand extends BaseCommand
|
||||
{
|
||||
use UnixSystemUtilTrait;
|
||||
|
||||
public function configure(): void
|
||||
{
|
||||
$this->addArgument('packages', InputArgument::REQUIRED, 'The packages will be installed, comma separated');
|
||||
$this->addOption('shallow-clone', null, null, 'Clone shallow');
|
||||
$this->addOption('custom-url', 'U', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Specify custom source download url, e.g "php-src:https://downloads.php.net/~eric/php-8.3.0beta1.tar.gz"');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
try {
|
||||
// Use shallow-clone can reduce git resource download
|
||||
if ($this->getOption('shallow-clone')) {
|
||||
define('GIT_SHALLOW_CLONE', true);
|
||||
}
|
||||
|
||||
// Process -U options
|
||||
$custom_urls = [];
|
||||
foreach ($this->input->getOption('custom-url') as $value) {
|
||||
[$pkg_name, $url] = explode(':', $value, 2);
|
||||
$custom_urls[$pkg_name] = $url;
|
||||
}
|
||||
|
||||
$chosen_pkgs = array_map('trim', array_filter(explode(',', $this->getArgument('packages'))));
|
||||
|
||||
// Download them
|
||||
f_mkdir(DOWNLOAD_PATH);
|
||||
$ni = 0;
|
||||
$cnt = count($chosen_pkgs);
|
||||
|
||||
foreach ($chosen_pkgs as $pkg) {
|
||||
++$ni;
|
||||
if (isset($custom_urls[$pkg])) {
|
||||
$config = Config::getPkg($pkg);
|
||||
$new_config = [
|
||||
'type' => 'url',
|
||||
'url' => $custom_urls[$pkg],
|
||||
];
|
||||
if (isset($config['extract'])) {
|
||||
$new_config['extract'] = $config['extract'];
|
||||
}
|
||||
if (isset($config['filename'])) {
|
||||
$new_config['filename'] = $config['filename'];
|
||||
}
|
||||
logger()->info("Installing source {$pkg} from custom url [{$ni}/{$cnt}]");
|
||||
PackageManager::installPackage($pkg, $new_config);
|
||||
} else {
|
||||
logger()->info("Fetching package {$pkg} [{$ni}/{$cnt}]");
|
||||
PackageManager::installPackage($pkg, Config::getPkg($pkg));
|
||||
}
|
||||
}
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Install packages complete, used ' . $time . ' s !');
|
||||
return static::SUCCESS;
|
||||
} catch (DownloaderException $e) {
|
||||
logger()->error($e->getMessage());
|
||||
return static::FAILURE;
|
||||
} catch (WrongUsageException $e) {
|
||||
logger()->critical($e->getMessage());
|
||||
return static::FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,6 @@ use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
use function Laravel\Prompts\table;
|
||||
|
||||
#[AsCommand('dev:extensions', 'Helper command that lists available extension details', ['list-ext'])]
|
||||
class AllExtCommand extends BaseCommand
|
||||
{
|
||||
@@ -61,7 +59,7 @@ class AllExtCommand extends BaseCommand
|
||||
}
|
||||
|
||||
try {
|
||||
[, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps([$extension]);
|
||||
[, $libraries, $not_included] = DependencyUtil::getExtsAndLibs([$extension]);
|
||||
} catch (WrongUsageException) {
|
||||
$libraries = $not_included = [];
|
||||
}
|
||||
@@ -88,7 +86,8 @@ class AllExtCommand extends BaseCommand
|
||||
if ($data === []) {
|
||||
$style->warning('Unknown extension selected: ' . implode(',', $extensions));
|
||||
} else {
|
||||
table($columns, $data);
|
||||
$func = PHP_OS_FAMILY === 'Windows' ? [$style, 'table'] : '\Laravel\Prompts\table';
|
||||
call_user_func($func, $columns, $data);
|
||||
}
|
||||
|
||||
return static::SUCCESS;
|
||||
|
||||
@@ -57,6 +57,15 @@ class SortConfigCommand extends BaseCommand
|
||||
return static::FAILURE;
|
||||
}
|
||||
break;
|
||||
case 'pkg':
|
||||
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/pkg.json'), true);
|
||||
ConfigValidator::validatePkgs($file);
|
||||
ksort($file);
|
||||
if (!file_put_contents(ROOT_DIR . '/config/pkg.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n")) {
|
||||
$this->output->writeln('<error>Write file pkg.json failed!</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$this->output->writeln("<error>invalid config name: {$name}</error>");
|
||||
return 1;
|
||||
|
||||
@@ -57,7 +57,7 @@ class BSDToolCheckList
|
||||
$prefix = '';
|
||||
}
|
||||
try {
|
||||
shell(true)->exec("ASSUME_ALWAYS_YES=yes {$prefix} pkg install -y " . implode(' ', $missing));
|
||||
shell(true)->exec("ASSUME_ALWAYS_YES=yes {$prefix}pkg install -y " . implode(' ', $missing));
|
||||
} catch (RuntimeException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Downloader;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\store\PackageManager;
|
||||
|
||||
class LinuxMuslCheck
|
||||
{
|
||||
@@ -84,7 +85,6 @@ class LinuxMuslCheck
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
/**
|
||||
* @throws DownloaderException
|
||||
* @throws FileSystemException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
@@ -98,15 +98,10 @@ class LinuxMuslCheck
|
||||
logger()->warning('Current user is not root, using sudo for running command');
|
||||
}
|
||||
$arch = arch2gnu(php_uname('m'));
|
||||
$musl_compile_source = [
|
||||
'type' => 'url',
|
||||
'url' => "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/{$arch}-musl-toolchain.tgz",
|
||||
];
|
||||
logger()->info('Downloading ' . $musl_compile_source['url']);
|
||||
Downloader::downloadSource('musl-compile', $musl_compile_source);
|
||||
logger()->info('Extracting musl-cross');
|
||||
FileSystem::extractSource('musl-compile', DOWNLOAD_PATH . "/{$arch}-musl-toolchain.tgz");
|
||||
shell()->exec($prefix . 'cp -rf ' . SOURCE_PATH . '/musl-compile/* /usr/local/musl');
|
||||
PackageManager::installPackage("musl-toolchain-{$arch}-linux");
|
||||
$pkg_root = PKG_ROOT_PATH . "/musl-toolchain-{$arch}-linux";
|
||||
shell()->exec("{$prefix}cp -rf {$pkg_root}/* /usr/local/musl");
|
||||
FileSystem::removeDir($pkg_root);
|
||||
return true;
|
||||
} catch (RuntimeException) {
|
||||
return false;
|
||||
|
||||
@@ -31,6 +31,7 @@ class MacOSToolCheckList
|
||||
'gzip',
|
||||
'bzip2',
|
||||
'cmake',
|
||||
'glibtoolize',
|
||||
];
|
||||
|
||||
#[AsCheckItem('if homebrew has installed', limit_os: 'Darwin', level: 998)]
|
||||
@@ -72,8 +73,14 @@ class MacOSToolCheckList
|
||||
#[AsFixItem('build-tools')]
|
||||
public function fixBuildTools(array $missing): bool
|
||||
{
|
||||
$replacement = [
|
||||
'glibtoolize' => 'libtool',
|
||||
];
|
||||
foreach ($missing as $cmd) {
|
||||
try {
|
||||
if (isset($replacement[$cmd])) {
|
||||
$cmd = $replacement[$cmd];
|
||||
}
|
||||
shell(true)->exec('brew install --formula ' . escapeshellarg($cmd));
|
||||
} catch (RuntimeException) {
|
||||
return false;
|
||||
|
||||
@@ -9,8 +9,8 @@ use SPC\doctor\AsCheckItem;
|
||||
use SPC\doctor\AsFixItem;
|
||||
use SPC\doctor\CheckResult;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\Downloader;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\store\PackageManager;
|
||||
|
||||
class WindowsToolCheckList
|
||||
{
|
||||
@@ -64,8 +64,9 @@ class WindowsToolCheckList
|
||||
#[AsCheckItem('if perl(strawberry) installed', limit_os: 'Windows', level: 994)]
|
||||
public function checkPerl(): ?CheckResult
|
||||
{
|
||||
if (file_exists(BUILD_ROOT_PATH . '\perl\perl\bin\perl.exe')) {
|
||||
return CheckResult::ok(BUILD_ROOT_PATH . '\perl\perl\bin\perl.exe');
|
||||
$arch = arch2gnu(php_uname('m'));
|
||||
if (file_exists(PKG_ROOT_PATH . '\strawberry-perl-' . $arch . '-win\perl\bin\perl.exe')) {
|
||||
return CheckResult::ok(PKG_ROOT_PATH . '\strawberry-perl-' . $arch . '-win\perl\bin\perl.exe');
|
||||
}
|
||||
if (($path = SystemUtil::findCommand('perl.exe')) === null) {
|
||||
return CheckResult::fail('perl not found in path.', 'install-perl');
|
||||
@@ -91,32 +92,15 @@ class WindowsToolCheckList
|
||||
#[AsFixItem('install-nasm')]
|
||||
public function installNasm(): bool
|
||||
{
|
||||
// The hardcoded version here is to be consistent with the version compiled by `musl-cross-toolchain`.
|
||||
$nasm_ver = '2.16.01';
|
||||
$nasm_dist = "nasm-{$nasm_ver}";
|
||||
$source = [
|
||||
'type' => 'url',
|
||||
'url' => "https://www.nasm.us/pub/nasm/releasebuilds/{$nasm_ver}/win64/{$nasm_dist}-win64.zip",
|
||||
];
|
||||
logger()->info('Downloading ' . $source['url']);
|
||||
Downloader::downloadSource('nasm', $source);
|
||||
FileSystem::extractSource('nasm', DOWNLOAD_PATH . "\\{$nasm_dist}-win64.zip");
|
||||
copy(SOURCE_PATH . "\\nasm\\{$nasm_dist}\\nasm.exe", PHP_SDK_PATH . '\bin\nasm.exe');
|
||||
copy(SOURCE_PATH . "\\nasm\\{$nasm_dist}\\ndisasm.exe", PHP_SDK_PATH . '\bin\ndisasm.exe');
|
||||
PackageManager::installPackage('nasm-x86_64-win');
|
||||
return true;
|
||||
}
|
||||
|
||||
#[AsFixItem('install-perl')]
|
||||
public function installPerl(): bool
|
||||
{
|
||||
$url = 'https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/download/SP_5380_5361/strawberry-perl-5.38.0.1-64bit-portable.zip';
|
||||
$source = [
|
||||
'type' => 'url',
|
||||
'url' => $url,
|
||||
];
|
||||
logger()->info("Downloading {$url}");
|
||||
Downloader::downloadSource('strawberry-perl', $source);
|
||||
FileSystem::extractSource('strawberry-perl', DOWNLOAD_PATH . '\strawberry-perl-5.38.0.1-64bit-portable.zip', '../buildroot/perl');
|
||||
$arch = arch2gnu(php_uname('m'));
|
||||
PackageManager::installPackage("strawberry-perl-{$arch}-win");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ use SPC\exception\WrongUsageException;
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
public static ?array $pkg = null;
|
||||
|
||||
public static ?array $source = null;
|
||||
|
||||
public static ?array $lib = null;
|
||||
@@ -31,6 +33,19 @@ class Config
|
||||
return self::$source[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read pkg from pkg.json
|
||||
*
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getPkg(string $name): ?array
|
||||
{
|
||||
if (self::$pkg === null) {
|
||||
self::$pkg = FileSystem::loadConfigArray('pkg');
|
||||
}
|
||||
return self::$pkg[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据不同的操作系统分别选择不同的 lib 库依赖项
|
||||
* 如果 key 为 null,那么直接返回整个 meta。
|
||||
|
||||
@@ -246,6 +246,92 @@ class Downloader
|
||||
}*/
|
||||
}
|
||||
|
||||
public static function downloadPackage(string $name, ?array $pkg = null, bool $force = false): void
|
||||
{
|
||||
if ($pkg === null) {
|
||||
$pkg = Config::getPkg($name);
|
||||
}
|
||||
|
||||
if ($pkg === null) {
|
||||
logger()->warning('Package {name} unknown. Skipping.', ['name' => $name]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_dir(DOWNLOAD_PATH)) {
|
||||
FileSystem::createDir(DOWNLOAD_PATH);
|
||||
}
|
||||
|
||||
// load lock file
|
||||
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
|
||||
$lock = [];
|
||||
} else {
|
||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
|
||||
}
|
||||
// If lock file exists, skip downloading
|
||||
if (isset($lock[$name]) && !$force) {
|
||||
if ($lock[$name]['source_type'] === 'archive' && file_exists(DOWNLOAD_PATH . '/' . $lock[$name]['filename'])) {
|
||||
logger()->notice("Package [{$name}] already downloaded: " . $lock[$name]['filename']);
|
||||
return;
|
||||
}
|
||||
if ($lock[$name]['source_type'] === 'dir' && is_dir(DOWNLOAD_PATH . '/' . $lock[$name]['dirname'])) {
|
||||
logger()->notice("Package [{$name}] already downloaded: " . $lock[$name]['dirname']);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
switch ($pkg['type']) {
|
||||
case 'bitbuckettag': // BitBucket Tag
|
||||
[$url, $filename] = self::getLatestBitbucketTag($name, $pkg);
|
||||
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null);
|
||||
break;
|
||||
case 'ghtar': // GitHub Release (tar)
|
||||
[$url, $filename] = self::getLatestGithubTarball($name, $pkg);
|
||||
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null);
|
||||
break;
|
||||
case 'ghtagtar': // GitHub Tag (tar)
|
||||
[$url, $filename] = self::getLatestGithubTarball($name, $pkg, 'tags');
|
||||
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null);
|
||||
break;
|
||||
case 'ghrel': // GitHub Release (uploaded)
|
||||
[$url, $filename] = self::getLatestGithubRelease($name, $pkg);
|
||||
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null);
|
||||
break;
|
||||
case 'filelist': // Basic File List (regex based crawler)
|
||||
[$url, $filename] = self::getFromFileList($name, $pkg);
|
||||
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null);
|
||||
break;
|
||||
case 'url': // Direct download URL
|
||||
$url = $pkg['url'];
|
||||
$filename = $pkg['filename'] ?? basename($pkg['url']);
|
||||
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null);
|
||||
break;
|
||||
case 'git': // Git repo
|
||||
self::downloadGit($name, $pkg['url'], $pkg['rev'], $pkg['extract'] ?? null);
|
||||
break;
|
||||
case 'custom': // Custom download method, like API-based download or other
|
||||
$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();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new DownloaderException('unknown source type: ' . $pkg['type']);
|
||||
}
|
||||
} catch (RuntimeException $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());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download source by name and meta.
|
||||
*
|
||||
|
||||
@@ -16,7 +16,7 @@ class FileSystem
|
||||
*/
|
||||
public static function loadConfigArray(string $config, ?string $config_dir = null): array
|
||||
{
|
||||
$whitelist = ['ext', 'lib', 'source'];
|
||||
$whitelist = ['ext', 'lib', 'source', 'pkg'];
|
||||
if (!in_array($config, $whitelist)) {
|
||||
throw new FileSystemException('Reading ' . $config . '.json is not allowed');
|
||||
}
|
||||
@@ -138,6 +138,37 @@ class FileSystem
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function extractPackage(string $name, string $filename, ?string $extract_path = null): void
|
||||
{
|
||||
if ($extract_path !== null) {
|
||||
// replace
|
||||
$extract_path = self::replacePathVariable($extract_path);
|
||||
$extract_path = self::isRelativePath($extract_path) ? (WORKING_DIR . '/' . $extract_path) : $extract_path;
|
||||
} else {
|
||||
$extract_path = PKG_ROOT_PATH . '/' . $name;
|
||||
}
|
||||
logger()->info("extracting {$name} package to {$extract_path} ...");
|
||||
$target = self::convertPath($extract_path);
|
||||
|
||||
if (!is_dir($dir = dirname($target))) {
|
||||
self::createDir($dir);
|
||||
}
|
||||
try {
|
||||
self::extractArchive($filename, $target);
|
||||
} catch (RuntimeException $e) {
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
f_passthru('rmdir /s /q ' . $target);
|
||||
} else {
|
||||
f_passthru('rm -rf ' . $target);
|
||||
}
|
||||
throw new FileSystemException('Cannot extract package ' . $name, $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压缩下载的资源包到 source 目录
|
||||
*
|
||||
@@ -152,52 +183,26 @@ class FileSystem
|
||||
if (self::$_extract_hook === []) {
|
||||
SourcePatcher::init();
|
||||
}
|
||||
if (!is_dir(SOURCE_PATH)) {
|
||||
self::createDir(SOURCE_PATH);
|
||||
}
|
||||
if ($move_path !== null) {
|
||||
$move_path = SOURCE_PATH . '/' . $move_path;
|
||||
} else {
|
||||
$move_path = SOURCE_PATH . "/{$name}";
|
||||
}
|
||||
$target = self::convertPath($move_path);
|
||||
logger()->info("extracting {$name} source to {$target}" . ' ...');
|
||||
if (!is_dir($dir = dirname($target))) {
|
||||
self::createDir($dir);
|
||||
}
|
||||
logger()->info("extracting {$name} source to " . ($move_path ?? SOURCE_PATH . "/{$name}") . ' ...');
|
||||
try {
|
||||
$target = self::convertPath($move_path ?? (SOURCE_PATH . "/{$name}"));
|
||||
// Git source, just move
|
||||
if (is_dir(self::convertPath($filename))) {
|
||||
self::copyDir(self::convertPath($filename), $target);
|
||||
self::emitSourceExtractHook($name);
|
||||
return;
|
||||
}
|
||||
if (f_mkdir(directory: $target, recursive: true) !== true) {
|
||||
throw new FileSystemException('create ' . $name . 'source dir failed');
|
||||
}
|
||||
|
||||
if (in_array(PHP_OS_FAMILY, ['Darwin', 'Linux', 'BSD'])) {
|
||||
match (self::extname($filename)) {
|
||||
'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}"),
|
||||
default => throw new FileSystemException('unknown archive format: ' . $filename),
|
||||
};
|
||||
} elseif (PHP_OS_FAMILY === 'Windows') {
|
||||
// use php-sdk-binary-tools/bin/7za.exe
|
||||
$_7z = self::convertPath(PHP_SDK_PATH . '/bin/7za.exe');
|
||||
f_mkdir(SOURCE_PATH . "/{$name}", recursive: true);
|
||||
match (self::extname($filename)) {
|
||||
'tar' => f_passthru("tar -xf {$filename} -C {$target} --strip-components 1"),
|
||||
'xz', 'txz', 'gz', 'tgz', 'bz2' => f_passthru("\"{$_7z}\" x -so {$filename} | tar -f - -x -C {$target} --strip-components 1"),
|
||||
'zip' => f_passthru("\"{$_7z}\" x {$filename} -o{$target} -y"),
|
||||
default => throw new FileSystemException("unknown archive format: {$filename}"),
|
||||
};
|
||||
}
|
||||
self::extractArchive($filename, $target);
|
||||
self::emitSourceExtractHook($name);
|
||||
} catch (RuntimeException $e) {
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
f_passthru('rmdir /s /q ' . SOURCE_PATH . "/{$name}");
|
||||
f_passthru('rmdir /s /q ' . $target);
|
||||
} else {
|
||||
f_passthru('rm -r ' . SOURCE_PATH . "/{$name}");
|
||||
f_passthru('rm -rf ' . $target);
|
||||
}
|
||||
throw new FileSystemException('Cannot extract source ' . $name, $e->getCode(), $e);
|
||||
throw new FileSystemException('Cannot extract source ' . $name . ': ' . $e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,6 +219,14 @@ class FileSystem
|
||||
return str_replace('/', DIRECTORY_SEPARATOR, $path);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归或非递归扫描目录,可返回相对目录的文件列表或绝对目录的文件列表
|
||||
*
|
||||
@@ -227,8 +240,12 @@ class FileSystem
|
||||
{
|
||||
$dir = self::convertPath($dir);
|
||||
// 不是目录不扫,直接 false 处理
|
||||
if (!file_exists($dir)) {
|
||||
logger()->debug('Scan dir failed, no such file or directory.');
|
||||
return false;
|
||||
}
|
||||
if (!is_dir($dir)) {
|
||||
logger()->warning('Scan dir failed, no such directory.');
|
||||
logger()->warning('Scan dir failed, not directory.');
|
||||
return false;
|
||||
}
|
||||
logger()->debug('scanning directory ' . $dir);
|
||||
@@ -317,8 +334,12 @@ class FileSystem
|
||||
$dir = FileSystem::convertPath($dir);
|
||||
logger()->debug('Removing path recursively: "' . $dir . '"');
|
||||
// 不是目录不扫,直接 false 处理
|
||||
if (!file_exists($dir)) {
|
||||
logger()->debug('Scan dir failed, no such file or directory.');
|
||||
return false;
|
||||
}
|
||||
if (!is_dir($dir)) {
|
||||
logger()->warning('Scan dir failed, no such directory.');
|
||||
logger()->warning('Scan dir failed, not directory.');
|
||||
return false;
|
||||
}
|
||||
logger()->debug('scanning directory ' . $dir);
|
||||
@@ -403,6 +424,63 @@ class FileSystem
|
||||
return strlen($path) > 0 && $path[0] !== '/';
|
||||
}
|
||||
|
||||
public static function replacePathVariable(string $path): string
|
||||
{
|
||||
$replacement = [
|
||||
'{pkg_root_path}' => PKG_ROOT_PATH,
|
||||
'{php_sdk_path}' => defined('PHP_SDK_PATH') ? PHP_SDK_PATH : WORKING_DIR . '/php-sdk-binary-tools',
|
||||
'{working_dir}' => WORKING_DIR,
|
||||
'{download_path}' => DOWNLOAD_PATH,
|
||||
'{source_path}' => SOURCE_PATH,
|
||||
];
|
||||
return str_replace(array_keys($replacement), array_values($replacement), $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
private static function extractArchive(string $filename, string $target): void
|
||||
{
|
||||
// Git source, just move
|
||||
if (is_dir(self::convertPath($filename))) {
|
||||
self::copyDir(self::convertPath($filename), $target);
|
||||
return;
|
||||
}
|
||||
// Create base dir
|
||||
if (f_mkdir(directory: $target, recursive: true) !== true) {
|
||||
throw new FileSystemException('create ' . $target . ' dir failed');
|
||||
}
|
||||
if (!file_exists($filename)) {
|
||||
throw new FileSystemException('File not exists');
|
||||
}
|
||||
|
||||
if (in_array(PHP_OS_FAMILY, ['Darwin', 'Linux', 'BSD'])) {
|
||||
match (self::extname($filename)) {
|
||||
'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}"),
|
||||
default => throw new FileSystemException('unknown archive format: ' . $filename),
|
||||
};
|
||||
} elseif (PHP_OS_FAMILY === 'Windows') {
|
||||
// use php-sdk-binary-tools/bin/7za.exe
|
||||
$_7z = self::convertPath(PHP_SDK_PATH . '/bin/7za.exe');
|
||||
|
||||
// Windows notes: I hate windows tar.......
|
||||
// When extracting .tar.gz like libxml2, it shows a symlink error and returns code[1].
|
||||
// Related posts: https://answers.microsoft.com/en-us/windows/forum/all/tar-on-windows-fails-to-extract-archive-containing/0ee9a7ea-9b1f-4fef-86a9-5d9dc35cea2f
|
||||
// And MinGW tar.exe cannot work on temporarily storage ??? (GitHub Actions hosted runner)
|
||||
// Yeah, I will be an MS HATER !
|
||||
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"),
|
||||
default => throw new FileSystemException("unknown archive format: {$filename}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
|
||||
67
src/SPC/store/PackageManager.php
Normal file
67
src/SPC/store/PackageManager.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\store;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
|
||||
class PackageManager
|
||||
{
|
||||
public static function installPackage(string $pkg_name, ?array $config = null, bool $force = false): void
|
||||
{
|
||||
if ($config === null) {
|
||||
$config = Config::getPkg($pkg_name);
|
||||
}
|
||||
if ($config === null) {
|
||||
$arch = arch2gnu(php_uname('m'));
|
||||
$os = match (PHP_OS_FAMILY) {
|
||||
'Linux' => 'linux',
|
||||
'Windows' => 'win',
|
||||
'BSD' => 'freebsd',
|
||||
'Darwin' => 'macos',
|
||||
default => throw new WrongUsageException('Unsupported OS!'),
|
||||
};
|
||||
$config = Config::getPkg("{$pkg_name}-{$arch}-{$os}");
|
||||
}
|
||||
if ($config === null) {
|
||||
throw new WrongUsageException("Package [{$pkg_name}] does not exist, please check the name and correct it !");
|
||||
}
|
||||
|
||||
// Download package
|
||||
Downloader::downloadPackage($pkg_name, $config, $force);
|
||||
// After download, read lock file name
|
||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true);
|
||||
$filename = DOWNLOAD_PATH . '/' . ($lock[$pkg_name]['filename'] ?? $lock[$pkg_name]['dirname']);
|
||||
$extract = $lock[$pkg_name]['move_path'] === null ? (PKG_ROOT_PATH . '/' . $pkg_name) : $lock[$pkg_name]['move_path'];
|
||||
FileSystem::extractPackage($pkg_name, $filename, $extract);
|
||||
|
||||
// if contains extract-files, we just move this file to destination, and remove extract dir
|
||||
if (is_array($config['extract-files'] ?? null) && is_assoc_array($config['extract-files'])) {
|
||||
$scandir = FileSystem::scanDirFiles($extract, true, true);
|
||||
foreach ($config['extract-files'] as $file => $target) {
|
||||
$target = FileSystem::convertPath(FileSystem::replacePathVariable($target));
|
||||
if (!is_dir($dir = dirname($target))) {
|
||||
f_mkdir($dir, 0755, true);
|
||||
}
|
||||
logger()->debug("Moving package [{$pkg_name}] file {$file} to {$target}");
|
||||
// match pattern, needs to scan dir
|
||||
$file = FileSystem::convertPath($file);
|
||||
$found = false;
|
||||
foreach ($scandir as $item) {
|
||||
if (match_pattern($file, $item)) {
|
||||
$file = $item;
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($found === false) {
|
||||
throw new FileSystemException('Unable to find extract-files item: ' . $file);
|
||||
}
|
||||
rename(FileSystem::convertPath($extract . '/' . $file), $target);
|
||||
}
|
||||
FileSystem::removeDir($extract);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
|
||||
class SourceExtractor
|
||||
class SourceManager
|
||||
{
|
||||
/**
|
||||
* @throws WrongUsageException
|
||||
@@ -106,7 +106,7 @@ class SourcePatcher
|
||||
}
|
||||
$patch_list = $list ?? $default;
|
||||
$patches = [];
|
||||
$serial = ['80', '81', '82', '83'];
|
||||
$serial = ['80', '81', '82', '83', '84'];
|
||||
foreach ($patch_list as $patchName) {
|
||||
if (file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}.patch")) {
|
||||
$patches[] = "sapi/micro/patches/{$patchName}.patch";
|
||||
@@ -197,7 +197,6 @@ class SourcePatcher
|
||||
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_STRLCAT 1$/m', '');
|
||||
}
|
||||
if ($builder instanceof UnixBuilderBase) {
|
||||
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_OPENPTY 1$/m', '');
|
||||
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'install-micro', '');
|
||||
}
|
||||
|
||||
|
||||
@@ -70,4 +70,12 @@ class ConfigValidator
|
||||
{
|
||||
is_array($data) || throw new ValidationException('ext.json is broken');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public static function validatePkgs(mixed $data): void
|
||||
{
|
||||
is_array($data) || throw new ValidationException('pkg.json is broken');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,90 +5,164 @@ declare(strict_types=1);
|
||||
namespace SPC\util;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
|
||||
/**
|
||||
* 依赖处理工具类,包含处理扩展、库的依赖列表顺序等
|
||||
* Dependency processing tool class, including processing extensions, library dependency list order, etc.
|
||||
*/
|
||||
class DependencyUtil
|
||||
{
|
||||
/**
|
||||
* Obtain the dependent lib list according to the required ext list, and sort according to the dependency
|
||||
* Convert platform extensions to library dependencies and suggestions.
|
||||
*
|
||||
* @param array $exts extensions list
|
||||
* @param array $additional_libs List of additional libraries to add to activate the extra library features triggered by lib-suggests
|
||||
* @return array Returns an array containing three arrays, [extensions, libraries, not included extensions]
|
||||
* @throws WrongUsageException
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getExtLibsByDeps(array $exts, array $additional_libs = []): array
|
||||
public static function platExtToLibs(): array
|
||||
{
|
||||
$sorted = [];
|
||||
$visited = [];
|
||||
$not_included_exts = [];
|
||||
foreach ($exts as $ext) {
|
||||
if (!isset($visited[$ext])) {
|
||||
self::visitExtDeps($ext, $visited, $sorted);
|
||||
}
|
||||
$exts = Config::getExts();
|
||||
$libs = Config::getLibs();
|
||||
$dep_list = [];
|
||||
foreach ($exts as $ext_name => $ext) {
|
||||
// convert ext-depends value to ext@xxx
|
||||
$ext_depends = Config::getExt($ext_name, 'ext-depends', []);
|
||||
$ext_depends = array_map(fn ($x) => "ext@{$x}", $ext_depends);
|
||||
// convert ext-suggests value to ext@xxx
|
||||
$ext_suggests = Config::getExt($ext_name, 'ext-suggests', []);
|
||||
$ext_suggests = array_map(fn ($x) => "ext@{$x}", $ext_suggests);
|
||||
// merge ext-depends with lib-depends
|
||||
$lib_depends = Config::getExt($ext_name, 'lib-depends', []);
|
||||
$depends = array_merge($ext_depends, $lib_depends);
|
||||
// merge ext-suggests with lib-suggests
|
||||
$lib_suggests = Config::getExt($ext_name, 'lib-suggests', []);
|
||||
$suggests = array_merge($ext_suggests, $lib_suggests);
|
||||
$dep_list["ext@{$ext_name}"] = [
|
||||
'depends' => $depends,
|
||||
'suggests' => $suggests,
|
||||
];
|
||||
}
|
||||
$sorted_suggests = [];
|
||||
$visited_suggests = [];
|
||||
$final = [];
|
||||
foreach ($exts as $ext) {
|
||||
if (!isset($visited_suggests[$ext])) {
|
||||
self::visitExtAllDeps($ext, $visited_suggests, $sorted_suggests);
|
||||
}
|
||||
foreach ($libs as $lib_name => $lib) {
|
||||
$dep_list[$lib_name] = [
|
||||
'depends' => Config::getLib($lib_name, 'lib-depends', []),
|
||||
'suggests' => Config::getLib($lib_name, 'lib-suggests', []),
|
||||
];
|
||||
}
|
||||
foreach ($sorted_suggests as $suggest) {
|
||||
if (in_array($suggest, $sorted)) {
|
||||
$final[] = $suggest;
|
||||
}
|
||||
}
|
||||
$libs = $additional_libs;
|
||||
|
||||
foreach ($final as $ext) {
|
||||
if (!in_array($ext, $exts)) {
|
||||
$not_included_exts[] = $ext;
|
||||
}
|
||||
foreach (Config::getExt($ext, 'lib-depends', []) as $lib) {
|
||||
if (!in_array($lib, $libs)) {
|
||||
$libs[] = $lib;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [$final, self::getLibsByDeps($libs), $not_included_exts];
|
||||
// here is an array that only contains dependency map
|
||||
return $dep_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 lib 库的依赖关系进行一个排序,同时返回多出来的依赖列表
|
||||
*
|
||||
* @param array $libs 要排序的 libs 列表
|
||||
* @return array 排序后的列表
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
* @throws WrongUsageException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getLibsByDeps(array $libs): array
|
||||
public static function getLibs(array $libs, bool $include_suggested_libs = false): array
|
||||
{
|
||||
$sorted = [];
|
||||
$visited = [];
|
||||
$dep_list = self::platExtToLibs();
|
||||
|
||||
// 遍历所有
|
||||
foreach ($libs as $lib) {
|
||||
if (!isset($visited[$lib])) {
|
||||
self::visitLibDeps($lib, $visited, $sorted);
|
||||
if ($include_suggested_libs) {
|
||||
foreach ($dep_list as $name => $obj) {
|
||||
foreach ($obj['suggests'] as $id => $suggest) {
|
||||
if (!str_starts_with($suggest, 'ext@')) {
|
||||
$dep_list[$name]['depends'][] = $suggest;
|
||||
array_splice($dep_list[$name]['suggests'], $id, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$final = self::doVisitPlat($libs, $dep_list);
|
||||
|
||||
$libs_final = [];
|
||||
foreach ($final as $item) {
|
||||
if (!str_starts_with($item, 'ext@')) {
|
||||
$libs_final[] = $item;
|
||||
}
|
||||
}
|
||||
return $libs_final;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException|WrongUsageException
|
||||
*/
|
||||
public static function getExtsAndLibs(array $exts, array $additional_libs = [], bool $include_suggested_exts = false, bool $include_suggested_libs = false): array
|
||||
{
|
||||
$dep_list = self::platExtToLibs();
|
||||
|
||||
// include suggested extensions
|
||||
if ($include_suggested_exts) {
|
||||
// check every deps suggests contains ext@
|
||||
foreach ($dep_list as $name => $obj) {
|
||||
foreach ($obj['suggests'] as $id => $suggest) {
|
||||
if (str_starts_with($suggest, 'ext@')) {
|
||||
$dep_list[$name]['depends'][] = $suggest;
|
||||
array_splice($dep_list[$name]['suggests'], $id, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// include suggested libraries
|
||||
if ($include_suggested_libs) {
|
||||
// check every deps suggests
|
||||
foreach ($dep_list as $name => $obj) {
|
||||
foreach ($obj['suggests'] as $id => $suggest) {
|
||||
if (!str_starts_with($suggest, 'ext@')) {
|
||||
$dep_list[$name]['depends'][] = $suggest;
|
||||
array_splice($dep_list[$name]['suggests'], $id, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert ext_name to ext@ext_name
|
||||
$origin_exts = $exts;
|
||||
$exts = array_map(fn ($x) => "ext@{$x}", $exts);
|
||||
$exts = array_merge($exts, $additional_libs);
|
||||
|
||||
$final = self::doVisitPlat($exts, $dep_list);
|
||||
|
||||
// revert array
|
||||
$exts_final = [];
|
||||
$libs_final = [];
|
||||
$not_included_final = [];
|
||||
foreach ($final as $item) {
|
||||
if (str_starts_with($item, 'ext@')) {
|
||||
$tmp = substr($item, 4);
|
||||
if (!in_array($tmp, $origin_exts)) {
|
||||
$not_included_final[] = $tmp;
|
||||
}
|
||||
$exts_final[] = $tmp;
|
||||
} else {
|
||||
$libs_final[] = $item;
|
||||
}
|
||||
}
|
||||
return [$exts_final, $libs_final, $not_included_final];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
private static function doVisitPlat(array $deps, array $dep_list): array
|
||||
{
|
||||
// default: get extension exts and libs sorted by dep_list
|
||||
$sorted = [];
|
||||
$visited = [];
|
||||
foreach ($deps as $ext_name) {
|
||||
if (!isset($dep_list[$ext_name])) {
|
||||
$ext_name = str_starts_with($ext_name, 'ext@') ? ('Extension [' . substr($ext_name, 4) . ']') : ('Library [' . $ext_name . ']');
|
||||
throw new WrongUsageException("{$ext_name} not exist !");
|
||||
}
|
||||
if (!isset($visited[$ext_name])) {
|
||||
self::visitPlatDeps($ext_name, $dep_list, $visited, $sorted);
|
||||
}
|
||||
}
|
||||
$sorted_suggests = [];
|
||||
$visited_suggests = [];
|
||||
$final = [];
|
||||
foreach ($libs as $lib) {
|
||||
if (!isset($visited_suggests[$lib])) {
|
||||
self::visitLibAllDeps($lib, $visited_suggests, $sorted_suggests);
|
||||
foreach ($deps as $ext_name) {
|
||||
if (!isset($visited_suggests[$ext_name])) {
|
||||
self::visitPlatAllDeps($ext_name, $dep_list, $visited_suggests, $sorted_suggests);
|
||||
}
|
||||
}
|
||||
foreach ($sorted_suggests as $suggest) {
|
||||
@@ -99,49 +173,7 @@ class DependencyUtil
|
||||
return $final;
|
||||
}
|
||||
|
||||
public static function getAllExtLibsByDeps(array $exts): array
|
||||
{
|
||||
$sorted = [];
|
||||
$visited = [];
|
||||
$not_included_exts = [];
|
||||
foreach ($exts as $ext) {
|
||||
if (!isset($visited[$ext])) {
|
||||
self::visitExtAllDeps($ext, $visited, $sorted);
|
||||
}
|
||||
}
|
||||
$libs = [];
|
||||
foreach ($sorted as $ext) {
|
||||
if (!in_array($ext, $exts)) {
|
||||
$not_included_exts[] = $ext;
|
||||
}
|
||||
foreach (array_merge(Config::getExt($ext, 'lib-depends', []), Config::getExt($ext, 'lib-suggests', [])) as $dep) {
|
||||
if (!in_array($dep, $libs)) {
|
||||
$libs[] = $dep;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [$sorted, self::getAllLibsByDeps($libs), $not_included_exts];
|
||||
}
|
||||
|
||||
public static function getAllLibsByDeps(array $libs): array
|
||||
{
|
||||
$sorted = [];
|
||||
$visited = [];
|
||||
|
||||
foreach ($libs as $lib) {
|
||||
if (!isset($visited[$lib])) {
|
||||
self::visitLibAllDeps($lib, $visited, $sorted);
|
||||
}
|
||||
}
|
||||
return $sorted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
private static function visitLibAllDeps(string $lib_name, array &$visited, array &$sorted): void
|
||||
private static function visitPlatAllDeps(string $lib_name, array $dep_list, array &$visited, array &$sorted): void
|
||||
{
|
||||
// 如果已经识别到了,那就不管
|
||||
if (isset($visited[$lib_name])) {
|
||||
@@ -149,37 +181,13 @@ class DependencyUtil
|
||||
}
|
||||
$visited[$lib_name] = true;
|
||||
// 遍历该依赖的所有依赖(此处的 getLib 如果检测到当前库不存在的话,会抛出异常)
|
||||
foreach (array_merge(Config::getLib($lib_name, 'lib-depends', []), Config::getLib($lib_name, 'lib-suggests', [])) as $dep) {
|
||||
self::visitLibDeps($dep, $visited, $sorted);
|
||||
foreach (array_merge($dep_list[$lib_name]['depends'], $dep_list[$lib_name]['suggests']) as $dep) {
|
||||
self::visitPlatAllDeps($dep, $dep_list, $visited, $sorted);
|
||||
}
|
||||
$sorted[] = $lib_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
private static function visitExtAllDeps(string $ext_name, array &$visited, array &$sorted): void
|
||||
{
|
||||
// 如果已经识别到了,那就不管
|
||||
if (isset($visited[$ext_name])) {
|
||||
return;
|
||||
}
|
||||
$visited[$ext_name] = true;
|
||||
// 遍历该依赖的所有依赖(此处的 getLib 如果检测到当前库不存在的话,会抛出异常)
|
||||
foreach (array_merge(Config::getExt($ext_name, 'ext-depends', []), Config::getExt($ext_name, 'ext-suggests', [])) as $dep) {
|
||||
self::visitExtDeps($dep, $visited, $sorted);
|
||||
}
|
||||
$sorted[] = $ext_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
private static function visitLibDeps(string $lib_name, array &$visited, array &$sorted): void
|
||||
private static function visitPlatDeps(string $lib_name, array $dep_list, array &$visited, array &$sorted): void
|
||||
{
|
||||
// 如果已经识别到了,那就不管
|
||||
if (isset($visited[$lib_name])) {
|
||||
@@ -187,26 +195,9 @@ class DependencyUtil
|
||||
}
|
||||
$visited[$lib_name] = true;
|
||||
// 遍历该依赖的所有依赖(此处的 getLib 如果检测到当前库不存在的话,会抛出异常)
|
||||
foreach (Config::getLib($lib_name, 'lib-depends', []) as $dep) {
|
||||
self::visitLibDeps($dep, $visited, $sorted);
|
||||
foreach ($dep_list[$lib_name]['depends'] as $dep) {
|
||||
self::visitPlatDeps($dep, $dep_list, $visited, $sorted);
|
||||
}
|
||||
$sorted[] = $lib_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
private static function visitExtDeps(string $ext_name, array &$visited, array &$sorted): void
|
||||
{
|
||||
if (isset($visited[$ext_name])) {
|
||||
return;
|
||||
}
|
||||
$visited[$ext_name] = true;
|
||||
foreach (Config::getExt($ext_name, 'ext-depends', []) as $dep) {
|
||||
self::visitExtDeps($dep, $visited, $sorted);
|
||||
}
|
||||
$sorted[] = $ext_name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ define('START_TIME', microtime(true));
|
||||
define('BUILD_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_ROOT_PATH')) ? $a : (WORKING_DIR . '/buildroot')));
|
||||
define('SOURCE_PATH', FileSystem::convertPath(is_string($a = getenv('SOURCE_PATH')) ? $a : (WORKING_DIR . '/source')));
|
||||
define('DOWNLOAD_PATH', FileSystem::convertPath(is_string($a = getenv('DOWNLOAD_PATH')) ? $a : (WORKING_DIR . '/downloads')));
|
||||
define('PKG_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('PKG_ROOT_PATH')) ? $a : (WORKING_DIR . '/pkgroot')));
|
||||
define('BUILD_BIN_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_BIN_PATH')) ? $a : (BUILD_ROOT_PATH . '/bin')));
|
||||
define('BUILD_LIB_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_LIB_PATH')) ? $a : (BUILD_ROOT_PATH . '/lib')));
|
||||
define('BUILD_INCLUDE_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_INCLUDE_PATH')) ? $a : (BUILD_ROOT_PATH . '/include')));
|
||||
|
||||
69
src/globals/extra/Makefile-sqlite
Normal file
69
src/globals/extra/Makefile-sqlite
Normal file
@@ -0,0 +1,69 @@
|
||||
|
||||
CC=cl.exe /nologo
|
||||
AR=lib.exe /nologo
|
||||
LINK=link.exe /nologo
|
||||
|
||||
!IF "" == "$(MACHINE)"
|
||||
MACHINE=x64
|
||||
!ENDIF
|
||||
|
||||
!IF "" == "$(CRT)"
|
||||
CRT=vc15
|
||||
!ENDIF
|
||||
|
||||
!IF "" == "$(PREFIX)"
|
||||
PREFIX="$(CRT)-$(MACHINE)"
|
||||
!ENDIF
|
||||
|
||||
COMMON_CFLAGS=/D SQLITE_THREADSAFE=1 /DSQLITE_ENABLE_FTS3=1 /D SQLITE_ENABLE_FTS4=1 /D SQLITE_ENABLE_FTS5=1 /D SQLITE_ENABLE_JSON1=1 /D SQLITE_ENABLE_COLUMN_METADATA=1 /D SQLITE_CORE=1
|
||||
!IF "$(DEBUG)"=="1"
|
||||
SQLITE3_STATIC_BASE=libsqlite3_a_debug
|
||||
SQLITE3_DLL_BASE=libsqlite3_debug
|
||||
SQLITE3_EXE_BASE=sqlite3
|
||||
CFLAGS=$(COMMON_CFLAGS) /Zi /MDd /Od /W3
|
||||
LDFLAGS=/DEBUG /GUARD:CF /INCREMENTAL:NO
|
||||
!ELSE
|
||||
SQLITE3_STATIC_BASE=libsqlite3_a
|
||||
SQLITE3_DLL_BASE=libsqlite3
|
||||
SQLITE3_EXE_BASE=sqlite3
|
||||
CFLAGS=$(COMMON_CFLAGS) /Zi /MT /guard:cf /Zc:inline /Qspectre /Ox /W3 /GF /GL /Gw
|
||||
LDFLAGS=/GUARD:CF /INCREMENTAL:NO /NXCOMPAT /DYNAMICBASE
|
||||
!ENDIF
|
||||
|
||||
|
||||
all: $(SQLITE3_STATIC_BASE).lib $(SQLITE3_EXE_BASE).exe $(SQLITE3_DLL_BASE).dll
|
||||
|
||||
install: all
|
||||
if not exist $(PREFIX)\bin mkdir $(PREFIX)\bin
|
||||
if not exist $(PREFIX)\include mkdir $(PREFIX)\include
|
||||
if not exist $(PREFIX)\lib mkdir $(PREFIX)\lib
|
||||
copy /Y sqlite3.h $(PREFIX)\include
|
||||
copy /Y sqlite3ext.h $(PREFIX)\include
|
||||
copy /Y $(SQLITE3_STATIC_BASE).lib $(PREFIX)\lib
|
||||
copy /Y $(SQLITE3_STATIC_BASE).pdb $(PREFIX)\lib
|
||||
copy /Y $(SQLITE3_DLL_BASE).lib $(PREFIX)\lib
|
||||
copy /Y $(SQLITE3_DLL_BASE).pdb $(PREFIX)\bin
|
||||
copy /Y $(SQLITE3_DLL_BASE).dll $(PREFIX)\bin
|
||||
copy /Y $(SQLITE3_EXE_BASE).exe $(PREFIX)\bin
|
||||
copy /Y $(SQLITE3_EXE_BASE).pdb $(PREFIX)\bin
|
||||
|
||||
install-static: $(SQLITE3_STATIC_BASE).lib
|
||||
if not exist $(PREFIX)\include mkdir $(PREFIX)\include
|
||||
if not exist $(PREFIX)\lib mkdir $(PREFIX)\lib
|
||||
copy /Y sqlite3.h $(PREFIX)\include
|
||||
copy /Y sqlite3ext.h $(PREFIX)\include
|
||||
copy /Y $(SQLITE3_STATIC_BASE).lib $(PREFIX)\lib
|
||||
|
||||
clean:
|
||||
del *.obj *.lib *.exe *.pdb *.dll *.exp
|
||||
|
||||
$(SQLITE3_STATIC_BASE).lib: sqlite3.c sqlite3.h
|
||||
$(CC) $(CFLAGS) /Fd$(SQLITE3_STATIC_BASE).pdb /c sqlite3.c
|
||||
$(AR) sqlite3.obj /OUT:$(SQLITE3_STATIC_BASE).lib
|
||||
|
||||
$(SQLITE3_EXE_BASE).exe: shell.c sqlite3.c sqlite3.h
|
||||
$(CC) $(CFLAGS) shell.c sqlite3.c /Fd$(SQLITE3_EXE_BASE).pdb /Fe$(SQLITE3_EXE_BASE).exe
|
||||
|
||||
$(SQLITE3_DLL_BASE).dll: sqlite3.c sqlite3.h
|
||||
$(CC) $(CFLAGS) /DSQLITE_API=__declspec(dllexport) /Fd$(SQLITE3_DLL_BASE).pdb /c sqlite3.c
|
||||
$(LINK) /DLL /OUT:$(SQLITE3_DLL_BASE).dll sqlite3.obj
|
||||
@@ -47,6 +47,13 @@ function arch2gnu(string $arch): string
|
||||
};
|
||||
}
|
||||
|
||||
function match_pattern(string $pattern, string $subject): bool
|
||||
{
|
||||
$pattern = str_replace(['\*', '\\\\.*'], ['.*', '\*'], preg_quote($pattern, '/'));
|
||||
$pattern = '/^' . $pattern . '$/i';
|
||||
return preg_match($pattern, $subject) === 1;
|
||||
}
|
||||
|
||||
function quote(string $str, string $quote = '"'): string
|
||||
{
|
||||
return $quote . $str . $quote;
|
||||
|
||||
@@ -13,13 +13,13 @@ declare(strict_types=1);
|
||||
|
||||
// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
|
||||
$extensions = match (PHP_OS_FAMILY) {
|
||||
'Linux', 'Darwin' => 'calendar,ctype,curl,dom,fileinfo,filter,gd,iconv,imagick,mbregex,mbstring,mysqli,mysqlnd,openssl,pcntl,pdo,pdo_mysql,pdo_sqlite,phar,posix,rar,redis,session,simplexml,soap,sockets,sqlite3,swoole,swoole-hook-mysql,tokenizer,xlswriter,xml,xmlreader,xmlwriter,zip,zlib',
|
||||
'Windows' => 'mbstring,openssl',
|
||||
'Linux', 'Darwin' => 'imagick,zstd,bz2,zip,xml,dom',
|
||||
'Windows' => 'mbstring,pdo_sqlite,mbregex,ffi',
|
||||
};
|
||||
|
||||
// If you want to test lib-suggests feature with extension, add them below (comma separated, example `libwebp,libavif`).
|
||||
$with_libs = match (PHP_OS_FAMILY) {
|
||||
'Linux', 'Darwin' => '',
|
||||
'Linux', 'Darwin' => 'xz',
|
||||
'Windows' => '',
|
||||
};
|
||||
|
||||
@@ -27,7 +27,7 @@ $with_libs = match (PHP_OS_FAMILY) {
|
||||
// You can use `common`, `bulk`, `minimal` or `none`.
|
||||
// note: combination is only available for *nix platform. Windows must use `none` combination
|
||||
$base_combination = match (PHP_OS_FAMILY) {
|
||||
'Linux', 'Darwin' => 'none',
|
||||
'Linux', 'Darwin' => 'minimal',
|
||||
'Windows' => 'none',
|
||||
};
|
||||
|
||||
@@ -62,6 +62,7 @@ $final_libs = trim($with_libs, $trim_value);
|
||||
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$final_extensions_cmd = '"' . $final_extensions . '"';
|
||||
$final_libs = $final_libs === '' ? '' : ('"' . $final_libs . '"');
|
||||
} else {
|
||||
$final_extensions_cmd = $final_extensions;
|
||||
}
|
||||
@@ -69,7 +70,7 @@ if (PHP_OS_FAMILY === 'Windows') {
|
||||
echo match ($argv[1]) {
|
||||
'extensions' => $final_extensions,
|
||||
'libs' => $final_libs,
|
||||
'libs_cmd' => ($final_libs === '' ? '' : (' --with-libs="' . $final_libs . '"')),
|
||||
'cmd' => $final_extensions_cmd . ($final_libs === '' ? '' : (' --with-libs="' . $final_libs . '"')),
|
||||
'libs_cmd' => ($final_libs === '' ? '' : (' --with-libs=' . $final_libs)),
|
||||
'cmd' => $final_extensions_cmd . ($final_libs === '' ? '' : (' --with-libs=' . $final_libs)),
|
||||
default => '',
|
||||
};
|
||||
|
||||
25
src/globals/tests/gettext.php
Normal file
25
src/globals/tests/gettext.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
assert(function_exists('gettext'));
|
||||
assert(function_exists('bindtextdomain'));
|
||||
assert(function_exists('textdomain'));
|
||||
assert(function_exists('bind_textdomain_codeset'));
|
||||
|
||||
if (!is_dir('locale/en_US/LC_MESSAGES/')) {
|
||||
mkdir('locale/en_US/LC_MESSAGES/', 0755, true);
|
||||
}
|
||||
if (!file_exists('locale/en_US/LC_MESSAGES/test.mo')) {
|
||||
$mo = '3hIElQAAAAACAAAAHAAAACwAAAAFAAAAPAAAAAAAAABQAAAABgAAAFEAAAAXAQAAWAAAAAcAAABwAQAAAQAAAAAAAAAAAAAAAgAAAAAAAAAA56S65L6LAFByb2plY3QtSWQtVmVyc2lvbjogUEFDS0FHRSBWRVJTSU9OClJlcG9ydC1Nc2dpZC1CdWdzLVRvOiAKUE8tUmV2aXNpb24tRGF0ZTogWUVBUi1NTy1EQSBITzpNSStaT05FCkxhc3QtVHJhbnNsYXRvcjogRlVMTCBOQU1FIDxFTUFJTEBBRERSRVNTPgpMYW5ndWFnZS1UZWFtOiBMQU5HVUFHRSA8TExAbGkub3JnPgpMYW5ndWFnZTogCk1JTUUtVmVyc2lvbjogMS4wCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD1VVEYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA4Yml0CgBFeGFtcGxlAA==';
|
||||
file_put_contents('locale/en_US/LC_MESSAGES/test.mo', base64_decode($mo));
|
||||
}
|
||||
putenv('LANG=en_US');
|
||||
setlocale(LC_ALL, 'en_US');
|
||||
|
||||
$domain = 'test';
|
||||
bindtextdomain($domain, 'locale/');
|
||||
bind_textdomain_codeset($domain, 'UTF-8');
|
||||
textdomain($domain);
|
||||
|
||||
assert(gettext(json_decode('"\u793a\u4f8b"', true)) === 'Example');
|
||||
90
tests/SPC/util/DependencyUtilTest.php
Normal file
90
tests/SPC/util/DependencyUtilTest.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\Tests\util;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\util\DependencyUtil;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class DependencyUtilTest extends TestCase
|
||||
{
|
||||
public function testGetExtLibsByDeps(): void
|
||||
{
|
||||
// example
|
||||
Config::$source = [
|
||||
'test1' => [
|
||||
'type' => 'url',
|
||||
'url' => 'https://pecl.php.net/get/APCu',
|
||||
'filename' => 'apcu.tgz',
|
||||
'license' => [
|
||||
'type' => 'file',
|
||||
'path' => 'LICENSE',
|
||||
],
|
||||
],
|
||||
];
|
||||
Config::$lib = [
|
||||
'libaaa' => [
|
||||
'source' => 'test1',
|
||||
'static-libs' => ['libaaa.a'],
|
||||
'lib-depends' => ['libbbb', 'libccc'],
|
||||
'lib-suggests' => ['libeee'],
|
||||
],
|
||||
'libbbb' => [
|
||||
'source' => 'test1',
|
||||
'static-libs' => ['libbbb.a'],
|
||||
'lib-suggests' => ['libccc'],
|
||||
],
|
||||
'libccc' => [
|
||||
'source' => 'test1',
|
||||
'static-libs' => ['libccc.a'],
|
||||
],
|
||||
'libeee' => [
|
||||
'source' => 'test1',
|
||||
'static-libs' => ['libeee.a'],
|
||||
'lib-suggests' => ['libfff'],
|
||||
],
|
||||
'libfff' => [
|
||||
'source' => 'test1',
|
||||
'static-libs' => ['libfff.a'],
|
||||
],
|
||||
];
|
||||
Config::$ext = [
|
||||
'ext-a' => [
|
||||
'type' => 'builtin',
|
||||
'lib-depends' => ['libaaa'],
|
||||
'ext-suggests' => ['ext-b'],
|
||||
],
|
||||
'ext-b' => [
|
||||
'type' => 'builtin',
|
||||
'lib-depends' => ['libeee'],
|
||||
],
|
||||
];
|
||||
// test getExtLibsByDeps (notmal test with ext-depends and lib-depends)
|
||||
|
||||
[$exts, $libs, $not_included] = DependencyUtil::getExtsAndLibs(['ext-a'], include_suggested_exts: true);
|
||||
$this->assertContains('libbbb', $libs);
|
||||
$this->assertContains('libccc', $libs);
|
||||
$this->assertContains('ext-b', $exts);
|
||||
$this->assertContains('ext-b', $not_included);
|
||||
// test dep order
|
||||
$this->assertIsInt($b = array_search('libbbb', $libs));
|
||||
$this->assertIsInt($c = array_search('libccc', $libs));
|
||||
$this->assertIsInt($a = array_search('libaaa', $libs));
|
||||
// libbbb, libaaa
|
||||
$this->assertTrue($b < $a);
|
||||
$this->assertTrue($c < $a);
|
||||
$this->assertTrue($c < $b);
|
||||
}
|
||||
|
||||
public function testNotExistExtException(): void
|
||||
{
|
||||
$this->expectException(WrongUsageException::class);
|
||||
DependencyUtil::getExtsAndLibs(['sdsd']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user