Compare commits

...

33 Commits

Author SHA1 Message Date
Jerry Ma
5012da96be Update ConsoleApplication.php 2024-02-23 11:42:10 +08:00
Jerry Ma
df0e37cd0f Add mbregex support for windows (#351)
* add mbregex support for windows

* cs fix

* fix curl http2 support
2024-02-23 11:41:35 +08:00
Jerry Ma
4ab7b6bfdc Add sqlite support for Windows (#350)
* add sqlite support

* cs fix
2024-02-23 11:14:51 +08:00
Jerry Ma
f498250001 Add multiple XML related extensions support for Windows (#349)
* update libxml2 version

* use msys2 tar.exe instead of system32/tar.exe

* add iconv, xml, dom, xmlreader, xmlwriter, soap, libxml, simplexml support

* add test

* add sysvshm support

* add quote

* add debug

* use mingw target

* fix windows tar

* fix windows tar

* fix windows tar

* fix windows tar

* fix windows tar [skip ci]
2024-02-23 00:56:28 +08:00
Jerry Ma
3945ac037b Add curl and ssh2 support for windows (#348)
* add curl and ssh2 support for windows

* add curl and ssh2 test for windows

* cs fix

* update README [skip ci]

* update README [skip ci]

* update README [skip ci]

* update README [skip ci]

* update README [skip ci]
2024-02-22 14:37:10 +08:00
crazywhalecc
b0d8b00fcc optimize pkg-config build process 2024-02-20 15:45:00 +08:00
crazywhalecc
62b0bf8af0 fix no-strip not working for linux micro 2024-02-20 10:56:39 +08:00
crazywhalecc
905e080770 fix upx linux build 2024-02-19 15:29:43 +08:00
crazywhalecc
097ecd3fb0 test build spc 2024-02-19 13:43:58 +08:00
crazywhalecc
b9359759dd update README 2024-02-19 13:25:47 +08:00
crazywhalecc
50fe366c42 restore strip line 2024-02-19 13:25:47 +08:00
crazywhalecc
9dd89e6b02 add --with-upx-pack command 2024-02-19 13:25:47 +08:00
crazywhalecc
c03220d1ee fix source extract 2024-02-19 13:25:47 +08:00
crazywhalecc
9db843ab66 fix source extract 2024-02-19 13:25:47 +08:00
Jerry Ma
304973d9bc Update FUNDING.yml 2024-02-19 09:33:54 +08:00
Jerry Ma
ab386f820c Update FUNDING.yml 2024-02-19 09:29:45 +08:00
crazywhalecc
4adf1f5e2e add musl-toolchain pkg for aarch64 2024-02-18 16:22:17 +08:00
crazywhalecc
b4ae87585c treat https://github.com/crazywhalecc/static-php-cli/issues/249 as feature 2024-02-18 16:22:17 +08:00
crazywhalecc
ae3298472d upx use ghrel 2024-02-18 16:22:17 +08:00
crazywhalecc
d241cb993e fix 2024-02-18 16:22:17 +08:00
crazywhalecc
8376122634 sort pkg.json 2024-02-18 16:22:17 +08:00
crazywhalecc
a30e054d7d Add package management 2024-02-18 16:22:17 +08:00
Jerry Ma
78f4317660 Merge pull request #341 from crazywhalecc/no-ansi-build
Apply default `--no-ansi` output
2024-02-18 13:54:27 +08:00
crazywhalecc
3ac3dab6c8 apply default no-ansi output 2024-02-18 09:44:04 +08:00
Jerry Ma
1a7e436ee1 Merge pull request #339 from crazywhalecc/ext/gettext
Add gettext support
2024-02-17 01:31:10 +08:00
crazywhalecc
f11b36ab3c Merge remote-tracking branch 'origin/main' into ext/gettext 2024-02-17 00:47:40 +08:00
Jerry Ma
e2ef195a84 Update release-build.yml 2024-02-17 00:44:03 +08:00
crazywhalecc
faed569e8a fix gettext build for linux 2024-02-16 23:28:14 +08:00
crazywhalecc
49ddb3ec13 fix gettext build 2024-02-16 23:04:58 +08:00
crazywhalecc
71c0387ab0 remove tune flags, add debug output for extension sanity check 2024-02-16 20:17:34 +08:00
crazywhalecc
e5d2d5e689 Merge branch 'main' into ext/gettext
# Conflicts:
#	config/ext.json
#	src/globals/test-extensions.php
2024-02-16 19:42:18 +08:00
crazywhalecc
227bf73870 libxml2 use zlib prefix 2024-02-16 01:45:14 +08:00
crazywhalecc
05e3898e7a add gettext support 2024-02-16 01:28:10 +08:00
54 changed files with 1351 additions and 159 deletions

View File

@@ -104,7 +104,7 @@ jobs:
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
if: ${{startsWith(github.ref, 'refs/tags/') }} if: ${{startsWith(github.ref, 'refs/tags/') }}
with: with:
files: ${{ env.filename }} files: dist/${{ env.filename }}
- name: "Deploy to Self-Hosted Server" - name: "Deploy to Self-Hosted Server"
if: github.repository == 'crazywhalecc/static-php-cli' if: github.repository == 'crazywhalecc/static-php-cli'

3
.gitignore vendored
View File

@@ -16,6 +16,9 @@ docker/source/
# default source build root directory # default source build root directory
/buildroot/ /buildroot/
# default package root directory
/pkgroot/
# tools cache files # tools cache files
.php-cs-fixer.cache .php-cs-fixer.cache
.phpunit.result.cache .phpunit.result.cache

View File

@@ -20,11 +20,12 @@ static-php-cli简称 `spc`)有许多特性:
- :handbag: 构建独立的单文件 PHP 解释器,无需任何依赖 - :handbag: 构建独立的单文件 PHP 解释器,无需任何依赖
- :hamburger: 构建 **[phpmicro](https://github.com/dixyes/phpmicro)** 自执行二进制(将 PHP 代码和 PHP 解释器打包为一个文件) - :hamburger: 构建 **[phpmicro](https://github.com/dixyes/phpmicro)** 自执行二进制(将 PHP 代码和 PHP 解释器打包为一个文件)
- :pill: 提供一键检查和修复编译环境的 Doctor 模块 - :pill: 提供一键检查和修复编译环境的 Doctor 模块
- :zap: 支持多个系统:`Linux``macOS``FreeBSD`[`Windows (WIP)`](https://github.com/crazywhalecc/static-php-cli/pull/301) - :zap: 支持多个系统:`Linux``macOS``FreeBSD``Windows`
- :wrench: 高度自定义的代码 patch 功能 - :wrench: 高度自定义的代码 patch 功能
- :books: 自带编译依赖管理 - :books: 自带编译依赖管理
- 📦 提供由自身编译的独立 `spc` 二进制(使用 spc 和 [box](https://github.com/box-project/box) 构建) - 📦 提供由自身编译的独立 `spc` 二进制(使用 spc 和 [box](https://github.com/box-project/box) 构建)
- :fire: 支持大量 [扩展](https://static-php.dev/zh/guide/extensions.html) - :fire: 支持大量 [扩展](https://static-php.dev/zh/guide/extensions.html)
- :floppy_disk: 整合 UPX 工具(减小二进制文件体积)
**静态 php-cli:** **静态 php-cli:**
@@ -46,16 +47,18 @@ static-php-cli简称 `spc`)有许多特性:
- [扩展组合 - bulk](https://dl.static-php.dev/static-php-cli/bulk/)bulk 组合包含了 [50+](https://dl.static-php.dev/static-php-cli/bulk/README.txt) 个扩展,体积为 70MB 左右。 - [扩展组合 - bulk](https://dl.static-php.dev/static-php-cli/bulk/)bulk 组合包含了 [50+](https://dl.static-php.dev/static-php-cli/bulk/README.txt) 个扩展,体积为 70MB 左右。
- [扩展组合 - minimal](https://dl.static-php.dev/static-php-cli/minimal/)minimal 组合包含了 [5](https://dl.static-php.dev/static-php-cli/minimal/README.txt) 个扩展,体积为 6MB 左右。 - [扩展组合 - minimal](https://dl.static-php.dev/static-php-cli/minimal/)minimal 组合包含了 [5](https://dl.static-php.dev/static-php-cli/minimal/README.txt) 个扩展,体积为 6MB 左右。
对于 Windows 系统,目前支持的扩展较少,故仅提供 SPC 自身运行的最小扩展组合的 `cli``micro`[扩展组合 - spc-min](https://dl.static-php.dev/static-php-cli/windows/spc-min/)。
## 使用 static-php-cli 构建 PHP ## 使用 static-php-cli 构建 PHP
### 编译环境需求 ### 编译环境需求
- PHP >= 8.1(这是 spc 自身需要的版本,不是支持的构建版本) - PHP >= 8.1(这是 spc 自身需要的版本,不是支持的构建版本)
- 扩展:`mbstring,pcntl,posix,tokenizer,phar` - 扩展:`mbstring,tokenizer,phar`
- 系统安装了 `curl``git` - 系统安装了 `curl``git`
是的,本项目采用 PHP 编写,编译前需要一个 PHP 环境,比较滑稽。 是的,本项目采用 PHP 编写,编译前需要一个 PHP 环境,比较滑稽。
但本项目默认可通过自身构建的 micro 和 static-php 二进制运行,其他只需要包含 mbstring、pcntl 扩展和 PHP 版本大于等于 8.1 即可。 但本项目默认可通过自身构建的 micro 和 static-php 二进制运行,其他只需要包含上面提到的扩展和 PHP 版本大于等于 8.1 即可。
下面是架构支持情况,:octocat: 代表支持 GitHub Action 构建,:computer: 代表支持本地构建,空 代表暂不支持。 下面是架构支持情况,:octocat: 代表支持 GitHub Action 构建,:computer: 代表支持本地构建,空 代表暂不支持。
@@ -63,10 +66,26 @@ static-php-cli简称 `spc`)有许多特性:
|---------|----------------------|----------------------| |---------|----------------------|----------------------|
| macOS | :octocat: :computer: | :octocat: :computer: | | macOS | :octocat: :computer: | :octocat: :computer: |
| Linux | :octocat: :computer: | :octocat: :computer: | | Linux | :octocat: :computer: | :octocat: :computer: |
| Windows | | | | Windows | :computer: | |
| FreeBSD | :computer: | :computer: | | FreeBSD | :computer: | :computer: |
前支持编译的 PHP 版本为:`7.3``7.4``8.0``8.1``8.2``8.3` 前支持编译的 PHP 版本
> :warning: 支持,但可能不再提供修复
>
> :heavy_check_mark: 支持
>
> :x: 不支持
| PHP Version | Status | Comment |
|-------------|--------------------|------------------------------|
| 7.2 | :x: | |
| 7.3 | :warning: | phpmicro 和许多扩展不支持 7.3、7.4 版本 |
| 7.4 | :warning: | phpmicro 和许多扩展不支持 7.3、7.4 版本 |
| 8.0 | :heavy_check_mark: | PHP 官方已停止 8.0 的维护 |
| 8.1 | :heavy_check_mark: | |
| 8.2 | :heavy_check_mark: | |
| 8.3 | :heavy_check_mark: | |
### 支持的扩展 ### 支持的扩展
@@ -106,10 +125,16 @@ curl -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-a
curl -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-x86_64 curl -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-x86_64
# macOS aarch64 (Apple) # macOS aarch64 (Apple)
curl -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-aarch64 curl -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-aarch64
# Windows (x86_64, win10 build 17063 or later)
curl.exe -o spc.exe https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-windows-x64.exe
# add x perm # Add execute perm (Linux and macOS only)
chmod +x ./spc chmod +x ./spc
# Run (Linux and macOS)
./spc --version ./spc --version
# Run (Windows powershell)
.\spc.exe --version
``` ```
自托管 `spc` 由 GitHub Actions 构建,你也可以从 Actions 直接下载:[此处](https://github.com/crazywhalecc/static-php-cli/actions/workflows/release-build.yml)。 自托管 `spc` 由 GitHub Actions 构建,你也可以从 Actions 直接下载:[此处](https://github.com/crazywhalecc/static-php-cli/actions/workflows/release-build.yml)。
@@ -148,14 +173,16 @@ bin/spc --version
# 拉取所有依赖库 # 拉取所有依赖库
./bin/spc download --all ./bin/spc download --all
# 只拉取编译指定扩展需要的所有依赖(推荐) # 只拉取编译指定扩展需要的所有依赖(推荐)
./bin/spc download --for-extensions=openssl,pcntl,mbstring,pdo_sqlite ./bin/spc download --for-extensions="openssl,pcntl,mbstring,pdo_sqlite"
# 下载编译不同版本的 PHP (--with-php=x.y推荐 7.3 ~ 8.3) # 下载编译不同版本的 PHP (--with-php=x.y推荐 7.3 ~ 8.3)
./bin/spc download --for-extensions=openssl,curl,mbstring --with-php=8.1 ./bin/spc download --for-extensions="openssl,curl,mbstring" --with-php=8.1
# 构建包含 bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl 扩展的 php-cli 和 micro.sfx # 构建包含 bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl 扩展的 php-cli 和 micro.sfx
./bin/spc build "bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl" --build-cli --build-micro ./bin/spc build "bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl" --build-cli --build-micro
# 编译线程安全版本 (--enable-zts) # 编译线程安全版本 (--enable-zts)
./bin/spc build curl,phar --enable-zts --build-cli ./bin/spc build "curl,phar" --enable-zts --build-cli
# 编译后使用 UPX 减小可执行文件体积 (--with-upx-pack) (至少压缩至原来的 30~50%)
./bin/spc build "curl,phar" --enable-zts --build-cli --with-upx-pack
``` ```
其中,目前支持构建 climicrofpm 和 embed使用以下参数的一个或多个来指定编译的 SAPI 其中,目前支持构建 climicrofpm 和 embed使用以下参数的一个或多个来指定编译的 SAPI
@@ -169,7 +196,7 @@ bin/spc --version
如果出现了任何错误,可以使用 `--debug` 参数来展示完整的输出日志,以供排查错误: 如果出现了任何错误,可以使用 `--debug` 参数来展示完整的输出日志,以供排查错误:
```bash ```bash
./bin/spc build openssl,pcntl,mbstring --debug --build-all ./bin/spc build "openssl,pcntl,mbstring" --debug --build-all
./bin/spc download --all --debug ./bin/spc download --all --debug
``` ```
@@ -248,7 +275,7 @@ bin/spc micro:combine my-app.phar -I "memory_limit=4G" -I "disable_functions=sys
## 赞助本项目 ## 赞助本项目
你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。 你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。你捐赠的一部分将会被用于维护 **static-php.dev** 服务器。
## 开源协议 ## 开源协议

View File

@@ -27,6 +27,7 @@ static-php-cli (you can call it `spc`) has a lot of features:
- :books: Build dependency management - :books: Build dependency management
- 📦 Provide `spc` own standalone executable (built by spc and [box](https://github.com/box-project/box)) - 📦 Provide `spc` own standalone executable (built by spc and [box](https://github.com/box-project/box))
- :fire: Support many popular [extensions](https://static-php.dev/en/guide/extensions.html) - :fire: Support many popular [extensions](https://static-php.dev/en/guide/extensions.html)
- :floppy_disk: UPX integration (significantly reduces binary size)
**Single-file standalone php-cli:** **Single-file standalone php-cli:**
@@ -52,16 +53,19 @@ which can be downloaded directly according to your needs.
- [Extension-Combination - bulk](https://dl.static-php.dev/static-php-cli/bulk/): `bulk` contains [50+](https://dl.static-php.dev/static-php-cli/bulk/README.txt) extensions and is about 70MB in size. - [Extension-Combination - bulk](https://dl.static-php.dev/static-php-cli/bulk/): `bulk` contains [50+](https://dl.static-php.dev/static-php-cli/bulk/README.txt) extensions and is about 70MB in size.
- [Extension-Combination - minimal](https://dl.static-php.dev/static-php-cli/minimal/): `minimal` contains [5](https://dl.static-php.dev/static-php-cli/minimal/README.txt) extensions and is about 6MB in size. - [Extension-Combination - minimal](https://dl.static-php.dev/static-php-cli/minimal/): `minimal` contains [5](https://dl.static-php.dev/static-php-cli/minimal/README.txt) extensions and is about 6MB in size.
For Windows systems, there are currently fewer extensions supported,
so only `cli` and `micro` that run the minimum extension combination of SPC itself are provided: [Extension-Combination - spc-min](https://dl.static-php.dev/static-php-cli/windows/spc-min/).
## Build ## Build
### Compilation Requirements ### Compilation Requirements
- PHP >= 8.1 (This is the version required by spc itself, not the build version)
- Extension: `mbstring,pcntl,posix,tokenizer,phar`
- Supported OS with `curl` and `git` installed
You can say I made a PHP builder written in PHP, pretty funny. You can say I made a PHP builder written in PHP, pretty funny.
But static-php-cli runtime only requires an environment above PHP 8.1 and `mbstring`, `pcntl` extension. But static-php-cli runtime only requires an environment above PHP 8.1 and extensions mentioned below.
- PHP >= 8.1 (This is the version required by spc itself, not the build version)
- Extension: `mbstring,tokenizer,phar`
- Supported OS with `curl` and `git` installed
Here is the supported OS and arch, where :octocat: represents support for GitHub Action builds, Here is the supported OS and arch, where :octocat: represents support for GitHub Action builds,
:computer: represents support for local manual builds, and blank represents not currently supported. :computer: represents support for local manual builds, and blank represents not currently supported.
@@ -73,7 +77,23 @@ Here is the supported OS and arch, where :octocat: represents support for GitHub
| Windows | :computer: | | | Windows | :computer: | |
| FreeBSD | :computer: | :computer: | | FreeBSD | :computer: | :computer: |
Currently supported PHP versions for compilation are: `7.3`, `7.4`, `8.0`, `8.1`, `8.2`, `8.3`. Currently supported PHP versions for compilation:
> :warning: supported but not maintained
>
> :heavy_check_mark: supported
>
> :x: not supported
| PHP Version | Status | Comment |
|-------------|--------------------|---------------------------------------------------|
| 7.2 | :x: | |
| 7.3 | :warning: | phpmicro and some extensions not supported on 7.x |
| 7.4 | :warning: | phpmicro and some extensions not supported on 7.x |
| 8.0 | :heavy_check_mark: | PHP official has stopped maintenance of 8.0 |
| 8.1 | :heavy_check_mark: | |
| 8.2 | :heavy_check_mark: | |
| 8.3 | :heavy_check_mark: | |
### Supported Extensions ### Supported Extensions
@@ -116,10 +136,16 @@ curl -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-a
curl -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-x86_64 curl -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-x86_64
# macOS aarch64 (Apple) # macOS aarch64 (Apple)
curl -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-aarch64 curl -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-aarch64
# Windows (x86_64, win10 build 17063 or later)
curl.exe -o spc.exe https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-windows-x64.exe
# add x perm # Add execute perm (Linux and macOS only)
chmod +x ./spc chmod +x ./spc
# Run (Linux and macOS)
./spc --version ./spc --version
# Run (Windows powershell)
.\spc.exe --version
``` ```
Self-hosted `spc` is built by GitHub Actions, you can also download from Actions artifacts [here](https://github.com/crazywhalecc/static-php-cli/actions/workflows/release-build.yml). Self-hosted `spc` is built by GitHub Actions, you can also download from Actions artifacts [here](https://github.com/crazywhalecc/static-php-cli/actions/workflows/release-build.yml).
@@ -149,7 +175,7 @@ bin/spc --version
Basic usage for building php with some extensions: Basic usage for building php with some extensions:
> If you are using the packaged `spc` binary, you need to replace `bin/spc` with `./spc` in the following commands. > If you are using the packaged standalone `spc` binary, you need to replace `bin/spc` with `./spc` or `.\spc.exe` in the following commands.
```bash ```bash
# Check system tool dependencies, auto-fix them if possible # Check system tool dependencies, auto-fix them if possible
@@ -158,14 +184,16 @@ Basic usage for building php with some extensions:
# fetch all libraries # fetch all libraries
./bin/spc download --all ./bin/spc download --all
# only fetch necessary sources by needed extensions (recommended) # only fetch necessary sources by needed extensions (recommended)
./bin/spc download --for-extensions=openssl,pcntl,mbstring,pdo_sqlite ./bin/spc download --for-extensions="openssl,pcntl,mbstring,pdo_sqlite"
# download different PHP version (--with-php=x.y, recommend 7.3 ~ 8.3) # download different PHP version (--with-php=x.y, recommend 7.3 ~ 8.3)
./bin/spc download --for-extensions=openssl,curl,mbstring --with-php=8.1 ./bin/spc download --for-extensions="openssl,curl,mbstring" --with-php=8.1
# with bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl extension, build both CLI and phpmicro SAPI # with bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl extension, build both CLI and phpmicro SAPI
./bin/spc build bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl --build-cli --build-micro ./bin/spc build "bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl" --build-cli --build-micro
# build thread-safe (ZTS) version (--enable-zts) # build thread-safe (ZTS) version (--enable-zts)
./bin/spc build curl,phar --enable-zts --build-cli ./bin/spc build "curl,phar" --enable-zts --build-cli
# build, pack executable with UPX (--with-upx-pack) (reduce binary size for 30~50%)
./bin/spc build "curl,phar" --enable-zts --build-cli --with-upx-pack
``` ```
Now we support `cli`, `micro`, `fpm` and `embed` SAPI. You can use one or more of the following parameters to specify the compiled SAPI: Now we support `cli`, `micro`, `fpm` and `embed` SAPI. You can use one or more of the following parameters to specify the compiled SAPI:
@@ -179,7 +207,7 @@ Now we support `cli`, `micro`, `fpm` and `embed` SAPI. You can use one or more o
If anything goes wrong, use `--debug` option to display full terminal output: If anything goes wrong, use `--debug` option to display full terminal output:
```bash ```bash
./bin/spc build openssl,pcntl,mbstring --debug --build-all ./bin/spc build "openssl,pcntl,mbstring" --debug --build-all
./bin/spc download --all --debug ./bin/spc download --all --debug
``` ```
@@ -272,7 +300,7 @@ Now there is a [static-php](https://github.com/static-php) organization, which i
## Sponsor this project ## Sponsor this project
You can sponsor my project on [this page](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md). You can sponsor my project on [this page](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md). A portion of your donation will be used to maintain the **static-php.dev** server.
## Open-Source License ## Open-Source License

View File

@@ -24,6 +24,10 @@
"arg-type": "with", "arg-type": "with",
"lib-depends": [ "lib-depends": [
"curl" "curl"
],
"ext-depends-windows": [
"zlib",
"openssl"
] ]
}, },
"dba": { "dba": {
@@ -93,6 +97,13 @@
"freetype" "freetype"
] ]
}, },
"gettext": {
"type": "builtin",
"arg-type": "with-prefix",
"lib-depends": [
"gettext"
]
},
"glfw": { "glfw": {
"type": "external", "type": "external",
"arg-type": "custom", "arg-type": "custom",
@@ -112,8 +123,12 @@
"iconv": { "iconv": {
"type": "builtin", "type": "builtin",
"arg-type": "with-prefix", "arg-type": "with-prefix",
"lib-depends": [ "arg-type-windows": "with",
"lib-depends-unix": [
"libiconv" "libiconv"
],
"lib-depends-windows": [
"libiconv-win"
] ]
}, },
"igbinary": { "igbinary": {
@@ -340,9 +355,11 @@
"simplexml": { "simplexml": {
"type": "builtin", "type": "builtin",
"arg-type": "custom", "arg-type": "custom",
"arg-type-windows": "with",
"lib-depends": [ "lib-depends": [
"libxml2" "libxml2"
],
"ext-depends-windows": [
"xml"
] ]
}, },
"snappy": { "snappy": {
@@ -362,6 +379,9 @@
"arg-type": "custom", "arg-type": "custom",
"lib-depends": [ "lib-depends": [
"libxml2" "libxml2"
],
"ext-depends-windows": [
"xml"
] ]
}, },
"sockets": { "sockets": {
@@ -377,6 +397,7 @@
"sqlite3": { "sqlite3": {
"type": "builtin", "type": "builtin",
"arg-type": "with-prefix", "arg-type": "with-prefix",
"arg-type-windows": "with",
"lib-depends": [ "lib-depends": [
"sqlite" "sqlite"
] ]
@@ -396,8 +417,13 @@
"type": "external", "type": "external",
"source": "ext-ssh2", "source": "ext-ssh2",
"arg-type": "with-prefix", "arg-type": "with-prefix",
"arg-type-windows": "with",
"lib-depends": [ "lib-depends": [
"libssh2" "libssh2"
],
"ext-depends-windows": [
"openssl",
"zlib"
] ]
}, },
"swoole": { "swoole": {
@@ -472,8 +498,7 @@
"unix-only": true "unix-only": true
}, },
"sysvshm": { "sysvshm": {
"type": "builtin", "type": "builtin"
"unix-only": true
}, },
"tidy": { "tidy": {
"type": "builtin", "type": "builtin",
@@ -518,6 +543,9 @@
"arg-type-windows": "with", "arg-type-windows": "with",
"lib-depends": [ "lib-depends": [
"libxml2" "libxml2"
],
"ext-depends-windows": [
"iconv"
] ]
}, },
"xmlreader": { "xmlreader": {
@@ -525,6 +553,10 @@
"arg-type": "custom", "arg-type": "custom",
"lib-depends": [ "lib-depends": [
"libxml2" "libxml2"
],
"ext-depends-windows": [
"xml",
"dom"
] ]
}, },
"xmlwriter": { "xmlwriter": {
@@ -532,6 +564,9 @@
"arg-type": "custom", "arg-type": "custom",
"lib-depends": [ "lib-depends": [
"libxml2" "libxml2"
],
"ext-depends-windows": [
"xml"
] ]
}, },
"xsl": { "xsl": {

View File

@@ -45,19 +45,21 @@
"openssl", "openssl",
"zlib" "zlib"
], ],
"lib-suggests": [ "lib-depends-windows": [
"openssl",
"zlib",
"libssh2",
"nghttp2"
],
"lib-suggests-unix": [
"libssh2", "libssh2",
"brotli", "brotli",
"nghttp2", "nghttp2",
"zstd" "zstd"
], ],
"lib-suggests-windows": [ "lib-suggests-windows": [
"zlib",
"libssh2",
"brotli", "brotli",
"nghttp2", "zstd"
"zstd",
"openssl"
], ],
"frameworks": [ "frameworks": [
"CoreFoundation", "CoreFoundation",
@@ -83,6 +85,22 @@
"brotli" "brotli"
] ]
}, },
"gettext": {
"source": "gettext",
"static-libs-unix": [
"libintl.a"
],
"lib-depends": [
"libiconv"
],
"lib-suggests": [
"ncurses",
"libxml2"
],
"frameworks": [
"CoreFoundation"
]
},
"glfw": { "glfw": {
"source": "ext-glfw", "source": "ext-glfw",
"static-libs-unix": [ "static-libs-unix": [
@@ -227,6 +245,13 @@
"localcharset.h" "localcharset.h"
] ]
}, },
"libiconv-win": {
"source": "libiconv-win",
"static-libs-windows": [
"libiconv.lib",
"libiconv_a.lib"
]
},
"libjpeg": { "libjpeg": {
"source": "libjpeg", "source": "libjpeg",
"static-libs-unix": [ "static-libs-unix": [
@@ -316,21 +341,25 @@
"libxml2.a" "libxml2.a"
], ],
"static-libs-windows": [ "static-libs-windows": [
[ "libxml2s.lib",
"libxml2s.lib", "libxml2_a.lib"
"libxml2_a.lib"
]
], ],
"headers": [ "headers": [
"libxml2" "libxml2"
], ],
"lib-depends": [ "lib-depends-unix": [
"libiconv" "libiconv"
], ],
"lib-suggests": [ "lib-suggests-unix": [
"xz", "xz",
"icu", "icu",
"zlib" "zlib"
],
"lib-depends-windows": [
"libiconv-win"
],
"lib-suggests-windows": [
"zlib"
] ]
}, },
"libxslt": { "libxslt": {
@@ -411,10 +440,8 @@
"libonig.a" "libonig.a"
], ],
"static-libs-windows": [ "static-libs-windows": [
[ "onig.lib",
"onig.lib", "onig_a.lib"
"onig_a.lib"
]
], ],
"headers": [ "headers": [
"oniggnu.h", "oniggnu.h",
@@ -491,7 +518,10 @@
"static-libs-unix": [ "static-libs-unix": [
"libsqlite3.a" "libsqlite3.a"
], ],
"headers-unix": [ "static-libs-windows": [
"libsqlite3_a.lib"
],
"headers": [
"sqlite3.h", "sqlite3.h",
"sqlite3ext.h" "sqlite3ext.h"
] ]

46
config/pkg.json Normal file
View File

@@ -0,0 +1,46 @@
{
"musl-toolchain-aarch64-linux": {
"type": "url",
"url": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/aarch64-musl-toolchain.tgz"
},
"musl-toolchain-x86_64-linux": {
"type": "url",
"url": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/x86_64-musl-toolchain.tgz"
},
"nasm-x86_64-win": {
"type": "url",
"url": "https://www.nasm.us/pub/nasm/releasebuilds/2.16.01/win64/nasm-2.16.01-win64.zip",
"extract-files": {
"nasm-2.16.01/nasm.exe": "{php_sdk_path}/bin/nasm.exe",
"nasm-2.16.01/ndisasm.exe": "{php_sdk_path}/bin/ndisasm.exe"
}
},
"strawberry-perl-x86_64-win": {
"type": "url",
"url": "https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/download/SP_5380_5361/strawberry-perl-5.38.0.1-64bit-portable.zip"
},
"upx-aarch64-linux": {
"type": "ghrel",
"repo": "upx/upx",
"match": "upx.+-arm64_linux\\.tar\\.xz",
"extract-files": {
"upx": "{pkg_root_path}/bin/upx"
}
},
"upx-x86_64-linux": {
"type": "ghrel",
"repo": "upx/upx",
"match": "upx.+-amd64_linux\\.tar\\.xz",
"extract-files": {
"upx": "{pkg_root_path}/bin/upx"
}
},
"upx-x86_64-win": {
"type": "ghrel",
"repo": "upx/upx",
"match": "upx.+-win64\\.zip",
"extract-files": {
"upx-*-win64/upx.exe": "{pkg_root_path}/bin/upx.exe"
}
}
}

View File

@@ -129,6 +129,15 @@
"path": "LICENSE.TXT" "path": "LICENSE.TXT"
} }
}, },
"gettext": {
"type": "filelist",
"url": "https://ftp.gnu.org/pub/gnu/gettext/",
"regex": "/href=\"(?<file>gettext-(?<version>[^\"]+)\\.tar\\.xz)\"/",
"license": {
"type": "file",
"path": "COPYING"
}
},
"gmp": { "gmp": {
"type": "ghtagtar", "type": "ghtagtar",
"repo": "alisw/GMP", "repo": "alisw/GMP",
@@ -245,6 +254,15 @@
"path": "COPYING" "path": "COPYING"
} }
}, },
"libiconv-win": {
"type": "git",
"rev": "master",
"url": "https://github.com/static-php/libiconv-win.git",
"license": {
"type": "file",
"path": "source/COPYING"
}
},
"libjpeg": { "libjpeg": {
"type": "ghtar", "type": "ghtar",
"repo": "libjpeg-turbo/libjpeg-turbo", "repo": "libjpeg-turbo/libjpeg-turbo",
@@ -322,7 +340,7 @@
}, },
"libxml2": { "libxml2": {
"type": "url", "type": "url",
"url": "https://github.com/GNOME/libxml2/archive/refs/tags/v2.9.14.tar.gz", "url": "https://github.com/GNOME/libxml2/archive/refs/tags/v2.12.5.tar.gz",
"license": { "license": {
"type": "file", "type": "file",
"path": "Copyright" "path": "Copyright"

View File

@@ -6,6 +6,7 @@ namespace SPC;
use SPC\command\BuildCliCommand; use SPC\command\BuildCliCommand;
use SPC\command\BuildLibsCommand; use SPC\command\BuildLibsCommand;
use SPC\command\DeleteDownloadCommand;
use SPC\command\dev\AllExtCommand; use SPC\command\dev\AllExtCommand;
use SPC\command\dev\PhpVerCommand; use SPC\command\dev\PhpVerCommand;
use SPC\command\dev\SortConfigCommand; use SPC\command\dev\SortConfigCommand;
@@ -13,6 +14,7 @@ use SPC\command\DoctorCommand;
use SPC\command\DownloadCommand; use SPC\command\DownloadCommand;
use SPC\command\DumpLicenseCommand; use SPC\command\DumpLicenseCommand;
use SPC\command\ExtractCommand; use SPC\command\ExtractCommand;
use SPC\command\InstallPkgCommand;
use SPC\command\MicroCombineCommand; use SPC\command\MicroCombineCommand;
use Symfony\Component\Console\Application; use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\HelpCommand; use Symfony\Component\Console\Command\HelpCommand;
@@ -23,7 +25,7 @@ use Symfony\Component\Console\Command\ListCommand;
*/ */
final class ConsoleApplication extends Application final class ConsoleApplication extends Application
{ {
public const VERSION = '2.1.0-beta.3'; public const VERSION = '2.1.0';
public function __construct() public function __construct()
{ {
@@ -35,6 +37,8 @@ final class ConsoleApplication extends Application
new BuildLibsCommand(), new BuildLibsCommand(),
new DoctorCommand(), new DoctorCommand(),
new DownloadCommand(), new DownloadCommand(),
new InstallPkgCommand(),
new DeleteDownloadCommand(),
new DumpLicenseCommand(), new DumpLicenseCommand(),
new ExtractCommand(), new ExtractCommand(),
new MicroCombineCommand(), new MicroCombineCommand(),

View File

@@ -9,7 +9,7 @@ use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\SourceExtractor; use SPC\store\SourceManager;
use SPC\util\CustomExt; use SPC\util\CustomExt;
abstract class BuilderBase abstract class BuilderBase
@@ -144,15 +144,15 @@ abstract class BuilderBase
{ {
CustomExt::loadCustomExt(); CustomExt::loadCustomExt();
$this->emitPatchPoint('before-php-extract'); $this->emitPatchPoint('before-php-extract');
SourceExtractor::initSource(sources: ['php-src']); SourceManager::initSource(sources: ['php-src']);
$this->emitPatchPoint('after-php-extract'); $this->emitPatchPoint('after-php-extract');
if ($this->getPHPVersionID() >= 80000) { if ($this->getPHPVersionID() >= 80000) {
$this->emitPatchPoint('before-micro-extract'); $this->emitPatchPoint('before-micro-extract');
SourceExtractor::initSource(sources: ['micro']); SourceManager::initSource(sources: ['micro']);
$this->emitPatchPoint('after-micro-extract'); $this->emitPatchPoint('after-micro-extract');
} }
$this->emitPatchPoint('before-exts-extract'); $this->emitPatchPoint('before-exts-extract');
SourceExtractor::initSource(exts: $extensions); SourceManager::initSource(exts: $extensions);
$this->emitPatchPoint('after-exts-extract'); $this->emitPatchPoint('after-exts-extract');
foreach ($extensions as $extension) { foreach ($extensions as $extension) {
$class = CustomExt::getExtClass($extension); $class = CustomExt::getExtClass($extension);

View File

@@ -185,8 +185,11 @@ class Extension
file_get_contents(ROOT_DIR . '/src/globals/tests/' . $this->getName() . '.php') file_get_contents(ROOT_DIR . '/src/globals/tests/' . $this->getName() . '.php')
); );
[$ret] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -r "' . trim($test) . '"'); [$ret, $out] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -r "' . trim($test) . '"');
if ($ret !== 0) { if ($ret !== 0) {
if ($this->builder->getOption('debug')) {
var_dump($out);
}
throw new RuntimeException('extension ' . $this->getName() . ' failed sanity check'); throw new RuntimeException('extension ' . $this->getName() . ' failed sanity check');
} }
} }
@@ -209,7 +212,7 @@ class Extension
// Trim additional content & escape special characters to allow inline usage // Trim additional content & escape special characters to allow inline usage
$test = str_replace( $test = str_replace(
['<?php', 'declare(strict_types=1);', "\n", '"', '$'], ['<?php', 'declare(strict_types=1);', "\n", '"', '$'],
['', '', '', '\"', '\$'], ['', '', '', '\"', '$'],
file_get_contents(FileSystem::convertPath(ROOT_DIR . '/src/globals/tests/' . $this->getName() . '.php')) file_get_contents(FileSystem::convertPath(ROOT_DIR . '/src/globals/tests/' . $this->getName() . '.php'))
); );

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\builder\macos\MacOSBuilder;
use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('gettext')]
class gettext extends Extension
{
/**
* @throws FileSystemException
*/
public function patchBeforeBuildconf(): bool
{
if ($this->builder instanceof MacOSBuilder) {
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/gettext/config.m4', 'AC_CHECK_LIB($GETTEXT_CHECK_IN_LIB', 'AC_CHECK_LIB(intl');
}
return true;
}
/**
* @throws WrongUsageException
* @throws FileSystemException
*/
public function patchBeforeConfigure(): bool
{
if ($this->builder instanceof MacOSBuilder) {
$frameworks = ' ' . $this->builder->getFrameworks(true) . ' ';
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/configure', '-lintl', $this->getLibFilesString() . $frameworks);
}
return true;
}
}

View File

@@ -31,4 +31,16 @@ class mbregex extends Extension
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: compiled php-cli mbstring extension does not contain regex !'); throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: compiled php-cli mbstring extension does not contain regex !');
} }
} }
public function runCliCheckWindows(): void
{
[$ret, $out] = cmd()->execWithResult(BUILD_ROOT_PATH . '/bin/php --ri "mbstring"', false);
if ($ret !== 0) {
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: compiled php-cli does not contain mbstring !');
}
$out = implode("\n", $out);
if (!str_contains($out, 'regex')) {
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: compiled php-cli mbstring extension does not contain regex !');
}
}
} }

View File

@@ -15,6 +15,8 @@ class mbstring extends Extension
$arg = '--enable-mbstring'; $arg = '--enable-mbstring';
if ($this->builder->getExt('mbregex') === null) { if ($this->builder->getExt('mbregex') === null) {
$arg .= ' --disable-mbregex'; $arg .= ' --disable-mbregex';
} else {
$arg .= ' --enable-mbregex';
} }
return $arg; return $arg;
} }

View File

@@ -6,6 +6,7 @@ namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
#[CustomExt('xml')] #[CustomExt('xml')]
@@ -33,4 +34,25 @@ class xml extends Extension
$arg .= ' --with-libxml="' . BUILD_ROOT_PATH . '"'; $arg .= ' --with-libxml="' . BUILD_ROOT_PATH . '"';
return $arg; return $arg;
} }
public function patchBeforeBuildconf(): bool
{
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/win32/build/config.w32', 'dllmain.c ', '');
return true;
}
public function getWindowsConfigureArg(): string
{
$arg = match ($this->name) {
'xml' => '--with-xml',
'soap' => '--enable-soap',
'xmlreader' => '--enable-xmlreader',
'xmlwriter' => '--enable-xmlwriter',
'dom' => '--with-dom',
'simplexml' => '--with-simplexml',
default => throw new RuntimeException('Not accept non-xml extension'),
};
$arg .= ' --with-libxml';
return $arg;
}
} }

View File

@@ -169,6 +169,27 @@ class LinuxBuilder extends UnixBuilderBase
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO; $enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED; $enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
// upx pack and strip for micro
if ($this->getOption('with-upx-pack', false)) {
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
'/POST_MICRO_BUILD_COMMANDS=.*/',
'POST_MICRO_BUILD_COMMANDS=\$(STRIP) \$(MICRO_STRIP_FLAGS) \$(SAPI_MICRO_PATH) && ' . $this->getOption('upx-exec') . ' --best \$(SAPI_MICRO_PATH)',
);
} elseif (!$this->getOption('no-strip', false)) {
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
'/POST_MICRO_BUILD_COMMANDS=.*/',
'POST_MICRO_BUILD_COMMANDS=\$(STRIP) \$(MICRO_STRIP_FLAGS) \$(SAPI_MICRO_PATH)',
);
} else {
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
'/POST_MICRO_BUILD_COMMANDS=.*/',
'POST_MICRO_BUILD_COMMANDS=true',
);
}
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec( ->exec(
"{$this->getOption('ld_library_path')} " . "{$this->getOption('ld_library_path')} " .
@@ -236,7 +257,11 @@ class LinuxBuilder extends UnixBuilderBase
->exec('sed -i "s|//lib|/lib|g" Makefile') ->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec("make -j{$this->concurrency} {$vars} cli"); ->exec("make -j{$this->concurrency} {$vars} cli");
if (!$this->getOption('no-strip', false)) { if ($this->getOption('with-upx-pack')) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')
->exec('strip --strip-all php')
->exec($this->getOption('upx-exec') . ' --best php');
} elseif (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')->exec('strip --strip-all php'); shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')->exec('strip --strip-all php');
} }
@@ -267,10 +292,6 @@ class LinuxBuilder extends UnixBuilderBase
->exec('sed -i "s|//lib|/lib|g" Makefile') ->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec("make -j{$this->concurrency} {$vars} micro"); ->exec("make -j{$this->concurrency} {$vars} micro");
if (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec('strip --strip-all micro.sfx');
}
$this->deployBinary(BUILD_TARGET_MICRO); $this->deployBinary(BUILD_TARGET_MICRO);
if ($this->phar_patched) { if ($this->phar_patched) {
@@ -291,10 +312,13 @@ class LinuxBuilder extends UnixBuilderBase
->exec('sed -i "s|//lib|/lib|g" Makefile') ->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec("make -j{$this->concurrency} {$vars} fpm"); ->exec("make -j{$this->concurrency} {$vars} fpm");
if (!$this->getOption('no-strip', false)) { if ($this->getOption('with-upx-pack')) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')
->exec('strip --strip-all php-fpm')
->exec($this->getOption('upx-exec') . ' --best php-fpm');
} elseif (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')->exec('strip --strip-all php-fpm'); shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')->exec('strip --strip-all php-fpm');
} }
$this->deployBinary(BUILD_TARGET_FPM); $this->deployBinary(BUILD_TARGET_FPM);
} }

View File

@@ -110,11 +110,7 @@ class SystemUtil
public static function getTuneCFlags(string $arch): array public static function getTuneCFlags(string $arch): array
{ {
return match ($arch) { return match ($arch) {
'x86_64' => [ 'x86_64', 'arm64', 'aarch64' => [],
'-march=corei7',
'-mtune=core-avx2',
],
'arm64', 'aarch64' => [],
default => throw new RuntimeException('unsupported arch: ' . $arch), default => throw new RuntimeException('unsupported arch: ' . $arch),
}; };
} }

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\linux\library;
class gettext extends LinuxLibraryBase
{
use \SPC\builder\unix\library\gettext;
public const NAME = 'gettext';
}

View File

@@ -18,7 +18,7 @@ class libxml2 extends LinuxLibraryBase
*/ */
public function build(): void public function build(): void
{ {
$enable_zlib = $this->builder->getLib('zlib') ? 'ON' : 'OFF'; $enable_zlib = $this->builder->getLib('zlib') ? ('ON -DZLIB_LIBRARY=' . BUILD_LIB_PATH . '/libz.a -DZLIB_INCLUDE_DIR=' . BUILD_INCLUDE_PATH) : 'OFF';
$enable_icu = $this->builder->getLib('icu') ? 'ON' : 'OFF'; $enable_icu = $this->builder->getLib('icu') ? 'ON' : 'OFF';
$enable_xz = $this->builder->getLib('xz') ? 'ON' : 'OFF'; $enable_xz = $this->builder->getLib('xz') ? 'ON' : 'OFF';

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\macos\library;
class gettext extends MacOSLibraryBase
{
use \SPC\builder\unix\library\gettext;
public const NAME = 'gettext';
}

View File

@@ -18,7 +18,7 @@ class libxml2 extends MacOSLibraryBase
*/ */
protected function build(): void protected function build(): void
{ {
$enable_zlib = $this->builder->getLib('zlib') ? 'ON' : 'OFF'; $enable_zlib = $this->builder->getLib('zlib') ? ('ON -DZLIB_LIBRARY=' . BUILD_LIB_PATH . '/libz.a -DZLIB_INCLUDE_DIR=' . BUILD_INCLUDE_PATH) : 'OFF';
$enable_icu = $this->builder->getLib('icu') ? 'ON' : 'OFF'; $enable_icu = $this->builder->getLib('icu') ? 'ON' : 'OFF';
$enable_xz = $this->builder->getLib('xz') ? 'ON' : 'OFF'; $enable_xz = $this->builder->getLib('xz') ? 'ON' : 'OFF';

View File

@@ -11,7 +11,7 @@ use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\SourceExtractor; use SPC\store\SourceManager;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
abstract class UnixBuilderBase extends BuilderBase abstract class UnixBuilderBase extends BuilderBase
@@ -134,7 +134,7 @@ abstract class UnixBuilderBase extends BuilderBase
$this->emitPatchPoint('before-libs-extract'); $this->emitPatchPoint('before-libs-extract');
// extract sources // extract sources
SourceExtractor::initSource(libs: $sorted_libraries); SourceManager::initSource(libs: $sorted_libraries);
$this->emitPatchPoint('after-libs-extract'); $this->emitPatchPoint('after-libs-extract');

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
trait gettext
{
protected function build(): void
{
$extra = $this->builder->getLib('ncurses') ? ('--with-libncurses-prefix=' . BUILD_ROOT_PATH . ' ') : '';
$extra .= $this->builder->getLib('libxml2') ? ('--with-libxml2-prefix=' . BUILD_ROOT_PATH . ' ') : '';
shell()->cd($this->source_dir)
->exec(
'./configure ' .
'--enable-static ' .
'--disable-shared ' .
'--disable-java ' .
'--disable-c+ ' .
$extra .
'--with-libiconv-prefix=' . BUILD_ROOT_PATH . ' ' .
'--prefix=' . BUILD_ROOT_PATH
)
->exec('make clean')
->exec("make -j{$this->builder->concurrency}")
->exec('make install');
}
}

View File

@@ -21,6 +21,8 @@ trait pkgconfig
'--disable-shared ' . '--disable-shared ' .
'--enable-static ' . '--enable-static ' .
'--with-internal-glib ' . '--with-internal-glib ' .
'--disable-host-tool ' .
'--with-pic ' .
'--prefix=' . BUILD_ROOT_PATH . ' ' . '--prefix=' . BUILD_ROOT_PATH . ' ' .
'--without-sysroot ' . '--without-sysroot ' .
'--without-system-include-path ' . '--without-system-include-path ' .
@@ -29,6 +31,7 @@ trait pkgconfig
) )
->exec('make clean') ->exec('make clean')
->exec("make -j{$this->builder->concurrency}") ->exec("make -j{$this->builder->concurrency}")
->exec('make install'); ->exec('make install-exec');
shell()->exec('strip ' . BUILD_ROOT_PATH . '/bin/pkg-config');
} }
} }

View File

@@ -10,7 +10,7 @@ use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\SourceExtractor; use SPC\store\SourceManager;
use SPC\store\SourcePatcher; use SPC\store\SourcePatcher;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
@@ -72,6 +72,19 @@ class WindowsBuilder extends BuilderBase
$zts = $this->zts ? '--enable-zts=yes ' : '--enable-zts=no '; $zts = $this->zts ? '--enable-zts=yes ' : '--enable-zts=no ';
// with-upx-pack for phpmicro
$makefile = FileSystem::convertPath(SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag.w32');
if ($this->getOption('with-upx-pack', false)) {
if (!file_exists($makefile . '.originfile')) {
copy($makefile, $makefile . '.originfile');
FileSystem::replaceFileStr($makefile, '$(MICRO_SFX):', "_MICRO_UPX = {$this->getOption('upx-exec')} --best $(MICRO_SFX)\n$(MICRO_SFX):");
FileSystem::replaceFileStr($makefile, '@$(_MICRO_MT)', "@$(_MICRO_MT)\n\t@$(_MICRO_UPX)");
}
} elseif (file_exists($makefile . '.originfile')) {
copy($makefile . '.originfile', $makefile);
unlink($makefile . '.originfile');
}
cmd()->cd(SOURCE_PATH . '\php-src') cmd()->cd(SOURCE_PATH . '\php-src')
->exec( ->exec(
"{$this->sdk_prefix} configure.bat --task-args \"" . "{$this->sdk_prefix} configure.bat --task-args \"" .
@@ -211,7 +224,7 @@ class WindowsBuilder extends BuilderBase
} }
// extract sources // extract sources
SourceExtractor::initSource(libs: $sorted_libraries); SourceManager::initSource(libs: $sorted_libraries);
// build all libs // build all libs
foreach ($this->libs as $lib) { foreach ($this->libs as $lib) {
@@ -293,6 +306,12 @@ class WindowsBuilder extends BuilderBase
BUILD_TARGET_MICRO => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\micro.sfx", BUILD_TARGET_MICRO => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\micro.sfx",
default => throw new RuntimeException('Deployment does not accept type ' . $type), default => throw new RuntimeException('Deployment does not accept type ' . $type),
}; };
// with-upx-pack for cli
if ($this->getOption('with-upx-pack', false) && $type === BUILD_TARGET_CLI) {
cmd()->exec($this->getOption('upx-exec') . ' --best ' . escapeshellarg($src));
}
logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file'); logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file');
FileSystem::createDir(BUILD_ROOT_PATH . '\bin'); FileSystem::createDir(BUILD_ROOT_PATH . '\bin');

View File

@@ -8,6 +8,8 @@ use SPC\builder\BuilderBase;
use SPC\builder\LibraryBase; use SPC\builder\LibraryBase;
use SPC\builder\windows\WindowsBuilder; use SPC\builder\windows\WindowsBuilder;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
abstract class WindowsLibraryBase extends LibraryBase abstract class WindowsLibraryBase extends LibraryBase
@@ -22,6 +24,36 @@ abstract class WindowsLibraryBase extends LibraryBase
return $this->builder; return $this->builder;
} }
/**
* @throws RuntimeException
* @throws FileSystemException
* @throws WrongUsageException
*/
public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true): string
{
$libs = [$this];
if ($recursive) {
array_unshift($libs, ...array_values($this->getDependencies(recursive: true)));
}
$sep = match ($style) {
'autoconf' => ' ',
'cmake' => ';',
default => throw new RuntimeException('style only support autoconf and cmake'),
};
$ret = [];
foreach ($libs as $lib) {
$libFiles = [];
foreach ($lib->getStaticLibs() as $name) {
$name = str_replace(' ', '\ ', FileSystem::convertPath(BUILD_LIB_PATH . "/{$name}"));
$name = str_replace('"', '\"', $name);
$libFiles[] = $name;
}
array_unshift($ret, implode($sep, $libFiles));
}
return implode($sep, $ret);
}
/** /**
* Create a nmake wrapper file. * Create a nmake wrapper file.
* *

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\store\FileSystem;
class curl extends WindowsLibraryBase
{
public const NAME = 'curl';
protected function build(): void
{
// reset cmake
FileSystem::resetDir($this->source_dir . '\build');
// start build
cmd()->cd($this->source_dir)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
'-B build ' .
'-A x64 ' .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DBUILD_STATIC_LIBS=ON ' .
'-DBUILD_CURL_EXE=OFF ' .
'-DUSE_ZLIB=ON ' .
'-DCURL_USE_OPENSSL=ON ' .
'-DCURL_USE_LIBLSSH2=ON ' .
'-DUSE_NGHTTP2=ON ' . // php-src with curl needs nghttp2
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
"--build build --config Release --target install -j{$this->builder->concurrency}"
);
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\builder\windows\SystemUtil;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
class libiconv_win extends WindowsLibraryBase
{
public const NAME = 'libiconv-win';
protected function build()
{
$vs_ver_dir = match (SystemUtil::findVisualStudio()['version']) {
'vs17' => '/MSVC17',
'vs16' => '/MSVC16',
default => throw new RuntimeException('Current VS version is not supported yet!'),
};
// start build
cmd()->cd($this->source_dir . $vs_ver_dir)
->execWithWrapper(
$this->builder->makeSimpleWrapper('msbuild'),
'libiconv.sln /t:Rebuild /p:Configuration=Release /p:Platform=x64'
);
FileSystem::createDir(BUILD_LIB_PATH);
FileSystem::createDir(BUILD_INCLUDE_PATH);
copy($this->source_dir . $vs_ver_dir . '\x64\lib\libiconv.lib', BUILD_LIB_PATH . '\libiconv.lib');
copy($this->source_dir . $vs_ver_dir . '\x64\lib\libiconv_a.lib', BUILD_LIB_PATH . '\libiconv_a.lib');
copy($this->source_dir . '\source\include\iconv.h', BUILD_INCLUDE_PATH . '\iconv.h');
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\store\FileSystem;
class libssh2 extends WindowsLibraryBase
{
public const NAME = 'libssh2';
protected function build(): void
{
$zlib = $this->builder->getLib('zlib') ? 'ON' : 'OFF';
// reset cmake
FileSystem::resetDir($this->source_dir . '\build');
// start build
cmd()->cd($this->source_dir)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
'-B build ' .
'-A x64 ' .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DBUILD_STATIC_LIBS=ON ' .
'-DBUILD_TESTING=OFF ' .
"-DENABLE_ZLIB_COMPRESSION={$zlib} " .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
"--build build --config Release --target install -j{$this->builder->concurrency}"
);
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\store\FileSystem;
class libxml2 extends WindowsLibraryBase
{
public const NAME = 'libxml2';
protected function build(): void
{
$zlib = $this->builder->getLib('zlib') ? 'ON' : 'OFF';
// reset cmake
FileSystem::resetDir($this->source_dir . '\build');
// start build
cmd()->cd($this->source_dir)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
'-B build ' .
'-A x64 ' .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DBUILD_STATIC_LIBS=ON ' .
"-DLIBXML2_WITH_ZLIB={$zlib} " .
'-DLIBXML2_WITH_PYTHON=OFF ' .
'-DLIBXML2_WITH_ICONV=ON ' .
'-DIconv_LIBRARY=' . BUILD_LIB_PATH . ' ' .
'-DIconv_INCLUDE_DIR=' . BUILD_INCLUDE_PATH . ' ' .
'-DLIBXML2_WITH_LZMA=OFF ' . // xz not supported yet
'-DLIBXML2_WITH_PROGRAMS=OFF ' .
'-DLIBXML2_WITH_TESTS=OFF ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
"--build build --config Release --target install -j{$this->builder->concurrency}"
);
copy(BUILD_LIB_PATH . '\libxml2s.lib', BUILD_LIB_PATH . '\libxml2_a.lib');
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\store\FileSystem;
class nghttp2 extends WindowsLibraryBase
{
public const NAME = 'nghttp2';
protected function build(): void
{
// reset cmake
FileSystem::resetDir($this->source_dir . '\build');
// start build
cmd()->cd($this->source_dir)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
'-B build ' .
'-A x64 ' .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DBUILD_STATIC_LIBS=ON ' .
'-DENABLE_STATIC_CRT=ON ' .
'-DENABLE_LIB_ONLY=ON ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
"--build build --config Release --target install -j{$this->builder->concurrency}"
);
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\store\FileSystem;
class onig extends WindowsLibraryBase
{
public const NAME = 'onig';
protected function build(): void
{
// reset cmake
FileSystem::resetDir($this->source_dir . '\build');
// start build
cmd()->cd($this->source_dir)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
'-B build ' .
'-A x64 ' .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DBUILD_STATIC_LIBS=ON ' .
'-DMSVC_STATIC_RUNTIME=ON ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
"--build build --config Release --target install -j{$this->builder->concurrency}"
);
copy(BUILD_LIB_PATH . '/onig.lib', BUILD_LIB_PATH . '/onig_a.lib');
}
}

View File

@@ -14,7 +14,8 @@ class openssl extends WindowsLibraryBase
protected function build(): void protected function build(): void
{ {
$perl = file_exists(BUILD_ROOT_PATH . '\perl\perl\bin\perl.exe') ? (BUILD_ROOT_PATH . '\perl\perl\bin\perl.exe') : SystemUtil::findCommand('perl.exe'); $perl_path_native = PKG_ROOT_PATH . '\strawberry-perl-' . arch2gnu(php_uname('m')) . '-win\perl\bin\perl.exe';
$perl = file_exists($perl_path_native) ? ($perl_path_native) : SystemUtil::findCommand('perl.exe');
if ($perl === null) { if ($perl === null) {
throw new RuntimeException('You need to install perl first! (easiest way is using static-php-cli command "doctor")'); throw new RuntimeException('You need to install perl first! (easiest way is using static-php-cli command "doctor")');
} }

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
class sqlite extends WindowsLibraryBase
{
public const NAME = 'sqlite';
public function patchBeforeBuild(): bool
{
copy(ROOT_DIR . '/src/globals/extra/Makefile-sqlite', $this->source_dir . '/Makefile');
return true;
}
protected function build(): void
{
cmd()->cd($this->source_dir)->execWithWrapper($this->builder->makeSimpleWrapper('nmake'), 'PREFIX=' . BUILD_ROOT_PATH . ' install-static');
}
}

View File

@@ -56,11 +56,6 @@ abstract class BaseCommand extends Command
// 如果 return false 则错误会继续递交给 PHP 标准错误处理 // 如果 return false 则错误会继续递交给 PHP 标准错误处理
return true; return true;
}, E_ALL | E_STRICT); }, E_ALL | E_STRICT);
if ($input->getOption('debug')) {
global $ob_logger;
$ob_logger = new ConsoleLogger(LogLevel::DEBUG);
define('DEBUG_MODE', true);
}
$version = ConsoleApplication::VERSION; $version = ConsoleApplication::VERSION;
if (!$this->no_motd) { if (!$this->no_motd) {
echo " _ _ _ _ echo " _ _ _ _
@@ -83,6 +78,14 @@ abstract class BaseCommand extends Command
$this->input = $input; $this->input = $input;
$this->output = $output; $this->output = $output;
global $ob_logger;
if ($input->getOption('debug')) {
$ob_logger = new ConsoleLogger(LogLevel::DEBUG, decorated: !$input->getOption('no-ansi'));
define('DEBUG_MODE', true);
} else {
$ob_logger = new ConsoleLogger(decorated: !$input->getOption('no-ansi'));
}
// windows fallback // windows fallback
Prompt::fallbackWhen(PHP_OS_FAMILY === 'Windows'); Prompt::fallbackWhen(PHP_OS_FAMILY === 'Windows');
ConfirmPrompt::fallbackUsing(function (ConfirmPrompt $prompt) use ($input, $output) { ConfirmPrompt::fallbackUsing(function (ConfirmPrompt $prompt) use ($input, $output) {

View File

@@ -32,11 +32,13 @@ class BuildCliCommand extends BuildCommand
$this->addOption('enable-zts', null, null, 'enable ZTS support'); $this->addOption('enable-zts', null, null, 'enable ZTS support');
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit'); $this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');
$this->addOption('with-hardcoded-ini', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Patch PHP source code, inject hardcoded INI'); $this->addOption('with-hardcoded-ini', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Patch PHP source code, inject hardcoded INI');
$this->addOption('with-micro-fake-cli', null, null, 'Enable phpmicro fake cli'); $this->addOption('with-micro-fake-cli', null, null, 'Let phpmicro\'s PHP_SAPI use "cli" instead of "micro"');
$this->addOption('with-suggested-libs', 'L', null, 'Build with suggested libs for selected exts and libs'); $this->addOption('with-suggested-libs', 'L', null, 'Build with suggested libs for selected exts and libs');
$this->addOption('with-suggested-exts', 'E', null, 'Build with suggested extensions for selected exts'); $this->addOption('with-suggested-exts', 'E', null, 'Build with suggested extensions for selected exts');
$this->addOption('with-added-patch', 'P', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Inject patch script outside'); $this->addOption('with-added-patch', 'P', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Inject patch script outside');
$this->addOption('without-micro-ext-test', null, null, 'Disable phpmicro with extension test code'); $this->addOption('without-micro-ext-test', null, null, 'Disable phpmicro with extension test code');
$this->addOption('with-upx-pack', null, null, 'Compress / pack binary using UPX tool (linux/windows only)');
} }
public function handle(): int public function handle(): int
@@ -58,6 +60,31 @@ class BuildCliCommand extends BuildCommand
$this->output->writeln("<comment>\t--build-all\tBuild all SAPI: cli, micro, fpm, embed</comment>"); $this->output->writeln("<comment>\t--build-all\tBuild all SAPI: cli, micro, fpm, embed</comment>");
return static::FAILURE; return static::FAILURE;
} }
if ($rule === BUILD_TARGET_ALL) {
logger()->warning('--build-all option makes `--no-strip` always true, be aware!');
}
// Check upx
$suffix = PHP_OS_FAMILY === 'Windows' ? '.exe' : '';
if ($this->getOption('with-upx-pack')) {
// only available for linux for now
if (!in_array(PHP_OS_FAMILY, ['Linux', 'Windows'])) {
logger()->error('UPX is only available on Linux and Windows!');
return static::FAILURE;
}
// need to install this manually
if (!file_exists(PKG_ROOT_PATH . '/bin/upx' . $suffix)) {
global $argv;
logger()->error('upx does not exist, please install it first:');
logger()->error('');
logger()->error("\t" . $argv[0] . ' install-pkg upx');
logger()->error('');
return static::FAILURE;
}
// exclusive with no-strip
if ($this->getOption('no-strip')) {
logger()->warning('--with-upx-pack conflicts with --no-strip, --no-strip won\'t work!');
}
}
try { try {
// create builder // create builder
$builder = BuilderProvider::makeBuilderByInput($this->input); $builder = BuilderProvider::makeBuilderByInput($this->input);
@@ -80,6 +107,10 @@ class BuildCliCommand extends BuildCommand
if ($this->input->getOption('disable-opcache-jit')) { if ($this->input->getOption('disable-opcache-jit')) {
$indent_texts['Opcache JIT'] = 'disabled'; $indent_texts['Opcache JIT'] = 'disabled';
} }
if ($this->input->getOption('with-upx-pack') && in_array(PHP_OS_FAMILY, ['Linux', 'Windows'])) {
$indent_texts['UPX Pack'] = 'enabled';
$builder->setOption('upx-exec', FileSystem::convertPath(PKG_ROOT_PATH . '/bin/upx' . $suffix));
}
try { try {
$ver = $builder->getPHPVersion(); $ver = $builder->getPHPVersion();
$indent_texts['PHP Version'] = $ver; $indent_texts['PHP Version'] = $ver;

View File

@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand('del-download', 'Remove locked download source or package using name', ['delete-download', 'del-down'])]
class DeleteDownloadCommand extends BaseCommand
{
public function configure(): void
{
$this->addArgument('sources', InputArgument::REQUIRED, 'The sources/packages will be deleted, comma separated');
$this->addOption('all', 'A', null, 'Delete all downloaded and locked sources/packages');
}
public function initialize(InputInterface $input, OutputInterface $output): void
{
if ($input->getOption('all')) {
$input->setArgument('sources', '');
}
parent::initialize($input, $output);
}
/**
* @throws FileSystemException
*/
public function handle(): int
{
try {
// get source list that will be downloaded
$sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources'))));
if (empty($sources)) {
logger()->notice('Removing downloads/ directory ...');
FileSystem::removeDir(DOWNLOAD_PATH);
logger()->info('Removed downloads/ dir!');
return static::SUCCESS;
}
$chosen_sources = $sources;
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
foreach ($chosen_sources as $source) {
$source = trim($source);
if (!isset($lock[$source])) {
logger()->warning("Source/Package [{$source}] not locked or not downloaded, skipped.");
continue;
}
// remove download file/dir if exists
if ($lock[$source]['source_type'] === 'archive') {
if (file_exists($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock[$source]['filename']))) {
logger()->info('Deleting file ' . $path);
unlink($path);
} else {
logger()->warning("Source/Package [{$source}] file not found, skip deleting file.");
}
} else {
if (is_dir($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock[$source]['dirname']))) {
logger()->info('Deleting dir ' . $path);
FileSystem::removeDir($path);
} else {
logger()->warning("Source/Package [{$source}] directory not found, skip deleting dir.");
}
}
// remove locked sources
unset($lock[$source]);
}
FileSystem::writeFile(DOWNLOAD_PATH . '/.lock.json', json_encode($lock, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
logger()->info('Delete success!');
return static::SUCCESS;
} catch (DownloaderException $e) {
logger()->error($e->getMessage());
return static::FAILURE;
} catch (WrongUsageException $e) {
logger()->critical($e->getMessage());
return static::FAILURE;
}
}
}

