mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-08 17:35:36 +08:00
Compare commits
80 Commits
4872ff58bb
...
2.0-rc5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a00f8945ba | ||
|
|
2bc02bdaac | ||
|
|
b97327d6d7 | ||
|
|
61d1507a4d | ||
|
|
a6f07051c3 | ||
|
|
75417d15b7 | ||
|
|
4bab7ecfab | ||
|
|
7298e2441b | ||
|
|
1ed104d2f6 | ||
|
|
cf6125b9cc | ||
|
|
f2cfe33cdd | ||
|
|
a1e4125ded | ||
|
|
7a1433a994 | ||
|
|
460238a6b0 | ||
|
|
2a197487d5 | ||
|
|
51ce2befd8 | ||
|
|
e909dd15b0 | ||
|
|
aaf712be3c | ||
|
|
725e6b25dc | ||
|
|
ea322c0d3b | ||
|
|
f7730735c0 | ||
|
|
752b88f62d | ||
|
|
6131e1881b | ||
|
|
980da4ea0f | ||
|
|
b698ae2f50 | ||
|
|
50632f6527 | ||
|
|
6d4755d8c9 | ||
|
|
75b85c26dc | ||
|
|
1c8bbfbbdf | ||
|
|
c5a70f101a | ||
|
|
956c87a657 | ||
|
|
778b0eadcd | ||
|
|
b8e6f9d1be | ||
|
|
0e024a8c43 | ||
|
|
65b0bd01c8 | ||
|
|
3745dfc931 | ||
|
|
0186ae5ff2 | ||
|
|
f1eacac4fd | ||
|
|
4abe0064e6 | ||
|
|
cbc3adbec0 | ||
|
|
0902f80b46 | ||
|
|
47101d058b | ||
|
|
bc978ecbde | ||
|
|
a2cb5165d3 | ||
|
|
e582fa8b22 | ||
|
|
8ec8838634 | ||
|
|
8f259ffed9 | ||
|
|
74c2cf824b | ||
|
|
9ea3b04e82 | ||
|
|
99cb8c77b7 | ||
|
|
7056280c57 | ||
|
|
c14421c9ca | ||
|
|
97eff64b5b | ||
|
|
e522258693 | ||
|
|
b84c68fe11 | ||
|
|
8505feaa66 | ||
|
|
41f49c20ff | ||
|
|
1912ae36e6 | ||
|
|
08efc81cf0 | ||
|
|
a3b09c69cc | ||
|
|
5cf105c3a5 | ||
|
|
7408781d13 | ||
|
|
0afc8ea2c3 | ||
|
|
67b073776c | ||
|
|
8b48cf7f70 | ||
|
|
63287dd9c4 | ||
|
|
fc7ac921e2 | ||
|
|
57e3193a2a | ||
|
|
388f547878 | ||
|
|
f2e483540b | ||
|
|
7d31c371e0 | ||
|
|
bc750733d5 | ||
|
|
a66e22d9ed | ||
|
|
cc5974d07d | ||
|
|
085437e925 | ||
|
|
fa17a48483 | ||
|
|
78c1484570 | ||
|
|
9c2ea79bec | ||
|
|
b7ffe3fd1f | ||
|
|
dac14ae16e |
9
.github/workflows/build-linux-x86_64.yml
vendored
9
.github/workflows/build-linux-x86_64.yml
vendored
@@ -77,10 +77,10 @@ jobs:
|
||||
|
||||
# If there's no dependencies cache, fetch sources, with or without debug
|
||||
- if: steps.cache-download.outputs.cache-hit != 'true'
|
||||
run: CACHE_API_EXEC=yes ./bin/spc download --with-php=${{ inputs.version }} --all ${{ env.SPC_BUILD_DEBUG }}
|
||||
run: CACHE_API_EXEC=yes ./bin/spc-alpine-docker download --with-php=${{ inputs.version }} --all ${{ env.SPC_BUILD_DEBUG }}
|
||||
|
||||
# Run build command
|
||||
- run: ./bin/spc build ${{ inputs.extensions }} ${{ env.SPC_BUILD_DEBUG }} ${{ env.SPC_BUILD_CLI }} ${{ env.SPC_BUILD_MICRO }} ${{ env.SPC_BUILD_FPM }}
|
||||
- run: ./bin/spc-alpine-docker build ${{ inputs.extensions }} ${{ env.SPC_BUILD_DEBUG }} ${{ env.SPC_BUILD_CLI }} ${{ env.SPC_BUILD_MICRO }} ${{ env.SPC_BUILD_FPM }}
|
||||
|
||||
# Upload cli executable
|
||||
- if: ${{ inputs.build-cli == true }}
|
||||
@@ -115,8 +115,3 @@ jobs:
|
||||
buildroot/build-extensions.json
|
||||
buildroot/build-libraries.json
|
||||
|
||||
# Upload downloaded files
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: download-files
|
||||
path: downloads/
|
||||
|
||||
5
.github/workflows/build-macos-x86_64.yml
vendored
5
.github/workflows/build-macos-x86_64.yml
vendored
@@ -115,8 +115,3 @@ jobs:
|
||||
buildroot/build-extensions.json
|
||||
buildroot/build-libraries.json
|
||||
|
||||
# Upload downloaded files
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: download-files
|
||||
path: downloads/
|
||||
|
||||
40
.github/workflows/download-cache.yml
vendored
Normal file
40
.github/workflows/download-cache.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: archive download sources weekly
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "* 14 * * 5"
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
jobs:
|
||||
download:
|
||||
name: cache download sources
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: [ "8.0", "8.1", "8.2" ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# Cache composer dependencies
|
||||
- id: cache-composer-deps
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: vendor
|
||||
key: composer-dependencies
|
||||
|
||||
# If there's no Composer cache, install dependencies
|
||||
- if: steps.cache-composer-deps.outputs.cache-hit != 'true'
|
||||
run: composer update --no-dev
|
||||
|
||||
# If there's no dependencies cache, fetch sources, with or without debug
|
||||
- if: steps.cache-download.outputs.cache-hit != 'true'
|
||||
run: ./bin/spc download --with-php=${{ matrix.php-version }} --all --debug
|
||||
|
||||
# Upload downloaded files
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: download-files-${{ matrix.php-version }}
|
||||
path: downloads/
|
||||
31
README-en.md
31
README-en.md
@@ -11,18 +11,20 @@ This feature is provided by [dixyes/phpmicro](https://github.com/dixyes/phpmicro
|
||||
|
||||
<img width="600" alt="截屏2023-05-02 15 52 33" src="https://user-images.githubusercontent.com/20330940/235610318-2ef4e3f1-278b-4ca4-99f4-b38120efc395.png">
|
||||
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
[](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml)
|
||||
[](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml)
|
||||
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
|
||||
> The project has renamed the `refactor` branch to the `main` branch, please pay attention to changing the branch name for dependent projects.
|
||||
|
||||
## Compilation Requirements
|
||||
|
||||
Yes, this project is written in PHP, pretty funny.
|
||||
But static-php-cli runtime only requires an environment above PHP 8.0 and `tokenizer`, `iconv` extension.
|
||||
But static-php-cli runtime only requires an environment above PHP 8.1 and `mbstring`, `pcntl` extension.
|
||||
|
||||
Here is the architecture support status, where `CI` represents support for GitHub Action builds,
|
||||
`Local` represents support for local builds, and blank represents not currently supported.
|
||||
@@ -37,7 +39,11 @@ Here is the architecture support status, where `CI` represents support for GitHu
|
||||
|
||||
Currently supported PHP versions for compilation are: `7.4`, `8.0`, `8.1`, `8.2`.
|
||||
|
||||
## Usage
|
||||
## Docs
|
||||
|
||||
docs here: <https://static-php-cli.zhamao.me>.
|
||||
|
||||
## Simple Usage
|
||||
|
||||
Please first select the extension you want to compile based on the extension list below.
|
||||
|
||||
@@ -49,7 +55,7 @@ If you don't compile yourself, you can download pre-compiled artifact from Actio
|
||||
|
||||
### Supported Extensions
|
||||
|
||||
[Supported Extension List](/ext-support.md)
|
||||
[Supported Extension List](https://static-php-cli.zhamao.me/en/guide/extensions.html)
|
||||
|
||||
> If there is no extension you need here, you can submit an issue.
|
||||
|
||||
@@ -100,7 +106,7 @@ Basic usage for building php and micro with some extensions:
|
||||
cd static-php-cli
|
||||
composer update
|
||||
chmod +x bin/spc
|
||||
# Check system tool dependencies, fix them automatically (only support macOS) (TODO: Linux distro support)
|
||||
# Check system tool dependencies, fix them automatically
|
||||
./bin/spc doctor
|
||||
# fetch all libraries
|
||||
./bin/spc fetch --all
|
||||
@@ -217,6 +223,13 @@ The basic principles for contributing are as follows:
|
||||
camelCase and underscore formats should be followed, and mixing within the same module is prohibited.
|
||||
- When compiling external libraries and creating patches, compatibility with different operating systems should be considered.
|
||||
|
||||
If you want to contribute document content, please go to [crazywhalecc/static-php-cli-docs](https://github.com/crazywhalecc/static-php-cli-docs).
|
||||
|
||||
Part of the English document is written by me, and part is translated by Google,
|
||||
and there may be inaccurate descriptions, strange or offensive expressions.
|
||||
If you are a native English speaker, some corrections to the documentation are welcome.
|
||||
|
||||
|
||||
## Sponsor this project
|
||||
|
||||
You can sponsor my project on [this page](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md).
|
||||
@@ -238,6 +251,6 @@ and comply with the corresponding project's LICENSE.
|
||||
|
||||
## Advanced
|
||||
|
||||
This project is pure open source project, and some modules are separated for developing.
|
||||
|
||||
This section will be improved after refactor version released.
|
||||
The refactoring branch of this project is written modularly.
|
||||
If you are interested in this project and want to join the development,
|
||||
you can refer to the [Contribution Guide](https://static-php-cli.zhamao.me) of the documentation to contribute code or documentation. (TODO)
|
||||
|
||||
24
README.md
24
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
Compile A Statically Linked PHP With Swoole and other Extensions.
|
||||
|
||||
If you are using English, see [English README](README-en.md).
|
||||
**If you are using English, see [English README](README-en.md).**
|
||||
|
||||
编译纯静态的 PHP Binary 二进制文件,带有各种扩展,让 PHP-cli 应用变得更便携!(cli SAPI)
|
||||
|
||||
@@ -12,17 +12,19 @@ If you are using English, see [English README](README-en.md).
|
||||
|
||||
<img width="600" alt="截屏2023-05-02 15 52 33" src="https://user-images.githubusercontent.com/20330940/235610318-2ef4e3f1-278b-4ca4-99f4-b38120efc395.png">
|
||||
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
[](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml)
|
||||
[](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml)
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
|
||||
> 项目已重命名 `refactor` 分支为 `main` 分支,请依赖的项目注意更改分支名称。
|
||||
|
||||
## 编译环境需求
|
||||
|
||||
是的,本项目采用 PHP 编写,编译前需要一个 PHP 环境,比较滑稽。
|
||||
但本项目默认可通过自身构建的 micro 和 static-php 二进制运行,其他只需要包含 tokenizer 扩展和 PHP 版本大于等于 8.0 即可。
|
||||
但本项目默认可通过自身构建的 micro 和 static-php 二进制运行,其他只需要包含 mbstring、pcntl 扩展和 PHP 版本大于等于 8.1 即可。
|
||||
|
||||
下面是架构支持情况,`CI` 代表支持 GitHub Action 构建,`Local` 代表支持本地构建,空 代表暂不支持。
|
||||
|
||||
@@ -36,6 +38,10 @@ If you are using English, see [English README](README-en.md).
|
||||
|
||||
目前支持编译的 PHP 版本为:`7.4`,`8.0`,`8.1`,`8.2`。
|
||||
|
||||
## 文档
|
||||
|
||||
点击这里查看文档:<https://static-php-cli.zhamao.me>。
|
||||
|
||||
## 使用
|
||||
|
||||
请先根据下方扩展列表选择你要编译的扩展。
|
||||
@@ -48,7 +54,7 @@ If you are using English, see [English README](README-en.md).
|
||||
|
||||
### 支持的扩展情况
|
||||
|
||||
[扩展支持列表](/ext-support.md)
|
||||
[扩展支持列表](https://static-php-cli.zhamao.me/zh/guide/extensions.html)
|
||||
|
||||
> 如果这里没有你需要的扩展,可以提交 Issue。
|
||||
|
||||
@@ -94,7 +100,7 @@ chmod +x bin/setup-runtime
|
||||
cd static-php-cli
|
||||
composer update
|
||||
chmod +x bin/spc
|
||||
# 检查环境依赖,并根据提示的命令安装缺失的编译工具(目前仅支持 macOS,Linux 后续会支持)
|
||||
# 检查环境依赖,并根据提示的命令安装缺失的编译工具
|
||||
./bin/spc doctor
|
||||
# 拉取所有依赖库
|
||||
./bin/spc fetch --all
|
||||
@@ -200,6 +206,8 @@ cat micro.sfx code.php > single-app && chmod +x single-app
|
||||
|
||||
另外,添加新扩展的贡献方式,可以参考下方 `进阶`。
|
||||
|
||||
如果你想贡献文档内容,请到项目仓库 [crazywhalecc/static-php-cli-docs](https://github.com/crazywhalecc/static-php-cli-docs) 贡献。
|
||||
|
||||
## 赞助本项目
|
||||
|
||||
你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。
|
||||
@@ -216,6 +224,4 @@ cat micro.sfx code.php > single-app && chmod +x single-app
|
||||
|
||||
## 进阶
|
||||
|
||||
本项目重构分支为模块化编写。
|
||||
|
||||
TODO:这部分将在基础功能完成后编写完成。
|
||||
本项目重构分支为模块化编写。如果你对本项目感兴趣,想加入开发,可以参照文档的 [贡献指南](https://static-php-cli.zhamao.me) 贡献代码或文档。(TODO)
|
||||
|
||||
@@ -9,16 +9,15 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">= 8.0",
|
||||
"ext-tokenizer": "*",
|
||||
"ext-iconv": "*",
|
||||
"php": ">= 8.1",
|
||||
"ext-mbstring": "*",
|
||||
"ext-pcntl": "*",
|
||||
"laravel/prompts": "^0.1.3",
|
||||
"symfony/console": "^6 || ^5 || ^4",
|
||||
"zhamao/logger": "^1.0",
|
||||
"crazywhalecc/cli-helper": "^0.1.0",
|
||||
"nunomaduro/collision": "*",
|
||||
"ext-pcntl": "*"
|
||||
"zhamao/logger": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nunomaduro/collision": "*",
|
||||
"friendsofphp/php-cs-fixer": "^3.2 != 3.7.0",
|
||||
"phpstan/phpstan": "^1.1",
|
||||
"captainhook/captainhook": "^5.10",
|
||||
|
||||
@@ -53,6 +53,19 @@
|
||||
"sockets"
|
||||
]
|
||||
},
|
||||
"memcached": {
|
||||
"type": "external",
|
||||
"source": "memcached",
|
||||
"arg-type": "custom",
|
||||
"cpp-extension": true,
|
||||
"lib-depends": [
|
||||
"libmemcached"
|
||||
],
|
||||
"ext-depends": [
|
||||
"session",
|
||||
"zlib"
|
||||
]
|
||||
},
|
||||
"exif": {
|
||||
"type": "builtin"
|
||||
},
|
||||
@@ -122,6 +135,14 @@
|
||||
"imagemagick"
|
||||
]
|
||||
},
|
||||
"glfw": {
|
||||
"type": "external",
|
||||
"arg-type": "custom",
|
||||
"source": "ext-glfw",
|
||||
"lib-depends": [
|
||||
"glfw"
|
||||
]
|
||||
},
|
||||
"imap": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
@@ -138,6 +159,7 @@
|
||||
},
|
||||
"intl": {
|
||||
"type": "builtin",
|
||||
"cpp-extension": true,
|
||||
"lib-depends": [
|
||||
"icu"
|
||||
]
|
||||
@@ -151,8 +173,9 @@
|
||||
},
|
||||
"mbregex": {
|
||||
"type": "builtin",
|
||||
"lib-depends": [
|
||||
"onig"
|
||||
"arg-type": "custom",
|
||||
"ext-depends": [
|
||||
"mbstring"
|
||||
]
|
||||
},
|
||||
"mbstring": {
|
||||
@@ -162,6 +185,17 @@
|
||||
"onig"
|
||||
]
|
||||
},
|
||||
"memcache": {
|
||||
"type": "external",
|
||||
"source": "ext-memcache",
|
||||
"arg-type": "custom",
|
||||
"lib-depends": [
|
||||
"zlib"
|
||||
],
|
||||
"ext-depends": [
|
||||
"session"
|
||||
]
|
||||
},
|
||||
"mongodb": {
|
||||
"type": "external",
|
||||
"source": "mongodb",
|
||||
@@ -182,7 +216,10 @@
|
||||
},
|
||||
"mysqlnd": {
|
||||
"type": "builtin",
|
||||
"arg-type-windows": "with"
|
||||
"arg-type-windows": "with",
|
||||
"lib-depends": [
|
||||
"zlib"
|
||||
]
|
||||
},
|
||||
"opcache": {
|
||||
"type": "builtin"
|
||||
@@ -211,12 +248,13 @@
|
||||
},
|
||||
"pdo_pgsql": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"arg-type": "with-prefix",
|
||||
"ext-depends": [
|
||||
"pdo"
|
||||
"pdo",
|
||||
"pgsql"
|
||||
],
|
||||
"lib-depends": [
|
||||
"pq"
|
||||
"postgresql"
|
||||
]
|
||||
},
|
||||
"pdo_sqlite": {
|
||||
@@ -230,6 +268,13 @@
|
||||
"sqlite"
|
||||
]
|
||||
},
|
||||
"pgsql": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with-prefix",
|
||||
"lib-depends": [
|
||||
"postgresql"
|
||||
]
|
||||
},
|
||||
"phar": {
|
||||
"type": "builtin",
|
||||
"ext-depends": [
|
||||
@@ -261,7 +306,10 @@
|
||||
"redis": {
|
||||
"type": "external",
|
||||
"source": "redis",
|
||||
"arg-type": "custom"
|
||||
"arg-type": "custom",
|
||||
"ext-suggests": [
|
||||
"session"
|
||||
]
|
||||
},
|
||||
"session": {
|
||||
"type": "builtin"
|
||||
@@ -320,6 +368,7 @@
|
||||
"type": "external",
|
||||
"source": "swoole",
|
||||
"arg-type": "custom",
|
||||
"cpp-extension": true,
|
||||
"lib-depends": [
|
||||
"openssl"
|
||||
],
|
||||
|
||||
@@ -15,6 +15,18 @@
|
||||
"brotli"
|
||||
]
|
||||
},
|
||||
"glfw": {
|
||||
"source": "ext-glfw",
|
||||
"static-libs-unix": [
|
||||
"libglfw3.a"
|
||||
],
|
||||
"frameworks": [
|
||||
"CoreVideo",
|
||||
"OpenGL",
|
||||
"Cocoa",
|
||||
"IOKit"
|
||||
]
|
||||
},
|
||||
"bzip2": {
|
||||
"source": "bzip2",
|
||||
"static-libs-unix": [
|
||||
@@ -381,7 +393,19 @@
|
||||
"postgresql": {
|
||||
"source": "postgresql",
|
||||
"static-libs-unix": [
|
||||
"libpg.a"
|
||||
"libpq.a",
|
||||
"libpgport.a",
|
||||
"libpgcommon.a"
|
||||
],
|
||||
"lib-depends": [
|
||||
"libiconv",
|
||||
"libxml2",
|
||||
"openssl",
|
||||
"zlib",
|
||||
"readline"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"icu"
|
||||
]
|
||||
},
|
||||
"pthreads4w": {
|
||||
@@ -450,6 +474,13 @@
|
||||
"zconf.h"
|
||||
]
|
||||
},
|
||||
"libmemcached": {
|
||||
"source": "libmemcached",
|
||||
"static-libs-unix": [
|
||||
"libmemcached.a",
|
||||
"libmemcachedutil.a"
|
||||
]
|
||||
},
|
||||
"zstd": {
|
||||
"source": "zstd",
|
||||
"static-libs-unix": [
|
||||
|
||||
@@ -6,6 +6,15 @@
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"ext-glfw": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mario-deluna/php-glfw",
|
||||
"rev": "master",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"apcu": {
|
||||
"type": "url",
|
||||
"url": "https://pecl.php.net/get/APCu",
|
||||
@@ -16,6 +25,25 @@
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"memcached": {
|
||||
"type": "url",
|
||||
"url": "https://pecl.php.net/get/memcached",
|
||||
"path": "php-src/ext/memcached",
|
||||
"filename": "memcached.tgz",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"libmemcached": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/crazywhalecc/libmemcached-macos.git",
|
||||
"rev": "master",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "COPYING"
|
||||
}
|
||||
},
|
||||
"brotli": {
|
||||
"type": "ghtar",
|
||||
"repo": "google/brotli",
|
||||
@@ -61,6 +89,16 @@
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"ext-memcache": {
|
||||
"type": "url",
|
||||
"url": "https://pecl.php.net/get/memcache",
|
||||
"path": "php-src/ext/memcache",
|
||||
"filename": "memcache.tgz",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"ext-ssh2": {
|
||||
"type": "url",
|
||||
"url": "https://pecl.php.net/get/ssh2",
|
||||
@@ -91,9 +129,8 @@
|
||||
}
|
||||
},
|
||||
"gmp": {
|
||||
"type": "filelist",
|
||||
"url": "https://gmplib.org/download/gmp/",
|
||||
"regex": "/href=\"(?<file>gmp-(?<version>[^\"]+)\\.tar\\.xz)\"/",
|
||||
"type": "ghtagtar",
|
||||
"repo": "alisw/GMP",
|
||||
"license": {
|
||||
"type": "text",
|
||||
"text": "Since version 6, GMP is distributed under the dual licenses, GNU LGPL v3 and GNU GPL v2. These licenses make the library free to use, share, and improve, and allow you to pass on the result. The GNU licenses give freedoms, but also set firm restrictions on the use with non-free programs."
|
||||
@@ -267,7 +304,7 @@
|
||||
"type": "ghrel",
|
||||
"repo": "mongodb/mongo-php-driver",
|
||||
"path": "php-src/ext/mongodb",
|
||||
"match": "mongodb.+\\.tgz",
|
||||
"match": "mongodb.+\\.zip",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "LICENSE"
|
||||
@@ -319,7 +356,8 @@
|
||||
}
|
||||
},
|
||||
"postgresql": {
|
||||
"type": "custom",
|
||||
"type": "url",
|
||||
"url": "https://ftp.postgresql.org/pub/source/v15.1/postgresql-15.1.tar.gz",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "COPYRIGHT"
|
||||
@@ -438,4 +476,4 @@
|
||||
"path": "LICENSE"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
110
ext-support.md
110
ext-support.md
@@ -1,111 +1,3 @@
|
||||
# Extension List
|
||||
|
||||
> - yes: supported and tested
|
||||
> - untested: supported, but not tested
|
||||
> - empty: not supported yet
|
||||
> - no with issue link: not supported yet due to issue
|
||||
> - partial with issue link: supported but not perfect due to issue
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
|-----------------|---------------------------------------------------------------------|----------------------------------------------------------------|---------|
|
||||
| apcu | yes, untested | yes, untested | |
|
||||
| bcmath | yes | yes | |
|
||||
| bz2 | yes | yes | |
|
||||
| calendar | yes | yes | |
|
||||
| ctype | yes | yes | |
|
||||
| curl | yes | yes | |
|
||||
| dba | yes | yes | |
|
||||
| dom | yes | yes | |
|
||||
| enchant | | | |
|
||||
| event | yes | yes | |
|
||||
| exif | yes | yes | |
|
||||
| ffi | | yes, [docs]() | |
|
||||
| filter | yes | yes | |
|
||||
| fileinfo | yes | yes | |
|
||||
| ftp | yes | yes | |
|
||||
| gd | yes | yes | |
|
||||
| gettext | | | |
|
||||
| gmp | yes | yes | |
|
||||
| iconv | yes | yes | |
|
||||
| imagick | yes | yes | |
|
||||
| inotify | yes | yes | |
|
||||
| intl | [partial](https://github.com/crazywhalecc/static-php-cli/issues/66) | yes | |
|
||||
| mbstring | yes | yes | |
|
||||
| mbregex | yes | yes | |
|
||||
| mcrypt | | [no](https://github.com/crazywhalecc/static-php-cli/issues/32) | |
|
||||
| mongodb | yes | yes | |
|
||||
| mysqli | yes | yes | |
|
||||
| mysqlnd | yes | yes | |
|
||||
| openssl | yes | yes | |
|
||||
| password-argon2 | | | |
|
||||
| pcntl | yes | yes | |
|
||||
| pdo | yes | yes | |
|
||||
| pdo_mysql | yes | yes | |
|
||||
| pdo_sqlite | yes | yes | |
|
||||
| pdo_pgsql | | | |
|
||||
| phar | yes | yes | |
|
||||
| posix | yes | yes | |
|
||||
| protobuf | yes | yes | |
|
||||
| readline | yes, untested | yes, untested | |
|
||||
| redis | yes | yes | |
|
||||
| session | yes | yes | |
|
||||
| shmop | yes | yes | |
|
||||
| simplexml | yes | yes | |
|
||||
| soap | yes | yes | |
|
||||
| sockets | yes | yes | |
|
||||
| sodium | yes | yes | |
|
||||
| sqlite3 | yes | yes | |
|
||||
| ssh2 | yes, untested | yes, untested | |
|
||||
| swow | yes | yes | |
|
||||
| swoole | [partial](https://github.com/crazywhalecc/static-php-cli/issues/51) | yes | |
|
||||
| tokenizer | yes | yes | |
|
||||
| xlswriter | yes | yes | |
|
||||
| xml | yes | yes | |
|
||||
| xmlreader | yes, untested | yes, untested | |
|
||||
| xmlwriter | yes, untested | yes, untested | |
|
||||
| zip | yes, untested | yes, untested | |
|
||||
| zlib | yes | yes | |
|
||||
| zstd | yes | yes | |
|
||||
|
||||
## Additional Requirements
|
||||
|
||||
- phpmicro requires PHP >= 8.0
|
||||
- swoole >= 5.0 requires PHP >= 8.0
|
||||
- swow requires PHP >= 8.0
|
||||
|
||||
## Typical Extension List Example
|
||||
|
||||
Here are some extension list example for different use.
|
||||
|
||||
- For general use: `"bcmath,bz2,calendar,ctype,curl,dom,exif,fileinfo,ftp,filter,gd,iconv,xml,mbstring,mysqlnd,openssl,pcntl,pdo,pdo_mysql,pdo_sqlite,phar,posix,redis,simplexml,soap,sockets,sqlite3,tokenizer,xmlwriter,xmlreader,zlib,zip"`
|
||||
- For static-php-cli self: `"posix,pcntl,phar,tokenizer,iconv,zlib"`
|
||||
- For static-php-cli self (with dev dependencies): `"posix,pcntl,phar,tokenizer,iconv,zlib,xml,dom,xmlwriter,xmlreader,fileinfo"`
|
||||
- Minimum, with no extension: `""`
|
||||
|
||||
Compile with all supported extensions (exclude `swow`, `swoole`, because these will change the default behavior of php):
|
||||
|
||||
```bash
|
||||
bin/spc build --build-all bcmath,bz2,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,ftp,gd,gmp,iconv,mbregex,mbstring,mongodb,mysqli,mysqlnd,openssl,pcntl,pdo,pdo_mysql,pdo_sqlite,phar,posix,protobuf,redis,session,shmop,simplexml,soap,sockets,sqlite3,tokenizer,xml,xmlreader,xmlwriter,yaml,zip,zlib,zstd --with-libs=libjpeg,freetype,libwebp,libavif --debug
|
||||
```
|
||||
|
||||
## Additional Libraries
|
||||
|
||||
Some extensions have soft dependencies, you can enable extra features by adding these libs using `--with-libs`.
|
||||
|
||||
For example, to compile with gd extension, with `libwebp, libgif, libavif, libjpeg, freetype` extra features:
|
||||
|
||||
```bash
|
||||
bin/spc build gd --with-libs=libjpeg,freetype,libwebp,libavif --build-cli
|
||||
```
|
||||
|
||||
> If you don't add them, your compilation will not enable these features.
|
||||
|
||||
## Limitations
|
||||
|
||||
- swow and swoole cannot be compiled at the same time.
|
||||
- openssl needs manual configuration for ssl certificate. (TODO: I will write a wiki about it)
|
||||
- some extensions need system configuration, e.g. `curl` and `openssl` will search ssl certificate on your system.
|
||||
|
||||
## Bugs and TODOs
|
||||
|
||||
See [#32](https://github.com/crazywhalecc/static-php-cli/issues/32).
|
||||
See: [Docs - Extensions](https://static-php-cli.zhamao.me/en/guide/extensions.html)
|
||||
|
||||
@@ -4,10 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC;
|
||||
|
||||
use SPC\command\DeployCommand;
|
||||
use SPC\store\FileSystem;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Command\HelpCommand;
|
||||
use Symfony\Component\Console\Command\ListCommand;
|
||||
|
||||
@@ -16,7 +14,7 @@ use Symfony\Component\Console\Command\ListCommand;
|
||||
*/
|
||||
class ConsoleApplication extends Application
|
||||
{
|
||||
public const VERSION = '2.0-rc2';
|
||||
public const VERSION = '2.0-rc5';
|
||||
|
||||
/**
|
||||
* @throws \ReflectionException
|
||||
@@ -33,13 +31,19 @@ class ConsoleApplication extends Application
|
||||
|
||||
// 通过扫描目录 src/static-php-cli/command/ 添加子命令
|
||||
$commands = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/command', 'SPC\\command');
|
||||
$this->addCommands(array_map(function ($x) { return new $x(); }, array_filter($commands, function ($y) {
|
||||
if (is_a($y, DeployCommand::class, true) && (class_exists('\\Phar') && \Phar::running() || !class_exists('\\Phar'))) {
|
||||
$phar = class_exists('\\Phar') && \Phar::running() || !class_exists('\\Phar');
|
||||
$commands = array_filter($commands, function ($y) use ($phar) {
|
||||
$archive_blacklist = [
|
||||
'SPC\command\dev\SortConfigCommand',
|
||||
'SPC\command\DeployCommand',
|
||||
];
|
||||
if ($phar && in_array($y, $archive_blacklist)) {
|
||||
return false;
|
||||
}
|
||||
$reflection = new \ReflectionClass($y);
|
||||
return !$reflection->isAbstract() && !$reflection->isInterface();
|
||||
})));
|
||||
});
|
||||
$this->addCommands(array_map(function ($x) { return new $x(); }, $commands));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,7 @@ use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\store\SourceExtractor;
|
||||
use SPC\util\CustomExt;
|
||||
use SPC\util\DependencyUtil;
|
||||
|
||||
@@ -87,7 +88,7 @@ abstract class BuilderBase
|
||||
$lib->calcDependency();
|
||||
}
|
||||
|
||||
$this->initSource(libs: $libraries);
|
||||
SourceExtractor::initSource(libs: $libraries);
|
||||
|
||||
// 构建库
|
||||
foreach ($this->libs as $lib) {
|
||||
@@ -140,6 +141,37 @@ abstract class BuilderBase
|
||||
return $this->exts[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有要编译的扩展对象
|
||||
*
|
||||
* @return Extension[]
|
||||
*/
|
||||
public function getExts(): array
|
||||
{
|
||||
return $this->exts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 C++ 扩展是否存在
|
||||
*
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
public function hasCppExtension(): bool
|
||||
{
|
||||
// judge cpp-extension
|
||||
$exts = array_keys($this->getExts());
|
||||
$cpp = false;
|
||||
foreach ($exts as $ext) {
|
||||
if (Config::getExt($ext, 'cpp-extension', false) === true) {
|
||||
$cpp = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $cpp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置本次 Builder 是否为仅编译库的模式
|
||||
*/
|
||||
@@ -153,15 +185,17 @@ abstract class BuilderBase
|
||||
*
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
* @throws \ReflectionException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
public function proveExts(array $extensions): void
|
||||
{
|
||||
CustomExt::loadCustomExt();
|
||||
$this->initSource(sources: ['php-src']);
|
||||
SourceExtractor::initSource(sources: ['php-src']);
|
||||
if ($this->getPHPVersionID() >= 80000) {
|
||||
$this->initSource(sources: ['micro']);
|
||||
SourceExtractor::initSource(sources: ['micro']);
|
||||
}
|
||||
$this->initSource(exts: $extensions);
|
||||
SourceExtractor::initSource(exts: $extensions);
|
||||
foreach ($extensions as $extension) {
|
||||
$class = CustomExt::getExtClass($extension);
|
||||
$ext = new $class($extension, $this);
|
||||
@@ -190,6 +224,7 @@ abstract class BuilderBase
|
||||
*
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
public function makeExtensionArgs(): string
|
||||
{
|
||||
@@ -261,52 +296,4 @@ abstract class BuilderBase
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function initSource(?array $sources = null, ?array $libs = null, ?array $exts = null): void
|
||||
{
|
||||
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
|
||||
throw new WrongUsageException('Download lock file "downloads/.lock.json" not found, maybe you need to download sources first ?');
|
||||
}
|
||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true);
|
||||
|
||||
$sources_extracted = [];
|
||||
// source check exist
|
||||
if (is_array($sources)) {
|
||||
foreach ($sources as $source) {
|
||||
$sources_extracted[$source] = true;
|
||||
}
|
||||
}
|
||||
// lib check source exist
|
||||
if (is_array($libs)) {
|
||||
foreach ($libs as $lib) {
|
||||
// get source name for lib
|
||||
$source = Config::getLib($lib, 'source');
|
||||
$sources_extracted[$source] = true;
|
||||
}
|
||||
}
|
||||
// ext check source exist
|
||||
if (is_array($exts)) {
|
||||
foreach ($exts as $ext) {
|
||||
// get source name for ext
|
||||
if (Config::getExt($ext, 'type') !== 'external') {
|
||||
continue;
|
||||
}
|
||||
$source = Config::getExt($ext, 'source');
|
||||
$sources_extracted[$source] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// start check
|
||||
foreach ($sources_extracted as $source => $item) {
|
||||
if (!isset($lock[$source])) {
|
||||
throw new WrongUsageException('Source [' . $source . '] not downloaded, you should download it first !');
|
||||
}
|
||||
|
||||
// check source dir exist
|
||||
$check = $lock[$source]['move_path'] === null ? SOURCE_PATH . '/' . $source : SOURCE_PATH . '/' . $lock[$source]['move_path'];
|
||||
if (!is_dir($check)) {
|
||||
FileSystem::extractSource($source, DOWNLOAD_PATH . '/' . ($lock[$source]['filename'] ?? $lock[$source]['dirname']), $lock[$source]['move_path']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +136,33 @@ class Extension
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch code before ./buildconf
|
||||
* If you need to patch some code, overwrite this and return true
|
||||
*/
|
||||
public function patchBeforeBuildconf(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch code before ./configure
|
||||
* If you need to patch some code, overwrite this and return true
|
||||
*/
|
||||
public function patchBeforeConfigure(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch code before make
|
||||
* If you need to patch some code, overwrite this and return true
|
||||
*/
|
||||
public function patchBeforeMake(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
|
||||
@@ -135,6 +135,7 @@ abstract class LibraryBase
|
||||
// 传入 true,表明直接编译
|
||||
if ($force_build) {
|
||||
logger()->info('Building required library [' . static::NAME . ']');
|
||||
$this->patchBeforeBuild();
|
||||
$this->build();
|
||||
return BUILD_STATUS_OK;
|
||||
}
|
||||
@@ -162,6 +163,14 @@ abstract class LibraryBase
|
||||
return BUILD_STATUS_ALREADY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch before build, overwrite this and return true to patch libs
|
||||
*/
|
||||
public function patchBeforeBuild(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取构建当前 lib 的 Builder 对象
|
||||
*/
|
||||
|
||||
25
src/SPC/builder/extension/bz2.php
Normal file
25
src/SPC/builder/extension/bz2.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
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;
|
||||
|
||||
#[CustomExt('bz2')]
|
||||
class bz2 extends Extension
|
||||
{
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function patchBeforeConfigure(): bool
|
||||
{
|
||||
$frameworks = $this->builder instanceof MacOSBuilder ? ' ' . $this->builder->getFrameworks(true) . ' ' : '';
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/configure', REPLACE_FILE_PREG, '/-lbz2/', $this->getLibFilesString() . $frameworks);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
52
src/SPC/builder/extension/curl.php
Normal file
52
src/SPC/builder/extension/curl.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
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;
|
||||
|
||||
#[CustomExt('curl')]
|
||||
class curl extends Extension
|
||||
{
|
||||
public function patchBeforeBuildconf(): bool
|
||||
{
|
||||
logger()->info('patching before-configure for curl checks');
|
||||
$file1 = "AC_DEFUN([PHP_CHECK_LIBRARY], [\n $3\n])";
|
||||
$files = FileSystem::readFile(SOURCE_PATH . '/php-src/ext/curl/config.m4');
|
||||
$file2 = 'AC_DEFUN([PHP_CHECK_LIBRARY], [
|
||||
save_old_LDFLAGS=$LDFLAGS
|
||||
ac_stuff="$5"
|
||||
|
||||
save_ext_shared=$ext_shared
|
||||
ext_shared=yes
|
||||
PHP_EVAL_LIBLINE([$]ac_stuff, LDFLAGS)
|
||||
AC_CHECK_LIB([$1],[$2],[
|
||||
LDFLAGS=$save_old_LDFLAGS
|
||||
ext_shared=$save_ext_shared
|
||||
$3
|
||||
],[
|
||||
LDFLAGS=$save_old_LDFLAGS
|
||||
ext_shared=$save_ext_shared
|
||||
unset ac_cv_lib_$1[]_$2
|
||||
$4
|
||||
])dnl
|
||||
])';
|
||||
file_put_contents(SOURCE_PATH . '/php-src/ext/curl/config.m4', $file1 . "\n" . $files . "\n" . $file2);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function patchBeforeConfigure(): bool
|
||||
{
|
||||
$frameworks = $this->builder instanceof MacOSBuilder ? ' ' . $this->builder->getFrameworks(true) . ' ' : '';
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/configure', REPLACE_FILE_PREG, '/-lcurl/', $this->getLibFilesString() . $frameworks);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ declare(strict_types=1);
|
||||
namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\CustomExt;
|
||||
|
||||
#[CustomExt('event')]
|
||||
@@ -23,4 +25,18 @@ class event extends Extension
|
||||
}
|
||||
return $arg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function patchBeforeConfigure(): bool
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/-levent_openssl/',
|
||||
$this->getLibFilesString()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
24
src/SPC/builder/extension/glfw.php
Normal file
24
src/SPC/builder/extension/glfw.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\CustomExt;
|
||||
|
||||
#[CustomExt('glfw')]
|
||||
class glfw extends Extension
|
||||
{
|
||||
public function patchBeforeBuildconf(): bool
|
||||
{
|
||||
FileSystem::copyDir(SOURCE_PATH . '/ext-glfw', SOURCE_PATH . '/php-src/ext/glfw');
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getUnixConfigureArg(): string
|
||||
{
|
||||
return '--enable-glfw --with-glfw-dir=' . BUILD_ROOT_PATH;
|
||||
}
|
||||
}
|
||||
@@ -14,4 +14,9 @@ class mbregex extends Extension
|
||||
{
|
||||
return 'mbstring';
|
||||
}
|
||||
|
||||
public function getConfigureArg(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
48
src/SPC/builder/extension/memcache.php
Normal file
48
src/SPC/builder/extension/memcache.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\CustomExt;
|
||||
|
||||
#[CustomExt('memcache')]
|
||||
class memcache extends Extension
|
||||
{
|
||||
public function getUnixConfigureArg(): string
|
||||
{
|
||||
return '--enable-memcache --with-zlib-dir=' . BUILD_ROOT_PATH;
|
||||
}
|
||||
|
||||
public function patchBeforeBuildconf(): bool
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/ext/memcache/config9.m4',
|
||||
REPLACE_FILE_STR,
|
||||
'if test -d $abs_srcdir/src ; then',
|
||||
'if test -d $abs_srcdir/main ; then'
|
||||
);
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/ext/memcache/config9.m4',
|
||||
REPLACE_FILE_STR,
|
||||
'export CPPFLAGS="$CPPFLAGS $INCLUDES"',
|
||||
'export CPPFLAGS="$CPPFLAGS $INCLUDES -I$abs_srcdir/main"'
|
||||
);
|
||||
// add for in-tree building
|
||||
file_put_contents(
|
||||
SOURCE_PATH . '/php-src/ext/memcache/php_memcache.h',
|
||||
<<<'EOF'
|
||||
#ifndef PHP_MEMCACHE_H
|
||||
#define PHP_MEMCACHE_H
|
||||
|
||||
extern zend_module_entry memcache_module_entry;
|
||||
#define phpext_memcache_ptr &memcache_module_entry
|
||||
|
||||
#endif
|
||||
EOF
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
18
src/SPC/builder/extension/memcached.php
Normal file
18
src/SPC/builder/extension/memcached.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\util\CustomExt;
|
||||
|
||||
#[CustomExt('memcached')]
|
||||
class memcached extends Extension
|
||||
{
|
||||
public function getUnixConfigureArg(): string
|
||||
{
|
||||
$rootdir = BUILD_ROOT_PATH;
|
||||
return "--enable-memcached --with-zlib-dir={$rootdir} --with-libmemcached-dir={$rootdir} --disable-memcached-sasl --enable-memcached-json";
|
||||
}
|
||||
}
|
||||
25
src/SPC/builder/extension/openssl.php
Normal file
25
src/SPC/builder/extension/openssl.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\util\CustomExt;
|
||||
use SPC\util\Util;
|
||||
|
||||
#[CustomExt('openssl')]
|
||||
class openssl extends Extension
|
||||
{
|
||||
public function patchBeforeMake(): bool
|
||||
{
|
||||
// patch openssl3 with php8.0 bug
|
||||
if (file_exists(SOURCE_PATH . '/openssl/VERSION.dat') && Util::getPHPVersionID() < 80100) {
|
||||
$openssl_c = file_get_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c');
|
||||
$openssl_c = preg_replace('/REGISTER_LONG_CONSTANT\s*\(\s*"OPENSSL_SSLV23_PADDING"\s*.+;/', '', $openssl_c);
|
||||
file_put_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c', $openssl_c);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
28
src/SPC/builder/extension/pdo_sqlite.php
Normal file
28
src/SPC/builder/extension/pdo_sqlite.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\CustomExt;
|
||||
|
||||
#[CustomExt('pdo_sqlite')]
|
||||
class pdo_sqlite extends Extension
|
||||
{
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function patchBeforeConfigure(): bool
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/sqlite3_column_table_name=yes/',
|
||||
'sqlite3_column_table_name=no'
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
28
src/SPC/builder/extension/pgsql.php
Normal file
28
src/SPC/builder/extension/pgsql.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\CustomExt;
|
||||
|
||||
#[CustomExt('pgsql')]
|
||||
class pgsql extends Extension
|
||||
{
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function patchBeforeConfigure(): bool
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/-lpq/',
|
||||
$this->getLibFilesString()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
28
src/SPC/builder/extension/readline.php
Normal file
28
src/SPC/builder/extension/readline.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\CustomExt;
|
||||
|
||||
#[CustomExt('readline')]
|
||||
class readline extends Extension
|
||||
{
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function patchBeforeConfigure(): bool
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/-lncurses/',
|
||||
$this->getLibFilesString()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,12 @@ class redis extends Extension
|
||||
{
|
||||
public function getUnixConfigureArg(): string
|
||||
{
|
||||
$arg = '--enable-redis --disable-redis-session';
|
||||
$arg = '--enable-redis';
|
||||
if (!$this->builder->getExt('session')) {
|
||||
$arg .= ' --disable-redis-session';
|
||||
} else {
|
||||
$arg .= ' --enable-redis-session';
|
||||
}
|
||||
if ($this->builder->getLib('zstd')) {
|
||||
$arg .= ' --enable-redis-zstd --with-libzstd="' . BUILD_ROOT_PATH . '"';
|
||||
}
|
||||
|
||||
28
src/SPC/builder/extension/ssh2.php
Normal file
28
src/SPC/builder/extension/ssh2.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\CustomExt;
|
||||
|
||||
#[CustomExt('ssh2')]
|
||||
class ssh2 extends Extension
|
||||
{
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function patchBeforeConfigure(): bool
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/-lssh2/',
|
||||
$this->getLibFilesString()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ namespace SPC\builder\extension;
|
||||
|
||||
use SPC\builder\Extension;
|
||||
use SPC\util\CustomExt;
|
||||
use SPC\util\Util;
|
||||
|
||||
#[CustomExt('swow')]
|
||||
class swow extends Extension
|
||||
@@ -17,4 +18,17 @@ class swow extends Extension
|
||||
$arg .= $this->builder->getLib('curl') ? ' --enable-swow-curl' : ' --disable-swow-curl';
|
||||
return $arg;
|
||||
}
|
||||
|
||||
public function patchBeforeBuildconf(): bool
|
||||
{
|
||||
if (Util::getPHPVersionID() >= 80000 && !is_link(SOURCE_PATH . '/php-src/ext/swow')) {
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && mklink /D swow swow-src\ext');
|
||||
} else {
|
||||
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && ln -s swow-src/ext swow');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class LinuxBuilder extends BuilderBase
|
||||
use UnixBuilderTrait;
|
||||
|
||||
/** @var string[] Linux 环境下编译依赖的命令 */
|
||||
public const REQUIRED_COMMANDS = ['make', 'bison', 'flex', 'pkg-config', 'git', 'autoconf', 'automake', 'tar', 'unzip', /* 'xz', 好像不需要 */ 'gzip', 'bzip2', 'cmake'];
|
||||
public const REQUIRED_COMMANDS = ['make', 'bison', 'flex', 'git', 'autoconf', 'automake', 'tar', 'unzip', /* 'xz', 好像不需要 */ 'gzip', 'bzip2', 'cmake'];
|
||||
|
||||
/** @var string 使用的 libc */
|
||||
public string $libc;
|
||||
@@ -139,7 +139,8 @@ class LinuxBuilder extends BuilderBase
|
||||
)
|
||||
);
|
||||
}
|
||||
if ($this->getExt('swoole') || $this->getExt('intl')) {
|
||||
|
||||
if ($this->hasCppExtension()) {
|
||||
$extra_libs .= ' -lstdc++';
|
||||
}
|
||||
if ($this->getExt('imagick')) {
|
||||
@@ -168,11 +169,11 @@ class LinuxBuilder extends BuilderBase
|
||||
|
||||
$envs = "{$envs} CFLAGS='{$cflags}' LIBS='-ldl -lpthread'";
|
||||
|
||||
SourcePatcher::patchPHPBuildconf($this);
|
||||
SourcePatcher::patchBeforeBuildconf($this);
|
||||
|
||||
shell()->cd(SOURCE_PATH . '/php-src')->exec('./buildconf --force');
|
||||
|
||||
SourcePatcher::patchPHPConfigure($this);
|
||||
SourcePatcher::patchBeforeConfigure($this);
|
||||
|
||||
if ($this->getPHPVersionID() < 80000) {
|
||||
$json_74 = '--enable-json ';
|
||||
@@ -199,7 +200,7 @@ class LinuxBuilder extends BuilderBase
|
||||
$envs
|
||||
);
|
||||
|
||||
SourcePatcher::patchPHPAfterConfigure($this);
|
||||
SourcePatcher::patchBeforeMake($this);
|
||||
|
||||
file_put_contents('/tmp/comment', $this->note_section);
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ abstract class LinuxLibraryBase extends LibraryBase
|
||||
// 传入 true,表明直接编译
|
||||
if ($force_build) {
|
||||
logger()->info('Building required library [' . static::NAME . ']');
|
||||
$this->patchBeforeBuild();
|
||||
$this->build();
|
||||
return BUILD_STATUS_OK;
|
||||
}
|
||||
|
||||
20
src/SPC/builder/linux/library/libmemcached.php
Normal file
20
src/SPC/builder/linux/library/libmemcached.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\linux\library;
|
||||
|
||||
use SPC\exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* gmp is a template library class for unix
|
||||
*/
|
||||
class libmemcached extends LinuxLibraryBase
|
||||
{
|
||||
public const NAME = 'libmemcached';
|
||||
|
||||
public function build()
|
||||
{
|
||||
throw new RuntimeException('libmemcached is currently not supported on Linux platform');
|
||||
}
|
||||
}
|
||||
@@ -20,14 +20,34 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\linux\library;
|
||||
|
||||
use SPC\builder\linux\SystemUtil;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\SourcePatcher;
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
class libpng extends LinuxLibraryBase
|
||||
{
|
||||
public const NAME = 'libpng';
|
||||
|
||||
public function patchBeforeBuild(): bool
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/libpng/configure',
|
||||
REPLACE_FILE_STR,
|
||||
'-lz',
|
||||
BUILD_LIB_PATH . '/libz.a'
|
||||
);
|
||||
if (SystemUtil::getOSRelease()['dist'] === 'alpine') {
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/libpng/configure',
|
||||
REPLACE_FILE_STR,
|
||||
'-lm',
|
||||
'/usr/lib/libm.a'
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
@@ -39,12 +59,9 @@ class libpng extends LinuxLibraryBase
|
||||
'arm64' => '--enable-arm-neon ',
|
||||
default => '',
|
||||
};
|
||||
|
||||
// patch configure
|
||||
SourcePatcher::patchUnixLibpng();
|
||||
|
||||
shell()->cd($this->source_dir)
|
||||
->exec('chmod +x ./configure')
|
||||
->exec('chmod +x ./install-sh')
|
||||
->exec(
|
||||
"{$this->builder->configure_env} ./configure " .
|
||||
"--host={$this->builder->gnu_arch}-unknown-linux " .
|
||||
|
||||
12
src/SPC/builder/linux/library/postgresql.php
Normal file
12
src/SPC/builder/linux/library/postgresql.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\linux\library;
|
||||
|
||||
class postgresql extends LinuxLibraryBase
|
||||
{
|
||||
use \SPC\builder\unix\library\postgresql;
|
||||
|
||||
public const NAME = 'postgresql';
|
||||
}
|
||||
@@ -54,7 +54,7 @@ class MacOSBuilder extends BuilderBase
|
||||
'PKG_CONFIG_PATH="' . BUILD_LIB_PATH . '/pkgconfig/" ' .
|
||||
"CC='{$this->cc}' " .
|
||||
"CXX='{$this->cxx}' " .
|
||||
"CFLAGS='{$this->arch_c_flags} -Wimplicit-function-declaration'";
|
||||
"CFLAGS='{$this->arch_c_flags} -Wimplicit-function-declaration -Os'";
|
||||
|
||||
// 创立 pkg-config 和放头文件的目录
|
||||
f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true);
|
||||
@@ -118,12 +118,15 @@ class MacOSBuilder extends BuilderBase
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @param int $build_target build target
|
||||
* @param bool $bloat just raw add all lib files
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
public function buildPHP(int $build_target = BUILD_TARGET_NONE, bool $bloat = false): void
|
||||
{
|
||||
$extra_libs = $this->getFrameworks(true) . ' ' . ($this->getExt('swoole') || $this->getExt('intl') ? '-lc++ ' : '');
|
||||
$extra_libs = $this->getFrameworks(true) . ' ' . ($this->hasCppExtension() ? '-lc++ ' : '');
|
||||
if (!$bloat) {
|
||||
$extra_libs .= implode(' ', $this->getAllStaticLibFiles());
|
||||
} else {
|
||||
@@ -137,12 +140,12 @@ class MacOSBuilder extends BuilderBase
|
||||
);
|
||||
}
|
||||
|
||||
// patch before configure
|
||||
SourcePatcher::patchPHPBuildconf($this);
|
||||
// patch before buildconf
|
||||
SourcePatcher::patchBeforeBuildconf($this);
|
||||
|
||||
shell()->cd(SOURCE_PATH . '/php-src')->exec('./buildconf --force');
|
||||
|
||||
SourcePatcher::patchPHPConfigure($this);
|
||||
SourcePatcher::patchBeforeConfigure($this);
|
||||
|
||||
if ($this->getLib('libxml2') || $this->getExt('iconv')) {
|
||||
$extra_libs .= ' -liconv';
|
||||
@@ -174,7 +177,7 @@ class MacOSBuilder extends BuilderBase
|
||||
$this->configure_env
|
||||
);
|
||||
|
||||
SourcePatcher::patchPHPAfterConfigure($this);
|
||||
SourcePatcher::patchBeforeMake($this);
|
||||
|
||||
$this->cleanMake();
|
||||
|
||||
@@ -209,7 +212,7 @@ class MacOSBuilder extends BuilderBase
|
||||
public function buildCli(string $extra_libs): void
|
||||
{
|
||||
$shell = shell()->cd(SOURCE_PATH . '/php-src');
|
||||
$shell->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" cli");
|
||||
$shell->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" cli");
|
||||
if ($this->strip) {
|
||||
$shell->exec('dsymutil -f sapi/cli/php')->exec('strip sapi/cli/php');
|
||||
}
|
||||
|
||||
@@ -20,19 +20,32 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
use SPC\store\SourcePatcher;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
class curl extends MacOSLibraryBase
|
||||
{
|
||||
use \SPC\builder\unix\library\curl {
|
||||
build as unixBuild;
|
||||
}
|
||||
use \SPC\builder\unix\library\curl;
|
||||
|
||||
public const NAME = 'curl';
|
||||
|
||||
protected function build()
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function patchBeforeBuild(): bool
|
||||
{
|
||||
SourcePatcher::patchCurlMacOS();
|
||||
$this->unixBuild();
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/curl/CMakeLists.txt',
|
||||
REPLACE_FILE_PREG,
|
||||
'/NOT COREFOUNDATION_FRAMEWORK/m',
|
||||
'FALSE'
|
||||
);
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/curl/CMakeLists.txt',
|
||||
REPLACE_FILE_PREG,
|
||||
'/NOT SYSTEMCONFIGURATION_FRAMEWORK/m',
|
||||
'FALSE'
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
21
src/SPC/builder/macos/library/glfw.php
Normal file
21
src/SPC/builder/macos/library/glfw.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class glfw extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'glfw';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
// compile!
|
||||
shell()->cd(SOURCE_PATH . '/ext-glfw/vendor/glfw')
|
||||
->exec("{$this->builder->configure_env} cmake . {$this->builder->makeCmakeArgs()} -DBUILD_SHARED_LIBS=OFF -DGLFW_BUILD_EXAMPLES=OFF -DGLFW_BUILD_TESTS=OFF")
|
||||
->exec("make -j{$this->builder->concurrency}")
|
||||
->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
|
||||
// patch pkgconf
|
||||
$this->patchPkgconfPrefix(['glfw3.pc']);
|
||||
}
|
||||
}
|
||||
31
src/SPC/builder/macos/library/libmemcached.php
Normal file
31
src/SPC/builder/macos/library/libmemcached.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
/**
|
||||
* gmp is a template library class for unix
|
||||
*/
|
||||
class libmemcached extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'libmemcached';
|
||||
|
||||
public function build()
|
||||
{
|
||||
$rootdir = BUILD_ROOT_PATH;
|
||||
|
||||
shell()->cd($this->source_dir)
|
||||
->exec('chmod +x configure')
|
||||
->exec(
|
||||
"{$this->builder->configure_env} ./configure " .
|
||||
'--enable-static --disable-shared ' .
|
||||
'--disable-sasl ' .
|
||||
"--prefix={$rootdir}"
|
||||
)
|
||||
->exec('make clean')
|
||||
->exec('sed -ie "s/-Werror//g" ' . $this->source_dir . '/Makefile')
|
||||
->exec("make -j{$this->builder->concurrency}")
|
||||
->exec('make install');
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ class libpng extends MacOSLibraryBase
|
||||
};
|
||||
shell()->cd($this->source_dir)
|
||||
->exec('chmod +x ./configure')
|
||||
->exec('chmod +x ./install-sh')
|
||||
->exec(
|
||||
"{$this->builder->configure_env} ./configure " .
|
||||
"--host={$this->builder->gnu_arch}-apple-darwin " .
|
||||
|
||||
@@ -31,18 +31,16 @@ class libxml2 extends MacOSLibraryBase
|
||||
'-DBUILD_SHARED_LIBS=OFF ' .
|
||||
'-DLIBXML2_WITH_ICONV=ON ' .
|
||||
"-DLIBXML2_WITH_ZLIB={$enable_zlib} " .
|
||||
"-DLIBXML2_WITH_ICU={$enable_icu} " .
|
||||
'-DLIBXML2_WITH_ICU=OFF ' .
|
||||
"-DLIBXML2_WITH_LZMA={$enable_xz} " .
|
||||
'-DLIBXML2_WITH_PYTHON=OFF ' .
|
||||
'-DLIBXML2_WITH_PROGRAMS=OFF ' .
|
||||
'-DLIBXML2_WITH_TESTS=OFF ' .
|
||||
'-DCMAKE_INSTALL_PREFIX=/ ' .
|
||||
"-DCMAKE_INSTALL_LIBDIR={$lib} " .
|
||||
"-DCMAKE_INSTALL_INCLUDEDIR={$include} " .
|
||||
"-DCMAKE_INSTALL_PREFIX={$destdir} " .
|
||||
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
|
||||
'..'
|
||||
)
|
||||
->exec("cmake --build . -j {$this->builder->concurrency}")
|
||||
->exec("make install DESTDIR={$destdir}");
|
||||
->exec('make install');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ class openssl extends MacOSLibraryBase
|
||||
"{$this->builder->configure_env} ./Configure no-shared {$extra} " .
|
||||
'--prefix=/ ' . // use prefix=/
|
||||
"--libdir={$lib} " .
|
||||
" darwin64-{$this->builder->arch}-cc"
|
||||
'--openssldir=/System/Library/OpenSSL ' .
|
||||
"darwin64-{$this->builder->arch}-cc"
|
||||
)
|
||||
->exec('make clean')
|
||||
->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"")
|
||||
|
||||
12
src/SPC/builder/macos/library/postgresql.php
Normal file
12
src/SPC/builder/macos/library/postgresql.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class postgresql extends MacOSLibraryBase
|
||||
{
|
||||
use \SPC\builder\unix\library\postgresql;
|
||||
|
||||
public const NAME = 'postgresql';
|
||||
}
|
||||
@@ -22,6 +22,7 @@ trait libevent
|
||||
'-DEVENT__LIBRARY_TYPE=STATIC ' .
|
||||
'-DEVENT__DISABLE_BENCHMARK=ON ' .
|
||||
'-DEVENT__DISABLE_THREAD_SUPPORT=ON ' .
|
||||
'-DEVENT__DISABLE_MBEDTLS=ON ' .
|
||||
'-DEVENT__DISABLE_TESTS=ON ' .
|
||||
'-DEVENT__DISABLE_SAMPLES=ON ' .
|
||||
'..'
|
||||
|
||||
89
src/SPC/builder/unix/library/postgresql.php
Normal file
89
src/SPC/builder/unix/library/postgresql.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\unix\library;
|
||||
|
||||
use SPC\builder\macos\library\MacOSLibraryBase;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
trait postgresql
|
||||
{
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
protected function build()
|
||||
{
|
||||
$builddir = BUILD_ROOT_PATH;
|
||||
$env = $this->builder->configure_env;
|
||||
$envs = $env;
|
||||
$packages = 'openssl zlib readline libxml-2.0'; // icu-uc icu-io icu-i18n libzstd
|
||||
|
||||
$pkgconfig_executable = $builddir . '/bin/pkg-config';
|
||||
$output = shell()->execWithResult($env . " {$pkgconfig_executable} --cflags-only-I --static " . $packages);
|
||||
if (!empty($output[1][0])) {
|
||||
$cppflags = $output[1][0];
|
||||
$envs .= " CPPFLAGS=\"{$cppflags}\"";
|
||||
}
|
||||
$output = shell()->execWithResult($env . " {$pkgconfig_executable} --libs-only-L --static " . $packages);
|
||||
if (!empty($output[1][0])) {
|
||||
$ldflags = $output[1][0];
|
||||
$envs .= $this instanceof MacOSLibraryBase ? " LDFLAGS=\"{$ldflags}\" " : " LDFLAGS=\"{$ldflags} -static\" ";
|
||||
}
|
||||
$output = shell()->execWithResult($env . " {$pkgconfig_executable} --libs-only-l --static " . $packages);
|
||||
if (!empty($output[1][0])) {
|
||||
$libs = $output[1][0];
|
||||
$envs .= " LIBS=\"{$libs}\" ";
|
||||
}
|
||||
|
||||
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 "293 s/^/#$/" ../src/Makefile.shlib')
|
||||
->exec('sed -i.backup "441 s/^/#$/" ../src/Makefile.shlib');
|
||||
|
||||
// configure
|
||||
shell()->cd($this->source_dir . '/build')
|
||||
->exec(
|
||||
"{$envs} ../configure " .
|
||||
"--prefix={$builddir} " .
|
||||
'--disable-thread-safety ' .
|
||||
'--enable-coverage=no ' .
|
||||
'--with-ssl=openssl ' .
|
||||
'--with-readline ' .
|
||||
'--with-libxml ' .
|
||||
($this->builder->getLib('icu') ? '--with-icu ' : '--without-icu ') .
|
||||
'--without-ldap ' .
|
||||
'--without-libxslt ' .
|
||||
'--without-lz4 ' .
|
||||
'--without-zstd ' .
|
||||
'--without-perl ' .
|
||||
'--without-python ' .
|
||||
'--without-pam ' .
|
||||
'--without-ldap ' .
|
||||
'--without-bonjour ' .
|
||||
'--without-tcl '
|
||||
);
|
||||
|
||||
// build
|
||||
shell()->cd($this->source_dir . '/build')
|
||||
->exec($envs . ' make -C src/bin/pg_config install')
|
||||
->exec($envs . ' make -C src/include install')
|
||||
->exec($envs . ' make -C src/common install')
|
||||
->exec($envs . ' make -C src/backend/port install')
|
||||
->exec($envs . ' make -C src/port install')
|
||||
->exec($envs . ' make -C src/backend/libpq install')
|
||||
->exec($envs . ' make -C src/interfaces/libpq install');
|
||||
|
||||
// remove dynamic libs
|
||||
shell()->cd($this->source_dir . '/build')
|
||||
->exec("rm -rf {$builddir}/lib/*.so.*")
|
||||
->exec("rm -rf {$builddir}/lib/*.so")
|
||||
->exec("rm -rf {$builddir}/lib/*.dylib");
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ abstract class BaseCommand extends Command
|
||||
foreach ($msg as $v) {
|
||||
logger()->error($v);
|
||||
}
|
||||
return self::FAILURE;
|
||||
return static::FAILURE;
|
||||
} catch (\Throwable $e) {
|
||||
// 不开 debug 模式就不要再显示复杂的调试栈信息了
|
||||
if ($this->getOption('debug')) {
|
||||
@@ -102,10 +102,10 @@ abstract class BaseCommand extends Command
|
||||
logger()->error($v);
|
||||
}
|
||||
}
|
||||
return self::FAILURE;
|
||||
return static::FAILURE;
|
||||
}
|
||||
}
|
||||
return self::SUCCESS;
|
||||
return static::SUCCESS;
|
||||
}
|
||||
|
||||
protected function getOption(string $name): mixed
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace SPC\command;
|
||||
use SPC\builder\BuilderProvider;
|
||||
use SPC\exception\ExceptionHandler;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\SourcePatcher;
|
||||
use SPC\util\DependencyUtil;
|
||||
use SPC\util\LicenseDumper;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
@@ -27,6 +28,7 @@ class BuildCliCommand extends BuildCommand
|
||||
$this->addOption('build-all', null, null, 'build cli, micro, fpm');
|
||||
$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('with-hardcoded-ini', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Patch PHP source code, inject hardcoded INI');
|
||||
}
|
||||
|
||||
public function handle(): int
|
||||
@@ -49,7 +51,7 @@ class BuildCliCommand extends BuildCommand
|
||||
$this->output->writeln("<comment>\t--build-micro\tBuild phpmicro SAPI</comment>");
|
||||
$this->output->writeln("<comment>\t--build-fpm\tBuild php-fpm SAPI</comment>");
|
||||
$this->output->writeln("<comment>\t--build-all\tBuild all SAPI: cli, micro, fpm</comment>");
|
||||
return 1;
|
||||
return static::FAILURE;
|
||||
}
|
||||
try {
|
||||
// 构建对象
|
||||
@@ -71,7 +73,17 @@ class BuildCliCommand extends BuildCommand
|
||||
// 执行扩展检测
|
||||
$builder->proveExts($extensions);
|
||||
// strip
|
||||
$builder->setStrip(false);
|
||||
$builder->setStrip(!$this->getOption('no-strip'));
|
||||
// Process -I option
|
||||
$custom_ini = [];
|
||||
foreach ($this->input->getOption('with-hardcoded-ini') as $value) {
|
||||
[$source_name, $ini_value] = explode('=', $value, 2);
|
||||
$custom_ini[$source_name] = $ini_value;
|
||||
logger()->info('Adding hardcoded INI [' . $source_name . ' = ' . $ini_value . ']');
|
||||
}
|
||||
if (!empty($custom_ini)) {
|
||||
SourcePatcher::patchHardcodedINI($custom_ini);
|
||||
}
|
||||
// 构建
|
||||
$builder->buildPHP($rule, $this->getOption('bloat'));
|
||||
// 统计时间
|
||||
@@ -101,10 +113,10 @@ class BuildCliCommand extends BuildCommand
|
||||
$dumper = new LicenseDumper();
|
||||
$dumper->addExts($extensions)->addLibs($libraries)->addSources(['php-src'])->dump(BUILD_ROOT_PATH . '/license');
|
||||
logger()->info('License path' . $fixed . ': ' . $build_root_path . '/license/');
|
||||
return 0;
|
||||
return static::SUCCESS;
|
||||
} catch (WrongUsageException $e) {
|
||||
logger()->critical($e->getMessage());
|
||||
return 1;
|
||||
return static::FAILURE;
|
||||
} catch (\Throwable $e) {
|
||||
if ($this->getOption('debug')) {
|
||||
ExceptionHandler::getInstance()->handle($e);
|
||||
@@ -112,7 +124,7 @@ class BuildCliCommand extends BuildCommand
|
||||
logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage());
|
||||
logger()->critical('Please check with --debug option to see more details.');
|
||||
}
|
||||
return 1;
|
||||
return static::FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ class BuildLibsCommand extends BuildCommand
|
||||
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Build libs complete, used ' . $time . ' s !');
|
||||
return 0;
|
||||
return static::SUCCESS;
|
||||
} catch (\Throwable $e) {
|
||||
if ($this->getOption('debug')) {
|
||||
ExceptionHandler::getInstance()->handle($e);
|
||||
@@ -71,7 +71,7 @@ class BuildLibsCommand extends BuildCommand
|
||||
logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage());
|
||||
logger()->critical('Please check with --debug option to see more details.');
|
||||
}
|
||||
return 1;
|
||||
return static::FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use CliHelper\Tools\ArgFixer;
|
||||
use CliHelper\Tools\DataProvider;
|
||||
use CliHelper\Tools\SeekableArrayIterator;
|
||||
use SPC\store\FileSystem;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
use function Laravel\Prompts\confirm;
|
||||
use function Laravel\Prompts\text;
|
||||
|
||||
#[AsCommand('deploy', 'Deploy static-php-cli self to an .phar application')]
|
||||
class DeployCommand extends BaseCommand
|
||||
{
|
||||
@@ -20,26 +21,51 @@ class DeployCommand extends BaseCommand
|
||||
$this->addArgument('target', InputArgument::OPTIONAL, 'The file or directory to pack.');
|
||||
$this->addOption('auto-phar-fix', null, InputOption::VALUE_NONE, 'Automatically fix ini option.');
|
||||
$this->addOption('overwrite', 'W', InputOption::VALUE_NONE, 'Overwrite existing files.');
|
||||
$this->addOption('with-no-dev', 'D', InputOption::VALUE_NONE, 'Automatically use non-dev composer dependencies to reduce size');
|
||||
$this->addOption('with-dev', 'd', InputOption::VALUE_NONE, 'Automatically use dev composer dependencies');
|
||||
}
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
// 第一阶段流程:如果没有写path,将会提示输入要打包的path
|
||||
$prompt = new ArgFixer($this->input, $this->output);
|
||||
$composer = require ROOT_DIR . '/vendor/composer/installed.php';
|
||||
if (($composer['root']['dev'] ?? false) === true) {
|
||||
if (!$this->getOption('with-no-dev')) {
|
||||
$this->output->writeln('<comment>Current static-php-cli dependencies have installed dev-dependencies</comment>');
|
||||
$this->output->writeln('<comment>If you want to remove, you can choose "Yes" to run command "composer update --no-dev" to remove.</comment>');
|
||||
$this->output->writeln('<comment>Or choose "No", just pack, deploy.</comment>');
|
||||
$ask = confirm('Do you want to remove dev-dependencies to reduce size of phar file?');
|
||||
} elseif (!$this->getOption('with-dev')) {
|
||||
$ask = true;
|
||||
} else {
|
||||
$ask = false;
|
||||
}
|
||||
if ($ask) {
|
||||
[$code] = shell()->execWithResult('composer update --no-dev');
|
||||
if ($code !== 0) {
|
||||
$this->output->writeln('<error>"composer update --no-dev" failed with exit code [' . $code . ']</error>');
|
||||
$this->output->writeln('<error>You may need to run this command by your own.</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
$this->output->writeln('<info>Update successfully, you need to re-run deploy command to pack.</info>');
|
||||
return static::SUCCESS;
|
||||
}
|
||||
}
|
||||
// 首先得确认是不是关闭了readonly模式
|
||||
if (ini_get('phar.readonly') == 1) {
|
||||
if ($this->getOption('auto-phar-fix')) {
|
||||
$ask = true;
|
||||
} else {
|
||||
$ask = $prompt->requireBool('<comment>pack command needs "phar.readonly" = "Off" !</comment>' . PHP_EOL . 'If you want to automatically set it and continue, just Enter', true);
|
||||
$this->output->writeln('<comment>pack command needs "phar.readonly" = "Off" !</comment>');
|
||||
$ask = confirm('Do you want to automatically set it and continue ?');
|
||||
// $ask = $prompt->requireBool('<comment>pack command needs "phar.readonly" = "Off" !</comment>' . PHP_EOL . 'If you want to automatically set it and continue, just Enter', true);
|
||||
}
|
||||
if ($ask) {
|
||||
global $argv;
|
||||
$args = array_merge(['-d', 'phar.readonly=0'], $_SERVER['argv']);
|
||||
$args = array_merge(['-d', 'phar.readonly=0'], $_SERVER['argv'], ['--no-motd']);
|
||||
if (function_exists('pcntl_exec')) {
|
||||
$this->output->writeln('<info>Changing to phar.readonly=0 mode ...</info>');
|
||||
if (pcntl_exec(PHP_BINARY, $args) === false) {
|
||||
throw new \PharException('切换到读写模式失败,请检查环境。');
|
||||
throw new \PharException('Switching to read write mode failed, please check the environment.');
|
||||
}
|
||||
} else {
|
||||
$this->output->writeln('<info>Now running command in child process.</info>');
|
||||
@@ -51,23 +77,29 @@ class DeployCommand extends BaseCommand
|
||||
// 获取路径
|
||||
$path = WORKING_DIR;
|
||||
// 如果是目录,则将目录下的所有文件打包
|
||||
$phar_path = $prompt->requireArgument('target', 'Please input the phar target filename', 'static-php-cli.phar');
|
||||
$phar_path = text('Please input the phar target filename', default: '/tmp/static-php-cli.phar');
|
||||
// $phar_path = $prompt->requireArgument('target', 'Please input the phar target filename', 'static-php-cli.phar');
|
||||
|
||||
if (DataProvider::isRelativePath($phar_path)) {
|
||||
$phar_path = '/tmp/' . $phar_path;
|
||||
if (FileSystem::isRelativePath($phar_path)) {
|
||||
$phar_path = WORKING_DIR . '/' . $phar_path;
|
||||
}
|
||||
if (file_exists($phar_path)) {
|
||||
$ask = $this->getOption('overwrite') ? true : $prompt->requireBool('<comment>The file "' . $phar_path . '" already exists, do you want to overwrite it?</comment>' . PHP_EOL . 'If you want to, just Enter');
|
||||
if (!$this->getOption('overwrite')) {
|
||||
$this->output->writeln('<comment>The file "' . $phar_path . '" already exists.</comment>');
|
||||
$ask = confirm('Do you want to overwrite it?');
|
||||
} else {
|
||||
$ask = true;
|
||||
}
|
||||
if (!$ask) {
|
||||
$this->output->writeln('<comment>User canceled.</comment>');
|
||||
return 1;
|
||||
return static::FAILURE;
|
||||
}
|
||||
@unlink($phar_path);
|
||||
}
|
||||
$phar = new \Phar($phar_path);
|
||||
$phar->startBuffering();
|
||||
|
||||
$all = DataProvider::scanDirFiles($path, true, true);
|
||||
$all = FileSystem::scanDirFiles($path, true, true);
|
||||
|
||||
$all = array_filter($all, function ($x) {
|
||||
$dirs = preg_match('/(^(config|src|vendor)\\/|^(composer\\.json|README\\.md|source\\.json|LICENSE|README-en\\.md)$)/', $x);
|
||||
@@ -97,7 +129,7 @@ class DeployCommand extends BaseCommand
|
||||
$phar->setStub($phar->createDefaultStub($stub));
|
||||
} catch (\Throwable $e) {
|
||||
$this->output->writeln($e);
|
||||
return 1;
|
||||
return static::FAILURE;
|
||||
}
|
||||
$phar->addFromString('.prod', 'true');
|
||||
$phar->stopBuffering();
|
||||
@@ -114,7 +146,7 @@ class DeployCommand extends BaseCommand
|
||||
}
|
||||
chmod($phar_path, 0755);
|
||||
$this->output->writeln('<info>Phar Executable: ' . $phar_path . '</info>');
|
||||
return 0;
|
||||
return static::SUCCESS;
|
||||
}
|
||||
|
||||
private function progress(int $max = 0): ProgressBar
|
||||
|
||||
@@ -10,17 +10,22 @@ use Symfony\Component\Console\Attribute\AsCommand;
|
||||
#[AsCommand('doctor', 'Diagnose whether the current environment can compile normally')]
|
||||
class DoctorCommand extends BaseCommand
|
||||
{
|
||||
public function configure()
|
||||
{
|
||||
$this->addOption('auto-fix', null, null, 'Automatically fix failed items (if possible)');
|
||||
}
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
try {
|
||||
$checker = new CheckListHandler($this->input, $this->output);
|
||||
$checker->runCheck(FIX_POLICY_PROMPT);
|
||||
$checker->runCheck($this->input->getOption('auto-fix') ? FIX_POLICY_AUTOFIX : FIX_POLICY_PROMPT);
|
||||
$this->output->writeln('<info>Doctor check complete !</info>');
|
||||
} catch (\Throwable $e) {
|
||||
$this->output->writeln('<error>' . $e->getMessage() . '</error>');
|
||||
pcntl_signal(SIGINT, SIG_IGN);
|
||||
return 1;
|
||||
return static::FAILURE;
|
||||
}
|
||||
return 0;
|
||||
return static::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\builder\traits\UnixSystemUtilTrait;
|
||||
use SPC\exception\DownloaderException;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
@@ -18,6 +19,8 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
#[AsCommand('download', 'Download required sources', ['fetch'])]
|
||||
class DownloadCommand extends BaseCommand
|
||||
{
|
||||
use UnixSystemUtilTrait;
|
||||
|
||||
protected string $php_major_ver;
|
||||
|
||||
public function configure()
|
||||
@@ -28,12 +31,14 @@ class DownloadCommand extends BaseCommand
|
||||
$this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'version in major.minor format like 8.1', '8.1');
|
||||
$this->addOption('clean', null, null, 'Clean old download cache and source before fetch');
|
||||
$this->addOption('all', 'A', null, 'Fetch all sources that static-php-cli needed');
|
||||
$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"');
|
||||
$this->addOption('from-zip', 'Z', InputOption::VALUE_REQUIRED, 'Fetch from zip archive');
|
||||
}
|
||||
|
||||
public function initialize(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// --all 等于 "" "",也就是所有东西都要下载
|
||||
if ($input->getOption('all') || $input->getOption('clean')) {
|
||||
if ($input->getOption('all') || $input->getOption('clean') || $input->getOption('from-zip')) {
|
||||
$input->setArgument('sources', '');
|
||||
}
|
||||
parent::initialize($input, $output);
|
||||
@@ -63,7 +68,45 @@ class DownloadCommand extends BaseCommand
|
||||
f_passthru('rm -rf ' . DOWNLOAD_PATH . '/*');
|
||||
f_passthru('rm -rf ' . BUILD_ROOT_PATH . '/*');
|
||||
}
|
||||
return 0;
|
||||
return static::FAILURE;
|
||||
}
|
||||
|
||||
// --from-zip
|
||||
if ($path = $this->getOption('from-zip')) {
|
||||
if (!file_exists($path)) {
|
||||
logger()->critical('File ' . $path . ' not exist or not a zip archive.');
|
||||
return static::FAILURE;
|
||||
}
|
||||
// remove old download files first
|
||||
if (is_dir(DOWNLOAD_PATH)) {
|
||||
logger()->warning('You are doing some operations that not recoverable: removing directories below');
|
||||
logger()->warning(DOWNLOAD_PATH);
|
||||
logger()->alert('I will remove these dir after 5 seconds !');
|
||||
sleep(5);
|
||||
f_passthru((PHP_OS_FAMILY === 'Windows' ? 'rmdir /s /q ' : 'rm -rf ') . DOWNLOAD_PATH);
|
||||
}
|
||||
// unzip command check
|
||||
if (PHP_OS_FAMILY !== 'Windows' && !$this->findCommand('unzip')) {
|
||||
logger()->critical('Missing unzip command, you need to install it first !');
|
||||
logger()->critical('You can use "bin/spc doctor" command to check and install required tools');
|
||||
return static::FAILURE;
|
||||
}
|
||||
// create downloads
|
||||
try {
|
||||
if (PHP_OS_FAMILY !== 'Windows') {
|
||||
f_passthru('mkdir ' . DOWNLOAD_PATH . ' && cd ' . DOWNLOAD_PATH . ' && unzip ' . escapeshellarg($path));
|
||||
}
|
||||
// Windows TODO
|
||||
|
||||
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
|
||||
throw new RuntimeException('.lock.json not exist in "downloads/"');
|
||||
}
|
||||
} catch (RuntimeException $e) {
|
||||
logger()->critical('Extract failed: ' . $e->getMessage());
|
||||
return static::FAILURE;
|
||||
}
|
||||
logger()->info('Extract success');
|
||||
return static::SUCCESS;
|
||||
}
|
||||
|
||||
// Define PHP major version
|
||||
@@ -72,7 +115,7 @@ class DownloadCommand extends BaseCommand
|
||||
preg_match('/^\d+\.\d+$/', $ver, $matches);
|
||||
if (!$matches) {
|
||||
logger()->error("bad version arg: {$ver}, x.y required!");
|
||||
return 1;
|
||||
return static::FAILURE;
|
||||
}
|
||||
|
||||
// Use shallow-clone can reduce git resource download
|
||||
@@ -97,18 +140,41 @@ class DownloadCommand extends BaseCommand
|
||||
}
|
||||
$chosen_sources = $sources;
|
||||
|
||||
// Process -U options
|
||||
$custom_urls = [];
|
||||
foreach ($this->input->getOption('custom-url') as $value) {
|
||||
[$source_name, $url] = explode(':', $value, 2);
|
||||
$custom_urls[$source_name] = $url;
|
||||
}
|
||||
|
||||
// Download them
|
||||
f_mkdir(DOWNLOAD_PATH);
|
||||
$cnt = count($chosen_sources);
|
||||
$ni = 0;
|
||||
foreach ($chosen_sources as $source) {
|
||||
++$ni;
|
||||
logger()->info("Fetching source {$source} [{$ni}/{$cnt}]");
|
||||
Downloader::downloadSource($source, Config::getSource($source));
|
||||
if (isset($custom_urls[$source])) {
|
||||
$config = Config::getSource($source);
|
||||
$new_config = [
|
||||
'type' => 'url',
|
||||
'url' => $custom_urls[$source],
|
||||
];
|
||||
if (isset($config['path'])) {
|
||||
$new_config['path'] = $config['path'];
|
||||
}
|
||||
if (isset($config['filename'])) {
|
||||
$new_config['filename'] = $config['filename'];
|
||||
}
|
||||
logger()->info("Fetching source {$source} from custom url [{$ni}/{$cnt}]");
|
||||
Downloader::downloadSource($source, $new_config, true);
|
||||
} else {
|
||||
logger()->info("Fetching source {$source} [{$ni}/{$cnt}]");
|
||||
Downloader::downloadSource($source, Config::getSource($source));
|
||||
}
|
||||
}
|
||||
// 打印拉取资源用时
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Download complete, used ' . $time . ' s !');
|
||||
return 0;
|
||||
return static::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class DumpLicenseCommand extends BaseCommand
|
||||
$this->output->writeln('Dump license with libraries: ' . implode(', ', $libraries));
|
||||
$this->output->writeln('Dump license with' . ($this->getOption('without-php') ? 'out' : '') . ' php-src');
|
||||
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||
return 0;
|
||||
return static::SUCCESS;
|
||||
}
|
||||
if ($this->getOption('by-libs') !== null) {
|
||||
$libraries = array_map('trim', array_filter(explode(',', $this->getOption('by-libs'))));
|
||||
@@ -58,16 +58,16 @@ class DumpLicenseCommand extends BaseCommand
|
||||
$dumper->addLibs($libraries);
|
||||
$dumper->dump($this->getOption('dump-dir'));
|
||||
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||
return 0;
|
||||
return static::SUCCESS;
|
||||
}
|
||||
if ($this->getOption('by-sources') !== null) {
|
||||
$sources = array_map('trim', array_filter(explode(',', $this->getOption('by-sources'))));
|
||||
$dumper->addSources($sources);
|
||||
$dumper->dump($this->getOption('dump-dir'));
|
||||
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||
return 0;
|
||||
return static::SUCCESS;
|
||||
}
|
||||
$this->output->writeln('You must use one of "--by-extensions=", "--by-libs=", "--by-sources=" to dump');
|
||||
return 1;
|
||||
return static::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
35
src/SPC/command/ExtractCommand.php
Normal file
35
src/SPC/command/ExtractCommand.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\builder\traits\UnixSystemUtilTrait;
|
||||
use SPC\store\SourceExtractor;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
#[AsCommand('extract', 'Extract required sources')]
|
||||
class ExtractCommand extends BaseCommand
|
||||
{
|
||||
use UnixSystemUtilTrait;
|
||||
|
||||
protected string $php_major_ver;
|
||||
|
||||
public function configure()
|
||||
{
|
||||
$this->addArgument('sources', InputArgument::REQUIRED, 'The sources will be compiled, comma separated');
|
||||
}
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources'))));
|
||||
if (empty($sources)) {
|
||||
$this->output->writeln('<error>sources cannot be empty, at least contain one !</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
SourceExtractor::initSource(sources: $sources);
|
||||
logger()->info('Extract done !');
|
||||
return static::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\store\Config;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand('list-ext', 'List supported extensions')]
|
||||
class ListExtCommand extends BaseCommand
|
||||
{
|
||||
protected bool $no_motd = true;
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
foreach (Config::getExts() as $ext => $meta) {
|
||||
echo $ext . PHP_EOL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
116
src/SPC/command/MicroCombineCommand.php
Normal file
116
src/SPC/command/MicroCombineCommand.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\store\FileSystem;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand('micro:combine', 'Combine micro.sfx and php code together')]
|
||||
class MicroCombineCommand extends BaseCommand
|
||||
{
|
||||
public function configure()
|
||||
{
|
||||
$this->addArgument('file', InputArgument::REQUIRED, 'The php or phar file to be combined');
|
||||
$this->addOption('with-micro', 'M', InputOption::VALUE_REQUIRED, 'Customize your micro.sfx file');
|
||||
$this->addOption('with-ini-set', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'ini to inject into micro.sfx when combining');
|
||||
$this->addOption('with-ini-file', 'N', InputOption::VALUE_REQUIRED, 'ini file to inject into micro.sfx when combining');
|
||||
$this->addOption('output', 'O', InputOption::VALUE_REQUIRED, 'Customize your output binary file name');
|
||||
}
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
// 0. Initialize path variables
|
||||
$internal = FileSystem::convertPath(BUILD_ROOT_PATH . '/bin/micro.sfx');
|
||||
$micro_file = $this->input->getOption('with-micro');
|
||||
$file = $this->getArgument('file');
|
||||
$ini_set = $this->input->getOption('with-ini-set');
|
||||
$ini_file = $this->input->getOption('with-ini-file');
|
||||
$target_ini = [];
|
||||
$output = $this->input->getOption('output') ?? 'my-app';
|
||||
$ini_part = '';
|
||||
// 1. Make sure specified micro.sfx file exists
|
||||
if ($micro_file !== null && !file_exists($micro_file)) {
|
||||
$this->output->writeln('<error>The micro.sfx file you specified is incorrect or does not exist!</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
// 2. Make sure buildroot/bin/micro.sfx exists
|
||||
if ($micro_file === null && !file_exists($internal)) {
|
||||
$this->output->writeln('<error>You haven\'t compiled micro.sfx yet, please use "build" command and "--build-micro" to compile phpmicro first!</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
// 3. Use buildroot/bin/micro.sfx
|
||||
if ($micro_file === null) {
|
||||
$micro_file = $internal;
|
||||
}
|
||||
// 4. Make sure php or phar file exists
|
||||
if (!is_file(FileSystem::convertPath($file))) {
|
||||
$this->output->writeln('<error>The file to combine does not exist!</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
// 5. Confirm ini files (ini-set has higher priority)
|
||||
if ($ini_file !== null) {
|
||||
// Check file exist first
|
||||
if (!file_exists($ini_file)) {
|
||||
$this->output->writeln('<error>The ini file to combine does not exist! (' . $ini_file . ')</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
$arr = parse_ini_file($ini_file);
|
||||
if ($arr === false) {
|
||||
$this->output->writeln('<error>Cannot parse ini file</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
$target_ini = array_merge($target_ini, $arr);
|
||||
}
|
||||
// 6. Confirm ini sets
|
||||
if ($ini_set !== []) {
|
||||
foreach ($ini_set as $item) {
|
||||
$arr = parse_ini_string($item);
|
||||
if ($arr === false) {
|
||||
$this->output->writeln('<error>--with-ini-set parse failed</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
$target_ini = array_merge($target_ini, $arr);
|
||||
}
|
||||
}
|
||||
// 7. Generate ini injection parts
|
||||
if (!empty($target_ini)) {
|
||||
$ini_str = $this->encodeINI($target_ini);
|
||||
logger()->debug('Injecting ini parts: ' . PHP_EOL . $ini_str);
|
||||
$ini_part = "\xfd\xf6\x69\xe6";
|
||||
$ini_part .= pack('N', strlen($ini_str));
|
||||
$ini_part .= $ini_str;
|
||||
}
|
||||
// 8. Combine !
|
||||
$output = FileSystem::isRelativePath($output) ? (WORKING_DIR . '/' . $output) : $output;
|
||||
$file_target = file_get_contents($micro_file) . $ini_part . file_get_contents($file);
|
||||
$result = file_put_contents($output, $file_target);
|
||||
if ($result === false) {
|
||||
$this->output->writeln('<error>Combine failed.</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
// 9. chmod +x
|
||||
chmod($output, 0755);
|
||||
$this->output->writeln('<info>Combine success! Binary file: ' . $output . '</info>');
|
||||
return static::SUCCESS;
|
||||
}
|
||||
|
||||
private function encodeINI(array $array): string
|
||||
{
|
||||
$res = [];
|
||||
foreach ($array as $key => $val) {
|
||||
if (is_array($val)) {
|
||||
$res[] = "[{$key}]";
|
||||
foreach ($val as $skey => $sval) {
|
||||
$res[] = "{$skey}=" . (is_numeric($sval) ? $sval : '"' . $sval . '"');
|
||||
}
|
||||
} else {
|
||||
$res[] = "{$key}=" . (is_numeric($val) ? $val : '"' . $val . '"');
|
||||
}
|
||||
}
|
||||
return implode("\n", $res);
|
||||
}
|
||||
}
|
||||
24
src/SPC/command/dev/AllExtCommand.php
Normal file
24
src/SPC/command/dev/AllExtCommand.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command\dev;
|
||||
|
||||
use SPC\command\BaseCommand;
|
||||
use SPC\store\Config;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand('dev:ext-all', 'Dev command', ['list-ext'])]
|
||||
class AllExtCommand extends BaseCommand
|
||||
{
|
||||
public function configure()
|
||||
{
|
||||
$this->addOption('line', 'l', null, 'Show with separate lines');
|
||||
}
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$this->output->writeln(implode($this->input->getOption('line') ? PHP_EOL : ',', array_keys(Config::getExts())));
|
||||
return static::SUCCESS;
|
||||
}
|
||||
}
|
||||
42
src/SPC/command/dev/ExtInfoCommand.php
Normal file
42
src/SPC/command/dev/ExtInfoCommand.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command\dev;
|
||||
|
||||
use SPC\command\BaseCommand;
|
||||
use SPC\store\Config;
|
||||
use SPC\util\DependencyUtil;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
#[AsCommand('dev:ext-info', 'Dev command')]
|
||||
class ExtInfoCommand extends BaseCommand
|
||||
{
|
||||
public function configure()
|
||||
{
|
||||
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extension name you need to get info');
|
||||
}
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$extensions = array_map('trim', array_filter(explode(',', $this->getArgument('extensions'))));
|
||||
|
||||
// 根据提供的扩展列表获取依赖库列表并编译
|
||||
foreach ($extensions as $extension) {
|
||||
$this->output->writeln('<comment>[ ' . $extension . ' ]</comment>');
|
||||
[, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps([$extension]);
|
||||
$lib_suggests = Config::getExt($extension, 'lib-suggests', []);
|
||||
$ext_suggests = Config::getExt($extension, 'ext-suggests', []);
|
||||
$this->output->writeln("<info>lib-depends:\t" . implode(', ', $libraries) . '</info>');
|
||||
$this->output->writeln("<info>lib-suggests:\t" . implode(', ', $lib_suggests) . '</info>');
|
||||
$this->output->writeln("<info>ext-depends:\t" . implode(',', $not_included) . '</info>');
|
||||
$this->output->writeln("<info>ext-suggests:\t" . implode(', ', $ext_suggests) . '</info>');
|
||||
if (Config::getExt($extension, 'unix-only', false)) {
|
||||
$this->output->writeln("<info>Unix only:\ttrue</info>");
|
||||
}
|
||||
$this->output->writeln('');
|
||||
}
|
||||
return static::SUCCESS;
|
||||
}
|
||||
}
|
||||
33
src/SPC/command/dev/PhpVerCommand.php
Normal file
33
src/SPC/command/dev/PhpVerCommand.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command\dev;
|
||||
|
||||
use SPC\command\BaseCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
#[AsCommand('dev:php-ver', 'Dev command')]
|
||||
class PhpVerCommand extends BaseCommand
|
||||
{
|
||||
public function initialize(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->no_motd = true;
|
||||
parent::initialize($input, $output);
|
||||
}
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
// Find php from source/php-src
|
||||
$file = SOURCE_PATH . '/php-src/main/php_version.h';
|
||||
$result = preg_match('/#define PHP_VERSION "([^"]+)"/', file_get_contents($file), $match);
|
||||
if ($result === false) {
|
||||
$this->output->writeln('<error>PHP source not found, maybe you need to extract first ?</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
$this->output->writeln('<info>' . $match[1] . '</info>');
|
||||
return static::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
namespace SPC\command\dev;
|
||||
|
||||
use SPC\command\BaseCommand;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\ValidationException;
|
||||
use SPC\store\FileSystem;
|
||||
@@ -12,9 +13,9 @@ use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
/**
|
||||
* 修改 config 后对其 kv 进行排序的操作
|
||||
* Modify config file: sort lib, ext, source by name.
|
||||
*/
|
||||
#[AsCommand('sort-config', 'After config edited, sort it by alphabet')]
|
||||
#[AsCommand('dev:sort-config', 'After config edited, sort it by alphabet', ['sort-config'])]
|
||||
class SortConfigCommand extends BaseCommand
|
||||
{
|
||||
public function configure()
|
||||
@@ -33,25 +34,34 @@ class SortConfigCommand extends BaseCommand
|
||||
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/lib.json'), true);
|
||||
ConfigValidator::validateLibs($file);
|
||||
ksort($file);
|
||||
file_put_contents(ROOT_DIR . '/config/lib.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
if (!file_put_contents(ROOT_DIR . '/config/lib.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) {
|
||||
$this->output->writeln('<error>Write file lib.json failed!</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
break;
|
||||
case 'source':
|
||||
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/source.json'), true);
|
||||
ConfigValidator::validateSource($file);
|
||||
uksort($file, fn ($a, $b) => $a === 'php-src' ? -1 : ($b === 'php-src' ? 1 : ($a < $b ? -1 : 1)));
|
||||
file_put_contents(ROOT_DIR . '/config/source.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
if (!file_put_contents(ROOT_DIR . '/config/source.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) {
|
||||
$this->output->writeln('<error>Write file source.json failed!</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
break;
|
||||
case 'ext':
|
||||
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/ext.json'), true);
|
||||
ConfigValidator::validateExts($file);
|
||||
ksort($file);
|
||||
file_put_contents(ROOT_DIR . '/config/ext.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
if (!file_put_contents(ROOT_DIR . '/config/ext.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) {
|
||||
$this->output->writeln('<error>Write file ext.json failed!</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$this->output->writeln("<error>invalid config name: {$name}</error>");
|
||||
return 1;
|
||||
}
|
||||
$this->output->writeln('<info>sort success</info>');
|
||||
return 0;
|
||||
return static::SUCCESS;
|
||||
}
|
||||
}
|
||||
90
src/SPC/doctor/item/LinuxToolCheckList.php
Normal file
90
src/SPC/doctor/item/LinuxToolCheckList.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\doctor\item;
|
||||
|
||||
use SPC\builder\linux\SystemUtil;
|
||||
use SPC\builder\traits\UnixSystemUtilTrait;
|
||||
use SPC\doctor\AsCheckItem;
|
||||
use SPC\doctor\AsFixItem;
|
||||
use SPC\doctor\CheckResult;
|
||||
use SPC\exception\RuntimeException;
|
||||
|
||||
class LinuxToolCheckList
|
||||
{
|
||||
use UnixSystemUtilTrait;
|
||||
|
||||
public const TOOLS_ALPINE = [
|
||||
'make', 'bison', 'flex',
|
||||
'git', 'autoconf', 'automake',
|
||||
'tar', 'unzip', 'gzip',
|
||||
'bzip2', 'cmake', 'gcc',
|
||||
'g++', 'patch',
|
||||
];
|
||||
|
||||
public const TOOLS_DEBIAN = [
|
||||
'make', 'bison', 'flex',
|
||||
'git', 'autoconf', 'automake',
|
||||
'tar', 'unzip', 'gzip',
|
||||
'bzip2', 'cmake', 'patch',
|
||||
];
|
||||
|
||||
#[AsCheckItem('if necessary tools are installed', limit_os: 'Linux')]
|
||||
public function checkCliTools(): ?CheckResult
|
||||
{
|
||||
$distro = SystemUtil::getOSRelease();
|
||||
|
||||
$required = match ($distro['dist']) {
|
||||
'alpine' => self::TOOLS_ALPINE,
|
||||
default => self::TOOLS_DEBIAN,
|
||||
};
|
||||
$missing = [];
|
||||
foreach ($required as $cmd) {
|
||||
if ($this->findCommand($cmd) === null) {
|
||||
$missing[] = $cmd;
|
||||
}
|
||||
}
|
||||
if (!empty($missing)) {
|
||||
return match ($distro['dist']) {
|
||||
'ubuntu', 'alpine', 'debian' => CheckResult::fail(implode(', ', $missing) . ' not installed on your system', 'install-linux-tools', [$distro, $missing]),
|
||||
default => CheckResult::fail(implode(', ', $missing) . ' not installed on your system'),
|
||||
};
|
||||
}
|
||||
return CheckResult::ok();
|
||||
}
|
||||
|
||||
#[AsCheckItem('if necessary packages are installed', limit_os: 'Linux')]
|
||||
public function checkSystemOSPackages(): ?CheckResult
|
||||
{
|
||||
$distro = SystemUtil::getOSRelease();
|
||||
if ($distro['dist'] === 'alpine') {
|
||||
// check linux-headers installation
|
||||
if (!file_exists('/usr/include/linux/mman.h')) {
|
||||
return CheckResult::fail('linux-headers not installed on your system', 'install-linux-tools', [$distro, ['linux-headers']]);
|
||||
}
|
||||
}
|
||||
return CheckResult::ok();
|
||||
}
|
||||
|
||||
#[AsFixItem('install-linux-tools')]
|
||||
public function fixBuildTools(array $distro, array $missing): bool
|
||||
{
|
||||
$install_cmd = match ($distro['dist']) {
|
||||
'ubuntu', 'debian' => 'apt install -y',
|
||||
'alpine' => 'apk add',
|
||||
default => throw new RuntimeException('Current linux distro is not supported for auto-install musl packages'),
|
||||
};
|
||||
$prefix = '';
|
||||
if (get_current_user() !== 'root') {
|
||||
$prefix = 'sudo ';
|
||||
logger()->warning('Current user is not root, using sudo for running command');
|
||||
}
|
||||
try {
|
||||
shell(true)->exec($prefix . $install_cmd . ' ' . implode(' ', $missing));
|
||||
} catch (RuntimeException) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,6 @@ class MacOSToolCheckList
|
||||
'xz',
|
||||
'gzip',
|
||||
'bzip2',
|
||||
'gotop',
|
||||
'cmake',
|
||||
];
|
||||
|
||||
|
||||
@@ -47,7 +47,8 @@ class ExceptionHandler
|
||||
logger()->error($e->getTraceAsString());
|
||||
return;
|
||||
}
|
||||
|
||||
$this->whoops->handleException($e);
|
||||
|
||||
logger()->critical('You can report this exception to static-php-cli GitHub repo.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ class Downloader
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function downloadSource(string $name, ?array $source = null): void
|
||||
public static function downloadSource(string $name, ?array $source = null, bool $force = false): void
|
||||
{
|
||||
if ($source === null) {
|
||||
$source = Config::getSource($name);
|
||||
@@ -278,7 +278,7 @@ class Downloader
|
||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
|
||||
}
|
||||
// If lock file exists, skip downloading
|
||||
if (isset($lock[$name])) {
|
||||
if (isset($lock[$name]) && !$force) {
|
||||
if ($lock[$name]['source_type'] === 'archive' && file_exists(DOWNLOAD_PATH . '/' . $lock[$name]['filename'])) {
|
||||
logger()->notice("source [{$name}] already downloaded: " . $lock[$name]['filename']);
|
||||
return;
|
||||
|
||||
@@ -320,28 +320,7 @@ class FileSystem
|
||||
foreach ($files as $v) {
|
||||
$pathinfo = pathinfo($v);
|
||||
if (($pathinfo['extension'] ?? '') == 'php') {
|
||||
$path = rtrim($dir, '/') . '/' . rtrim($pathinfo['dirname'], './') . '/' . $pathinfo['basename'];
|
||||
|
||||
// 过滤不包含类的文件
|
||||
$tokens = token_get_all(self::readFile($path));
|
||||
$found = false;
|
||||
foreach ($tokens as $token) {
|
||||
if (!is_array($token)) {
|
||||
continue;
|
||||
}
|
||||
if ($token[0] === T_CLASS) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$found) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($rule === null) { // 规则未设置回调时候,使用默认的识别过滤规则
|
||||
/*if (substr(file_get_contents($dir . '/' . $v), 6, 6) == '#plain') {
|
||||
continue;
|
||||
}*/
|
||||
if ($rule === null) {
|
||||
if (file_exists($dir . '/' . $pathinfo['basename'] . '.ignore')) {
|
||||
continue;
|
||||
}
|
||||
@@ -439,6 +418,20 @@ class FileSystem
|
||||
self::$_extract_hook[$name][] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the path is a relative path (judging according to whether the first character is "/")
|
||||
*
|
||||
* @param string $path Path
|
||||
*/
|
||||
public static function isRelativePath(string $path): bool
|
||||
{
|
||||
// 适配 Windows 的多盘符目录形式
|
||||
if (DIRECTORY_SEPARATOR === '\\') {
|
||||
return !(strlen($path) > 2 && ctype_alpha($path[0]) && $path[1] === ':');
|
||||
}
|
||||
return strlen($path) > 0 && $path[0] !== '/';
|
||||
}
|
||||
|
||||
private static function emitSourceExtractHook(string $name)
|
||||
{
|
||||
foreach ((self::$_extract_hook[$name] ?? []) as $hook) {
|
||||
|
||||
61
src/SPC/store/SourceExtractor.php
Normal file
61
src/SPC/store/SourceExtractor.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\store;
|
||||
|
||||
use SPC\exception\WrongUsageException;
|
||||
|
||||
class SourceExtractor
|
||||
{
|
||||
public static function initSource(?array $sources = null, ?array $libs = null, ?array $exts = null): void
|
||||
{
|
||||
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
|
||||
throw new WrongUsageException('Download lock file "downloads/.lock.json" not found, maybe you need to download sources first ?');
|
||||
}
|
||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true);
|
||||
|
||||
$sources_extracted = [];
|
||||
// source check exist
|
||||
if (is_array($sources)) {
|
||||
foreach ($sources as $source) {
|
||||
$sources_extracted[$source] = true;
|
||||
}
|
||||
}
|
||||
// lib check source exist
|
||||
if (is_array($libs)) {
|
||||
foreach ($libs as $lib) {
|
||||
// get source name for lib
|
||||
$source = Config::getLib($lib, 'source');
|
||||
$sources_extracted[$source] = true;
|
||||
}
|
||||
}
|
||||
// ext check source exist
|
||||
if (is_array($exts)) {
|
||||
foreach ($exts as $ext) {
|
||||
// get source name for ext
|
||||
if (Config::getExt($ext, 'type') !== 'external') {
|
||||
continue;
|
||||
}
|
||||
$source = Config::getExt($ext, 'source');
|
||||
$sources_extracted[$source] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// start check
|
||||
foreach ($sources_extracted as $source => $item) {
|
||||
if (Config::getSource($source) === null) {
|
||||
throw new WrongUsageException("Source [{$source}] not exists, please check name and correct it !");
|
||||
}
|
||||
if (!isset($lock[$source])) {
|
||||
throw new WrongUsageException('Source [' . $source . '] not downloaded or not locked, you should download it first !');
|
||||
}
|
||||
|
||||
// check source dir exist
|
||||
$check = $lock[$source]['move_path'] === null ? SOURCE_PATH . '/' . $source : SOURCE_PATH . '/' . $lock[$source]['move_path'];
|
||||
if (!is_dir($check)) {
|
||||
FileSystem::extractSource($source, DOWNLOAD_PATH . '/' . ($lock[$source]['filename'] ?? $lock[$source]['dirname']), $lock[$source]['move_path']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,137 +6,47 @@ namespace SPC\store;
|
||||
|
||||
use SPC\builder\BuilderBase;
|
||||
use SPC\builder\linux\LinuxBuilder;
|
||||
use SPC\builder\linux\SystemUtil;
|
||||
use SPC\builder\macos\MacOSBuilder;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\util\Util;
|
||||
|
||||
class SourcePatcher
|
||||
{
|
||||
public static function init()
|
||||
{
|
||||
FileSystem::addSourceExtractHook('swow', [SourcePatcher::class, 'patchSwow']);
|
||||
// FileSystem::addSourceExtractHook('swow', [SourcePatcher::class, 'patchSwow']);
|
||||
FileSystem::addSourceExtractHook('micro', [SourcePatcher::class, 'patchMicro']);
|
||||
FileSystem::addSourceExtractHook('openssl', [SourcePatcher::class, 'patchOpenssl11Darwin']);
|
||||
}
|
||||
|
||||
public static function patchPHPBuildconf(BuilderBase $builder): void
|
||||
/**
|
||||
* Source patcher runner before buildconf
|
||||
*
|
||||
* @param BuilderBase $builder Builder
|
||||
*/
|
||||
public static function patchBeforeBuildconf(BuilderBase $builder): void
|
||||
{
|
||||
if ($builder->getExt('curl')) {
|
||||
logger()->info('patching before-configure for curl checks');
|
||||
$file1 = "AC_DEFUN([PHP_CHECK_LIBRARY], [\n $3\n])";
|
||||
$files = FileSystem::readFile(SOURCE_PATH . '/php-src/ext/curl/config.m4');
|
||||
$file2 = 'AC_DEFUN([PHP_CHECK_LIBRARY], [
|
||||
save_old_LDFLAGS=$LDFLAGS
|
||||
ac_stuff="$5"
|
||||
|
||||
save_ext_shared=$ext_shared
|
||||
ext_shared=yes
|
||||
PHP_EVAL_LIBLINE([$]ac_stuff, LDFLAGS)
|
||||
AC_CHECK_LIB([$1],[$2],[
|
||||
LDFLAGS=$save_old_LDFLAGS
|
||||
ext_shared=$save_ext_shared
|
||||
$3
|
||||
],[
|
||||
LDFLAGS=$save_old_LDFLAGS
|
||||
ext_shared=$save_ext_shared
|
||||
unset ac_cv_lib_$1[]_$2
|
||||
$4
|
||||
])dnl
|
||||
])';
|
||||
file_put_contents(SOURCE_PATH . '/php-src/ext/curl/config.m4', $file1 . "\n" . $files . "\n" . $file2);
|
||||
}
|
||||
|
||||
// if ($builder->getExt('pdo_sqlite')) {
|
||||
// FileSystem::replaceFile()
|
||||
// }
|
||||
}
|
||||
|
||||
public static function patchSwow(): bool
|
||||
{
|
||||
if (Util::getPHPVersionID() >= 80000) {
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && mklink /D swow swow-src\ext');
|
||||
} else {
|
||||
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && ln -s swow-src/ext swow');
|
||||
foreach ($builder->getExts() as $ext) {
|
||||
if ($ext->patchBeforeBuildconf() === true) {
|
||||
logger()->info('Extension [' . $ext->getName() . '] patched before buildconf');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function patchPHPConfigure(BuilderBase $builder): void
|
||||
{
|
||||
$frameworks = $builder instanceof MacOSBuilder ? ' ' . $builder->getFrameworks(true) . ' ' : '';
|
||||
$patch = [];
|
||||
if ($curl = $builder->getExt('curl')) {
|
||||
$patch[] = ['curl check', '/-lcurl/', $curl->getLibFilesString() . $frameworks];
|
||||
}
|
||||
if ($bzip2 = $builder->getExt('bz2')) {
|
||||
$patch[] = ['bzip2 check', '/-lbz2/', $bzip2->getLibFilesString() . $frameworks];
|
||||
}
|
||||
if ($pdo_sqlite = $builder->getExt('pdo_sqlite')) {
|
||||
$patch[] = ['pdo_sqlite linking', '/sqlite3_column_table_name=yes/', 'sqlite3_column_table_name=no'];
|
||||
}
|
||||
if ($event = $builder->getExt('event')) {
|
||||
$patch[] = ['event check', '/-levent_openssl/', $event->getLibFilesString()];
|
||||
}
|
||||
if ($readline = $builder->getExt('readline')) {
|
||||
$patch[] = ['readline patch', '/-lncurses/', $readline->getLibFilesString()];
|
||||
}
|
||||
if ($ssh2 = $builder->getExt('ssh2')) {
|
||||
$patch[] = ['ssh2 patch', '/-lssh2/', $ssh2->getLibFilesString()];
|
||||
}
|
||||
$patch[] = ['disable capstone', '/have_capstone="yes"/', 'have_capstone="no"'];
|
||||
foreach ($patch as $item) {
|
||||
logger()->info('Patching configure: ' . $item[0]);
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/configure', REPLACE_FILE_PREG, $item[1], $item[2]);
|
||||
}
|
||||
}
|
||||
|
||||
public static function patchUnixLibpng(): void
|
||||
/**
|
||||
* Source patcher runner before configure
|
||||
*
|
||||
* @param BuilderBase $builder Builder
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function patchBeforeConfigure(BuilderBase $builder): void
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/libpng/configure',
|
||||
REPLACE_FILE_STR,
|
||||
'-lz',
|
||||
BUILD_LIB_PATH . '/libz.a'
|
||||
);
|
||||
if (SystemUtil::getOSRelease()['dist'] === 'alpine') {
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/libpng/configure',
|
||||
REPLACE_FILE_STR,
|
||||
'-lm',
|
||||
'/usr/lib/libm.a'
|
||||
);
|
||||
foreach ($builder->getExts() as $ext) {
|
||||
if ($ext->patchBeforeConfigure() === true) {
|
||||
logger()->info('Extension [' . $ext->getName() . '] patched before configure');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function patchUnixSsh2(): void
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_STR,
|
||||
'-lssh2',
|
||||
BUILD_LIB_PATH . '/libssh2.a'
|
||||
);
|
||||
}
|
||||
|
||||
public static function patchCurlMacOS(): void
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/curl/CMakeLists.txt',
|
||||
REPLACE_FILE_PREG,
|
||||
'/NOT COREFOUNDATION_FRAMEWORK/m',
|
||||
'FALSE'
|
||||
);
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/curl/CMakeLists.txt',
|
||||
REPLACE_FILE_PREG,
|
||||
'/NOT SYSTEMCONFIGURATION_FRAMEWORK/m',
|
||||
'FALSE'
|
||||
);
|
||||
// patch capstone
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/configure', REPLACE_FILE_PREG, '/have_capstone="yes"/', 'have_capstone="no"');
|
||||
}
|
||||
|
||||
public static function patchMicro(?array $list = null, bool $reverse = false): bool
|
||||
@@ -176,7 +86,7 @@ class SourcePatcher
|
||||
}
|
||||
$patch_list = $list ?? $default;
|
||||
$patches = [];
|
||||
$serial = ['80', '81', '82'];
|
||||
$serial = ['80', '81', '82', '83'];
|
||||
foreach ($patch_list as $patchName) {
|
||||
if (file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}.patch")) {
|
||||
$patches[] = "sapi/micro/patches/{$patchName}.patch";
|
||||
@@ -217,19 +127,75 @@ class SourcePatcher
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function patchPHPAfterConfigure(BuilderBase $param)
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function patchBeforeMake(BuilderBase $builder): void
|
||||
{
|
||||
if ($param instanceof LinuxBuilder) {
|
||||
// Try to fix debian environment build lack HAVE_STRLCAT problem
|
||||
if ($builder instanceof LinuxBuilder) {
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCPY 1$/m', '');
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCAT 1$/m', '');
|
||||
}
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_OPENPTY 1$/m', '');
|
||||
|
||||
// patch openssl3 with php8.0 bug
|
||||
if (file_exists(SOURCE_PATH . '/openssl/VERSION.dat') && Util::getPHPVersionID() < 80100) {
|
||||
$openssl_c = file_get_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c');
|
||||
$openssl_c = preg_replace('/REGISTER_LONG_CONSTANT\s*\(\s*"OPENSSL_SSLV23_PADDING"\s*.+;/', '', $openssl_c);
|
||||
file_put_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c', $openssl_c);
|
||||
// call extension patch before make
|
||||
foreach ($builder->getExts() as $ext) {
|
||||
if ($ext->patchBeforeMake() === true) {
|
||||
logger()->info('Extension [' . $ext->getName() . '] patched before make');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function patchHardcodedINI(array $ini = []): bool
|
||||
{
|
||||
$cli_c = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c';
|
||||
$cli_c_bak = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c.bak';
|
||||
$micro_c = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c';
|
||||
$micro_c_bak = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c.bak';
|
||||
|
||||
// Try to reverse backup file
|
||||
$find_pattern = 'const char HARDCODED_INI[] =';
|
||||
$patch_str = '';
|
||||
foreach ($ini as $key => $value) {
|
||||
$patch_str .= "\"{$key}={$value}\\n\"\n";
|
||||
}
|
||||
$patch_str = "const char HARDCODED_INI[] =\n{$patch_str}";
|
||||
|
||||
// Detect backup, if we have backup, it means we need to reverse first
|
||||
if (file_exists($cli_c_bak) || file_exists($micro_c_bak)) {
|
||||
self::unpatchHardcodedINI();
|
||||
}
|
||||
|
||||
// Backup it
|
||||
$result = file_put_contents($cli_c_bak, file_get_contents($cli_c));
|
||||
$result = $result && file_put_contents($micro_c_bak, file_get_contents($micro_c));
|
||||
if ($result === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Patch it
|
||||
FileSystem::replaceFile($cli_c, REPLACE_FILE_STR, $find_pattern, $patch_str);
|
||||
FileSystem::replaceFile($micro_c, REPLACE_FILE_STR, $find_pattern, $patch_str);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function unpatchHardcodedINI(): bool
|
||||
{
|
||||
$cli_c = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c';
|
||||
$cli_c_bak = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c.bak';
|
||||
$micro_c = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c';
|
||||
$micro_c_bak = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c.bak';
|
||||
if (!file_exists($cli_c_bak) && !file_exists($micro_c_bak)) {
|
||||
return false;
|
||||
}
|
||||
$result = file_put_contents($cli_c, file_get_contents($cli_c_bak));
|
||||
$result = $result && file_put_contents($micro_c, file_get_contents($micro_c_bak));
|
||||
@unlink($cli_c_bak);
|
||||
@unlink($micro_c_bak);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user