Compare commits

..

20 Commits

Author SHA1 Message Date
crazywhalecc
a00f8945ba add glfw support for macOS 2023-08-19 00:27:20 +08:00
crazywhalecc
2bc02bdaac add glfw support for macOS 2023-08-19 00:27:20 +08:00
Chuong
b97327d6d7 mongodb source.json change from tgz to zip
since ver https://github.com/mongodb/mongo-php-driver/releases/tag/1.16.2

they don't support tgz anymore.
2023-08-18 17:57:08 +08:00
crazywhalecc
61d1507a4d add redis-session support 2023-08-09 11:01:36 +08:00
crazywhalecc
a6f07051c3 update readme about version and dependencies 2023-08-08 20:35:25 +08:00
crazywhalecc
75417d15b7 change laravel/prompts version 2023-08-08 20:30:06 +08:00
crazywhalecc
4bab7ecfab add laravel/prompts support, and change deploy commands and exception handlers 2023-08-08 20:30:06 +08:00
crazywhalecc
7298e2441b remove list-ext command, use all-ext and alias to support its function 2023-08-08 19:24:11 +08:00
Jerry Ma
1ed104d2f6 Update README-en.md 2023-08-07 11:38:22 +08:00
Jerry Ma
cf6125b9cc Update README.md 2023-08-07 11:38:12 +08:00
crazywhalecc
f2cfe33cdd remove gotop (Why I needed it?) 2023-08-06 12:33:26 +08:00
crazywhalecc
a1e4125ded replace symfony console return values 2023-08-06 10:44:36 +08:00
Viktor Szépe
7a1433a994 Fix output of ExtractCommand 2023-08-06 10:32:15 +08:00
crazywhalecc
460238a6b0 add hardcoded ini injection on build process 2023-08-02 22:16:33 +08:00
crazywhalecc
2a197487d5 add php 8.3 support for phpmicro 2023-08-01 23:45:51 +08:00
crazywhalecc
51ce2befd8 add library, extension patches in separate classes 2023-08-01 23:42:02 +08:00
crazywhalecc
e909dd15b0 add library, extension patch methods base 2023-08-01 23:42:02 +08:00
crazywhalecc
aaf712be3c add comments 2023-08-01 23:42:02 +08:00
crazywhalecc
725e6b25dc Fix #111 2023-07-29 19:02:19 +08:00
crazywhalecc
ea322c0d3b add extract command, add -U option (custom download source 2023-07-28 00:29:19 +08:00
48 changed files with 807 additions and 357 deletions

View File

@@ -11,7 +11,7 @@ This feature is provided by [dixyes/phpmicro](https://github.com/dixyes/phpmicro
<img width="600" alt="截屏2023-05-02 15 52 33" src="https://user-images.githubusercontent.com/20330940/235610318-2ef4e3f1-278b-4ca4-99f4-b38120efc395.png"> <img width="600" alt="截屏2023-05-02 15 52 33" src="https://user-images.githubusercontent.com/20330940/235610318-2ef4e3f1-278b-4ca4-99f4-b38120efc395.png">
[![Version](https://img.shields.io/badge/Version-2.0--rc3-pink.svg?style=flat-square)]() [![Version](https://img.shields.io/badge/Version-2.0--rc4-pink.svg?style=flat-square)]()
[![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)]() [![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)]()
[![](https://img.shields.io/github/actions/workflow/status/crazywhalecc/static-php-cli/build-linux-x86_64.yml?branch=refactor&label=Linux%20Build&style=flat-square)](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml) [![](https://img.shields.io/github/actions/workflow/status/crazywhalecc/static-php-cli/build-linux-x86_64.yml?branch=refactor&label=Linux%20Build&style=flat-square)](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml)
[![](https://img.shields.io/github/actions/workflow/status/crazywhalecc/static-php-cli/build-macos-x86_64.yml?branch=refactor&label=macOS%20Build&style=flat-square)](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml) [![](https://img.shields.io/github/actions/workflow/status/crazywhalecc/static-php-cli/build-macos-x86_64.yml?branch=refactor&label=macOS%20Build&style=flat-square)](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml)
@@ -24,7 +24,7 @@ This feature is provided by [dixyes/phpmicro](https://github.com/dixyes/phpmicro
## Compilation Requirements ## Compilation Requirements
Yes, this project is written in PHP, pretty funny. Yes, this project is written in PHP, pretty funny.
But static-php-cli runtime only requires an environment above PHP 8.0 and `tokenizer`, `iconv` extension. But static-php-cli runtime only requires an environment above PHP 8.1 and `mbstring`, `pcntl` extension.
Here is the architecture support status, where `CI` represents support for GitHub Action builds, Here is the architecture support status, where `CI` represents support for GitHub Action builds,
`Local` represents support for local builds, and blank represents not currently supported. `Local` represents support for local builds, and blank represents not currently supported.

View File

@@ -12,7 +12,7 @@ Compile A Statically Linked PHP With Swoole and other Extensions.
<img width="600" alt="截屏2023-05-02 15 52 33" src="https://user-images.githubusercontent.com/20330940/235610318-2ef4e3f1-278b-4ca4-99f4-b38120efc395.png"> <img width="600" alt="截屏2023-05-02 15 52 33" src="https://user-images.githubusercontent.com/20330940/235610318-2ef4e3f1-278b-4ca4-99f4-b38120efc395.png">
[![Version](https://img.shields.io/badge/Version-2.0--rc3-pink.svg?style=flat-square)]() [![Version](https://img.shields.io/badge/Version-2.0--rc4-pink.svg?style=flat-square)]()
[![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)]() [![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)]()
[![](https://img.shields.io/github/actions/workflow/status/crazywhalecc/static-php-cli/build-linux-x86_64.yml?branch=refactor&label=Linux%20Build&style=flat-square)](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml) [![](https://img.shields.io/github/actions/workflow/status/crazywhalecc/static-php-cli/build-linux-x86_64.yml?branch=refactor&label=Linux%20Build&style=flat-square)](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml)
[![](https://img.shields.io/github/actions/workflow/status/crazywhalecc/static-php-cli/build-macos-x86_64.yml?branch=refactor&label=macOS%20Build&style=flat-square)](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml) [![](https://img.shields.io/github/actions/workflow/status/crazywhalecc/static-php-cli/build-macos-x86_64.yml?branch=refactor&label=macOS%20Build&style=flat-square)](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml)
@@ -24,7 +24,7 @@ Compile A Statically Linked PHP With Swoole and other Extensions.
## 编译环境需求 ## 编译环境需求
是的,本项目采用 PHP 编写,编译前需要一个 PHP 环境,比较滑稽。 是的,本项目采用 PHP 编写,编译前需要一个 PHP 环境,比较滑稽。
但本项目默认可通过自身构建的 micro 和 static-php 二进制运行,其他只需要包含 tokenizer 扩展和 PHP 版本大于等于 8.0 即可。 但本项目默认可通过自身构建的 micro 和 static-php 二进制运行,其他只需要包含 mbstring、pcntl 扩展和 PHP 版本大于等于 8.1 即可。
下面是架构支持情况,`CI` 代表支持 GitHub Action 构建,`Local` 代表支持本地构建,空 代表暂不支持。 下面是架构支持情况,`CI` 代表支持 GitHub Action 构建,`Local` 代表支持本地构建,空 代表暂不支持。

View File

@@ -9,16 +9,15 @@
} }
], ],
"require": { "require": {
"php": ">= 8.0", "php": ">= 8.1",
"ext-tokenizer": "*", "ext-mbstring": "*",
"ext-iconv": "*", "ext-pcntl": "*",
"laravel/prompts": "^0.1.3",
"symfony/console": "^6 || ^5 || ^4", "symfony/console": "^6 || ^5 || ^4",
"zhamao/logger": "^1.0", "zhamao/logger": "^1.0"
"crazywhalecc/cli-helper": "^0.1.0",
"nunomaduro/collision": "*",
"ext-pcntl": "*"
}, },
"require-dev": { "require-dev": {
"nunomaduro/collision": "*",
"friendsofphp/php-cs-fixer": "^3.2 != 3.7.0", "friendsofphp/php-cs-fixer": "^3.2 != 3.7.0",
"phpstan/phpstan": "^1.1", "phpstan/phpstan": "^1.1",
"captainhook/captainhook": "^5.10", "captainhook/captainhook": "^5.10",

View File

@@ -135,6 +135,14 @@
"imagemagick" "imagemagick"
] ]
}, },
"glfw": {
"type": "external",
"arg-type": "custom",
"source": "ext-glfw",
"lib-depends": [
"glfw"
]
},
"imap": { "imap": {
"type": "builtin", "type": "builtin",
"arg-type": "with", "arg-type": "with",
@@ -208,7 +216,10 @@
}, },
"mysqlnd": { "mysqlnd": {
"type": "builtin", "type": "builtin",
"arg-type-windows": "with" "arg-type-windows": "with",
"lib-depends": [
"zlib"
]
}, },
"opcache": { "opcache": {
"type": "builtin" "type": "builtin"
@@ -295,7 +306,10 @@
"redis": { "redis": {
"type": "external", "type": "external",
"source": "redis", "source": "redis",
"arg-type": "custom" "arg-type": "custom",
"ext-suggests": [
"session"
]
}, },
"session": { "session": {
"type": "builtin" "type": "builtin"

View File

@@ -15,6 +15,18 @@
"brotli" "brotli"
] ]
}, },
"glfw": {
"source": "ext-glfw",
"static-libs-unix": [
"libglfw3.a"
],
"frameworks": [
"CoreVideo",
"OpenGL",
"Cocoa",
"IOKit"
]
},
"bzip2": { "bzip2": {
"source": "bzip2", "source": "bzip2",
"static-libs-unix": [ "static-libs-unix": [

View File

@@ -6,6 +6,15 @@
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"ext-glfw": {
"type": "git",
"url": "https://github.com/mario-deluna/php-glfw",
"rev": "master",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"apcu": { "apcu": {
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/APCu", "url": "https://pecl.php.net/get/APCu",
@@ -295,7 +304,7 @@
"type": "ghrel", "type": "ghrel",
"repo": "mongodb/mongo-php-driver", "repo": "mongodb/mongo-php-driver",
"path": "php-src/ext/mongodb", "path": "php-src/ext/mongodb",
"match": "mongodb.+\\.tgz", "match": "mongodb.+\\.zip",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"

View File

@@ -4,10 +4,8 @@ declare(strict_types=1);
namespace SPC; namespace SPC;
use SPC\command\DeployCommand;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use Symfony\Component\Console\Application; use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\HelpCommand; use Symfony\Component\Console\Command\HelpCommand;
use Symfony\Component\Console\Command\ListCommand; use Symfony\Component\Console\Command\ListCommand;
@@ -16,7 +14,7 @@ use Symfony\Component\Console\Command\ListCommand;
*/ */
class ConsoleApplication extends Application class ConsoleApplication extends Application
{ {
public const VERSION = '2.0-rc3'; public const VERSION = '2.0-rc5';
/** /**
* @throws \ReflectionException * @throws \ReflectionException
@@ -33,13 +31,19 @@ class ConsoleApplication extends Application
// 通过扫描目录 src/static-php-cli/command/ 添加子命令 // 通过扫描目录 src/static-php-cli/command/ 添加子命令
$commands = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/command', 'SPC\\command'); $commands = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/command', 'SPC\\command');
$this->addCommands(array_map(function ($x) { return new $x(); }, array_filter($commands, function ($y) { $phar = class_exists('\\Phar') && \Phar::running() || !class_exists('\\Phar');
if (is_a($y, DeployCommand::class, true) && (class_exists('\\Phar') && \Phar::running() || !class_exists('\\Phar'))) { $commands = array_filter($commands, function ($y) use ($phar) {
$archive_blacklist = [
'SPC\command\dev\SortConfigCommand',
'SPC\command\DeployCommand',
];
if ($phar && in_array($y, $archive_blacklist)) {
return false; return false;
} }
$reflection = new \ReflectionClass($y); $reflection = new \ReflectionClass($y);
return !$reflection->isAbstract() && !$reflection->isInterface(); return !$reflection->isAbstract() && !$reflection->isInterface();
}))); });
$this->addCommands(array_map(function ($x) { return new $x(); }, $commands));
} }
/** /**

View File

@@ -9,6 +9,7 @@ use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\SourceExtractor;
use SPC\util\CustomExt; use SPC\util\CustomExt;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
@@ -87,7 +88,7 @@ abstract class BuilderBase
$lib->calcDependency(); $lib->calcDependency();
} }
$this->initSource(libs: $libraries); SourceExtractor::initSource(libs: $libraries);
// 构建库 // 构建库
foreach ($this->libs as $lib) { foreach ($this->libs as $lib) {
@@ -184,15 +185,17 @@ abstract class BuilderBase
* *
* @throws FileSystemException * @throws FileSystemException
* @throws RuntimeException * @throws RuntimeException
* @throws \ReflectionException
* @throws WrongUsageException
*/ */
public function proveExts(array $extensions): void public function proveExts(array $extensions): void
{ {
CustomExt::loadCustomExt(); CustomExt::loadCustomExt();
$this->initSource(sources: ['php-src']); SourceExtractor::initSource(sources: ['php-src']);
if ($this->getPHPVersionID() >= 80000) { if ($this->getPHPVersionID() >= 80000) {
$this->initSource(sources: ['micro']); SourceExtractor::initSource(sources: ['micro']);
} }
$this->initSource(exts: $extensions); SourceExtractor::initSource(exts: $extensions);
foreach ($extensions as $extension) { foreach ($extensions as $extension) {
$class = CustomExt::getExtClass($extension); $class = CustomExt::getExtClass($extension);
$ext = new $class($extension, $this); $ext = new $class($extension, $this);
@@ -221,6 +224,7 @@ abstract class BuilderBase
* *
* @throws RuntimeException * @throws RuntimeException
* @throws FileSystemException * @throws FileSystemException
* @throws WrongUsageException
*/ */
public function makeExtensionArgs(): string public function makeExtensionArgs(): string
{ {
@@ -292,52 +296,4 @@ abstract class BuilderBase
); );
} }
} }
protected function initSource(?array $sources = null, ?array $libs = null, ?array $exts = null): void
{
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
throw new WrongUsageException('Download lock file "downloads/.lock.json" not found, maybe you need to download sources first ?');
}
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true);
$sources_extracted = [];
// source check exist
if (is_array($sources)) {
foreach ($sources as $source) {
$sources_extracted[$source] = true;
}
}
// lib check source exist
if (is_array($libs)) {
foreach ($libs as $lib) {
// get source name for lib
$source = Config::getLib($lib, 'source');
$sources_extracted[$source] = true;
}
}
// ext check source exist
if (is_array($exts)) {
foreach ($exts as $ext) {
// get source name for ext
if (Config::getExt($ext, 'type') !== 'external') {
continue;
}
$source = Config::getExt($ext, 'source');
$sources_extracted[$source] = true;
}
}
// start check
foreach ($sources_extracted as $source => $item) {
if (!isset($lock[$source])) {
throw new WrongUsageException('Source [' . $source . '] not downloaded, you should download it first !');
}
// check source dir exist
$check = $lock[$source]['move_path'] === null ? SOURCE_PATH . '/' . $source : SOURCE_PATH . '/' . $lock[$source]['move_path'];
if (!is_dir($check)) {
FileSystem::extractSource($source, DOWNLOAD_PATH . '/' . ($lock[$source]['filename'] ?? $lock[$source]['dirname']), $lock[$source]['move_path']);
}
}
}
} }

View File

@@ -136,6 +136,33 @@ class Extension
return ''; return '';
} }
/**
* Patch code before ./buildconf
* If you need to patch some code, overwrite this and return true
*/
public function patchBeforeBuildconf(): bool
{
return false;
}
/**
* Patch code before ./configure
* If you need to patch some code, overwrite this and return true
*/
public function patchBeforeConfigure(): bool
{
return false;
}
/**
* Patch code before make
* If you need to patch some code, overwrite this and return true
*/
public function patchBeforeMake(): bool
{
return false;
}
/** /**
* @throws RuntimeException * @throws RuntimeException
*/ */

View File

@@ -135,6 +135,7 @@ abstract class LibraryBase
// 传入 true表明直接编译 // 传入 true表明直接编译
if ($force_build) { if ($force_build) {
logger()->info('Building required library [' . static::NAME . ']'); logger()->info('Building required library [' . static::NAME . ']');
$this->patchBeforeBuild();
$this->build(); $this->build();
return BUILD_STATUS_OK; return BUILD_STATUS_OK;
} }
@@ -162,6 +163,14 @@ abstract class LibraryBase
return BUILD_STATUS_ALREADY; return BUILD_STATUS_ALREADY;
} }
/**
* Patch before build, overwrite this and return true to patch libs
*/
public function patchBeforeBuild(): bool
{
return false;
}
/** /**
* 获取构建当前 lib 的 Builder 对象 * 获取构建当前 lib 的 Builder 对象
*/ */

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\builder\macos\MacOSBuilder;
use SPC\exception\FileSystemException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('bz2')]
class bz2 extends Extension
{
/**
* @throws FileSystemException
*/
public function patchBeforeConfigure(): bool
{
$frameworks = $this->builder instanceof MacOSBuilder ? ' ' . $this->builder->getFrameworks(true) . ' ' : '';
FileSystem::replaceFile(SOURCE_PATH . '/php-src/configure', REPLACE_FILE_PREG, '/-lbz2/', $this->getLibFilesString() . $frameworks);
return true;
}
}

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\builder\macos\MacOSBuilder;
use SPC\exception\FileSystemException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('curl')]
class curl extends Extension
{
public function patchBeforeBuildconf(): bool
{
logger()->info('patching before-configure for curl checks');
$file1 = "AC_DEFUN([PHP_CHECK_LIBRARY], [\n $3\n])";
$files = FileSystem::readFile(SOURCE_PATH . '/php-src/ext/curl/config.m4');
$file2 = 'AC_DEFUN([PHP_CHECK_LIBRARY], [
save_old_LDFLAGS=$LDFLAGS
ac_stuff="$5"
save_ext_shared=$ext_shared
ext_shared=yes
PHP_EVAL_LIBLINE([$]ac_stuff, LDFLAGS)
AC_CHECK_LIB([$1],[$2],[
LDFLAGS=$save_old_LDFLAGS
ext_shared=$save_ext_shared
$3
],[
LDFLAGS=$save_old_LDFLAGS
ext_shared=$save_ext_shared
unset ac_cv_lib_$1[]_$2
$4
])dnl
])';
file_put_contents(SOURCE_PATH . '/php-src/ext/curl/config.m4', $file1 . "\n" . $files . "\n" . $file2);
return true;
}
/**
* @throws FileSystemException
*/
public function patchBeforeConfigure(): bool
{
$frameworks = $this->builder instanceof MacOSBuilder ? ' ' . $this->builder->getFrameworks(true) . ' ' : '';
FileSystem::replaceFile(SOURCE_PATH . '/php-src/configure', REPLACE_FILE_PREG, '/-lcurl/', $this->getLibFilesString() . $frameworks);
return true;
}
}

View File

@@ -5,6 +5,8 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\exception\FileSystemException;
use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
#[CustomExt('event')] #[CustomExt('event')]
@@ -23,4 +25,18 @@ class event extends Extension
} }
return $arg; return $arg;
} }
/**
* @throws FileSystemException
*/
public function patchBeforeConfigure(): bool
{
FileSystem::replaceFile(
SOURCE_PATH . '/php-src/configure',
REPLACE_FILE_PREG,
'/-levent_openssl/',
$this->getLibFilesString()
);
return true;
}
} }

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('glfw')]
class glfw extends Extension
{
public function patchBeforeBuildconf(): bool
{
FileSystem::copyDir(SOURCE_PATH . '/ext-glfw', SOURCE_PATH . '/php-src/ext/glfw');
return true;
}
public function getUnixConfigureArg(): string
{
return '--enable-glfw --with-glfw-dir=' . BUILD_ROOT_PATH;
}
}

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
#[CustomExt('memcache')] #[CustomExt('memcache')]
@@ -14,4 +15,34 @@ class memcache extends Extension
{ {
return '--enable-memcache --with-zlib-dir=' . BUILD_ROOT_PATH; return '--enable-memcache --with-zlib-dir=' . BUILD_ROOT_PATH;
} }
public function patchBeforeBuildconf(): bool
{
FileSystem::replaceFile(
SOURCE_PATH . '/php-src/ext/memcache/config9.m4',
REPLACE_FILE_STR,
'if test -d $abs_srcdir/src ; then',
'if test -d $abs_srcdir/main ; then'
);
FileSystem::replaceFile(
SOURCE_PATH . '/php-src/ext/memcache/config9.m4',
REPLACE_FILE_STR,
'export CPPFLAGS="$CPPFLAGS $INCLUDES"',
'export CPPFLAGS="$CPPFLAGS $INCLUDES -I$abs_srcdir/main"'
);
// add for in-tree building
file_put_contents(
SOURCE_PATH . '/php-src/ext/memcache/php_memcache.h',
<<<'EOF'
#ifndef PHP_MEMCACHE_H
#define PHP_MEMCACHE_H
extern zend_module_entry memcache_module_entry;
#define phpext_memcache_ptr &memcache_module_entry
#endif
EOF
);
return true;
}
} }

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\util\CustomExt;
use SPC\util\Util;
#[CustomExt('openssl')]
class openssl extends Extension
{
public function patchBeforeMake(): bool
{
// patch openssl3 with php8.0 bug
if (file_exists(SOURCE_PATH . '/openssl/VERSION.dat') && Util::getPHPVersionID() < 80100) {
$openssl_c = file_get_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c');
$openssl_c = preg_replace('/REGISTER_LONG_CONSTANT\s*\(\s*"OPENSSL_SSLV23_PADDING"\s*.+;/', '', $openssl_c);
file_put_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c', $openssl_c);
return true;
}
return false;
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\exception\FileSystemException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('pdo_sqlite')]
class pdo_sqlite extends Extension
{
/**
* @throws FileSystemException
*/
public function patchBeforeConfigure(): bool
{
FileSystem::replaceFile(
SOURCE_PATH . '/php-src/configure',
REPLACE_FILE_PREG,
'/sqlite3_column_table_name=yes/',
'sqlite3_column_table_name=no'
);
return true;
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\exception\FileSystemException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('pgsql')]
class pgsql extends Extension
{
/**
* @throws FileSystemException
*/
public function patchBeforeConfigure(): bool
{
FileSystem::replaceFile(
SOURCE_PATH . '/php-src/configure',
REPLACE_FILE_PREG,
'/-lpq/',
$this->getLibFilesString()
);
return true;
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\exception\FileSystemException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('readline')]
class readline extends Extension
{
/**
* @throws FileSystemException
*/
public function patchBeforeConfigure(): bool
{
FileSystem::replaceFile(
SOURCE_PATH . '/php-src/configure',
REPLACE_FILE_PREG,
'/-lncurses/',
$this->getLibFilesString()
);
return true;
}
}

View File

@@ -12,7 +12,12 @@ class redis extends Extension
{ {
public function getUnixConfigureArg(): string public function getUnixConfigureArg(): string
{ {
$arg = '--enable-redis --disable-redis-session'; $arg = '--enable-redis';
if (!$this->builder->getExt('session')) {
$arg .= ' --disable-redis-session';
} else {
$arg .= ' --enable-redis-session';
}
if ($this->builder->getLib('zstd')) { if ($this->builder->getLib('zstd')) {
$arg .= ' --enable-redis-zstd --with-libzstd="' . BUILD_ROOT_PATH . '"'; $arg .= ' --enable-redis-zstd --with-libzstd="' . BUILD_ROOT_PATH . '"';
} }

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\exception\FileSystemException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('ssh2')]
class ssh2 extends Extension
{
/**
* @throws FileSystemException
*/
public function patchBeforeConfigure(): bool
{
FileSystem::replaceFile(
SOURCE_PATH . '/php-src/configure',
REPLACE_FILE_PREG,
'/-lssh2/',
$this->getLibFilesString()
);
return true;
}
}

View File

@@ -6,6 +6,7 @@ namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\util\CustomExt; use SPC\util\CustomExt;
use SPC\util\Util;
#[CustomExt('swow')] #[CustomExt('swow')]
class swow extends Extension class swow extends Extension
@@ -17,4 +18,17 @@ class swow extends Extension
$arg .= $this->builder->getLib('curl') ? ' --enable-swow-curl' : ' --disable-swow-curl'; $arg .= $this->builder->getLib('curl') ? ' --enable-swow-curl' : ' --disable-swow-curl';
return $arg; return $arg;
} }
public function patchBeforeBuildconf(): bool
{
if (Util::getPHPVersionID() >= 80000 && !is_link(SOURCE_PATH . '/php-src/ext/swow')) {
if (PHP_OS_FAMILY === 'Windows') {
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && mklink /D swow swow-src\ext');
} else {
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && ln -s swow-src/ext swow');
}
return true;
}
return false;
}
} }

View File

@@ -169,11 +169,11 @@ class LinuxBuilder extends BuilderBase
$envs = "{$envs} CFLAGS='{$cflags}' LIBS='-ldl -lpthread'"; $envs = "{$envs} CFLAGS='{$cflags}' LIBS='-ldl -lpthread'";
SourcePatcher::patchPHPBuildconf($this); SourcePatcher::patchBeforeBuildconf($this);
shell()->cd(SOURCE_PATH . '/php-src')->exec('./buildconf --force'); shell()->cd(SOURCE_PATH . '/php-src')->exec('./buildconf --force');
SourcePatcher::patchPHPConfigure($this); SourcePatcher::patchBeforeConfigure($this);
if ($this->getPHPVersionID() < 80000) { if ($this->getPHPVersionID() < 80000) {
$json_74 = '--enable-json '; $json_74 = '--enable-json ';
@@ -200,7 +200,7 @@ class LinuxBuilder extends BuilderBase
$envs $envs
); );
SourcePatcher::patchPHPAfterConfigure($this); SourcePatcher::patchBeforeMake($this);
file_put_contents('/tmp/comment', $this->note_section); file_put_contents('/tmp/comment', $this->note_section);

View File

@@ -43,6 +43,7 @@ abstract class LinuxLibraryBase extends LibraryBase
// 传入 true表明直接编译 // 传入 true表明直接编译
if ($force_build) { if ($force_build) {
logger()->info('Building required library [' . static::NAME . ']'); logger()->info('Building required library [' . static::NAME . ']');
$this->patchBeforeBuild();
$this->build(); $this->build();
return BUILD_STATUS_OK; return BUILD_STATUS_OK;
} }

View File

@@ -20,14 +20,34 @@ declare(strict_types=1);
namespace SPC\builder\linux\library; namespace SPC\builder\linux\library;
use SPC\builder\linux\SystemUtil;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\store\SourcePatcher; use SPC\store\FileSystem;
class libpng extends LinuxLibraryBase class libpng extends LinuxLibraryBase
{ {
public const NAME = 'libpng'; public const NAME = 'libpng';
public function patchBeforeBuild(): bool
{
FileSystem::replaceFile(
SOURCE_PATH . '/libpng/configure',
REPLACE_FILE_STR,
'-lz',
BUILD_LIB_PATH . '/libz.a'
);
if (SystemUtil::getOSRelease()['dist'] === 'alpine') {
FileSystem::replaceFile(
SOURCE_PATH . '/libpng/configure',
REPLACE_FILE_STR,
'-lm',
'/usr/lib/libm.a'
);
}
return true;
}
/** /**
* @throws RuntimeException * @throws RuntimeException
* @throws FileSystemException * @throws FileSystemException
@@ -39,10 +59,6 @@ class libpng extends LinuxLibraryBase
'arm64' => '--enable-arm-neon ', 'arm64' => '--enable-arm-neon ',
default => '', default => '',
}; };
// patch configure
SourcePatcher::patchUnixLibpng();
shell()->cd($this->source_dir) shell()->cd($this->source_dir)
->exec('chmod +x ./configure') ->exec('chmod +x ./configure')
->exec('chmod +x ./install-sh') ->exec('chmod +x ./install-sh')

View File

@@ -140,12 +140,12 @@ class MacOSBuilder extends BuilderBase
); );
} }
// patch before configure // patch before buildconf
SourcePatcher::patchPHPBuildconf($this); SourcePatcher::patchBeforeBuildconf($this);
shell()->cd(SOURCE_PATH . '/php-src')->exec('./buildconf --force'); shell()->cd(SOURCE_PATH . '/php-src')->exec('./buildconf --force');
SourcePatcher::patchPHPConfigure($this); SourcePatcher::patchBeforeConfigure($this);
if ($this->getLib('libxml2') || $this->getExt('iconv')) { if ($this->getLib('libxml2') || $this->getExt('iconv')) {
$extra_libs .= ' -liconv'; $extra_libs .= ' -liconv';
@@ -177,7 +177,7 @@ class MacOSBuilder extends BuilderBase
$this->configure_env $this->configure_env
); );
SourcePatcher::patchPHPAfterConfigure($this); SourcePatcher::patchBeforeMake($this);
$this->cleanMake(); $this->cleanMake();

View File

@@ -20,19 +20,32 @@ declare(strict_types=1);
namespace SPC\builder\macos\library; namespace SPC\builder\macos\library;
use SPC\store\SourcePatcher; use SPC\exception\FileSystemException;
use SPC\store\FileSystem;
class curl extends MacOSLibraryBase class curl extends MacOSLibraryBase
{ {
use \SPC\builder\unix\library\curl { use \SPC\builder\unix\library\curl;
build as unixBuild;
}
public const NAME = 'curl'; public const NAME = 'curl';
protected function build() /**
* @throws FileSystemException
*/
public function patchBeforeBuild(): bool
{ {
SourcePatcher::patchCurlMacOS(); FileSystem::replaceFile(
$this->unixBuild(); SOURCE_PATH . '/curl/CMakeLists.txt',
REPLACE_FILE_PREG,
'/NOT COREFOUNDATION_FRAMEWORK/m',
'FALSE'
);
FileSystem::replaceFile(
SOURCE_PATH . '/curl/CMakeLists.txt',
REPLACE_FILE_PREG,
'/NOT SYSTEMCONFIGURATION_FRAMEWORK/m',
'FALSE'
);
return true;
} }
} }

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace SPC\builder\macos\library;
class glfw extends MacOSLibraryBase
{
public const NAME = 'glfw';
protected function build()
{
// compile
shell()->cd(SOURCE_PATH . '/ext-glfw/vendor/glfw')
->exec("{$this->builder->configure_env} cmake . {$this->builder->makeCmakeArgs()} -DBUILD_SHARED_LIBS=OFF -DGLFW_BUILD_EXAMPLES=OFF -DGLFW_BUILD_TESTS=OFF")
->exec("make -j{$this->builder->concurrency}")
->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
// patch pkgconf
$this->patchPkgconfPrefix(['glfw3.pc']);
}
}

View File

@@ -91,7 +91,7 @@ abstract class BaseCommand extends Command
foreach ($msg as $v) { foreach ($msg as $v) {
logger()->error($v); logger()->error($v);
} }
return self::FAILURE; return static::FAILURE;
} catch (\Throwable $e) { } catch (\Throwable $e) {
// 不开 debug 模式就不要再显示复杂的调试栈信息了 // 不开 debug 模式就不要再显示复杂的调试栈信息了
if ($this->getOption('debug')) { if ($this->getOption('debug')) {
@@ -102,10 +102,10 @@ abstract class BaseCommand extends Command
logger()->error($v); logger()->error($v);
} }
} }
return self::FAILURE; return static::FAILURE;
} }
} }
return self::SUCCESS; return static::SUCCESS;
} }
protected function getOption(string $name): mixed protected function getOption(string $name): mixed