View File

@@ -8,11 +8,11 @@ use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\SourceExtractor; use SPC\store\SourceManager;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
#[AsCommand('extract', 'Extract required sources')] #[AsCommand('extract', 'Extract required sources', ['extract-source'])]
class ExtractCommand extends BaseCommand class ExtractCommand extends BaseCommand
{ {
use UnixSystemUtilTrait; use UnixSystemUtilTrait;
@@ -34,7 +34,7 @@ class ExtractCommand extends BaseCommand
$this->output->writeln('<error>sources cannot be empty, at least contain one !</error>'); $this->output->writeln('<error>sources cannot be empty, at least contain one !</error>');
return static::FAILURE; return static::FAILURE;
} }
SourceExtractor::initSource(sources: $sources); SourceManager::initSource(sources: $sources);
logger()->info('Extract done !'); logger()->info('Extract done !');
return static::SUCCESS; return static::SUCCESS;
} }

View File

@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\PackageManager;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
#[AsCommand('install-pkg', 'Install additional packages', ['i', 'install-package'])]
class InstallPkgCommand extends BaseCommand
{
use UnixSystemUtilTrait;
public function configure(): void
{
$this->addArgument('packages', InputArgument::REQUIRED, 'The packages will be installed, comma separated');
$this->addOption('shallow-clone', null, null, 'Clone shallow');
$this->addOption('custom-url', 'U', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Specify custom source download url, e.g "php-src:https://downloads.php.net/~eric/php-8.3.0beta1.tar.gz"');
}
/**
* @throws FileSystemException
*/
public function handle(): int
{
try {
// Use shallow-clone can reduce git resource download
if ($this->getOption('shallow-clone')) {
define('GIT_SHALLOW_CLONE', true);
}
// Process -U options
$custom_urls = [];
foreach ($this->input->getOption('custom-url') as $value) {
[$pkg_name, $url] = explode(':', $value, 2);
$custom_urls[$pkg_name] = $url;
}
$chosen_pkgs = array_map('trim', array_filter(explode(',', $this->getArgument('packages'))));
// Download them
f_mkdir(DOWNLOAD_PATH);
$ni = 0;
$cnt = count($chosen_pkgs);
foreach ($chosen_pkgs as $pkg) {
++$ni;
if (isset($custom_urls[$pkg])) {
$config = Config::getPkg($pkg);
$new_config = [
'type' => 'url',
'url' => $custom_urls[$pkg],
];
if (isset($config['extract'])) {
$new_config['extract'] = $config['extract'];
}
if (isset($config['filename'])) {
$new_config['filename'] = $config['filename'];
}
logger()->info("Installing source {$pkg} from custom url [{$ni}/{$cnt}]");
PackageManager::installPackage($pkg, $new_config);
} else {
logger()->info("Fetching package {$pkg} [{$ni}/{$cnt}]");
PackageManager::installPackage($pkg, Config::getPkg($pkg));
}
}
$time = round(microtime(true) - START_TIME, 3);
logger()->info('Install packages complete, used ' . $time . ' s !');
return static::SUCCESS;
} catch (DownloaderException $e) {
logger()->error($e->getMessage());
return static::FAILURE;
} catch (WrongUsageException $e) {
logger()->critical($e->getMessage());
return static::FAILURE;
}
}
}

View File

@@ -57,6 +57,15 @@ class SortConfigCommand extends BaseCommand
return static::FAILURE; return static::FAILURE;
} }
break; break;
case 'pkg':
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/pkg.json'), true);
ConfigValidator::validatePkgs($file);
ksort($file);
if (!file_put_contents(ROOT_DIR . '/config/pkg.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n")) {
$this->output->writeln('<error>Write file pkg.json failed!</error>');
return static::FAILURE;
}
break;
default: default:
$this->output->writeln("<error>invalid config name: {$name}</error>"); $this->output->writeln("<error>invalid config name: {$name}</error>");
return 1; return 1;

View File

@@ -57,7 +57,7 @@ class BSDToolCheckList
$prefix = ''; $prefix = '';
} }
try { try {
shell(true)->exec("ASSUME_ALWAYS_YES=yes {$prefix} pkg install -y " . implode(' ', $missing)); shell(true)->exec("ASSUME_ALWAYS_YES=yes {$prefix}pkg install -y " . implode(' ', $missing));
} catch (RuntimeException) { } catch (RuntimeException) {
return false; return false;
} }

