diff --git a/bin/spc-alpine-docker b/bin/spc-alpine-docker index 2640ffba..dc6eaa36 100755 --- a/bin/spc-alpine-docker +++ b/bin/spc-alpine-docker @@ -108,8 +108,7 @@ RUN apk update; \ wget \ xz \ gettext-dev \ - binutils-gold \ - patchelf + binutils-gold RUN curl -#fSL https://dl.static-php.dev/static-php-cli/bulk/php-8.4.4-cli-linux-\$(uname -m).tar.gz | tar -xz -C /usr/local/bin && \ chmod +x /usr/local/bin/php diff --git a/bin/spc-gnu-docker b/bin/spc-gnu-docker index 68f85109..286ef985 100755 --- a/bin/spc-gnu-docker +++ b/bin/spc-gnu-docker @@ -92,11 +92,6 @@ RUN echo "source scl_source enable devtoolset-10" >> /etc/bashrc RUN source /etc/bashrc RUN yum install -y which -RUN curl -fsSL -o patchelf.tgz https://github.com/NixOS/patchelf/releases/download/0.18.0/patchelf-0.18.0-$SPC_USE_ARCH.tar.gz && \ - mkdir -p /patchelf && \ - tar -xzf patchelf.tgz -C /patchelf --strip-components=1 && \ - cp /patchelf/bin/patchelf /usr/bin/ - RUN curl -o cmake.tgz -#fSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$SPC_USE_ARCH.tar.gz && \ mkdir /cmake && \ tar -xzf cmake.tgz -C /cmake --strip-components 1 diff --git a/config/env.ini b/config/env.ini index 8e25aa6e..bd2f3d4f 100644 --- a/config/env.ini +++ b/config/env.ini @@ -122,17 +122,20 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE ; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS="" +; optional, path to openssl conf. This affects where openssl will look for the default CA. +; default on Debian/Alpine: /etc/ssl, default on RHEL: /etc/pki/tls +OPENSSLDIR="" + [macos] ; build target: macho or macho (possibly we could support macho-universal in the future) ; Currently we do not support universal and cross-compilation for macOS. SPC_TARGET=native-macos ; compiler environments -CC=${SPC_LINUX_DEFAULT_CC} -CXX=${SPC_LINUX_DEFAULT_CXX} -AR=${SPC_LINUX_DEFAULT_AR} -LD=${SPC_LINUX_DEFAULT_LD} +CC=clang +CXX=clang++ +AR=ar +LD=ld ; default compiler flags, used in CMake toolchain file, openssl and pkg-config build -; this will be added to all CFLAGS and CXXFLAGS for the library builds SPC_DEFAULT_C_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" SPC_DEFAULT_LD_FLAGS="" @@ -150,3 +153,5 @@ SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --enable- SPC_CMD_VAR_PHP_EMBED_TYPE="static" ; EXTRA_CFLAGS for `configure` and `make` php SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -Werror=unknown-warning-option ${SPC_DEFAULT_C_FLAGS}" +; minimum compatible macOS version (LLVM vars, availability not guaranteed) +MACOSX_DEPLOYMENT_TARGET=12.0 diff --git a/config/ext.json b/config/ext.json index d3fd2aa2..566974b9 100644 --- a/config/ext.json +++ b/config/ext.json @@ -127,6 +127,14 @@ "sockets" ] }, + "excimer": { + "support": { + "Windows": "wip", + "BSD": "wip" + }, + "type": "external", + "source": "ext-excimer" + }, "exif": { "type": "builtin" }, @@ -232,11 +240,13 @@ "BSD": "wip" }, "type": "external", - "source": "grpc", + "source": "ext-grpc", "arg-type-unix": "enable-path", "cpp-extension": true, "lib-depends": [ - "grpc" + "zlib", + "openssl", + "libcares" ] }, "iconv": { @@ -408,8 +418,7 @@ "ext-depends": [ "zlib", "session" - ], - "build-with-php": true + ] }, "memcached": { "support": { @@ -487,6 +496,40 @@ "zlib" ] }, + "mysqlnd_ed25519": { + "type": "external", + "source": "mysqlnd_ed25519", + "arg-type": "enable", + "target": [ + "shared" + ], + "ext-depends": [ + "mysqlnd" + ], + "lib-depends": [ + "libsodium" + ], + "lib-suggests": [ + "openssl" + ] + }, + "mysqlnd_parsec": { + "type": "external", + "source": "mysqlnd_parsec", + "arg-type": "enable", + "target": [ + "shared" + ], + "ext-depends": [ + "mysqlnd" + ], + "lib-depends": [ + "libsodium" + ], + "lib-suggests": [ + "openssl" + ] + }, "oci8": { "type": "wip", "support": { diff --git a/config/lib.json b/config/lib.json index 47f3c7b8..3be97248 100644 --- a/config/lib.json +++ b/config/lib.json @@ -361,6 +361,9 @@ "source": "libargon2", "static-libs-unix": [ "libargon2.a" + ], + "lib-suggests": [ + "libsodium" ] }, "libavif": { diff --git a/config/source.json b/config/source.json index 9a80cd05..03626015 100644 --- a/config/source.json +++ b/config/source.json @@ -126,13 +126,23 @@ }, "ext-event": { "type": "url", - "url": "https://bitbucket.org/osmanov/pecl-event/get/3.0.8.tar.gz", + "url": "https://bitbucket.org/osmanov/pecl-event/get/3.1.4.tar.gz", "path": "php-src/ext/event", "license": { "type": "file", "path": "LICENSE" } }, + "ext-excimer": { + "type": "url", + "url": "https://pecl.php.net/get/excimer", + "path": "php-src/ext/excimer", + "filename": "excimer.tgz", + "license": { + "type": "file", + "path": "LICENSE" + } + }, "ext-glfw": { "type": "git", "url": "https://github.com/mario-deluna/php-glfw", @@ -151,6 +161,18 @@ "path": "LICENSE" } }, + "ext-grpc": { + "type": "url", + "url": "https://pecl.php.net/get/grpc", + "path": "php-src/ext/grpc", + "filename": "grpc.tgz", + "license": { + "type": "file", + "path": [ + "LICENSE" + ] + } + }, "ext-imagick": { "type": "url", "url": "https://pecl.php.net/get/imagick", @@ -670,9 +692,10 @@ } }, "libpng": { - "type": "git", - "url": "https://github.com/glennrp/libpng.git", - "rev": "libpng16", + "type": "ghtagtar", + "repo": "pnggroup/libpng", + "match": "v1\\.6\\.\\d+", + "query": "?per_page=150", "provide-pre-built": true, "license": { "type": "file", @@ -680,9 +703,9 @@ } }, "librabbitmq": { - "type": "git", - "url": "https://github.com/alanxz/rabbitmq-c.git", - "rev": "master", + "type": "ghtar", + "repo": "alanxz/rabbitmq-c", + "prefer-stable": true, "license": { "type": "file", "path": "LICENSE" @@ -699,7 +722,7 @@ "libsodium": { "type": "ghrel", "repo": "jedisct1/libsodium", - "match": "libsodium-\\d+(\\.\\d+)*\\.tar\\.gz", + "match": "libsodium-(?!1\\.0\\.21)\\d+(\\.\\d+)*\\.tar\\.gz", "prefer-stable": true, "provide-pre-built": true, "license": { @@ -771,8 +794,9 @@ ] }, "libwebp": { - "type": "url", - "url": "https://github.com/webmproject/libwebp/archive/refs/tags/v1.3.2.tar.gz", + "type": "ghtagtar", + "repo": "webmproject/libwebp", + "match": "v1\\.\\d+\\.\\d+$", "provide-pre-built": true, "license": { "type": "file", @@ -780,8 +804,10 @@ } }, "libxml2": { - "type": "url", - "url": "https://github.com/GNOME/libxml2/archive/refs/tags/v2.12.5.tar.gz", + "type": "ghtagtar", + "repo": "GNOME/libxml2", + "match": "v2\\.\\d+\\.\\d+$", + "provide-pre-built": false, "license": { "type": "file", "path": "Copyright" @@ -868,6 +894,24 @@ "path": "LICENSE" } }, + "mysqlnd_ed25519": { + "type": "pie", + "repo": "mariadb/mysqlnd_ed25519", + "path": "php-src/ext/mysqlnd_ed25519", + "license": { + "type": "file", + "path": "LICENSE" + } + }, + "mysqlnd_parsec": { + "type": "pie", + "repo": "mariadb/mysqlnd_parsec", + "path": "php-src/ext/mysqlnd_parsec", + "license": { + "type": "file", + "path": "LICENSE" + } + }, "ncurses": { "type": "filelist", "url": "https://ftp.gnu.org/pub/gnu/ncurses/", @@ -1169,9 +1213,8 @@ } }, "xdebug": { - "type": "url", - "url": "https://pecl.php.net/get/xdebug", - "filename": "xdebug.tgz", + "type": "pie", + "repo": "xdebug/xdebug", "license": { "type": "file", "path": "LICENSE" diff --git a/docs/en/guide/manual-build.md b/docs/en/guide/manual-build.md index 28f0ec87..319022ca 100644 --- a/docs/en/guide/manual-build.md +++ b/docs/en/guide/manual-build.md @@ -549,22 +549,24 @@ otherwise it will be executed repeatedly in other events. The following are the supported `patch_point` event names and corresponding locations: -| Event name | Event description | -|------------------------------|----------------------------------------------------------------------------------------------------| -| before-libs-extract | Triggered before the dependent libraries extracted | -| after-libs-extract | Triggered after the compiled dependent libraries extracted | -| before-php-extract | Triggered before PHP source code extracted | -| after-php-extract | Triggered after PHP source code extracted | -| before-micro-extract | Triggered before phpmicro extract | -| after-micro-extract | Triggered after phpmicro extracted | -| before-exts-extract | Triggered before the extension (to be compiled) extracted to the PHP source directory | -| after-exts-extract | Triggered after the extension extracted to the PHP source directory | -| before-library[*name*]-build | Triggered before the library named `name` is compiled (such as `before-library[postgresql]-build`) | -| after-library[*name*]-build | Triggered after the library named `name` is compiled | -| before-php-buildconf | Triggered before compiling PHP command `./buildconf` | -| before-php-configure | Triggered before compiling PHP command `./configure` | -| before-php-make | Triggered before compiling PHP command `make` | -| before-sanity-check | Triggered after compiling PHP but before running extended checks | +| Event name | Event description | +|---------------------------------|----------------------------------------------------------------------------------------------------| +| before-libs-extract | Triggered before the dependent libraries extracted | +| after-libs-extract | Triggered after the compiled dependent libraries extracted | +| before-php-extract | Triggered before PHP source code extracted | +| after-php-extract | Triggered after PHP source code extracted | +| before-micro-extract | Triggered before phpmicro extract | +| after-micro-extract | Triggered after phpmicro extracted | +| before-exts-extract | Triggered before the extension (to be compiled) extracted to the PHP source directory | +| after-exts-extract | Triggered after the extension extracted to the PHP source directory | +| before-library[*name*]-build | Triggered before the library named `name` is compiled (such as `before-library[postgresql]-build`) | +| after-library[*name*]-build | Triggered after the library named `name` is compiled | +| after-shared-ext[*name*]-build | Triggered after the shared extension named `name` is compiled | +| before-shared-ext[*name*]-build | Triggered before the shared extension named `name` is compiled | +| before-php-buildconf | Triggered before compiling PHP command `./buildconf` | +| before-php-configure | Triggered before compiling PHP command `./configure` | +| before-php-make | Triggered before compiling PHP command `make` | +| before-sanity-check | Triggered after compiling PHP but before running extended checks | The following is a simple example of temporarily modifying the PHP source code. Enable the CLI function to search for the `php.ini` configuration in the current working directory: diff --git a/docs/zh/guide/manual-build.md b/docs/zh/guide/manual-build.md index 4c24cab8..d7745a02 100644 --- a/docs/zh/guide/manual-build.md +++ b/docs/zh/guide/manual-build.md @@ -500,6 +500,8 @@ bin/spc dev:sort-config ext | after-exts-extract | 在要编译的扩展解压到 PHP 源码目录后触发 | | before-library[*name*]-build | 在名称为 `name` 的库编译前触发(如 `before-library[postgresql]-build`) | | after-library[*name*]-build | 在名称为 `name` 的库编译后触发 | +| after-shared-ext[*name*]-build | 在名称为 `name` 的共享扩展编译后触发(如 `after-shared-ext[redis]-build`) | +| before-shared-ext[*name*]-build | 在名称为 `name` 的共享扩展编译前触发 | | before-php-buildconf | 在编译 PHP 命令 `./buildconf` 前触发 | | before-php-configure | 在编译 PHP 命令 `./configure` 前触发 | | before-php-make | 在编译 PHP 命令 `make` 前触发 | diff --git a/src/SPC/ConsoleApplication.php b/src/SPC/ConsoleApplication.php index 414c3bde..19fdd41d 100644 --- a/src/SPC/ConsoleApplication.php +++ b/src/SPC/ConsoleApplication.php @@ -34,7 +34,7 @@ use Symfony\Component\Console\Application; */ final class ConsoleApplication extends Application { - public const string VERSION = '3.0.0-dev'; + public const string VERSION = '2.8.0'; public function __construct() { diff --git a/src/SPC/builder/Extension.php b/src/SPC/builder/Extension.php index 08b403e6..925a4c8e 100644 --- a/src/SPC/builder/Extension.php +++ b/src/SPC/builder/Extension.php @@ -385,6 +385,9 @@ class Extension logger()->info('Shared extension [' . $this->getName() . '] was already built, skipping (' . $this->getName() . '.so)'); return; } + if ((string) Config::getExt($this->getName(), 'type') === 'addon') { + return; + } logger()->info('Building extension [' . $this->getName() . '] as shared extension (' . $this->getName() . '.so)'); foreach ($this->dependencies as $dependency) { if (!$dependency instanceof Extension) { @@ -395,13 +398,12 @@ class Extension $dependency->buildShared([...$visited, $this->getName()]); } } - if (Config::getExt($this->getName(), 'type') === 'addon') { - return; - } + $this->builder->emitPatchPoint('before-shared-ext[' . $this->getName() . ']-build'); match (PHP_OS_FAMILY) { 'Darwin', 'Linux' => $this->buildUnixShared(), default => throw new WrongUsageException(PHP_OS_FAMILY . ' build shared extensions is not supported yet'), }; + $this->builder->emitPatchPoint('after-shared-ext[' . $this->getName() . ']-build'); } catch (SPCException $e) { $e->bindExtensionInfo(['extension_name' => $this->getName()]); throw $e; @@ -452,12 +454,17 @@ class Extension // process *.so file $soFile = BUILD_MODULES_PATH . '/' . $this->getName() . '.so'; + $soDest = $soFile; + preg_match('/-release\s+(\S*)/', getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), $matches); + if (!empty($matches[1])) { + $soDest = str_replace('.so', '-' . $matches[1] . '.so', $soFile); + } if (!file_exists($soFile)) { throw new ValidationException("extension {$this->getName()} build failed: {$soFile} not found", validation_module: "Extension {$this->getName()} build"); } /** @var UnixBuilderBase $builder */ $builder = $this->builder; - $builder->deployBinary($soFile, $soFile, false); + $builder->deployBinary($soFile, $soDest, false); } /** @@ -543,6 +550,7 @@ class Extension 'CFLAGS' => $config['cflags'], 'CXXFLAGS' => $config['cflags'], 'LDFLAGS' => $config['ldflags'], + 'EXTRA_LDFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), 'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"), 'LD_LIBRARY_PATH' => BUILD_LIB_PATH, ]; diff --git a/src/SPC/builder/LibraryBase.php b/src/SPC/builder/LibraryBase.php index c88e8d96..383faa41 100644 --- a/src/SPC/builder/LibraryBase.php +++ b/src/SPC/builder/LibraryBase.php @@ -184,18 +184,18 @@ abstract class LibraryBase // extract first if not exists if (!is_dir($this->source_dir)) { - $this->getBuilder()->emitPatchPoint('before-library[ ' . static::NAME . ']-extract'); + $this->getBuilder()->emitPatchPoint('before-library[' . static::NAME . ']-extract'); SourceManager::initSource(libs: [static::NAME], source_only: true); - $this->getBuilder()->emitPatchPoint('after-library[ ' . static::NAME . ']-extract'); + $this->getBuilder()->emitPatchPoint('after-library[' . static::NAME . ']-extract'); } if (!$this->patched && $this->patchBeforeBuild()) { file_put_contents($this->source_dir . '/.spc.patched', 'PATCHED!!!'); } - $this->getBuilder()->emitPatchPoint('before-library[ ' . static::NAME . ']-build'); + $this->getBuilder()->emitPatchPoint('before-library[' . static::NAME . ']-build'); $this->build(); $this->installLicense(); - $this->getBuilder()->emitPatchPoint('after-library[ ' . static::NAME . ']-build'); + $this->getBuilder()->emitPatchPoint('after-library[' . static::NAME . ']-build'); return LIB_STATUS_OK; } @@ -346,19 +346,19 @@ abstract class LibraryBase */ protected function installLicense(): void { - FileSystem::createDir(BUILD_ROOT_PATH . '/source-licenses/' . $this->getName()); $source = Config::getLib($this->getName(), 'source'); + FileSystem::createDir(BUILD_ROOT_PATH . "/source-licenses/{$source}"); $license_files = Config::getSource($source)['license'] ?? []; if (is_assoc_array($license_files)) { $license_files = [$license_files]; } foreach ($license_files as $index => $license) { if ($license['type'] === 'text') { - FileSystem::writeFile(BUILD_ROOT_PATH . '/source-licenses/' . $this->getName() . "/{$index}.txt", $license['text']); + FileSystem::writeFile(BUILD_ROOT_PATH . "/source-licenses/{$source}/{$index}.txt", $license['text']); continue; } if ($license['type'] === 'file') { - copy($this->source_dir . '/' . $license['path'], BUILD_ROOT_PATH . '/source-licenses/' . $this->getName() . "/{$index}.txt"); + copy($this->source_dir . '/' . $license['path'], BUILD_ROOT_PATH . "/source-licenses/{$source}/{$index}.txt"); } } } @@ -375,8 +375,17 @@ abstract class LibraryBase return false; } } + $pkg_config_path = getenv('PKG_CONFIG_PATH') ?: ''; + $search_paths = array_filter(explode(is_unix() ? ':' : ';', $pkg_config_path)); foreach (Config::getLib(static::NAME, 'pkg-configs', []) as $name) { - if (!file_exists(BUILD_LIB_PATH . "/pkgconfig/{$name}.pc")) { + $found = false; + foreach ($search_paths as $path) { + if (file_exists($path . "/{$name}.pc")) { + $found = true; + break; + } + } + if (!$found) { return false; } } diff --git a/src/SPC/builder/extension/excimer.php b/src/SPC/builder/extension/excimer.php new file mode 100644 index 00000000..03dd8f22 --- /dev/null +++ b/src/SPC/builder/extension/excimer.php @@ -0,0 +1,19 @@ +builder instanceof WindowsBuilder) { throw new ValidationException('grpc extension does not support windows yet'); } - if (file_exists(SOURCE_PATH . '/php-src/ext/grpc')) { - return false; - } - // soft link to the grpc source code - if (is_dir($this->source_dir . '/src/php/ext/grpc')) { - shell()->exec('ln -s ' . $this->source_dir . '/src/php/ext/grpc ' . SOURCE_PATH . '/php-src/ext/grpc'); - } else { - throw new ValidationException('Cannot find grpc source code in ' . $this->source_dir . '/src/php/ext/grpc'); - } + FileSystem::replaceFileStr( + $this->source_dir . '/src/php/ext/grpc/call.c', + 'zend_exception_get_default(TSRMLS_C),', + 'zend_ce_exception,', + ); if (SPCTarget::getTargetOS() === 'Darwin') { FileSystem::replaceFileRegex( - SOURCE_PATH . '/php-src/ext/grpc/config.m4', + $this->source_dir . '/config.m4', '/GRPC_LIBDIR=.*$/m', 'GRPC_LIBDIR=' . BUILD_LIB_PATH . "\n" . 'LDFLAGS="$LDFLAGS -framework CoreFoundation"' ); diff --git a/src/SPC/builder/extension/imagick.php b/src/SPC/builder/extension/imagick.php index bef772ee..a548a8a3 100644 --- a/src/SPC/builder/extension/imagick.php +++ b/src/SPC/builder/extension/imagick.php @@ -5,6 +5,8 @@ declare(strict_types=1); namespace SPC\builder\extension; use SPC\builder\Extension; +use SPC\toolchain\ToolchainManager; +use SPC\toolchain\ZigToolchain; use SPC\util\CustomExt; #[CustomExt('imagick')] @@ -19,7 +21,9 @@ class imagick extends Extension protected function splitLibsIntoStaticAndShared(string $allLibs): array { [$static, $shared] = parent::splitLibsIntoStaticAndShared($allLibs); - if (str_contains(getenv('PATH'), 'rh/devtoolset') || str_contains(getenv('PATH'), 'rh/gcc-toolset')) { + if (ToolchainManager::getToolchainClass() !== ZigToolchain::class && + (str_contains(getenv('PATH'), 'rh/devtoolset') || str_contains(getenv('PATH'), 'rh/gcc-toolset')) + ) { $static .= ' -l:libstdc++.a'; $shared = str_replace('-lstdc++', '', $shared); } diff --git a/src/SPC/builder/extension/memcache.php b/src/SPC/builder/extension/memcache.php index b63fa47a..32cb301c 100644 --- a/src/SPC/builder/extension/memcache.php +++ b/src/SPC/builder/extension/memcache.php @@ -18,6 +18,9 @@ class memcache extends Extension public function patchBeforeBuildconf(): bool { + if (!$this->isBuildStatic()) { + return false; + } FileSystem::replaceFileStr( SOURCE_PATH . '/php-src/ext/memcache/config9.m4', 'if test -d $abs_srcdir/src ; then', @@ -43,4 +46,27 @@ EOF ); return true; } + + public function patchBeforeSharedConfigure(): bool + { + if (!$this->isBuildShared()) { + return false; + } + FileSystem::replaceFileStr( + SOURCE_PATH . '/php-src/ext/memcache/config9.m4', + 'if test -d $abs_srcdir/main ; then', + 'if test -d $abs_srcdir/src ; then', + ); + FileSystem::replaceFileStr( + SOURCE_PATH . '/php-src/ext/memcache/config9.m4', + 'export CPPFLAGS="$CPPFLAGS $INCLUDES -I$abs_srcdir/main"', + 'export CPPFLAGS="$CPPFLAGS $INCLUDES"', + ); + return true; + } + + protected function getExtraEnv(): array + { + return ['CFLAGS' => '-std=c17']; + } } diff --git a/src/SPC/builder/extension/mongodb.php b/src/SPC/builder/extension/mongodb.php index 745417bb..08861e4e 100644 --- a/src/SPC/builder/extension/mongodb.php +++ b/src/SPC/builder/extension/mongodb.php @@ -24,4 +24,9 @@ class mongodb extends Extension $arg .= $this->builder->getLib('zlib') ? ' --with-mongodb-zlib=yes ' : ' --with-mongodb-zlib=bundled '; return clean_spaces($arg); } + + public function getExtraEnv(): array + { + return ['CFLAGS' => '-std=c17']; + } } diff --git a/src/SPC/builder/extension/mysqlnd_ed25519.php b/src/SPC/builder/extension/mysqlnd_ed25519.php new file mode 100644 index 00000000..7b2b4abc --- /dev/null +++ b/src/SPC/builder/extension/mysqlnd_ed25519.php @@ -0,0 +1,22 @@ +getConfigureArg(); + } +} diff --git a/src/SPC/builder/extension/mysqlnd_parsec.php b/src/SPC/builder/extension/mysqlnd_parsec.php new file mode 100644 index 00000000..d044b1c5 --- /dev/null +++ b/src/SPC/builder/extension/mysqlnd_parsec.php @@ -0,0 +1,22 @@ +getConfigureArg(); + } +} diff --git a/src/SPC/builder/extension/password_argon2.php b/src/SPC/builder/extension/password_argon2.php index 30e6fd2c..d42fe4e3 100644 --- a/src/SPC/builder/extension/password_argon2.php +++ b/src/SPC/builder/extension/password_argon2.php @@ -24,25 +24,6 @@ class password_argon2 extends Extension } } - public function patchBeforeMake(): bool - { - $patched = parent::patchBeforeMake(); - if ($this->builder->getLib('libsodium') !== null) { - $extraLibs = getenv('SPC_EXTRA_LIBS'); - if ($extraLibs !== false) { - $extraLibs = str_replace( - [BUILD_LIB_PATH . '/libargon2.a', BUILD_LIB_PATH . '/libsodium.a'], - ['', BUILD_LIB_PATH . '/libargon2.a ' . BUILD_LIB_PATH . '/libsodium.a'], - $extraLibs, - ); - $extraLibs = trim(preg_replace('/\s+/', ' ', $extraLibs)); // normalize spacing - f_putenv('SPC_EXTRA_LIBS=' . $extraLibs); - return true; - } - } - return $patched; - } - public function getConfigureArg(bool $shared = false): string { if ($this->builder->getLib('openssl') !== null) { diff --git a/src/SPC/builder/extension/pgsql.php b/src/SPC/builder/extension/pgsql.php index f22f7ba6..06efa8b1 100644 --- a/src/SPC/builder/extension/pgsql.php +++ b/src/SPC/builder/extension/pgsql.php @@ -45,7 +45,7 @@ class pgsql extends Extension protected function getExtraEnv(): array { return [ - 'CFLAGS' => '-Wno-int-conversion', + 'CFLAGS' => '-std=c17 -Wno-int-conversion', ]; } } diff --git a/src/SPC/builder/extension/swoole.php b/src/SPC/builder/extension/swoole.php index f6ff5931..4e292a36 100644 --- a/src/SPC/builder/extension/swoole.php +++ b/src/SPC/builder/extension/swoole.php @@ -17,6 +17,7 @@ class swoole extends Extension public function patchBeforeMake(): bool { $patched = parent::patchBeforeMake(); + FileSystem::replaceFileStr($this->source_dir . '/ext-src/php_swoole_private.h', 'PHP_VERSION_ID > 80500', 'PHP_VERSION_ID >= 80600'); if ($this->builder instanceof MacOSBuilder) { // Fix swoole with event extension conflict bug $util_path = shell()->execWithResult('xcrun --show-sdk-path', false)[1][0] . '/usr/include/util.h'; diff --git a/src/SPC/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php index 0d6f77fb..004c37de 100644 --- a/src/SPC/builder/linux/LinuxBuilder.php +++ b/src/SPC/builder/linux/LinuxBuilder.php @@ -283,11 +283,14 @@ class LinuxBuilder extends UnixBuilderBase // process libphp.so for shared embed $libphpSo = BUILD_LIB_PATH . '/libphp.so'; + $libphpSoDest = BUILD_LIB_PATH . '/libphp.so'; if (file_exists($libphpSo)) { - // post actions: rename libphp.so to libphp-.so if -release is set in LDFLAGS - $this->processLibphpSoFile($libphpSo); // deploy libphp.so - $this->deployBinary($libphpSo, $libphpSo, false); + preg_match('/-release\s+(\S*)/', getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), $matches); + if (!empty($matches[1])) { + $libphpSoDest = str_replace('.so', '-' . $matches[1] . '.so', $libphpSo); + } + $this->deployBinary($libphpSo, $libphpSoDest, false); } // process shared extensions build-with-php @@ -324,74 +327,6 @@ class LinuxBuilder extends UnixBuilderBase ]); } - private function processLibphpSoFile(string $libphpSo): void - { - $ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS') ?: ''; - $libDir = BUILD_LIB_PATH; - $modulesDir = BUILD_MODULES_PATH; - $realLibName = 'libphp.so'; - $cwd = getcwd(); - - if (preg_match('/-release\s+(\S+)/', $ldflags, $matches)) { - $release = $matches[1]; - $realLibName = "libphp-{$release}.so"; - $libphpRelease = "{$libDir}/{$realLibName}"; - if (!file_exists($libphpRelease) && file_exists($libphpSo)) { - rename($libphpSo, $libphpRelease); - } - if (file_exists($libphpRelease)) { - chdir($libDir); - if (file_exists($libphpSo)) { - unlink($libphpSo); - } - symlink($realLibName, 'libphp.so'); - shell()->exec(sprintf( - 'patchelf --set-soname %s %s', - escapeshellarg($realLibName), - escapeshellarg($libphpRelease) - )); - } - if (is_dir($modulesDir)) { - chdir($modulesDir); - foreach ($this->getExts() as $ext) { - if (!$ext->isBuildShared()) { - continue; - } - $name = $ext->getName(); - $versioned = "{$name}-{$release}.so"; - $unversioned = "{$name}.so"; - $src = "{$modulesDir}/{$versioned}"; - $dst = "{$modulesDir}/{$unversioned}"; - if (is_file($src)) { - rename($src, $dst); - shell()->exec(sprintf( - 'patchelf --set-soname %s %s', - escapeshellarg($unversioned), - escapeshellarg($dst) - )); - } - } - } - chdir($cwd); - } - - $target = "{$libDir}/{$realLibName}"; - if (file_exists($target)) { - [, $output] = shell()->execWithResult('readelf -d ' . escapeshellarg($target)); - $output = implode("\n", $output); - if (preg_match('/SONAME.*\[(.+)]/', $output, $sonameMatch)) { - $currentSoname = $sonameMatch[1]; - if ($currentSoname !== basename($target)) { - shell()->exec(sprintf( - 'patchelf --set-soname %s %s', - escapeshellarg(basename($target)), - escapeshellarg($target) - )); - } - } - } - } - /** * Patch micro.sfx after UPX compression. * micro needs special section handling in LinuxBuilder. diff --git a/src/SPC/builder/linux/library/liburing.php b/src/SPC/builder/linux/library/liburing.php index 82249cf8..9a67f50c 100644 --- a/src/SPC/builder/linux/library/liburing.php +++ b/src/SPC/builder/linux/library/liburing.php @@ -6,6 +6,8 @@ namespace SPC\builder\linux\library; use SPC\builder\linux\SystemUtil; use SPC\store\FileSystem; +use SPC\toolchain\GccNativeToolchain; +use SPC\toolchain\ToolchainManager; use SPC\util\executor\UnixAutoconfExecutor; use SPC\util\SPCTarget; @@ -15,26 +17,19 @@ class liburing extends LinuxLibraryBase public function patchBeforeBuild(): bool { - if (!SystemUtil::isMuslDist()) { - return false; + if (SystemUtil::isMuslDist()) { + FileSystem::replaceFileStr($this->source_dir . '/configure', 'realpath -s', 'realpath'); + return true; } - FileSystem::replaceFileStr($this->source_dir . '/configure', 'realpath -s', 'realpath'); - return true; + return false; } protected function build(): void { - $use_libc = SPCTarget::getLibc() !== 'glibc' || version_compare(SPCTarget::getLibcVersion(), '2.30', '>='); + $use_libc = ToolchainManager::getToolchainClass() !== GccNativeToolchain::class || version_compare(SPCTarget::getLibcVersion(), '2.30', '>='); $make = UnixAutoconfExecutor::create($this); - if (!$use_libc) { - $make->appendEnv([ - 'CC' => 'gcc', // libc-less version fails to compile with clang or zig - 'CXX' => 'g++', - 'AR' => 'ar', - 'LD' => 'ld', - ]); - } else { + if ($use_libc) { $make->appendEnv([ 'CFLAGS' => '-D_GNU_SOURCE', ]); @@ -51,7 +46,7 @@ class liburing extends LinuxLibraryBase $use_libc ? '--use-libc' : '', ) ->configure() - ->make('library', 'install ENABLE_SHARED=0', with_clean: false); + ->make('library ENABLE_SHARED=0', 'install ENABLE_SHARED=0', with_clean: false); $this->patchPkgconfPrefix(); } diff --git a/src/SPC/builder/linux/library/openssl.php b/src/SPC/builder/linux/library/openssl.php index d9e04b32..bfc3936b 100644 --- a/src/SPC/builder/linux/library/openssl.php +++ b/src/SPC/builder/linux/library/openssl.php @@ -21,6 +21,7 @@ declare(strict_types=1); namespace SPC\builder\linux\library; +use SPC\builder\linux\SystemUtil; use SPC\store\FileSystem; class openssl extends LinuxLibraryBase @@ -51,6 +52,9 @@ class openssl extends LinuxLibraryBase $zlib_extra = ''; } + $openssl_dir = getenv('OPENSSLDIR') ?: null; + // TODO: in v3 use the following: $openssl_dir ??= SystemUtil::getOSRelease()['dist'] === 'redhat' ? '/etc/pki/tls' : '/etc/ssl'; + $openssl_dir ??= '/etc/ssl'; $ex_lib = trim($ex_lib); shell()->cd($this->source_dir)->initializeEnv($this) @@ -58,10 +62,11 @@ class openssl extends LinuxLibraryBase "{$env} ./Configure no-shared {$extra} " . '--prefix=' . BUILD_ROOT_PATH . ' ' . '--libdir=' . BUILD_LIB_PATH . ' ' . - '--openssldir=/etc/ssl ' . + "--openssldir={$openssl_dir} " . "{$zlib_extra}" . 'enable-pie ' . 'no-legacy ' . + 'no-tests ' . "linux-{$arch}" ) ->exec('make clean') diff --git a/src/SPC/builder/traits/UnixLibraryTrait.php b/src/SPC/builder/traits/UnixLibraryTrait.php index 868cf6f7..ec1e7c2b 100644 --- a/src/SPC/builder/traits/UnixLibraryTrait.php +++ b/src/SPC/builder/traits/UnixLibraryTrait.php @@ -34,7 +34,7 @@ trait UnixLibraryTrait $files = array_map(fn ($x) => "{$x}.pc", $conf_pc); } foreach ($files as $name) { - $realpath = realpath(BUILD_ROOT_PATH . '/lib/pkgconfig/' . $name); + $realpath = realpath(BUILD_LIB_PATH . '/pkgconfig/' . $name); if ($realpath === false) { throw new PatchException('pkg-config prefix patcher', 'Cannot find library [' . static::NAME . '] pkgconfig file [' . $name . '] in ' . BUILD_LIB_PATH . '/pkgconfig/ !'); } diff --git a/src/SPC/builder/traits/UnixSystemUtilTrait.php b/src/SPC/builder/traits/UnixSystemUtilTrait.php index b1ef9db4..ff75bf7c 100644 --- a/src/SPC/builder/traits/UnixSystemUtilTrait.php +++ b/src/SPC/builder/traits/UnixSystemUtilTrait.php @@ -72,12 +72,8 @@ trait UnixSystemUtilTrait if (!is_file($symbol_file)) { throw new SPCInternalException("The symbol file {$symbol_file} does not exist, please check if nm command is available."); } - // https://github.com/ziglang/zig/issues/24662 - if (ToolchainManager::getToolchainClass() === ZigToolchain::class) { - return '-Wl,--export-dynamic'; - } - // macOS - if (SPCTarget::getTargetOS() !== 'Linux') { + // macOS/zig + if (SPCTarget::getTargetOS() !== 'Linux' || ToolchainManager::getToolchainClass() === ZigToolchain::class) { return "-Wl,-exported_symbols_list,{$symbol_file}"; } return "-Wl,--dynamic-list={$symbol_file}"; diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php index d98059cb..464c9b2f 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -145,11 +145,10 @@ abstract class UnixBuilderBase extends BuilderBase throw new SPCInternalException("Deploy failed. Cannot find file after copy: {$dst}"); } - // extract debug info - $this->extractDebugInfo($dst); - - // strip if (!$this->getOption('no-strip')) { + // extract debug info + $this->extractDebugInfo($dst); + // extra strip $this->stripBinary($dst); } @@ -236,8 +235,10 @@ abstract class UnixBuilderBase extends BuilderBase $lens .= ' -static'; } $dynamic_exports = ''; + $embedType = 'static'; // if someone changed to EMBED_TYPE=shared, we need to add LD_LIBRARY_PATH if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') { + $embedType = 'shared'; if (PHP_OS_FAMILY === 'Darwin') { $ext_path = 'DYLD_LIBRARY_PATH=' . BUILD_LIB_PATH . ':$DYLD_LIBRARY_PATH '; } else { @@ -256,18 +257,19 @@ abstract class UnixBuilderBase extends BuilderBase } } $cc = getenv('CC'); + [$ret, $out] = shell()->cd($sample_file_path)->execWithResult("{$cc} -o embed embed.c {$lens} {$dynamic_exports}"); if ($ret !== 0) { throw new ValidationException( - 'embed failed sanity check: build failed. Error message: ' . implode("\n", $out), - validation_module: 'static libphp.a sanity check' + 'embed failed to build. Error message: ' . implode("\n", $out), + validation_module: $embedType . ' libphp embed build sanity check' ); } [$ret, $output] = shell()->cd($sample_file_path)->execWithResult($ext_path . './embed'); if ($ret !== 0 || trim(implode('', $output)) !== 'hello') { throw new ValidationException( - 'embed failed sanity check: run failed. Error message: ' . implode("\n", $output), - validation_module: 'static libphp.a sanity check' + 'embed failed to run. Error message: ' . implode("\n", $output), + validation_module: $embedType . ' libphp embed run sanity check' ); } } diff --git a/src/SPC/builder/unix/library/gmp.php b/src/SPC/builder/unix/library/gmp.php index f09976d8..97a88ba1 100644 --- a/src/SPC/builder/unix/library/gmp.php +++ b/src/SPC/builder/unix/library/gmp.php @@ -10,7 +10,14 @@ trait gmp { protected function build(): void { - UnixAutoconfExecutor::create($this)->configure()->make(); + UnixAutoconfExecutor::create($this) + ->appendEnv([ + 'CFLAGS' => '-std=c17', + ]) + ->configure( + '--enable-fat' + ) + ->make(); $this->patchPkgconfPrefix(['gmp.pc']); } } diff --git a/src/SPC/builder/unix/library/libjxl.php b/src/SPC/builder/unix/library/libjxl.php index 13f8481b..4c922d9d 100644 --- a/src/SPC/builder/unix/library/libjxl.php +++ b/src/SPC/builder/unix/library/libjxl.php @@ -29,13 +29,17 @@ trait libjxl ); if (ToolchainManager::getToolchainClass() === ZigToolchain::class) { - $cmake->addConfigureArgs( - '-DCXX_MAVX512F_SUPPORTED:BOOL=FALSE', - '-DCXX_MAVX512DQ_SUPPORTED:BOOL=FALSE', - '-DCXX_MAVX512CD_SUPPORTED:BOOL=FALSE', - '-DCXX_MAVX512BW_SUPPORTED:BOOL=FALSE', - '-DCXX_MAVX512VL_SUPPORTED:BOOL=FALSE' - ); + $cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: getenv('CFLAGS') ?: ''; + $has_avx512 = str_contains($cflags, '-mavx512') || str_contains($cflags, '-march=x86-64-v4'); + if (!$has_avx512) { + $cmake->addConfigureArgs( + '-DCXX_MAVX512F_SUPPORTED:BOOL=FALSE', + '-DCXX_MAVX512DQ_SUPPORTED:BOOL=FALSE', + '-DCXX_MAVX512CD_SUPPORTED:BOOL=FALSE', + '-DCXX_MAVX512BW_SUPPORTED:BOOL=FALSE', + '-DCXX_MAVX512VL_SUPPORTED:BOOL=FALSE' + ); + } } $cmake->build(); diff --git a/src/SPC/builder/unix/library/libwebp.php b/src/SPC/builder/unix/library/libwebp.php index 46a88af4..015fa73b 100644 --- a/src/SPC/builder/unix/library/libwebp.php +++ b/src/SPC/builder/unix/library/libwebp.php @@ -10,8 +10,26 @@ trait libwebp { protected function build(): void { + $code = '#include +int main() { return _mm256_cvtsi256_si32(_mm256_setzero_si256()); }'; + $cc = getenv('CC') ?: 'gcc'; + [$ret] = shell()->execWithResult("printf '%s' '{$code}' | {$cc} -x c -mavx2 -o /dev/null - 2>&1"); + $disableAvx2 = $ret !== 0 && GNU_ARCH === 'x86_64' && PHP_OS_FAMILY === 'Linux'; + UnixCMakeExecutor::create($this) - ->addConfigureArgs('-DWEBP_BUILD_EXTRAS=ON') + ->addConfigureArgs( + '-DWEBP_BUILD_EXTRAS=OFF', + '-DWEBP_BUILD_ANIM_UTILS=OFF', + '-DWEBP_BUILD_CWEBP=OFF', + '-DWEBP_BUILD_DWEBP=OFF', + '-DWEBP_BUILD_GIF2WEBP=OFF', + '-DWEBP_BUILD_IMG2WEBP=OFF', + '-DWEBP_BUILD_VWEBP=OFF', + '-DWEBP_BUILD_WEBPINFO=OFF', + '-DWEBP_BUILD_WEBPMUX=OFF', + '-DWEBP_BUILD_FUZZTEST=OFF', + $disableAvx2 ? '-DWEBP_ENABLE_SIMD=OFF' : '' + ) ->build(); // patch pkgconfig $this->patchPkgconfPrefix(patch_option: PKGCONF_PATCH_PREFIX | PKGCONF_PATCH_LIBDIR); diff --git a/src/SPC/builder/unix/library/ncurses.php b/src/SPC/builder/unix/library/ncurses.php index 27725c3d..cb8c10df 100644 --- a/src/SPC/builder/unix/library/ncurses.php +++ b/src/SPC/builder/unix/library/ncurses.php @@ -16,6 +16,7 @@ trait ncurses UnixAutoconfExecutor::create($this) ->appendEnv([ + 'CFLAGS' => '-std=c17', 'LDFLAGS' => SPCTarget::isStatic() ? '-static' : '', ]) ->configure( @@ -29,7 +30,7 @@ trait ncurses '--without-tests', '--without-dlsym', '--without-debug', - '-enable-symlinks', + '--enable-symlinks', "--bindir={$this->getBinDir()}", "--includedir={$this->getIncludeDir()}", "--libdir={$this->getLibDir()}", diff --git a/src/SPC/builder/unix/library/postgresql.php b/src/SPC/builder/unix/library/postgresql.php index 6e0cb606..a72f3a1a 100644 --- a/src/SPC/builder/unix/library/postgresql.php +++ b/src/SPC/builder/unix/library/postgresql.php @@ -50,7 +50,7 @@ trait postgresql $config = $spc->config(libraries: $libs, include_suggest_lib: $this->builder->getOption('with-suggested-libs', false)); $env_vars = [ - 'CFLAGS' => $config['cflags'], + 'CFLAGS' => $config['cflags'] . ' -std=c17', 'CPPFLAGS' => '-DPIC', 'LDFLAGS' => $config['ldflags'], 'LIBS' => $config['libs'], diff --git a/src/SPC/builder/windows/library/curl.php b/src/SPC/builder/windows/library/curl.php index 1229dbd6..bba130e1 100644 --- a/src/SPC/builder/windows/library/curl.php +++ b/src/SPC/builder/windows/library/curl.php @@ -30,7 +30,6 @@ class curl extends WindowsLibraryBase '-DCMAKE_BUILD_TYPE=Release ' . '-DBUILD_SHARED_LIBS=OFF ' . '-DBUILD_STATIC_LIBS=ON ' . - '-DCURL_STATICLIB=ON ' . '-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' ' . '-DBUILD_CURL_EXE=OFF ' . // disable curl.exe '-DBUILD_TESTING=OFF ' . // disable tests @@ -42,9 +41,9 @@ class curl extends WindowsLibraryBase '-DCURL_USE_OPENSSL=OFF ' . // disable openssl due to certificate issue '-DCURL_ENABLE_SSL=ON ' . '-DUSE_NGHTTP2=ON ' . // enable nghttp2 + '-DSHARE_LIB_OBJECT=OFF ' . // disable shared lib object '-DCURL_USE_LIBSSH2=ON ' . // enable libssh2 '-DENABLE_IPV6=ON ' . // enable ipv6 - '-DNGHTTP2_CFLAGS="/DNGHTTP2_STATICLIB" ' . $alt ) ->execWithWrapper( @@ -53,5 +52,7 @@ class curl extends WindowsLibraryBase ); // move libcurl.lib to libcurl_a.lib rename(BUILD_LIB_PATH . '\libcurl.lib', BUILD_LIB_PATH . '\libcurl_a.lib'); + + FileSystem::replaceFileStr(BUILD_INCLUDE_PATH . '\curl\curl.h', '#ifdef CURL_STATICLIB', '#if 1'); } } diff --git a/src/SPC/builder/windows/library/nghttp2.php b/src/SPC/builder/windows/library/nghttp2.php index 7e6e999d..5a1c6bf1 100644 --- a/src/SPC/builder/windows/library/nghttp2.php +++ b/src/SPC/builder/windows/library/nghttp2.php @@ -29,11 +29,16 @@ class nghttp2 extends WindowsLibraryBase '-DBUILD_SHARED_LIBS=OFF ' . '-DENABLE_STATIC_CRT=ON ' . '-DENABLE_LIB_ONLY=ON ' . - '-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' ' + '-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' ' . + '-DENABLE_STATIC_CRT=ON ' . + '-DENABLE_DOC=OFF ' . + '-DBUILD_TESTING=OFF ' ) ->execWithWrapper( $this->builder->makeSimpleWrapper('cmake'), "--build build --config Release --target install -j{$this->builder->concurrency}" ); + + FileSystem::replaceFileStr(BUILD_INCLUDE_PATH . '\nghttp2\nghttp2.h', '#ifdef NGHTTP2_STATICLIB', '#if 1'); } } diff --git a/src/SPC/doctor/item/LinuxToolCheckList.php b/src/SPC/doctor/item/LinuxToolCheckList.php index 08a2b4dc..6e4c5034 100644 --- a/src/SPC/doctor/item/LinuxToolCheckList.php +++ b/src/SPC/doctor/item/LinuxToolCheckList.php @@ -22,7 +22,6 @@ class LinuxToolCheckList 'bzip2', 'cmake', 'gcc', 'g++', 'patch', 'binutils-gold', 'libtoolize', 'which', - 'patchelf', ]; public const TOOLS_DEBIAN = [ @@ -31,7 +30,6 @@ class LinuxToolCheckList 'tar', 'unzip', 'gzip', 'gcc', 'g++', 'bzip2', 'cmake', 'patch', 'xz', 'libtoolize', 'which', - 'patchelf', ]; public const TOOLS_RHEL = [ @@ -39,8 +37,7 @@ class LinuxToolCheckList 'git', 'autoconf', 'automake', 'tar', 'unzip', 'gzip', 'gcc', 'g++', 'bzip2', 'cmake', 'patch', 'which', - 'xz', 'libtool', 'gettext-devel', - 'patchelf', 'file', + 'xz', 'libtool', 'gettext-devel', 'file', ]; public const TOOLS_ARCH = [ diff --git a/src/SPC/store/Downloader.php b/src/SPC/store/Downloader.php index 63bec807..ccf61dd8 100644 --- a/src/SPC/store/Downloader.php +++ b/src/SPC/store/Downloader.php @@ -97,8 +97,9 @@ class Downloader public static function getLatestGithubTarball(string $name, array $source, string $type = 'releases'): array { logger()->debug("finding {$name} source from github {$type} tarball"); + $source['query'] ??= ''; $data = json_decode(self::curlExec( - url: "https://api.github.com/repos/{$source['repo']}/{$type}", + url: "https://api.github.com/repos/{$source['repo']}/{$type}{$source['query']}", hooks: [[CurlHook::class, 'setupGithubToken']], retries: self::getRetryAttempts() ), true, 512, JSON_THROW_ON_ERROR); @@ -108,6 +109,9 @@ class Downloader if (($rel['prerelease'] ?? false) === true && ($source['prefer-stable'] ?? false)) { continue; } + if (($rel['draft'] ?? false) === true && (($source['prefer-stable'] ?? false) || !$rel['tarball_url'])) { + continue; + } if (!($source['match'] ?? null)) { $url = $rel['tarball_url'] ?? null; break; diff --git a/src/SPC/store/FileSystem.php b/src/SPC/store/FileSystem.php index 3b88a2bc..1d0815ce 100644 --- a/src/SPC/store/FileSystem.php +++ b/src/SPC/store/FileSystem.php @@ -408,13 +408,13 @@ class FileSystem continue; } $sub_file = self::convertPath($dir . '/' . $v); - if (is_dir($sub_file)) { - # 如果是 目录 且 递推 , 则递推添加下级文件 - if (!self::removeDir($sub_file)) { + if (is_link($sub_file) || is_file($sub_file)) { + if (!unlink($sub_file)) { return false; } - } elseif (is_link($sub_file) || is_file($sub_file)) { - if (!unlink($sub_file)) { + } elseif (is_dir($sub_file)) { + # 如果是 目录 且 递推 , 则递推添加下级文件 + if (!self::removeDir($sub_file)) { return false; } } @@ -572,6 +572,44 @@ class FileSystem return file_put_contents($file, implode('', $lines)); } + /** + * Move file or directory, handling cross-device scenarios + * Uses rename() if possible, falls back to copy+delete for cross-device moves + * + * @param string $source Source path + * @param string $dest Destination path + */ + public static function moveFileOrDir(string $source, string $dest): void + { + $source = self::convertPath($source); + $dest = self::convertPath($dest); + + // Check if source and dest are on the same device to avoid cross-device rename errors + $source_stat = @stat($source); + $dest_parent = dirname($dest); + $dest_stat = @stat($dest_parent); + + // Only use rename if on same device + if ($source_stat !== false && $dest_stat !== false && $source_stat['dev'] === $dest_stat['dev']) { + if (@rename($source, $dest)) { + return; + } + } + + // Fall back to copy + delete for cross-device moves or if rename failed + if (is_dir($source)) { + self::copyDir($source, $dest); + self::removeDir($source); + } else { + if (!copy($source, $dest)) { + throw new FileSystemException("Failed to copy file from {$source} to {$dest}"); + } + if (!unlink($source)) { + throw new FileSystemException("Failed to remove source file: {$source}"); + } + } + } + private static function extractArchive(string $filename, string $target): void { // Create base dir @@ -648,44 +686,6 @@ class FileSystem }; } - /** - * Move file or directory, handling cross-device scenarios - * Uses rename() if possible, falls back to copy+delete for cross-device moves - * - * @param string $source Source path - * @param string $dest Destination path - */ - private static function moveFileOrDir(string $source, string $dest): void - { - $source = self::convertPath($source); - $dest = self::convertPath($dest); - - // Check if source and dest are on the same device to avoid cross-device rename errors - $source_stat = @stat($source); - $dest_parent = dirname($dest); - $dest_stat = @stat($dest_parent); - - // Only use rename if on same device - if ($source_stat !== false && $dest_stat !== false && $source_stat['dev'] === $dest_stat['dev']) { - if (@rename($source, $dest)) { - return; - } - } - - // Fall back to copy + delete for cross-device moves or if rename failed - if (is_dir($source)) { - self::copyDir($source, $dest); - self::removeDir($source); - } else { - if (!copy($source, $dest)) { - throw new FileSystemException("Failed to copy file from {$source} to {$dest}"); - } - if (!unlink($source)) { - throw new FileSystemException("Failed to remove source file: {$source}"); - } - } - } - /** * Unzip file with stripping top-level directory */ diff --git a/src/SPC/store/SourcePatcher.php b/src/SPC/store/SourcePatcher.php index f628544f..0068f53e 100644 --- a/src/SPC/store/SourcePatcher.php +++ b/src/SPC/store/SourcePatcher.php @@ -25,6 +25,7 @@ class SourcePatcher FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchFfiCentos7FixO3strncmp']); FileSystem::addSourceExtractHook('sqlsrv', [__CLASS__, 'patchSQLSRVWin32']); FileSystem::addSourceExtractHook('pdo_sqlsrv', [__CLASS__, 'patchSQLSRVWin32']); + FileSystem::addSourceExtractHook('pdo_sqlsrv', [__CLASS__, 'patchSQLSRVPhp85']); FileSystem::addSourceExtractHook('yaml', [__CLASS__, 'patchYamlWin32']); FileSystem::addSourceExtractHook('libyaml', [__CLASS__, 'patchLibYaml']); FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchImapLicense']); @@ -432,6 +433,23 @@ class SourcePatcher return false; } + /** + * Fix the compilation issue of pdo_sqlsrv with php 8.5 + */ + public static function patchSQLSRVPhp85(): bool + { + $source_dir = SOURCE_PATH . '/php-src/ext/pdo_sqlsrv'; + if (!file_exists($source_dir . '/config.m4') && is_dir($source_dir . '/source/pdo_sqlsrv')) { + FileSystem::moveFileOrDir($source_dir . '/LICENSE', $source_dir . '/source/pdo_sqlsrv/LICENSE'); + FileSystem::moveFileOrDir($source_dir . '/source/shared', $source_dir . '/source/pdo_sqlsrv/shared'); + FileSystem::moveFileOrDir($source_dir . '/source/pdo_sqlsrv', SOURCE_PATH . '/pdo_sqlsrv'); + FileSystem::removeDir($source_dir); + FileSystem::moveFileOrDir(SOURCE_PATH . '/pdo_sqlsrv', $source_dir); + return true; + } + return false; + } + public static function patchYamlWin32(): bool { FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/yaml/config.w32', "lib.substr(lib.length - 6, 6) == '_a.lib'", "lib.substr(lib.length - 6, 6) == '_a.lib' || 'yes' == 'yes'"); diff --git a/src/SPC/store/pkg/GoXcaddy.php b/src/SPC/store/pkg/GoXcaddy.php index 0c1c6f8c..93821aaa 100644 --- a/src/SPC/store/pkg/GoXcaddy.php +++ b/src/SPC/store/pkg/GoXcaddy.php @@ -48,10 +48,10 @@ class GoXcaddy extends CustomPackage 'macos' => 'darwin', default => throw new \InvalidArgumentException('Unsupported OS: ' . $name), }; - $go_version = '1.25.0'; + [$go_version] = explode("\n", Downloader::curlExec('https://go.dev/VERSION?m=text')); $config = [ 'type' => 'url', - 'url' => "https://go.dev/dl/go{$go_version}.{$os}-{$arch}.tar.gz", + 'url' => "https://go.dev/dl/{$go_version}.{$os}-{$arch}.tar.gz", ]; Downloader::downloadPackage($name, $config, $force); } diff --git a/src/SPC/store/pkg/Zig.php b/src/SPC/store/pkg/Zig.php index e9865dbe..c2a81c0d 100644 --- a/src/SPC/store/pkg/Zig.php +++ b/src/SPC/store/pkg/Zig.php @@ -72,8 +72,11 @@ class Zig extends CustomPackage $latest_version = null; foreach ($index_json as $version => $data) { - $latest_version = $version; - break; + // Skip the master branch, get the latest stable release + if ($version !== 'master') { + $latest_version = $version; + break; + } } if (!$latest_version) { diff --git a/src/SPC/toolchain/ZigToolchain.php b/src/SPC/toolchain/ZigToolchain.php index bb423db2..1b7cc70d 100644 --- a/src/SPC/toolchain/ZigToolchain.php +++ b/src/SPC/toolchain/ZigToolchain.php @@ -67,7 +67,8 @@ class ZigToolchain implements ToolchainInterface $cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: getenv('CFLAGS') ?: ''; $has_avx512 = str_contains($cflags, '-mavx512') || str_contains($cflags, '-march=x86-64-v4'); if (!$has_avx512) { - GlobalEnvManager::putenv('SPC_EXTRA_PHP_VARS=php_cv_have_avx512=no php_cv_have_avx512vbmi=no'); + $extra_vars = getenv('SPC_EXTRA_PHP_VARS') ?: ''; + GlobalEnvManager::putenv("SPC_EXTRA_PHP_VARS=php_cv_have_avx512=no php_cv_have_avx512vbmi=no {$extra_vars}"); } } diff --git a/src/SPC/util/SPCConfigUtil.php b/src/SPC/util/SPCConfigUtil.php index a74d6a24..8c4fa926 100644 --- a/src/SPC/util/SPCConfigUtil.php +++ b/src/SPC/util/SPCConfigUtil.php @@ -80,7 +80,6 @@ class SPCConfigUtil $libs = $this->getLibsString($libraries, !$this->absolute_libs); // additional OS-specific libraries (e.g. macOS -lresolv) - // embed if ($extra_libs = SPCTarget::getRuntimeLibs()) { $libs .= " {$extra_libs}"; } @@ -226,9 +225,17 @@ class SPCConfigUtil // parse pkg-configs foreach ($libraries as $library) { $pc = Config::getLib($library, 'pkg-configs', []); + $pkg_config_path = getenv('PKG_CONFIG_PATH') ?: ''; + $search_paths = array_filter(explode(is_unix() ? ':' : ';', $pkg_config_path)); foreach ($pc as $file) { - if (!file_exists(BUILD_LIB_PATH . "/pkgconfig/{$file}.pc")) { - throw new WrongUsageException("pkg-config file '{$file}.pc' for lib [{$library}] does not exist in '" . BUILD_LIB_PATH . "/pkgconfig'. Please build it first."); + $found = false; + foreach ($search_paths as $path) { + if (file_exists($path . "/{$file}.pc")) { + $found = true; + } + } + if (!$found) { + throw new WrongUsageException("pkg-config file '{$file}.pc' for lib [{$library}] does not exist. Please build it first."); } } $pc_cflags = implode(' ', $pc); @@ -257,9 +264,17 @@ class SPCConfigUtil foreach ($libraries as $library) { // add pkg-configs libs $pkg_configs = Config::getLib($library, 'pkg-configs', []); - foreach ($pkg_configs as $pkg_config) { - if (!file_exists(BUILD_LIB_PATH . "/pkgconfig/{$pkg_config}.pc")) { - throw new WrongUsageException("pkg-config file '{$pkg_config}.pc' for lib [{$library}] does not exist in '" . BUILD_LIB_PATH . "/pkgconfig'. Please build it first."); + $pkg_config_path = getenv('PKG_CONFIG_PATH') ?: ''; + $search_paths = array_filter(explode(is_unix() ? ':' : ';', $pkg_config_path)); + foreach ($pkg_configs as $file) { + $found = false; + foreach ($search_paths as $path) { + if (file_exists($path . "/{$file}.pc")) { + $found = true; + } + } + if (!$found) { + throw new WrongUsageException("pkg-config file '{$file}.pc' for lib [{$library}] does not exist. Please build it first."); } } $pkg_configs = implode(' ', $pkg_configs); diff --git a/src/globals/test-extensions.php b/src/globals/test-extensions.php index f44914ec..e2186c8e 100644 --- a/src/globals/test-extensions.php +++ b/src/globals/test-extensions.php @@ -14,8 +14,8 @@ declare(strict_types=1); // test php version (8.1 ~ 8.4 available, multiple for matrix) $test_php_version = [ // '8.1', - // '8.2', - // '8.3', + '8.2', + '8.3', '8.4', '8.5', // 'git', @@ -25,11 +25,11 @@ $test_php_version = [ $test_os = [ 'macos-15-intel', // bin/spc for x86_64 'macos-15', // bin/spc for arm64 - // 'ubuntu-latest', // bin/spc-alpine-docker for x86_64 + 'ubuntu-latest', // bin/spc-alpine-docker for x86_64 'ubuntu-22.04', // bin/spc-gnu-docker for x86_64 - // 'ubuntu-24.04', // bin/spc for x86_64 + 'ubuntu-24.04', // bin/spc for x86_64 // 'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64 - 'ubuntu-24.04-arm', // bin/spc for arm64 + // 'ubuntu-24.04-arm', // bin/spc for arm64 // 'windows-2022', // .\bin\spc.ps1 // 'windows-2025', ]; @@ -50,14 +50,14 @@ $prefer_pre_built = false; // If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`). $extensions = match (PHP_OS_FAMILY) { - 'Linux', 'Darwin' => 'bcmath', + 'Linux', 'Darwin' => 'mysqli,gmp', 'Windows' => 'bcmath', }; // If you want to test shared extensions, add them below (comma separated, example `bcmath,openssl`). $shared_extensions = match (PHP_OS_FAMILY) { - 'Linux' => 'pcov', - 'Darwin' => 'pcov', + 'Linux' => 'grpc,mysqlnd_parsec,mysqlnd_ed25519', + 'Darwin' => '', 'Windows' => '', }; @@ -66,7 +66,7 @@ $with_suggested_libs = false; // If you want to test extra libs for extensions, add them below (comma separated, example `libwebp,libavif`). Unnecessary, when $with_suggested_libs is true. $with_libs = match (PHP_OS_FAMILY) { - 'Linux', 'Darwin' => '', + 'Linux', 'Darwin' => 'libwebp', 'Windows' => '', };