View File

@@ -7,6 +7,7 @@ namespace SPC\command;
use SPC\builder\BuilderProvider; use SPC\builder\BuilderProvider;
use SPC\exception\ExceptionHandler; use SPC\exception\ExceptionHandler;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\SourcePatcher;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\LicenseDumper; use SPC\util\LicenseDumper;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
@@ -27,6 +28,7 @@ class BuildCliCommand extends BuildCommand
$this->addOption('build-all', null, null, 'build cli, micro, fpm'); $this->addOption('build-all', null, null, 'build cli, micro, fpm');
$this->addOption('no-strip', null, null, 'build without strip, in order to debug and load external extensions'); $this->addOption('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('enable-zts', null, null, 'enable ZTS support');
$this->addOption('with-hardcoded-ini', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Patch PHP source code, inject hardcoded INI');
} }
public function handle(): int public function handle(): int
@@ -49,7 +51,7 @@ class BuildCliCommand extends BuildCommand
$this->output->writeln("<comment>\t--build-micro\tBuild phpmicro SAPI</comment>"); $this->output->writeln("<comment>\t--build-micro\tBuild phpmicro SAPI</comment>");
$this->output->writeln("<comment>\t--build-fpm\tBuild php-fpm SAPI</comment>"); $this->output->writeln("<comment>\t--build-fpm\tBuild php-fpm SAPI</comment>");
$this->output->writeln("<comment>\t--build-all\tBuild all SAPI: cli, micro, fpm</comment>"); $this->output->writeln("<comment>\t--build-all\tBuild all SAPI: cli, micro, fpm</comment>");
return 1; return static::FAILURE;
} }
try { try {
// 构建对象 // 构建对象
@@ -72,6 +74,16 @@ class BuildCliCommand extends BuildCommand
$builder->proveExts($extensions); $builder->proveExts($extensions);
// strip // strip
$builder->setStrip(!$this->getOption('no-strip')); $builder->setStrip(!$this->getOption('no-strip'));
// Process -I option
$custom_ini = [];
foreach ($this->input->getOption('with-hardcoded-ini') as $value) {
[$source_name, $ini_value] = explode('=', $value, 2);
$custom_ini[$source_name] = $ini_value;
logger()->info('Adding hardcoded INI [' . $source_name . ' = ' . $ini_value . ']');
}
if (!empty($custom_ini)) {
SourcePatcher::patchHardcodedINI($custom_ini);
}
// 构建 // 构建
$builder->buildPHP($rule, $this->getOption('bloat')); $builder->buildPHP($rule, $this->getOption('bloat'));
// 统计时间 // 统计时间
@@ -101,10 +113,10 @@ class BuildCliCommand extends BuildCommand
$dumper = new LicenseDumper(); $dumper = new LicenseDumper();
$dumper->addExts($extensions)->addLibs($libraries)->addSources(['php-src'])->dump(BUILD_ROOT_PATH . '/license'); $dumper->addExts($extensions)->addLibs($libraries)->addSources(['php-src'])->dump(BUILD_ROOT_PATH . '/license');
logger()->info('License path' . $fixed . ': ' . $build_root_path . '/license/'); logger()->info('License path' . $fixed . ': ' . $build_root_path . '/license/');
return 0; return static::SUCCESS;
} catch (WrongUsageException $e) { } catch (WrongUsageException $e) {
logger()->critical($e->getMessage()); logger()->critical($e->getMessage());
return 1; return static::FAILURE;
} catch (\Throwable $e) { } catch (\Throwable $e) {
if ($this->getOption('debug')) { if ($this->getOption('debug')) {
ExceptionHandler::getInstance()->handle($e); ExceptionHandler::getInstance()->handle($e);
@@ -112,7 +124,7 @@ class BuildCliCommand extends BuildCommand
logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage()); logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage());
logger()->critical('Please check with --debug option to see more details.'); logger()->critical('Please check with --debug option to see more details.');
} }
return 1; return static::FAILURE;
} }
} }
} }