View File

@@ -14,6 +14,7 @@ use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Downloader; use SPC\store\Downloader;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\PackageManager;
class LinuxMuslCheck class LinuxMuslCheck
{ {
@@ -84,7 +85,6 @@ class LinuxMuslCheck
/** @noinspection PhpUnused */ /** @noinspection PhpUnused */
/** /**
* @throws DownloaderException
* @throws FileSystemException * @throws FileSystemException
* @throws WrongUsageException * @throws WrongUsageException
*/ */
@@ -98,15 +98,10 @@ class LinuxMuslCheck
logger()->warning('Current user is not root, using sudo for running command'); logger()->warning('Current user is not root, using sudo for running command');
} }
$arch = arch2gnu(php_uname('m')); $arch = arch2gnu(php_uname('m'));
$musl_compile_source = [ PackageManager::installPackage("musl-toolchain-{$arch}-linux");
'type' => 'url', $pkg_root = PKG_ROOT_PATH . "/musl-toolchain-{$arch}-linux";
'url' => "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/{$arch}-musl-toolchain.tgz", shell()->exec("{$prefix}cp -rf {$pkg_root}/* /usr/local/musl");
]; FileSystem::removeDir($pkg_root);
logger()->info('Downloading ' . $musl_compile_source['url']);
Downloader::downloadSource('musl-compile', $musl_compile_source);
logger()->info('Extracting musl-cross');
FileSystem::extractSource('musl-compile', DOWNLOAD_PATH . "/{$arch}-musl-toolchain.tgz");
shell()->exec($prefix . 'cp -rf ' . SOURCE_PATH . '/musl-compile/* /usr/local/musl');
return true; return true;
} catch (RuntimeException) { } catch (RuntimeException) {
return false; return false;

View File

@@ -9,8 +9,8 @@ use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem; use SPC\doctor\AsFixItem;
use SPC\doctor\CheckResult; use SPC\doctor\CheckResult;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\store\Downloader;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\PackageManager;
class WindowsToolCheckList class WindowsToolCheckList
{ {
@@ -64,8 +64,9 @@ class WindowsToolCheckList
#[AsCheckItem('if perl(strawberry) installed', limit_os: 'Windows', level: 994)] #[AsCheckItem('if perl(strawberry) installed', limit_os: 'Windows', level: 994)]
public function checkPerl(): ?CheckResult public function checkPerl(): ?CheckResult
{ {
if (file_exists(BUILD_ROOT_PATH . '\perl\perl\bin\perl.exe')) { $arch = arch2gnu(php_uname('m'));
return CheckResult::ok(BUILD_ROOT_PATH . '\perl\perl\bin\perl.exe'); if (file_exists(PKG_ROOT_PATH . '\strawberry-perl-' . $arch . '-win\perl\bin\perl.exe')) {
return CheckResult::ok(PKG_ROOT_PATH . '\strawberry-perl-' . $arch . '-win\perl\bin\perl.exe');
} }
if (($path = SystemUtil::findCommand('perl.exe')) === null) { if (($path = SystemUtil::findCommand('perl.exe')) === null) {
return CheckResult::fail('perl not found in path.', 'install-perl'); return CheckResult::fail('perl not found in path.', 'install-perl');
@@ -91,32 +92,15 @@ class WindowsToolCheckList
#[AsFixItem('install-nasm')] #[AsFixItem('install-nasm')]
public function installNasm(): bool public function installNasm(): bool
{ {
// The hardcoded version here is to be consistent with the version compiled by `musl-cross-toolchain`. PackageManager::installPackage('nasm-x86_64-win');
$nasm_ver = '2.16.01';
$nasm_dist = "nasm-{$nasm_ver}";
$source = [
'type' => 'url',
'url' => "https://www.nasm.us/pub/nasm/releasebuilds/{$nasm_ver}/win64/{$nasm_dist}-win64.zip",
];
logger()->info('Downloading ' . $source['url']);
Downloader::downloadSource('nasm', $source);
FileSystem::extractSource('nasm', DOWNLOAD_PATH . "\\{$nasm_dist}-win64.zip");
copy(SOURCE_PATH . "\\nasm\\{$nasm_dist}\\nasm.exe", PHP_SDK_PATH . '\bin\nasm.exe');
copy(SOURCE_PATH . "\\nasm\\{$nasm_dist}\\ndisasm.exe", PHP_SDK_PATH . '\bin\ndisasm.exe');
return true; return true;
} }
#[AsFixItem('install-perl')] #[AsFixItem('install-perl')]
public function installPerl(): bool public function installPerl(): bool
{ {
$url = 'https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/download/SP_5380_5361/strawberry-perl-5.38.0.1-64bit-portable.zip'; $arch = arch2gnu(php_uname('m'));
$source = [ PackageManager::installPackage("strawberry-perl-{$arch}-win");
'type' => 'url',
'url' => $url,
];
logger()->info("Downloading {$url}");
Downloader::downloadSource('strawberry-perl', $source);
FileSystem::extractSource('strawberry-perl', DOWNLOAD_PATH . '\strawberry-perl-5.38.0.1-64bit-portable.zip', '../buildroot/perl');
return true; return true;
} }
} }

View File

@@ -12,6 +12,8 @@ use SPC\exception\WrongUsageException;
*/ */
class Config class Config
{ {
public static ?array $pkg = null;
public static ?array $source = null; public static ?array $source = null;
public static ?array $lib = null; public static ?array $lib = null;
@@ -31,6 +33,19 @@ class Config
return self::$source[$name] ?? null; return self::$source[$name] ?? null;
} }
/**
* Read pkg from pkg.json
*
* @throws FileSystemException
*/
public static function getPkg(string $name): ?array
{
if (self::$pkg === null) {
self::$pkg = FileSystem::loadConfigArray('pkg');
}
return self::$pkg[$name] ?? null;
}
/** /**
* 根据不同的操作系统分别选择不同的 lib 库依赖项 * 根据不同的操作系统分别选择不同的 lib 库依赖项
* 如果 key 为 null那么直接返回整个 meta。 * 如果 key 为 null那么直接返回整个 meta。

View File

@@ -246,6 +246,92 @@ class Downloader
}*/ }*/
} }
public static function downloadPackage(string $name, ?array $pkg = null, bool $force = false): void
{
if ($pkg === null) {
$pkg = Config::getPkg($name);
}
if ($pkg === null) {
logger()->warning('Package {name} unknown. Skipping.', ['name' => $name]);
return;
}
if (!is_dir(DOWNLOAD_PATH)) {
FileSystem::createDir(DOWNLOAD_PATH);
}
// load lock file
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
$lock = [];
} else {
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
}
// If lock file exists, skip downloading
if (isset($lock[$name]) && !$force) {
if ($lock[$name]['source_type'] === 'archive' && file_exists(DOWNLOAD_PATH . '/' . $lock[$name]['filename'])) {
logger()->notice("Package [{$name}] already downloaded: " . $lock[$name]['filename']);
return;
}
if ($lock[$name]['source_type'] === 'dir' && is_dir(DOWNLOAD_PATH . '/' . $lock[$name]['dirname'])) {
logger()->notice("Package [{$name}] already downloaded: " . $lock[$name]['dirname']);
return;
}
}
try {
switch ($pkg['type']) {
case 'bitbuckettag': // BitBucket Tag
[$url, $filename] = self::getLatestBitbucketTag($name, $pkg);
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null);
break;
case 'ghtar': // GitHub Release (tar)
[$url, $filename] = self::getLatestGithubTarball($name, $pkg);
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null);
break;
case 'ghtagtar': // GitHub Tag (tar)
[$url, $filename] = self::getLatestGithubTarball($name, $pkg, 'tags');
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null);
break;
case 'ghrel': // GitHub Release (uploaded)
[$url, $filename] = self::getLatestGithubRelease($name, $pkg);
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null);
break;
case 'filelist': // Basic File List (regex based crawler)
[$url, $filename] = self::getFromFileList($name, $pkg);
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null);
break;
case 'url': // Direct download URL
$url = $pkg['url'];
$filename = $pkg['filename'] ?? basename($pkg['url']);
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null);
break;
case 'git': // Git repo
self::downloadGit($name, $pkg['url'], $pkg['rev'], $pkg['extract'] ?? null);
break;
case 'custom': // Custom download method, like API-based download or other
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/source', 'SPC\\store\\source');
foreach ($classes as $class) {
if (is_a($class, CustomSourceBase::class, true) && $class::NAME === $name) {
(new $class())->fetch();
break;
}
}
break;
default:
throw new DownloaderException('unknown source type: ' . $pkg['type']);
}
} catch (RuntimeException $e) {
// Because sometimes files downloaded through the command line are not automatically deleted after a failure.
// Here we need to manually delete the file if it is detected to exist.
if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) {
logger()->warning('Deleting download file: ' . $filename);
unlink(DOWNLOAD_PATH . '/' . $filename);
}
throw new DownloaderException('Download failed! ' . $e->getMessage());
}
}
/** /**
* Download source by name and meta. * Download source by name and meta.
* *

View File

@@ -16,7 +16,7 @@ class FileSystem
*/ */
public static function loadConfigArray(string $config, ?string $config_dir = null): array public static function loadConfigArray(string $config, ?string $config_dir = null): array
{ {
$whitelist = ['ext', 'lib', 'source']; $whitelist = ['ext', 'lib', 'source', 'pkg'];
if (!in_array($config, $whitelist)) { if (!in_array($config, $whitelist)) {
throw new FileSystemException('Reading ' . $config . '.json is not allowed'); throw new FileSystemException('Reading ' . $config . '.json is not allowed');
} }
@@ -138,6 +138,37 @@ class FileSystem
} }
} }
/**
* @throws RuntimeException
* @throws FileSystemException
*/
public static function extractPackage(string $name, string $filename, ?string $extract_path = null): void
{
if ($extract_path !== null) {
// replace
$extract_path = self::replacePathVariable($extract_path);
$extract_path = self::isRelativePath($extract_path) ? (WORKING_DIR . '/' . $extract_path) : $extract_path;
} else {
$extract_path = PKG_ROOT_PATH . '/' . $name;
}
logger()->info("extracting {$name} package to {$extract_path} ...");
$target = self::convertPath($extract_path);
if (!is_dir($dir = dirname($target))) {
self::createDir($dir);
}
try {
self::extractArchive($filename, $target);
} catch (RuntimeException $e) {
if (PHP_OS_FAMILY === 'Windows') {
f_passthru('rmdir /s /q ' . $target);
} else {
f_passthru('rm -rf ' . $target);
}
throw new FileSystemException('Cannot extract package ' . $name, $e->getCode(), $e);
}
}
/** /**
* 解压缩下载的资源包到 source 目录 * 解压缩下载的资源包到 source 目录
* *
@@ -152,52 +183,26 @@ class FileSystem
if (self::$_extract_hook === []) { if (self::$_extract_hook === []) {
SourcePatcher::init(); SourcePatcher::init();
} }
if (!is_dir(SOURCE_PATH)) {
self::createDir(SOURCE_PATH);
}
if ($move_path !== null) { if ($move_path !== null) {
$move_path = SOURCE_PATH . '/' . $move_path; $move_path = SOURCE_PATH . '/' . $move_path;
} else {
$move_path = SOURCE_PATH . "/{$name}";
}
$target = self::convertPath($move_path);
logger()->info("extracting {$name} source to {$target}" . ' ...');
if (!is_dir($dir = dirname($target))) {
self::createDir($dir);
} }
logger()->info("extracting {$name} source to " . ($move_path ?? SOURCE_PATH . "/{$name}") . ' ...');
try { try {
$target = self::convertPath($move_path ?? (SOURCE_PATH . "/{$name}")); self::extractArchive($filename, $target);
// Git source, just move
if (is_dir(self::convertPath($filename))) {
self::copyDir(self::convertPath($filename), $target);
self::emitSourceExtractHook($name);
return;
}
if (f_mkdir(directory: $target, recursive: true) !== true) {
throw new FileSystemException('create ' . $name . 'source dir failed');
}
if (in_array(PHP_OS_FAMILY, ['Darwin', 'Linux', 'BSD'])) {
match (self::extname($filename)) {
'tar', 'xz', 'txz' => f_passthru("tar -xf {$filename} -C {$target} --strip-components 1"),
'tgz', 'gz' => f_passthru("tar -xzf {$filename} -C {$target} --strip-components 1"),
'bz2' => f_passthru("tar -xjf {$filename} -C {$target} --strip-components 1"),
'zip' => f_passthru("unzip {$filename} -d {$target}"),
default => throw new FileSystemException('unknown archive format: ' . $filename),
};
} elseif (PHP_OS_FAMILY === 'Windows') {
// use php-sdk-binary-tools/bin/7za.exe
$_7z = self::convertPath(PHP_SDK_PATH . '/bin/7za.exe');
f_mkdir(SOURCE_PATH . "/{$name}", recursive: true);
match (self::extname($filename)) {
'tar' => f_passthru("tar -xf {$filename} -C {$target} --strip-components 1"),
'xz', 'txz', 'gz', 'tgz', 'bz2' => f_passthru("\"{$_7z}\" x -so {$filename} | tar -f - -x -C {$target} --strip-components 1"),
'zip' => f_passthru("\"{$_7z}\" x {$filename} -o{$target} -y"),
default => throw new FileSystemException("unknown archive format: {$filename}"),
};
}
self::emitSourceExtractHook($name); self::emitSourceExtractHook($name);
} catch (RuntimeException $e) { } catch (RuntimeException $e) {
if (PHP_OS_FAMILY === 'Windows') { if (PHP_OS_FAMILY === 'Windows') {
f_passthru('rmdir /s /q ' . SOURCE_PATH . "/{$name}"); f_passthru('rmdir /s /q ' . $target);
} else { } else {
f_passthru('rm -r ' . SOURCE_PATH . "/{$name}"); f_passthru('rm -rf ' . $target);
} }
throw new FileSystemException('Cannot extract source ' . $name, $e->getCode(), $e); throw new FileSystemException('Cannot extract source ' . $name . ': ' . $e->getMessage(), $e->getCode(), $e);
} }
} }
@@ -214,6 +219,14 @@ class FileSystem
return str_replace('/', DIRECTORY_SEPARATOR, $path); return str_replace('/', DIRECTORY_SEPARATOR, $path);
} }
public static function convertWinPathToMinGW(string $path): string
{
if (preg_match('/^[A-Za-z]:/', $path)) {
$path = '/' . strtolower(substr($path, 0, 1)) . '/' . str_replace('\\', '/', substr($path, 2));
}
return $path;
}
/** /**
* 递归或非递归扫描目录,可返回相对目录的文件列表或绝对目录的文件列表 * 递归或非递归扫描目录,可返回相对目录的文件列表或绝对目录的文件列表
* *
@@ -411,6 +424,63 @@ class FileSystem
return strlen($path) > 0 && $path[0] !== '/'; return strlen($path) > 0 && $path[0] !== '/';
} }
public static function replacePathVariable(string $path): string
{
$replacement = [
'{pkg_root_path}' => PKG_ROOT_PATH,
'{php_sdk_path}' => defined('PHP_SDK_PATH') ? PHP_SDK_PATH : WORKING_DIR . '/php-sdk-binary-tools',
'{working_dir}' => WORKING_DIR,
'{download_path}' => DOWNLOAD_PATH,
'{source_path}' => SOURCE_PATH,
];
return str_replace(array_keys($replacement), array_values($replacement), $path);
}
/**
* @throws RuntimeException
* @throws FileSystemException
*/
private static function extractArchive(string $filename, string $target): void
{
// Git source, just move
if (is_dir(self::convertPath($filename))) {
self::copyDir(self::convertPath($filename), $target);
return;
}
// Create base dir
if (f_mkdir(directory: $target, recursive: true) !== true) {
throw new FileSystemException('create ' . $target . ' dir failed');
}
if (!file_exists($filename)) {
throw new FileSystemException('File not exists');
}
if (in_array(PHP_OS_FAMILY, ['Darwin', 'Linux', 'BSD'])) {
match (self::extname($filename)) {
'tar', 'xz', 'txz' => f_passthru("tar -xf {$filename} -C {$target} --strip-components 1"),
'tgz', 'gz' => f_passthru("tar -xzf {$filename} -C {$target} --strip-components 1"),
'bz2' => f_passthru("tar -xjf {$filename} -C {$target} --strip-components 1"),
'zip' => f_passthru("unzip {$filename} -d {$target}"),
default => throw new FileSystemException('unknown archive format: ' . $filename),
};
} elseif (PHP_OS_FAMILY === 'Windows') {
// use php-sdk-binary-tools/bin/7za.exe
$_7z = self::convertPath(PHP_SDK_PATH . '/bin/7za.exe');
// Windows notes: I hate windows tar.......
// When extracting .tar.gz like libxml2, it shows a symlink error and returns code[1].
// Related posts: https://answers.microsoft.com/en-us/windows/forum/all/tar-on-windows-fails-to-extract-archive-containing/0ee9a7ea-9b1f-4fef-86a9-5d9dc35cea2f
// And MinGW tar.exe cannot work on temporarily storage ??? (GitHub Actions hosted runner)
// Yeah, I will be an MS HATER !
match (self::extname($filename)) {
'tar' => f_passthru("tar -xf {$filename} -C {$target} --strip-components 1"),
'xz', 'txz', 'gz', 'tgz', 'bz2' => cmd()->execWithResult("\"{$_7z}\" x -so {$filename} | tar -f - -x -C \"{$target}\" --strip-components 1"),
'zip' => f_passthru("\"{$_7z}\" x {$filename} -o{$target} -y"),
default => throw new FileSystemException("unknown archive format: {$filename}"),
};
}
}
/** /**
* @throws FileSystemException * @throws FileSystemException
*/ */

