Compare commits

...

7 Commits

Author SHA1 Message Date
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
27 changed files with 597 additions and 227 deletions

View File

@@ -208,7 +208,10 @@
},
"mysqlnd": {
"type": "builtin",
"arg-type-windows": "with"
"arg-type-windows": "with",
"lib-depends": [
"zlib"
]
},
"opcache": {
"type": "builtin"

View File

@@ -7,7 +7,6 @@ namespace SPC;
use SPC\command\DeployCommand;
use SPC\store\FileSystem;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\HelpCommand;
use Symfony\Component\Console\Command\ListCommand;
@@ -16,7 +15,7 @@ use Symfony\Component\Console\Command\ListCommand;
*/
class ConsoleApplication extends Application
{
public const VERSION = '2.0-rc3';
public const VERSION = '2.0-rc4';
/**
* @throws \ReflectionException

View File

@@ -9,6 +9,7 @@ use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\FileSystem;
use SPC\store\SourceExtractor;
use SPC\util\CustomExt;
use SPC\util\DependencyUtil;
@@ -87,7 +88,7 @@ abstract class BuilderBase
$lib->calcDependency();
}
$this->initSource(libs: $libraries);
SourceExtractor::initSource(libs: $libraries);
// 构建库
foreach ($this->libs as $lib) {
@@ -184,15 +185,17 @@ abstract class BuilderBase
*
* @throws FileSystemException
* @throws RuntimeException
* @throws \ReflectionException
* @throws WrongUsageException
*/
public function proveExts(array $extensions): void
{
CustomExt::loadCustomExt();
$this->initSource(sources: ['php-src']);
SourceExtractor::initSource(sources: ['php-src']);
if ($this->getPHPVersionID() >= 80000) {
$this->initSource(sources: ['micro']);
SourceExtractor::initSource(sources: ['micro']);
}
$this->initSource(exts: $extensions);
SourceExtractor::initSource(exts: $extensions);
foreach ($extensions as $extension) {
$class = CustomExt::getExtClass($extension);
$ext = new $class($extension, $this);
@@ -221,6 +224,7 @@ abstract class BuilderBase
*
* @throws RuntimeException
* @throws FileSystemException
* @throws WrongUsageException
*/
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 '';
}
/**
* 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
*/

View File

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

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;
use SPC\builder\Extension;
use SPC\exception\FileSystemException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('event')]
@@ -23,4 +25,18 @@ class event extends Extension
}
return $arg;
}
/**
* @throws FileSystemException
*/
public function patchBeforeConfigure(): bool
{
FileSystem::replaceFile(
SOURCE_PATH . '/php-src/configure',
REPLACE_FILE_PREG,
'/-levent_openssl/',
$this->getLibFilesString()
);
return true;
}
}

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('memcache')]
@@ -14,4 +15,34 @@ class memcache extends Extension
{
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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,6 +7,7 @@ namespace SPC\command;
use SPC\builder\BuilderProvider;
use SPC\exception\ExceptionHandler;
use SPC\exception\WrongUsageException;
use SPC\store\SourcePatcher;
use SPC\util\DependencyUtil;
use SPC\util\LicenseDumper;
use Symfony\Component\Console\Attribute\AsCommand;
@@ -27,6 +28,7 @@ class BuildCliCommand extends BuildCommand
$this->addOption('build-all', null, null, 'build cli, micro, fpm');
$this->addOption('no-strip', null, null, 'build without strip, in order to debug and load external extensions');
$this->addOption('enable-zts', null, null, 'enable ZTS support');
$this->addOption('with-hardcoded-ini', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Patch PHP source code, inject hardcoded INI');
}
public function handle(): int
@@ -72,6 +74,16 @@ class BuildCliCommand extends BuildCommand
$builder->proveExts($extensions);
// 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'));
// 统计时间

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('clean', null, null, 'Clean old download cache and source before fetch');
$this->addOption('all', 'A', null, 'Fetch all sources that static-php-cli needed');
$this->addOption('custom-url', 'U', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Specify custom source download url, e.g "php-src:https://downloads.php.net/~eric/php-8.3.0beta1.tar.gz"');
$this->addOption('from-zip', 'Z', InputOption::VALUE_REQUIRED, 'Fetch from zip archive');
}
@@ -139,14 +140,37 @@ class DownloadCommand extends BaseCommand
}
$chosen_sources = $sources;
// Process -U options
$custom_urls = [];
foreach ($this->input->getOption('custom-url') as $value) {
[$source_name, $url] = explode(':', $value, 2);
$custom_urls[$source_name] = $url;
}
// Download them
f_mkdir(DOWNLOAD_PATH);
$cnt = count($chosen_sources);
$ni = 0;
foreach ($chosen_sources as $source) {
++$ni;
logger()->info("Fetching source {$source} [{$ni}/{$cnt}]");
Downloader::downloadSource($source, Config::getSource($source));
if (isset($custom_urls[$source])) {
$config = Config::getSource($source);
$new_config = [
'type' => 'url',
'url' => $custom_urls[$source],
];
if (isset($config['path'])) {
$new_config['path'] = $config['path'];
}
if (isset($config['filename'])) {
$new_config['filename'] = $config['filename'];
}
logger()->info("Fetching source {$source} from custom url [{$ni}/{$cnt}]");
Downloader::downloadSource($source, $new_config, true);
} else {
logger()->info("Fetching source {$source} [{$ni}/{$cnt}]");
Downloader::downloadSource($source, Config::getSource($source));
}
}
// 打印拉取资源用时
$time = round(microtime(true) - START_TIME, 3);

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('<erorr>sources cannot be empty, at least contain one !</erorr>');
return 1;
}
SourceExtractor::initSource(sources: $sources);
logger()->info('Extract done !');
return 0;
}
}

View File

@@ -22,7 +22,7 @@ class PhpVerCommand extends BaseCommand
{
// Find php from source/php-src
$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) {
$this->output->writeln('<error>PHP source not found, maybe you need to extract first ?</error>');
return 1;

View File

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

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