View File

@@ -63,7 +63,7 @@ class BuildLibsCommand extends BuildCommand
$time = round(microtime(true) - START_TIME, 3); $time = round(microtime(true) - START_TIME, 3);
logger()->info('Build libs complete, used ' . $time . ' s !'); logger()->info('Build libs complete, used ' . $time . ' s !');
return 0; return static::SUCCESS;
} catch (\Throwable $e) { } catch (\Throwable $e) {
if ($this->getOption('debug')) { if ($this->getOption('debug')) {
ExceptionHandler::getInstance()->handle($e); ExceptionHandler::getInstance()->handle($e);
@@ -71,7 +71,7 @@ class BuildLibsCommand extends BuildCommand
logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage()); logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage());
logger()->critical('Please check with --debug option to see more details.'); logger()->critical('Please check with --debug option to see more details.');
} }
return 1; return static::FAILURE;
} }
} }
} }

View File

@@ -4,14 +4,15 @@ declare(strict_types=1);
namespace SPC\command; namespace SPC\command;
use CliHelper\Tools\ArgFixer; use SPC\store\FileSystem;
use CliHelper\Tools\DataProvider;
use CliHelper\Tools\SeekableArrayIterator;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\text;
#[AsCommand('deploy', 'Deploy static-php-cli self to an .phar application')] #[AsCommand('deploy', 'Deploy static-php-cli self to an .phar application')]
class DeployCommand extends BaseCommand class DeployCommand extends BaseCommand
{ {
@@ -20,26 +21,51 @@ class DeployCommand extends BaseCommand
$this->addArgument('target', InputArgument::OPTIONAL, 'The file or directory to pack.'); $this->addArgument('target', InputArgument::OPTIONAL, 'The file or directory to pack.');
$this->addOption('auto-phar-fix', null, InputOption::VALUE_NONE, 'Automatically fix ini option.'); $this->addOption('auto-phar-fix', null, InputOption::VALUE_NONE, 'Automatically fix ini option.');
$this->addOption('overwrite', 'W', InputOption::VALUE_NONE, 'Overwrite existing files.'); $this->addOption('overwrite', 'W', InputOption::VALUE_NONE, 'Overwrite existing files.');
$this->addOption('with-no-dev', 'D', InputOption::VALUE_NONE, 'Automatically use non-dev composer dependencies to reduce size');
$this->addOption('with-dev', 'd', InputOption::VALUE_NONE, 'Automatically use dev composer dependencies');
} }
public function handle(): int public function handle(): int
{ {
// 第一阶段流程如果没有写path将会提示输入要打包的path $composer = require ROOT_DIR . '/vendor/composer/installed.php';
$prompt = new ArgFixer($this->input, $this->output); if (($composer['root']['dev'] ?? false) === true) {
if (!$this->getOption('with-no-dev')) {
$this->output->writeln('<comment>Current static-php-cli dependencies have installed dev-dependencies</comment>');
$this->output->writeln('<comment>If you want to remove, you can choose "Yes" to run command "composer update --no-dev" to remove.</comment>');
$this->output->writeln('<comment>Or choose "No", just pack, deploy.</comment>');
$ask = confirm('Do you want to remove dev-dependencies to reduce size of phar file?');
} elseif (!$this->getOption('with-dev')) {
$ask = true;
} else {
$ask = false;
}
if ($ask) {
[$code] = shell()->execWithResult('composer update --no-dev');
if ($code !== 0) {
$this->output->writeln('<error>"composer update --no-dev" failed with exit code [' . $code . ']</error>');
$this->output->writeln('<error>You may need to run this command by your own.</error>');
return static::FAILURE;
}
$this->output->writeln('<info>Update successfully, you need to re-run deploy command to pack.</info>');
return static::SUCCESS;
}
}
// 首先得确认是不是关闭了readonly模式 // 首先得确认是不是关闭了readonly模式
if (ini_get('phar.readonly') == 1) { if (ini_get('phar.readonly') == 1) {
if ($this->getOption('auto-phar-fix')) { if ($this->getOption('auto-phar-fix')) {
$ask = true; $ask = true;
} else { } else {
$ask = $prompt->requireBool('<comment>pack command needs "phar.readonly" = "Off" !</comment>' . PHP_EOL . 'If you want to automatically set it and continue, just Enter', true); $this->output->writeln('<comment>pack command needs "phar.readonly" = "Off" !</comment>');
$ask = confirm('Do you want to automatically set it and continue ?');
// $ask = $prompt->requireBool('<comment>pack command needs "phar.readonly" = "Off" !</comment>' . PHP_EOL . 'If you want to automatically set it and continue, just Enter', true);
} }
if ($ask) { if ($ask) {
global $argv; global $argv;
$args = array_merge(['-d', 'phar.readonly=0'], $_SERVER['argv']); $args = array_merge(['-d', 'phar.readonly=0'], $_SERVER['argv'], ['--no-motd']);
if (function_exists('pcntl_exec')) { if (function_exists('pcntl_exec')) {
$this->output->writeln('<info>Changing to phar.readonly=0 mode ...</info>'); $this->output->writeln('<info>Changing to phar.readonly=0 mode ...</info>');
if (pcntl_exec(PHP_BINARY, $args) === false) { if (pcntl_exec(PHP_BINARY, $args) === false) {
throw new \PharException('切换到读写模式失败,请检查环境。'); throw new \PharException('Switching to read write mode failed, please check the environment.');
} }
} else { } else {
$this->output->writeln('<info>Now running command in child process.</info>'); $this->output->writeln('<info>Now running command in child process.</info>');
@@ -51,23 +77,29 @@ class DeployCommand extends BaseCommand
// 获取路径 // 获取路径
$path = WORKING_DIR; $path = WORKING_DIR;
// 如果是目录,则将目录下的所有文件打包 // 如果是目录,则将目录下的所有文件打包
$phar_path = $prompt->requireArgument('target', 'Please input the phar target filename', 'static-php-cli.phar'); $phar_path = text('Please input the phar target filename', default: '/tmp/static-php-cli.phar');
// $phar_path = $prompt->requireArgument('target', 'Please input the phar target filename', 'static-php-cli.phar');
if (DataProvider::isRelativePath($phar_path)) { if (FileSystem::isRelativePath($phar_path)) {
$phar_path = '/tmp/' . $phar_path; $phar_path = WORKING_DIR . '/' . $phar_path;
} }
if (file_exists($phar_path)) { if (file_exists($phar_path)) {
$ask = $this->getOption('overwrite') ? true : $prompt->requireBool('<comment>The file "' . $phar_path . '" already exists, do you want to overwrite it?</comment>' . PHP_EOL . 'If you want to, just Enter'); if (!$this->getOption('overwrite')) {
$this->output->writeln('<comment>The file "' . $phar_path . '" already exists.</comment>');
$ask = confirm('Do you want to overwrite it?');
} else {
$ask = true;
}
if (!$ask) { if (!$ask) {
$this->output->writeln('<comment>User canceled.</comment>'); $this->output->writeln('<comment>User canceled.</comment>');
return 1; return static::FAILURE;
} }
@unlink($phar_path); @unlink($phar_path);
} }
$phar = new \Phar($phar_path); $phar = new \Phar($phar_path);
$phar->startBuffering(); $phar->startBuffering();
$all = DataProvider::scanDirFiles($path, true, true); $all = FileSystem::scanDirFiles($path, true, true);
$all = array_filter($all, function ($x) { $all = array_filter($all, function ($x) {
$dirs = preg_match('/(^(config|src|vendor)\\/|^(composer\\.json|README\\.md|source\\.json|LICENSE|README-en\\.md)$)/', $x); $dirs = preg_match('/(^(config|src|vendor)\\/|^(composer\\.json|README\\.md|source\\.json|LICENSE|README-en\\.md)$)/', $x);
@@ -97,7 +129,7 @@ class DeployCommand extends BaseCommand
$phar->setStub($phar->createDefaultStub($stub)); $phar->setStub($phar->createDefaultStub($stub));
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->output->writeln($e); $this->output->writeln($e);
return 1; return static::FAILURE;
} }
$phar->addFromString('.prod', 'true'); $phar->addFromString('.prod', 'true');
$phar->stopBuffering(); $phar->stopBuffering();
@@ -114,7 +146,7 @@ class DeployCommand extends BaseCommand
} }
chmod($phar_path, 0755); chmod($phar_path, 0755);
$this->output->writeln('<info>Phar Executable: ' . $phar_path . '</info>'); $this->output->writeln('<info>Phar Executable: ' . $phar_path . '</info>');
return 0; return static::SUCCESS;
} }
private function progress(int $max = 0): ProgressBar private function progress(int $max = 0): ProgressBar

View File

@@ -24,8 +24,8 @@ class DoctorCommand extends BaseCommand
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->output->writeln('<error>' . $e->getMessage() . '</error>'); $this->output->writeln('<error>' . $e->getMessage() . '</error>');
pcntl_signal(SIGINT, SIG_IGN); pcntl_signal(SIGINT, SIG_IGN);
return 1; return static::FAILURE;
} }
return 0; return static::SUCCESS;
} }
} }