View File

@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace SPC\store;
use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException;
class PackageManager
{
public static function installPackage(string $pkg_name, ?array $config = null, bool $force = false): void
{
if ($config === null) {
$config = Config::getPkg($pkg_name);
}
if ($config === null) {
$arch = arch2gnu(php_uname('m'));
$os = match (PHP_OS_FAMILY) {
'Linux' => 'linux',
'Windows' => 'win',
'BSD' => 'freebsd',
'Darwin' => 'macos',
default => throw new WrongUsageException('Unsupported OS!'),
};
$config = Config::getPkg("{$pkg_name}-{$arch}-{$os}");
}
if ($config === null) {
throw new WrongUsageException("Package [{$pkg_name}] does not exist, please check the name and correct it !");
}
// Download package
Downloader::downloadPackage($pkg_name, $config, $force);
// After download, read lock file name
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true);
$filename = DOWNLOAD_PATH . '/' . ($lock[$pkg_name]['filename'] ?? $lock[$pkg_name]['dirname']);
$extract = $lock[$pkg_name]['move_path'] === null ? (PKG_ROOT_PATH . '/' . $pkg_name) : $lock[$pkg_name]['move_path'];
FileSystem::extractPackage($pkg_name, $filename, $extract);
// if contains extract-files, we just move this file to destination, and remove extract dir
if (is_array($config['extract-files'] ?? null) && is_assoc_array($config['extract-files'])) {
$scandir = FileSystem::scanDirFiles($extract, true, true);
foreach ($config['extract-files'] as $file => $target) {
$target = FileSystem::convertPath(FileSystem::replacePathVariable($target));
if (!is_dir($dir = dirname($target))) {
f_mkdir($dir, 0755, true);
}
logger()->debug("Moving package [{$pkg_name}] file {$file} to {$target}");
// match pattern, needs to scan dir
$file = FileSystem::convertPath($file);
$found = false;
foreach ($scandir as $item) {
if (match_pattern($file, $item)) {
$file = $item;
$found = true;
break;
}
}
if ($found === false) {
throw new FileSystemException('Unable to find extract-files item: ' . $file);
}
rename(FileSystem::convertPath($extract . '/' . $file), $target);
}
FileSystem::removeDir($extract);
}
}
}

