diff --git a/.github/workflows/ext-matrix-tests.yml b/.github/workflows/ext-matrix-tests.yml index 1c04f5c9..e38ed2ed 100644 --- a/.github/workflows/ext-matrix-tests.yml +++ b/.github/workflows/ext-matrix-tests.yml @@ -136,11 +136,4 @@ jobs: - name: "Build library: ${{ matrix.library }}" run: | SPC_USE_SUDO=yes ./bin/spc doctor --auto-fix - if [ "${{ env.OS }}" = "linux-x86_64" ]; then - ./bin/spc install-pkg upx - UPX=--with-upx-pack - elif [ "${{ env.OS }}" = "linux-aarch64" ]; then - ./bin/spc install-pkg upx - UPX=--with-upx-pack - fi - ./bin/spc build --build-cli --build-micro --build-fpm ${{ matrix.extension }} --debug $UPX --with-suggested-libs --with-suggested-exts + ./bin/spc build --build-cli --build-micro --build-fpm ${{ matrix.extension }} --debug --with-suggested-libs --with-suggested-exts diff --git a/config/env.ini b/config/env.ini index afbce73c..375ee13c 100644 --- a/config/env.ini +++ b/config/env.ini @@ -89,7 +89,7 @@ LD=${SPC_LINUX_DEFAULT_LD} SPC_DEFAULT_C_FLAGS="-fPIC -Os" SPC_DEFAULT_CXX_FLAGS="-fPIC -Os" ; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated) -SPC_EXTRA_LIBS= +SPC_EXTRA_LIBS="" ; upx executable path UPX_EXEC=${PKG_ROOT_PATH}/bin/upx ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches @@ -119,7 +119,7 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE ; EXTRA_LIBS for `make` php SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm" ; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so -SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS="" +SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS="-L${BUILD_LIB_PATH}" ; EXTRA_LDFLAGS_PROGRAM for `make` php SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie" @@ -134,7 +134,7 @@ CXX=clang++ SPC_DEFAULT_C_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" ; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated) -SPC_EXTRA_LIBS= +SPC_EXTRA_LIBS="" ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches SPC_MICRO_PATCHES=cli_checks,macos_iconv diff --git a/config/lib.json b/config/lib.json index 92b625be..5d356920 100644 --- a/config/lib.json +++ b/config/lib.json @@ -35,10 +35,10 @@ }, "brotli": { "source": "brotli", - "static-libs-unix": [ - "libbrotlidec.a", - "libbrotlienc.a", - "libbrotlicommon.a" + "pkg-configs": [ + "libbrotlicommon", + "libbrotlidec", + "libbrotlienc" ], "static-libs-windows": [ "brotlicommon.lib", @@ -89,7 +89,8 @@ "nghttp3", "ngtcp2", "zstd", - "libcares" + "libcares", + "ldap" ], "lib-suggests-windows": [ "brotli", @@ -201,11 +202,10 @@ "icu": { "source": "icu", "cpp-library": true, - "static-libs-unix": [ - "libicui18n.a", - "libicuio.a", - "libicuuc.a", - "libicudata.a" + "pkg-configs": [ + "icu-uc", + "icu-i18n", + "icu-io" ] }, "icu-static-win": { @@ -222,10 +222,10 @@ }, "imagemagick": { "source": "imagemagick", - "static-libs-unix": [ - "libMagick++-7.Q16HDRI.a", - "libMagickWand-7.Q16HDRI.a", - "libMagickCore-7.Q16HDRI.a" + "pkg-configs": [ + "Magick++-7.Q16HDRI", + "MagickCore-7.Q16HDRI", + "MagickWand-7.Q16HDRI" ], "lib-depends": [ "zlib", @@ -268,9 +268,9 @@ }, "ldap": { "source": "ldap", - "static-libs-unix": [ - "libldap.a", - "liblber.a" + "pkg-configs": [ + "ldap", + "lber" ], "lib-depends": [ "openssl", @@ -423,11 +423,11 @@ }, "libjxl": { "source": "libjxl", - "static-libs-unix": [ - "libjxl.a", - "libjxl_cms.a", - "libjxl_threads.a", - "libhwy.a" + "pkg-configs": [ + "libjxl", + "libjxl_cms", + "libjxl_threads", + "libhwy" ], "lib-depends": [ "brotli", @@ -557,12 +557,12 @@ }, "libwebp": { "source": "libwebp", - "static-libs-unix": [ - "libwebp.a", - "libwebpdecoder.a", - "libwebpdemux.a", - "libwebpmux.a", - "libsharpyuv.a" + "pkg-configs": [ + "libwebp", + "libwebpdecoder", + "libwebpdemux", + "libwebpmux", + "libsharpyuv" ], "static-libs-windows": [ "libwebp.lib", @@ -738,8 +738,8 @@ "openssl": { "source": "openssl", "static-libs-unix": [ - "libssl.a", - "libcrypto.a" + "libcrypto.a", + "libssl.a" ], "static-libs-windows": [ "libssl.lib", @@ -754,10 +754,8 @@ }, "postgresql": { "source": "postgresql", - "static-libs-unix": [ - "libpq.a", - "libpgport.a", - "libpgcommon.a" + "pkg-configs": [ + "libpq" ], "lib-depends": [ "libiconv", diff --git a/src/SPC/builder/BuilderBase.php b/src/SPC/builder/BuilderBase.php index 184d88dc..3c3acdda 100644 --- a/src/SPC/builder/BuilderBase.php +++ b/src/SPC/builder/BuilderBase.php @@ -66,13 +66,17 @@ abstract class BuilderBase // build all libs foreach ($this->libs as $lib) { $starttime = microtime(true); - match ($lib->setup($this->getOption('rebuild', false))) { + $status = $lib->setup($this->getOption('rebuild', false)); + match ($status) { LIB_STATUS_OK => logger()->info('lib [' . $lib::NAME . '] setup success, took ' . round(microtime(true) - $starttime, 2) . ' s'), LIB_STATUS_ALREADY => logger()->notice('lib [' . $lib::NAME . '] already built'), LIB_STATUS_BUILD_FAILED => logger()->error('lib [' . $lib::NAME . '] build failed'), LIB_STATUS_INSTALL_FAILED => logger()->error('lib [' . $lib::NAME . '] install failed'), default => logger()->warning('lib [' . $lib::NAME . '] build status unknown'), }; + if (in_array($status, [LIB_STATUS_BUILD_FAILED, LIB_STATUS_INSTALL_FAILED])) { + throw new RuntimeException('Library [' . $lib::NAME . '] setup failed.'); + } } } diff --git a/src/SPC/builder/LibraryBase.php b/src/SPC/builder/LibraryBase.php index b97b5929..6606e713 100644 --- a/src/SPC/builder/LibraryBase.php +++ b/src/SPC/builder/LibraryBase.php @@ -182,22 +182,8 @@ abstract class LibraryBase return LIB_STATUS_INSTALL_FAILED; } } - foreach ($this->getStaticLibs() as $name) { - if (!file_exists(BUILD_LIB_PATH . "/{$name}")) { - $this->tryInstall($lock, true); - return LIB_STATUS_OK; - } - } - foreach ($this->getHeaders() as $name) { - if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) { - $this->tryInstall($lock, true); - return LIB_STATUS_OK; - } - } - // pkg-config is treated specially. If it is pkg-config, check if the pkg-config binary exists - if (static::NAME === 'pkg-config' && !file_exists(BUILD_ROOT_PATH . '/bin/pkg-config')) { - $this->tryInstall($lock, true); - return LIB_STATUS_OK; + if (!$this->isLibraryInstalled()) { + return $this->tryInstall($lock, true); } return LIB_STATUS_ALREADY; } @@ -240,28 +226,8 @@ abstract class LibraryBase return LIB_STATUS_OK; } - // check if these libraries exist, if not, invoke compilation and return the result status - foreach ($this->getStaticLibs() as $name) { - if (!file_exists(BUILD_LIB_PATH . "/{$name}")) { - $this->tryBuild(true); - return LIB_STATUS_OK; - } - } - // header files the same - foreach ($this->getHeaders() as $name) { - if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) { - $this->tryBuild(true); - return LIB_STATUS_OK; - } - } - // current library is package and binary file is not exists - if (Config::getLib(static::NAME, 'type', 'lib') === 'package') { - foreach ($this->getBinaryFiles() as $name) { - if (!file_exists(BUILD_BIN_PATH . "/{$name}")) { - $this->tryBuild(true); - return LIB_STATUS_OK; - } - } + if (!$this->isLibraryInstalled()) { + return $this->tryBuild(true); } // if all the files exist at this point, skip the compilation process return LIB_STATUS_ALREADY; @@ -350,7 +316,27 @@ abstract class LibraryBase protected function install(): void { - // do something after extracting pre-built files, default do nothing. overwrite this method to do something + // replace placeholders if BUILD_ROOT_PATH/.spc-extract-placeholder.json exists + $placeholder_file = BUILD_ROOT_PATH . '/.spc-extract-placeholder.json'; + if (!file_exists($placeholder_file)) { + return; + } + $placeholder = json_decode(file_get_contents($placeholder_file), true); + if (!is_array($placeholder)) { + throw new RuntimeException('Invalid placeholder file: ' . $placeholder_file); + } + $placeholder = get_pack_placehoder(); + // replace placeholders in BUILD_ROOT_PATH + foreach ($placeholder as $item) { + $filepath = BUILD_ROOT_PATH . "/{$item}"; + FileSystem::replaceFileStr( + $filepath, + array_values($placeholder), + array_keys($placeholder), + ); + } + // remove placeholder file + unlink($placeholder_file); } /** @@ -397,4 +383,29 @@ abstract class LibraryBase } } } + + protected function isLibraryInstalled(): bool + { + foreach (Config::getLib(static::NAME, 'static-libs', []) as $name) { + if (!file_exists(BUILD_LIB_PATH . "/{$name}")) { + return false; + } + } + foreach (Config::getLib(static::NAME, 'headers', []) as $name) { + if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) { + return false; + } + } + foreach (Config::getLib(static::NAME, 'pkg-configs', []) as $name) { + if (!file_exists(BUILD_LIB_PATH . "/pkgconfig/{$name}.pc")) { + return false; + } + } + foreach (Config::getLib(static::NAME, 'bin', []) as $name) { + if (!file_exists(BUILD_BIN_PATH . "/{$name}")) { + return false; + } + } + return true; + } } diff --git a/src/SPC/builder/freebsd/BSDBuilder.php b/src/SPC/builder/freebsd/BSDBuilder.php index 65ebea57..3e9cc80b 100644 --- a/src/SPC/builder/freebsd/BSDBuilder.php +++ b/src/SPC/builder/freebsd/BSDBuilder.php @@ -63,18 +63,6 @@ class BSDBuilder extends UnixBuilderBase */ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void { - // ---------- Update extra-libs ---------- - $extra_libs = $this->getOption('extra-libs', ''); - // add libc++, some extensions or libraries need it (C++ cannot be linked statically) - $extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCpp() ? '-lc++ ' : ''); - if (!$this->getOption('bloat', false)) { - $extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', $this->getAllStaticLibFiles()); - } else { - logger()->info('bloat linking'); - $extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', array_map(fn ($x) => "-Wl,-force_load,{$x}", array_filter($this->getAllStaticLibFiles()))); - } - $this->setOption('extra-libs', $extra_libs); - $this->emitPatchPoint('before-php-buildconf'); SourcePatcher::patchBeforeBuildconf($this); diff --git a/src/SPC/builder/freebsd/library/curl.php b/src/SPC/builder/freebsd/library/curl.php index 4eeab20d..2cad3ee7 100644 --- a/src/SPC/builder/freebsd/library/curl.php +++ b/src/SPC/builder/freebsd/library/curl.php @@ -9,13 +9,4 @@ class curl extends BSDLibraryBase use \SPC\builder\unix\library\curl; public const NAME = 'curl'; - - public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true, bool $include_self = true): string - { - $libs = parent::getStaticLibFiles($style, $recursive, $include_self); - if ($this->builder->getLib('openssl')) { - $this->builder->setOption('extra-libs', $this->builder->getOption('extra-libs') . ' /usr/lib/libpthread.a /usr/lib/libdl.a'); - } - return $libs; - } } diff --git a/src/SPC/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php index 94d8a41e..869f088f 100644 --- a/src/SPC/builder/linux/LinuxBuilder.php +++ b/src/SPC/builder/linux/LinuxBuilder.php @@ -11,6 +11,7 @@ use SPC\exception\WrongUsageException; use SPC\store\FileSystem; use SPC\store\SourcePatcher; use SPC\util\GlobalEnvManager; +use SPC\util\SPCConfigUtil; class LinuxBuilder extends UnixBuilderBase { @@ -56,17 +57,6 @@ class LinuxBuilder extends UnixBuilderBase */ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void { - // ---------- Update extra-libs ---------- - $extra_libs = getenv('SPC_EXTRA_LIBS') ?: ''; - // bloat means force-load all static libraries, even if they are not used - if (!$this->getOption('bloat', false)) { - $extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', $this->getAllStaticLibFiles()); - } else { - $extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", array_filter($this->getAllStaticLibFiles()))); - } - // add libstdc++, some extensions or libraries need it - $extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCpp() ? '-lstdc++ ' : ''); - f_putenv('SPC_EXTRA_LIBS=' . $extra_libs); $cflags = $this->arch_c_flags; f_putenv('CFLAGS=' . $cflags); @@ -308,9 +298,10 @@ class LinuxBuilder extends UnixBuilderBase private function getMakeExtraVars(): array { + $config = (new SPCConfigUtil($this, ['libs_only_deps' => true, 'absolute_libs' => true]))->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs')); return [ 'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), - 'EXTRA_LIBS' => getenv('SPC_EXTRA_LIBS') . ' ' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS'), + 'EXTRA_LIBS' => $config['libs'], 'EXTRA_LDFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), 'EXTRA_LDFLAGS_PROGRAM' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM'), ]; diff --git a/src/SPC/builder/linux/library/curl.php b/src/SPC/builder/linux/library/curl.php index f382773f..20ed10f2 100644 --- a/src/SPC/builder/linux/library/curl.php +++ b/src/SPC/builder/linux/library/curl.php @@ -9,13 +9,4 @@ class curl extends LinuxLibraryBase use \SPC\builder\unix\library\curl; public const NAME = 'curl'; - - public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true, bool $include_self = true): string - { - $libs = parent::getStaticLibFiles($style, $recursive, $include_self); - if ($this->builder->getLib('openssl')) { - $libs .= ' -ldl -lpthread'; - } - return $libs; - } } diff --git a/src/SPC/builder/linux/library/icu.php b/src/SPC/builder/linux/library/icu.php index c911a2fe..7b8bcf8e 100644 --- a/src/SPC/builder/linux/library/icu.php +++ b/src/SPC/builder/linux/library/icu.php @@ -38,7 +38,7 @@ class icu extends LinuxLibraryBase ->exec("make -j{$this->builder->concurrency}") ->exec('make install'); - $this->patchPkgconfPrefix(['icu-i18n.pc', 'icu-io.pc', 'icu-uc.pc'], PKGCONF_PATCH_PREFIX); + $this->patchPkgconfPrefix(patch_option: PKGCONF_PATCH_PREFIX); FileSystem::removeDir(BUILD_LIB_PATH . '/icu'); } } diff --git a/src/SPC/builder/linux/library/openssl.php b/src/SPC/builder/linux/library/openssl.php index 252eb47f..51633ac2 100644 --- a/src/SPC/builder/linux/library/openssl.php +++ b/src/SPC/builder/linux/library/openssl.php @@ -67,8 +67,8 @@ class openssl extends LinuxLibraryBase shell()->cd($this->source_dir)->initializeEnv($this) ->exec( "{$env} ./Configure no-shared {$extra} " . - '--prefix=/ ' . - '--libdir=lib ' . + '--prefix=' . BUILD_ROOT_PATH . ' ' . + '--libdir=' . BUILD_LIB_PATH . ' ' . '--openssldir=/etc/ssl ' . "{$zlib_extra}" . 'no-legacy ' . @@ -76,28 +76,19 @@ class openssl extends LinuxLibraryBase ) ->exec('make clean') ->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"") - ->exec("make install_sw DESTDIR={$destdir}"); + ->exec('make install_sw'); $this->patchPkgconfPrefix(['libssl.pc', 'openssl.pc', 'libcrypto.pc']); // patch for openssl 3.3.0+ if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libssl.pc'), 'prefix=')) { - FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libssl.pc', 'prefix=${pcfiledir}/../..' . "\n" . $file); + FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libssl.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file); } if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/openssl.pc'), 'prefix=')) { - FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/openssl.pc', 'prefix=${pcfiledir}/../..' . "\n" . $file); + FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/openssl.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file); } if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc'), 'prefix=')) { - FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=${pcfiledir}/../..' . "\n" . $file); + FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file); } FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Libs.private: ${libdir}/libz.a'); FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")'); } - - public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true, bool $include_self = true): string - { - $libFiles = parent::getStaticLibFiles($style, $recursive, $include_self); - if (!str_contains('-ldl -lpthread', $libFiles)) { - $libFiles .= ' -ldl -lpthread'; - } - return $libFiles; - } } diff --git a/src/SPC/builder/macos/MacOSBuilder.php b/src/SPC/builder/macos/MacOSBuilder.php index 362a68fe..50efa013 100644 --- a/src/SPC/builder/macos/MacOSBuilder.php +++ b/src/SPC/builder/macos/MacOSBuilder.php @@ -12,6 +12,7 @@ use SPC\exception\WrongUsageException; use SPC\store\FileSystem; use SPC\store\SourcePatcher; use SPC\util\GlobalEnvManager; +use SPC\util\SPCConfigUtil; class MacOSBuilder extends UnixBuilderBase { @@ -88,21 +89,6 @@ class MacOSBuilder extends UnixBuilderBase */ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void { - $extra_libs = getenv('SPC_EXTRA_LIBS') ?: ''; - // ---------- Update extra-libs ---------- - // add macOS frameworks - $extra_libs .= (empty($extra_libs) ? '' : ' ') . $this->getFrameworks(true); - // add libc++, some extensions or libraries need it (C++ cannot be linked statically) - $extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCpp() ? '-lc++ ' : ''); - // bloat means force-load all static libraries, even if they are not used - if (!$this->getOption('bloat', false)) { - $extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', $this->getAllStaticLibFiles()); - } else { - logger()->info('bloat linking'); - $extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', array_map(fn ($x) => "-Wl,-force_load,{$x}", array_filter($this->getAllStaticLibFiles()))); - } - f_putenv('SPC_EXTRA_LIBS=' . $extra_libs); - $this->emitPatchPoint('before-php-buildconf'); SourcePatcher::patchBeforeBuildconf($this); @@ -291,9 +277,10 @@ class MacOSBuilder extends UnixBuilderBase private function getMakeExtraVars(): array { + $config = (new SPCConfigUtil($this, ['libs_only_deps' => true]))->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs')); return [ 'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), - 'EXTRA_LIBS' => getenv('SPC_EXTRA_LIBS') . ' ' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS'), + 'EXTRA_LIBS' => $config['libs'], ]; } } diff --git a/src/SPC/builder/macos/library/icu.php b/src/SPC/builder/macos/library/icu.php index d49f43f8..ebe1946f 100644 --- a/src/SPC/builder/macos/library/icu.php +++ b/src/SPC/builder/macos/library/icu.php @@ -21,7 +21,7 @@ class icu extends MacOSLibraryBase ->exec("make -j{$this->builder->concurrency}") ->exec('make install'); - $this->patchPkgconfPrefix(['icu-i18n.pc', 'icu-io.pc', 'icu-uc.pc'], PKGCONF_PATCH_PREFIX); + $this->patchPkgconfPrefix(patch_option: PKGCONF_PATCH_PREFIX); FileSystem::removeDir(BUILD_LIB_PATH . '/icu'); } } diff --git a/src/SPC/builder/macos/library/openssl.php b/src/SPC/builder/macos/library/openssl.php index d641fc1d..c1826b5c 100644 --- a/src/SPC/builder/macos/library/openssl.php +++ b/src/SPC/builder/macos/library/openssl.php @@ -37,8 +37,6 @@ class openssl extends MacOSLibraryBase */ protected function build(): void { - [$lib,,$destdir] = SEPARATED_PATH; - // lib:zlib $extra = ''; $ex_lib = ''; @@ -52,24 +50,24 @@ class openssl extends MacOSLibraryBase shell()->cd($this->source_dir)->initializeEnv($this) ->exec( "./Configure no-shared {$extra} " . - '--prefix=/ ' . // use prefix=/ - "--libdir={$lib} " . + '--prefix=' . BUILD_ROOT_PATH . ' ' . // use prefix=/ + '--libdir=lib ' . '--openssldir=/etc/ssl ' . "darwin64-{$arch}-cc" ) ->exec('make clean') ->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"") - ->exec("make install_sw DESTDIR={$destdir}"); + ->exec('make install_sw'); $this->patchPkgconfPrefix(['libssl.pc', 'openssl.pc', 'libcrypto.pc']); // patch for openssl 3.3.0+ if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libssl.pc'), 'prefix=')) { - FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libssl.pc', 'prefix=${pcfiledir}/../..' . "\n" . $file); + FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libssl.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file); } if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/openssl.pc'), 'prefix=')) { - FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/openssl.pc', 'prefix=${pcfiledir}/../..' . "\n" . $file); + FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/openssl.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file); } if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc'), 'prefix=')) { - FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=${pcfiledir}/../..' . "\n" . $file); + FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file); } FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Libs.private: ${libdir}/libz.a'); FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")'); diff --git a/src/SPC/builder/traits/UnixLibraryTrait.php b/src/SPC/builder/traits/UnixLibraryTrait.php index e98a6f3a..1d69f82a 100644 --- a/src/SPC/builder/traits/UnixLibraryTrait.php +++ b/src/SPC/builder/traits/UnixLibraryTrait.php @@ -4,11 +4,12 @@ declare(strict_types=1); namespace SPC\builder\traits; -use SPC\builder\LibraryBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; +use SPC\store\Config; use SPC\store\FileSystem; +use SPC\util\SPCConfigUtil; trait UnixLibraryTrait { @@ -17,44 +18,13 @@ trait UnixLibraryTrait * @throws FileSystemException * @throws WrongUsageException */ - public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true, bool $include_self = true): string + public function getStaticLibFiles(bool $include_self = true): string { $libs = $include_self ? [$this] : []; - if ($recursive) { - array_unshift($libs, ...array_values($this->getDependencies(recursive: true))); - } - - $sep = match ($style) { - 'autoconf' => ' ', - 'cmake' => ';', - default => throw new RuntimeException('style only support autoconf and cmake'), - }; - $ret = []; - /** @var LibraryBase $lib */ - foreach ($libs as $lib) { - $libFiles = []; - foreach ($lib->getStaticLibs() as $name) { - $name = str_replace(' ', '\ ', FileSystem::convertPath(BUILD_LIB_PATH . "/{$name}")); - $name = str_replace('"', '\"', $name); - $libFiles[] = $name; - } - array_unshift($ret, implode($sep, $libFiles)); - } - return implode($sep, $ret); - } - - /** - * @throws FileSystemException - * @throws RuntimeException - * @throws WrongUsageException - */ - public function makeAutoconfEnv(?string $prefix = null): string - { - if ($prefix === null) { - $prefix = str_replace('-', '_', strtoupper(static::NAME)); - } - return $prefix . '_CFLAGS="-I' . BUILD_INCLUDE_PATH . '" ' . - $prefix . '_LIBS="' . $this->getStaticLibFiles() . '"'; + array_unshift($libs, ...array_values($this->getDependencies(recursive: true))); + $config = new SPCConfigUtil($this->builder, options: ['libs_only_deps' => true, 'absolute_libs' => true]); + $res = $config->config(libraries: array_map(fn ($x) => $x->getName(), $libs)); + return $res['libs']; } /** @@ -64,9 +34,12 @@ trait UnixLibraryTrait * @throws FileSystemException * @throws RuntimeException */ - public function patchPkgconfPrefix(array $files, int $patch_option = PKGCONF_PATCH_ALL, ?array $custom_replace = null): void + public function patchPkgconfPrefix(array $files = [], int $patch_option = PKGCONF_PATCH_ALL, ?array $custom_replace = null): void { logger()->info('Patching library [' . static::NAME . '] pkgconfig'); + if ($files === [] && ($conf_pc = Config::getLib(static::NAME, 'pkg-configs', [])) !== []) { + $files = array_map(fn ($x) => "{$x}.pc", $conf_pc); + } foreach ($files as $name) { $realpath = realpath(BUILD_ROOT_PATH . '/lib/pkgconfig/' . $name); if ($realpath === false) { @@ -75,7 +48,7 @@ trait UnixLibraryTrait logger()->debug('Patching ' . $realpath); // replace prefix $file = FileSystem::readFile($realpath); - $file = ($patch_option & PKGCONF_PATCH_PREFIX) === PKGCONF_PATCH_PREFIX ? preg_replace('/^prefix\s*=.*$/m', 'prefix=${pcfiledir}/../..', $file) : $file; + $file = ($patch_option & PKGCONF_PATCH_PREFIX) === PKGCONF_PATCH_PREFIX ? preg_replace('/^prefix\s*=.*$/m', 'prefix=' . BUILD_ROOT_PATH, $file) : $file; $file = ($patch_option & PKGCONF_PATCH_EXEC_PREFIX) === PKGCONF_PATCH_EXEC_PREFIX ? preg_replace('/^exec_prefix\s*=.*$/m', 'exec_prefix=${prefix}', $file) : $file; $file = ($patch_option & PKGCONF_PATCH_LIBDIR) === PKGCONF_PATCH_LIBDIR ? preg_replace('/^libdir\s*=.*$/m', 'libdir=${prefix}/lib', $file) : $file; $file = ($patch_option & PKGCONF_PATCH_INCLUDEDIR) === PKGCONF_PATCH_INCLUDEDIR ? preg_replace('/^includedir\s*=.*$/m', 'includedir=${prefix}/include', $file) : $file; diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php index b8b5e120..27f9c01f 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -5,9 +5,6 @@ declare(strict_types=1); namespace SPC\builder\unix; use SPC\builder\BuilderBase; -use SPC\builder\freebsd\library\BSDLibraryBase; -use SPC\builder\linux\library\LinuxLibraryBase; -use SPC\builder\macos\library\MacOSLibraryBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; @@ -27,85 +24,6 @@ abstract class UnixBuilderBase extends BuilderBase /** @var string C++ flags */ public string $arch_cxx_flags; - /** - * @throws WrongUsageException - * @throws FileSystemException - */ - public function getAllStaticLibFiles(): array - { - $libs = []; - - // reorder libs - foreach ($this->libs as $lib) { - foreach ($lib->getDependencies() as $dep) { - $libs[] = $dep; - } - $libs[] = $lib; - } - - $libFiles = []; - $libNames = []; - // merge libs - foreach ($libs as $lib) { - if (!in_array($lib::NAME, $libNames, true)) { - $libNames[] = $lib::NAME; - array_unshift($libFiles, ...$lib->getStaticLibs()); - } - } - return array_map(fn ($x) => realpath(BUILD_LIB_PATH . "/{$x}"), $libFiles); - } - - /** - * Generate configure flags - */ - public function makeAutoconfFlags(int $flag = AUTOCONF_ALL): string - { - $extra = ''; - // TODO: add auto pkg-config support - if (($flag & AUTOCONF_LIBS) === AUTOCONF_LIBS) { - $extra .= 'LIBS="' . BUILD_LIB_PATH . '" '; - } - if (($flag & AUTOCONF_CFLAGS) === AUTOCONF_CFLAGS) { - $extra .= 'CFLAGS="-I' . BUILD_INCLUDE_PATH . '" '; - } - if (($flag & AUTOCONF_CPPFLAGS) === AUTOCONF_CPPFLAGS) { - $extra .= 'CPPFLAGS="-I' . BUILD_INCLUDE_PATH . '" '; - } - if (($flag & AUTOCONF_LDFLAGS) === AUTOCONF_LDFLAGS) { - $extra .= 'LDFLAGS="-L' . BUILD_LIB_PATH . '" '; - } - return $extra; - } - - /** - * @throws FileSystemException - * @throws RuntimeException - * @throws WrongUsageException - */ - public function makeAutoconfArgs(string $name, array $libSpecs): string - { - $ret = ''; - foreach ($libSpecs as $libName => $arr) { - $lib = $this->getLib($libName); - if ($lib === null && str_starts_with($libName, 'lib')) { - $lib = $this->getLib(substr($libName, 3)); - } - - $arr = $arr ?? []; - - $disableArgs = $arr[0] ?? null; - $prefix = $arr[1] ?? null; - if ($lib instanceof LinuxLibraryBase || $lib instanceof MacOSLibraryBase || $lib instanceof BSDLibraryBase) { - logger()->info("{$name} \033[32;1mwith\033[0;1m {$libName} support"); - $ret .= "--with-{$libName}=yes " . $lib->makeAutoconfEnv($prefix) . ' '; - } else { - logger()->info("{$name} \033[31;1mwithout\033[0;1m {$libName} support"); - $ret .= ($disableArgs ?? "--with-{$libName}=no") . ' '; - } - } - return rtrim($ret); - } - public function proveLibs(array $sorted_libraries): void { // search all supported libs diff --git a/src/SPC/builder/unix/library/curl.php b/src/SPC/builder/unix/library/curl.php index c38201e5..6e304d60 100644 --- a/src/SPC/builder/unix/library/curl.php +++ b/src/SPC/builder/unix/library/curl.php @@ -21,10 +21,10 @@ trait curl UnixCMakeExecutor::create($this) ->optionalLib('openssl', '-DCURL_USE_OPENSSL=ON -DCURL_CA_BUNDLE=OFF -DCURL_CA_PATH=OFF -DCURL_CA_FALLBACK=ON', '-DCURL_USE_OPENSSL=OFF -DCURL_ENABLE_SSL=OFF') ->optionalLib('brotli', ...cmake_boolean_args('CURL_BROTLI')) - ->optionalLib('libssh2', fn ($lib) => "-DLIBSSH2_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DLIBSSH2_INCLUDE_DIR={$lib->getIncludeDir()}", '-DCURL_USE_LIBSSH2=OFF') - ->optionalLib('nghttp2', fn ($lib) => "-DUSE_NGHTTP2=ON -DNGHTTP2_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DNGHTTP2_INCLUDE_DIR={$lib->getIncludeDir()}", '-DUSE_NGHTTP2=OFF') - ->optionalLib('nghttp3', fn ($lib) => "-DUSE_NGHTTP3=ON -DNGHTTP3_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DNGHTTP3_INCLUDE_DIR={$lib->getIncludeDir()}", '-DUSE_NGHTTP3=OFF') - ->optionalLib('ngtcp2', fn ($lib) => "-DUSE_NGTCP2=ON -DNGNGTCP2_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DNGNGTCP2_INCLUDE_DIR={$lib->getIncludeDir()}", '-DUSE_NGTCP2=OFF') + ->optionalLib('libssh2', ...cmake_boolean_args('CURL_USE_LIBSSH2')) + ->optionalLib('nghttp2', ...cmake_boolean_args('USE_NGHTTP2')) + ->optionalLib('nghttp3', ...cmake_boolean_args('USE_NGHTTP3')) + ->optionalLib('ngtcp2', ...cmake_boolean_args('USE_NGTCP2')) ->optionalLib('ldap', ...cmake_boolean_args('CURL_DISABLE_LDAP', true)) ->optionalLib('zstd', ...cmake_boolean_args('CURL_ZSTD')) ->optionalLib('idn2', ...cmake_boolean_args('USE_LIBIDN2')) diff --git a/src/SPC/builder/unix/library/icu.php b/src/SPC/builder/unix/library/icu.php index d1aa8dc1..6f2e8611 100644 --- a/src/SPC/builder/unix/library/icu.php +++ b/src/SPC/builder/unix/library/icu.php @@ -17,6 +17,7 @@ trait icu protected function install(): void { + parent::install(); $icu_config = BUILD_ROOT_PATH . '/bin/icu-config'; FileSystem::replaceFileStr($icu_config, '{BUILD_ROOT_PATH}', BUILD_ROOT_PATH); } diff --git a/src/SPC/builder/unix/library/libevent.php b/src/SPC/builder/unix/library/libevent.php index 55889d1d..ab4838e3 100644 --- a/src/SPC/builder/unix/library/libevent.php +++ b/src/SPC/builder/unix/library/libevent.php @@ -68,6 +68,7 @@ trait libevent protected function install(): void { + parent::install(); FileSystem::replaceFileStr( BUILD_LIB_PATH . '/cmake/libevent/LibeventTargets-static.cmake', '{BUILD_ROOT_PATH}', diff --git a/src/SPC/builder/unix/library/libwebp.php b/src/SPC/builder/unix/library/libwebp.php index 32331ea2..a7651dd5 100644 --- a/src/SPC/builder/unix/library/libwebp.php +++ b/src/SPC/builder/unix/library/libwebp.php @@ -22,8 +22,7 @@ trait libwebp ->addConfigureArgs('-DWEBP_BUILD_EXTRAS=ON') ->build(); // patch pkgconfig - $this->patchPkgconfPrefix(['libsharpyuv.pc', 'libwebp.pc', 'libwebpdecoder.pc', 'libwebpdemux.pc', 'libwebpmux.pc'], PKGCONF_PATCH_PREFIX | PKGCONF_PATCH_LIBDIR); + $this->patchPkgconfPrefix(patch_option: PKGCONF_PATCH_PREFIX | PKGCONF_PATCH_LIBDIR); $this->patchPkgconfPrefix(['libsharpyuv.pc'], PKGCONF_PATCH_CUSTOM, ['/^includedir=.*$/m', 'includedir=${prefix}/include/webp']); - $this->patchPkgconfPrefix(['libwebp.pc'], PKGCONF_PATCH_CUSTOM, ['/-lwebp$/m', '-lwebp -lsharpyuv']); } } diff --git a/src/SPC/builder/unix/library/libxslt.php b/src/SPC/builder/unix/library/libxslt.php index aabc277d..98ceba8d 100644 --- a/src/SPC/builder/unix/library/libxslt.php +++ b/src/SPC/builder/unix/library/libxslt.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace SPC\builder\unix\library; use SPC\builder\linux\library\LinuxLibraryBase; +use SPC\builder\macos\library\MacOSLibraryBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; @@ -20,11 +21,12 @@ trait libxslt protected function build(): void { $static_libs = $this instanceof LinuxLibraryBase ? $this->getStaticLibFiles(include_self: false) : ''; + $cpp = $this instanceof MacOSLibraryBase ? '-lc++' : '-lstdc++'; $ac = UnixAutoconfExecutor::create($this) ->appendEnv([ 'CFLAGS' => "-I{$this->getIncludeDir()}", 'LDFLAGS' => "-L{$this->getLibDir()}", - 'LIBS' => "{$static_libs} -lstdc++", + 'LIBS' => "{$static_libs} {$cpp}", ]) ->addConfigureArgs( '--without-python', @@ -41,7 +43,7 @@ trait libxslt } $ac->configure()->make(); - $this->patchPkgconfPrefix(['libexslt.pc']); + $this->patchPkgconfPrefix(['libexslt.pc', 'libxslt.pc']); $this->patchLaDependencyPrefix(); shell()->cd(BUILD_LIB_PATH) ->exec("ar -t libxslt.a | grep '\\.a$' | xargs -n1 ar d libxslt.a") diff --git a/src/SPC/builder/unix/library/ngtcp2.php b/src/SPC/builder/unix/library/ngtcp2.php index df29fba3..9c9919d8 100644 --- a/src/SPC/builder/unix/library/ngtcp2.php +++ b/src/SPC/builder/unix/library/ngtcp2.php @@ -4,6 +4,8 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\builder\linux\library\LinuxLibraryBase; +use SPC\builder\macos\library\MacOSLibraryBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; @@ -19,7 +21,7 @@ trait ngtcp2 protected function build(): void { UnixAutoconfExecutor::create($this) - ->optionalLib('openssl', fn ($lib) => implode(' ', [ + ->optionalLib('openssl', fn (LinuxLibraryBase|MacOSLibraryBase $lib) => implode(' ', [ '--with-openssl=yes', "OPENSSL_LIBS=\"{$lib->getStaticLibFiles()}\"", "OPENSSL_CFLAGS=\"-I{$lib->getIncludeDir()}\"", @@ -29,7 +31,7 @@ trait ngtcp2 ->optionalLib('jemalloc', ...ac_with_args('jemalloc', true)) ->optionalLib( 'brotli', - fn ($lib) => implode(' ', [ + fn (LinuxLibraryBase|MacOSLibraryBase $lib) => implode(' ', [ '--with-brotlidec=yes', "LIBBROTLIDEC_CFLAGS=\"-I{$lib->getIncludeDir()}\"", "LIBBROTLIDEC_LIBS=\"{$lib->getStaticLibFiles()}\"", diff --git a/src/SPC/builder/unix/library/xz.php b/src/SPC/builder/unix/library/xz.php index f127e59a..98f33bea 100644 --- a/src/SPC/builder/unix/library/xz.php +++ b/src/SPC/builder/unix/library/xz.php @@ -21,6 +21,7 @@ trait xz '--disable-scripts', '--disable-doc', '--with-libiconv', + '--bindir=/tmp/xz', // xz binary will corrupt `tar` command, that's really strange. ) ->make(); $this->patchPkgconfPrefix(['liblzma.pc']); diff --git a/src/SPC/command/BuildCommand.php b/src/SPC/command/BuildCommand.php index 6020931f..0285c333 100644 --- a/src/SPC/command/BuildCommand.php +++ b/src/SPC/command/BuildCommand.php @@ -18,7 +18,6 @@ abstract class BuildCommand extends BaseCommand } $this->addOption('with-clean', null, null, 'fresh build, remove `source` and `buildroot` dir before build'); - $this->addOption('bloat', null, null, 'add all libraries into binary'); $this->addOption('rebuild', 'r', null, 'Delete old build and rebuild'); $this->addOption('enable-zts', null, null, 'enable ZTS support'); } diff --git a/src/SPC/command/SPCConfigCommand.php b/src/SPC/command/SPCConfigCommand.php index a1886ed1..4b6d1bc4 100644 --- a/src/SPC/command/SPCConfigCommand.php +++ b/src/SPC/command/SPCConfigCommand.php @@ -23,6 +23,9 @@ class SPCConfigCommand extends BaseCommand $this->addOption('with-suggested-exts', 'E', null, 'Build with suggested extensions for selected exts'); $this->addOption('includes', null, null, 'Add additional include path'); $this->addOption('libs', null, null, 'Add additional libs path'); + $this->addOption('libs-only-deps', null, null, 'Output dependent libraries with -l prefix'); + $this->addOption('absolute-libs', null, null, 'Output absolute paths for libraries'); + $this->addOption('no-php', null, null, 'Do not link to PHP library'); } /** @@ -37,16 +40,19 @@ class SPCConfigCommand extends BaseCommand $include_suggest_ext = $this->getOption('with-suggested-exts'); $include_suggest_lib = $this->getOption('with-suggested-libs'); - $util = new SPCConfigUtil(); + $util = new SPCConfigUtil(options: [ + 'no_php' => $this->getOption('no-php'), + 'libs_only_deps' => $this->getOption('libs-only-deps'), + 'absolute_libs' => $this->getOption('absolute-libs'), + ]); $config = $util->config($extensions, $libraries, $include_suggest_ext, $include_suggest_lib); - if ($this->getOption('includes')) { - $this->output->writeln($config['cflags']); - } elseif ($this->getOption('libs')) { - $this->output->writeln("{$config['ldflags']} {$config['libs']}"); - } else { - $this->output->writeln("{$config['cflags']} {$config['ldflags']} {$config['libs']}"); - } + $this->output->writeln(match (true) { + $this->getOption('includes') => $config['cflags'], + $this->getOption('libs-only-deps') => $config['libs'], + $this->getOption('libs') => "{$config['ldflags']} {$config['libs']}", + default => "{$config['cflags']} {$config['ldflags']} {$config['libs']}", + }); return 0; } diff --git a/src/SPC/command/dev/PackLibCommand.php b/src/SPC/command/dev/PackLibCommand.php index 6440588c..7b6aec07 100644 --- a/src/SPC/command/dev/PackLibCommand.php +++ b/src/SPC/command/dev/PackLibCommand.php @@ -51,6 +51,10 @@ class PackLibCommand extends BuildCommand } } + $origin_files = []; + // get pack placehoder defines + $placehoder = get_pack_placehoder(); + foreach ($builder->getLibs() as $lib) { if ($lib->getName() !== $lib_name) { // other dependencies: install or build, both ok @@ -73,6 +77,27 @@ class PackLibCommand extends BuildCommand // After build: load buildroot/ directory, and calculate increase files $after_buildroot = FileSystem::scanDirFiles(BUILD_ROOT_PATH, relative: true); $increase_files = array_diff($after_buildroot, $before_buildroot); + + // patch pkg-config and la files with absolute path + foreach ($increase_files as $file) { + if (str_ends_with($file, '.pc') || str_ends_with($file, '.la')) { + $content = FileSystem::readFile(BUILD_ROOT_PATH . '/' . $file); + $origin_files[$file] = $content; + // replace relative paths with absolute paths + $content = str_replace( + array_keys($placehoder), + array_values($placehoder), + $content + ); + FileSystem::writeFile(BUILD_ROOT_PATH . '/' . $file, $content); + } + } + + // add .spc-extract-placeholder.json in BUILD_ROOT_PATH + $placeholder_file = BUILD_ROOT_PATH . '/.spc-extract-placeholder.json'; + file_put_contents($placeholder_file, json_encode(array_keys($origin_files), JSON_PRETTY_PRINT)); + $increase_files[] = '.spc-extract-placeholder.json'; + // every file mapped with BUILD_ROOT_PATH // get BUILD_ROOT_PATH last dir part $buildroot_part = basename(BUILD_ROOT_PATH); @@ -94,6 +119,16 @@ class PackLibCommand extends BuildCommand $filename = WORKING_DIR . '/dist/' . $filename; f_passthru("tar {$tar_option} {$filename} -T " . WORKING_DIR . '/packlib_files.txt'); logger()->info('Pack library ' . $lib->getName() . ' to ' . $filename . ' complete.'); + + // remove temp files + unlink($placeholder_file); + } + } + + foreach ($origin_files as $file => $content) { + // restore original files + if (file_exists(BUILD_ROOT_PATH . '/' . $file)) { + FileSystem::writeFile(BUILD_ROOT_PATH . '/' . $file, $content); } } diff --git a/src/SPC/util/ConfigValidator.php b/src/SPC/util/ConfigValidator.php index dbc8ce23..b908efce 100644 --- a/src/SPC/util/ConfigValidator.php +++ b/src/SPC/util/ConfigValidator.php @@ -90,6 +90,9 @@ class ConfigValidator if (isset($lib['static-libs' . $suffix]) && !is_list_array($lib['static-libs' . $suffix])) { throw new ValidationException("lib {$name} static-libs must be a list"); } + if (isset($lib['pkg-configs' . $suffix]) && !is_list_array($lib['pkg-configs' . $suffix])) { + throw new ValidationException("lib {$name} pkg-configs must be a list"); + } } // check if frameworks is a list array if (isset($lib['frameworks']) && !is_list_array($lib['frameworks'])) { diff --git a/src/SPC/util/PkgConfigUtil.php b/src/SPC/util/PkgConfigUtil.php new file mode 100644 index 00000000..87075b49 --- /dev/null +++ b/src/SPC/util/PkgConfigUtil.php @@ -0,0 +1,75 @@ +builder = $builder; // BuilderProvider::makeBuilderByInput($input ?? new ArgvInput()); } + $this->no_php = $options['no_php'] ?? false; + $this->libs_only_deps = $options['libs_only_deps'] ?? false; + $this->absolute_libs = $options['absolute_libs'] ?? false; } /** @@ -54,45 +69,85 @@ class SPCConfigUtil } ob_get_clean(); $ldflags = $this->getLdflagsString(); - $libs = $this->getLibsString($libraries, $with_dependencies); - if (SPCTarget::getTargetOS() === 'Darwin') { - $libs .= " {$this->getFrameworksString($extensions)}"; - } - $cflags = $this->getIncludesString(); + $cflags = $this->getIncludesString($libraries); + $libs = $this->getLibsString($libraries, !$this->absolute_libs); - // embed - $libs = trim("-lphp -lc {$libs}"); + // additional OS-specific libraries (e.g. macOS -lresolv) $extra_env = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS'); if (is_string($extra_env)) { $libs .= ' ' . trim($extra_env, '"'); } - // c++ - if ($this->builder->hasCpp()) { - $libs .= $this->builder instanceof MacOSBuilder ? ' -lc++' : ' -lstdc++'; + $extra_env = getenv('SPC_EXTRA_LIBS'); + if (is_string($extra_env) && !empty($extra_env)) { + $libs .= " {$extra_env}"; } + // extension frameworks + if (SPCTarget::getTargetOS() === 'Darwin') { + $libs .= " {$this->getFrameworksString($extensions)}"; + } + if ($this->builder->hasCpp()) { + $libs .= SPCTarget::getTargetOS() === 'Darwin' ? ' -lc++' : ' -lstdc++'; + } + + if ($this->libs_only_deps) { + return [ + 'cflags' => trim(getenv('CFLAGS') . ' ' . $cflags), + 'ldflags' => trim(getenv('LDFLAGS') . ' ' . $ldflags), + 'libs' => trim(getenv('LIBS') . ' ' . $libs), + ]; + } + + // embed + if (!$this->no_php) { + $libs = "-lphp {$libs} -lc"; + } + + $allLibs = getenv('LIBS') . ' ' . $libs; + // mimalloc must come first if (str_contains($libs, BUILD_LIB_PATH . '/mimalloc.o')) { - $libs = BUILD_LIB_PATH . '/mimalloc.o ' . str_replace(BUILD_LIB_PATH . '/mimalloc.o', '', $libs); + $allLibs = BUILD_LIB_PATH . '/mimalloc.o ' . str_replace(BUILD_LIB_PATH . '/mimalloc.o', '', $allLibs); } + return [ 'cflags' => trim(getenv('CFLAGS') . ' ' . $cflags), 'ldflags' => trim(getenv('LDFLAGS') . ' ' . $ldflags), - 'libs' => trim(getenv('LIBS') . ' ' . $libs), + 'libs' => trim($allLibs), ]; } - private function getIncludesString(): string + private function getIncludesString(array $libraries): string { $base = BUILD_INCLUDE_PATH; - $php_embed_includes = [ - "-I{$base}", - "-I{$base}/php", - "-I{$base}/php/main", - "-I{$base}/php/TSRM", - "-I{$base}/php/Zend", - "-I{$base}/php/ext", - ]; - return implode(' ', $php_embed_includes); + $includes = ["-I{$base}"]; + + // link with libphp + if (!$this->no_php) { + $includes = [ + ...$includes, + "-I{$base}/php", + "-I{$base}/php/main", + "-I{$base}/php/TSRM", + "-I{$base}/php/Zend", + "-I{$base}/php/ext", + ]; + } + + // parse pkg-configs + foreach ($libraries as $library) { + $pc = Config::getLib($library, 'pkg-configs', []); + 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."); + } + } + $pc_cflags = implode(' ', $pc); + if ($pc_cflags !== '' && ($pc_cflags = PkgConfigUtil::getCflags($pc_cflags)) !== '') { + $includes[] = $pc_cflags; + } + } + $includes = array_unique($includes); + return implode(' ', $includes); } private function getLdflagsString(): string @@ -100,78 +155,61 @@ class SPCConfigUtil return '-L' . BUILD_LIB_PATH; } - private function getLibsString(array $libraries, bool $withDependencies = false): string + private function getLibsString(array $libraries, bool $use_short_libs = true): string { - $short_name = []; - foreach (array_reverse($libraries) as $library) { + $lib_names = []; + $frameworks = []; + + foreach ($libraries as $library) { + // convert all static-libs to short names $libs = Config::getLib($library, 'static-libs', []); foreach ($libs as $lib) { - $noExt = str_replace('.a', '', $lib); - $noExtNoLib = str_replace('lib', '', $noExt); - $pkgconfFileNoExt = BUILD_LIB_PATH . "/pkgconfig/{$noExt}.pc"; - $pkgconfFileNoExtNoLib = BUILD_LIB_PATH . "/pkgconfig/{$noExtNoLib}.pc"; - $llibs = null; - if (file_exists($pkgconfFileNoExt)) { - $llibs = shell()->execWithResult("pkg-config --libs --static {$noExt}")[1][0]; - } elseif (file_exists($pkgconfFileNoExtNoLib)) { - $llibs = shell()->execWithResult("pkg-config --libs --static {$noExtNoLib}")[1][0]; + // check file existence + if (!file_exists(BUILD_LIB_PATH . "/{$lib}")) { + throw new WrongUsageException("Library file '{$lib}' for lib [{$library}] does not exist in '" . BUILD_LIB_PATH . "'. Please build it first."); } + $lib_names[] = $this->getShortLibName($lib); + } + // add frameworks for macOS + if (SPCTarget::getTargetOS() === 'Darwin') { + $frameworks = array_merge($frameworks, Config::getLib($library, 'frameworks', [])); + } + // 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_configs = implode(' ', $pkg_configs); + if ($pkg_configs !== '') { + // static libs with dependencies come in reverse order, so reverse this too + $pc_libs = array_reverse(PkgConfigUtil::getLibsArray($pkg_configs)); + $lib_names = [...$lib_names, ...$pc_libs]; + } + } - if (!empty($llibs)) { - $filtered = str_replace('-pthread', '', $llibs); - $filtered = preg_replace('/-L\S+/', '', $filtered); - $filtered = preg_replace('/(?:\S*\/)?lib([a-zA-Z0-9_+-]+)\.a\b/', '-l$1', $filtered); - $filtered = preg_replace('/\s+/', ' ', $filtered); - foreach (explode(' ', $filtered) as $item) { - $short_name[] = $item; - } - } elseif ($withDependencies) { - $noExt = str_replace('.a', '', $lib); - $requiredLibs = []; - $pkgconfFile = BUILD_LIB_PATH . "/pkgconfig/{$noExt}.pc"; - if (!file_exists($pkgconfFile)) { - $noExtNoLib = str_replace('lib', '', $noExt); - $pkgconfFile = BUILD_LIB_PATH . "/pkgconfig/{$noExtNoLib}.pc"; - } - if (file_exists($pkgconfFile)) { - $lines = file($pkgconfFile); - foreach ($lines as $value) { - if (str_starts_with($value, 'Libs')) { - $items = explode(' ', $value); - foreach ($items as $item) { - $item = trim($item); - if (str_starts_with($item, '-l')) { - $requiredLibs[] = $item; - } - } - } - } - } else { - $requiredLibs[] = $this->getShortLibName($lib); - } - foreach ($requiredLibs as $requiredLib) { - if (!in_array($requiredLib, $short_name)) { - $short_name[] = $requiredLib; - } - } - } else { - $short_name[] = $this->getShortLibName($lib); - } - } - if (PHP_OS_FAMILY !== 'Darwin') { - continue; - } - foreach (Config::getLib($library, 'frameworks', []) as $fw) { + // post-process + $lib_names = array_reverse(array_unique(array_filter($lib_names, fn ($x) => $x !== ''))); + $frameworks = array_unique($frameworks); + + // process frameworks to short_name + if (SPCTarget::getTargetOS() === 'Darwin') { + foreach ($frameworks as $fw) { $ks = '-framework ' . $fw; - if (!in_array($ks, $short_name)) { - $short_name[] = $ks; + if (!in_array($ks, $lib_names)) { + $lib_names[] = $ks; } } } + if (in_array('imap', $libraries) && SPCTarget::getLibc() === 'glibc') { - $short_name[] = '-lcrypt'; + $lib_names[] = '-lcrypt'; } - return implode(' ', $short_name); + if (!$use_short_libs) { + $lib_names = array_map(fn ($l) => $this->getFullLibName($l), $lib_names); + } + return implode(' ', $lib_names); } private function getShortLibName(string $lib): string @@ -183,6 +221,19 @@ class SPCConfigUtil return '-l' . substr($lib, 3, -2); } + private function getFullLibName(string $lib) + { + if (!str_starts_with($lib, '-l')) { + return $lib; + } + $libname = substr($lib, 2); + $staticLib = BUILD_LIB_PATH . '/' . "lib{$libname}.a"; + if (file_exists($staticLib)) { + return $staticLib; + } + return $lib; + } + private function getFrameworksString(array $extensions): string { $list = []; diff --git a/src/SPC/util/SPCTarget.php b/src/SPC/util/SPCTarget.php index 071f2620..b674379e 100644 --- a/src/SPC/util/SPCTarget.php +++ b/src/SPC/util/SPCTarget.php @@ -73,7 +73,7 @@ class SPCTarget public static function getTargetOS(): string { $target = getenv('SPC_TARGET'); - if ($target === false) { + if ($target === false || $target === '') { return PHP_OS_FAMILY; } // TODO: zig target parser like below? diff --git a/src/globals/ext-tests/curl.php b/src/globals/ext-tests/curl.php index a890a8cf..7c3eecf7 100644 --- a/src/globals/ext-tests/curl.php +++ b/src/globals/ext-tests/curl.php @@ -9,7 +9,7 @@ assert(function_exists('curl_close')); $curl_version = curl_version(); if (stripos($curl_version['ssl_version'], 'schannel') !== false) { $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, 'https://example.com/'); + curl_setopt($curl, CURLOPT_URL, 'https://captive.apple.com/'); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_HEADER, 0); $data = curl_exec($curl); diff --git a/src/globals/ext-tests/openssl.php b/src/globals/ext-tests/openssl.php index 3b3452b3..0453101a 100644 --- a/src/globals/ext-tests/openssl.php +++ b/src/globals/ext-tests/openssl.php @@ -5,5 +5,5 @@ declare(strict_types=1); assert(function_exists('openssl_digest')); assert(openssl_digest('123456', 'md5') === 'e10adc3949ba59abbe56e057f20f883e'); if (file_exists('/etc/ssl/openssl.cnf')) { - assert(file_get_contents('https://example.com/') !== false); + assert(file_get_contents('https://captive.apple.com/') !== false); } diff --git a/src/globals/functions.php b/src/globals/functions.php index 998b2d1c..28f059ac 100644 --- a/src/globals/functions.php +++ b/src/globals/functions.php @@ -232,3 +232,13 @@ function ac_with_args(string $arg_name, bool $use_value = false): array { return $use_value ? ["--with-{$arg_name}=yes", "--with-{$arg_name}=no"] : ["--with-{$arg_name}", "--without-{$arg_name}"]; } + +function get_pack_placehoder(): array +{ + return [ + BUILD_LIB_PATH => '@build_lib_path@', + BUILD_BIN_PATH => '@build_bin_path@', + BUILD_INCLUDE_PATH => '@build_include_path@', + BUILD_ROOT_PATH => '@build_root_path@', + ]; +} diff --git a/src/globals/test-extensions.php b/src/globals/test-extensions.php index 5f44332e..4d1f7d2a 100644 --- a/src/globals/test-extensions.php +++ b/src/globals/test-extensions.php @@ -21,19 +21,19 @@ $test_php_version = [ // test os (macos-13, macos-14, macos-15, ubuntu-latest, windows-latest are available) $test_os = [ - 'macos-13', // bin/spc for x86_64 + // 'macos-13', // bin/spc for x86_64 // 'macos-14', // bin/spc for arm64 'macos-15', // bin/spc for arm64 '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-22.04', // bin/spc-gnu-docker 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 // 'windows-latest', // .\bin\spc.ps1 ]; // whether enable thread safe -$zts = true; +$zts = false; $no_strip = false; @@ -72,7 +72,7 @@ $with_libs = match (PHP_OS_FAMILY) { // You can use `common`, `bulk`, `minimal` or `none`. // note: combination is only available for *nix platform. Windows must use `none` combination $base_combination = match (PHP_OS_FAMILY) { - 'Linux', 'Darwin' => 'minimal', + 'Linux', 'Darwin' => 'none', 'Windows' => 'none', }; diff --git a/tests/SPC/builder/ExtensionTest.php b/tests/SPC/builder/ExtensionTest.php index 499a800f..c387dc99 100644 --- a/tests/SPC/builder/ExtensionTest.php +++ b/tests/SPC/builder/ExtensionTest.php @@ -78,11 +78,6 @@ class ExtensionTest extends TestCase } } - public function testGetLibFilesString() - { - $this->assertStringEndsWith('libonig.a', $this->extension->getLibFilesString()); - } - public function testGetName() { $this->assertEquals('mbregex', $this->extension->getName()); diff --git a/tests/SPC/util/SPCConfigUtilTest.php b/tests/SPC/util/SPCConfigUtilTest.php index ebd2f92a..c37f01f3 100644 --- a/tests/SPC/util/SPCConfigUtilTest.php +++ b/tests/SPC/util/SPCConfigUtilTest.php @@ -54,7 +54,7 @@ class SPCConfigUtilTest extends TestCase $this->assertStringContainsString('-lphp', $result['libs']); // has cpp - $result = (new SPCConfigUtil())->config(['swoole']); + $result = (new SPCConfigUtil())->config(['rar']); $this->assertStringContainsString(PHP_OS_FAMILY === 'Darwin' ? '-lc++' : '-lstdc++', $result['libs']); // has mimalloc.o in lib dir