View File

@@ -31,6 +31,7 @@ class DownloadCommand extends BaseCommand
$this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'version in major.minor format like 8.1', '8.1'); $this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'version in major.minor format like 8.1', '8.1');
$this->addOption('clean', null, null, 'Clean old download cache and source before fetch'); $this->addOption('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('all', 'A', null, 'Fetch all sources that static-php-cli needed');
$this->addOption('custom-url', 'U', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Specify custom source download url, e.g "php-src:https://downloads.php.net/~eric/php-8.3.0beta1.tar.gz"');
$this->addOption('from-zip', 'Z', InputOption::VALUE_REQUIRED, 'Fetch from zip archive'); $this->addOption('from-zip', 'Z', InputOption::VALUE_REQUIRED, 'Fetch from zip archive');
} }
@@ -67,14 +68,14 @@ class DownloadCommand extends BaseCommand
f_passthru('rm -rf ' . DOWNLOAD_PATH . '/*'); f_passthru('rm -rf ' . DOWNLOAD_PATH . '/*');
f_passthru('rm -rf ' . BUILD_ROOT_PATH . '/*'); f_passthru('rm -rf ' . BUILD_ROOT_PATH . '/*');
} }
return 0; return static::FAILURE;
} }
// --from-zip // --from-zip
if ($path = $this->getOption('from-zip')) { if ($path = $this->getOption('from-zip')) {
if (!file_exists($path)) { if (!file_exists($path)) {
logger()->critical('File ' . $path . ' not exist or not a zip archive.'); logger()->critical('File ' . $path . ' not exist or not a zip archive.');
return 1; return static::FAILURE;
} }
// remove old download files first // remove old download files first
if (is_dir(DOWNLOAD_PATH)) { if (is_dir(DOWNLOAD_PATH)) {
@@ -88,7 +89,7 @@ class DownloadCommand extends BaseCommand
if (PHP_OS_FAMILY !== 'Windows' && !$this->findCommand('unzip')) { if (PHP_OS_FAMILY !== 'Windows' && !$this->findCommand('unzip')) {
logger()->critical('Missing unzip command, you need to install it first !'); logger()->critical('Missing unzip command, you need to install it first !');
logger()->critical('You can use "bin/spc doctor" command to check and install required tools'); logger()->critical('You can use "bin/spc doctor" command to check and install required tools');
return 1; return static::FAILURE;
} }
// create downloads // create downloads
try { try {
@@ -102,10 +103,10 @@ class DownloadCommand extends BaseCommand
} }
} catch (RuntimeException $e) { } catch (RuntimeException $e) {
logger()->critical('Extract failed: ' . $e->getMessage()); logger()->critical('Extract failed: ' . $e->getMessage());
return 1; return static::FAILURE;
} }
logger()->info('Extract success'); logger()->info('Extract success');
return 0; return static::SUCCESS;
} }
// Define PHP major version // Define PHP major version
@@ -114,7 +115,7 @@ class DownloadCommand extends BaseCommand
preg_match('/^\d+\.\d+$/', $ver, $matches); preg_match('/^\d+\.\d+$/', $ver, $matches);
if (!$matches) { if (!$matches) {
logger()->error("bad version arg: {$ver}, x.y required!"); logger()->error("bad version arg: {$ver}, x.y required!");
return 1; return static::FAILURE;
} }
// Use shallow-clone can reduce git resource download // Use shallow-clone can reduce git resource download
@@ -139,18 +140,41 @@ class DownloadCommand extends BaseCommand
} }
$chosen_sources = $sources; $chosen_sources = $sources;
// Process -U options
$custom_urls = [];
foreach ($this->input->getOption('custom-url') as $value) {
[$source_name, $url] = explode(':', $value, 2);
$custom_urls[$source_name] = $url;
}
// Download them // Download them
f_mkdir(DOWNLOAD_PATH); f_mkdir(DOWNLOAD_PATH);
$cnt = count($chosen_sources); $cnt = count($chosen_sources);
$ni = 0; $ni = 0;
foreach ($chosen_sources as $source) { foreach ($chosen_sources as $source) {
++$ni; ++$ni;
logger()->info("Fetching source {$source} [{$ni}/{$cnt}]"); if (isset($custom_urls[$source])) {
Downloader::downloadSource($source, Config::getSource($source)); $config = Config::getSource($source);
$new_config = [
'type' => 'url',
'url' => $custom_urls[$source],
];
if (isset($config['path'])) {
$new_config['path'] = $config['path'];
}
if (isset($config['filename'])) {
$new_config['filename'] = $config['filename'];
}
logger()->info("Fetching source {$source} from custom url [{$ni}/{$cnt}]");
Downloader::downloadSource($source, $new_config, true);
} else {
logger()->info("Fetching source {$source} [{$ni}/{$cnt}]");
Downloader::downloadSource($source, Config::getSource($source));
}
} }
// 打印拉取资源用时 // 打印拉取资源用时
$time = round(microtime(true) - START_TIME, 3); $time = round(microtime(true) - START_TIME, 3);
logger()->info('Download complete, used ' . $time . ' s !'); logger()->info('Download complete, used ' . $time . ' s !');
return 0; return static::SUCCESS;
} }
} }