View File

@@ -8,7 +8,7 @@ use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
class SourceExtractor class SourceManager
{ {
/** /**
* @throws WrongUsageException * @throws WrongUsageException

View File

@@ -70,4 +70,12 @@ class ConfigValidator
{ {
is_array($data) || throw new ValidationException('ext.json is broken'); is_array($data) || throw new ValidationException('ext.json is broken');
} }
/**
* @throws ValidationException
*/
public static function validatePkgs(mixed $data): void
{
is_array($data) || throw new ValidationException('pkg.json is broken');
}
} }

View File

@@ -14,6 +14,7 @@ define('START_TIME', microtime(true));
define('BUILD_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_ROOT_PATH')) ? $a : (WORKING_DIR . '/buildroot'))); define('BUILD_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_ROOT_PATH')) ? $a : (WORKING_DIR . '/buildroot')));
define('SOURCE_PATH', FileSystem::convertPath(is_string($a = getenv('SOURCE_PATH')) ? $a : (WORKING_DIR . '/source'))); define('SOURCE_PATH', FileSystem::convertPath(is_string($a = getenv('SOURCE_PATH')) ? $a : (WORKING_DIR . '/source')));
define('DOWNLOAD_PATH', FileSystem::convertPath(is_string($a = getenv('DOWNLOAD_PATH')) ? $a : (WORKING_DIR . '/downloads'))); define('DOWNLOAD_PATH', FileSystem::convertPath(is_string($a = getenv('DOWNLOAD_PATH')) ? $a : (WORKING_DIR . '/downloads')));
define('PKG_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('PKG_ROOT_PATH')) ? $a : (WORKING_DIR . '/pkgroot')));
define('BUILD_BIN_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_BIN_PATH')) ? $a : (BUILD_ROOT_PATH . '/bin'))); define('BUILD_BIN_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_BIN_PATH')) ? $a : (BUILD_ROOT_PATH . '/bin')));
define('BUILD_LIB_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_LIB_PATH')) ? $a : (BUILD_ROOT_PATH . '/lib'))); define('BUILD_LIB_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_LIB_PATH')) ? $a : (BUILD_ROOT_PATH . '/lib')));
define('BUILD_INCLUDE_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_INCLUDE_PATH')) ? $a : (BUILD_ROOT_PATH . '/include'))); define('BUILD_INCLUDE_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_INCLUDE_PATH')) ? $a : (BUILD_ROOT_PATH . '/include')));

