Compare commits

..

66 Commits
2.3.2 ... 2.3.6

Author SHA1 Message Date
Kévin Dunglas
0fb7784a20 feat: better default for the php.ini path (#553)
* update

* update

* fix

* Adjust docs

* Adjust docs

* Add config-file-scan-dir for WindowsBuilder

* Bump version

---------

Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2024-10-18 20:46:00 +08:00
Jerry Ma
ef18264de3 Update FUNDING.yml 2024-10-17 22:03:43 +08:00
Jerry Ma
e752874f22 Update FUNDING.yml 2024-10-17 21:56:56 +08:00
Andrea Esu
ec584bf704 Add option to manually specify config file path (#511)
* Add Config File Path option

* Fix Manual

* quotation marks changes

* php-cs-fixer

* Remove windows config file path option

* Add macOS config file path option

* Add chinese docs

---------

Co-authored-by: andrea.esu <KjsnM@@1_-2!>
Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2024-10-12 16:41:45 +08:00
crazywhalecc
ca8ec70c40 Update docs vue components 2024-10-05 15:34:10 +08:00
crazywhalecc
3c802119ed cs-fix and fix tests 2024-10-05 14:14:36 +08:00
crazywhalecc
58095a61c8 Update redis with latest release 2024-10-05 14:14:36 +08:00
crazywhalecc
774919e03c Add missing filelist 2024-10-05 14:14:36 +08:00
crazywhalecc
566f6980ca cs-fix 2024-10-05 14:14:36 +08:00
crazywhalecc
648c43cc7f Add allowed commands 2024-10-05 14:14:36 +08:00
crazywhalecc
357dfc53c9 Add full Downloader tests 2024-10-05 14:14:36 +08:00
crazywhalecc
948b55026c Fix getPHPVersionID test 2024-10-05 14:14:36 +08:00
crazywhalecc
d6bea6295e Add sanity check for pack:libs 2024-10-05 14:14:36 +08:00
crazywhalecc
83ab430b9a Adjust windows build CI 2024-10-05 14:14:36 +08:00
crazywhalecc
c09bf1e878 Adjust caches 2024-10-05 14:14:36 +08:00
crazywhalecc
ecb17cc4c9 Final tests 2024-10-05 14:14:36 +08:00
crazywhalecc
7d56822e91 Final tests 2024-10-05 14:14:36 +08:00
crazywhalecc
850e6afbd0 Eval 2024-10-05 14:14:36 +08:00
crazywhalecc
09c36844e6 Eval 2024-10-05 14:14:36 +08:00
crazywhalecc
bc2fe576a7 Fix tests CI quotes 2024-10-05 14:14:36 +08:00
crazywhalecc
7facbc7a08 Fix tests CI 2024-10-05 14:14:36 +08:00
crazywhalecc
85df4731d1 Fix tests CI 2024-10-05 14:14:36 +08:00
crazywhalecc
d93c8fcb45 Add full tests 2024-10-05 14:14:36 +08:00
crazywhalecc
67a31ef4fa Add full tests 2024-10-05 14:14:36 +08:00
crazywhalecc
5349ebe73f Fix linux concurrency build 2024-10-05 14:14:36 +08:00
crazywhalecc
f067a510b7 Fix composer phpunit version problem 2024-10-05 14:14:36 +08:00
crazywhalecc
54cf6fe692 Fix curl hook test 2024-10-05 14:14:36 +08:00
crazywhalecc
3bdeafa6b6 Fix curl hook test 2024-10-05 14:14:36 +08:00
crazywhalecc
3f55d0cec3 Update composer.lock 2024-10-05 14:14:36 +08:00
crazywhalecc
ee8d9eeee9 Update env-vars docs 2024-10-05 14:14:36 +08:00
crazywhalecc
732fa06abb Add new tests, remove redundant code 2024-10-05 14:14:36 +08:00
crazywhalecc
c800e3b93a Remove proveExts 2024-10-05 14:14:36 +08:00
crazywhalecc
0568d4b4c8 Ignore env that already set in shell scripts 2024-10-05 14:14:36 +08:00
crazywhalecc
7325368a4f Add ignore musl for phpunit test 2024-10-05 14:14:36 +08:00
crazywhalecc
dc9d6703bc Fix phpstan, add more phpunit test 2024-10-05 14:14:36 +08:00
crazywhalecc
c841ef5b9a Fix missing constant for PHP_SDK_PATH 2024-10-05 14:14:36 +08:00
crazywhalecc
1ce3c1bc47 Use CPU_COUNT instead of SPC_CONCURRENCY 2024-10-05 14:14:36 +08:00
crazywhalecc
25850ef8eb Fix LD_LIBRARY_PATH 2024-10-05 14:14:36 +08:00
crazywhalecc
8f80548739 Fix LD_LIBRARY_PATH 2024-10-05 14:14:36 +08:00
crazywhalecc
4d551f3994 Add SPC_NO_MUSL_PATH env var 2024-10-05 14:14:36 +08:00
crazywhalecc
cfda286532 Add pre-built test 2024-10-05 14:14:36 +08:00
crazywhalecc
24aac06051 Overwrite compile variables for musl wrapper build 2024-10-05 14:14:36 +08:00
crazywhalecc
6af0a85dce Add debug messages for doctor 2024-10-05 14:14:36 +08:00
crazywhalecc
4e88dba630 Separate env to env.ini file 2024-10-05 14:14:36 +08:00
Jerry Ma
2f320507ae Fix windows xz build libs (#550) 2024-10-04 22:09:06 +08:00
Jerry Ma
b62963489a Add optional workflow (#548)
* Add optional workflow

* Add optional workflow
2024-09-29 15:24:33 +08:00
Gabe
5383cf7c25 Fix typo in README.md (#546) 2024-09-27 19:00:54 +08:00
Jerry Ma
29efc2c5a5 Add extension gmssl support (#544)
* Add extension gmssl support

* cs-fix

* Add framework for gmssl
2024-09-20 12:32:31 +08:00
Jerry Ma
e35836943e Update FUNDING.yml 2024-09-19 21:27:24 +08:00
Jerry Ma
2beecee219 Add extension msgpack support (#543) 2024-09-17 22:34:57 +08:00
Jerry Ma
ad098d085e Update redis to 6.0.2, add alternative license file searcher (#539)
* Update redis to 6.0.2, add alternative license file searcher

* Update docs about source module
2024-09-09 17:41:29 +08:00
Jerry Ma
c55ccf242b Use static-php/phpmicro temporarily (#536) 2024-09-06 13:38:54 +08:00
Jerry Ma
b45081dd9c Add --custom-git (-G) option for download command (#534)
* Add --custom-git (-G) option for download command

* Update manual-build.md

* Update manual-build.md

* Update DownloadCommand.php
2024-09-05 00:00:58 +08:00
Jerry Ma
326d682e44 Make opcache patch independent (#533)
* Make opcache patch independent

* Restore tests.yml

* Workaround for older php
2024-09-05 00:00:36 +08:00
crazywhalecc
b354e017ba Add switch-php-version specific patch version support 2024-09-04 16:41:53 +08:00
crazywhalecc
3ce302d0e6 Use old version 2024-09-04 16:35:39 +08:00
crazywhalecc
a38f9e6344 Update docs 2024-09-04 16:35:39 +08:00
crazywhalecc
4e08295cea Add patch version support for downloading php-src 2024-09-04 16:35:39 +08:00
crazywhalecc
73654e5984 Add dependency map generator and related docs 2024-08-21 11:53:26 +08:00
crazywhalecc
077da6f6a4 Correct snappy ext-suggest to ext-suggests 2024-08-21 11:53:26 +08:00
crazywhalecc
0665af2537 Add SPC_MICRO_PATCHES env var 2024-08-20 12:23:12 +08:00
crazywhalecc
d5832c22e3 Change to deploy OSS 2024-08-18 15:50:08 +08:00
crazywhalecc
d231364c1a Add tests 2024-08-18 15:50:08 +08:00
crazywhalecc
fdb5c6aa1d Fix alpine edge build with PHP 7.4 and 8.0 2024-08-18 15:50:08 +08:00
crazywhalecc
47ba881870 Fix alpine edge build imap library bug 2024-08-18 15:50:08 +08:00
crazywhalecc
9fd0508417 Fix swoole-hook-sqlite with PHP 8.3 on macOS arm64 bug 2024-08-18 15:50:08 +08:00
92 changed files with 2813 additions and 796 deletions

4
.github/FUNDING.yml vendored
View File

@@ -3,7 +3,7 @@
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
ko_fi: crazywhalecc # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
@@ -11,5 +11,5 @@ issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
# noinspection YAMLSchemaValidation
buy_me_a_coffee: crazywhalecc
buy_me_a_coffee: # crazywhalecc
custom: 'https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md' # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -115,17 +115,16 @@ jobs:
with:
files: dist/${{ matrix.operating-system.filename }}
- name: "Deploy to Self-Hosted Server"
- name: "Deploy to self-hosted OSS"
if: github.repository == 'crazywhalecc/static-php-cli'
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_SERVER_SECRET_KEY }}
ARGS: "-rltgoDzvO"
SOURCE: "dist/"
REMOTE_HOST: ${{ secrets.DEPLOY_SERVER_HOST }}
REMOTE_PORT: ${{ secrets.DEPLOY_SERVER_PORT }}
REMOTE_USER: ${{ secrets.DEPLOY_SERVER_USER }}
TARGET: ${{ secrets.DEPLOY_SERVER_TARGET_SPC_NIGHTLY }}
uses: static-php/upload-s3-action@v1.0.0
with:
aws_key_id: ${{ secrets.AWS_KEY_ID }}
aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws_bucket: ${{ secrets.AWS_BUCKET }}
source_dir: "dist/"
destination_dir: static-php-cli/spc-bin/nightly/
endpoint: ${{ secrets.AWS_ENDPOINT }}
- name: "Upload Artifact"
uses: actions/upload-artifact@v4

View File

@@ -109,24 +109,41 @@ jobs:
- name: "Run PHPUnit Tests"
run: |
vendor/bin/phpunit tests/ --no-coverage
SPC_NO_MUSL_PATH=yes vendor/bin/phpunit tests/ --no-coverage
define-matrix:
name: "Define Matrix"
runs-on: ubuntu-latest
outputs:
php: ${{ steps.gendef.outputs.php }}
os: ${{ steps.gendef.outputs.os }}
steps:
- name: "Checkout"
uses: actions/checkout@v4
- name: "Setup PHP"
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
- name: Define
id: gendef
run: |
PHP_VERSIONS=$(php src/globals/test-extensions.php php)
OS_VERSIONS=$(php src/globals/test-extensions.php os)
echo 'php='"$PHP_VERSIONS" >> "$GITHUB_OUTPUT"
echo 'os='"$OS_VERSIONS" >> "$GITHUB_OUTPUT"
build:
name: "Build PHP Test (PHP ${{ matrix.php }} ${{ matrix.os }})"
runs-on: ${{ matrix.os }}
needs: define-matrix
timeout-minutes: 120
strategy:
matrix:
php:
- "8.0"
- "8.1"
- "8.2"
- "8.3"
os:
- ubuntu-latest
- macos-13
- windows-latest
- macos-14
php: ${{ fromJSON(needs.define-matrix.outputs.php) }}
os: ${{ fromJSON(needs.define-matrix.outputs.os) }}
fail-fast: false
steps:
- name: "Checkout"
@@ -139,6 +156,8 @@ jobs:
tools: pecl, composer
extensions: curl, openssl, mbstring
ini-values: memory_limit=-1
env:
phpts: nts
- name: "Cache composer packages"
id: composer-cache
@@ -154,13 +173,13 @@ jobs:
uses: actions/cache@v4
with:
path: downloads
key: php-${{ matrix.php }}-dependencies
key: php-dependencies-${{ matrix.os }}
- name: "Install Dependencies"
run: composer update -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
- name: "Run Build Tests (doctor)"
run: bin/spc doctor --auto-fix
run: bin/spc doctor --auto-fix --debug
- name: "Prepare UPX for Windows"
if: matrix.os == 'windows-latest'
@@ -175,19 +194,7 @@ jobs:
echo "UPX_CMD=$(php src/globals/test-extensions.php upx)" >> $GITHUB_ENV
- name: "Run Build Tests (download)"
run: |
bin/spc download --for-extensions="$(php src/globals/test-extensions.php extensions)" --for-libs="$(php src/globals/test-extensions.php libs)" --with-php=${{ matrix.php }} --ignore-cache-sources=php-src --debug --retry=5 --shallow-clone
run: php src/globals/test-extensions.php download_cmd ${{ matrix.os }} ${{ matrix.php }}
- name: "Download pre-built libraries for pkg-config"
if: matrix.os != 'windows-latest'
run: |
bin/spc del-download pkg-config
bin/spc download pkg-config --prefer-pre-built --debug
- name: "Run Build Tests (build, *nix)"
if: matrix.os != 'windows-latest'
run: bin/spc build "$(php src/globals/test-extensions.php extensions)" $(php src/globals/test-extensions.php zts) $(php src/globals/test-extensions.php no_strip) $UPX_CMD --with-libs="$(php src/globals/test-extensions.php libs)" --build-cli --build-micro --build-fpm --debug
- name: "Run Build Tests (build, windows)"
if: matrix.os == 'windows-latest'
run: bin/spc build "$(php src/globals/test-extensions.php extensions)" $(php src/globals/test-extensions.php zts) $(php src/globals/test-extensions.php no_strip) $env:UPX_CMD --with-libs="$(php src/globals/test-extensions.php libs)" --build-cli --build-micro --debug
- name: "Run Build Tests (build)"
run: php src/globals/test-extensions.php build_cmd ${{ matrix.os }} ${{ matrix.php }}

View File

@@ -58,6 +58,8 @@ jobs:
- name: "Generate Extension Support List"
run: |
bin/spc dev:gen-ext-docs > docs/extensions.md
bin/spc dev:gen-ext-dep-docs > docs/deps-map-ext.md
bin/spc dev:gen-lib-dep-docs > docs/deps-map-lib.md
- name: Build
run: yarn docs:build

View File

@@ -184,7 +184,7 @@ bin/spc --version
./bin/spc download --for-extensions="openssl,pcntl,mbstring,pdo_sqlite"
# 下载依赖时,优先下载有预编译的库(节省编译依赖的时间)
./bin/spc download --for-extensions="openssl,curl,mbstring,mbregex" --prefer-pre-built
# 下载编译不同版本的 PHP (--with-php=x.y推荐 7.3 ~ 8.3)
# 下载编译不同版本的 PHP (--with-php=x.y 或 --with-php=x.y.z,推荐 8.1 ~ 8.3)
./bin/spc download --for-extensions="openssl,curl,mbstring" --with-php=8.1
# 构建包含 bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl 扩展的 php-cli 和 micro.sfx

View File

@@ -142,7 +142,7 @@ curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-m
# macOS aarch64 (Apple)
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-aarch64
# Windows (x86_64, win10 build 17063 or later)
curl.exe -fsSL -o spc.exehttps://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-windows-x64.exe
curl.exe -fsSL -o spc.exe https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-windows-x64.exe
# Add execute perm (Linux and macOS only)
chmod +x ./spc
@@ -195,7 +195,7 @@ Basic usage for building php with some extensions:
./bin/spc download --for-extensions="openssl,pcntl,mbstring,pdo_sqlite"
# download pre-built libraries first (save time for compiling dependencies)
./bin/spc download --for-extensions="openssl,curl,mbstring,mbregex" --prefer-pre-built
# download different PHP version (--with-php=x.y, recommend 7.3 ~ 8.3)
# download different PHP version (--with-php=x.y or --with-php=x.y.z, recommend 8.1 ~ 8.3)
./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

View File

@@ -51,10 +51,10 @@ if ($action -eq 'add-path') {
}
# get php 8.1 specific version
$API = (Invoke-WebRequest -Uri "https://www.php.net/releases/index.php?json&version=8.1") | ConvertFrom-Json
$API = (Invoke-WebRequest -Uri "https://www.php.net/releases/index.php?json&version=8.3") | ConvertFrom-Json
# php windows download
$PHPRuntimeUrl = "https://windows.php.net/downloads/releases/php-" + $API.version + "-Win32-vs16-x64.zip"
$PHPRuntimeUrl = "https://windows.php.net/downloads/releases/php-" + $API.version + "-nts-Win32-vs16-x64.zip"
$ComposerUrl = "https://getcomposer.org/download/latest-stable/composer.phar"
# create dir

View File

@@ -11,6 +11,7 @@
"require": {
"php": ">= 8.1",
"ext-mbstring": "*",
"ext-zlib": "*",
"laravel/prompts": "^0.1.12",
"symfony/console": "^5.4 || ^6 || ^7",
"zhamao/logger": "^1.0"
@@ -19,10 +20,10 @@
"captainhook/captainhook-phar": "^5.23",
"captainhook/hook-installer": "^1.0",
"friendsofphp/php-cs-fixer": "^3.25",
"humbug/box": "^4.5",
"humbug/box": "^4.5.0 || ^4.6.0",
"nunomaduro/collision": "^7.8",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^10.3 || ^9"
"phpunit/phpunit": "^10.3 || ^9.5"
},
"autoload": {
"psr-4": {

475
composer.lock generated

File diff suppressed because it is too large Load Diff

134
config/env.ini Normal file
View File

@@ -0,0 +1,134 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; static-php-cli (spc) env configuration
;
; This file is used to set default env vars for static-php-cli build.
; As dynamic build process, some of these vars can be overwritten by CLI options.
; And you can also overwrite these vars by setting them in your shell environment.
;
; We need to use some pre-defined internal env vars, like `BUILD_ROOT_PATH`, `DOWNLOAD_PATH`, etc.
; Please note that these vars cannot be defined in this file, they are only be defined before static-php-cli running.
; Here's a list of these pre-defined internal env vars, these vars are only be defined in the static-php-cli build process if not set in the shell environment:
;
; BUILD_ROOT_PATH: the root path of the build process. (default: `$(pwd)/buildroot`)
; BUILD_INCLUDE_PATH: the path of the include files. (default: `$BUILD_ROOT_PATH/include`)
; BUILD_LIB_PATH: the path of the lib files. (default: `$BUILD_ROOT_PATH/lib`)
; BUILD_BIN_PATH: the path of the bin files. (default: `$BUILD_ROOT_PATH/bin`)
; PKG_ROOT_PATH: the root path of the package files. (default: `$(pwd)/pkgroot`)
; SOURCE_PATH: the path of the source files. (default: `$(pwd)/source`)
; DOWNLOAD_PATH: the path of the download files. (default: `$(pwd)/downloads`)
; CPU_COUNT: the count of the CPU cores. (default: `$(nproc)`)
; GNU_ARCH: the GNU arch of the current system. (default: `$(uname -m)`, e.g. `x86_64`, `aarch64`)
; MAC_ARCH: the MAC arch of the current system. (default: `$(uname -m)`, e.g. `x86_64`, `arm64`)
;
; Here's a list of env vars, these value cannot be changed anywhere:
;
; WORKING_DIR: the working directory of the build process. (default: `$(pwd)`)
; ROOT_DIR: the root directory of static-php-cli. (default: `/path/to/static-php-cli`, when running in phar or micro mode: `phar://path/to/spc.phar`)
;
; * These vars are only be defined in Unix (macOS, Linux, FreeBSD)Builder *
; PATH: static-php-cli will add `$BUILD_BIN_PATH` to PATH.
; PKG_CONFIG: static-php-cli will set `$BUILD_BIN_PATH/pkg-config` to PKG_CONFIG.
; PKG_CONFIG_PATH: static-php-cli will set `$BUILD_LIB_PATH/pkgconfig` to PKG_CONFIG_PATH.
; SPC_PHP_DEFAULT_OPTIMIZE_CFLAGS: the default optimization CFLAGS for compiling php. (if --no-strip option is set: `-g -O0`, else: `-g -Os`)
;
; * These vars are only be defined in LinuxBuilder *
; SPC_LINUX_DEFAULT_CC: the default compiler for linux. (For alpine linux: `gcc`, default: `$GNU_ARCH-linux-musl-gcc`)
; SPC_LINUX_DEFAULT_CXX: the default c++ compiler for linux. (For alpine linux: `g++`, default: `$GNU_ARCH-linux-musl-g++`)
; SPC_LINUX_DEFAULT_AR: the default archiver for linux. (For alpine linux: `ar`, default: `$GNU_ARCH-linux-musl-ar`)
; SPC_PHP_DEFAULT_LD_LIBRARY_PATH_CMD: the default LD_LIBRARY_PATH for php. (linux: `LD_LIBRARY_PATH=/usr/local/musl/$GNU_ARCH-linux-musl/lib`, default: empty)
[global]
; Build concurrency for make -jN, default is CPU_COUNT, this value are used in every libs.
SPC_CONCURRENCY=${CPU_COUNT}
; Ignore PHP version check before building some extensions
SPC_SKIP_PHP_VERSION_CHECK="no"
; Ignore some check item for bin/spc doctor command, comma separated (e.g. SPC_SKIP_DOCTOR_CHECK_ITEMS="if homebrew has installed")
SPC_SKIP_DOCTOR_CHECK_ITEMS=""
[windows]
; php-sdk-binary-tools path
PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools"
; upx executable path
UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe"
; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static
[linux]
; include PATH for musl libc.
SPC_NO_MUSL_PATH=no
; compiler environments
CC=${SPC_LINUX_DEFAULT_CC}
CXX=${SPC_LINUX_DEFAULT_CXX}
AR=${SPC_LINUX_DEFAULT_AR}
LD=ld.gold
; default compiler flags, used in CMake toolchain file, openssl and pkg-config build
SPC_DEFAULT_C_FLAGS=
SPC_DEFAULT_CXX_FLAGS=
; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated)
SPC_EXTRA_LIBS=
; upx executable path
UPX_EXEC=${PKG_ROOT_PATH}/bin/upx
; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream
; *** default build command for building php ***
; buildconf command
SPC_CMD_PREFIX_PHP_BUILDCONF="./buildconf --force"
; configure command
SPC_CMD_PREFIX_PHP_CONFIGURE="${SPC_PHP_DEFAULT_LD_LIBRARY_PATH_CMD} ./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg"
; make command
SPC_CMD_PREFIX_PHP_MAKE="make -j${CPU_COUNT}"
; *** default build vars for building php ***
; CFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS}"
; CPPFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS="-I${BUILD_INCLUDE_PATH}"
; LDFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}"
; LIBS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_LIBS="-ldl -lpthread -lm"
; EXTRA_CFLAGS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="${SPC_PHP_DEFAULT_OPTIMIZE_CFLAGS} -fno-ident -fPIE"
; EXTRA_LIBS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS=""
; EXTRA_LDFLAGS_PROGRAM for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static"
[macos]
; compiler environments
CC=clang
CXX=clang++
; default compiler flags, used in CMake toolchain file, openssl and pkg-config build
SPC_DEFAULT_C_FLAGS="--target=${MAC_ARCH}-apple-darwin"
SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin"
; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated)
SPC_EXTRA_LIBS=
; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,macos_iconv
; *** default build command for building php ***
; buildconf command
SPC_CMD_PREFIX_PHP_BUILDCONF="./buildconf --force"
; configure command
SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg"
; make command
SPC_CMD_PREFIX_PHP_MAKE="make -j${CPU_COUNT}"
; *** default build vars for building php ***
; CFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -Werror=unknown-warning-option"
; CPPFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS="-I${BUILD_INCLUDE_PATH}"
; LDFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}"
; EXTRA_CFLAGS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="${SPC_PHP_DEFAULT_OPTIMIZE_CFLAGS}"
; EXTRA_LIBS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-lresolv"
[freebsd]
; compiler environments
CC=clang
CXX=clang++

View File

@@ -189,6 +189,16 @@
"gmp"
]
},
"gmssl": {
"support": {
"BSD": "wip"
},
"type": "external",
"source": "ext-gmssl",
"lib-depends": [
"gmssl"
]
},
"iconv": {
"support": {
"BSD": "wip"
@@ -360,6 +370,15 @@
"zlib"
]
},
"msgpack": {
"support": {
"BSD": "wip"
},
"type": "external",
"source": "msgpack",
"arg-type-unix": "with",
"arg-type-win": "enable"
},
"mysqli": {
"type": "builtin",
"arg-type": "with",
@@ -590,7 +609,7 @@
"lib-depends": [
"snappy"
],
"ext-suggest": [
"ext-suggests": [
"apcu"
]
},

View File

@@ -127,6 +127,18 @@
"gmp.h"
]
},
"gmssl": {
"source": "gmssl",
"static-libs-unix": [
"libgmssl.a"
],
"static-libs-windows": [
"gmssl.lib"
],
"frameworks": [
"Security"
]
},
"icu": {
"source": "icu",
"cpp-library": true,
@@ -631,7 +643,7 @@
"liblzma.a"
],
"static-libs-windows": [
"liblzma.lib",
"lzma.lib",
"liblzma_a.lib"
],
"headers-unix": [

View File

@@ -83,6 +83,15 @@
"path": "LICENSE"
}
},
"ext-gmssl": {
"type": "ghtar",
"repo": "gmssl/GmSSL-PHP",
"path": "php-src/ext/gmssl",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"ext-imagick": {
"type": "url",
"url": "https://pecl.php.net/get/imagick",
@@ -194,6 +203,14 @@
"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."
}
},
"gmssl": {
"type": "ghtar",
"repo": "guanzhi/GmSSL",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"icu": {
"type": "ghrel",
"repo": "unicode-org/icu",
@@ -497,7 +514,7 @@
"type": "git",
"path": "php-src/sapi/micro",
"rev": "master",
"url": "https://github.com/easysoft/phpmicro",
"url": "https://github.com/static-php/phpmicro",
"license": {
"type": "file",
"path": "LICENSE"
@@ -514,6 +531,16 @@
"path": "LICENSE"
}
},
"msgpack": {
"type": "url",
"url": "https://pecl.php.net/get/msgpack",
"path": "php-src/ext/msgpack",
"filename": "msgpack.tgz",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"ncurses": {
"type": "filelist",
"url": "https://ftp.gnu.org/pub/gnu/ncurses/",
@@ -647,13 +674,16 @@
}
},
"redis": {
"type": "git",
"type": "url",
"url": "https://pecl.php.net/get/redis",
"path": "php-src/ext/redis",
"rev": "5.3.7",
"url": "https://github.com/phpredis/phpredis",
"filename": "redis.tgz",
"license": {
"type": "file",
"path": "COPYING"
"path": [
"LICENSE",
"COPYING"
]
}
},
"snappy": {

View File

@@ -2,17 +2,29 @@
<div>
<h2>{{ I18N[lang].selectedSystem }}</h2>
<div class="option-line">
<span v-for="(item, index) in osList" :key="index" style="margin-right: 4px">
<span v-for="(item, index) in osList" :key="index" style="margin-right: 8px">
<input type="radio" :id="'os-' + item.os" :value="item.os" :disabled="item.disabled === true" v-model="selectedSystem" />
<label :for="'os-' + item.os">{{ item.label }}</label>
</span>
</div>
<div class="option-line">
<select v-model="selectedArch">
<option value="x86_64">x86_64</option>
<option value="aarch64" :disabled="selectedSystem === 'windows'">aarch64</option>
</select>
</div>
<h2>{{ I18N[lang].selectExt }}{{ checkedExts.length > 0 ? (' (' + checkedExts.length + ')') : '' }}</h2>
<div class="box">
<div v-for="(item, index) in ext" class="ext-item">
<span v-if="isSupported(index, selectedSystem)">
<input type="checkbox" :id="index" :value="index" v-model="checkedExts" :disabled="extDisableList.indexOf(index) !== -1">
<label :for="index">{{ index }}</label>
<input class="input" v-model="filterText" placeholder="Highlight search..." />
<br>
<div v-for="item in extFilter" class="ext-item">
<span>
<input type="checkbox" :id="item" :value="item" v-model="checkedExts" :disabled="extDisableList.indexOf(item) !== -1">
<label :for="item">
<span>{{ highlightItem(item, 0) }}</span>
<span style="color: orangered; font-weight: bolder">{{ highlightItem(item, 1) }}</span>
<span>{{ highlightItem(item, 2) }}</span>
</label>
</span>
</div>
@@ -20,7 +32,7 @@
<div class="my-btn" v-if="selectedSystem !== 'windows'" @click="selectCommon">{{ I18N[lang].selectCommon }}</div>
<div class="my-btn" @click="checkedExts = []">{{ I18N[lang].selectNone }}</div>
<details class="details custom-block">
<details class="details custom-block" open>
<summary>{{ I18N[lang].buildLibs }}{{ checkedLibs.length > 0 ? (' (' + checkedLibs.length + ')') : '' }}</summary>
<div class="box">
<div v-for="(item, index) in libContain" class="ext-item">
@@ -135,24 +147,33 @@
<div class="warning custom-block">
<p class="custom-block-title">WARNING</p>
<p>{{ I18N[lang].windowsDownSPCWarning }}</p>
<a href="https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-windows-x64.exe" target="_blank">https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-windows-x64.exe</a>
</div>
</div>
</div>
<div v-if="downloadByExt" class="command-container">
<b>{{ I18N[lang].downloadExtOnlyCommand }}</b>
<div class="command-preview">{{ spcCommand }} download --with-php={{ selectedPhpVersion }} --for-extensions "{{ extList }}"{{ preBuilt ? ' --prefer-pre-built' : '' }}{{ debug ? ' --debug' : '' }}</div>
<div id="download-ext-cmd" class="command-preview">
{{ downloadExtCommand }}
</div>
</div>
<div v-else class="command-container">
<b>{{ I18N[lang].downloadAllCommand }}</b>
<div class="command-preview">{{ spcCommand }} download --all --with-php={{ selectedPhpVersion }}{{ preBuilt ? ' --prefer-pre-built' : '' }}{{ debug ? ' --debug' : '' }}</div>
<div id="download-all-cmd" class="command-preview">
{{ downloadAllCommand }}
</div>
</div>
<div class="command-container" v-if="enableUPX">
<b>{{ I18N[lang].downloadUPXCommand }}</b>
<div class="command-preview">{{ spcCommand }} install-pkg upx{{ debug ? ' --debug' : '' }}</div>
<div id="download-pkg-cmd" class="command-preview">
{{ downloadPkgCommand }}
</div>
</div>
<div class="command-container">
<b>{{ I18N[lang].compileCommand }}</b>
<div class="command-preview">{{ spcCommand }} build {{ buildCommand }} "{{ extList }}"{{ additionalLibs }}{{ debug ? ' --debug' : '' }}{{ zts ? ' --enable-zts' : '' }}{{ enableUPX ? ' --with-upx-pack' : '' }}{{ displayINI }}</div>
<div id="build-cmd" class="command-preview">
{{ buildCommandString }}
</div>
</div>
</div>
</template>
@@ -170,6 +191,15 @@ import libData from '../config/lib.json';
import { getAllExtLibsByDeps } from './DependencyUtil.js';
const ext = ref(extData);
const extFilter = computed(() => {
const ls = [];
for (const [name, item] of Object.entries(ext.value)) {
if (isSupported(name, selectedSystem.value)) {
ls.push(name);
}
}
return ls;
});
const lib = ref(libData);
const libContain = ref([]);
@@ -234,7 +264,7 @@ const I18N = {
microUnavailable: 'micro 不支持 PHP 7.4 及更早版本!',
windowsSAPIUnavailable: 'Windows 目前不支持 fpm、embed 构建!',
useUPX: '是否开启 UPX 压缩(减小二进制体积)',
windowsDownSPCWarning: 'Windows 下请手动下载 spc.exe 二进制文件解压到当前目录!',
windowsDownSPCWarning: 'Windows 下请手动下载 spc.exe 二进制文件解压到当前目录并重命名为 spc.exe',
usePreBuilt: '如果可能,下载预编译的依赖库(减少编译时间)',
},
en: {
@@ -268,7 +298,7 @@ const I18N = {
microUnavailable: 'Micro does not support PHP 7.4 and earlier versions!',
windowsSAPIUnavailable: 'Windows does not support fpm and embed build!',
useUPX: 'Enable UPX compression (reduce binary size)',
windowsDownSPCWarning: 'Please download the spc.exe binary file manually and extract it to the current directory on Windows!',
windowsDownSPCWarning: 'Please download the binary file manually, extract it to the current directory and rename to spc.exe on Windows!',
usePreBuilt: 'Download pre-built dependencies if possible (reduce compile time)',
}
};
@@ -325,7 +355,7 @@ const libDisableList = ref([]);
const checkedTargets = ref(['cli']);
// chosen env
const selectedEnv = ref('native');
const selectedEnv = ref('spc');
// chosen php version
const selectedPhpVersion = ref('8.2');
@@ -348,6 +378,13 @@ const enableUPX = ref(0);
const hardcodedINIData = ref('');
const selectedSystem = ref('linux');
watch(selectedSystem, () => {
if (selectedSystem.value === 'windows') {
selectedArch.value = 'x86_64';
}
});
const selectedArch = ref('x86_64');
// spc command string, alt: spc-alpine-docker, spc
@@ -381,6 +418,25 @@ const displayINI = computed(() => {
return ' ' + str.map((x) => '-I "' + x + '"').join(' ');
});
const filterText = ref('');
const highlightItem = (item, step) => {
if (item.includes(filterText.value)) {
if (step === 0) {
return item.substring(0, item.indexOf(filterText.value));
} else if (step === 1) {
return filterText.value;
} else {
return item.substring(item.indexOf(filterText.value) + filterText.value.length);
}
} else {
if (step === 0) {
return item;
}
return '';
}
};
const onTargetChange = (event) => {
let id;
if (checkedTargets.value.indexOf('all') !== -1 && event.target.value === 'all') {
@@ -427,6 +483,22 @@ const calculateExtDepends = (input) => {
return Array.from(result);
};
const downloadAllCommand = computed(() => {
return `${spcCommand.value} download --all --with-php=${selectedPhpVersion.value}${preBuilt.value ? ' --prefer-pre-built' : ''}${debug.value ? ' --debug' : ''}`;
});
const downloadExtCommand = computed(() => {
return `${spcCommand.value} download --with-php=${selectedPhpVersion.value} --for-extensions "${extList.value}"${preBuilt.value ? ' --prefer-pre-built' : ''}${debug.value ? ' --debug' : ''}`;
});
const downloadPkgCommand = computed(() => {
return `${spcCommand.value} install-pkg upx${debug.value ? ' --debug' : ''}`;
});
const buildCommandString = computed(() => {
return `${spcCommand.value} build ${buildCommand.value} "${extList.value}"${additionalLibs.value}${debug.value ? ' --debug' : ''}${zts.value ? ' --enable-zts' : ''}${enableUPX.value ? ' --with-upx-pack' : ''}${displayINI.value}`;
});
const calculateExtLibDepends = (input) => {
const result = new Set();
@@ -539,9 +611,12 @@ h2 {
.command-preview {
padding: 1.2rem;
background: var(--vp-c-divider);
border-radius: 8px;
word-break: break-all;
font-family: monospace;
overflow-wrap: break-word;
}
.option-line {
padding: 4px 8px;
}
@@ -582,12 +657,38 @@ select {
background-color: var(--vp-button-alt-active-bg);
}
.textarea {
border: 1px solid var(--vp-button-alt-border);
padding: 0 4px;
min-width: 300px;
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
width: calc(100% - 12px);
padding: 4px 8px;
}
.input {
display: block;
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
width: 100%;
padding: 4px 8px;
}
.command-container {
margin-bottom: 24px;
}
.modal-button {
padding: 4px 8px;
border-radius: 4px;
border-color: var(--vp-button-alt-border);
color: var(--vp-button-alt-text);
background-color: var(--vp-button-alt-bg);
}
.modal-button:hover {
border-color: var(--vp-button-alt-hover-border);
color: var(--vp-button-alt-hover-text);
background-color: var(--vp-button-alt-hover-bg)
}
.modal-button:active {
border-color: var(--vp-button-alt-active-border);
color: var(--vp-button-alt-active-text);
background-color: var(--vp-button-alt-active-bg)
}
</style>

View File

@@ -0,0 +1,79 @@
<template>
<div>
<header class="DocSearch-SearchBar" style="padding: 0">
<form class="DocSearch-Form searchinput">
<input class="DocSearch-Input" v-model="filterText" placeholder="Filter name..." @input="doFilter" />
</form>
</header>
<table>
<thead>
<tr>
<th>Extension Name</th>
<th>Linux</th>
<th>macOS</th>
<th>FreeBSD</th>
<th>Windows</th>
</tr>
</thead>
<tbody>
<tr v-for="item in filterData">
<td v-if="!item.notes">{{ item.name }}</td>
<td v-else>
<a :href="'./extension-notes.html#' + item.name">{{ item.name }}</a>
</td>
<td>{{ item.linux }}</td>
<td>{{ item.macos }}</td>
<td>{{ item.freebsd }}</td>
<td>{{ item.windows }}</td>
</tr>
</tbody>
</table>
<div v-if="filterData.length === 0" style="margin: 0 4px 20px 4px; color: var(--vp-c-text-2); font-size: 14px">
No result, please try another keyword.
</div>
</div>
</template>
<script>
export default {
name: "SearchTable"
}
</script>
<script setup>
import {ref} from "vue";
import ext from '../../../config/ext.json';
// 将 ext 转换为列表,方便后续操作
const data = ref([]);
for (const [name, item] of Object.entries(ext)) {
data.value.push({
name,
linux: item.support?.Linux === undefined ? 'yes' : (item.support?.Linux === 'wip' ? '' : item.support?.Linux),
macos: item.support?.Darwin === undefined ? 'yes' : (item.support?.Darwin === 'wip' ? '' : item.support?.Darwin),
freebsd: item.support?.BSD === undefined ? 'yes' : (item.support?.BSD === 'wip' ? '' : item.support?.BSD),
windows: item.support?.Windows === undefined ? 'yes' : (item.support?.Windows === 'wip' ? '' : item.support?.Windows),
notes: item.notes === true,
});
}
const filterData = ref(data.value);
const filterText = ref('');
const doFilter = () => {
if (filterText.value === '') {
filterData.value = data.value;
return;
}
filterData.value = data.value.filter(item => {
return item.name.toLowerCase().includes(filterText.value.toLowerCase());
});
}
</script>
<style>
.searchinput {
border: 1px solid var(--vp-c-divider);
}
</style>

View File

@@ -10,6 +10,7 @@ export default {
{text: 'Extension Notes', link: '/en/guide/extension-notes'},
{text: 'Command Generator', link: '/en/guide/cli-generator'},
{text: 'Environment Variables', link: '/en/guide/env-vars', collapsed: true,},
{text: 'Dependency Table', link: '/en/guide/deps-map'},
]
},
{

View File

@@ -10,6 +10,7 @@ export default {
{text: '扩展注意事项', link: '/zh/guide/extension-notes'},
{text: '编译命令生成器', link: '/zh/guide/cli-generator'},
{text: '环境变量列表', link: '/zh/guide/env-vars'},
{text: '依赖关系图表', link: '/zh/guide/deps-map'},
]
},
{

0
docs/deps-map-ext.md Normal file
View File

0
docs/deps-map-lib.md Normal file
View File

View File

@@ -317,3 +317,24 @@ When an open source project has multiple licenses, multiple files can be specifi
}
}
```
When the license of an open source project uses different files between versions,
`path` can be used as an array to list the possible license files:
```json
{
"redis": {
"type": "git",
"path": "php-src/ext/redis",
"rev": "release/6.0.2",
"url": "https://github.com/phpredis/phpredis",
"license": {
"type": "file",
"path": [
"LICENSE",
"COPYING"
]
}
}
}
```

View File

@@ -2,7 +2,19 @@
Here will be some questions that you may encounter easily. There are currently many, but I need to take time to organize them.
## Can statically compiled PHP install extensions?
## What is the path of php.ini ?
On Linux, macOS and FreeBSD, the path of `php.ini` is `/usr/local/etc/php/php.ini`.
On Windows, the path is `C:\windows\php.ini` or the current directory of `php.exe`.
The directory where to look for `php.ini` can be changed on *nix using the manual build option `--with-config-file-path`.
In addition, on Linux, macOS and FreeBSD, `.ini` files present in the `/usr/local/etc/php/conf.d` directory will also be loaded.
On Windows, this path is empty by default.
The directory can be changed using the manual build option `--with-config-file-scan-dir`.
`php.ini` will also be searched for in [the other standard locations](https://www.php.net/manual/configuration.file.php).
## Can statically-compiled PHP install extensions?
Because the principle of installing extensions in PHP under the traditional architecture is to install new extensions using `.so` type dynamic link libraries,
and statically linked PHP compiled using this project cannot **directly** install new extensions using dynamic link libraries.

View File

@@ -159,7 +159,8 @@ You can try to use the following commands:
- `--with-clean`: clean up old make files before compiling PHP
- `--enable-zts`: Make compiled PHP thread-safe version (default is NTS version)
- `--with-libs=XXX,YYY`: Compile the specified dependent library before compiling PHP, and activate some extension optional functions
- `--with-libs=XXX,YYY`: Compile the specified dependent library before compiling PHP, and activate some extension optional functions
- `--with-config-file-scan-dir=XXX`: Set the directory to scan for `.ini` files after reading `php.ini` (Check [here](../faq/index.html#what-is-the-path-of-php-ini) for default paths)
- `-I xxx=yyy`: Hard compile INI options into PHP before compiling (support multiple options, alias is `--with-hardcoded-ini`)
- `--with-micro-fake-cli`: When compiling micro, let micro's `PHP_SAPI` pretend to be `cli` (for compatibility with some programs that check `PHP_SAPI`)
- `--disable-opcache-jit`: Disable opcache jit (enabled by default)

View File

@@ -1,3 +1,7 @@
---
aside: false
---
<script setup lang="ts">
import CliGenerator from "../../.vitepress/components/CliGenerator.vue";
</script>

26
docs/en/guide/deps-map.md Normal file
View File

@@ -0,0 +1,26 @@
---
outline: 'deep'
---
# Dependency Table
When compiling PHP, each extension and library has dependencies, which may be required or optional.
You can choose whether to include these optional dependencies.
For example, when compiling the `gd` extension under Linux,
the `zlib,libpng` libraries and the `zlib` extension are forced to be compiled,
while the `libavif,libwebp,libjpeg,freetype` libraries are optional libraries and will not be compiled by default
unless specified by the `--with-libs=avif,webp,jpeg,freetype` option.
- For optional extensions (optional features of extensions), you need to specify them manually at compile time, for example, to enable igbinary support for Redis: `bin/spc build redis,igbinary`.
- For optional libraries, you need to compile and specify them through the `--with-libs=XXX` option.
- If you want to enable all optional extensions, you can use `bin/spc build redis --with-suggested-exts`.
- If you want to enable all optional libraries, you can use `--with-suggested-libs`.
## Extension Dependency Table
<!--@include: ../../deps-map-ext.md-->
## Library Dependency Table
<!--@include: ../../deps-map-lib.md-->

View File

@@ -1,12 +1,23 @@
---
aside: false
---
# Environment variables
All environment variables mentioned in the list on this page have default values unless otherwise noted.
You can override the default values by setting these environment variables.
## Environment variables list
Starting from version 2.3.5, we have centralized the environment variables in the `config/env.ini` file.
You can set environment variables by modifying this file.
We divide the environment variables supported by static-php-cli into three types:
- Global internal environment variables: declared after static-php-cli starts, you can use `getenv()` to get them internally in static-php-cli, and you can override them before starting static-php-cli.
- Fixed environment variables: declared after static-php-cli starts, you can only use `getenv()` to get them, but you cannot override them through shell scripts.
- Config file environment variables: declared before static-php-cli build, you can set these environment variables by modifying the `config/env.ini` file or through shell scripts.
You can read the comments for each parameter in [config/env.ini](https://github.com/crazywhalecc/static-php-cli/blob/main/config/env.ini) to understand its purpose.
## Custom environment variables
Generally, you don't need to modify any of the following environment variables as they are already set to optimal values.
However, if you have special needs, you can set these environment variables to meet your needs
(for example, you need to debug PHP performance under different compilation parameters).
@@ -22,91 +33,13 @@ bin/spc build mbstring,pcntl --build-cli
SPC_CONCURRENCY=4 bin/spc build mbstring,pcntl --build-cli
```
## General environment variables
Or, if you need to modify an environment variable for a long time, you can modify the `config/env.ini` file.
General environment variables can be used by all build targets.
`config/env.ini` is divided into three sections, `[global]` is globally effective, `[windows]`, `[macos]`, `[linux]` are only effective for the corresponding operating system.
| var name | default value | comment |
|------------------------------|---------------------------|-------------------------------------------------|
| `BUILD_ROOT_PATH` | `{pwd}/buildroot` | The root directory of the build target |
| `BUILD_LIB_PATH` | `{pwd}/buildroot/lib` | The root directory of compilation libraries |
| `BUILD_INCLUDE_PATH` | `{pwd}/buildroot/include` | Header file directory for compiling libraries |
| `BUILD_BIN_PATH` | `{pwd}/buildroot/bin` | Compiled binary file directory |
| `PKG_ROOT_PATH` | `{pwd}/pkgroot` | Directory where precompiled tools are installed |
| `SOURCE_PATH` | `{pwd}/source` | The source code extract directory |
| `DOWNLOAD_PATH` | `{pwd}/downloads` | Downloaded file directory |
| `SPC_CONCURRENCY` | Depends on CPU cores | Number of parallel compilations |
| `SPC_SKIP_PHP_VERSION_CHECK` | empty | Skip PHP version check when set to `yes` |
For example, if you need to modify the `./configure` command for compiling PHP, you can find the `SPC_CMD_PREFIX_PHP_CONFIGURE` environment variable in the `config/env.ini` file, and then modify its value.
## OS specific variables
These environment variables are system-specific and will only take effect on a specific OS.
### Windows
| var name | default value | comment |
|----------------|------------------------------|---------------------------|
| `PHP_SDK_PATH` | `{pwd}\php-sdk-binary-tools` | PHP SDK tools path |
| `UPX_EXEC` | `$PKG_ROOT_PATH\bin\upx.exe` | UPX compression tool path |
### macOS
| var name | default value | comment |
|--------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------|
| `CC` | `clang` | C Compiler |
| `CXX` | `clang++` | C++ Compiler |
| `SPC_DEFAULT_C_FLAGS` | `--target=arm64-apple-darwin` or `--target=x86_64-apple-darwin` | Default C flags (not the same as `CFLAGS`) |
| `SPC_DEFAULT_CXX_FLAGS` | `--target=arm64-apple-darwin` or `--target=x86_64-apple-darwin` | Default C flags (not the same as `CPPFLAGS`) |
| `SPC_CMD_PREFIX_PHP_BUILDCONF` | `./buildconf --force` | PHP `buildconf` command prefix |
| `SPC_CMD_PREFIX_PHP_CONFIGURE` | `./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg` | PHP `configure` command prefix |
| `SPC_CMD_PREFIX_PHP_MAKE` | `make -j$SPC_CONCURRENCY` | PHP `make` command prefix |
| `SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS` | `$SPC_DEFAULT_C_FLAGS -Werror=unknown-warning-option` | `CFLAGS` variable of PHP `configure` command |
| `SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS` | `-I$BUILD_INCLUDE_PATH` | `CPPFLAGS` variable of PHP `configure` command |
| `SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS` | `-L$BUILD_LIB_PATH` | `LDFLAGS` variable of PHP `configure` command |
| `SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS` | `-g0 -Os` or `-g -O0` (the latter when using `--no-strip`) | `EXTRA_CFLAGS` variable of PHP `make` command |
| `SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS` | `-lresolv` | Extra `EXTRA_LIBS` variables for PHP `make` command |
### Linux
| var name | default value | comment |
|----------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
| `UPX_EXEC` | `$PKG_ROOT_PATH/bin/upx` | UPX compression tool path |
| `GNU_ARCH` | `x86_64` or `aarch64` | CPU architecture |
| `CC` | Alpine: `gcc`, Other: `$GNU_ARCH-linux-musl-gcc` | C Compiler |
| `CXX` | Alpine: `g++`, Other: `$GNU_ARCH-linux-musl-g++` | C++ Compiler |
| `AR` | Alpine: `ar`, Other: `$GNU_ARCH-linux-musl-ar` | Static library tools |
| `LD` | `ld.gold` | Linker |
| `PATH` | `/usr/local/musl/bin:/usr/local/musl/$GNU_ARCH-linux-musl/bin:$PATH` | System PATH |
| `SPC_DEFAULT_C_FLAGS` | empty | Default C flags |
| `SPC_DEFAULT_CXX_FLAGS` | empty | Default C++ flags |
| `SPC_CMD_PREFIX_PHP_BUILDCONF` | `./buildconf --force` | PHP `buildconf` command prefix |
| `SPC_CMD_PREFIX_PHP_CONFIGURE` | `LD_LIBRARY_PATH={ld_lib_path} ./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg` | PHP `configure` command prefix |
| `SPC_CMD_PREFIX_PHP_MAKE` | `make -j$SPC_CONCURRENCY` | PHP `make` command prefix |
| `SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS` | `$SPC_DEFAULT_C_FLAGS` | `CFLAGS` variable of PHP `configure` command |
| `SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS` | `-I$BUILD_INCLUDE_PATH` | `CPPFLAGS` variable of PHP `configure` command |
| `SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS` | `-L$BUILD_LIB_PATH` | `LDFLAGS` variable of PHP `configure` command |
| `SPC_CMD_VAR_PHP_CONFIGURE_LIBS` | `-ldl -lpthread` | `LIBS` variable of PHP `configure` command |
| `SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS` | `-g0 -Os -fno-ident -fPIE` or `-g -O0 -fno-ident -fPIE` (the latter when using `--no-strip`) | `EXTRA_CFLAGS` variable of PHP `make` command |
| `SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS` | empty | Extra `EXTRA_LIBS` variables for PHP `make` command |
| `SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM` | `-all-static` (when using `clang`: `-Xcompiler -fuse-ld=lld -all-static`) | Additional `LDFLAGS` variable for `make` command |
| `SPC_NO_MUSL_PATH` | empty | Whether to not insert the PATH of the musl toolchain (not inserted when the value is `yes`) |
> `{ld_lib_path}` value is `/usr/local/musl/$GNU_ARCH-linux-musl/lib`。
### FreeBSD
Due to the small number of users of the FreeBSD system, we do not provide environment variables for the FreeBSD system for the time being.
### Unix
For Unix systems such as macOS, Linux, FreeBSD, etc., the following environment variables are common.
| var name | default value | comment |
|-------------------|------------------------------|----------------------------|
| `PATH` | `$BUILD_BIN_PATH:$PATH` | System PATH |
| `PKG_CONFIG_PATH` | `$BUILD_LIB_PATH/pkgconfig` | pkg-config search path |
| `PKG_CONFIG` | `$BUILD_BIN_PATH/pkg-config` | pkg-config executable path |
## Library Environment variables (Unix only)
## Library environment variables (Unix only)
Starting from 2.2.0, static-php-cli supports custom environment variables for all compilation dependent library commands of macOS, Linux, FreeBSD and other Unix systems.

View File

@@ -1,3 +1,7 @@
<script setup>
import SearchTable from "../../.vitepress/components/SearchTable.vue";
</script>
# Extensions
> - `yes`: supported
@@ -5,7 +9,7 @@
> - `no` with issue link: confirmed to be unavailable due to issue
> - `partial` with issue link: supported but not perfect due to issue
<!--@include: ../../extensions.md-->
<search-table />
::: tip
If an extension you need is missing, you can create a [Feature Request](https://github.com/crazywhalecc/static-php-cli/issues).
@@ -14,14 +18,6 @@ Some extensions or libraries that the extension depends on will have some option
For example, the gd library optionally supports libwebp, freetype, etc.
If you only use `bin/spc build gd --build-cli` they will not be included (static-php-cli defaults to the minimum dependency principle).
You can use `--with-libs=` to add these libraries when compiling.
When the dependent libraries of this compilation include them, gd will automatically use them to enable these features.
(For example: `bin/spc build gd --with-libs=libwebp,freetype --build-cli`)
Alternatively you can use `--with-suggested-exts` and `--with-suggested-libs` to enable all optional dependencies of these extensions and libraries.
(For example: `bin/spc build gd --with-suggested-libs --build-cli`)
If you don't know whether an extension has optional features,
you can check the [spc configuration file](https://github.com/crazywhalecc/static-php-cli/tree/main/config)
or use the command `bin/spc dev:extensions` (library dependency is `lib-suggests`, extension dependency is `ext-suggests`).
For more information about optional libraries, see [Extensions, Library Dependency Map](./deps-map).
For optional libraries, you can also select an extension from the [Command Generator](./cli-generator) and then select optional libraries.
:::

View File

@@ -143,6 +143,7 @@ including php-src and the source code of various dependent libraries.
bin/spc download --all
# Download all dependent packages, and specify the main version of PHP to download, optional: 7.3, 7.4, 8.0, 8.1, 8.2, 8.3
# Also supports specific version of php release: 8.3.10, 8.2.22, etc.
bin/spc download --all --with-php=8.2
# Show download progress bar while downloading (curl)
@@ -170,7 +171,7 @@ bin/spc download --for-libs=liblz4,libevent --for-extensions=pcntl,rar,xml
bin/spc download --for-libs=liblz4,libevent --without-suggestions
# When downloading sources, ignore some source caches (always force download, e.g. switching PHP version)
bin/spc download --for-extensions=curl,pcntl,xml --ignore-cache-sources=php-src --with-php=8.3
bin/spc download --for-extensions=curl,pcntl,xml --ignore-cache-sources=php-src --with-php=8.3.10
# Set retry times (default is 0)
bin/spc download --all --retry=2
@@ -203,6 +204,19 @@ bin/spc download --all -U "php-src:https://downloads.php.net/~eric/php-8.3.0beta
bin/spc download --all -U "curl:https://curl.se/download/curl-7.88.1.tar.gz"
```
If the source you download is not a link, but a git repository, you can use `-G` or `--custom-git` to rewrite the download link,
so that the downloader can force the use of the specified git repository to download packages from this source.
The usage method is `{source-name}:{branch}:{url}`, which can rewrite the download link of multiple libraries at the same time.
It is also available when downloading with the `--for-extensions` option.
```bash
# Specifying to download the source code of the PHP extension from the specified branch of the git repository
bin/spc download --for-extensions=redis -G "php-src:master:https://github.com/php/php-src.git"
# Download the latest code from the master branch of the swoole-src repository instead of PECL release version
bin/spc download --for-extensions=swoole -G "swoole:master:https://github.com/swoole/swoole-src.git"
```
## Command - doctor
If you can run `bin/spc` normally but cannot compile static PHP or dependent libraries normally,
@@ -290,6 +304,8 @@ You can try to use the following commands:
- `--enable-zts`: Make compiled PHP thread-safe version (default is NTS version)
- `--no-strip`: Do not run `strip` after compiling the PHP library to trim the binary file to reduce its size (the macOS binary file without trim can use dynamically linked third-party extensions)
- `--with-libs=XXX,YYY`: Compile the specified dependent library before compiling PHP, and activate some extended optional functions (such as libavif of the gd library, etc.)
- `--with-config-file-path=XXX`: Set the path in which to look for `php.ini` (Check [here](../faq/index.html#what-is-the-path-of-php-ini) for default paths)
- `--with-config-file-scan-dir=XXX`: Set the directory to scan for `.ini` files after reading `php.ini` (Check [here](../faq/index.html#what-is-the-path-of-php-ini) for default paths)
- `-I xxx=yyy`: Hard compile INI options into PHP before compiling (support multiple options, alias is `--with-hardcoded-ini`)
- `--with-micro-fake-cli`: When compiling micro, let micro's `PHP_SAPI` pretend to be `cli` (for compatibility with some programs that check `PHP_SAPI`)
- `--disable-opcache-jit`: Disable opcache jit (enabled by default)

View File

@@ -297,3 +297,23 @@ pkg.json 存放的是非源码类型的文件资源,例如 musl-toolchain、UP
}
}
```
当一个开源项目的许可证在不同版本间使用不同的文件,`path` 参数可以使用数组将可能的许可证文件列出:
```json
{
"redis": {
"type": "git",
"path": "php-src/ext/redis",
"rev": "release/6.0.2",
"url": "https://github.com/phpredis/phpredis",
"license": {
"type": "file",
"path": [
"LICENSE",
"COPYING"
]
}
}
}
```

View File

@@ -2,6 +2,18 @@
这里将会编写一些你容易遇到的问题。目前有很多,但是我需要花时间来整理一下。
## php.ini 的路径是什么?
在 Linux、macOS 和 FreeBSD 上,`php.ini` 的默认路径是 `/usr/local/etc/php/php.ini`
在 Windows 中,路径是 `C:\windows\php.ini``php.exe` 所在的当前目录。
可以在 *nix 系统中使用手动构建选项 `--with-config-file-path` 来更改查找 `php.ini` 的目录。
此外,在 Linux、macOS 和 FreeBSD 上,`/usr/local/etc/php/conf.d` 目录中的 `*.ini` 文件也会被加载。
在 Windows 中,该路径默认为空。
可以使用手动构建选项 `--with-config-file-scan-dir` 更改该目录。
PHP 默认也会从 [其他标准位置](https://www.php.net/manual/zh/configuration.file.php) 中搜索 `php.ini`
## 静态编译的 PHP 可以安装扩展吗
因为传统架构下的 PHP 安装扩展的原理是使用 `.so` 类型的动态链接的库方式安装新扩展,而使用本项目编译的静态链接的 PHP 无法**直接**使用动态链接库安装新扩展。

View File

@@ -146,6 +146,7 @@ bin/spc build "openssl" --build-cli --debug
- `--with-clean`: 编译 PHP 前先清理旧的 make 产生的文件
- `--enable-zts`: 让编译的 PHP 为线程安全版本(默认为 NTS 版本)
- `--with-libs=XXX,YYY`: 编译 PHP 前先编译指定的依赖库,激活部分扩展的可选功能
- `--with-config-file-scan-dir=XXX` 读取 `php.ini` 后扫描 `.ini` 文件的目录(在 [这里](../faq/index.html#php-ini-的路径是什么) 查看默认路径)
- `-I xxx=yyy`: 编译前将 INI 选项硬编译到 PHP 内(支持多个选项,别名是 `--with-hardcoded-ini`
- `--with-micro-fake-cli`: 在编译 micro 时,让 micro 的 SAPI 伪装为 `cli`(用于兼容一些检查 `PHP_SAPI` 的程序)
- `--disable-opcache-jit`: 禁用 opcache jit默认启用

View File

@@ -1,3 +1,7 @@
---
aside: false
---
<script setup lang="ts">
import CliGenerator from "../../.vitepress/components/CliGenerator.vue";
</script>

22
docs/zh/guide/deps-map.md Normal file
View File

@@ -0,0 +1,22 @@
---
outline: 'deep'
---
# 依赖关系图表
在编译 PHP 时,每个扩展、库都有依赖关系,这些依赖关系可能是必需的,也可能是可选的。在编译 PHP 时,可以选择是否包含这些可选的依赖关系。
例如,在 Linux 下编译 `gd` 扩展时,会强制编译 `zlib,libpng` 库和 `zlib` 扩展,而 `libavif,libwebp,libjpeg,freetype` 库都是可选的库,默认不会编译,除非通过 `--with-libs=avif,webp,jpeg,freetype` 选项指定。
- 对于可选扩展(扩展的可选特性),需手动在编译时指定,例如启用 Redis 的 igbinary 支持:`bin/spc build redis,igbinary`
- 对于可选库,需通过 `--with-libs=XXX` 选项编译指定。
- 如果想启用所有的可选扩展,可以使用 `bin/spc build redis --with-suggested-exts` 参数。
- 如果想启用所有的可选库,可以使用 `--with-suggested-libs` 参数。
## 扩展的依赖图
<!--@include: ../../deps-map-ext.md-->
## 库的依赖表
<!--@include: ../../deps-map-lib.md-->

View File

@@ -1,11 +1,21 @@
---
aside: false
---
# 环境变量列表
# 环境变量
本页面的环境变量列表中所提到的所有环境变量都具有默认值,除非另有说明。你可以通过设置这些环境变量来覆盖默认值。
## 环境变量列表
在 2.3.5 版本之后,我们将环境变量集中到了 `config/env.ini` 文件中,你可以通过修改这个文件来设置环境变量。
我们将 static-php-cli 支持的环境变量分为三种:
- 全局内部环境变量:在 static-php-cli 启动后即声明,你可以在 static-php-cli 的内部使用 `getenv()` 来获取他们,也可以在启动 static-php-cli 前覆盖。
- 固定环境变量:在 static-php-cli 启动后声明,你仅可使用 `getenv()` 获取,但无法通过 shell 脚本对其覆盖。
- 配置文件环境变量:在 static-php-cli 构建前声明,你可以通过修改 `config/env.ini` 文件或通过 shell 脚本来设置这些环境变量。
你可以阅读 [config/env.ini](https://github.com/crazywhalecc/static-php-cli/blob/main/config/env.ini) 中每项参数的注释来了解其作用(仅限英文版)。
## 自定义环境变量
一般情况下,你不需要修改任何以下环境变量,因为它们已经被设置为最佳值。
但是,如果你有特殊需求,你可以通过设置这些环境变量来满足你的需求(比如你需要调试不同编译参数下的 PHP 性能表现)。
@@ -20,89 +30,11 @@ bin/spc build mbstring,pcntl --build-cli
SPC_CONCURRENCY=4 bin/spc build mbstring,pcntl --build-cli
```
## 通用环境变量
或者,如果你需要长期修改某个环境变量,你可以通过修改 `config/env.ini` 文件来实现。
通用环境变量是所有构建目标都可以使用的环境变量
`config/env.ini` 分为三段,其中 `[global]` 全局有效,`[windows]``[macos]``[linux]` 仅对应的操作系统有效
| var name | default value | comment |
|------------------------------|---------------------------|-----------------------------|
| `BUILD_ROOT_PATH` | `{pwd}/buildroot` | 编译目标的根目录 |
| `BUILD_LIB_PATH` | `{pwd}/buildroot/lib` | 编译依赖库的根目录 |
| `BUILD_INCLUDE_PATH` | `{pwd}/buildroot/include` | 编译依赖库的头文件目录 |
| `BUILD_BIN_PATH` | `{pwd}/buildroot/bin` | 编译依赖库的二进制文件目录 |
| `PKG_ROOT_PATH` | `{pwd}/pkgroot` | 闭源或预编译工具下载后安装的目录 |
| `SOURCE_PATH` | `{pwd}/source` | 编译项目的源码解压缩目录 |
| `DOWNLOAD_PATH` | `{pwd}/downloads` | 下载的文件存放目录 |
| `SPC_CONCURRENCY` | 取决于当前 CPU 核心数量 | 并行编译的数量 |
| `SPC_SKIP_PHP_VERSION_CHECK` | 空 | 设置为 `yes` 时,跳过扩展对 PHP 版本的检查 |
## 系统特定变量
这些环境变量是特定于系统的,它们只在特定的系统上才会生效。
### Windows
| var name | default value | comment |
|----------------|------------------------------|-----------------|
| `PHP_SDK_PATH` | `{pwd}\php-sdk-binary-tools` | PHP SDK 工具的安装目录 |
| `UPX_EXEC` | `$PKG_ROOT_PATH\bin\upx.exe` | UPX 压缩工具的路径 |
### macOS
| var name | default value | comment |
|--------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|
| `CC` | `clang` | C 编译器 |
| `CXX` | `clang++` | C++ 编译器 |
| `SPC_DEFAULT_C_FLAGS` | `--target=arm64-apple-darwin``--target=x86_64-apple-darwin` | 默认 C 编译标志(与 `CFLAGS` 不同) |
| `SPC_DEFAULT_CXX_FLAGS` | `--target=arm64-apple-darwin``--target=x86_64-apple-darwin` | 默认 C++ 编译标志(与 `CXXFLAGS` 不同) |
| `SPC_CMD_PREFIX_PHP_BUILDCONF` | `./buildconf --force` | 编译 PHP `buildconf` 命令前缀 |
| `SPC_CMD_PREFIX_PHP_CONFIGURE` | `./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg` | 编译 PHP `configure` 命令前缀 |
| `SPC_CMD_PREFIX_PHP_MAKE` | `make -j$SPC_CONCURRENCY` | 编译 PHP `make` 命令前缀 |
| `SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS` | `$SPC_DEFAULT_C_FLAGS -Werror=unknown-warning-option` | PHP `configure` 命令的 `CFLAGS` 变量 |
| `SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS` | `-I$BUILD_INCLUDE_PATH` | PHP `configure` 命令的 `CPPFLAGS` 变量 |
| `SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS` | `-L$BUILD_LIB_PATH` | PHP `configure` 命令的 `LDFLAGS` 变量 |
| `SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS` | `-g0 -Os``-g -O0`(当使用 `--no-strip` 时为后者) | PHP `make` 命令的 `EXTRA_CFLAGS` 变量 |
| `SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS` | `-lresolv` | PHP `make` 命令的额外 `EXTRA_LIBS` 变量 |
### Linux
| var name | default value | comment |
|----------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------|
| `UPX_EXEC` | `$PKG_ROOT_PATH/bin/upx` | UPX 压缩工具的路径 |
| `GNU_ARCH` | `x86_64``aarch64` | 当前环境的 CPU 架构 |
| `CC` | Alpine: `gcc`, Other: `$GNU_ARCH-linux-musl-gcc` | C 编译器 |
| `CXX` | Alpine: `g++`, Other: `$GNU_ARCH-linux-musl-g++` | C++ 编译器 |
| `AR` | Alpine: `ar`, Other: `$GNU_ARCH-linux-musl-ar` | 静态库工具 |
| `LD` | `ld.gold` | 链接器 |
| `PATH` | `/usr/local/musl/bin:/usr/local/musl/$GNU_ARCH-linux-musl/bin:$PATH` | 系统 PATH |
| `SPC_DEFAULT_C_FLAGS` | empty | 默认 C 编译标志 |
| `SPC_DEFAULT_CXX_FLAGS` | empty | 默认 C++ 编译标志 |
| `SPC_CMD_PREFIX_PHP_BUILDCONF` | `./buildconf --force` | 编译 PHP `buildconf` 命令前缀 |
| `SPC_CMD_PREFIX_PHP_CONFIGURE` | `LD_LIBRARY_PATH={ld_lib_path} ./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg` | 编译 PHP `configure` 命令前缀 |
| `SPC_CMD_PREFIX_PHP_MAKE` | `make -j$SPC_CONCURRENCY` | 编译 PHP `make` 命令前缀 |
| `SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS` | `$SPC_DEFAULT_C_FLAGS` | PHP `configure` 命令的 `CFLAGS` 变量 |
| `SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS` | `-I$BUILD_INCLUDE_PATH` | PHP `configure` 命令的 `CPPFLAGS` 变量 |
| `SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS` | `-L$BUILD_LIB_PATH` | PHP `configure` 命令的 `LDFLAGS` 变量 |
| `SPC_CMD_VAR_PHP_CONFIGURE_LIBS` | `-ldl -lpthread` | PHP `configure` 命令的 `LIBS` 变量 |
| `SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS` | `-g0 -Os -fno-ident -fPIE``-g -O0 -fno-ident -fPIE`(当使用 `--no-strip` 时为后者) | PHP `make` 命令的 `EXTRA_CFLAGS` 变量 |
| `SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS` | empty | PHP `make` 命令的额外 `EXTRA_LIBS` 变量 |
| `SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM` | `-all-static`(当使用 `clang` 时:`-Xcompiler -fuse-ld=lld -all-static` | `make` 命令的额外 `LDFLAGS` 变量(用于编译程序) |
| `SPC_NO_MUSL_PATH` | empty | 是否不插入 musl 工具链的 PATH值为 `yes` 时不插入) |
> `{ld_lib_path}` 值为 `/usr/local/musl/$GNU_ARCH-linux-musl/lib`。
### FreeBSD
因 FreeBSD 系统的用户较少,我们暂时不提供 FreeBSD 系统的环境变量。
### Unix
对于 macOS、Linux、FreeBSD 等 Unix 系统,以下环境变量是通用的。
| var name | default value | comment |
|-------------------|------------------------------|------------------|
| `PATH` | `$BUILD_BIN_PATH:$PATH` | 系统 PATH |
| `PKG_CONFIG_PATH` | `$BUILD_LIB_PATH/pkgconfig` | pkg-config 的搜索路径 |
| `PKG_CONFIG` | `$BUILD_BIN_PATH/pkg-config` | pkg-config 命令路径 |
例如,你需要修改编译 PHP 的 `./configure` 命令,你可以在 `config/env.ini` 文件中找到 `SPC_CMD_PREFIX_PHP_CONFIGURE` 环境变量,然后修改其值即可。
## 编译依赖库的环境变量(仅限 Unix 系统)
@@ -138,6 +70,7 @@ openssl_CFLAGS="-O0"
| `ldap_LDFLAGS` | `-L$BUILD_LIB_PATH` |
| `openssl_CFLAGS` | Linux: `$SPC_DEFAULT_C_FLAGS`, Other: empty |
| others... | empty |
:::
下表是支持自定义以上三种变量的依赖库名称列表:
@@ -161,5 +94,6 @@ openssl_CFLAGS="-O0"
::: tip
因为给每个库适配自定义环境变量是一项特别繁琐的工作,且大部分情况下你都不需要这些库的自定义环境变量,所以我们目前只支持了部分库的自定义环境变量。
如果你需要自定义环境变量的库不在上方列表,可以通过 [GitHub Issue](https://github.com/crazywhalecc/static-php-cli/issues) 来提出需求。
如果你需要自定义环境变量的库不在上方列表,可以通过 [GitHub Issue](https://github.com/crazywhalecc/static-php-cli/issues)
来提出需求。
:::

View File

@@ -132,7 +132,7 @@ event 扩展在 macOS 系统下编译后暂无法使用 `openpty` 特性。相
parallel 扩展只支持 PHP 8.0 及以上版本,并只支持 ZTS 构建(`--enable-zts`)。
# spx
## spx
1. [SPX 扩展](https://github.com/NoiseByNorthwest/php-spx) 只支持非线程模式。
2. SPX 目前不支持 Windows且官方仓库也不支持静态编译static-php-cli 使用了 [修改版本](https://github.com/static-php/php-spx)。

View File

@@ -1,3 +1,7 @@
<script setup>
import SearchTable from "../../.vitepress/components/SearchTable.vue";
</script>
# 扩展列表
> - `yes`: 已支持
@@ -5,7 +9,8 @@
> - `no` with issue link: 确定不支持或无法支持
> - `partial` with issue link: 已支持,但是无法完美工作
<!--@include: ../../extensions.md-->
<search-table />
::: tip
如果缺少您需要的扩展,您可以创建 [功能请求](https://github.com/crazywhalecc/static-php-cli/issues)。
@@ -13,12 +18,5 @@
有些扩展或扩展依赖的库会有一些可选的特性,例如 gd 库可选支持 libwebp、freetype 等。
如果你只使用 `bin/spc build gd --build-cli` 是不会包含它们static-php-cli 默认为最小依赖原则)。
你可以在编译时使用 `--with-libs=` 加入这些库当本次编译的依赖库中包含它们gd 会自动依赖它们启用这些特性
(如:`bin/spc build gd --with-libs=libwebp,freetype --build-cli`
或者你也可以使用 `--with-suggested-exts``--with-suggested-libs` 启用这些扩展和库所有可选的依赖。
(如:`bin/spc build gd --with-suggested-libs --build-cli`
如果你不知道某个扩展是否有可选特性,可以通过查看 [spc 配置文件](https://github.com/crazywhalecc/static-php-cli/tree/main/config)
或使用命令 `bin/spc dev:extensions` 查看(库依赖为 `lib-suggests`,扩展依赖为 `ext-suggests`)。
有关编译可选库,请参考 [扩展、库的依赖关系图表](./deps-map)。对于可选的库,你也可以从 [编译命令生成器](./cli-generator) 中选择扩展后展开选择可选库
:::

View File

@@ -132,8 +132,8 @@ bin/spc download --for-libs=liblz4,libevent --for-extensions=pcntl,rar,xml
# 仅下载要编译的库(包括其依赖,使用库名,不包含可选库)
bin/spc download --for-libs=liblz4,libevent --without-suggestions
# 下载资源时,忽略部分资源的缓存,强制下载(如切换 PHP 版本)
bin/spc download --for-extensions=curl,pcntl,xml --ignore-cache-sources=php-src --with-php=8.3
# 下载资源时,忽略部分资源的缓存,强制下载(如切换特定 PHP 版本)
bin/spc download --for-extensions=curl,pcntl,xml --ignore-cache-sources=php-src --with-php=8.3.10
# 下载资源时,优先下载有预编译包的依赖库(减少编译依赖的时间)
bin/spc download --for-extensions="curl,pcntl,xml,mbstring" --prefer-pre-built
@@ -141,7 +141,7 @@ bin/spc download --for-extensions="curl,pcntl,xml,mbstring" --prefer-pre-built
# 下载所有依赖包
bin/spc download --all
# 下载所有依赖包,并指定下载的 PHP 主版本可选7.37.48.08.18.28.3。
# 下载所有依赖包,并指定下载的 PHP 主版本可选7.37.48.08.18.28.3,也可以使用特定的版本,如 8.3.10
bin/spc download --all --with-php=8.2
# 下载时显示下载进度条curl
@@ -176,6 +176,17 @@ bin/spc download --all -U "php-src:https://downloads.php.net/~eric/php-8.3.0beta
bin/spc download --all -U "curl:https://curl.se/download/curl-7.88.1.tar.gz"
```
如果你下载的资源不是链接,而是一个 Git 仓库,你可以使用 `-G``--custom-git` 重写下载链接,让下载器强制使用你指定的 Git 仓库下载此 source 的包。
使用方法为 `{source-name}:{branch}:{url}` 即可,可同时重写多个库的下载地址。在使用 `--for-extensions` 选项下载时同样可用。
```bash
# 例如:下载 master 分支的 php-src
bin/spc download --for-extensions=redis,phar -G "php-src:master:https://github.com/php/php-src.git"
# 从 swoole-src 仓库下载 master 分支的最新代码,而不是发行版
bin/spc download --for-extensions=swoole -G "swoole:master:https://github.com/swoole/swoole-src.git"
```
## 命令 doctor - 环境检查
如果你可以正常运行 `bin/spc` 但无法正常编译静态的 PHP 或依赖库,可以先运行 `bin/spc doctor` 检查系统自身是否缺少依赖。
@@ -254,6 +265,8 @@ bin/spc build mysqlnd,pdo_mysql --build-all --debug
- `--enable-zts`: 让编译的 PHP 为线程安全版本(默认为 NTS 版本)
- `--no-strip`: 编译 PHP 库后不运行 `strip` 裁剪二进制文件缩小体积(不裁剪的 macOS 二进制文件可使用动态链接的第三方扩展)
- `--with-libs=XXX,YYY`: 编译 PHP 前先编译指定的依赖库,激活部分扩展的可选功能(例如 gd 库的 libavif 等)
- `--with-config-file-path=XXX` 查找 `php.ini` 的路径(在 [这里](../faq/index.html#php-ini-的路径是什么) 查看默认路径)
- `--with-config-file-scan-dir=XXX` 读取 `php.ini` 后扫描 `.ini` 文件的目录(在 [这里](../faq/index.html#php-ini-的路径是什么) 查看默认路径)
- `-I xxx=yyy`: 编译前将 INI 选项硬编译到 PHP 内(支持多个选项,别名是 `--with-hardcoded-ini`
- `--with-micro-fake-cli`: 在编译 micro 时,让 micro 的 SAPI 伪装为 `cli`(用于兼容一些检查 `PHP_SAPI` 的程序)
- `--disable-opcache-jit`: 禁用 opcache jit默认启用

View File

@@ -4,7 +4,6 @@ parameters:
paths:
- ./src/
ignoreErrors:
- '#Constant .* not found#'
- '#Unsafe usage of new static#'
- '#class Fiber#'
- '#Attribute class JetBrains\\PhpStorm\\ArrayShape does not exist#'
@@ -15,4 +14,4 @@ parameters:
analyseAndScan:
- ./src/globals/ext-tests/swoole.php
- ./src/globals/ext-tests/swoole.phpt
- ./src/globals/test-extensions.php
- ./src/globals/test-extensions.php

5
phpunit.xml.dist Normal file
View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<phpunit
bootstrap="tests/bootstrap.php"
>
</phpunit>

View File

@@ -9,7 +9,9 @@ use SPC\command\BuildLibsCommand;
use SPC\command\DeleteDownloadCommand;
use SPC\command\dev\AllExtCommand;
use SPC\command\dev\ExtVerCommand;
use SPC\command\dev\GenerateExtDepDocsCommand;
use SPC\command\dev\GenerateExtDocCommand;
use SPC\command\dev\GenerateLibDepDocsCommand;
use SPC\command\dev\LibVerCommand;
use SPC\command\dev\PackLibCommand;
use SPC\command\dev\PhpVerCommand;
@@ -28,12 +30,15 @@ use Symfony\Component\Console\Application;
*/
final class ConsoleApplication extends Application
{
public const VERSION = '2.3.2';
public const VERSION = '2.3.6';
public function __construct()
{
parent::__construct('static-php-cli', self::VERSION);
// Define internal env vars and constants
require_once ROOT_DIR . '/src/globals/internal-env.php';
$this->addCommands(
[
// Common commands
@@ -55,6 +60,8 @@ final class ConsoleApplication extends Application
new ExtVerCommand(),
new SortConfigCommand(),
new GenerateExtDocCommand(),
new GenerateExtDepDocsCommand(),
new GenerateLibDepDocsCommand(),
new PackLibCommand(),
]
);

View File

@@ -418,30 +418,6 @@ abstract class BuilderBase
}
}
/**
* Check if all libs are downloaded.
* If not, throw exception.
*
* @throws RuntimeException
*/
protected function checkLibsSource(): void
{
$not_downloaded = [];
foreach ($this->libs as $lib) {
if (!file_exists($lib->getSourceDir())) {
$not_downloaded[] = $lib::NAME;
}
}
if ($not_downloaded !== []) {
throw new RuntimeException(
'"' . implode(', ', $not_downloaded) .
'" totally ' . count($not_downloaded) .
' source' . (count($not_downloaded) === 1 ? '' : 's') .
' not downloaded, maybe you need to "fetch" ' . (count($not_downloaded) === 1 ? 'it' : 'them') . ' first?'
);
}
}
/**
* Generate micro extension test php code.
*/

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('gmssl')]
class gmssl extends Extension
{
public function patchBeforeBuildconf(): bool
{
if (str_contains(file_get_contents(SOURCE_PATH . '/php-src/ext/gmssl/config.w32'), 'CHECK_LIB(')) {
return false;
}
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/gmssl/config.w32', 'AC_DEFINE(', 'CHECK_LIB("gmssl.lib", "gmssl", PHP_GMSSL);' . PHP_EOL . 'AC_DEFINE(');
return true;
}
}

View File

@@ -7,6 +7,7 @@ namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\SourcePatcher;
use SPC\util\CustomExt;
#[CustomExt('opcache')]
@@ -23,6 +24,24 @@ class opcache extends Extension
}
}
public function patchBeforeBuildconf(): bool
{
if (file_exists(SOURCE_PATH . '/php-src/.opcache_patched')) {
return false;
}
// if 8.2.0 <= PHP_VERSION < 8.2.23, we need to patch from legacy patch file
if (version_compare($this->builder->getPHPVersion(), '8.2.0', '>=') && version_compare($this->builder->getPHPVersion(), '8.2.23', '<')) {
SourcePatcher::patchFile('spc_fix_static_opcache_before_80222.patch', SOURCE_PATH . '/php-src');
}
// if 8.3.0 <= PHP_VERSION < 8.3.11, we need to patch from legacy patch file
elseif (version_compare($this->builder->getPHPVersion(), '8.3.0', '>=') && version_compare($this->builder->getPHPVersion(), '8.3.11', '<')) {
SourcePatcher::patchFile('spc_fix_static_opcache_before_80310.patch', SOURCE_PATH . '/php-src');
} else {
SourcePatcher::patchMicro(items: ['static_opcache']);
}
return file_put_contents(SOURCE_PATH . '/php-src/.opcache_patched', '1') !== false;
}
public function getUnixConfigureArg(): string
{
return '--enable-opcache';

View File

@@ -89,6 +89,11 @@ class BSDBuilder extends UnixBuilderBase
$zts_enable = $this->getPHPVersionID() < 80000 ? '--enable-maintainer-zts --disable-zend-signals' : '--enable-zts --disable-zend-signals';
$zts = $this->getOption('enable-zts', false) ? $zts_enable : '';
$config_file_path = $this->getOption('with-config-file-path', false) ?
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
$enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
@@ -109,6 +114,8 @@ class BSDBuilder extends UnixBuilderBase
($enableFpm ? '--enable-fpm ' : '--disable-fpm ') .
($enableEmbed ? '--enable-embed=static ' : '--disable-embed ') .
($enableMicro ? '--enable-micro ' : '--disable-micro ') .
$config_file_path .
$config_file_scan_dir .
$json_74 .
$zts .
$this->makeExtensionArgs()

View File

@@ -35,7 +35,7 @@ class LinuxBuilder extends UnixBuilderBase
GlobalEnvManager::init($this);
if (str_ends_with(getenv('CC'), 'linux-musl-gcc') && !file_exists("/usr/local/musl/bin/{$arch}-linux-musl-gcc")) {
if (str_ends_with(getenv('CC'), 'linux-musl-gcc') && !file_exists("/usr/local/musl/bin/{$arch}-linux-musl-gcc") && (getenv('SPC_NO_MUSL_PATH') !== 'yes')) {
throw new WrongUsageException('musl-cross-make not installed, please install it first. (You can use `doctor` command to install it)');
}
@@ -134,6 +134,11 @@ class LinuxBuilder extends UnixBuilderBase
}
$disable_jit = $this->getOption('disable-opcache-jit', false) ? '--disable-opcache-jit ' : '';
$config_file_path = $this->getOption('with-config-file-path', false) ?
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
$enable_cli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
$enable_fpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enable_micro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
@@ -163,6 +168,8 @@ class LinuxBuilder extends UnixBuilderBase
($enable_fpm ? '--enable-fpm ' : '--disable-fpm ') .
($enable_embed ? '--enable-embed=static ' : '--disable-embed ') .
($enable_micro ? '--enable-micro=all-static ' : '--disable-micro ') .
$config_file_path .
$config_file_scan_dir .
$disable_jit .
$json_74 .
$zts .

View File

@@ -6,7 +6,6 @@ namespace SPC\builder\linux;
use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
class SystemUtil
{
@@ -83,52 +82,6 @@ class SystemUtil
};
}
/**
* @throws RuntimeException
* @throws WrongUsageException
* @throws WrongUsageException
*/
public static function getArchCFlags(string $cc, string $arch): string
{
if (php_uname('m') === $arch) {
return '';
}
return match (static::getCCType($cc)) {
'clang' => match ($arch) {
'x86_64' => '--target=x86_64-unknown-linux',
'arm64', 'aarch64' => '--target=arm64-unknown-linux',
default => throw new WrongUsageException('unsupported arch: ' . $arch),
},
'gcc' => '',
default => throw new WrongUsageException('cc compiler ' . $cc . ' is not supported'),
};
}
/**
* @throws RuntimeException
*/
public static function getTuneCFlags(string $arch): array
{
return match ($arch) {
'x86_64', 'arm64', 'aarch64' => [],
default => throw new RuntimeException('unsupported arch: ' . $arch),
};
}
public static function checkCCFlags(array $flags, string $cc): array
{
return array_filter($flags, fn ($flag) => static::checkCCFlag($flag, $cc));
}
public static function checkCCFlag(string $flag, string $cc): string
{
[$ret] = shell()->execWithResult("echo | {$cc} -E -x c - {$flag} 2>/dev/null");
if ($ret != 0) {
return '';
}
return $flag;
}
/**
* @throws RuntimeException
* @noinspection PhpUnused

View File

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

View File

@@ -52,7 +52,7 @@ class imap extends LinuxLibraryBase
->exec('chmod +x src/osdep/unix/drivers')
->exec('chmod +x src/osdep/unix/mkauths')
->exec(
"yes | make slx {$ssl_options}"
"yes | make slx {$ssl_options} EXTRACFLAGS='-fPIC -fpermissive'"
);
try {
shell()

View File

@@ -137,6 +137,11 @@ class MacOSBuilder extends UnixBuilderBase
$json_74 = $this->getPHPVersionID() < 80000 ? '--enable-json ' : '';
$zts = $this->getOption('enable-zts', false) ? '--enable-zts --disable-zend-signals ' : '';
$config_file_path = $this->getOption('with-config-file-path', false) ?
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
$enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
@@ -164,6 +169,8 @@ class MacOSBuilder extends UnixBuilderBase
($enableFpm ? '--enable-fpm ' : '--disable-fpm ') .
($enableEmbed ? '--enable-embed=static ' : '--disable-embed ') .
($enableMicro ? '--enable-micro ' : '--disable-micro ') .
$config_file_path .
$config_file_scan_dir .
$json_74 .
$zts .
$this->makeExtensionArgs() . ' ' .

View File

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

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
trait gmssl
{
/**
* @throws FileSystemException
* @throws RuntimeException
*/
protected function build(): void
{
// CMake needs a clean build directory
FileSystem::resetDir($this->source_dir . '/build');
// Start build
shell()->cd($this->source_dir . '/build')
->setEnv(['CFLAGS' => $this->getLibExtraCFlags(), 'LDFLAGS' => $this->getLibExtraLdFlags(), 'LIBS' => $this->getLibExtraLibs()])
->execWithEnv("cmake {$this->builder->makeCmakeArgs()} -DBUILD_SHARED_LIBS=OFF ..")
->execWithEnv("cmake --build . -j {$this->builder->concurrency}")
->execWithEnv('make install DESTDIR=' . BUILD_ROOT_PATH);
}
}

View File

@@ -21,7 +21,7 @@ class SystemUtil
if (!$paths) {
$paths = explode(PATH_SEPARATOR, getenv('Path'));
if ($include_sdk_bin) {
$paths[] = PHP_SDK_PATH . '\bin';
$paths[] = getenv('PHP_SDK_PATH') . '\bin';
}
}
foreach ($paths as $path) {

View File

@@ -38,7 +38,7 @@ class WindowsBuilder extends BuilderBase
// ---------- set necessary options ----------
// set sdk (require visual studio 16 or 17)
$vs = SystemUtil::findVisualStudio()['version'];
$this->sdk_prefix = PHP_SDK_PATH . "\\phpsdk-{$vs}-x64.bat -t";
$this->sdk_prefix = getenv('PHP_SDK_PATH') . "\\phpsdk-{$vs}-x64.bat -t";
// set zts
$this->zts = $this->getOption('enable-zts', false);
@@ -103,6 +103,9 @@ class WindowsBuilder extends BuilderBase
$micro_w32 = $this->getOption('enable-micro-win32') ? ' --enable-micro-win32=yes' : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
cmd()->cd(SOURCE_PATH . '\php-src')
->exec(
"{$this->sdk_prefix} configure.bat --task-args \"" .
@@ -114,6 +117,7 @@ class WindowsBuilder extends BuilderBase
($enableCli ? '--enable-cli=yes ' : '--enable-cli=no ') .
($enableMicro ? ('--enable-micro=yes ' . $micro_logo . $micro_w32) : '--enable-micro=no ') .
($enableEmbed ? '--enable-embed=yes ' : '--enable-embed=no ') .
$config_file_scan_dir .
"{$this->makeExtensionArgs()} " .
$zts .
'"'

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\store\FileSystem;
class gmssl extends WindowsLibraryBase
{
public const NAME = 'gmssl';
protected function build(): void
{
// reset cmake
FileSystem::resetDir($this->source_dir . '\builddir');
// start build
cmd()->cd($this->source_dir . '\builddir')
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake .. -G "NMake Makefiles" -DWIN32=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG" -DCMAKE_CXX_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG" -DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH),
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH
);
FileSystem::writeFile($this->source_dir . '\builddir\cmake_install.cmake', 'set(CMAKE_INSTALL_PREFIX "' . str_replace('\\', '/', BUILD_ROOT_PATH) . '")' . PHP_EOL . FileSystem::readFile($this->source_dir . '\builddir\cmake_install.cmake'));
cmd()->cd($this->source_dir . '\builddir')
->execWithWrapper(
$this->builder->makeSimpleWrapper('nmake'),
'install XCFLAGS=/MT'
);
}
}

View File

@@ -32,7 +32,7 @@ class xz extends WindowsLibraryBase
);
// copy liblzma.lib to liblzma_a.lib
copy(BUILD_LIB_PATH . '/liblzma.lib', BUILD_LIB_PATH . '/liblzma_a.lib');
copy(BUILD_LIB_PATH . '/lzma.lib', BUILD_LIB_PATH . '/liblzma_a.lib');
// patch lzma.h
FileSystem::replaceFileStr(BUILD_INCLUDE_PATH . '/lzma.h', 'defined(LZMA_API_STATIC)', 'defined(_WIN32)');
}

View File

@@ -10,6 +10,7 @@ use Psr\Log\LogLevel;
use SPC\ConsoleApplication;
use SPC\exception\ExceptionHandler;
use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
@@ -94,6 +95,11 @@ abstract class BaseCommand extends Command
$question = new ConfirmationQuestion($prompt->label . $case, $prompt->default);
return $helper->ask($input, $output, $question);
});
// init GlobalEnv
if (!$this instanceof BuildCommand) {
GlobalEnvManager::init();
}
if ($this->shouldExecute()) {
try {
// show raw argv list for logger()->debug

View File

@@ -22,6 +22,8 @@ class BuildCliCommand extends BuildCommand
{
public function configure(): void
{
$isWindows = PHP_OS_FAMILY === 'Windows';
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated');
$this->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', '');
$this->addOption('build-micro', null, null, 'Build micro SAPI');
@@ -32,6 +34,8 @@ class BuildCliCommand extends BuildCommand
$this->addOption('no-strip', null, null, 'build without strip, in order to debug and load external extensions');
$this->addOption('enable-zts', null, null, 'enable ZTS support');
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');
$this->addOption('with-config-file-path', null, InputOption::VALUE_REQUIRED, 'Set the path in which to look for php.ini', $isWindows ? null : '/usr/local/etc/php');
$this->addOption('with-config-file-scan-dir', null, InputOption::VALUE_REQUIRED, 'Set the directory to scan for .ini files after reading php.ini', $isWindows ? null : '/usr/local/etc/php/conf.d');
$this->addOption('with-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, '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');
@@ -114,6 +118,9 @@ class BuildCliCommand extends BuildCommand
'Strip Binaries' => $builder->getOption('no-strip') ? 'no' : 'yes',
'Enable ZTS' => $builder->getOption('enable-zts') ? 'yes' : 'no',
];
if (!empty($this->input->getOption('with-config-file-path'))) {
$indent_texts['Config File Path'] = $this->input->getOption('with-config-file-path');
}
if (!empty($this->input->getOption('with-hardcoded-ini'))) {
$indent_texts['Hardcoded INI'] = $this->input->getOption('with-hardcoded-ini');
}

View File

@@ -23,6 +23,8 @@ class DoctorCommand extends BaseCommand
{
try {
$checker = new CheckListHandler();
// skipped items
$skip_items = array_filter(explode(',', getenv('SPC_SKIP_DOCTOR_CHECK_ITEMS') ?: ''));
$fix_policy = $this->input->getOption('auto-fix') ? FIX_POLICY_AUTOFIX : FIX_POLICY_PROMPT;
foreach ($checker->runChecks() as $check) {
@@ -32,13 +34,12 @@ class DoctorCommand extends BaseCommand
$this->output->write('Checking <comment>' . $check->item_name . '</comment> ... ');
$result = call_user_func($check->callback);
if ($result === null) {
// check if this item is skipped
if (in_array($check->item_name, $skip_items) || ($result = call_user_func($check->callback)) === null) {
$this->output->writeln('skipped');
} elseif ($result instanceof CheckResult) {
if ($result->isOK()) {
$this->output->writeln($result->getMessage() ?? 'ok');
continue;
}

View File

@@ -34,6 +34,7 @@ class DownloadCommand extends BaseCommand
$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('custom-git', 'G', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Specify custom source git url, e.g "php-src:master:https://github.com/php/php-src.git"');
$this->addOption('from-zip', 'Z', InputOption::VALUE_REQUIRED, 'Fetch from zip archive');
$this->addOption('for-extensions', 'e', InputOption::VALUE_REQUIRED, 'Fetch by extensions, e.g "openssl,mbstring"');
$this->addOption('for-libs', 'l', InputOption::VALUE_REQUIRED, 'Fetch by libraries, e.g "libcares,openssl,onig"');
@@ -123,10 +124,15 @@ class DownloadCommand extends BaseCommand
// Define PHP major version
$ver = $this->php_major_ver = $this->getOption('with-php') ?? '8.1';
define('SPC_BUILD_PHP_VERSION', $ver);
// match x.y
preg_match('/^\d+\.\d+$/', $ver, $matches);
if (!$matches) {
logger()->error("bad version arg: {$ver}, x.y required!");
return static::FAILURE;
// match x.y.z
preg_match('/^\d+\.\d+\.\d+$/', $ver, $matches);
if (!$matches) {
logger()->error("bad version arg: {$ver}, x.y or x.y.z required!");
return static::FAILURE;
}
}
// retry
@@ -174,6 +180,12 @@ class DownloadCommand extends BaseCommand
[$source_name, $url] = explode(':', $value, 2);
$custom_urls[$source_name] = $url;
}
// Process -G options
$custom_gits = [];
foreach ($this->input->getOption('custom-git') as $value) {
[$source_name, $branch, $url] = explode(':', $value, 3);
$custom_gits[$source_name] = [$branch, $url];
}
// If passing --prefer-pre-built option, we need to load pre-built library list from pre-built.json targeted releases
if ($this->getOption('prefer-pre-built')) {
@@ -206,6 +218,18 @@ class DownloadCommand extends BaseCommand
}
logger()->info("Fetching source {$source} from custom url [{$ni}/{$cnt}]");
Downloader::downloadSource($source, $new_config, true);
} elseif (isset($custom_gits[$source])) {
$config = Config::getSource($source);
$new_config = [
'type' => 'git',
'rev' => $custom_gits[$source][0],
'url' => $custom_gits[$source][1],
];
if (isset($config['path'])) {
$new_config['path'] = $config['path'];
}
logger()->info("Fetching source {$source} from custom git [{$ni}/{$cnt}]");
Downloader::downloadSource($source, $new_config, true);
} else {
$config = Config::getSource($source);
// Prefer pre-built, we need to search pre-built library

View File

@@ -32,8 +32,12 @@ class SwitchPhpVersionCommand extends BaseCommand
{
$php_ver = $this->input->getArgument('php-major-version');
if (!in_array($php_ver, ['7.4', '8.0', '8.1', '8.2', '8.3'])) {
$this->output->writeln('<error>Invalid PHP major version ' . $php_ver . ' !</error>');
return static::FAILURE;
// match x.y.z
preg_match('/^\d+\.\d+\.\d+$/', $php_ver, $matches);
if (!$matches) {
$this->output->writeln('<error>Invalid PHP version ' . $php_ver . ' !</error>');
return static::FAILURE;
}
}
// detect if downloads/.lock.json exists

View File

@@ -0,0 +1,166 @@
<?php
declare(strict_types=1);
namespace SPC\command\dev;
use SPC\command\BaseCommand;
use SPC\store\FileSystem;
use SPC\util\ConfigValidator;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand('dev:gen-ext-dep-docs', 'Generate ext dependency map markdown', [], true)]
class GenerateExtDepDocsCommand extends BaseCommand
{
protected bool $no_motd = true;
public function handle(): int
{
// Get ext.json
$exts = json_decode(FileSystem::readFile(ROOT_DIR . '/config/ext.json'), true);
ConfigValidator::validateExts($exts);
// Markdown table needs format, we need to calculate the max length of each column
$content = '';
// Calculate table column max length
$max_linux = [0, 20, 19, 20, 19];
$max_macos = [0, 20, 19, 20, 19];
$max_windows = [0, 20, 19, 20, 19];
$max_freebsd = [0, 20, 19, 20, 19];
$md_lines_linux = [];
$md_lines_macos = [];
$md_lines_windows = [];
$md_lines_freebsd = [];
foreach ($exts as $ext_name => $ext) {
$line_linux = [
"<b>{$ext_name}</b>",
implode('<br>', $ext['ext-depends-linux'] ?? $ext['ext-depends-unix'] ?? $ext['ext-depends'] ?? []),
implode('<br>', $ext['ext-suggests-linux'] ?? $ext['ext-suggests-unix'] ?? $ext['ext-suggests'] ?? []),
implode('<br>', $ext['lib-depends-linux'] ?? $ext['lib-depends-unix'] ?? $ext['lib-depends'] ?? []),
implode('<br>', $ext['lib-suggests-linux'] ?? $ext['lib-suggests-unix'] ?? $ext['lib-suggests'] ?? []),
];
$this->applyMaxLen($max_linux, $line_linux);
if ($this->isSupported($ext, 'Linux') && !$this->isEmptyLine($line_linux)) {
$md_lines_linux[] = $line_linux;
}
$line_macos = [
"<b>{$ext_name}</b>",
implode('<br>', $ext['ext-depends-macos'] ?? $ext['ext-depends-unix'] ?? $ext['ext-depends'] ?? []),
implode('<br>', $ext['ext-suggests-macos'] ?? $ext['ext-suggests-unix'] ?? $ext['ext-suggests'] ?? []),
implode('<br>', $ext['lib-depends-macos'] ?? $ext['lib-depends-unix'] ?? $ext['lib-depends'] ?? []),
implode('<br>', $ext['lib-suggests-macos'] ?? $ext['lib-suggests-unix'] ?? $ext['lib-suggests'] ?? []),
];
$this->applyMaxLen($max_macos, $line_macos);
if ($this->isSupported($ext, 'macOS') && !$this->isEmptyLine($line_macos)) {
$md_lines_macos[] = $line_macos;
}
$line_windows = [
"<b>{$ext_name}</b>",
implode('<br>', $ext['ext-depends-windows'] ?? $ext['ext-depends-win'] ?? $ext['ext-depends'] ?? []),
implode('<br>', $ext['ext-suggests-windows'] ?? $ext['ext-suggests-win'] ?? $ext['ext-suggests'] ?? []),
implode('<br>', $ext['lib-depends-windows'] ?? $ext['lib-depends-win'] ?? $ext['lib-depends'] ?? []),
implode('<br>', $ext['lib-suggests-windows'] ?? $ext['lib-suggests-win'] ?? $ext['lib-suggests'] ?? []),
];
$this->applyMaxLen($max_windows, $line_windows);
if ($this->isSupported($ext, 'Windows') && !$this->isEmptyLine($line_windows)) {
$md_lines_windows[] = $line_windows;
}
$line_freebsd = [
"<b>{$ext_name}</b>",
implode('<br>', $ext['ext-depends-freebsd'] ?? $ext['ext-depends-bsd'] ?? $ext['ext-depends-unix'] ?? $ext['ext-depends'] ?? []),
implode('<br>', $ext['ext-suggests-freebsd'] ?? $ext['ext-suggests-bsd'] ?? $ext['ext-suggests-unix'] ?? $ext['ext-suggests'] ?? []),
implode('<br>', $ext['lib-depends-freebsd'] ?? $ext['lib-depends-bsd'] ?? $ext['lib-depends-unix'] ?? $ext['lib-depends'] ?? []),
implode('<br>', $ext['lib-suggests-freebsd'] ?? $ext['lib-suggests-bsd'] ?? $ext['lib-suggests-unix'] ?? $ext['lib-suggests'] ?? []),
];
$this->applyMaxLen($max_freebsd, $line_freebsd);
if ($this->isSupported($ext, 'BSD') && !$this->isEmptyLine($line_freebsd)) {
$md_lines_freebsd[] = $line_freebsd;
}
}
// Generate markdown
if (!empty($md_lines_linux)) {
$content .= "### Linux\n\n";
$content .= '| ';
$pads = ['Extension Name', 'Required Extensions', 'Suggested Extensions', 'Required Libraries', 'Suggested Libraries'];
// 生成首行
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad($pad, $max_linux[$i]), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
// 生成第二行表格分割符 | --- | --- | --- | --- | --- |
$content .= '| ';
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad('', $max_linux[$i], '-'), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
foreach ($md_lines_linux as $line) {
$content .= '| ' . implode(' | ', array_map(fn ($i, $pad) => str_pad($line[$i], $max_linux[$i]), array_keys($line), $line)) . ' |' . PHP_EOL;
}
}
if (!empty($md_lines_macos)) {
$content .= "\n\n### macOS\n\n";
$content .= '| ';
$pads = ['Extension Name', 'Required Extensions', 'Suggested Extensions', 'Required Libraries', 'Suggested Libraries'];
// 生成首行
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad($pad, $max_macos[$i]), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
// 生成第二行表格分割符 | --- | --- | --- | --- | --- |
$content .= '| ';
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad('', $max_macos[$i], '-'), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
foreach ($md_lines_macos as $line) {
$content .= '| ' . implode(' | ', array_map(fn ($i, $pad) => str_pad($line[$i], $max_macos[$i]), array_keys($line), $line)) . ' |' . PHP_EOL;
}
}
if (!empty($md_lines_windows)) {
$content .= "\n\n### Windows\n\n";
$content .= '| ';
$pads = ['Extension Name', 'Required Extensions', 'Suggested Extensions', 'Required Libraries', 'Suggested Libraries'];
// 生成首行
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad($pad, $max_windows[$i]), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
// 生成第二行表格分割符 | --- | --- | --- | --- | --- |
$content .= '| ';
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad('', $max_windows[$i], '-'), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
foreach ($md_lines_windows as $line) {
$content .= '| ' . implode(' | ', array_map(fn ($i, $pad) => str_pad($line[$i], $max_windows[$i]), array_keys($line), $line)) . ' |' . PHP_EOL;
}
}
if (!empty($md_lines_freebsd)) {
$content .= "\n\n### FreeBSD\n\n";
$content .= '| ';
$pads = ['Extension Name', 'Required Extensions', 'Suggested Extensions', 'Required Libraries', 'Suggested Libraries'];
// 生成首行
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad($pad, $max_freebsd[$i]), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
// 生成第二行表格分割符 | --- | --- | --- | --- | --- |
$content .= '| ';
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad('', $max_freebsd[$i], '-'), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
foreach ($md_lines_freebsd as $line) {
$content .= '| ' . implode(' | ', array_map(fn ($i, $pad) => str_pad($line[$i], $max_freebsd[$i]), array_keys($line), $line)) . ' |' . PHP_EOL;
}
}
$this->output->writeln($content);
return static::SUCCESS;
}
private function applyMaxLen(array &$max, array $lines): void
{
foreach ($max as $k => $v) {
$max[$k] = max($v, strlen($lines[$k]));
}
}
private function isSupported(array $ext, string $os): bool
{
return !in_array($ext['support'][$os] ?? 'yes', ['no', 'wip']);
}
private function isEmptyLine(array $line): bool
{
return $line[1] === '' && $line[2] === '' && $line[3] === '' && $line[4] === '';
}
}

View File

@@ -0,0 +1,172 @@
<?php
declare(strict_types=1);
namespace SPC\command\dev;
use SPC\command\BaseCommand;
use SPC\store\Config;
use SPC\store\FileSystem;
use SPC\util\ConfigValidator;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand('dev:gen-lib-dep-docs', 'Generate lib dependency map markdown', [], true)]
class GenerateLibDepDocsCommand extends BaseCommand
{
protected bool $no_motd = true;
protected array $support_lib_list = [];
public function handle(): int
{
foreach (['linux', 'macos', 'windows', 'freebsd'] as $os) {
$this->support_lib_list[$os] = [];
$classes = FileSystem::getClassesPsr4(
FileSystem::convertPath(ROOT_DIR . '/src/SPC/builder/' . $os . '/library'),
'SPC\builder\\' . $os . '\library'
);
foreach ($classes as $class) {
if (defined($class . '::NAME') && $class::NAME !== 'unknown' && Config::getLib($class::NAME) !== null) {
$this->support_lib_list[$os][$class::NAME] = $class;
}
}
}
// Get lib.json
$libs = json_decode(FileSystem::readFile(ROOT_DIR . '/config/lib.json'), true);
ConfigValidator::validateLibs($libs);
// Markdown table needs format, we need to calculate the max length of each column
$content = '';
// Calculate table column max length
$max_linux = [0, 20, 19];
$max_macos = [0, 20, 19];
$max_windows = [0, 20, 19];
$max_freebsd = [0, 20, 19];
$md_lines_linux = [];
$md_lines_macos = [];
$md_lines_windows = [];
$md_lines_freebsd = [];
foreach ($libs as $lib_name => $lib) {
$line_linux = [
"<b>{$lib_name}</b>",
implode('<br>', $lib['lib-depends-linux'] ?? $lib['lib-depends-unix'] ?? $lib['lib-depends'] ?? []),
implode('<br>', $lib['lib-suggests-linux'] ?? $lib['lib-suggests-unix'] ?? $lib['lib-suggests'] ?? []),
];
$this->applyMaxLen($max_linux, $line_linux);
if ($this->isSupported($lib_name, 'linux') && !$this->isEmptyLine($line_linux)) {
$md_lines_linux[] = $line_linux;
}
$line_macos = [
"<b>{$lib_name}</b>",
implode('<br>', $lib['lib-depends-macos'] ?? $lib['lib-depends-unix'] ?? $lib['lib-depends'] ?? []),
implode('<br>', $lib['lib-suggests-macos'] ?? $lib['lib-suggests-unix'] ?? $lib['lib-suggests'] ?? []),
];
$this->applyMaxLen($max_macos, $line_macos);
if ($this->isSupported($lib_name, 'macos') && !$this->isEmptyLine($line_macos)) {
$md_lines_macos[] = $line_macos;
}
$line_windows = [
"<b>{$lib_name}</b>",
implode('<br>', $lib['lib-depends-windows'] ?? $lib['lib-depends-win'] ?? $lib['lib-depends'] ?? []),
implode('<br>', $lib['lib-suggests-windows'] ?? $lib['lib-suggests-win'] ?? $lib['lib-suggests'] ?? []),
];
$this->applyMaxLen($max_windows, $line_windows);
if ($this->isSupported($lib_name, 'windows') && !$this->isEmptyLine($line_windows)) {
$md_lines_windows[] = $line_windows;
}
$line_freebsd = [
"<b>{$lib_name}</b>",
implode('<br>', $lib['lib-depends-freebsd'] ?? $lib['lib-depends-bsd'] ?? $lib['lib-depends-unix'] ?? $lib['lib-depends'] ?? []),
implode('<br>', $lib['lib-suggests-freebsd'] ?? $lib['lib-suggests-bsd'] ?? $lib['lib-suggests-unix'] ?? $lib['lib-suggests'] ?? []),
];
$this->applyMaxLen($max_freebsd, $line_freebsd);
if ($this->isSupported($lib_name, 'freebsd') && !$this->isEmptyLine($line_freebsd)) {
$md_lines_freebsd[] = $line_freebsd;
}
}
// Generate markdown
if (!empty($md_lines_linux)) {
$content .= "### Linux\n\n";
$content .= '| ';
$pads = ['Library Name', 'Required Libraries', 'Suggested Libraries'];
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad($pad, $max_linux[$i]), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
$content .= '| ';
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad('', $max_linux[$i], '-'), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
foreach ($md_lines_linux as $line) {
$content .= '| ' . implode(' | ', array_map(fn ($i, $pad) => str_pad($line[$i], $max_linux[$i]), array_keys($line), $line)) . ' |' . PHP_EOL;
}
}
if (!empty($md_lines_macos)) {
$content .= "### macOS\n\n";
$content .= '| ';
$pads = ['Library Name', 'Required Libraries', 'Suggested Libraries'];
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad($pad, $max_macos[$i]), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
$content .= '| ';
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad('', $max_macos[$i], '-'), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
foreach ($md_lines_macos as $line) {
$content .= '| ' . implode(' | ', array_map(fn ($i, $pad) => str_pad($line[$i], $max_macos[$i]), array_keys($line), $line)) . ' |' . PHP_EOL;
}
}
if (!empty($md_lines_windows)) {
$content .= "### Windows\n\n";
$content .= '| ';
$pads = ['Library Name', 'Required Libraries', 'Suggested Libraries'];
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad($pad, $max_windows[$i]), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
$content .= '| ';
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad('', $max_windows[$i], '-'), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
foreach ($md_lines_windows as $line) {
$content .= '| ' . implode(' | ', array_map(fn ($i, $pad) => str_pad($line[$i], $max_windows[$i]), array_keys($line), $line)) . ' |' . PHP_EOL;
}
}
if (!empty($md_lines_freebsd)) {
$content .= "### FreeBSD\n\n";
$content .= '| ';
$pads = ['Library Name', 'Required Libraries', 'Suggested Libraries'];
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad($pad, $max_freebsd[$i]), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
$content .= '| ';
$content .= implode(' | ', array_map(fn ($i, $pad) => str_pad('', $max_freebsd[$i], '-'), array_keys($pads), $pads));
$content .= ' |' . PHP_EOL;
foreach ($md_lines_freebsd as $line) {
$content .= '| ' . implode(' | ', array_map(fn ($i, $pad) => str_pad($line[$i], $max_freebsd[$i]), array_keys($line), $line)) . ' |' . PHP_EOL;
}
}
$this->output->writeln($content);
return static::SUCCESS;
}
private function applyMaxLen(array &$max, array $lines): void
{
foreach ($max as $k => $v) {
$max[$k] = max($v, strlen($lines[$k]));
}
}
private function isSupported(string $ext_name, string $os): bool
{
if (!in_array($os, ['linux', 'macos', 'freebsd', 'windows'])) {
throw new \InvalidArgumentException('Invalid os: ' . $os);
}
return isset($this->support_lib_list[$os][$ext_name]);
}
private function isEmptyLine(array $line): bool
{
return $line[1] === '' && $line[2] === '';
}
}

View File

@@ -5,8 +5,12 @@ declare(strict_types=1);
namespace SPC\command\dev;
use SPC\builder\BuilderProvider;
use SPC\builder\LibraryBase;
use SPC\command\BuildCommand;
use SPC\exception\ExceptionHandler;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\FileSystem;
use SPC\util\DependencyUtil;
@@ -53,6 +57,8 @@ class PackLibCommand extends BuildCommand
$lib->tryBuild(true);
// do something like patching pkg-conf files.
$lib->beforePack();
// sanity check for libs (check if the libraries are built correctly)
$this->sanityCheckLib($lib);
// After build: load buildroot/ directory, and calculate increase files
$after_buildroot = FileSystem::scanDirFiles(BUILD_ROOT_PATH, relative: true);
$increase_files = array_diff($after_buildroot, $before_buildroot);
@@ -82,4 +88,20 @@ class PackLibCommand extends BuildCommand
return static::FAILURE;
}
}
/**
* @throws WrongUsageException
* @throws RuntimeException
* @throws FileSystemException
*/
private function sanityCheckLib(LibraryBase $lib): void
{
logger()->info('Sanity check for library ' . $lib->getName());
// config
foreach ($lib->getStaticLibs() as $static_lib) {
if (!file_exists(FileSystem::convertPath(BUILD_LIB_PATH . '/' . $static_lib))) {
throw new RuntimeException('Static library ' . $static_lib . ' not found in ' . BUILD_LIB_PATH);
}
}
}
}

View File

@@ -73,9 +73,9 @@ class LinuxMuslCheck
FileSystem::extractSource($musl_version_name, DOWNLOAD_PATH . "/{$musl_version_name}.tar.gz");
logger()->info('Installing musl wrapper');
shell()->cd(SOURCE_PATH . "/{$musl_version_name}")
->exec('./configure --disable-gcc-wrapper')
->exec('make -j')
->exec("{$prefix}make install");
->exec('CC=gcc CXX=g++ AR=ar LD=ld ./configure --disable-gcc-wrapper')
->exec('CC=gcc CXX=g++ AR=ar LD=ld make -j')
->exec("CC=gcc CXX=g++ AR=ar LD=ld {$prefix}make install");
// TODO: add path using putenv instead of editing /etc/profile
return true;
} catch (RuntimeException) {

View File

@@ -37,10 +37,10 @@ class WindowsToolCheckList
#[AsCheckItem('if php-sdk-binary-tools are downloaded', limit_os: 'Windows', level: 997)]
public function checkSDK(): ?CheckResult
{
if (!file_exists(PHP_SDK_PATH . DIRECTORY_SEPARATOR . 'phpsdk-starter.bat')) {
if (!file_exists(getenv('PHP_SDK_PATH') . DIRECTORY_SEPARATOR . 'phpsdk-starter.bat')) {
return CheckResult::fail('php-sdk-binary-tools not downloaded', 'install-php-sdk');
}
return CheckResult::ok(PHP_SDK_PATH);
return CheckResult::ok(getenv('PHP_SDK_PATH'));
}
#[AsCheckItem('if git associated command exists', limit_os: 'Windows', level: 996)]
@@ -81,8 +81,8 @@ class WindowsToolCheckList
public function installPhpSdk(): bool
{
try {
FileSystem::removeDir(PHP_SDK_PATH);
cmd(true)->exec('git.exe clone --depth 1 https://github.com/php/php-sdk-binary-tools.git ' . PHP_SDK_PATH);
FileSystem::removeDir(getenv('PHP_SDK_PATH'));
cmd(true)->exec('git.exe clone --depth 1 https://github.com/php/php-sdk-binary-tools.git ' . getenv('PHP_SDK_PATH'));
} catch (RuntimeException) {
return false;
}

View File

@@ -20,11 +20,17 @@ class CurlHook
}
if (getenv('GITHUB_USER')) {
$auth = base64_encode(getenv('GITHUB_USER') . ':' . getenv('GITHUB_TOKEN'));
$headers[] = "Authorization: Basic {$auth}";
$he = "Authorization: Basic {$auth}";
if (!in_array($he, $headers)) {
$headers[] = $he;
}
logger()->info("using basic github token for {$method} {$url}");
} else {
$auth = getenv('GITHUB_TOKEN');
$headers[] = "Authorization: Bearer {$auth}";
$he = "Authorization: Bearer {$auth}";
if (!in_array($he, $headers)) {
$headers[] = $he;
}
logger()->info("using bearer github token for {$method} {$url}");
}
}

View File

@@ -182,6 +182,7 @@ class Downloader
*
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public static function downloadFile(string $name, string $url, string $filename, ?string $move_path = null, int $lock_as = SPC_LOCK_SOURCE): void
{
@@ -220,6 +221,7 @@ class Downloader
*
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public static function downloadGit(string $name, string $url, string $branch, ?string $move_path = null, int $retry = 0, int $lock_as = SPC_LOCK_SOURCE): void
{
@@ -381,6 +383,7 @@ class Downloader
* @param int $lock_as Lock source type (default: SPC_LOCK_SOURCE)
* @throws DownloaderException
* @throws FileSystemException
* @throws WrongUsageException
*/
public static function downloadSource(string $name, ?array $source = null, bool $force = false, int $lock_as = SPC_LOCK_SOURCE): void
{
@@ -539,6 +542,7 @@ class Downloader
* Use curl to download sources from url
*
* @throws RuntimeException
* @throws WrongUsageException
*/
public static function curlDown(string $url, string $path, string $method = 'GET', array $headers = [], array $hooks = [], int $retry = 0): void
{

View File

@@ -428,7 +428,7 @@ class FileSystem
{
$replacement = [
'{pkg_root_path}' => PKG_ROOT_PATH,
'{php_sdk_path}' => defined('PHP_SDK_PATH') ? PHP_SDK_PATH : WORKING_DIR . '/php-sdk-binary-tools',
'{php_sdk_path}' => getenv('PHP_SDK_PATH') ? getenv('PHP_SDK_PATH') : WORKING_DIR . '/php-sdk-binary-tools',
'{working_dir}' => WORKING_DIR,
'{download_path}' => DOWNLOAD_PATH,
'{source_path}' => SOURCE_PATH,
@@ -480,7 +480,7 @@ class FileSystem
};
} elseif (PHP_OS_FAMILY === 'Windows') {
// use php-sdk-binary-tools/bin/7za.exe
$_7z = self::convertPath(PHP_SDK_PATH . '/bin/7za.exe');
$_7z = self::convertPath(getenv('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].

View File

@@ -88,7 +88,7 @@ class SourcePatcher
* @throws RuntimeException
* @throws FileSystemException
*/
public static function patchMicro(): bool
public static function patchMicro(string $name = '', string $target = '', ?array $items = null): bool
{
if (!file_exists(SOURCE_PATH . '/php-src/sapi/micro/php_micro.c')) {
return false;
@@ -108,22 +108,13 @@ class SourcePatcher
// $check = !defined('DEBUG_MODE') ? ' -q' : '';
// f_passthru('cd ' . SOURCE_PATH . '/php-src && git checkout' . $check . ' HEAD');
$default = [
'static_opcache',
'static_extensions_win32',
'cli_checks',
'disable_huge_page',
'vcruntime140',
'win32',
'zend_stream',
];
if (PHP_OS_FAMILY === 'Windows') {
$default[] = 'cli_static';
if ($items !== null) {
$spc_micro_patches = $items;
} else {
$spc_micro_patches = getenv('SPC_MICRO_PATCHES');
$spc_micro_patches = $spc_micro_patches === false ? [] : explode(',', $spc_micro_patches);
}
if (PHP_OS_FAMILY === 'Darwin') {
$default[] = 'macos_iconv';
}
$patch_list = $default;
$patch_list = $spc_micro_patches;
$patches = [];
$serial = ['80', '81', '82', '83', '84'];
foreach ($patch_list as $patchName) {
@@ -142,12 +133,14 @@ class SourcePatcher
throw new RuntimeException("failed finding patch {$patchName}");
}
$patchesStr = str_replace('/', DIRECTORY_SEPARATOR, implode(' ', $patches));
f_passthru(
'cd ' . SOURCE_PATH . '/php-src && ' .
(PHP_OS_FAMILY === 'Windows' ? 'type' : 'cat') . ' ' . $patchesStr . ' | patch -p1 '
);
foreach ($patches as $patch) {
logger()->info("Patching micro with {$patch}");
$patchesStr = str_replace('/', DIRECTORY_SEPARATOR, $patch);
f_passthru(
'cd ' . SOURCE_PATH . '/php-src && ' .
(PHP_OS_FAMILY === 'Windows' ? 'type' : 'cat') . ' ' . $patchesStr . ' | patch -p1 '
);
}
return true;
}
@@ -205,6 +198,19 @@ class SourcePatcher
'PHP_ADD_INCLUDE([$ext_srcdir])',
"PHP_ADD_INCLUDE( [\$ext_srcdir] )\n PHP_ADD_INCLUDE([\$abs_srcdir/ext])"
);
// swoole 5.1.3 build fix
// get swoole version first
$file = SOURCE_PATH . '/php-src/ext/swoole/include/swoole_version.h';
// Match #define SWOOLE_VERSION "5.1.3"
$pattern = '/#define SWOOLE_VERSION "(.+)"/';
if (preg_match($pattern, file_get_contents($file), $matches)) {
$version = $matches[1];
} else {
$version = '1.0.0';
}
if ($version === '5.1.3') {
self::patchFile('spc_fix_swoole_50513.patch', SOURCE_PATH . '/php-src/ext/swoole');
}
return true;
}
@@ -401,14 +407,17 @@ class SourcePatcher
if (preg_match('/PHP_VERSION_ID (\d+)/', $file, $match) !== 0) {
$ver_id = intval($match[1]);
if ($ver_id < 80000) {
return false;
self::patchFile('spc_fix_alpine_build_php80.patch', SOURCE_PATH . '/php-src');
return true;
}
if ($ver_id < 80100) {
self::patchFile('spc_fix_libxml2_12_php80.patch', SOURCE_PATH . '/php-src');
self::patchFile('spc_fix_alpine_build_php80.patch', SOURCE_PATH . '/php-src');
return true;
}
if ($ver_id < 80200) {
self::patchFile('spc_fix_libxml2_12_php81.patch', SOURCE_PATH . '/php-src');
self::patchFile('spc_fix_alpine_build_php80.patch', SOURCE_PATH . '/php-src');
return true;
}
return false;

View File

@@ -20,7 +20,11 @@ class PhpSource extends CustomSourceBase
public function fetch(bool $force = false, ?array $config = null, int $lock_as = SPC_LOCK_SOURCE): void
{
$major = defined('SPC_BUILD_PHP_VERSION') ? SPC_BUILD_PHP_VERSION : '8.1';
Downloader::downloadSource('php-src', self::getLatestPHPInfo($major), $force);
if ($major === '8.4') {
Downloader::downloadSource('php-src', ['type' => 'url', 'url' => 'https://downloads.php.net/~saki/php-8.4.0RC1.tar.xz'], $force);
} else {
Downloader::downloadSource('php-src', self::getLatestPHPInfo($major), $force);
}
}
/**

View File

@@ -5,11 +5,9 @@ declare(strict_types=1);
namespace SPC\util;
use SPC\builder\BuilderBase;
use SPC\builder\freebsd\SystemUtil as BSDSystemUtil;
use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
use SPC\builder\macos\SystemUtil as MacOSSystemUtil;
use SPC\builder\windows\SystemUtil as WindowsSystemUtil;
use SPC\builder\linux\SystemUtil;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
/**
* Environment variable manager
@@ -26,164 +24,87 @@ class GlobalEnvManager
/**
* Initialize the environment variables
*
* @param BuilderBase $builder Builder
* @param null|BuilderBase $builder Builder
* @throws RuntimeException
* @throws WrongUsageException
*/
public static function init(BuilderBase $builder): void
public static function init(?BuilderBase $builder = null): void
{
// Init global env, build related path
self::putenv('BUILD_ROOT_PATH=' . BUILD_ROOT_PATH);
self::putenv('BUILD_INCLUDE_PATH=' . BUILD_INCLUDE_PATH);
self::putenv('BUILD_LIB_PATH=' . BUILD_LIB_PATH);
self::putenv('BUILD_BIN_PATH=' . BUILD_BIN_PATH);
self::putenv('PKG_ROOT_PATH=' . PKG_ROOT_PATH);
self::putenv('SOURCE_PATH=' . SOURCE_PATH);
self::putenv('DOWNLOAD_PATH=' . DOWNLOAD_PATH);
// Check pre-defined env vars exists
if (getenv('BUILD_ROOT_PATH') === false) {
throw new RuntimeException('You must include src/globals/internal-env.php before using GlobalEnvManager');
}
// Init SPC env
self::initIfNotExists('SPC_CONCURRENCY', match (PHP_OS_FAMILY) {
'Windows' => (string) WindowsSystemUtil::getCpuCount(),
'Darwin' => (string) MacOSSystemUtil::getCpuCount(),
'Linux' => (string) LinuxSystemUtil::getCpuCount(),
'BSD' => (string) BSDSystemUtil::getCpuCount(),
default => '1',
});
// Define env vars for unix
if (is_unix()) {
self::putenv('PATH=' . BUILD_ROOT_PATH . '/bin:' . getenv('PATH'));
self::putenv('PKG_CONFIG=' . BUILD_BIN_PATH . '/pkg-config');
self::putenv('PKG_CONFIG_PATH=' . BUILD_ROOT_PATH . '/lib/pkgconfig');
if ($builder instanceof BuilderBase) {
self::putenv('SPC_PHP_DEFAULT_OPTIMIZE_CFLAGS=' . ($builder->getOption('no-strip') ? '-g -O0' : '-g -Os'));
}
}
// Init system-specific env
match (PHP_OS_FAMILY) {
'Windows' => self::initWindowsEnv(),
'Darwin' => self::initDarwinEnv($builder),
'Linux' => self::initLinuxEnv($builder),
'BSD' => 'TODO',
default => logger()->warning('Unknown OS: ' . PHP_OS_FAMILY),
};
}
private static function initWindowsEnv(): void
{
// Windows need php-sdk binary tools
self::initIfNotExists('PHP_SDK_PATH', WORKING_DIR . DIRECTORY_SEPARATOR . 'php-sdk-binary-tools');
self::initIfNotExists('UPX_EXEC', PKG_ROOT_PATH . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'upx.exe');
}
private static function initLinuxEnv(BuilderBase $builder): void
{
// Init C Compiler and C++ Compiler (alpine)
if (LinuxSystemUtil::isMuslDist()) {
self::initIfNotExists('CC', 'gcc');
self::initIfNotExists('CXX', 'g++');
self::initIfNotExists('AR', 'ar');
self::initIfNotExists('LD', 'ld.gold');
} else {
// Define env vars for linux
if (PHP_OS_FAMILY === 'Linux') {
$arch = arch2gnu(php_uname('m'));
self::initIfNotExists('CC', "{$arch}-linux-musl-gcc");
self::initIfNotExists('CXX', "{$arch}-linux-musl-g++");
self::initIfNotExists('AR', "{$arch}-linux-musl-ar");
self::initIfNotExists('LD', 'ld.gold');
if (SystemUtil::isMuslDist()) {
self::putenv('SPC_LINUX_DEFAULT_CC=gcc');
self::putenv('SPC_LINUX_DEFAULT_CXX=g++');
self::putenv('SPC_LINUX_DEFAULT_AR=ar');
} else {
self::putenv("SPC_LINUX_DEFAULT_CC={$arch}-linux-musl-gcc");
self::putenv("SPC_LINUX_DEFAULT_CXX={$arch}-linux-musl-g++");
self::putenv("SPC_LINUX_DEFAULT_AR={$arch}-linux-musl-ar");
}
self::putenv("SPC_PHP_DEFAULT_LD_LIBRARY_PATH_CMD=LD_LIBRARY_PATH=/usr/local/musl/{$arch}-linux-musl/lib");
if (getenv('SPC_NO_MUSL_PATH') !== 'yes') {
self::putenv("PATH=/usr/local/musl/bin:/usr/local/musl/{$arch}-linux-musl/bin:" . getenv('PATH'));
}
}
// Init arch-specific cflags
self::initIfNotExists('SPC_DEFAULT_C_FLAGS', '');
self::initIfNotExists('SPC_DEFAULT_CXX_FLAGS', '');
self::initIfNotExists('SPC_EXTRA_LIBS', '');
// Init linux-only env
self::initIfNotExists('UPX_EXEC', PKG_ROOT_PATH . '/bin/upx');
self::initIfNotExists('GNU_ARCH', arch2gnu(php_uname('m')));
// optimization flags with different strip option
$php_extra_cflags_optimize = $builder->getOption('no-strip') ? '-g -O0' : '-g -Os';
// optimization flags with different c compiler
$clang_use_lld = str_ends_with(getenv('CC'), 'clang') && LinuxSystemUtil::findCommand('lld') ? '-Xcompiler -fuse-ld=lld ' : '';
$init_spc_cmd_maps = [
// Init default build command prefix
'SPC_CMD_PREFIX_PHP_BUILDCONF' => './buildconf --force',
'SPC_CMD_PREFIX_PHP_CONFIGURE' => $builder->getOption('ld_library_path') . ' ./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg',
'SPC_CMD_PREFIX_PHP_MAKE' => 'make -j' . getenv('SPC_CONCURRENCY'),
// Init default build vars for build command
'SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS' => getenv('SPC_DEFAULT_C_FLAGS'),
'SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH,
'SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS' => '-L' . BUILD_LIB_PATH,
'SPC_CMD_VAR_PHP_CONFIGURE_LIBS' => '-ldl -lpthread -lm',
'SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS' => $php_extra_cflags_optimize . ' -fno-ident -fPIE',
'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS' => '',
'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM' => $clang_use_lld . '-all-static',
// Init env.ini file, read order:
// WORKING_DIR/config/env.ini
// ROOT_DIR/config/env.ini
$ini_files = [
WORKING_DIR . '/config/env.ini',
ROOT_DIR . '/config/env.ini',
];
foreach ($init_spc_cmd_maps as $name => $value) {
self::initIfNotExists($name, $value);
$ini = null;
foreach ($ini_files as $ini_file) {
if (file_exists($ini_file)) {
$ini = parse_ini_file($ini_file, true);
break;
}
}
self::initUnixEnv($builder);
}
private static function initDarwinEnv(BuilderBase $builder): void
{
// Init C Compiler and C++ Compiler
self::initIfNotExists('CC', 'clang');
self::initIfNotExists('CXX', 'clang++');
// Init arch-specific cflags
self::initIfNotExists('SPC_DEFAULT_C_FLAGS', match (php_uname('m')) {
'arm64', 'aarch64' => '--target=arm64-apple-darwin',
default => '--target=x86_64-apple-darwin',
});
// Init arch-specific cxxflags
self::initIfNotExists('SPC_DEFAULT_CXX_FLAGS', match (php_uname('m')) {
'arm64', 'aarch64' => '--target=arm64-apple-darwin',
default => '--target=x86_64-apple-darwin',
});
// Init extra libs (will be appended before `before-php-buildconf` event point)
self::initIfNotExists('SPC_EXTRA_LIBS', '');
$init_spc_cmd_maps = [
// Init default build command prefix
'SPC_CMD_PREFIX_PHP_BUILDCONF' => './buildconf --force',
'SPC_CMD_PREFIX_PHP_CONFIGURE' => './configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg',
'SPC_CMD_PREFIX_PHP_MAKE' => 'make -j' . getenv('SPC_CONCURRENCY'),
// Init default build vars for build command
'SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS' => getenv('SPC_DEFAULT_C_FLAGS') . ' -Werror=unknown-warning-option',
'SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH,
'SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS' => '-L' . BUILD_LIB_PATH,
'SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS' => $builder->getOption('no-strip') ? '-g -O0' : '-g -Os',
'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS' => '-lresolv',
];
foreach ($init_spc_cmd_maps as $name => $value) {
self::initIfNotExists($name, $value);
if ($ini === null) {
throw new WrongUsageException('env.ini not found');
}
self::initUnixEnv($builder);
}
private static function initUnixEnv(BuilderBase $builder): void
{
self::putenv('PATH=' . BUILD_ROOT_PATH . '/bin:' . getenv('PATH'));
self::putenv('PKG_CONFIG=' . BUILD_BIN_PATH . '/pkg-config');
self::putenv('PKG_CONFIG_PATH=' . BUILD_ROOT_PATH . '/lib/pkgconfig');
}
/**
* Initialize the environment variable if it does not exist
*
* @param string $name Environment variable name
* @param string $value Environment variable value
*/
private static function initIfNotExists(string $name, string $value): void
{
if (($val = getenv($name)) === false) {
self::putenv($name . '=' . $value);
} else {
logger()->debug("env [{$name}] existing: {$val}");
if ($ini === false || !isset($ini['global'])) {
throw new WrongUsageException('Failed to parse ' . $ini_file);
}
self::applyConfig($ini['global']);
match (PHP_OS_FAMILY) {
'Windows' => self::applyConfig($ini['windows']),
'Darwin' => self::applyConfig($ini['macos']),
'Linux' => self::applyConfig($ini['linux']),
'BSD' => self::applyConfig($ini['freebsd']),
default => null,
};
}
private static function putenv(string $val): void
public static function putenv(string $val): void
{
f_putenv($val);
self::$env_cache[] = $val;
}
private static function applyConfig(array $ini): void
{
foreach ($ini as $k => $v) {
if (getenv($k) === false) {
self::putenv($k . '=' . $v);
}
}
}
}

View File

@@ -118,20 +118,26 @@ class LicenseDumper
/**
* @throws RuntimeException
*/
private function loadSourceFile(string $source_name, int $index, ?string $in_path, ?string $custom_base_path = null): string
private function loadSourceFile(string $source_name, int $index, null|array|string $in_path, ?string $custom_base_path = null): string
{
if (is_null($in_path)) {
throw new RuntimeException('source [' . $source_name . '] license file is not set, please check config/source.json');
}
if (file_exists(SOURCE_PATH . '/' . ($custom_base_path ?? $source_name) . '/' . $in_path)) {
return file_get_contents(SOURCE_PATH . '/' . ($custom_base_path ?? $source_name) . '/' . $in_path);
if (!is_array($in_path)) {
$in_path = [$in_path];
}
foreach ($in_path as $item) {
if (file_exists(SOURCE_PATH . '/' . ($custom_base_path ?? $source_name) . '/' . $item)) {
return file_get_contents(SOURCE_PATH . '/' . ($custom_base_path ?? $source_name) . '/' . $item);
}
}
if (file_exists(BUILD_ROOT_PATH . '/source-licenses/' . $source_name . '/' . $index . '.txt')) {
return file_get_contents(BUILD_ROOT_PATH . '/source-licenses/' . $source_name . '/' . $index . '.txt');
}
throw new RuntimeException('source [' . $source_name . '] license file [' . $in_path . '] not exist');
throw new RuntimeException('Cannot find any license file in source [' . $source_name . '] directory!');
}
}

View File

@@ -2,32 +2,16 @@
declare(strict_types=1);
use SPC\store\FileSystem;
use ZM\Logger\ConsoleLogger;
define('WORKING_DIR', getcwd());
define('ROOT_DIR', dirname(__DIR__, 2));
putenv('WORKING_DIR=' . WORKING_DIR);
putenv('ROOT_DIR=' . ROOT_DIR);
// CLI start time
define('START_TIME', microtime(true));
define('BUILD_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_ROOT_PATH')) ? $a : (WORKING_DIR . '/buildroot')));
define('SOURCE_PATH', FileSystem::convertPath(is_string($a = getenv('SOURCE_PATH')) ? $a : (WORKING_DIR . '/source')));
define('DOWNLOAD_PATH', FileSystem::convertPath(is_string($a = getenv('DOWNLOAD_PATH')) ? $a : (WORKING_DIR . '/downloads')));
define('PKG_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('PKG_ROOT_PATH')) ? $a : (WORKING_DIR . '/pkgroot')));
define('BUILD_BIN_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_BIN_PATH')) ? $a : (BUILD_ROOT_PATH . '/bin')));
define('BUILD_LIB_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_LIB_PATH')) ? $a : (BUILD_ROOT_PATH . '/lib')));
define('BUILD_INCLUDE_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_INCLUDE_PATH')) ? $a : (BUILD_ROOT_PATH . '/include')));
define('SEPARATED_PATH', [
'/' . pathinfo(BUILD_LIB_PATH)['basename'], // lib
'/' . pathinfo(BUILD_INCLUDE_PATH)['basename'], // include
BUILD_ROOT_PATH,
]);
if (PHP_OS_FAMILY === 'Windows') {
define('PHP_SDK_PATH', is_string($a = getenv('PHP_SDK_PATH')) ? $a : (WORKING_DIR . DIRECTORY_SEPARATOR . 'php-sdk-binary-tools'));
}
// for windows, prevent calling Invoke-WebRequest and wsl command
const SPC_CURL_EXEC = PHP_OS_FAMILY === 'Windows' ? 'curl.exe' : 'curl';
const SPC_GIT_EXEC = PHP_OS_FAMILY === 'Windows' ? 'git.exe' : 'git';

View File

@@ -0,0 +1,8 @@
<?php
declare(strict_types=1);
assert(function_exists('gmssl_rand_bytes'));
assert(function_exists('gmssl_sm3'));
assert(bin2hex(gmssl_sm3('123456')) === '207cf410532f92a47dee245ce9b11ff71f578ebd763eb3bbea44ebd043d018fb');

View File

@@ -0,0 +1,7 @@
<?php
declare(strict_types=1);
assert(function_exists('msgpack_pack'));
assert(function_exists('msgpack_unpack'));
assert(msgpack_unpack(msgpack_pack(['foo', 'bar'])) === ['foo', 'bar']);

View File

@@ -31,6 +31,11 @@ function logger(): LoggerInterface
return $ob_logger;
}
function is_unix(): bool
{
return in_array(PHP_OS_FAMILY, ['Linux', 'Darwin', 'BSD']);
}
/**
* Transfer architecture name to gnu triplet
*

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
use SPC\builder\freebsd\SystemUtil as BSDSystemUtil;
use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
use SPC\builder\macos\SystemUtil as MacOSSystemUtil;
use SPC\builder\windows\SystemUtil as WindowsSystemUtil;
use SPC\store\FileSystem;
use SPC\util\GlobalEnvManager;
define('BUILD_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_ROOT_PATH')) ? $a : (WORKING_DIR . '/buildroot')));
define('BUILD_INCLUDE_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_INCLUDE_PATH')) ? $a : (BUILD_ROOT_PATH . '/include')));
define('BUILD_LIB_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_LIB_PATH')) ? $a : (BUILD_ROOT_PATH . '/lib')));
define('BUILD_BIN_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_BIN_PATH')) ? $a : (BUILD_ROOT_PATH . '/bin')));
define('PKG_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('PKG_ROOT_PATH')) ? $a : (WORKING_DIR . '/pkgroot')));
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('CPU_COUNT', match (PHP_OS_FAMILY) {
'Windows' => (string) WindowsSystemUtil::getCpuCount(),
'Darwin' => (string) MacOSSystemUtil::getCpuCount(),
'Linux' => (string) LinuxSystemUtil::getCpuCount(),
'BSD' => (string) BSDSystemUtil::getCpuCount(),
default => 1,
});
define('GNU_ARCH', arch2gnu(php_uname('m')));
define('MAC_ARCH', match ($_im8a = arch2gnu(php_uname('m'))) {
'aarch64' => 'arm64',
default => $_im8a
});
// deprecated variables
define('SEPARATED_PATH', [
'/' . pathinfo(BUILD_LIB_PATH)['basename'], // lib
'/' . pathinfo(BUILD_INCLUDE_PATH)['basename'], // include
BUILD_ROOT_PATH,
]);
// add these to env vars with same name
GlobalEnvManager::putenv('BUILD_ROOT_PATH=' . BUILD_ROOT_PATH);
GlobalEnvManager::putenv('BUILD_INCLUDE_PATH=' . BUILD_INCLUDE_PATH);
GlobalEnvManager::putenv('BUILD_LIB_PATH=' . BUILD_LIB_PATH);
GlobalEnvManager::putenv('BUILD_BIN_PATH=' . BUILD_BIN_PATH);
GlobalEnvManager::putenv('PKG_ROOT_PATH=' . PKG_ROOT_PATH);
GlobalEnvManager::putenv('SOURCE_PATH=' . SOURCE_PATH);
GlobalEnvManager::putenv('DOWNLOAD_PATH=' . DOWNLOAD_PATH);
GlobalEnvManager::putenv('CPU_COUNT=' . CPU_COUNT);
GlobalEnvManager::putenv('GNU_ARCH=' . GNU_ARCH);
GlobalEnvManager::putenv('MAC_ARCH=' . MAC_ARCH);

View File

@@ -0,0 +1,28 @@
diff --git a/main/streams/cast.c b/main/streams/cast.c
index 0978ca4d..677fc6dc 100644
--- a/main/streams/cast.c
+++ b/main/streams/cast.c
@@ -102,6 +102,9 @@ static ssize_t stream_cookie_writer(void *cookie, const char *buffer, size_t siz
# ifdef COOKIE_SEEKER_USES_OFF64_T
static int stream_cookie_seeker(void *cookie, off64_t *position, int whence)
+# else
+static int stream_cookie_seeker(void *cookie, off_t *position, int whence)
+# endif
{
*position = php_stream_seek((php_stream *)cookie, (zend_off_t)*position, whence);
@@ -111,13 +114,6 @@ static int stream_cookie_seeker(void *cookie, off64_t *position, int whence)
}
return 0;
}
-# else
-static int stream_cookie_seeker(void *cookie, zend_off_t position, int whence)
-{
-
- return php_stream_seek((php_stream *)cookie, position, whence);
-}
-# endif
static int stream_cookie_closer(void *cookie)
{

View File

@@ -0,0 +1,129 @@
diff --git a/build/order_by_dep.awk b/build/order_by_dep.awk
index 1e71ea2069..3da32d8830 100644
--- a/build/order_by_dep.awk
+++ b/build/order_by_dep.awk
@@ -37,6 +37,11 @@ function get_module_index(name, i)
function do_deps(mod_idx, module_name, mod_name_len, dep, ext, val, depidx)
{
module_name = mods[mod_idx];
+ # TODO: real skip zend extension
+ if (module_name == "opcache") {
+ delete mods[mod_idx];
+ return;
+ }
mod_name_len = length(module_name);
for (ext in mod_deps) {
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index 5f6b854d47..ea15c0d5bc 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -91,7 +91,10 @@ typedef int gid_t;
#include <immintrin.h>
#endif
+#ifdef COMPILE_DL_OPCACHE
+// avoid symbol conflict
ZEND_EXTENSION();
+#endif
#ifndef ZTS
zend_accel_globals accel_globals;
@@ -4808,7 +4811,11 @@ static int accel_finish_startup(void)
return SUCCESS;
}
+#ifdef COMPILE_DL_OPCACHE
ZEND_EXT_API zend_extension zend_extension_entry = {
+#else
+zend_extension opcache_zend_extension_entry = {
+#endif
ACCELERATOR_PRODUCT_NAME, /* name */
PHP_VERSION, /* version */
"Zend Technologies", /* author */
diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4
index 2a83fa2455..7b3b37182e 100644
--- a/ext/opcache/config.m4
+++ b/ext/opcache/config.m4
@@ -21,7 +21,8 @@ PHP_ARG_ENABLE([opcache-jit],
if test "$PHP_OPCACHE" != "no"; then
dnl Always build as shared extension
- ext_shared=yes
+ dnl why?
+ dnl ext_shared=yes
if test "$PHP_HUGE_CODE_PAGES" = "yes"; then
AC_DEFINE(HAVE_HUGE_CODE_PAGES, 1, [Define to enable copying PHP CODE pages into HUGE PAGES (experimental)])
@@ -327,7 +328,9 @@ int main() {
shared_alloc_mmap.c \
shared_alloc_posix.c \
$ZEND_JIT_SRC,
- shared,,"-Wno-implicit-fallthrough -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1",,yes)
+ $ext_shared,,"-Wno-implicit-fallthrough -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1",,yes)
+
+ AC_DEFINE(HAVE_OPCACHE, 1, [opcache enabled])
PHP_ADD_EXTENSION_DEP(opcache, pcre)
diff --git a/ext/opcache/config.w32 b/ext/opcache/config.w32
index 764a2edaab..95427090ce 100644
--- a/ext/opcache/config.w32
+++ b/ext/opcache/config.w32
@@ -16,7 +16,9 @@ if (PHP_OPCACHE != "no") {
zend_persist_calc.c \
zend_file_cache.c \
zend_shared_alloc.c \
- shared_alloc_win32.c", true, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
+ shared_alloc_win32.c", PHP_OPCACHE_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
+
+ AC_DEFINE('HAVE_OPCACHE', 1, 'opcache enabled');
if (PHP_OPCACHE_JIT == "yes") {
if (CHECK_HEADER_ADD_INCLUDE("dynasm/dasm_x86.h", "CFLAGS_OPCACHE", PHP_OPCACHE + ";ext\\opcache\\jit")) {
diff --git a/main/main.c b/main/main.c
index 8c16f01b11..0560348a06 100644
--- a/main/main.c
+++ b/main/main.c
@@ -2011,6 +2011,18 @@ void dummy_invalid_parameter_handler(
}
#endif
+// this can be moved to other place
+#if defined(HAVE_OPCACHE) && !defined(COMPILE_DL_OPCACHE)
+extern zend_extension opcache_zend_extension_entry;
+extern void zend_register_extension(zend_extension *new_extension, void *handle);
+
+int zend_load_static_extensions(void)
+{
+ zend_register_extension(&opcache_zend_extension_entry, NULL /*opcache cannot be unloaded*/);
+ return 0;
+}
+#endif
+
/* {{{ php_module_startup */
int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_modules, uint32_t num_additional_modules)
{
@@ -2253,6 +2265,9 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod
ahead of all other internals
*/
php_ini_register_extensions();
+#if defined(HAVE_OPCACHE) && !defined(COMPILE_DL_OPCACHE)
+ zend_load_static_extensions();
+#endif
zend_startup_modules();
/* start Zend extensions */
diff --git a/win32/build/confutils.js b/win32/build/confutils.js
index 1a2dfe43b4..ae405f035a 100644
--- a/win32/build/confutils.js
+++ b/win32/build/confutils.js
@@ -1535,6 +1535,8 @@ function EXTENSION(extname, file_list, shared, cflags, dllname, obj_dir)
}
}
+ // TODO: real skip zend extensions
+ if (extname != 'opcache')
extension_module_ptrs += '\tphpext_' + extname + '_ptr,\r\n';
DEFINE('CFLAGS_' + EXT + '_OBJ', '$(CFLAGS_PHP) $(CFLAGS_' + EXT + ')');

View File

@@ -0,0 +1,129 @@
diff --git a/build/order_by_dep.awk b/build/order_by_dep.awk
index 1e71ea2069..3da32d8830 100644
--- a/build/order_by_dep.awk
+++ b/build/order_by_dep.awk
@@ -37,6 +37,11 @@ function get_module_index(name, i)
function do_deps(mod_idx, module_name, mod_name_len, dep, ext, val, depidx)
{
module_name = mods[mod_idx];
+ # TODO: real skip zend extension
+ if (module_name == "opcache") {
+ delete mods[mod_idx];
+ return;
+ }
mod_name_len = length(module_name);
for (ext in mod_deps) {
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index ec33c69eb2..b8ce7e3eca 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -93,7 +93,10 @@ typedef int gid_t;
#include <immintrin.h>
#endif
+#ifdef COMPILE_DL_OPCACHE
+// avoid symbol conflict
ZEND_EXTENSION();
+#endif
#ifndef ZTS
zend_accel_globals accel_globals;
@@ -4814,7 +4817,11 @@ static int accel_finish_startup(void)
#endif /* ZEND_WIN32 */
}
+#ifdef COMPILE_DL_OPCACHE
ZEND_EXT_API zend_extension zend_extension_entry = {
+#else
+zend_extension opcache_zend_extension_entry = {
+#endif
ACCELERATOR_PRODUCT_NAME, /* name */
PHP_VERSION, /* version */
"Zend Technologies", /* author */
diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4
index 2a83fa2455..7b3b37182e 100644
--- a/ext/opcache/config.m4
+++ b/ext/opcache/config.m4
@@ -21,7 +21,8 @@ PHP_ARG_ENABLE([opcache-jit],
if test "$PHP_OPCACHE" != "no"; then
dnl Always build as shared extension
- ext_shared=yes
+ dnl why?
+ dnl ext_shared=yes
if test "$PHP_HUGE_CODE_PAGES" = "yes"; then
AC_DEFINE(HAVE_HUGE_CODE_PAGES, 1, [Define to enable copying PHP CODE pages into HUGE PAGES (experimental)])
@@ -327,7 +328,9 @@ int main() {
shared_alloc_mmap.c \
shared_alloc_posix.c \
$ZEND_JIT_SRC,
- shared,,"-Wno-implicit-fallthrough -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1",,yes)
+ $ext_shared,,"-Wno-implicit-fallthrough -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1",,yes)
+
+ AC_DEFINE(HAVE_OPCACHE, 1, [opcache enabled])
PHP_ADD_EXTENSION_DEP(opcache, pcre)
diff --git a/ext/opcache/config.w32 b/ext/opcache/config.w32
index 764a2edaab..95427090ce 100644
--- a/ext/opcache/config.w32
+++ b/ext/opcache/config.w32
@@ -16,7 +16,9 @@ if (PHP_OPCACHE != "no") {
zend_persist_calc.c \
zend_file_cache.c \
zend_shared_alloc.c \
- shared_alloc_win32.c", true, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
+ shared_alloc_win32.c", PHP_OPCACHE_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
+
+ AC_DEFINE('HAVE_OPCACHE', 1, 'opcache enabled');
if (PHP_OPCACHE_JIT == "yes") {
if (CHECK_HEADER_ADD_INCLUDE("dynasm/dasm_x86.h", "CFLAGS_OPCACHE", PHP_OPCACHE + ";ext\\opcache\\jit")) {
diff --git a/main/main.c b/main/main.c
index 6fdfbce13e..bcccfad6e3 100644
--- a/main/main.c
+++ b/main/main.c
@@ -2012,6 +2012,18 @@ void dummy_invalid_parameter_handler(
}
#endif
+// this can be moved to other place
+#if defined(HAVE_OPCACHE) && !defined(COMPILE_DL_OPCACHE)
+extern zend_extension opcache_zend_extension_entry;
+extern void zend_register_extension(zend_extension *new_extension, void *handle);
+
+int zend_load_static_extensions(void)
+{
+ zend_register_extension(&opcache_zend_extension_entry, NULL /*opcache cannot be unloaded*/);
+ return 0;
+}
+#endif
+
/* {{{ php_module_startup */
zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_module)
{
@@ -2196,6 +2208,9 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi
ahead of all other internals
*/
php_ini_register_extensions();
+#if defined(HAVE_OPCACHE) && !defined(COMPILE_DL_OPCACHE)
+ zend_load_static_extensions();
+#endif
zend_startup_modules();
/* start Zend extensions */
diff --git a/win32/build/confutils.js b/win32/build/confutils.js
index 359c751b7b..01068efcf6 100644
--- a/win32/build/confutils.js
+++ b/win32/build/confutils.js
@@ -1534,6 +1534,8 @@ function EXTENSION(extname, file_list, shared, cflags, dllname, obj_dir)
}
}
+ // TODO: real skip zend extensions
+ if (extname != 'opcache')
extension_module_ptrs += '\tphpext_' + extname + '_ptr,\r\n';
DEFINE('CFLAGS_' + EXT + '_OBJ', '$(CFLAGS_PHP) $(CFLAGS_' + EXT + ')');

View File

@@ -0,0 +1,27 @@
diff --git a/ext-src/php_swoole_call_stack.h b/ext-src/php_swoole_call_stack.h
index 8c89f83..639a303 100644
--- a/ext-src/php_swoole_call_stack.h
+++ b/ext-src/php_swoole_call_stack.h
@@ -18,8 +18,8 @@
#ifdef ZEND_CHECK_STACK_LIMIT
#define HOOK_PHP_CALL_STACK(callback) \
- auto __stack_limit = EG(stack_limit); \
- auto __stack_base = EG(stack_base); \
+ void *__stack_limit = EG(stack_limit); \
+ void *__stack_base = EG(stack_base); \
EG(stack_base) = (void *) 0; \
EG(stack_limit) = (void *) 0; \
callback EG(stack_limit) = __stack_limit; \
diff --git a/thirdparty/php83/pdo_odbc/odbc_driver.c b/thirdparty/php83/pdo_odbc/odbc_driver.c
index c83da64..a5e3a8b 100644
--- a/thirdparty/php83/pdo_odbc/odbc_driver.c
+++ b/thirdparty/php83/pdo_odbc/odbc_driver.c
@@ -25,6 +25,7 @@
#include "pdo/php_pdo.h"
#include "pdo/php_pdo_driver.h"
#include "zend_exceptions.h"
+#include <php_odbc_utils.h>
static void pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) {
pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;

View File

@@ -11,16 +11,37 @@ declare(strict_types=1);
// --------------------------------- edit area ---------------------------------
// test php version
$test_php_version = [
'8.0',
'8.1',
'8.2',
'8.3',
];
// test os (macos-13, macos-14, ubuntu-latest, windows-latest are available)
$test_os = [
'macos-14',
'ubuntu-latest',
'macos-13',
'windows-latest',
];
// whether enable thread safe
$zts = false;
$no_strip = false;
$upx = true;
// compress with upx
$upx = false;
// prefer downloading pre-built packages to speed up the build process
$prefer_pre_built = true;
// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
$extensions = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'iconv',
'Windows' => 'igbinary,redis,session',
'Linux', 'Darwin' => '',
'Windows' => 'amqp,apcu,bcmath,bz2,calendar,ctype,curl,dba,dom,ds,exif,ffi,fileinfo,filter,ftp,gd,iconv,igbinary,libxml,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,pdo,pdo_mysql,pdo_sqlite,pdo_sqlsrv,phar,rar,redis,session,shmop,simdjson,simplexml,soap,sockets,sqlite3,sqlsrv,ssh2,swow,sysvshm,tokenizer,xml,xmlreader,xmlwriter,yac,yaml,zip,zlib',
};
// If you want to test lib-suggests feature with extension, add them below (comma separated, example `libwebp,libavif`).
@@ -33,7 +54,7 @@ $with_libs = match (PHP_OS_FAMILY) {
// You can use `common`, `bulk`, `minimal` or `none`.
// note: combination is only available for *nix platform. Windows must use `none` combination
$base_combination = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'none',
'Linux', 'Darwin' => 'bulk',
'Windows' => 'none',
};
@@ -58,7 +79,7 @@ function _getCombination(string $type = 'common'): string
}
if (!isset($argv[1])) {
exit("Please use 'extensions', 'cmd' or 'libs' as output type");
exit("Please use 'extensions', 'cmd', 'os', 'php' or 'libs' as output type");
}
$trim_value = "\r\n \t,";
@@ -72,7 +93,44 @@ if (PHP_OS_FAMILY === 'Windows') {
$final_extensions_cmd = $final_extensions;
}
function quote2(string $param): string
{
global $argv;
if (str_starts_with($argv[2], 'windows-')) {
return '"' . $param . '"';
}
return $param;
}
// generate download command
if ($argv[1] === 'download_cmd') {
$down_cmd = 'download ';
$down_cmd .= '--for-extensions=' . quote2($final_extensions) . ' ';
$down_cmd .= '--for-libs=' . quote2($final_libs) . ' ';
$down_cmd .= '--with-php=' . quote2($argv[3]) . ' ';
$down_cmd .= '--ignore-cache-sources=php-src ';
$down_cmd .= '--debug ';
$down_cmd .= '--retry=5 ';
$down_cmd .= '--shallow-clone ';
$down_cmd .= $prefer_pre_built ? '--prefer-pre-built ' : '';
}
// generate build command
if ($argv[1] === 'build_cmd') {
$build_cmd = 'build ';
$build_cmd .= quote2($final_extensions) . ' ';
$build_cmd .= $zts ? '--enable-zts ' : '';
$build_cmd .= $no_strip ? '--no-strip ' : '';
$build_cmd .= $upx ? '--with-upx-pack ' : '';
$build_cmd .= $final_libs === '' ? '' : ('--with-libs=' . quote2($final_libs) . ' ');
$build_cmd .= '--build-cli --build-micro ';
$build_cmd .= str_starts_with($argv[2], 'windows-') ? '' : '--build-fpm ';
$build_cmd .= '--debug ';
}
echo match ($argv[1]) {
'os' => json_encode($test_os),
'php' => json_encode($test_php_version),
'extensions' => $final_extensions,
'libs' => $final_libs,
'libs_cmd' => ($final_libs === '' ? '' : (' --with-libs=' . $final_libs)),
@@ -80,5 +138,24 @@ echo match ($argv[1]) {
'zts' => $zts ? '--enable-zts' : '',
'no_strip' => $no_strip ? '--no-strip' : '',
'upx' => $upx ? '--with-upx-pack' : '',
'prefer_pre_built' => $prefer_pre_built ? '--prefer-pre-built' : '',
'download_cmd' => $down_cmd,
'build_cmd' => $build_cmd,
default => '',
};
if ($argv[1] === 'download_cmd') {
if (str_starts_with($argv[2], 'windows-')) {
passthru('powershell.exe -file .\bin\spc.ps1 ' . $down_cmd, $retcode);
} else {
passthru('./bin/spc ' . $down_cmd, $retcode);
}
} elseif ($argv[1] === 'build_cmd') {
if (str_starts_with($argv[2], 'windows-')) {
passthru('powershell.exe -file .\bin\spc.ps1 ' . $build_cmd, $retcode);
} else {
passthru('./bin/spc ' . $build_cmd, $retcode);
}
}
exit($retcode);

View File

@@ -0,0 +1,252 @@
<?php
declare(strict_types=1);
namespace SPC\Tests\builder;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use SPC\builder\BuilderBase;
use SPC\builder\BuilderProvider;
use SPC\builder\Extension;
use SPC\builder\LibraryBase;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
use SPC\util\DependencyUtil;
use Symfony\Component\Console\Input\ArgvInput;
/**
* @internal
*/
class BuilderTest extends TestCase
{
private BuilderBase $builder;
public static function setUpBeforeClass(): void
{
BuilderProvider::makeBuilderByInput(new ArgvInput());
BuilderProvider::getBuilder();
}
public function setUp(): void
{
$this->builder = BuilderProvider::makeBuilderByInput(new ArgvInput());
[$extensions, $libs] = DependencyUtil::getExtsAndLibs(['mbregex']);
$this->builder->proveLibs($libs);
CustomExt::loadCustomExt();
foreach ($extensions as $extension) {
$class = CustomExt::getExtClass($extension);
$ext = new $class($extension, $this->builder);
$this->builder->addExt($ext);
}
foreach ($this->builder->getExts() as $ext) {
$ext->checkDependency();
}
}
public function testMakeBuilderByInput(): void
{
$this->assertInstanceOf(BuilderBase::class, BuilderProvider::makeBuilderByInput(new ArgvInput()));
$this->assertInstanceOf(BuilderBase::class, BuilderProvider::getBuilder());
}
public function testGetLibAndGetLibs()
{
$this->assertIsArray($this->builder->getLibs());
$this->assertInstanceOf(LibraryBase::class, $this->builder->getLib('onig'));
}
public function testGetExtAndGetExts()
{
$this->assertIsArray($this->builder->getExts());
$this->assertInstanceOf(Extension::class, $this->builder->getExt('mbregex'));
}
public function testHasCpp()
{
// mbregex doesn't have cpp
$this->assertFalse($this->builder->hasCpp());
}
public function testMakeExtensionArgs()
{
$this->assertStringContainsString('--enable-mbstring', $this->builder->makeExtensionArgs());
}
public function testIsLibsOnly()
{
// mbregex is not libs only
$this->assertFalse($this->builder->isLibsOnly());
}
public function testGetPHPVersionID()
{
if (file_exists(SOURCE_PATH . '/php-src/main/php_version.h')) {
$file = SOURCE_PATH . '/php-src/main/php_version.h';
$cnt = preg_match('/PHP_VERSION_ID (\d+)/m', file_get_contents($file), $match);
if ($cnt !== 0) {
$this->assertEquals(intval($match[1]), $this->builder->getPHPVersionID());
} else {
$this->expectException(RuntimeException::class);
$this->builder->getPHPVersionID();
}
} else {
$this->expectException(WrongUsageException::class);
$this->builder->getPHPVersionID();
}
}
public function testGetPHPVersion()
{
if (file_exists(SOURCE_PATH . '/php-src/main/php_version.h')) {
$file = SOURCE_PATH . '/php-src/main/php_version.h';
$cnt = preg_match('/PHP_VERSION "(\d+\.\d+\.\d+)"/', file_get_contents($file), $match);
if ($cnt !== 0) {
$this->assertEquals($match[1], $this->builder->getPHPVersion());
} else {
$this->expectException(RuntimeException::class);
$this->builder->getPHPVersion();
}
} else {
$this->expectException(WrongUsageException::class);
$this->builder->getPHPVersion();
}
}
public function testGetPHPVersionFromArchive()
{
$lock = file_exists(DOWNLOAD_PATH . '/.lock.json') ? file_get_contents(DOWNLOAD_PATH . '/.lock.json') : false;
if ($lock === false) {
$this->assertFalse($this->builder->getPHPVersionFromArchive());
} else {
$lock = json_decode($lock, true);
$file = $lock['php-src']['filename'] ?? null;
if ($file === null) {
$this->assertFalse($this->builder->getPHPVersionFromArchive());
} else {
$cnt = preg_match('/php-(\d+\.\d+\.\d+)/', $file, $match);
if ($cnt !== 0) {
$this->assertEquals($match[1], $this->builder->getPHPVersionFromArchive());
} else {
$this->assertFalse($this->builder->getPHPVersionFromArchive());
}
}
}
}
public function testGetMicroVersion()
{
$file = FileSystem::convertPath(SOURCE_PATH . '/php-src/sapi/micro/php_micro.h');
if (!file_exists($file)) {
$this->assertFalse($this->builder->getMicroVersion());
} else {
$content = file_get_contents($file);
$ver = '';
preg_match('/#define PHP_MICRO_VER_MAJ (\d)/m', $content, $match);
$ver .= $match[1] . '.';
preg_match('/#define PHP_MICRO_VER_MIN (\d)/m', $content, $match);
$ver .= $match[1] . '.';
preg_match('/#define PHP_MICRO_VER_PAT (\d)/m', $content, $match);
$ver .= $match[1];
$this->assertEquals($ver, $this->builder->getMicroVersion());
}
}
public static function providerGetBuildTypeName(): array
{
return [
[BUILD_TARGET_CLI, 'cli'],
[BUILD_TARGET_FPM, 'fpm'],
[BUILD_TARGET_MICRO, 'micro'],
[BUILD_TARGET_EMBED, 'embed'],
[BUILD_TARGET_ALL, 'cli, micro, fpm, embed'],
[BUILD_TARGET_CLI | BUILD_TARGET_EMBED, 'cli, embed'],
];
}
/**
* @dataProvider providerGetBuildTypeName
*/
public function testGetBuildTypeName(int $target, string $name): void
{
$this->assertEquals($name, $this->builder->getBuildTypeName($target));
}
public function testGetOption()
{
// we cannot assure the option exists, so just tests default value
$this->assertEquals('foo', $this->builder->getOption('bar', 'foo'));
}
public function testGetOptions()
{
$this->assertIsArray($this->builder->getOptions());
}
public function testSetOptionIfNotExist()
{
$this->assertEquals(null, $this->builder->getOption('bar'));
$this->builder->setOptionIfNotExist('bar', 'foo');
$this->assertEquals('foo', $this->builder->getOption('bar'));
}
public function testSetOption()
{
$this->assertEquals(null, $this->builder->getOption('bar'));
$this->builder->setOption('bar', 'foo');
$this->assertEquals('foo', $this->builder->getOption('bar'));
}
public function testGetEnvString()
{
$this->assertIsString($this->builder->getEnvString());
putenv('TEST_SPC_BUILDER=foo');
$this->assertStringContainsString('TEST_SPC_BUILDER=foo', $this->builder->getEnvString(['TEST_SPC_BUILDER']));
}
public function testValidateLibsAndExts()
{
$this->builder->validateLibsAndExts();
$this->assertTrue(true);
}
public static function providerEmitPatchPoint(): array
{
return [
['before-libs-extract'],
['after-libs-extract'],
['before-php-extract'],
['after-php-extract'],
['before-micro-extract'],
['after-micro-extract'],
['before-exts-extract'],
['after-exts-extract'],
['before-php-buildconf'],
['before-php-configure'],
['before-php-make'],
['before-sanity-check'],
];
}
/**
* @dataProvider providerEmitPatchPoint
*/
public function testEmitPatchPoint(string $point)
{
$code = '<?php if (patch_point() === "' . $point . '") echo "GOOD:' . $point . '";';
// emulate patch point
$this->builder->setOption('with-added-patch', ['/tmp/patch-point.' . $point . '.php']);
FileSystem::writeFile('/tmp/patch-point.' . $point . '.php', $code);
$this->expectOutputString('GOOD:' . $point);
$this->builder->emitPatchPoint($point);
}
public function testEmitPatchPointNotExists()
{
$this->expectOutputRegex('/failed to run/');
$this->builder->setOption('with-added-patch', ['/tmp/patch-point.not_exsssists.php']);
$this->builder->emitPatchPoint('not-exists');
}
}

View File

@@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace SPC\Tests\builder;
use PHPUnit\Framework\TestCase;
use SPC\builder\BuilderProvider;
use SPC\builder\Extension;
use SPC\util\CustomExt;
use SPC\util\DependencyUtil;
use Symfony\Component\Console\Input\ArgvInput;
/**
* @internal
*/
class ExtensionTest extends TestCase
{
private Extension $extension;
protected function setUp(): void
{
$builder = BuilderProvider::makeBuilderByInput(new ArgvInput());
[$extensions, $libs] = DependencyUtil::getExtsAndLibs(['mbregex']);
$builder->proveLibs($libs);
CustomExt::loadCustomExt();
foreach ($extensions as $extension) {
$class = CustomExt::getExtClass($extension);
$ext = new $class($extension, $builder);
$builder->addExt($ext);
}
foreach ($builder->getExts() as $ext) {
$ext->checkDependency();
}
$this->extension = $builder->getExt('mbregex');
}
public function testPatches()
{
$this->assertFalse($this->extension->patchBeforeBuildconf());
$this->assertFalse($this->extension->patchBeforeConfigure());
$this->assertFalse($this->extension->patchBeforeMake());
}
public function testGetExtensionDependency()
{
$this->assertEquals('mbstring', current($this->extension->getExtensionDependency())->getName());
}
public function testGetWindowsConfigureArg()
{
$this->assertEquals('', $this->extension->getWindowsConfigureArg());
}
public function testGetConfigureArg()
{
$this->assertEquals('', $this->extension->getUnixConfigureArg());
}
public function testGetExtVersion()
{
// only swoole has version, we cannot test it
$this->assertEquals(null, $this->extension->getExtVersion());
}
public function testGetDistName()
{
$this->assertEquals('mbregex', $this->extension->getName());
}
public function testRunCliCheckWindows()
{
if (is_unix()) {
$this->markTestIncomplete('This test is for Windows only');
} else {
$this->extension->runCliCheckWindows();
$this->assertTrue(true);
}
}
public function testGetLibFilesString()
{
$this->assertStringEndsWith('libonig.a', $this->extension->getLibFilesString());
}
public function testGetName()
{
$this->assertEquals('mbregex', $this->extension->getName());
}
public function testGetUnixConfigureArg()
{
$this->assertEquals('', $this->extension->getUnixConfigureArg());
}
public function testGetEnableArg()
{
$this->assertEquals('', $this->extension->getEnableArg());
}
}

View File

@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace SPC\Tests\builder\linux;
use PHPUnit\Framework\TestCase;
use SPC\builder\linux\SystemUtil;
/**
* @internal
*/
class SystemUtilTest extends TestCase
{
public static function setUpBeforeClass(): void
{
if (PHP_OS_FAMILY !== 'Linux') {
self::markTestIncomplete('This test is only for Linux');
}
}
public function testIsMuslDistAndGetOSRelease()
{
$release = SystemUtil::getOSRelease();
// we cannot ensure what is the current distro, just test the key exists
$this->assertArrayHasKey('dist', $release);
$this->assertArrayHasKey('ver', $release);
$this->assertTrue($release['dist'] === 'alpine' && SystemUtil::isMuslDist() || $release['dist'] !== 'alpine' && !SystemUtil::isMuslDist());
}
public function testFindStaticLib()
{
$this->assertIsArray(SystemUtil::findStaticLib('ld-linux-x86-64.so.2'));
}
public function testGetCpuCount()
{
$this->assertIsInt(SystemUtil::getCpuCount());
}
public function testFindHeader()
{
$this->assertIsArray(SystemUtil::findHeader('elf.h'));
}
public function testGetCrossCompilePrefix()
{
$this->assertIsString(SystemUtil::getCrossCompilePrefix('gcc', 'x86_64'));
}
public function testGetCCType()
{
$this->assertEquals('gcc', SystemUtil::getCCType('xjfoiewjfoewof-gcc'));
}
public function testGetSupportedDistros()
{
$this->assertIsArray(SystemUtil::getSupportedDistros());
}
public function testFindHeaders()
{
$this->assertIsArray(SystemUtil::findHeaders(['elf.h']));
}
public function testFindStaticLibs()
{
$this->assertIsArray(SystemUtil::findStaticLibs(['ld-linux-x86-64.so.2']));
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace SPC\Tests\builder\macos;
use PHPUnit\Framework\TestCase;
use SPC\builder\macos\SystemUtil;
/**
* @internal
*/
class SystemUtilTest extends TestCase
{
public static function setUpBeforeClass(): void
{
if (PHP_OS_FAMILY !== 'Darwin') {
self::markTestIncomplete('This test is only for macOS');
}
}
public function testGetCpuCount()
{
$this->assertIsInt(SystemUtil::getCpuCount());
}
public function testGetArchCFlags()
{
$this->assertEquals('--target=x86_64-apple-darwin', SystemUtil::getArchCFlags('x86_64'));
}
}

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace SPC\Tests\builder\unix;
use PHPUnit\Framework\TestCase;
use SPC\builder\freebsd\SystemUtil as FreebsdSystemUtil;
use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
use SPC\builder\macos\SystemUtil as MacosSystemUtil;
use SPC\exception\FileSystemException;
/**
* @internal
*/
class UnixSystemUtilTest extends TestCase
{
private FreebsdSystemUtil|LinuxSystemUtil|MacosSystemUtil $util;
public function setUp(): void
{
$util_class = match (PHP_OS_FAMILY) {
'Linux' => 'SPC\builder\linux\SystemUtil',
'Darwin' => 'SPC\builder\macos\SystemUtil',
'FreeBSD' => 'SPC\builder\freebsd\SystemUtil',
default => null,
};
if ($util_class === null) {
self::markTestIncomplete('This test is only for Unix');
}
$this->util = new $util_class();
}
/**
* @throws FileSystemException
*/
public function testMakeCmakeToolchainFile()
{
$str = $this->util->makeCmakeToolchainFile(PHP_OS_FAMILY, 'x86_64', '');
$this->assertIsString($str);
}
public function testFindCommand()
{
$this->assertIsString($this->util->findCommand('bash'));
}
public function testMakeEnvVarString()
{
$this->assertEquals("PATH='/usr/bin' PKG_CONFIG='/usr/bin/pkg-config'", $this->util->makeEnvVarString(['PATH' => '/usr/bin', 'PKG_CONFIG' => '/usr/bin/pkg-config']));
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace SPC\Tests\store;
use PHPUnit\Framework\TestCase;
use SPC\store\CurlHook;
/**
* @internal
*/
class CurlHookTest extends TestCase
{
public function testSetupGithubToken()
{
$header = [];
CurlHook::setupGithubToken('GET', 'https://example.com', $header);
if (getenv('GITHUB_TOKEN') === false) {
$this->assertEmpty($header);
} else {
$this->assertEquals(['Authorization: Bearer ' . getenv('GITHUB_TOKEN')], $header);
}
$header = [];
putenv('GITHUB_TOKEN=token');
CurlHook::setupGithubToken('GET', 'https://example.com', $header);
$this->assertEquals(['Authorization: Bearer token'], $header);
}
}

View File

@@ -0,0 +1,112 @@
<?php
declare(strict_types=1);
namespace SPC\Tests\store;
use PHPUnit\Framework\TestCase;
use SPC\exception\WrongUsageException;
use SPC\store\Downloader;
/**
* @internal
* TODO: Test all methods
*/
class DownloaderTest extends TestCase
{
public function testGetLatestGithubTarball()
{
$this->assertEquals(
'https://api.github.com/repos/AOMediaCodec/libavif/tarball/v1.1.1',
Downloader::getLatestGithubTarball('libavif', [
'type' => 'ghtar',
'repo' => 'AOMediaCodec/libavif',
])[0]
);
}
public function testDownloadGit()
{
Downloader::downloadGit('setup-static-php', 'https://github.com/static-php/setup-static-php.git', 'main');
$this->assertTrue(true);
// test keyboard interrupt
try {
Downloader::downloadGit('setup-static-php', 'https://github.com/static-php/setup-static-php.git', 'SIGINT');
} catch (WrongUsageException $e) {
$this->assertStringContainsString('interrupted', $e->getMessage());
return;
}
$this->fail('Expected exception not thrown');
}
public function testDownloadFile()
{
Downloader::downloadFile('fake-file', 'https://fakecmd.com/curlDown', 'curlDown.exe');
$this->assertTrue(true);
// test keyboard interrupt
try {
Downloader::downloadFile('fake-file', 'https://fakecmd.com/curlDown', 'SIGINT');
} catch (WrongUsageException $e) {
$this->assertStringContainsString('interrupted', $e->getMessage());
return;
}
$this->fail('Expected exception not thrown');
}
public function testLockSource()
{
Downloader::lockSource('fake-file', ['source_type' => 'archive', 'filename' => 'fake-file-name', 'move_path' => 'fake-path', 'lock_as' => 'fake-lock-as']);
$this->assertFileExists(DOWNLOAD_PATH . '/.lock.json');
$json = json_decode(file_get_contents(DOWNLOAD_PATH . '/.lock.json'), true);
$this->assertIsArray($json);
$this->assertArrayHasKey('fake-file', $json);
$this->assertArrayHasKey('source_type', $json['fake-file']);
$this->assertArrayHasKey('filename', $json['fake-file']);
$this->assertArrayHasKey('move_path', $json['fake-file']);
$this->assertArrayHasKey('lock_as', $json['fake-file']);
$this->assertEquals('archive', $json['fake-file']['source_type']);
$this->assertEquals('fake-file-name', $json['fake-file']['filename']);
$this->assertEquals('fake-path', $json['fake-file']['move_path']);
$this->assertEquals('fake-lock-as', $json['fake-file']['lock_as']);
}
public function testGetLatestBitbucketTag()
{
$this->assertEquals(
'abc.tar.gz',
Downloader::getLatestBitbucketTag('abc', [
'repo' => 'MATCHED/def',
])[1]
);
$this->assertEquals(
'abc-1.0.0.tar.gz',
Downloader::getLatestBitbucketTag('abc', [
'repo' => 'abc/def',
])[1]
);
}
public function testGetLatestGithubRelease()
{
$this->assertEquals(
'ghreltest.tar.gz',
Downloader::getLatestGithubRelease('ghrel', [
'type' => 'ghrel',
'repo' => 'ghreltest/ghrel',
'match' => 'ghreltest.tar.gz',
])[1]
);
}
public function testGetFromFileList()
{
$filelist = Downloader::getFromFileList('fake-filelist', [
'url' => 'https://fakecmd.com/filelist',
'regex' => '/href="(?<file>filelist-(?<version>[^"]+)\.tar\.xz)"/',
]);
$this->assertIsArray($filelist);
$this->assertEquals('filelist-4.7.0.tar.xz', $filelist[1]);
}
}

BIN
tests/assets/filelist.gz Normal file

Binary file not shown.

6
tests/bootstrap.php Normal file
View File

@@ -0,0 +1,6 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../src/globals/internal-env.php';
require_once __DIR__ . '/mock/SPC_store.php';

75
tests/mock/SPC_store.php Normal file
View File

@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
// mock global functions
namespace SPC\store;
use SPC\exception\RuntimeException;
function f_exec(string $command, mixed &$output, mixed &$result_code): bool
{
$result_code = 0;
if (str_contains($command, 'https://api.github.com/repos/AOMediaCodec/libavif/releases')) {
$output = explode("\n", gzdecode(file_get_contents(__DIR__ . '/../assets/github_api_AOMediaCodec_libavif_releases.json.gz')));
return true;
}
if (str_contains($command, 'https://api.github.com/repos/AOMediaCodec/libavif/tarball/v1.1.1')) {
$output = explode("\n", "HTTP/1.1 200 OK\r\nContent-Disposition: attachment; filename=AOMediaCodec-libavif-v1.1.1-0-gbb24db0.tar.gz\r\n\r\n");
return true;
}
if (str_contains($command, 'https://api.bitbucket.org/2.0/repositories/')) {
$output = explode("\n", json_encode(['values' => [['name' => '1.0.0']], 'tag_name' => '1.0.0']));
return true;
}
if (str_contains($command, 'https://bitbucket.org/')) {
$output = explode("\n", str_contains($command, 'MATCHED') ? "HTTP/2 200 OK\r\ncontent-disposition: attachment; filename=abc.tar.gz\r\n\r\n" : "HTTP/2 200 OK\r\n\r\n");
return true;
}
if (str_contains($command, 'ghreltest/ghrel')) {
$output = explode("\n", json_encode([[
'prerelease' => false,
'assets' => [
[
'name' => 'ghreltest.tar.gz',
'browser_download_url' => 'https://fakecmd.com/ghreltest.tar.gz',
],
],
]]));
return true;
}
if (str_contains($command, 'filelist')) {
$output = explode("\n", gzdecode(file_get_contents(__DIR__ . '/../assets/filelist.gz')));
return true;
}
$result_code = -2;
$output = null;
return false;
}
function f_passthru(string $cmd): bool
{
if (str_starts_with($cmd, 'git')) {
if (str_contains($cmd, '--branch "SIGINT"')) {
throw new RuntimeException('Interrupt', 2);
}
return true;
}
if (str_contains($cmd, 'https://fakecmd.com/curlDown')) {
if (str_contains($cmd, 'SIGINT')) {
throw new RuntimeException('Interrupt', 2);
}
return true;
}
// allowed commands
$allowed = ['cp', 'copy', 'xcopy'];
foreach ($allowed as $a) {
if (str_starts_with($cmd, $a)) {
\f_passthru($cmd);
return true;
}
}
throw new RuntimeException('Invalid tests');
}