View File

@@ -50,7 +50,7 @@ class DumpLicenseCommand extends BaseCommand
$this->output->writeln('Dump license with libraries: ' . implode(', ', $libraries)); $this->output->writeln('Dump license with libraries: ' . implode(', ', $libraries));
$this->output->writeln('Dump license with' . ($this->getOption('without-php') ? 'out' : '') . ' php-src'); $this->output->writeln('Dump license with' . ($this->getOption('without-php') ? 'out' : '') . ' php-src');
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir')); $this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
return 0; return static::SUCCESS;
} }
if ($this->getOption('by-libs') !== null) { if ($this->getOption('by-libs') !== null) {
$libraries = array_map('trim', array_filter(explode(',', $this->getOption('by-libs')))); $libraries = array_map('trim', array_filter(explode(',', $this->getOption('by-libs'))));
@@ -58,16 +58,16 @@ class DumpLicenseCommand extends BaseCommand
$dumper->addLibs($libraries); $dumper->addLibs($libraries);
$dumper->dump($this->getOption('dump-dir')); $dumper->dump($this->getOption('dump-dir'));
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir')); $this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
return 0; return static::SUCCESS;
} }
if ($this->getOption('by-sources') !== null) { if ($this->getOption('by-sources') !== null) {
$sources = array_map('trim', array_filter(explode(',', $this->getOption('by-sources')))); $sources = array_map('trim', array_filter(explode(',', $this->getOption('by-sources'))));
$dumper->addSources($sources); $dumper->addSources($sources);
$dumper->dump($this->getOption('dump-dir')); $dumper->dump($this->getOption('dump-dir'));
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir')); $this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
return 0; return static::SUCCESS;
} }
$this->output->writeln('You must use one of "--by-extensions=", "--by-libs=", "--by-sources=" to dump'); $this->output->writeln('You must use one of "--by-extensions=", "--by-libs=", "--by-sources=" to dump');
return 1; return static::FAILURE;
} }
} }

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\store\SourceExtractor;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
#[AsCommand('extract', 'Extract required sources')]
class ExtractCommand extends BaseCommand
{
use UnixSystemUtilTrait;
protected string $php_major_ver;
public function configure()
{
$this->addArgument('sources', InputArgument::REQUIRED, 'The sources will be compiled, comma separated');
}
public function handle(): int
{
$sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources'))));
if (empty($sources)) {
$this->output->writeln('<error>sources cannot be empty, at least contain one !</error>');
return static::FAILURE;
}
SourceExtractor::initSource(sources: $sources);
logger()->info('Extract done !');
return static::SUCCESS;
}
}