View File

@@ -0,0 +1,69 @@
CC=cl.exe /nologo
AR=lib.exe /nologo
LINK=link.exe /nologo
!IF "" == "$(MACHINE)"
MACHINE=x64
!ENDIF
!IF "" == "$(CRT)"
CRT=vc15
!ENDIF
!IF "" == "$(PREFIX)"
PREFIX="$(CRT)-$(MACHINE)"
!ENDIF
COMMON_CFLAGS=/D SQLITE_THREADSAFE=1 /DSQLITE_ENABLE_FTS3=1 /D SQLITE_ENABLE_FTS4=1 /D SQLITE_ENABLE_FTS5=1 /D SQLITE_ENABLE_JSON1=1 /D SQLITE_ENABLE_COLUMN_METADATA=1 /D SQLITE_CORE=1
!IF "$(DEBUG)"=="1"
SQLITE3_STATIC_BASE=libsqlite3_a_debug
SQLITE3_DLL_BASE=libsqlite3_debug
SQLITE3_EXE_BASE=sqlite3
CFLAGS=$(COMMON_CFLAGS) /Zi /MDd /Od /W3
LDFLAGS=/DEBUG /GUARD:CF /INCREMENTAL:NO
!ELSE
SQLITE3_STATIC_BASE=libsqlite3_a
SQLITE3_DLL_BASE=libsqlite3
SQLITE3_EXE_BASE=sqlite3
CFLAGS=$(COMMON_CFLAGS) /Zi /MT /guard:cf /Zc:inline /Qspectre /Ox /W3 /GF /GL /Gw
LDFLAGS=/GUARD:CF /INCREMENTAL:NO /NXCOMPAT /DYNAMICBASE
!ENDIF
all: $(SQLITE3_STATIC_BASE).lib $(SQLITE3_EXE_BASE).exe $(SQLITE3_DLL_BASE).dll
install: all
if not exist $(PREFIX)\bin mkdir $(PREFIX)\bin
if not exist $(PREFIX)\include mkdir $(PREFIX)\include
if not exist $(PREFIX)\lib mkdir $(PREFIX)\lib
copy /Y sqlite3.h $(PREFIX)\include
copy /Y sqlite3ext.h $(PREFIX)\include
copy /Y $(SQLITE3_STATIC_BASE).lib $(PREFIX)\lib
copy /Y $(SQLITE3_STATIC_BASE).pdb $(PREFIX)\lib
copy /Y $(SQLITE3_DLL_BASE).lib $(PREFIX)\lib
copy /Y $(SQLITE3_DLL_BASE).pdb $(PREFIX)\bin
copy /Y $(SQLITE3_DLL_BASE).dll $(PREFIX)\bin
copy /Y $(SQLITE3_EXE_BASE).exe $(PREFIX)\bin
copy /Y $(SQLITE3_EXE_BASE).pdb $(PREFIX)\bin
install-static: $(SQLITE3_STATIC_BASE).lib
if not exist $(PREFIX)\include mkdir $(PREFIX)\include
if not exist $(PREFIX)\lib mkdir $(PREFIX)\lib
copy /Y sqlite3.h $(PREFIX)\include
copy /Y sqlite3ext.h $(PREFIX)\include
copy /Y $(SQLITE3_STATIC_BASE).lib $(PREFIX)\lib
clean:
del *.obj *.lib *.exe *.pdb *.dll *.exp
$(SQLITE3_STATIC_BASE).lib: sqlite3.c sqlite3.h
$(CC) $(CFLAGS) /Fd$(SQLITE3_STATIC_BASE).pdb /c sqlite3.c
$(AR) sqlite3.obj /OUT:$(SQLITE3_STATIC_BASE).lib
$(SQLITE3_EXE_BASE).exe: shell.c sqlite3.c sqlite3.h
$(CC) $(CFLAGS) shell.c sqlite3.c /Fd$(SQLITE3_EXE_BASE).pdb /Fe$(SQLITE3_EXE_BASE).exe
$(SQLITE3_DLL_BASE).dll: sqlite3.c sqlite3.h
$(CC) $(CFLAGS) /DSQLITE_API=__declspec(dllexport) /Fd$(SQLITE3_DLL_BASE).pdb /c sqlite3.c
$(LINK) /DLL /OUT:$(SQLITE3_DLL_BASE).dll sqlite3.obj

