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/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 5c0c4f0d..bee83560 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -10,7 +10,7 @@ on: env: PHP_VERSION: 8.4 - MICRO_VERSION: 8.4.4 + MICRO_VERSION: 8.4.10 jobs: build-release-artifacts: @@ -42,12 +42,13 @@ jobs: run: echo "SPC_BUILD_DEBUG=--debug" >> $GITHUB_ENV - name: "Install PHP for official runners" - uses: "shivammathur/setup-php@v2" + uses: shivammathur/setup-php@v2 with: coverage: none tools: composer:v2 php-version: "${{ env.PHP_VERSION }}" ini-values: memory_limit=-1 + extensions: curl, openssl, mbstring - name: "Get Composer Cache Directory" id: composer-cache diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ad6c308a..a360e54a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -123,6 +123,7 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: 8.4 + extensions: curl, openssl, mbstring - name: Define id: gendef diff --git a/.github/workflows/vitepress-deploy.yml b/.github/workflows/vitepress-deploy.yml index b44bef84..52aa8e25 100644 --- a/.github/workflows/vitepress-deploy.yml +++ b/.github/workflows/vitepress-deploy.yml @@ -32,12 +32,13 @@ jobs: cp -r config/* docs/.vitepress/config/ - name: "Install PHP for official runners" - uses: "shivammathur/setup-php@v2" + uses: shivammathur/setup-php@v2 with: coverage: none tools: composer:v2 php-version: 8.4 ini-values: memory_limit=-1 + extensions: curl, openssl, mbstring - name: "Get Composer Cache Directory" id: composer-cache diff --git a/composer.lock b/composer.lock index 2f2912db..a05a1e0d 100644 --- a/composer.lock +++ b/composer.lock @@ -2904,16 +2904,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.76.0", + "version": "v3.84.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "0e3c484cef0ae9314b0f85986a36296087432c40" + "reference": "38dad0767bf2a9b516b976852200ae722fe984ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/0e3c484cef0ae9314b0f85986a36296087432c40", - "reference": "0e3c484cef0ae9314b0f85986a36296087432c40", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/38dad0767bf2a9b516b976852200ae722fe984ca", + "reference": "38dad0767bf2a9b516b976852200ae722fe984ca", "shasum": "" }, "require": { @@ -2997,7 +2997,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.76.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.84.0" }, "funding": [ { @@ -3005,7 +3005,7 @@ "type": "github" } ], - "time": "2025-06-30T14:15:06+00:00" + "time": "2025-07-15T18:21:57+00:00" }, { "name": "humbug/box", @@ -3548,16 +3548,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.2", + "version": "1.13.3", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "d25e62e636b0a9b01e3bdebb7823b474876dd829" + "reference": "faed855a7b5f4d4637717c2b3863e277116beb36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/d25e62e636b0a9b01e3bdebb7823b474876dd829", - "reference": "d25e62e636b0a9b01e3bdebb7823b474876dd829", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36", + "reference": "faed855a7b5f4d4637717c2b3863e277116beb36", "shasum": "" }, "require": { @@ -3596,7 +3596,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.2" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.3" }, "funding": [ { @@ -3604,7 +3604,7 @@ "type": "tidelift" } ], - "time": "2025-07-04T14:07:32+00:00" + "time": "2025-07-05T12:25:42+00:00" }, { "name": "nikic/iter", @@ -4406,16 +4406,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68" + "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", - "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b9e61a61e39e02dd90944e9115241c7f7e76bfd8", + "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8", "shasum": "" }, "require": { @@ -4447,22 +4447,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.2.0" }, - "time": "2025-02-19T13:28:12+00:00" + "time": "2025-07-13T07:04:09+00:00" }, { "name": "phpstan/phpstan", - "version": "1.12.27", + "version": "1.12.28", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "3a6e423c076ab39dfedc307e2ac627ef579db162" + "reference": "fcf8b71aeab4e1a1131d1783cef97b23a51b87a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/3a6e423c076ab39dfedc307e2ac627ef579db162", - "reference": "3a6e423c076ab39dfedc307e2ac627ef579db162", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fcf8b71aeab4e1a1131d1783cef97b23a51b87a9", + "reference": "fcf8b71aeab4e1a1131d1783cef97b23a51b87a9", "shasum": "" }, "require": { @@ -4507,7 +4507,7 @@ "type": "github" } ], - "time": "2025-05-21T20:51:45+00:00" + "time": "2025-07-17T17:15:39+00:00" }, { "name": "phpunit/php-code-coverage", @@ -4832,16 +4832,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.47", + "version": "10.5.48", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3637b3e50d32ab3a0d1a33b3b6177169ec3d95a3" + "reference": "6e0a2bc39f6fae7617989d690d76c48e6d2eb541" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3637b3e50d32ab3a0d1a33b3b6177169ec3d95a3", - "reference": "3637b3e50d32ab3a0d1a33b3b6177169ec3d95a3", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6e0a2bc39f6fae7617989d690d76c48e6d2eb541", + "reference": "6e0a2bc39f6fae7617989d690d76c48e6d2eb541", "shasum": "" }, "require": { @@ -4851,7 +4851,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.1", + "myclabs/deep-copy": "^1.13.3", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.1", @@ -4913,7 +4913,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.47" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.48" }, "funding": [ { @@ -4937,7 +4937,7 @@ "type": "tidelift" } ], - "time": "2025-06-20T11:29:11+00:00" + "time": "2025-07-11T04:07:17+00:00" }, { "name": "psr/event-dispatcher", @@ -7628,7 +7628,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -7636,6 +7636,6 @@ "ext-mbstring": "*", "ext-zlib": "*" }, - "platform-dev": [], - "plugin-api-version": "2.3.0" + "platform-dev": {}, + "plugin-api-version": "2.6.0" } diff --git a/config/env.ini b/config/env.ini index b50e3feb..f52c13f8 100644 --- a/config/env.ini +++ b/config/env.ini @@ -73,7 +73,7 @@ SPC_EXTRA_LIBS="" ; - musl-native: used for alpine linux, can build `musl` and `musl -dynamic` target. ; - gnu-native: used for general linux distros, can build gnu target for the installed glibc version only. -; LEGACY option to specify the target, switch to SPC_TARGET with zig toolchain instead +; LEGACY option to specify the target SPC_LIBC=musl ; Recommended: specify your target here. Zig toolchain will be used. @@ -91,10 +91,8 @@ CXX=${SPC_LINUX_DEFAULT_CXX} AR=${SPC_LINUX_DEFAULT_AR} LD=${SPC_LINUX_DEFAULT_LD} ; default compiler flags, used in CMake toolchain file, openssl and pkg-config build -SPC_DEFAULT_C_FLAGS="-fPIC -Os -Wno-error=date-time" -SPC_DEFAULT_CXX_FLAGS="-fPIC -Os -Wno-error=date-time" -; 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_DEFAULT_C_FLAGS="-fPIC -Os" +SPC_DEFAULT_CXX_FLAGS="-fPIC -Os" ; upx executable path UPX_EXEC=${PKG_ROOT_PATH}/bin/upx ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches @@ -132,8 +130,6 @@ CXX=clang++ ; default compiler flags, used in CMake toolchain file, openssl and pkg-config build 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= ; 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/ext.json b/config/ext.json index a98ed3e3..2cf81b18 100644 --- a/config/ext.json +++ b/config/ext.json @@ -34,7 +34,7 @@ }, "bz2": { "type": "builtin", - "arg-type-unix": "with-prefix", + "arg-type-unix": "with-path", "arg-type-windows": "with", "lib-depends": [ "bzip2" @@ -185,7 +185,7 @@ "BSD": "wip" }, "type": "builtin", - "arg-type": "with-prefix", + "arg-type": "with-path", "lib-depends": [ "gettext" ] @@ -211,7 +211,7 @@ "BSD": "wip" }, "type": "builtin", - "arg-type": "with-prefix", + "arg-type": "with-path", "lib-depends": [ "gmp" ] @@ -233,7 +233,7 @@ }, "type": "external", "source": "grpc", - "arg-type-unix": "custom", + "arg-type-unix": "enable-path", "cpp-extension": true, "lib-depends": [ "grpc" @@ -244,7 +244,7 @@ "BSD": "wip" }, "type": "builtin", - "arg-type": "with-prefix", + "arg-type": "with-path", "arg-type-windows": "with", "lib-depends-unix": [ "libiconv" @@ -320,7 +320,7 @@ "BSD": "wip" }, "type": "builtin", - "arg-type": "with-prefix", + "arg-type": "with-path", "lib-depends": [ "ldap" ], @@ -530,7 +530,7 @@ }, "notes": true, "type": "builtin", - "arg-type": "with-prefix", + "arg-type": "with-path", "lib-depends": [ "libargon2" ] @@ -572,7 +572,7 @@ "BSD": "wip" }, "type": "builtin", - "arg-type": "with-prefix", + "arg-type": "with-path", "arg-type-windows": "custom", "ext-depends": [ "pdo", @@ -675,7 +675,7 @@ "BSD": "wip" }, "type": "builtin", - "arg-type": "with-prefix", + "arg-type": "with-path", "lib-depends": [ "readline" ], @@ -786,7 +786,7 @@ "BSD": "wip" }, "type": "builtin", - "arg-type": "with-prefix", + "arg-type": "with-path", "arg-type-windows": "with", "lib-depends": [ "sqlite" @@ -812,7 +812,7 @@ }, "type": "external", "source": "ext-ssh2", - "arg-type": "with-prefix", + "arg-type": "with-path", "arg-type-windows": "with", "lib-depends": [ "libssh2" @@ -938,7 +938,7 @@ "BSD": "wip" }, "type": "builtin", - "arg-type": "with-prefix", + "arg-type": "with-path", "lib-depends": [ "tidy" ] @@ -954,7 +954,7 @@ }, "type": "external", "source": "ext-uuid", - "arg-type": "with-prefix", + "arg-type": "with-path", "lib-depends": [ "libuuid" ] @@ -966,7 +966,7 @@ }, "type": "external", "source": "ext-uv", - "arg-type": "with-prefix", + "arg-type": "with-path", "lib-depends": [ "libuv" ], @@ -1068,7 +1068,7 @@ "BSD": "wip" }, "type": "builtin", - "arg-type": "with-prefix", + "arg-type": "with-path", "lib-depends": [ "libxslt" ], @@ -1105,7 +1105,7 @@ }, "type": "external", "source": "yaml", - "arg-type-unix": "with-prefix", + "arg-type-unix": "with-path", "arg-type-windows": "with", "lib-depends": [ "libyaml" @@ -1116,7 +1116,7 @@ "BSD": "wip" }, "type": "builtin", - "arg-type": "with-prefix", + "arg-type": "with-path", "arg-type-windows": "enable", "lib-depends-unix": [ "libzip" diff --git a/config/lib.json b/config/lib.json index 28775535..13f886ca 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", @@ -185,14 +186,15 @@ }, "grpc": { "source": "grpc", - "static-libs-unix": [ - "libgrpc.a", - "libcares.a" + "pkg-configs": [ + "grpc" ], "lib-depends": [ "zlib", - "openssl" + "openssl", + "libcares" ], + "provide-pre-built": true, "frameworks": [ "CoreFoundation" ] @@ -200,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": { @@ -221,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", @@ -267,9 +268,9 @@ }, "ldap": { "source": "ldap", - "static-libs-unix": [ - "libldap.a", - "liblber.a" + "pkg-configs": [ + "ldap", + "lber" ], "lib-depends": [ "openssl", @@ -422,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", @@ -556,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", @@ -737,8 +738,8 @@ "openssl": { "source": "openssl", "static-libs-unix": [ - "libssl.a", - "libcrypto.a" + "libcrypto.a", + "libssl.a" ], "static-libs-windows": [ "libssl.lib", @@ -753,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 48e72b11..becd0f43 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/Extension.php b/src/SPC/builder/Extension.php index 8efd4569..79aed284 100644 --- a/src/SPC/builder/Extension.php +++ b/src/SPC/builder/Extension.php @@ -84,13 +84,15 @@ class Extension */ public function getEnableArg(bool $shared = false): string { + $escapedPath = str_replace("'", '', escapeshellarg(BUILD_ROOT_PATH)) !== BUILD_ROOT_PATH || str_contains(BUILD_ROOT_PATH, ' ') ? escapeshellarg(BUILD_ROOT_PATH) : BUILD_ROOT_PATH; $_name = str_replace('_', '-', $this->name); return match ($arg_type = Config::getExt($this->name, 'arg-type', 'enable')) { 'enable' => '--enable-' . $_name . ($shared ? '=shared' : '') . ' ', + 'enable-path' => '--enable-' . $_name . '=' . ($shared ? 'shared,' : '') . $escapedPath . ' ', 'with' => '--with-' . $_name . ($shared ? '=shared' : '') . ' ', - 'with-prefix' => '--with-' . $_name . '=' . ($shared ? 'shared,' : '') . '"' . BUILD_ROOT_PATH . '" ', + 'with-path' => '--with-' . $_name . '=' . ($shared ? 'shared,' : '') . $escapedPath . ' ', 'none', 'custom' => '', - default => throw new WrongUsageException("argType does not accept {$arg_type}, use [enable/with/with-prefix] ."), + default => throw new WrongUsageException("argType does not accept {$arg_type}, use [enable/with/with-path] ."), }; } diff --git a/src/SPC/builder/LibraryBase.php b/src/SPC/builder/LibraryBase.php index b97b5929..5f03d72c 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 + $replace_item_file = BUILD_ROOT_PATH . '/.spc-extract-placeholder.json'; + if (!file_exists($replace_item_file)) { + return; + } + $replace_items = json_decode(file_get_contents($replace_item_file), true); + if (!is_array($replace_items)) { + throw new RuntimeException('Invalid placeholder file: ' . $replace_item_file); + } + $placeholders = get_pack_replace(); + // replace placeholders in BUILD_ROOT_PATH + foreach ($replace_items as $item) { + $filepath = BUILD_ROOT_PATH . "/{$item}"; + FileSystem::replaceFileStr( + $filepath, + array_values($placeholders), + array_keys($placeholders), + ); + } + // remove placeholder file + unlink($replace_item_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/extension/grpc.php b/src/SPC/builder/extension/grpc.php index 53eae210..bda36004 100644 --- a/src/SPC/builder/extension/grpc.php +++ b/src/SPC/builder/extension/grpc.php @@ -5,36 +5,47 @@ declare(strict_types=1); namespace SPC\builder\extension; use SPC\builder\Extension; -use SPC\builder\macos\MacOSBuilder; use SPC\builder\windows\WindowsBuilder; use SPC\store\FileSystem; use SPC\util\CustomExt; use SPC\util\GlobalEnvManager; +use SPC\util\SPCConfigUtil; +use SPC\util\SPCTarget; #[CustomExt('grpc')] class grpc extends Extension { public function patchBeforeBuildconf(): bool { - // soft link to the grpc source code if ($this->builder instanceof WindowsBuilder) { - // not support windows yet throw new \RuntimeException('grpc extension does not support windows yet'); } - if (!is_link(SOURCE_PATH . '/php-src/ext/grpc')) { - if (is_dir($this->builder->getLib('grpc')->getSourceDir() . '/src/php/ext/grpc')) { - shell()->exec('ln -s ' . $this->builder->getLib('grpc')->getSourceDir() . '/src/php/ext/grpc ' . SOURCE_PATH . '/php-src/ext/grpc'); - } elseif (is_dir(BUILD_ROOT_PATH . '/grpc_php_ext_src')) { - shell()->exec('ln -s ' . BUILD_ROOT_PATH . '/grpc_php_ext_src ' . SOURCE_PATH . '/php-src/ext/grpc'); - } else { - throw new \RuntimeException('Cannot find grpc source code'); - } - $macos = $this->builder instanceof MacOSBuilder ? "\n" . ' LDFLAGS="$LDFLAGS -framework CoreFoundation"' : ''; - FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/ext/grpc/config.m4', '/GRPC_LIBDIR=.*$/m', 'GRPC_LIBDIR=' . BUILD_LIB_PATH . $macos); - FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/ext/grpc/config.m4', '/SEARCH_PATH=.*$/m', 'SEARCH_PATH="' . BUILD_ROOT_PATH . '"'); - return true; + if (file_exists(SOURCE_PATH . '/php-src/ext/grpc')) { + return false; } - return false; + // soft link to the grpc source code + if (is_dir($this->source_dir . '/src/php/ext/grpc')) { + shell()->exec('ln -s ' . $this->source_dir . '/src/php/ext/grpc ' . SOURCE_PATH . '/php-src/ext/grpc'); + } else { + throw new \RuntimeException('Cannot find grpc source code'); + } + if (SPCTarget::getTargetOS() === 'Darwin') { + FileSystem::replaceFileRegex( + SOURCE_PATH . '/php-src/ext/grpc/config.m4', + '/GRPC_LIBDIR=.*$/m', + 'GRPC_LIBDIR=' . BUILD_LIB_PATH . "\n" . 'LDFLAGS="$LDFLAGS -framework CoreFoundation"' + ); + } + return true; + } + + public function patchBeforeConfigure(): bool + { + $util = new SPCConfigUtil($this->builder, ['libs_only_deps' => true]); + $config = $util->config(['grpc']); + $libs = $config['libs']; + FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/configure', '-lgrpc', $libs); + return true; } public function patchBeforeMake(): bool @@ -44,9 +55,4 @@ class grpc extends Extension GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes'); return true; } - - 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/freebsd/BSDBuilder.php b/src/SPC/builder/freebsd/BSDBuilder.php index b51e7b2c..ba04675c 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 8983cab7..cf38474f 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; use SPC\util\SPCTarget; class LinuxBuilder extends UnixBuilderBase @@ -50,17 +51,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); @@ -97,13 +87,13 @@ class LinuxBuilder extends UnixBuilderBase $enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED; $enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP; - $mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : ''; // prepare build php envs + $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')); $envs_build_php = SystemUtil::makeEnvVarString([ 'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'), 'CPPFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS'), 'LDFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS'), - 'LIBS' => $mimallocLibs . SPCTarget::getRuntimeLibs(), + 'LIBS' => $config['libs'], ]); $embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static'; @@ -341,9 +331,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') . ' ' . SPCTarget::getRuntimeLibs(), + 'EXTRA_LIBS' => $config['libs'] . ' ' . SPCTarget::getRuntimeLibs(), 'EXTRA_LDFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), 'EXTRA_LDFLAGS_PROGRAM' => SPCTarget::isStatic() ? '-all-static -pie' : '-pie', ]; 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 625af810..161182cb 100644 --- a/src/SPC/builder/linux/library/openssl.php +++ b/src/SPC/builder/linux/library/openssl.php @@ -64,8 +64,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}" . 'enable-pie ' . @@ -74,28 +74,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 f6e1fb64..bcf9e750 100644 --- a/src/SPC/builder/macos/MacOSBuilder.php +++ b/src/SPC/builder/macos/MacOSBuilder.php @@ -12,7 +12,7 @@ use SPC\exception\WrongUsageException; use SPC\store\FileSystem; use SPC\store\SourcePatcher; use SPC\util\GlobalEnvManager; -use SPC\util\SPCTarget; +use SPC\util\SPCConfigUtil; class MacOSBuilder extends UnixBuilderBase { @@ -34,7 +34,7 @@ class MacOSBuilder extends UnixBuilderBase // ---------- set necessary compile vars ---------- // concurrency - $this->concurrency = (int) getenv('SPC_CONCURRENCY'); + $this->concurrency = intval(getenv('SPC_CONCURRENCY')); // cflags $this->arch_c_flags = getenv('SPC_DEFAULT_C_FLAGS'); $this->arch_cxx_flags = getenv('SPC_DEFAULT_CXX_FLAGS'); @@ -132,7 +132,7 @@ class MacOSBuilder extends UnixBuilderBase 'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'), 'CPPFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS'), 'LDFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS'), - 'LIBS' => $mimallocLibs . SPCTarget::getRuntimeLibs(), + 'LIBS' => $mimallocLibs . getenv('SPC_CMD_VAR_PHP_CONFIGURE_LIBS'), ]); if ($this->getLib('postgresql')) { @@ -292,9 +292,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') . ' ' . SPCTarget::getRuntimeLibs(), + '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 b1688689..151ace1b 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 3488dc8e..930fb714 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; @@ -29,85 +26,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 @@ -233,8 +151,9 @@ abstract class UnixBuilderBase extends BuilderBase if (!file_exists($frankenphp)) { throw new RuntimeException('FrankenPHP binary not found: ' . $frankenphp); } + $prefix = PHP_OS_FAMILY === 'Darwin' ? 'DYLD_' : 'LD_'; [$ret, $output] = shell() - ->setEnv(['LD_LIBRARY_PATH' => BUILD_LIB_PATH]) + ->setEnv(["{$prefix}LIBRARY_PATH" => BUILD_LIB_PATH]) ->execWithResult("{$frankenphp} version"); if ($ret !== 0 || !str_contains(implode('', $output), 'FrankenPHP')) { throw new RuntimeException('FrankenPHP failed sanity check: ret[' . $ret . ']. out[' . implode('', $output) . ']'); 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/grpc.php b/src/SPC/builder/unix/library/grpc.php index a7472448..f8fed3d9 100644 --- a/src/SPC/builder/unix/library/grpc.php +++ b/src/SPC/builder/unix/library/grpc.php @@ -4,22 +4,55 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\builder\linux\SystemUtil; use SPC\store\FileSystem; +use SPC\util\executor\UnixCMakeExecutor; +use SPC\util\SPCTarget; trait grpc { + public function patchBeforeBuild(): bool + { + FileSystem::replaceFileStr( + $this->source_dir . '/third_party/re2/util/pcre.h', + ["#define UTIL_PCRE_H_\n#include ", '#define UTIL_PCRE_H_'], + ['#define UTIL_PCRE_H_', "#define UTIL_PCRE_H_\n#include "], + ); + return true; + } + protected function build(): void { - shell()->cd($this->source_dir) - ->exec('EXTRA_DEFINES=GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK EMBED_OPENSSL=false CXXFLAGS="-L' . BUILD_LIB_PATH . ' -I' . BUILD_INCLUDE_PATH . '" make static -j' . $this->builder->concurrency); - copy($this->source_dir . '/libs/opt/libgrpc.a', BUILD_LIB_PATH . '/libgrpc.a'); - copy($this->source_dir . '/libs/opt/libboringssl.a', BUILD_LIB_PATH . '/libboringssl.a'); - if (!file_exists(BUILD_LIB_PATH . '/libcares.a')) { - copy($this->source_dir . '/libs/opt/libcares.a', BUILD_LIB_PATH . '/libcares.a'); + $cmake = UnixCMakeExecutor::create($this) + ->setBuildDir("{$this->source_dir}/avoid_BUILD_file_conflict") + ->addConfigureArgs( + '-DgRPC_INSTALL_BINDIR=' . BUILD_BIN_PATH, + '-DgRPC_INSTALL_LIBDIR=' . BUILD_LIB_PATH, + '-DgRPC_INSTALL_SHAREDIR=' . BUILD_ROOT_PATH . '/share/grpc', + '-DCMAKE_C_FLAGS="-DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK -L' . BUILD_LIB_PATH . ' -I' . BUILD_INCLUDE_PATH . '"', + '-DCMAKE_CXX_FLAGS="-DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK -L' . BUILD_LIB_PATH . ' -I' . BUILD_INCLUDE_PATH . '"', + '-DgRPC_BUILD_CODEGEN=OFF', + '-DgRPC_DOWNLOAD_ARCHIVES=OFF', + '-DgRPC_BUILD_TESTS=OFF', + // providers + '-DgRPC_ZLIB_PROVIDER=package', + '-DgRPC_CARES_PROVIDER=package', + '-DgRPC_SSL_PROVIDER=package', + ); + + if (PHP_OS_FAMILY === 'Linux' && SPCTarget::isStatic() && !SystemUtil::isMuslDist()) { + $cmake->addConfigureArgs( + '-DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++"', + '-DCMAKE_SHARED_LINKER_FLAGS="-static-libgcc -static-libstdc++"', + '-DCMAKE_CXX_STANDARD_LIBRARIES="-static-libgcc -static-libstdc++"', + ); } - FileSystem::copyDir($this->source_dir . '/include/grpc', BUILD_INCLUDE_PATH . '/grpc'); - FileSystem::copyDir($this->source_dir . '/include/grpc++', BUILD_INCLUDE_PATH . '/grpc++'); - FileSystem::copyDir($this->source_dir . '/include/grpcpp', BUILD_INCLUDE_PATH . '/grpcpp'); - FileSystem::copyDir($this->source_dir . '/src/php/ext/grpc', BUILD_ROOT_PATH . '/grpc_php_ext_src'); + + $cmake->build(); + + $re2Content = file_get_contents($this->source_dir . '/third_party/re2/re2.pc'); + $re2Content = 'prefix=' . BUILD_ROOT_PATH . "\nexec_prefix=\${prefix}\n" . $re2Content; + file_put_contents(BUILD_LIB_PATH . '/pkgconfig/re2.pc', $re2Content); + $this->patchPkgconfPrefix(['grpc++.pc', 'grpc.pc', 'grpc++_unsecure.pc', 'grpc_unsecure.pc', 're2.pc']); } } 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 90c63f89..238997da 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..366ef71e 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_replace(); + 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,93 @@ 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); + // additional OS-specific libraries (e.g. macOS -lresolv) // embed - $libs = trim("-lphp -lc {$libs}"); if ($extra_libs = SPCTarget::getRuntimeLibs()) { $libs .= " {$extra_libs}"; } - // 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}"; } - $libs .= ' ' . (getenv('SPC_EXTRA_LIBS') ?: ''); + // 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) { + // 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); + } + 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)) !== '') { + $arr = explode(' ', $pc_cflags); + $arr = array_unique($arr); + $arr = array_filter($arr, fn ($x) => !str_starts_with($x, 'SHELL:-Xarch_')); + $pc_cflags = implode(' ', $arr); + $includes[] = $pc_cflags; + } + } + $includes = array_unique($includes); + return implode(' ', $includes); } private function getLdflagsString(): string @@ -100,55 +163,62 @@ 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) { + // 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]; + } + // convert all static-libs to short names $libs = Config::getLib($library, 'static-libs', []); foreach ($libs as $lib) { - if ($withDependencies) { - $noExt = str_replace('.a', '', $lib); - $requiredLibs = []; - $pkgconfFile = BUILD_LIB_PATH . "/pkgconfig/{$noExt}.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); + // 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); } - if (PHP_OS_FAMILY !== 'Darwin') { - continue; + // add frameworks for macOS + if (SPCTarget::getTargetOS() === 'Darwin') { + $frameworks = array_merge($frameworks, Config::getLib($library, 'frameworks', [])); } - foreach (Config::getLib($library, 'frameworks', []) as $fw) { + } + + // post-process + $lib_names = array_filter($lib_names, fn ($x) => $x !== ''); + $lib_names = array_reverse(array_unique($lib_names)); + $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 @@ -160,6 +230,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/executor/UnixAutoconfExecutor.php b/src/SPC/util/executor/UnixAutoconfExecutor.php index 1c9f496e..8f923b1f 100644 --- a/src/SPC/util/executor/UnixAutoconfExecutor.php +++ b/src/SPC/util/executor/UnixAutoconfExecutor.php @@ -12,7 +12,7 @@ use SPC\util\UnixShell; class UnixAutoconfExecutor extends Executor { - protected ?UnixShell $shell = null; + protected UnixShell $shell; protected array $configure_args = []; diff --git a/src/SPC/util/executor/UnixCMakeExecutor.php b/src/SPC/util/executor/UnixCMakeExecutor.php index b9c7ef58..71a81951 100644 --- a/src/SPC/util/executor/UnixCMakeExecutor.php +++ b/src/SPC/util/executor/UnixCMakeExecutor.php @@ -5,25 +5,37 @@ declare(strict_types=1); namespace SPC\util\executor; use Closure; +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\WrongUsageException; use SPC\store\FileSystem; +use SPC\util\UnixShell; /** * Unix-like OS cmake command executor. */ class UnixCMakeExecutor extends Executor { - protected ?string $build_dir = null; + protected UnixShell $shell; protected array $configure_args = []; + protected ?string $build_dir = null; + protected ?array $custom_default_args = null; protected int $steps = 3; protected bool $reset = true; + public function __construct(protected BSDLibraryBase|LinuxLibraryBase|MacOSLibraryBase $library) + { + parent::__construct($library); + $this->initShell(); + } + public function build(string $build_pos = '..'): void { // set cmake dir @@ -33,17 +45,16 @@ class UnixCMakeExecutor extends Executor FileSystem::resetDir($this->build_dir); } - // prepare shell - $shell = shell()->cd($this->build_dir)->initializeEnv($this->library); + $this->shell = $this->shell->cd($this->build_dir); // config - $this->steps >= 1 && $shell->exec("cmake {$this->getConfigureArgs()} {$this->getDefaultCMakeArgs()} {$build_pos}"); + $this->steps >= 1 && $this->shell->exec("cmake {$this->getConfigureArgs()} {$this->getDefaultCMakeArgs()} {$build_pos}"); // make - $this->steps >= 2 && $shell->exec("cmake --build . -j {$this->library->getBuilder()->concurrency}"); + $this->steps >= 2 && $this->shell->exec("cmake --build . -j {$this->library->getBuilder()->concurrency}"); // install - $this->steps >= 3 && $shell->exec('make install'); + $this->steps >= 3 && $this->shell->exec('make install'); } /** @@ -77,6 +88,12 @@ class UnixCMakeExecutor extends Executor return $this; } + public function appendEnv(array $env): static + { + $this->shell->appendEnv($env); + return $this; + } + /** * To build steps. * @@ -209,4 +226,9 @@ CMAKE; FileSystem::writeFile(SOURCE_PATH . '/toolchain.cmake', $toolchain); return $created = realpath(SOURCE_PATH . '/toolchain.cmake'); } + + private function initShell(): void + { + $this->shell = shell()->initializeEnv($this->library); + } } diff --git a/src/globals/functions.php b/src/globals/functions.php index 998b2d1c..b45d8c5e 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_replace(): 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/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