View File

@@ -1,26 +0,0 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use SPC\exception\FileSystemException;
use SPC\store\Config;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand('list-ext', 'List supported extensions')]
class ListExtCommand extends BaseCommand
{
protected bool $no_motd = true;
/**
* @throws FileSystemException
*/
public function handle(): int
{
foreach (Config::getExts() as $ext => $meta) {
echo $ext . PHP_EOL;
}
return 0;
}
}

View File

@@ -35,12 +35,12 @@ class MicroCombineCommand extends BaseCommand
// 1. Make sure specified micro.sfx file exists // 1. Make sure specified micro.sfx file exists
if ($micro_file !== null && !file_exists($micro_file)) { if ($micro_file !== null && !file_exists($micro_file)) {
$this->output->writeln('<error>The micro.sfx file you specified is incorrect or does not exist!</error>'); $this->output->writeln('<error>The micro.sfx file you specified is incorrect or does not exist!</error>');
return 1; return static::FAILURE;
} }
// 2. Make sure buildroot/bin/micro.sfx exists // 2. Make sure buildroot/bin/micro.sfx exists
if ($micro_file === null && !file_exists($internal)) { if ($micro_file === null && !file_exists($internal)) {
$this->output->writeln('<error>You haven\'t compiled micro.sfx yet, please use "build" command and "--build-micro" to compile phpmicro first!</error>'); $this->output->writeln('<error>You haven\'t compiled micro.sfx yet, please use "build" command and "--build-micro" to compile phpmicro first!</error>');
return 1; return static::FAILURE;
} }
// 3. Use buildroot/bin/micro.sfx // 3. Use buildroot/bin/micro.sfx
if ($micro_file === null) { if ($micro_file === null) {
@@ -49,19 +49,19 @@ class MicroCombineCommand extends BaseCommand
// 4. Make sure php or phar file exists // 4. Make sure php or phar file exists
if (!is_file(FileSystem::convertPath($file))) { if (!is_file(FileSystem::convertPath($file))) {
$this->output->writeln('<error>The file to combine does not exist!</error>'); $this->output->writeln('<error>The file to combine does not exist!</error>');
return 1; return static::FAILURE;
} }
// 5. Confirm ini files (ini-set has higher priority) // 5. Confirm ini files (ini-set has higher priority)
if ($ini_file !== null) { if ($ini_file !== null) {
// Check file exist first // Check file exist first
if (!file_exists($ini_file)) { if (!file_exists($ini_file)) {
$this->output->writeln('<error>The ini file to combine does not exist! (' . $ini_file . ')</error>'); $this->output->writeln('<error>The ini file to combine does not exist! (' . $ini_file . ')</error>');
return 1; return static::FAILURE;
} }
$arr = parse_ini_file($ini_file); $arr = parse_ini_file($ini_file);
if ($arr === false) { if ($arr === false) {
$this->output->writeln('<error>Cannot parse ini file</error>'); $this->output->writeln('<error>Cannot parse ini file</error>');
return 1; return static::FAILURE;
} }
$target_ini = array_merge($target_ini, $arr); $target_ini = array_merge($target_ini, $arr);
} }
@@ -71,7 +71,7 @@ class MicroCombineCommand extends BaseCommand
$arr = parse_ini_string($item); $arr = parse_ini_string($item);
if ($arr === false) { if ($arr === false) {
$this->output->writeln('<error>--with-ini-set parse failed</error>'); $this->output->writeln('<error>--with-ini-set parse failed</error>');
return 1; return static::FAILURE;
} }
$target_ini = array_merge($target_ini, $arr); $target_ini = array_merge($target_ini, $arr);
} }
@@ -90,12 +90,12 @@ class MicroCombineCommand extends BaseCommand
$result = file_put_contents($output, $file_target); $result = file_put_contents($output, $file_target);
if ($result === false) { if ($result === false) {
$this->output->writeln('<error>Combine failed.</error>'); $this->output->writeln('<error>Combine failed.</error>');
return 1; return static::FAILURE;
} }
// 9. chmod +x // 9. chmod +x
chmod($output, 0755); chmod($output, 0755);
$this->output->writeln('<info>Combine success! Binary file: ' . $output . '</info>'); $this->output->writeln('<info>Combine success! Binary file: ' . $output . '</info>');
return 0; return static::SUCCESS;
} }
private function encodeINI(array $array): string private function encodeINI(array $array): string

View File

@@ -8,17 +8,17 @@ use SPC\command\BaseCommand;
use SPC\store\Config; use SPC\store\Config;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand('dev:ext-all', 'Dev command')] #[AsCommand('dev:ext-all', 'Dev command', ['list-ext'])]
class AllExtCommand extends BaseCommand class AllExtCommand extends BaseCommand
{ {
public function configure() public function configure()
{ {
$this->addOption('line', 'l', null, 'Show with separate lines');
} }
public function handle(): int public function handle(): int
{ {
$this->output->writeln(implode(',', array_keys(Config::getExts()))); $this->output->writeln(implode($this->input->getOption('line') ? PHP_EOL : ',', array_keys(Config::getExts())));
return static::SUCCESS;
return 0;
} }
} }