View File

@@ -47,6 +47,13 @@ function arch2gnu(string $arch): string
}; };
} }
function match_pattern(string $pattern, string $subject): bool
{
$pattern = str_replace(['\*', '\\\\.*'], ['.*', '\*'], preg_quote($pattern, '/'));
$pattern = '/^' . $pattern . '$/i';
return preg_match($pattern, $subject) === 1;
}
function quote(string $str, string $quote = '"'): string function quote(string $str, string $quote = '"'): string
{ {
return $quote . $str . $quote; return $quote . $str . $quote;

View File

@@ -13,8 +13,9 @@ declare(strict_types=1);
// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`). // If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
$extensions = match (PHP_OS_FAMILY) { $extensions = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => '', 'Linux', 'Darwin' => 'event,gettext',
'Windows' => 'mbstring', 'Linux', 'Darwin' => 'event,gettext',
'Windows' => 'mbstring,pdo_sqlite,mbregex',
}; };
// If you want to test lib-suggests feature with extension, add them below (comma separated, example `libwebp,libavif`). // If you want to test lib-suggests feature with extension, add them below (comma separated, example `libwebp,libavif`).

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
assert(function_exists('gettext'));
assert(function_exists('bindtextdomain'));
assert(function_exists('textdomain'));
assert(function_exists('bind_textdomain_codeset'));
if (!is_dir('locale/en_US/LC_MESSAGES/')) {
mkdir('locale/en_US/LC_MESSAGES/', 0755, true);
}
if (!file_exists('locale/en_US/LC_MESSAGES/test.mo')) {
$mo = '3hIElQAAAAACAAAAHAAAACwAAAAFAAAAPAAAAAAAAABQAAAABgAAAFEAAAAXAQAAWAAAAAcAAABwAQAAAQAAAAAAAAAAAAAAAgAAAAAAAAAA56S65L6LAFByb2plY3QtSWQtVmVyc2lvbjogUEFDS0FHRSBWRVJTSU9OClJlcG9ydC1Nc2dpZC1CdWdzLVRvOiAKUE8tUmV2aXNpb24tRGF0ZTogWUVBUi1NTy1EQSBITzpNSStaT05FCkxhc3QtVHJhbnNsYXRvcjogRlVMTCBOQU1FIDxFTUFJTEBBRERSRVNTPgpMYW5ndWFnZS1UZWFtOiBMQU5HVUFHRSA8TExAbGkub3JnPgpMYW5ndWFnZTogCk1JTUUtVmVyc2lvbjogMS4wCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD1VVEYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA4Yml0CgBFeGFtcGxlAA==';
file_put_contents('locale/en_US/LC_MESSAGES/test.mo', base64_decode($mo));
}
putenv('LANG=en_US');
setlocale(LC_ALL, 'en_US');
$domain = 'test';
bindtextdomain($domain, 'locale/');
bind_textdomain_codeset($domain, 'UTF-8');
textdomain($domain);
assert(gettext(json_decode('"\u793a\u4f8b"', true)) === 'Example');