diff --git a/bin/spc-alpine-docker b/bin/spc-alpine-docker index 28cc4939..16609b5f 100755 --- a/bin/spc-alpine-docker +++ b/bin/spc-alpine-docker @@ -122,6 +122,20 @@ MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/pkgroot:/app/pkgroot" # shellcheck disable=SC2086 # shellcheck disable=SC2090 if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then + echo "* Debug mode enabled, run docker in interactive mode." + echo "* You can use 'exit' to exit the docker container." + echo "* You can use 'bin/spc' like normal builds." + echo "*" + echo "* Mounted directories:" + echo "* ./config: $(pwd)/config" + echo "* ./src: $(pwd)/src" + echo "* ./buildroot: $(pwd)/buildroot" + echo "* ./source: $(pwd)/source" + echo "* ./dist: $(pwd)/dist" + echo "* ./downloads: $(pwd)/downloads" + echo "* ./pkgroot: $(pwd)/pkgroot" + echo "*" + $DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-v2 else $DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-v2 bin/spc $@ diff --git a/bin/spc-gnu-docker b/bin/spc-gnu-docker index fa84e58c..6bc7bd4a 100755 --- a/bin/spc-gnu-docker +++ b/bin/spc-gnu-docker @@ -12,7 +12,7 @@ DOCKER_EXECUTABLE="docker" # shellcheck disable=SC2046 if [ $(id -u) -ne 0 ]; then if ! docker info > /dev/null 2>&1; then - if [ "$SPC_USE_SUDO" != "yes" ]; then + if [ "$SPC_USE_SUDO" != "yes" ] && [ "$SPC_DOCKER_DEBUG" != "yes" ]; then echo "Docker command requires sudo" # shellcheck disable=SC2039 echo -n 'To use sudo to run docker, run "export SPC_USE_SUDO=yes" and run command again' @@ -86,7 +86,7 @@ COPY ./composer.* /app/ ADD ./bin/setup-runtime /app/bin/setup-runtime ADD ./bin/spc /app/bin/spc RUN /app/bin/setup-runtime -RUN /app/bin/php /app/bin/composer install --no-dev --classmap-authoritative +RUN /app/bin/php /app/bin/composer install --no-dev ENV PATH="/app/bin:/cmake/bin:$PATH" ENV SPC_LIBC=glibc @@ -145,4 +145,23 @@ echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' # shellcheck disable=SC2086 # shellcheck disable=SC2090 -$DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH bin/spc $@ + +if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then + echo "* Debug mode enabled, run docker in interactive mode." + echo "* You can use 'exit' to exit the docker container." + echo "* You can use 'bin/spc' like normal builds." + echo "*" + echo "* Mounted directories:" + echo "* ./config: $(pwd)/config" + echo "* ./src: $(pwd)/src" + echo "* ./buildroot: $(pwd)/buildroot" + echo "* ./source: $(pwd)/source" + echo "* ./dist: $(pwd)/dist" + echo "* ./downloads: $(pwd)/downloads" + echo "* ./pkgroot: $(pwd)/pkgroot" + echo "*" + + $DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH +else + $DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH bin/spc $@ +fi diff --git a/config/env.ini b/config/env.ini index 652b0fe6..131fa07e 100644 --- a/config/env.ini +++ b/config/env.ini @@ -28,7 +28,6 @@ ; PATH: static-php-cli will add `$BUILD_BIN_PATH` to PATH. ; PKG_CONFIG: static-php-cli will set `$BUILD_BIN_PATH/pkg-config` to PKG_CONFIG. ; PKG_CONFIG_PATH: static-php-cli will set `$BUILD_LIB_PATH/pkgconfig` to PKG_CONFIG_PATH. -; SPC_PHP_DEFAULT_OPTIMIZE_CFLAGS: the default optimization CFLAGS for compiling php. (if --no-strip option is set: `-g -O0`, else: `-g -Os`) ; ; * These vars are only be defined in LinuxBuilder and cannot be changed anywhere: ; SPC_LINUX_DEFAULT_CC: the default compiler for linux. (For alpine linux: `gcc`, default: `$GNU_ARCH-linux-musl-gcc`) @@ -98,9 +97,9 @@ SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}" ; LIBS for configuring php SPC_CMD_VAR_PHP_CONFIGURE_LIBS="-ldl -lpthread -lm" ; EXTRA_CFLAGS for `make` php -SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="${SPC_PHP_DEFAULT_OPTIMIZE_CFLAGS} -fno-ident -fPIE -fPIC" +SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -Os -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -fno-ident -fPIE -fPIC" ; EXTRA_LIBS for `make` php -SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="" +SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm" ; EXTRA_LDFLAGS_PROGRAM for `make` php SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie" @@ -132,7 +131,7 @@ SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS="-I${BUILD_INCLUDE_PATH}" ; LDFLAGS for configuring php SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}" ; EXTRA_CFLAGS for `make` php -SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="${SPC_PHP_DEFAULT_OPTIMIZE_CFLAGS}" +SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -Os -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64" ; EXTRA_LIBS for `make` php SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-lresolv" ; embed type for php, static (libphp.a) or shared (libphp.dylib) diff --git a/config/ext.json b/config/ext.json index 691be764..3d99350c 100644 --- a/config/ext.json +++ b/config/ext.json @@ -771,6 +771,10 @@ "Windows": "no", "BSD": "wip" }, + "target": [ + "static", + "shared" + ], "notes": true, "type": "external", "source": "swoole", @@ -919,15 +923,16 @@ }, "xdebug": { "type": "external", + "source": "xdebug", + "target": [ + "shared" + ], "support": { "Windows": "wip", "BSD": "no", - "Darwin": "no", - "Linux": "wip" + "Darwin": "partial", + "Linux": "partial" }, - "lib-depends": [ - "xdebug" - ], "notes": true }, "xhprof": { @@ -1042,6 +1047,10 @@ "support": { "BSD": "wip" }, + "target": [ + "static", + "shared" + ], "type": "builtin", "arg-type": "with-prefix", "arg-type-windows": "enable", diff --git a/config/lib.json b/config/lib.json index a39409ed..cc9d4eb0 100644 --- a/config/lib.json +++ b/config/lib.json @@ -281,8 +281,7 @@ "headers-unix": [ "ares.h", "ares_dns.h", - "ares_nameser.h", - "ares_rules.h" + "ares_nameser.h" ] }, "libde265": { @@ -751,12 +750,6 @@ "libiconv" ] }, - "xdebug": { - "source": "xdebug", - "static-libs-unix": [ - "xdebug.so" - ] - }, "xz": { "source": "xz", "static-libs-unix": [ diff --git a/config/source.json b/config/source.json index 553f0527..4a9c3cef 100644 --- a/config/source.json +++ b/config/source.json @@ -914,11 +914,9 @@ } }, "xdebug": { - "type": "ghrel", - "repo": "xdebug/xdebug", - "match": "Source code", - "prefer-stable": true, - "provide-pre-built": false, + "type": "url", + "url": "https://pecl.php.net/get/xdebug", + "filename": "xdebug.tgz", "license": { "type": "file", "path": "LICENSE" diff --git a/src/SPC/builder/BuilderBase.php b/src/SPC/builder/BuilderBase.php index 0456af8b..429d950a 100644 --- a/src/SPC/builder/BuilderBase.php +++ b/src/SPC/builder/BuilderBase.php @@ -122,9 +122,12 @@ abstract class BuilderBase * * @return Extension[] */ - public function getExts(): array + public function getExts(bool $including_shared = true): array { - return $this->exts; + if ($including_shared) { + return $this->exts; + } + return array_filter($this->exts, fn ($ext) => !$ext->isBuildShared()); } /** @@ -136,7 +139,7 @@ abstract class BuilderBase public function hasCpp(): bool { // judge cpp-extension - $exts = array_keys($this->getExts()); + $exts = array_keys($this->getExts(false)); foreach ($exts as $ext) { if (Config::getExt($ext, 'cpp-extension', false) === true) { return true; @@ -170,9 +173,22 @@ abstract class BuilderBase * @throws \Throwable|WrongUsageException * @internal */ - public function proveExts(array $extensions, bool $skip_check_deps = false): void + public function proveExts(array $static_extensions, array $shared_extensions = [], bool $skip_check_deps = false): void { CustomExt::loadCustomExt(); + // judge ext + foreach ($static_extensions as $ext) { + // if extension does not support static build, throw exception + if (!in_array('static', Config::getExtTarget($ext))) { + throw new WrongUsageException('Extension [' . $ext . '] does not support static build!'); + } + } + foreach ($shared_extensions as $ext) { + // if extension does not support shared build, throw exception + if (!in_array('shared', Config::getExtTarget($ext)) && !in_array($ext, $shared_extensions)) { + throw new WrongUsageException('Extension [' . $ext . '] does not support shared build!'); + } + } $this->emitPatchPoint('before-php-extract'); SourceManager::initSource(sources: ['php-src']); $this->emitPatchPoint('after-php-extract'); @@ -182,11 +198,18 @@ abstract class BuilderBase $this->emitPatchPoint('after-micro-extract'); } $this->emitPatchPoint('before-exts-extract'); - SourceManager::initSource(exts: $extensions); + SourceManager::initSource(exts: [...$static_extensions, ...$shared_extensions]); $this->emitPatchPoint('after-exts-extract'); - foreach ($extensions as $extension) { + foreach ([...$static_extensions, ...$shared_extensions] as $extension) { $class = CustomExt::getExtClass($extension); + /** @var Extension $ext */ $ext = new $class($extension, $this); + if (in_array($extension, $static_extensions)) { + $ext->setBuildStatic(); + } + if (in_array($extension, $shared_extensions)) { + $ext->setBuildShared(); + } $this->addExt($ext); } @@ -194,10 +217,10 @@ abstract class BuilderBase return; } - foreach ($this->exts as $ext) { + foreach ($this->getExts() as $ext) { $ext->checkDependency(); } - $this->ext_list = $extensions; + $this->ext_list = [...$static_extensions, ...$shared_extensions]; } /** @@ -207,6 +230,17 @@ abstract class BuilderBase */ abstract public function buildPHP(int $build_target = BUILD_TARGET_NONE); + public function buildSharedExts(): void + { + foreach ($this->getExts() as $ext) { + if (!$ext->isBuildShared()) { + continue; + } + logger()->info('Building extension [' . $ext->getName() . '] as shared extension (' . $ext->getName() . '.so)'); + $ext->buildShared(); + } + } + /** * Generate extension enable arguments for configure. * e.g. --enable-mbstring @@ -214,10 +248,10 @@ abstract class BuilderBase * @throws FileSystemException * @throws WrongUsageException */ - public function makeExtensionArgs(): string + public function makeStaticExtensionArgs(): string { $ret = []; - foreach ($this->exts as $ext) { + foreach ($this->getExts(false) as $ext) { logger()->info($ext->getName() . ' is using ' . $ext->getConfigureArg()); $ret[] = trim($ext->getConfigureArg()); } @@ -396,7 +430,7 @@ abstract class BuilderBase foreach ($this->libs as $lib) { $lib->validate(); } - foreach ($this->exts as $ext) { + foreach ($this->getExts() as $ext) { $ext->validate(); } } @@ -441,7 +475,7 @@ abstract class BuilderBase { $php = "getExts() as $ext) { + foreach ($this->getExts(false) as $ext) { $ext_name = $ext->getDistName(); if (!empty($ext_name)) { $php .= "echo 'Running micro with {$ext_name} test' . PHP_EOL;\n"; diff --git a/src/SPC/builder/Extension.php b/src/SPC/builder/Extension.php index a7886cd8..6ada640a 100644 --- a/src/SPC/builder/Extension.php +++ b/src/SPC/builder/Extension.php @@ -14,6 +14,12 @@ class Extension { protected array $dependencies = []; + protected bool $build_shared = false; + + protected bool $build_static = false; + + protected string $source_dir; + /** * @throws FileSystemException * @throws RuntimeException @@ -30,6 +36,18 @@ class Extension if (PHP_OS_FAMILY === 'Windows' && $unix_only) { throw new RuntimeException("{$ext_type} extension {$name} is not supported on Windows platform"); } + // set source_dir for builtin + if ($ext_type === 'builtin') { + $this->source_dir = SOURCE_PATH . '/php-src/ext/' . $this->name; + } else { + $source = Config::getExt($this->name, 'source'); + if ($source === null) { + throw new RuntimeException("{$ext_type} extension {$name} source not found"); + } + $source_path = Config::getSource($source)['path'] ?? null; + $source_path = $source_path === null ? SOURCE_PATH . '/' . $source : SOURCE_PATH . '/' . $source_path; + $this->source_dir = $source_path; + } } /** @@ -132,7 +150,7 @@ class Extension // Windows is not supported yet } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { return ''; } @@ -167,6 +185,17 @@ class Extension return false; } + /** + * Run shared extension check when cli is enabled + */ + public function runSharedExtensionCheckUnix(): void + { + [$ret] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n -d "extension=' . BUILD_LIB_PATH . '/' . $this->getName() . '.so" --ri ' . $this->getName()); + if ($ret !== 0) { + throw new RuntimeException($this->getName() . '.so failed to load'); + } + } + /** * @throws RuntimeException */ @@ -231,6 +260,43 @@ class Extension // do nothing, just throw wrong usage exception if not valid } + /** + * Build shared extension + * + * @throws WrongUsageException + * @throws RuntimeException + */ + public function buildShared(): void + { + match (PHP_OS_FAMILY) { + 'Darwin', 'Linux' => $this->buildUnixShared(), + default => throw new WrongUsageException(PHP_OS_FAMILY . ' build shared extensions is not supported yet'), + }; + } + + /** + * Build shared extension for Unix + * + * @throws RuntimeException + */ + public function buildUnixShared(): void + { + // prepare configure args + shell()->cd($this->source_dir) + ->setEnv(['CFLAGS' => $this->builder->arch_c_flags ?? '']) + ->execWithEnv(BUILD_BIN_PATH . '/phpize') + ->execWithEnv('./configure ' . $this->getUnixConfigureArg(true) . ' --with-php-config=' . BUILD_BIN_PATH . '/php-config --enable-shared --disable-static') + ->execWithEnv('make clean') + ->execWithEnv('make -j' . $this->builder->concurrency); + + // copy shared library + copy($this->source_dir . '/modules/' . $this->getDistName() . '.so', BUILD_LIB_PATH . '/' . $this->getDistName() . '.so'); + // check shared extension with php-cli + if (file_exists(BUILD_BIN_PATH . '/php')) { + $this->runSharedExtensionCheckUnix(); + } + } + /** * Get current extension version * @@ -241,6 +307,32 @@ class Extension return null; } + public function setBuildStatic(): void + { + if (!in_array('static', Config::getExtTarget($this->name))) { + throw new WrongUsageException("Extension [{$this->name}] does not support static build!"); + } + $this->build_static = true; + } + + public function setBuildShared(): void + { + if (!in_array('shared', Config::getExtTarget($this->name))) { + throw new WrongUsageException("Extension [{$this->name}] does not support shared build!"); + } + $this->build_shared = true; + } + + public function isBuildShared(): bool + { + return $this->build_shared; + } + + public function isBuildStatic(): bool + { + return $this->build_static; + } + /** * @throws RuntimeException */ diff --git a/src/SPC/builder/extension/amqp.php b/src/SPC/builder/extension/amqp.php index d795f47d..8fbfea24 100644 --- a/src/SPC/builder/extension/amqp.php +++ b/src/SPC/builder/extension/amqp.php @@ -23,7 +23,7 @@ class amqp extends Extension return false; } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { return '--with-amqp --with-librabbitmq-dir=' . BUILD_ROOT_PATH; } diff --git a/src/SPC/builder/extension/dba.php b/src/SPC/builder/extension/dba.php index 011e4746..bd7388f3 100644 --- a/src/SPC/builder/extension/dba.php +++ b/src/SPC/builder/extension/dba.php @@ -10,7 +10,7 @@ use SPC\util\CustomExt; #[CustomExt('dba')] class dba extends Extension { - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { $qdbm = $this->builder->getLib('qdbm') ? (' --with-qdbm=' . BUILD_ROOT_PATH) : ''; return '--enable-dba' . $qdbm; diff --git a/src/SPC/builder/extension/enchant.php b/src/SPC/builder/extension/enchant.php index 26578519..c3046409 100644 --- a/src/SPC/builder/extension/enchant.php +++ b/src/SPC/builder/extension/enchant.php @@ -10,7 +10,7 @@ use SPC\util\CustomExt; #[CustomExt('enchant')] class enchant extends Extension { - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { $glibs = [ '/Users/jerry/project/git-project/static-php-cli/buildroot/lib/libgio-2.0.a', diff --git a/src/SPC/builder/extension/event.php b/src/SPC/builder/extension/event.php index a1cc7bcc..95d9aba6 100644 --- a/src/SPC/builder/extension/event.php +++ b/src/SPC/builder/extension/event.php @@ -13,7 +13,7 @@ use SPC\util\CustomExt; #[CustomExt('event')] class event extends Extension { - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { $arg = '--with-event-core --with-event-extra --with-event-libevent-dir=' . BUILD_ROOT_PATH; if ($this->builder->getLib('openssl')) { diff --git a/src/SPC/builder/extension/ffi.php b/src/SPC/builder/extension/ffi.php index 6f294481..51c3efac 100644 --- a/src/SPC/builder/extension/ffi.php +++ b/src/SPC/builder/extension/ffi.php @@ -10,7 +10,7 @@ use SPC\util\CustomExt; #[CustomExt('ffi')] class ffi extends Extension { - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { return '--with-ffi --enable-zend-signals'; } diff --git a/src/SPC/builder/extension/gd.php b/src/SPC/builder/extension/gd.php index 707a898f..f872733b 100644 --- a/src/SPC/builder/extension/gd.php +++ b/src/SPC/builder/extension/gd.php @@ -10,7 +10,7 @@ use SPC\util\CustomExt; #[CustomExt('gd')] class gd extends Extension { - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { $arg = '--enable-gd'; $arg .= $this->builder->getLib('freetype') ? ' --with-freetype' : ''; diff --git a/src/SPC/builder/extension/glfw.php b/src/SPC/builder/extension/glfw.php index c9d0c4f7..444b5d93 100644 --- a/src/SPC/builder/extension/glfw.php +++ b/src/SPC/builder/extension/glfw.php @@ -30,7 +30,7 @@ class glfw extends Extension return true; } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { return '--enable-glfw --with-glfw-dir=' . BUILD_ROOT_PATH; } diff --git a/src/SPC/builder/extension/grpc.php b/src/SPC/builder/extension/grpc.php index a63bad9d..852f2933 100644 --- a/src/SPC/builder/extension/grpc.php +++ b/src/SPC/builder/extension/grpc.php @@ -44,7 +44,7 @@ class grpc extends Extension return true; } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { return '--enable-grpc=' . BUILD_ROOT_PATH . '/grpc GRPC_LIB_SUBDIR=' . BUILD_LIB_PATH; } diff --git a/src/SPC/builder/extension/imagick.php b/src/SPC/builder/extension/imagick.php index 7becb143..c73a5f29 100644 --- a/src/SPC/builder/extension/imagick.php +++ b/src/SPC/builder/extension/imagick.php @@ -22,7 +22,7 @@ class imagick extends Extension return true; } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { return '--with-imagick=' . BUILD_ROOT_PATH; } diff --git a/src/SPC/builder/extension/imap.php b/src/SPC/builder/extension/imap.php index 3aa2c284..9cc9a87f 100644 --- a/src/SPC/builder/extension/imap.php +++ b/src/SPC/builder/extension/imap.php @@ -33,7 +33,7 @@ class imap extends Extension } } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { $arg = '--with-imap=' . BUILD_ROOT_PATH; if ($this->builder->getLib('openssl') !== null) { diff --git a/src/SPC/builder/extension/memcache.php b/src/SPC/builder/extension/memcache.php index eb3e2969..4625cae2 100644 --- a/src/SPC/builder/extension/memcache.php +++ b/src/SPC/builder/extension/memcache.php @@ -12,7 +12,7 @@ use SPC\util\CustomExt; #[CustomExt('memcache')] class memcache extends Extension { - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { return '--enable-memcache --with-zlib-dir=' . BUILD_ROOT_PATH; } diff --git a/src/SPC/builder/extension/memcached.php b/src/SPC/builder/extension/memcached.php index 1ef679d9..9c433954 100644 --- a/src/SPC/builder/extension/memcached.php +++ b/src/SPC/builder/extension/memcached.php @@ -10,7 +10,7 @@ use SPC\util\CustomExt; #[CustomExt('memcached')] class memcached extends Extension { - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { $rootdir = BUILD_ROOT_PATH; $zlib_dir = $this->builder->getPHPVersionID() >= 80400 ? '' : "--with-zlib-dir={$rootdir}"; diff --git a/src/SPC/builder/extension/mongodb.php b/src/SPC/builder/extension/mongodb.php index 2096de05..7ba1ea33 100644 --- a/src/SPC/builder/extension/mongodb.php +++ b/src/SPC/builder/extension/mongodb.php @@ -10,7 +10,7 @@ use SPC\util\CustomExt; #[CustomExt('mongodb')] class mongodb extends Extension { - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { $arg = ' --enable-mongodb '; $arg .= ' --with-mongodb-system-libs=no --with-mongodb-client-side-encryption=no '; diff --git a/src/SPC/builder/extension/odbc.php b/src/SPC/builder/extension/odbc.php index 6234f4e8..ac5c3e8f 100644 --- a/src/SPC/builder/extension/odbc.php +++ b/src/SPC/builder/extension/odbc.php @@ -10,7 +10,7 @@ use SPC\util\CustomExt; #[CustomExt('odbc')] class odbc extends Extension { - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { return '--with-unixODBC=' . BUILD_ROOT_PATH; } diff --git a/src/SPC/builder/extension/opcache.php b/src/SPC/builder/extension/opcache.php index 5276261a..5d9dda0a 100644 --- a/src/SPC/builder/extension/opcache.php +++ b/src/SPC/builder/extension/opcache.php @@ -42,7 +42,7 @@ class opcache extends Extension return file_put_contents(SOURCE_PATH . '/php-src/.opcache_patched', '1') !== false; } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { return '--enable-opcache'; } diff --git a/src/SPC/builder/extension/openssl.php b/src/SPC/builder/extension/openssl.php index 2576b0b2..add1aede 100644 --- a/src/SPC/builder/extension/openssl.php +++ b/src/SPC/builder/extension/openssl.php @@ -23,7 +23,7 @@ class openssl extends Extension return false; } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { $openssl_dir = $this->builder->getPHPVersionID() >= 80400 ? '' : ' --with-openssl-dir=' . BUILD_ROOT_PATH; return '--with-openssl=' . BUILD_ROOT_PATH . $openssl_dir; diff --git a/src/SPC/builder/extension/pdo_odbc.php b/src/SPC/builder/extension/pdo_odbc.php index 4ab45904..e436f381 100644 --- a/src/SPC/builder/extension/pdo_odbc.php +++ b/src/SPC/builder/extension/pdo_odbc.php @@ -17,7 +17,7 @@ class pdo_odbc extends Extension return true; } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { return '--with-pdo-odbc=unixODBC,' . BUILD_ROOT_PATH; } diff --git a/src/SPC/builder/extension/pgsql.php b/src/SPC/builder/extension/pgsql.php index 74085b21..1c63f163 100644 --- a/src/SPC/builder/extension/pgsql.php +++ b/src/SPC/builder/extension/pgsql.php @@ -33,7 +33,7 @@ class pgsql extends Extension * @throws WrongUsageException * @throws RuntimeException */ - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { if ($this->builder->getPHPVersionID() >= 80400) { return '--with-pgsql PGSQL_CFLAGS=-I' . BUILD_INCLUDE_PATH . ' PGSQL_LIBS="-L' . BUILD_LIB_PATH . ' -lpq -lpgport -lpgcommon"'; diff --git a/src/SPC/builder/extension/redis.php b/src/SPC/builder/extension/redis.php index bffc768e..0b60075c 100644 --- a/src/SPC/builder/extension/redis.php +++ b/src/SPC/builder/extension/redis.php @@ -10,7 +10,7 @@ use SPC\util\CustomExt; #[CustomExt('redis')] class redis extends Extension { - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { $arg = '--enable-redis'; $arg .= $this->builder->getExt('session') ? ' --enable-redis-session' : ' --disable-redis-session'; diff --git a/src/SPC/builder/extension/snappy.php b/src/SPC/builder/extension/snappy.php index 2dc2a33a..896a9be2 100644 --- a/src/SPC/builder/extension/snappy.php +++ b/src/SPC/builder/extension/snappy.php @@ -26,7 +26,7 @@ class snappy extends Extension return true; } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { return '--enable-snappy --with-snappy-includedir="' . BUILD_ROOT_PATH . '"'; } diff --git a/src/SPC/builder/extension/spx.php b/src/SPC/builder/extension/spx.php index 6c2a6a19..dc341e39 100644 --- a/src/SPC/builder/extension/spx.php +++ b/src/SPC/builder/extension/spx.php @@ -21,7 +21,7 @@ class spx extends Extension } } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { $arg = '--enable-spx'; if ($this->builder->getExt('zlib') === null) { diff --git a/src/SPC/builder/extension/swoole.php b/src/SPC/builder/extension/swoole.php index 78aaf339..6ce006b9 100644 --- a/src/SPC/builder/extension/swoole.php +++ b/src/SPC/builder/extension/swoole.php @@ -35,7 +35,7 @@ class swoole extends Extension return null; } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { // enable swoole $arg = '--enable-swoole'; @@ -49,7 +49,9 @@ class swoole extends Extension // additional feature: c-ares, brotli, nghttp2 (can be disabled, but we enable it by default in config to support full network feature) $arg .= $this->builder->getLib('libcares') ? ' --enable-cares' : ''; - $arg .= $this->builder->getLib('brotli') ? (' --with-brotli-dir=' . BUILD_ROOT_PATH) : ''; + if (!$shared) { + $arg .= $this->builder->getLib('brotli') ? (' --enable-brotli --with-brotli-dir=' . BUILD_ROOT_PATH) : ''; + } $arg .= $this->builder->getLib('nghttp2') ? (' --with-nghttp2-dir=' . BUILD_ROOT_PATH) : ''; // additional feature: swoole-pgsql, it should depend on lib [postgresql], but it will lack of CFLAGS etc. diff --git a/src/SPC/builder/extension/swoole_hook_mysql.php b/src/SPC/builder/extension/swoole_hook_mysql.php index 6d1d6828..e9684872 100644 --- a/src/SPC/builder/extension/swoole_hook_mysql.php +++ b/src/SPC/builder/extension/swoole_hook_mysql.php @@ -16,7 +16,7 @@ class swoole_hook_mysql extends Extension return 'swoole'; } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { // pdo_mysql doesn't need to be disabled // enable swoole-hook-mysql will enable mysqli, pdo, pdo_mysql, we don't need to add any additional options diff --git a/src/SPC/builder/extension/swoole_hook_pgsql.php b/src/SPC/builder/extension/swoole_hook_pgsql.php index 08e217be..113b8eb6 100644 --- a/src/SPC/builder/extension/swoole_hook_pgsql.php +++ b/src/SPC/builder/extension/swoole_hook_pgsql.php @@ -25,7 +25,7 @@ class swoole_hook_pgsql extends Extension } } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { // enable swoole pgsql hook return '--enable-swoole-pgsql'; diff --git a/src/SPC/builder/extension/swoole_hook_sqlite.php b/src/SPC/builder/extension/swoole_hook_sqlite.php index 5de18351..7948dd29 100644 --- a/src/SPC/builder/extension/swoole_hook_sqlite.php +++ b/src/SPC/builder/extension/swoole_hook_sqlite.php @@ -25,7 +25,7 @@ class swoole_hook_sqlite extends Extension } } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { // enable swoole pgsql hook return '--enable-swoole-sqlite'; diff --git a/src/SPC/builder/extension/xdebug.php b/src/SPC/builder/extension/xdebug.php index c5cd901d..ed592056 100644 --- a/src/SPC/builder/extension/xdebug.php +++ b/src/SPC/builder/extension/xdebug.php @@ -5,7 +5,17 @@ declare(strict_types=1); namespace SPC\builder\extension; use SPC\builder\Extension; -use SPC\util\DynamicExt; +use SPC\exception\RuntimeException; +use SPC\util\CustomExt; -#[DynamicExt] -class xdebug extends Extension {} +#[CustomExt('xdebug')] +class xdebug extends Extension +{ + public function runSharedExtensionCheckUnix(): void + { + [$ret] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n -d "zend_extension=' . BUILD_LIB_PATH . '/xdebug.so" --ri xdebug'); + if ($ret !== 0) { + throw new RuntimeException('xdebug.so failed to load.'); + } + } +} diff --git a/src/SPC/builder/extension/xlswriter.php b/src/SPC/builder/extension/xlswriter.php index 475a6c07..6319ce31 100644 --- a/src/SPC/builder/extension/xlswriter.php +++ b/src/SPC/builder/extension/xlswriter.php @@ -10,7 +10,7 @@ use SPC\util\CustomExt; #[CustomExt('xlswriter')] class xlswriter extends Extension { - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { $arg = '--with-xlswriter --enable-reader'; if ($this->builder->getLib('openssl')) { diff --git a/src/SPC/builder/extension/xml.php b/src/SPC/builder/extension/xml.php index 6242be6b..fc8bfc4d 100644 --- a/src/SPC/builder/extension/xml.php +++ b/src/SPC/builder/extension/xml.php @@ -20,7 +20,7 @@ class xml extends Extension /** * @throws RuntimeException */ - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { $arg = match ($this->name) { 'xml' => '--enable-xml', diff --git a/src/SPC/builder/extension/yac.php b/src/SPC/builder/extension/yac.php index a4152e42..bb085133 100644 --- a/src/SPC/builder/extension/yac.php +++ b/src/SPC/builder/extension/yac.php @@ -19,7 +19,7 @@ class yac extends Extension return true; } - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { return '--enable-yac --enable-igbinary --enable-json'; } diff --git a/src/SPC/builder/extension/zlib.php b/src/SPC/builder/extension/zlib.php index a9932999..86c45dbe 100644 --- a/src/SPC/builder/extension/zlib.php +++ b/src/SPC/builder/extension/zlib.php @@ -10,7 +10,7 @@ use SPC\util\CustomExt; #[CustomExt('zlib')] class zlib extends Extension { - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { $zlib_dir = $this->builder->getPHPVersionID() >= 80400 ? '' : ' --with-zlib-dir=' . BUILD_ROOT_PATH; return '--with-zlib' . $zlib_dir; diff --git a/src/SPC/builder/extension/zstd.php b/src/SPC/builder/extension/zstd.php index 13311979..3a6df602 100644 --- a/src/SPC/builder/extension/zstd.php +++ b/src/SPC/builder/extension/zstd.php @@ -10,7 +10,7 @@ use SPC\util\CustomExt; #[CustomExt('zstd')] class zstd extends Extension { - public function getUnixConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { return '--enable-zstd --with-libzstd="' . BUILD_ROOT_PATH . '"'; } diff --git a/src/SPC/builder/freebsd/BSDBuilder.php b/src/SPC/builder/freebsd/BSDBuilder.php index c1d41118..b2e246b3 100644 --- a/src/SPC/builder/freebsd/BSDBuilder.php +++ b/src/SPC/builder/freebsd/BSDBuilder.php @@ -118,7 +118,7 @@ class BSDBuilder extends UnixBuilderBase $config_file_scan_dir . $json_74 . $zts . - $this->makeExtensionArgs() + $this->makeStaticExtensionArgs() ); $this->emitPatchPoint('before-php-make'); diff --git a/src/SPC/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php index cd48728f..e38714f1 100644 --- a/src/SPC/builder/linux/LinuxBuilder.php +++ b/src/SPC/builder/linux/LinuxBuilder.php @@ -182,7 +182,7 @@ class LinuxBuilder extends UnixBuilderBase $json_74 . $zts . $maxExecutionTimers . - $this->makeExtensionArgs() . + $this->makeStaticExtensionArgs() . ' ' . $envs_build_php . ' ' ); @@ -311,15 +311,7 @@ class LinuxBuilder extends UnixBuilderBase shell()->cd(SOURCE_PATH . '/php-src') ->exec('sed -i "s|//lib|/lib|g" Makefile') ->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install"); - FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', "prefix=''", "prefix='" . BUILD_ROOT_PATH . "'"); - FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', 's##', 's#/usr/local#'); - $php_config_str = FileSystem::readFile(BUILD_BIN_PATH . '/php-config'); - str_replace('prefix=""', 'prefix="' . BUILD_ROOT_PATH . '"', $php_config_str); - // move mimalloc to the beginning of libs - $php_config_str = preg_replace('/(libs=")(.*?)\s*(' . preg_quote(BUILD_LIB_PATH, '/') . '\/mimalloc\.o)\s*(.*?)"/', '$1$3 $2 $4"', $php_config_str); - // move lstdc++ to the end of libs - $php_config_str = preg_replace('/(libs=")(.*?)\s*(-lstdc\+\+)\s*(.*?)"/', '$1$2 $4 $3"', $php_config_str); - FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str); + $this->patchPhpScripts(); } private function getMakeExtraVars(): array diff --git a/src/SPC/builder/linux/library/xdebug.php b/src/SPC/builder/linux/library/xdebug.php deleted file mode 100644 index b494bb1d..00000000 --- a/src/SPC/builder/linux/library/xdebug.php +++ /dev/null @@ -1,15 +0,0 @@ -makeExtensionArgs() . ' ' . + $this->makeStaticExtensionArgs() . ' ' . $envs_build_php ); @@ -300,13 +300,7 @@ class MacOSBuilder extends UnixBuilderBase ->exec('rm ' . BUILD_ROOT_PATH . '/lib/libphp.a') ->exec('ar rcs ' . BUILD_ROOT_PATH . '/lib/libphp.a *.o') ->exec('rm -Rf ' . BUILD_ROOT_PATH . '/lib/php-o'); - FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', "prefix=''", "prefix='" . BUILD_ROOT_PATH . "'"); - FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', 's##', 's#/usr/local#'); - $php_config_str = FileSystem::readFile(BUILD_BIN_PATH . '/php-config'); - str_replace('prefix=""', 'prefix="' . BUILD_ROOT_PATH . '"', $php_config_str); - // move mimalloc to the beginning of libs - $php_config_str = preg_replace('/(libs=")(.*?)\s*(' . preg_quote(BUILD_LIB_PATH, '/') . '\/mimalloc\.o)\s*(.*?)"/', '$1$3 $2 $4"', $php_config_str); - FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str); + $this->patchPhpScripts(); } private function getMakeExtraVars(): array diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php index ed07a725..4c8df333 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -146,7 +146,7 @@ abstract class UnixBuilderBase extends BuilderBase throw new RuntimeException("cli failed sanity check: ret[{$ret}]. out[{$raw_output}]"); } - foreach ($this->exts as $ext) { + foreach ($this->getExts(false) as $ext) { logger()->debug('testing ext: ' . $ext->getName()); $ext->runCliCheckUnix(); } @@ -238,4 +238,29 @@ abstract class UnixBuilderBase extends BuilderBase logger()->info('cleaning up'); shell()->cd(SOURCE_PATH . '/php-src')->exec('make clean'); } + + /** + * Patch phpize and php-config if needed + * @throws FileSystemException + */ + protected function patchPhpScripts(): void + { + // patch phpize + if (file_exists(BUILD_BIN_PATH . '/phpize')) { + logger()->debug('Patching phpize prefix'); + FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', "prefix=''", "prefix='" . BUILD_ROOT_PATH . "'"); + FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', 's##', 's#/usr/local#'); + } + // patch php-config + if (file_exists(BUILD_BIN_PATH . '/php-config')) { + logger()->debug('Patching php-config prefix and libs order'); + $php_config_str = FileSystem::readFile(BUILD_BIN_PATH . '/php-config'); + $php_config_str = str_replace('prefix=""', 'prefix="' . BUILD_ROOT_PATH . '"', $php_config_str); + // move mimalloc to the beginning of libs + $php_config_str = preg_replace('/(libs=")(.*?)\s*(' . preg_quote(BUILD_LIB_PATH, '/') . '\/mimalloc\.o)\s*(.*?)"/', '$1$3 $2 $4"', $php_config_str); + // move lstdc++ to the end of libs + $php_config_str = preg_replace('/(libs=")(.*?)\s*(-lstdc\+\+)\s*(.*?)"/', '$1$2 $4 $3"', $php_config_str); + FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str); + } + } } diff --git a/src/SPC/builder/unix/library/libcares.php b/src/SPC/builder/unix/library/libcares.php index 076a10c9..d99cc685 100644 --- a/src/SPC/builder/unix/library/libcares.php +++ b/src/SPC/builder/unix/library/libcares.php @@ -26,9 +26,9 @@ trait libcares { shell()->cd($this->source_dir) ->setEnv(['CFLAGS' => $this->getLibExtraCFlags(), 'LDFLAGS' => $this->getLibExtraLdFlags(), 'LIBS' => $this->getLibExtraLibs()]) - ->execWithEnv('./configure --prefix= --enable-static --disable-shared --disable-tests') + ->execWithEnv('./configure --prefix= --enable-static --disable-shared --disable-tests --with-pic') ->execWithEnv("make -j {$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); + ->execWithEnv('make install DESTDIR=' . BUILD_ROOT_PATH); $this->patchPkgconfPrefix(['libcares.pc'], PKGCONF_PATCH_PREFIX); } diff --git a/src/SPC/builder/unix/library/xdebug.php b/src/SPC/builder/unix/library/xdebug.php deleted file mode 100644 index 1aecb841..00000000 --- a/src/SPC/builder/unix/library/xdebug.php +++ /dev/null @@ -1,26 +0,0 @@ -cd($this->source_dir) - ->exec(BUILD_BIN_PATH . '/phpize') - ->exec('./configure --with-php-config=' . BUILD_BIN_PATH . '/php-config') - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}"); - copy($this->source_dir . '/modules/xdebug.so', BUILD_LIB_PATH . '/xdebug.so'); - copy($this->source_dir . '/modules/xdebug.la', BUILD_LIB_PATH . '/xdebug.la'); - } -} diff --git a/src/SPC/builder/windows/WindowsBuilder.php b/src/SPC/builder/windows/WindowsBuilder.php index fb7eaa0f..7a604ca8 100644 --- a/src/SPC/builder/windows/WindowsBuilder.php +++ b/src/SPC/builder/windows/WindowsBuilder.php @@ -119,7 +119,7 @@ class WindowsBuilder extends BuilderBase ($enableMicro ? ('--enable-micro=yes ' . $micro_logo . $micro_w32) : '--enable-micro=no ') . ($enableEmbed ? '--enable-embed=yes ' : '--enable-embed=no ') . $config_file_scan_dir . - "{$this->makeExtensionArgs()} " . + "{$this->makeStaticExtensionArgs()} " . $zts . '"' ); @@ -286,7 +286,7 @@ class WindowsBuilder extends BuilderBase throw new RuntimeException('cli failed sanity check'); } - foreach ($this->exts as $ext) { + foreach ($this->getExts(false) as $ext) { logger()->debug('testing ext: ' . $ext->getName()); $ext->runCliCheckWindows(); } diff --git a/src/SPC/command/BaseCommand.php b/src/SPC/command/BaseCommand.php index 3688c249..a1610cae 100644 --- a/src/SPC/command/BaseCommand.php +++ b/src/SPC/command/BaseCommand.php @@ -165,7 +165,7 @@ abstract class BaseCommand extends Command return SPC_EXTENSION_ALIAS[$lower]; } return $lower; - }, is_array($ext_list) ? $ext_list : explode(',', $ext_list)); + }, is_array($ext_list) ? $ext_list : array_filter(explode(',', $ext_list))); // filter internals return array_values(array_filter($ls, function ($x) { diff --git a/src/SPC/command/BuildPHPCommand.php b/src/SPC/command/BuildPHPCommand.php index 3a5c17a4..4e690bed 100644 --- a/src/SPC/command/BuildPHPCommand.php +++ b/src/SPC/command/BuildPHPCommand.php @@ -11,7 +11,6 @@ use SPC\store\Config; use SPC\store\FileSystem; use SPC\store\SourcePatcher; use SPC\util\DependencyUtil; -use SPC\util\DynamicExt; use SPC\util\GlobalEnvManager; use SPC\util\LicenseDumper; use Symfony\Component\Console\Attribute\AsCommand; @@ -28,6 +27,7 @@ class BuildPHPCommand extends BuildCommand $this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated'); $this->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', ''); + $this->addOption('build-shared', 'D', InputOption::VALUE_REQUIRED, 'Shared extensions to build, comma separated', ''); $this->addOption('build-micro', null, null, 'Build micro SAPI'); $this->addOption('build-cli', null, null, 'Build cli SAPI'); $this->addOption('build-fpm', null, null, 'Build fpm SAPI (not available on Windows)'); @@ -53,13 +53,27 @@ class BuildPHPCommand extends BuildCommand // transform string to array $libraries = array_map('trim', array_filter(explode(',', $this->getOption('with-libs')))); // transform string to array - $extensions = $this->parseExtensionList($this->getArgument('extensions')); + $shared_extensions = array_map('trim', array_filter(explode(',', $this->getOption('build-shared')))); + // transform string to array + $static_extensions = $this->parseExtensionList($this->getArgument('extensions')); // parse rule with options - $rule = $this->parseRules(); + $rule = $this->parseRules($shared_extensions); + + // check dynamic extension build env + // macOS must use --no-strip option + if (!empty($shared_extensions) && PHP_OS_FAMILY === 'Darwin' && !$this->getOption('no-strip')) { + $this->output->writeln('MacOS does not support dynamic extension loading with stripped binary, please use --no-strip option!'); + return static::FAILURE; + } + // linux must build with glibc + if (!empty($shared_extensions) && PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') !== 'glibc') { + $this->output->writeln('Linux does not support dynamic extension loading with musl-libc full-static build, please build with glibc!'); + return static::FAILURE; + } if ($rule === BUILD_TARGET_NONE) { - $this->output->writeln('Please add at least one build target!'); + $this->output->writeln('Please add at least one build SAPI!'); $this->output->writeln("\t--build-cli\tBuild php-cli SAPI"); $this->output->writeln("\t--build-micro\tBuild phpmicro SAPI"); $this->output->writeln("\t--build-fpm\tBuild php-fpm SAPI"); @@ -108,33 +122,22 @@ class BuildPHPCommand extends BuildCommand $builder = BuilderProvider::makeBuilderByInput($this->input); $include_suggest_ext = $this->getOption('with-suggested-exts'); $include_suggest_lib = $this->getOption('with-suggested-libs'); - [$extensions, $libraries, $not_included] = DependencyUtil::getExtsAndLibs($extensions, $libraries, $include_suggest_ext, $include_suggest_lib); + [$extensions, $libraries, $not_included] = DependencyUtil::getExtsAndLibs(array_merge($static_extensions, $shared_extensions), $libraries, $include_suggest_ext, $include_suggest_lib); $display_libs = array_filter($libraries, fn ($lib) => in_array(Config::getLib($lib, 'type', 'lib'), ['lib', 'package'])); - $dynamic_libs = $dynamic_exts = array_filter($extensions, function (string $ext) { - $classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/builder/extension', 'SPC\builder\extension'); - $extension = array_find($classes, function (string $class) use ($ext) { - $a = explode('\\', $class); - return end($a) === $ext; - }); - if (!$extension) { - return false; - } - $reflector = new \ReflectionClass($extension); - $attributes = $reflector->getAttributes(); - return array_find($attributes, fn ($attr) => $attr->getName() === DynamicExt::class) !== null; - }); - $extensions = array_diff($extensions, $dynamic_exts); - $libraries = array_diff($libraries, $dynamic_libs); + $display_extensions = array_map(fn ($ext) => in_array($ext, $shared_extensions) ? "*{$ext}" : $ext, $extensions); // print info $indent_texts = [ 'Build OS' => PHP_OS_FAMILY . ' (' . php_uname('m') . ')', 'Build SAPI' => $builder->getBuildTypeName($rule), - 'Extensions (' . count($extensions) . ')' => implode(',', $extensions), + 'Extensions (' . count($extensions) . ')' => implode(',', $display_extensions), 'Libraries (' . count($libraries) . ')' => implode(',', $display_libs), 'Strip Binaries' => $builder->getOption('no-strip') ? 'no' : 'yes', 'Enable ZTS' => $builder->getOption('enable-zts') ? 'yes' : 'no', ]; + if (!empty($shared_extensions) || ($rule & BUILD_TARGET_EMBED)) { + $indent_texts['Build Dev'] = 'yes'; + } if (!empty($this->input->getOption('with-config-file-path'))) { $indent_texts['Config File Path'] = $this->input->getOption('with-config-file-path'); } @@ -168,7 +171,7 @@ class BuildPHPCommand extends BuildCommand // compile libraries $builder->proveLibs($libraries); // check extensions - $builder->proveExts($extensions); + $builder->proveExts($static_extensions, $shared_extensions); // validate libs and extensions $builder->validateLibsAndExts(); @@ -199,11 +202,10 @@ class BuildPHPCommand extends BuildCommand // start to build $builder->buildPHP($rule); - if ($rule & BUILD_TARGET_EMBED) { - // build dynamic extensions - $builder->proveLibs($dynamic_libs); - // build or install libraries - $builder->setupLibs(); + // build dynamic extensions if needed + if (!empty($shared_extensions)) { + logger()->info('Building shared extensions ...'); + $builder->buildSharedExts(); } // compile stopwatch :P @@ -234,13 +236,19 @@ class BuildPHPCommand extends BuildCommand $path = FileSystem::convertPath("{$build_root_path}/bin/php-fpm"); logger()->info("Static php-fpm binary path{$fixed}: {$path}"); } + if (!empty($shared_extensions)) { + foreach ($shared_extensions as $ext) { + $path = FileSystem::convertPath("{$build_root_path}/lib/{$ext}.so"); + logger()->info("Shared extension [{$ext}] path{$fixed}: {$path}"); + } + } // export metadata file_put_contents(BUILD_ROOT_PATH . '/build-extensions.json', json_encode($extensions, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); file_put_contents(BUILD_ROOT_PATH . '/build-libraries.json', json_encode($libraries, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); // export licenses $dumper = new LicenseDumper(); - $dumper->addExts($extensions)->addLibs($libraries)->addLibs($dynamic_libs)->addSources(['php-src'])->dump(BUILD_ROOT_PATH . '/license'); + $dumper->addExts($extensions)->addLibs($libraries)->addSources(['php-src'])->dump(BUILD_ROOT_PATH . '/license'); $path = FileSystem::convertPath("{$build_root_path}/license/"); logger()->info("License path{$fixed}: {$path}"); return static::SUCCESS; @@ -262,13 +270,13 @@ class BuildPHPCommand extends BuildCommand /** * Parse build options to rule int. */ - private function parseRules(): int + private function parseRules(array $dynamic_exts = []): int { $rule = BUILD_TARGET_NONE; $rule |= ($this->getOption('build-cli') ? BUILD_TARGET_CLI : BUILD_TARGET_NONE); $rule |= ($this->getOption('build-micro') ? BUILD_TARGET_MICRO : BUILD_TARGET_NONE); $rule |= ($this->getOption('build-fpm') ? BUILD_TARGET_FPM : BUILD_TARGET_NONE); - $rule |= ($this->getOption('build-embed') ? BUILD_TARGET_EMBED : BUILD_TARGET_NONE); + $rule |= ($this->getOption('build-embed') || !empty($dynamic_exts) ? BUILD_TARGET_EMBED : BUILD_TARGET_NONE); $rule |= ($this->getOption('build-all') ? BUILD_TARGET_ALL : BUILD_TARGET_NONE); return $rule; } diff --git a/src/SPC/command/SPCConfigCommand.php b/src/SPC/command/SPCConfigCommand.php index 0377ace9..d3c7a0b2 100644 --- a/src/SPC/command/SPCConfigCommand.php +++ b/src/SPC/command/SPCConfigCommand.php @@ -37,7 +37,7 @@ class SPCConfigCommand extends BuildCommand $include_suggest_ext = $this->getOption('with-suggested-exts'); $include_suggest_lib = $this->getOption('with-suggested-libs'); - $util = new SPCConfigUtil(null, $this->input); + $util = new SPCConfigUtil(); $config = $util->config($extensions, $libraries, $include_suggest_ext, $include_suggest_lib); if ($this->getOption('includes')) { diff --git a/src/SPC/command/dev/ExtVerCommand.php b/src/SPC/command/dev/ExtVerCommand.php index 0f08fa9a..3154a6bb 100644 --- a/src/SPC/command/dev/ExtVerCommand.php +++ b/src/SPC/command/dev/ExtVerCommand.php @@ -6,7 +6,6 @@ namespace SPC\command\dev; use SPC\builder\BuilderProvider; use SPC\command\BaseCommand; -use SPC\store\Config; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -31,8 +30,7 @@ class ExtVerCommand extends BaseCommand // Get lib object $builder = BuilderProvider::makeBuilderByInput($this->input); - $ext_conf = Config::getExt($this->getArgument('extension')); - $builder->proveExts([$this->getArgument('extension')], true); + $builder->proveExts([$this->getArgument('extension')], [], true); // Check whether lib is extracted // if (!is_dir(SOURCE_PATH . '/' . $this->getArgument('library'))) { diff --git a/src/SPC/store/Config.php b/src/SPC/store/Config.php index 07ed2887..29455fa1 100644 --- a/src/SPC/store/Config.php +++ b/src/SPC/store/Config.php @@ -106,6 +106,21 @@ class Config return self::$lib; } + /** + * @throws WrongUsageException + * @throws FileSystemException + */ + public static function getExtTarget(string $name): ?array + { + if (self::$ext === null) { + self::$ext = FileSystem::loadConfigArray('ext'); + } + if (!isset(self::$ext[$name])) { + throw new WrongUsageException('ext [' . $name . '] is not supported yet'); + } + return self::$ext[$name]['target'] ?? ['static']; + } + /** * @throws FileSystemException * @throws WrongUsageException diff --git a/src/SPC/store/Downloader.php b/src/SPC/store/Downloader.php index 5644a3c2..fbaeb7a7 100644 --- a/src/SPC/store/Downloader.php +++ b/src/SPC/store/Downloader.php @@ -127,10 +127,6 @@ class Downloader if (!$match_result) { return $release['assets']; } - if ($source['match'] === 'Source code') { - $url = $release['tarball_url']; - break; - } foreach ($release['assets'] as $asset) { if (preg_match('|' . $source['match'] . '|', $asset['name'])) { $url = $asset['browser_download_url']; @@ -143,9 +139,6 @@ class Downloader throw new DownloaderException("failed to find {$name} release metadata"); } $filename = basename($url); - if ($source['match'] === 'Source code') { - $filename = $name . $filename . '.tar.gz'; - } return [$url, $filename]; } diff --git a/src/SPC/store/SourcePatcher.php b/src/SPC/store/SourcePatcher.php index d3ffc302..2db3498e 100644 --- a/src/SPC/store/SourcePatcher.php +++ b/src/SPC/store/SourcePatcher.php @@ -43,7 +43,7 @@ class SourcePatcher */ public static function patchBeforeBuildconf(BuilderBase $builder): void { - foreach ($builder->getExts() as $ext) { + foreach ($builder->getExts(false) as $ext) { if ($ext->patchBeforeBuildconf() === true) { logger()->info('Extension [' . $ext->getName() . '] patched before buildconf'); } @@ -86,7 +86,7 @@ class SourcePatcher */ public static function patchBeforeConfigure(BuilderBase $builder): void { - foreach ($builder->getExts() as $ext) { + foreach ($builder->getExts(false) as $ext) { if ($ext->patchBeforeConfigure() === true) { logger()->info('Extension [' . $ext->getName() . '] patched before configure'); } @@ -253,7 +253,7 @@ class SourcePatcher // } // call extension patch before make - foreach ($builder->getExts() as $ext) { + foreach ($builder->getExts(false) as $ext) { if ($ext->patchBeforeMake() === true) { logger()->info('Extension [' . $ext->getName() . '] patched before make'); } diff --git a/src/SPC/util/DynamicExt.php b/src/SPC/util/DynamicExt.php deleted file mode 100644 index d5fd549d..00000000 --- a/src/SPC/util/DynamicExt.php +++ /dev/null @@ -1,8 +0,0 @@ -getOption('no-strip') ? '-g -O0' : '-g -fstack-protector-strong -fpic -fpie -Os -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64')); - } } // Define env vars for linux diff --git a/src/SPC/util/SPCConfigUtil.php b/src/SPC/util/SPCConfigUtil.php index f4a121eb..7adf723b 100644 --- a/src/SPC/util/SPCConfigUtil.php +++ b/src/SPC/util/SPCConfigUtil.php @@ -9,14 +9,15 @@ use SPC\builder\BuilderProvider; use SPC\builder\macos\MacOSBuilder; use SPC\store\Config; use Symfony\Component\Console\Input\ArgvInput; -use Symfony\Component\Console\Input\InputInterface; class SPCConfigUtil { - public function __construct(private ?BuilderBase $builder = null, ?InputInterface $input = null) + private ?BuilderBase $builder = null; + + public function __construct(?BuilderBase $builder = null) { - if ($builder === null) { - $this->builder = BuilderProvider::makeBuilderByInput($input ?? new ArgvInput()); + if ($builder !== null) { + $this->builder = $builder; // BuilderProvider::makeBuilderByInput($input ?? new ArgvInput()); } } @@ -25,8 +26,11 @@ class SPCConfigUtil [$extensions, $libraries] = DependencyUtil::getExtsAndLibs($extensions, $libraries, $include_suggest_ext, $include_suggest_lib); ob_start(); - $this->builder->proveLibs($libraries); - $this->builder->proveExts($extensions); + if ($this->builder === null) { + $this->builder = BuilderProvider::makeBuilderByInput(new ArgvInput()); + $this->builder->proveLibs($libraries); + $this->builder->proveExts($extensions); + } ob_get_clean(); $ldflags = $this->getLdflagsString(); $libs = $this->getLibsString($libraries); @@ -47,9 +51,9 @@ class SPCConfigUtil $libs = BUILD_LIB_PATH . '/mimalloc.o ' . str_replace(BUILD_LIB_PATH . '/mimalloc.o', '', $libs); } return [ - 'cflags' => $cflags, - 'ldflags' => $ldflags, - 'libs' => $libs, + 'cflags' => trim(getenv('CFLAGS') . ' ' . $cflags), + 'ldflags' => trim(getenv('LDFLAGS') . ' ' . $ldflags), + 'libs' => trim(getenv('LIBS') . ' ' . $libs), ]; } diff --git a/tests/SPC/builder/BuilderTest.php b/tests/SPC/builder/BuilderTest.php index 5796f5df..17f1e799 100644 --- a/tests/SPC/builder/BuilderTest.php +++ b/tests/SPC/builder/BuilderTest.php @@ -72,7 +72,7 @@ class BuilderTest extends TestCase public function testMakeExtensionArgs() { - $this->assertStringContainsString('--enable-mbstring', $this->builder->makeExtensionArgs()); + $this->assertStringContainsString('--enable-mbstring', $this->builder->makeStaticExtensionArgs()); } public function testIsLibsOnly()