View File

@@ -37,7 +37,6 @@ class ExtInfoCommand extends BaseCommand
} }
$this->output->writeln(''); $this->output->writeln('');
} }
return static::SUCCESS;
return 0;
} }
} }

View File

@@ -22,12 +22,12 @@ class PhpVerCommand extends BaseCommand
{ {
// Find php from source/php-src // Find php from source/php-src
$file = SOURCE_PATH . '/php-src/main/php_version.h'; $file = SOURCE_PATH . '/php-src/main/php_version.h';
$result = preg_match('/#define PHP_VERSION "(\d+\.\d+\.\d+)"/', file_get_contents($file), $match); $result = preg_match('/#define PHP_VERSION "([^"]+)"/', file_get_contents($file), $match);
if ($result === false) { if ($result === false) {
$this->output->writeln('<error>PHP source not found, maybe you need to extract first ?</error>'); $this->output->writeln('<error>PHP source not found, maybe you need to extract first ?</error>');
return 1; return static::FAILURE;
} }
$this->output->writeln('<info>' . $match[1] . '</info>'); $this->output->writeln('<info>' . $match[1] . '</info>');
return 0; return static::SUCCESS;
} }
} }

View File

@@ -2,8 +2,9 @@
declare(strict_types=1); declare(strict_types=1);
namespace SPC\command; namespace SPC\command\dev;
use SPC\command\BaseCommand;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\ValidationException; use SPC\exception\ValidationException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
@@ -12,9 +13,9 @@ use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
/** /**
* 修改 config 后对其 kv 进行排序的操作 * Modify config file: sort lib, ext, source by name.
*/ */
#[AsCommand('sort-config', 'After config edited, sort it by alphabet')] #[AsCommand('dev:sort-config', 'After config edited, sort it by alphabet', ['sort-config'])]
class SortConfigCommand extends BaseCommand class SortConfigCommand extends BaseCommand
{ {
public function configure() public function configure()
@@ -33,25 +34,34 @@ class SortConfigCommand extends BaseCommand
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/lib.json'), true); $file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/lib.json'), true);
ConfigValidator::validateLibs($file); ConfigValidator::validateLibs($file);
ksort($file); ksort($file);
file_put_contents(ROOT_DIR . '/config/lib.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); if (!file_put_contents(ROOT_DIR . '/config/lib.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) {
$this->output->writeln('<error>Write file lib.json failed!</error>');
return static::FAILURE;
}
break; break;
case 'source': case 'source':
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/source.json'), true); $file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/source.json'), true);
ConfigValidator::validateSource($file); ConfigValidator::validateSource($file);
uksort($file, fn ($a, $b) => $a === 'php-src' ? -1 : ($b === 'php-src' ? 1 : ($a < $b ? -1 : 1))); uksort($file, fn ($a, $b) => $a === 'php-src' ? -1 : ($b === 'php-src' ? 1 : ($a < $b ? -1 : 1)));
file_put_contents(ROOT_DIR . '/config/source.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); if (!file_put_contents(ROOT_DIR . '/config/source.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) {
$this->output->writeln('<error>Write file source.json failed!</error>');
return static::FAILURE;
}
break; break;
case 'ext': case 'ext':
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/ext.json'), true); $file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/ext.json'), true);
ConfigValidator::validateExts($file); ConfigValidator::validateExts($file);
ksort($file); ksort($file);
file_put_contents(ROOT_DIR . '/config/ext.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); if (!file_put_contents(ROOT_DIR . '/config/ext.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) {
$this->output->writeln('<error>Write file ext.json failed!</error>');
return static::FAILURE;
}
break; break;
default: default:
$this->output->writeln("<error>invalid config name: {$name}</error>"); $this->output->writeln("<error>invalid config name: {$name}</error>");
return 1; return 1;
} }
$this->output->writeln('<info>sort success</info>'); $this->output->writeln('<info>sort success</info>');
return 0; return static::SUCCESS;
} }
} }

View File

@@ -29,7 +29,6 @@ class MacOSToolCheckList
'xz', 'xz',
'gzip', 'gzip',
'bzip2', 'bzip2',
'gotop',
'cmake', 'cmake',
]; ];

View File

@@ -47,7 +47,8 @@ class ExceptionHandler
logger()->error($e->getTraceAsString()); logger()->error($e->getTraceAsString());
return; return;
} }
$this->whoops->handleException($e); $this->whoops->handleException($e);
logger()->critical('You can report this exception to static-php-cli GitHub repo.');
} }
} }

View File

@@ -265,7 +265,7 @@ class Downloader
* @throws FileSystemException * @throws FileSystemException
* @throws RuntimeException * @throws RuntimeException
*/ */
public static function downloadSource(string $name, ?array $source = null): void public static function downloadSource(string $name, ?array $source = null, bool $force = false): void
{ {
if ($source === null) { if ($source === null) {
$source = Config::getSource($name); $source = Config::getSource($name);
@@ -278,7 +278,7 @@ class Downloader
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? []; $lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
} }
// If lock file exists, skip downloading // If lock file exists, skip downloading
if (isset($lock[$name])) { if (isset($lock[$name]) && !$force) {
if ($lock[$name]['source_type'] === 'archive' && file_exists(DOWNLOAD_PATH . '/' . $lock[$name]['filename'])) { if ($lock[$name]['source_type'] === 'archive' && file_exists(DOWNLOAD_PATH . '/' . $lock[$name]['filename'])) {
logger()->notice("source [{$name}] already downloaded: " . $lock[$name]['filename']); logger()->notice("source [{$name}] already downloaded: " . $lock[$name]['filename']);
return; return;

View File

@@ -320,28 +320,7 @@ class FileSystem
foreach ($files as $v) { foreach ($files as $v) {
$pathinfo = pathinfo($v); $pathinfo = pathinfo($v);
if (($pathinfo['extension'] ?? '') == 'php') { if (($pathinfo['extension'] ?? '') == 'php') {
$path = rtrim($dir, '/') . '/' . rtrim($pathinfo['dirname'], './') . '/' . $pathinfo['basename']; if ($rule === null) {
// 过滤不包含类的文件
$tokens = token_get_all(self::readFile($path));
$found = false;
foreach ($tokens as $token) {
if (!is_array($token)) {
continue;
}
if ($token[0] === T_CLASS) {
$found = true;
break;
}
}
if (!$found) {
continue;
}
if ($rule === null) { // 规则未设置回调时候,使用默认的识别过滤规则
/*if (substr(file_get_contents($dir . '/' . $v), 6, 6) == '#plain') {
continue;
}*/
if (file_exists($dir . '/' . $pathinfo['basename'] . '.ignore')) { if (file_exists($dir . '/' . $pathinfo['basename'] . '.ignore')) {
continue; continue;
} }

View File

@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace SPC\store;
use SPC\exception\WrongUsageException;
class SourceExtractor
{
public static function initSource(?array $sources = null, ?array $libs = null, ?array $exts = null): void
{
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
throw new WrongUsageException('Download lock file "downloads/.lock.json" not found, maybe you need to download sources first ?');
}
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true);
$sources_extracted = [];
// source check exist
if (is_array($sources)) {
foreach ($sources as $source) {
$sources_extracted[$source] = true;
}
}
// lib check source exist
if (is_array($libs)) {
foreach ($libs as $lib) {
// get source name for lib
$source = Config::getLib($lib, 'source');
$sources_extracted[$source] = true;
}
}
// ext check source exist
if (is_array($exts)) {
foreach ($exts as $ext) {
// get source name for ext
if (Config::getExt($ext, 'type') !== 'external') {
continue;
}
$source = Config::getExt($ext, 'source');
$sources_extracted[$source] = true;
}
}
// start check
foreach ($sources_extracted as $source => $item) {
if (Config::getSource($source) === null) {
throw new WrongUsageException("Source [{$source}] not exists, please check name and correct it !");
}
if (!isset($lock[$source])) {
throw new WrongUsageException('Source [' . $source . '] not downloaded or not locked, you should download it first !');
}
// check source dir exist
$check = $lock[$source]['move_path'] === null ? SOURCE_PATH . '/' . $source : SOURCE_PATH . '/' . $lock[$source]['move_path'];
if (!is_dir($check)) {
FileSystem::extractSource($source, DOWNLOAD_PATH . '/' . ($lock[$source]['filename'] ?? $lock[$source]['dirname']), $lock[$source]['move_path']);
}
}
}
}

View File

@@ -6,164 +6,47 @@ namespace SPC\store;
use SPC\builder\BuilderBase; use SPC\builder\BuilderBase;
use SPC\builder\linux\LinuxBuilder; use SPC\builder\linux\LinuxBuilder;
use SPC\builder\linux\SystemUtil;
use SPC\builder\macos\MacOSBuilder;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\util\Util;
class SourcePatcher class SourcePatcher
{ {
public static function init() public static function init()
{ {
FileSystem::addSourceExtractHook('swow', [SourcePatcher::class, 'patchSwow']); // FileSystem::addSourceExtractHook('swow', [SourcePatcher::class, 'patchSwow']);
FileSystem::addSourceExtractHook('micro', [SourcePatcher::class, 'patchMicro']); FileSystem::addSourceExtractHook('micro', [SourcePatcher::class, 'patchMicro']);
FileSystem::addSourceExtractHook('openssl', [SourcePatcher::class, 'patchOpenssl11Darwin']); FileSystem::addSourceExtractHook('openssl', [SourcePatcher::class, 'patchOpenssl11Darwin']);
} }
public static function patchPHPBuildconf(BuilderBase $builder): void /**
* Source patcher runner before buildconf
*
* @param BuilderBase $builder Builder
*/
public static function patchBeforeBuildconf(BuilderBase $builder): void
{ {
if ($builder->getExt('curl')) { foreach ($builder->getExts() as $ext) {
logger()->info('patching before-configure for curl checks'); if ($ext->patchBeforeBuildconf() === true) {
$file1 = "AC_DEFUN([PHP_CHECK_LIBRARY], [\n $3\n])"; logger()->info('Extension [' . $ext->getName() . '] patched before buildconf');
$files = FileSystem::readFile(SOURCE_PATH . '/php-src/ext/curl/config.m4');
$file2 = 'AC_DEFUN([PHP_CHECK_LIBRARY], [
save_old_LDFLAGS=$LDFLAGS
ac_stuff="$5"
save_ext_shared=$ext_shared
ext_shared=yes
PHP_EVAL_LIBLINE([$]ac_stuff, LDFLAGS)
AC_CHECK_LIB([$1],[$2],[
LDFLAGS=$save_old_LDFLAGS
ext_shared=$save_ext_shared
$3
],[
LDFLAGS=$save_old_LDFLAGS
ext_shared=$save_ext_shared
unset ac_cv_lib_$1[]_$2
$4
])dnl
])';
file_put_contents(SOURCE_PATH . '/php-src/ext/curl/config.m4', $file1 . "\n" . $files . "\n" . $file2);
}
if ($builder->getExt('memcache')) {
FileSystem::replaceFile(
SOURCE_PATH . '/php-src/ext/memcache/config9.m4',
REPLACE_FILE_STR,
'if test -d $abs_srcdir/src ; then',
'if test -d $abs_srcdir/main ; then'
);
FileSystem::replaceFile(
SOURCE_PATH . '/php-src/ext/memcache/config9.m4',
REPLACE_FILE_STR,
'export CPPFLAGS="$CPPFLAGS $INCLUDES"',
'export CPPFLAGS="$CPPFLAGS $INCLUDES -I$abs_srcdir/main"'
);
// add for in-tree building
file_put_contents(
SOURCE_PATH . '/php-src/ext/memcache/php_memcache.h',
<<<'EOF'
#ifndef PHP_MEMCACHE_H
#define PHP_MEMCACHE_H
extern zend_module_entry memcache_module_entry;
#define phpext_memcache_ptr &memcache_module_entry
#endif
EOF
);
}
}
public static function patchSwow(): bool
{
if (Util::getPHPVersionID() >= 80000) {
if (PHP_OS_FAMILY === 'Windows') {
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && mklink /D swow swow-src\ext');
} else {
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && ln -s swow-src/ext swow');
} }
return true;
}
return false;
}
public static function patchPHPConfigure(BuilderBase $builder): void
{
$frameworks = $builder instanceof MacOSBuilder ? ' ' . $builder->getFrameworks(true) . ' ' : '';
$patch = [];
if ($curl = $builder->getExt('curl')) {
$patch[] = ['curl check', '/-lcurl/', $curl->getLibFilesString() . $frameworks];
}
if ($bzip2 = $builder->getExt('bz2')) {
$patch[] = ['bzip2 check', '/-lbz2/', $bzip2->getLibFilesString() . $frameworks];
}
if ($pdo_sqlite = $builder->getExt('pdo_sqlite')) {
$patch[] = ['pdo_sqlite linking', '/sqlite3_column_table_name=yes/', 'sqlite3_column_table_name=no'];
}
if ($event = $builder->getExt('event')) {
$patch[] = ['event check', '/-levent_openssl/', $event->getLibFilesString()];
}
if ($readline = $builder->getExt('readline')) {
$patch[] = ['readline patch', '/-lncurses/', $readline->getLibFilesString()];
}
if ($ssh2 = $builder->getExt('ssh2')) {
$patch[] = ['ssh2 patch', '/-lssh2/', $ssh2->getLibFilesString()];
}
if ($pgsql = $builder->getExt('pgsql')) {
$patch[] = ['pgsql patch', '/-lpq/', $pgsql->getLibFilesString()];
}
$patch[] = ['disable capstone', '/have_capstone="yes"/', 'have_capstone="no"'];
foreach ($patch as $item) {
logger()->info('Patching configure: ' . $item[0]);
FileSystem::replaceFile(SOURCE_PATH . '/php-src/configure', REPLACE_FILE_PREG, $item[1], $item[2]);
} }
} }
public static function patchUnixLibpng(): void /**
* Source patcher runner before configure
*
* @param BuilderBase $builder Builder
* @throws FileSystemException
*/
public static function patchBeforeConfigure(BuilderBase $builder): void
{ {
FileSystem::replaceFile( foreach ($builder->getExts() as $ext) {
SOURCE_PATH . '/libpng/configure', if ($ext->patchBeforeConfigure() === true) {
REPLACE_FILE_STR, logger()->info('Extension [' . $ext->getName() . '] patched before configure');
'-lz', }
BUILD_LIB_PATH . '/libz.a'
);
if (SystemUtil::getOSRelease()['dist'] === 'alpine') {
FileSystem::replaceFile(
SOURCE_PATH . '/libpng/configure',
REPLACE_FILE_STR,
'-lm',
'/usr/lib/libm.a'
);
} }
} // patch capstone
FileSystem::replaceFile(SOURCE_PATH . '/php-src/configure', REPLACE_FILE_PREG, '/have_capstone="yes"/', 'have_capstone="no"');
public static function patchUnixSsh2(): void
{
FileSystem::replaceFile(
SOURCE_PATH . '/php-src/configure',
REPLACE_FILE_STR,
'-lssh2',
BUILD_LIB_PATH . '/libssh2.a'
);
}
public static function patchCurlMacOS(): void
{
FileSystem::replaceFile(
SOURCE_PATH . '/curl/CMakeLists.txt',
REPLACE_FILE_PREG,
'/NOT COREFOUNDATION_FRAMEWORK/m',
'FALSE'
);
FileSystem::replaceFile(
SOURCE_PATH . '/curl/CMakeLists.txt',
REPLACE_FILE_PREG,
'/NOT SYSTEMCONFIGURATION_FRAMEWORK/m',
'FALSE'
);
} }
public static function patchMicro(?array $list = null, bool $reverse = false): bool public static function patchMicro(?array $list = null, bool $reverse = false): bool
@@ -203,7 +86,7 @@ EOF
} }
$patch_list = $list ?? $default; $patch_list = $list ?? $default;
$patches = []; $patches = [];
$serial = ['80', '81', '82']; $serial = ['80', '81', '82', '83'];
foreach ($patch_list as $patchName) { foreach ($patch_list as $patchName) {
if (file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}.patch")) { if (file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}.patch")) {
$patches[] = "sapi/micro/patches/{$patchName}.patch"; $patches[] = "sapi/micro/patches/{$patchName}.patch";
@@ -244,19 +127,75 @@ EOF
return false; return false;
} }
public static function patchPHPAfterConfigure(BuilderBase $param) /**
* @throws FileSystemException
*/
public static function patchBeforeMake(BuilderBase $builder): void
{ {
if ($param instanceof LinuxBuilder) { // Try to fix debian environment build lack HAVE_STRLCAT problem
if ($builder instanceof LinuxBuilder) {
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCPY 1$/m', ''); FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCPY 1$/m', '');
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCAT 1$/m', ''); FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCAT 1$/m', '');
} }
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_OPENPTY 1$/m', ''); FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_OPENPTY 1$/m', '');
// patch openssl3 with php8.0 bug // call extension patch before make
if (file_exists(SOURCE_PATH . '/openssl/VERSION.dat') && Util::getPHPVersionID() < 80100) { foreach ($builder->getExts() as $ext) {
$openssl_c = file_get_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c'); if ($ext->patchBeforeMake() === true) {
$openssl_c = preg_replace('/REGISTER_LONG_CONSTANT\s*\(\s*"OPENSSL_SSLV23_PADDING"\s*.+;/', '', $openssl_c); logger()->info('Extension [' . $ext->getName() . '] patched before make');
file_put_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c', $openssl_c); }
} }
} }
/**
* @throws FileSystemException
*/
public static function patchHardcodedINI(array $ini = []): bool
{
$cli_c = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c';
$cli_c_bak = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c.bak';
$micro_c = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c';
$micro_c_bak = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c.bak';
// Try to reverse backup file
$find_pattern = 'const char HARDCODED_INI[] =';
$patch_str = '';
foreach ($ini as $key => $value) {
$patch_str .= "\"{$key}={$value}\\n\"\n";
}
$patch_str = "const char HARDCODED_INI[] =\n{$patch_str}";
// Detect backup, if we have backup, it means we need to reverse first
if (file_exists($cli_c_bak) || file_exists($micro_c_bak)) {
self::unpatchHardcodedINI();
}
// Backup it
$result = file_put_contents($cli_c_bak, file_get_contents($cli_c));
$result = $result && file_put_contents($micro_c_bak, file_get_contents($micro_c));
if ($result === false) {
return false;
}
// Patch it
FileSystem::replaceFile($cli_c, REPLACE_FILE_STR, $find_pattern, $patch_str);
FileSystem::replaceFile($micro_c, REPLACE_FILE_STR, $find_pattern, $patch_str);
return true;
}
public static function unpatchHardcodedINI(): bool
{
$cli_c = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c';
$cli_c_bak = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c.bak';
$micro_c = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c';
$micro_c_bak = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c.bak';
if (!file_exists($cli_c_bak) && !file_exists($micro_c_bak)) {
return false;
}
$result = file_put_contents($cli_c, file_get_contents($cli_c_bak));
$result = $result && file_put_contents($micro_c, file_get_contents($micro_c_bak));
@unlink($cli_c_bak);
@unlink($micro_c_bak);
return $result;
}
} }