diff --git a/config/env.ini b/config/env.ini index 4b9f294ad..0fbcd6934 100644 --- a/config/env.ini +++ b/config/env.ini @@ -34,6 +34,7 @@ ; SPC_LINUX_DEFAULT_CC: the default compiler for linux. (For alpine linux: `gcc`, default: `$GNU_ARCH-linux-musl-gcc`) ; SPC_LINUX_DEFAULT_CXX: the default c++ compiler for linux. (For alpine linux: `g++`, default: `$GNU_ARCH-linux-musl-g++`) ; SPC_LINUX_DEFAULT_AR: the default archiver for linux. (For alpine linux: `ar`, default: `$GNU_ARCH-linux-musl-ar`) +; SPC_EXTRA_PHP_VARS: the extra vars for building php, used in `configure` and `make` command. [global] ; Build concurrency for make -jN, default is CPU_COUNT, this value are used in every libs. diff --git a/config/ext.json b/config/ext.json index 188aea581..68612fe8a 100644 --- a/config/ext.json +++ b/config/ext.json @@ -759,11 +759,9 @@ }, "type": "builtin", "arg-type": "custom", - "lib-depends": [ - "libxml2" - ], - "ext-depends-windows": [ - "xml" + "ext-depends": [ + "libxml", + "session" ] }, "sockets": { diff --git a/config/lib.json b/config/lib.json index 37faca3c4..ac2c5705e 100644 --- a/config/lib.json +++ b/config/lib.json @@ -202,6 +202,7 @@ "openssl", "libcares" ], + "cpp-library": true, "provide-pre-built": true, "frameworks": [ "CoreFoundation" @@ -230,6 +231,7 @@ }, "imagemagick": { "source": "imagemagick", + "cpp-library": true, "pkg-configs": [ "Magick++-7.Q16HDRI", "MagickCore-7.Q16HDRI", @@ -822,6 +824,7 @@ }, "snappy": { "source": "snappy", + "cpp-library": true, "static-libs-unix": [ "libsnappy.a" ], @@ -867,6 +870,7 @@ }, "watcher": { "source": "watcher", + "cpp-library": true, "static-libs-unix": [ "libwatcher-c.a" ], diff --git a/config/source.json b/config/source.json index 4a0e0fdb5..cab584604 100644 --- a/config/source.json +++ b/config/source.json @@ -324,7 +324,7 @@ }, "grpc": { "type": "git", - "rev": "v1.68.x", + "rev": "v1.75.x", "url": "https://github.com/grpc/grpc.git", "provide-pre-built": true, "license": { diff --git a/src/SPC/builder/BuilderBase.php b/src/SPC/builder/BuilderBase.php index e6e27cda9..fdba936d7 100644 --- a/src/SPC/builder/BuilderBase.php +++ b/src/SPC/builder/BuilderBase.php @@ -128,27 +128,6 @@ public function getExts(bool $including_shared = true): array return array_filter($this->exts, fn ($ext) => $ext->isBuildStatic()); } - /** - * Check if there is a cpp extensions or libraries. - */ - public function hasCpp(): bool - { - // judge cpp-extension - $exts = array_keys($this->getExts(false)); - foreach ($exts as $ext) { - if (Config::getExt($ext, 'cpp-extension', false) === true) { - return true; - } - } - $libs = array_keys($this->getLibs()); - foreach ($libs as $lib) { - if (Config::getLib($lib, 'cpp-library', false) === true) { - return true; - } - } - return false; - } - /** * Set libs only mode. * diff --git a/src/SPC/builder/Extension.php b/src/SPC/builder/Extension.php index ac7efd1ec..a8e875191 100644 --- a/src/SPC/builder/Extension.php +++ b/src/SPC/builder/Extension.php @@ -5,16 +5,11 @@ namespace SPC\builder; use SPC\exception\EnvironmentException; -use SPC\exception\FileSystemException; use SPC\exception\SPCException; use SPC\exception\ValidationException; use SPC\exception\WrongUsageException; use SPC\store\Config; use SPC\store\FileSystem; -use SPC\toolchain\ClangNativeToolchain; -use SPC\toolchain\GccNativeToolchain; -use SPC\toolchain\ToolchainManager; -use SPC\toolchain\ZigToolchain; use SPC\util\SPCConfigUtil; use SPC\util\SPCTarget; @@ -226,12 +221,24 @@ public function patchBeforeSharedConfigure(): bool public function patchBeforeSharedMake(): bool { $config = (new SPCConfigUtil($this->builder))->config([$this->getName()], array_map(fn ($l) => $l->getName(), $this->builder->getLibs())); - [$staticLibs] = $this->splitLibsIntoStaticAndShared($config['libs']); - FileSystem::replaceFileRegex( - $this->source_dir . '/Makefile', - '/^(.*_SHARED_LIBADD\s*=.*)$/m', - '$1 ' . trim($staticLibs) - ); + [$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']); + $lstdcpp = str_contains($sharedLibs, '-l:libstdc++.a') ? '-l:libstdc++.a' : null; + $lstdcpp ??= str_contains($sharedLibs, '-lstdc++') ? '-lstdc++' : ''; + + $makefileContent = file_get_contents($this->source_dir . '/Makefile'); + if (preg_match('/^(.*_SHARED_LIBADD\s*=\s*)(.*)$/m', $makefileContent, $matches)) { + $prefix = $matches[1]; + $currentLibs = trim($matches[2]); + $newLibs = trim("{$currentLibs} {$staticLibs} {$lstdcpp}"); + $deduplicatedLibs = deduplicate_flags($newLibs); + + FileSystem::replaceFileRegex( + $this->source_dir . '/Makefile', + '/^(.*_SHARED_LIBADD\s*=.*)$/m', + $prefix . $deduplicatedLibs + ); + } + if ($objs = getenv('SPC_EXTRA_RUNTIME_OBJECTS')) { FileSystem::replaceFileRegex( $this->source_dir . '/Makefile', @@ -405,42 +412,7 @@ public function buildShared(array $visited = []): void */ public function buildUnixShared(): void { - $config = (new SPCConfigUtil($this->builder))->config( - [$this->getName()], - array_map(fn ($l) => $l->getName(), $this->getLibraryDependencies(recursive: true)), - $this->builder->getOption('with-suggested-exts'), - $this->builder->getOption('with-suggested-libs'), - ); - [$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']); - $preStatic = PHP_OS_FAMILY === 'Darwin' ? '' : '-Wl,--start-group '; - $postStatic = PHP_OS_FAMILY === 'Darwin' ? '' : ' -Wl,--end-group '; - $env = [ - 'CFLAGS' => $config['cflags'], - 'CXXFLAGS' => $config['cflags'], - 'LDFLAGS' => $config['ldflags'], - 'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"), - 'LD_LIBRARY_PATH' => BUILD_LIB_PATH, - ]; - if (str_contains($env['LIBS'], '-lstdc++') && SPCTarget::getTargetOS() === 'Linux') { - if (ToolchainManager::getToolchainClass() === ZigToolchain::class) { - $env['SPC_COMPILER_EXTRA'] = '-lstdc++'; - } elseif (ToolchainManager::getToolchainClass() === GccNativeToolchain::class || ToolchainManager::getToolchainClass() === ClangNativeToolchain::class) { - try { - $content = FileSystem::readFile($this->source_dir . '/config.m4'); - if ($content && !str_contains($content, 'PHP_ADD_LIBRARY(stdc++')) { - $pattern = '/(PHP_NEW_EXTENSION\(' . $this->name . ',.*\))/m'; - $replacement = "$1\nPHP_ADD_LIBRARY(stdc++, 1, " . strtoupper($this->name) . '_SHARED_LIBADD)'; - FileSystem::replaceFileRegex( - $this->source_dir . '/config.m4', - $pattern, - $replacement - ); - } - } catch (FileSystemException) { - } - } - } - + $env = $this->getSharedExtensionEnv(); if ($this->patchBeforeSharedPhpize()) { logger()->info("Extension [{$this->getName()}] patched before shared phpize"); } @@ -455,13 +427,15 @@ public function buildUnixShared(): void logger()->info("Extension [{$this->getName()}] patched before shared configure"); } + $phpvars = getenv('SPC_EXTRA_PHP_VARS') ?: ''; + shell()->cd($this->source_dir) ->setEnv($env) ->appendEnv($this->getExtraEnv()) ->exec( './configure ' . $this->getUnixConfigureArg(true) . ' --with-php-config=' . BUILD_BIN_PATH . '/php-config ' . - '--enable-shared --disable-static' + "--enable-shared --disable-static {$phpvars}" ); if ($this->patchBeforeSharedMake()) { @@ -512,6 +486,30 @@ public function isBuildStatic(): bool return $this->build_static; } + /** + * Returns the environment variables a shared extension needs to be built. + * CFLAGS, CXXFLAGS, LDFLAGS and so on. + */ + protected function getSharedExtensionEnv(): array + { + $config = (new SPCConfigUtil($this->builder))->config( + [$this->getName()], + array_map(fn ($l) => $l->getName(), $this->getLibraryDependencies(recursive: true)), + $this->builder->getOption('with-suggested-exts'), + $this->builder->getOption('with-suggested-libs'), + ); + [$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']); + $preStatic = PHP_OS_FAMILY === 'Darwin' ? '' : '-Wl,--start-group '; + $postStatic = PHP_OS_FAMILY === 'Darwin' ? '' : ' -Wl,--end-group '; + return [ + 'CFLAGS' => $config['cflags'], + 'CXXFLAGS' => $config['cflags'], + 'LDFLAGS' => $config['ldflags'], + 'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"), + 'LD_LIBRARY_PATH' => BUILD_LIB_PATH, + ]; + } + protected function addLibraryDependency(string $name, bool $optional = false): void { $depLib = $this->builder->getLib($name); @@ -584,12 +582,12 @@ private function getLibraryDependencies(bool $recursive = false): array $added = 0; foreach ($ret as $depName => $dep) { foreach ($dep->getDependencies(true) as $depdepName => $depdep) { - if (!in_array($depdepName, array_keys($deps), true)) { + if (!array_key_exists($depdepName, $deps)) { $deps[$depdepName] = $depdep; ++$added; } } - if (!in_array($depName, array_keys($deps), true)) { + if (!array_key_exists($depName, $deps)) { $deps[$depName] = $dep; } } diff --git a/src/SPC/builder/extension/grpc.php b/src/SPC/builder/extension/grpc.php index 61af6c04d..7388eaffd 100644 --- a/src/SPC/builder/extension/grpc.php +++ b/src/SPC/builder/extension/grpc.php @@ -56,4 +56,11 @@ public function patchBeforeMake(): bool GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes'); return true; } + + protected function getSharedExtensionEnv(): array + { + $env = parent::getSharedExtensionEnv(); + $env['CPPFLAGS'] = $env['CXXFLAGS'] . ' -Wno-attributes'; + return $env; + } } diff --git a/src/SPC/builder/extension/simdjson.php b/src/SPC/builder/extension/simdjson.php index 71796ebee..914fd674f 100644 --- a/src/SPC/builder/extension/simdjson.php +++ b/src/SPC/builder/extension/simdjson.php @@ -6,6 +6,8 @@ use SPC\builder\Extension; use SPC\store\FileSystem; +use SPC\toolchain\ToolchainManager; +use SPC\toolchain\ZigToolchain; use SPC\util\CustomExt; #[CustomExt('simdjson')] @@ -17,7 +19,7 @@ public function patchBeforeBuildconf(): bool FileSystem::replaceFileRegex( SOURCE_PATH . '/php-src/ext/simdjson/config.m4', '/php_version=(`.*`)$/m', - 'php_version=' . strval($php_ver) + 'php_version=' . $php_ver ); FileSystem::replaceFileStr( SOURCE_PATH . '/php-src/ext/simdjson/config.m4', @@ -31,4 +33,18 @@ public function patchBeforeBuildconf(): bool ); return true; } + + public function getSharedExtensionEnv(): array + { + $env = parent::getSharedExtensionEnv(); + if (ToolchainManager::getToolchainClass() === ZigToolchain::class) { + $extra = getenv('SPC_COMPILER_EXTRA'); + if (!str_contains((string) $extra, '-lstdc++')) { + f_putenv('SPC_COMPILER_EXTRA=' . clean_spaces($extra . ' -lstdc++')); + } + $env['CFLAGS'] .= ' -Xclang -target-feature -Xclang +evex512'; + $env['CXXFLAGS'] .= ' -Xclang -target-feature -Xclang +evex512'; + } + return $env; + } } diff --git a/src/SPC/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php index b1b1ffc57..d62e97bd4 100644 --- a/src/SPC/builder/linux/LinuxBuilder.php +++ b/src/SPC/builder/linux/LinuxBuilder.php @@ -95,6 +95,8 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void // 'LIBS' => SPCTarget::getRuntimeLibs(), // do not pass static libraries here yet, they may contain polyfills for libc functions! ]); + $phpvars = getenv('SPC_EXTRA_PHP_VARS') ?: ''; + $embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static'; if ($embed_type !== 'static' && SPCTarget::isStatic()) { throw new WrongUsageException( @@ -116,6 +118,7 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void $json_74 . $zts . $maxExecutionTimers . + "{$phpvars} " . $this->makeStaticExtensionArgs() . ' ' )); diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php index 5942f90c2..0d123d1f0 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -291,11 +291,11 @@ protected function buildFrankenphp(): void } } $debugFlags = $this->getOption('no-strip') ? '-w -s ' : ''; - $extLdFlags = "-extldflags '-pie{$dynamic_exports}'"; + $extLdFlags = "-extldflags '-pie{$dynamic_exports} {$this->arch_ld_flags}'"; $muslTags = ''; $staticFlags = ''; if (SPCTarget::isStatic()) { - $extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000{$dynamic_exports}'"; + $extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000{$dynamic_exports} {$this->arch_ld_flags}'"; $muslTags = 'static_build,'; $staticFlags = '-static-pie'; } @@ -303,7 +303,6 @@ protected function buildFrankenphp(): void $config = (new SPCConfigUtil($this))->config($this->ext_list, $this->lib_list); $cflags = "{$this->arch_c_flags} {$config['cflags']} " . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'); $libs = $config['libs']; - $libs .= PHP_OS_FAMILY === 'Linux' ? ' -lrt' : ''; // Go's gcc driver doesn't automatically link against -lgcov or -lrt. Ugly, but necessary fix. if ((str_contains((string) getenv('SPC_DEFAULT_C_FLAGS'), '-fprofile') || str_contains((string) getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), '-fprofile')) && diff --git a/src/SPC/builder/unix/library/imagemagick.php b/src/SPC/builder/unix/library/imagemagick.php index 2d3125498..42064e2d8 100644 --- a/src/SPC/builder/unix/library/imagemagick.php +++ b/src/SPC/builder/unix/library/imagemagick.php @@ -12,6 +12,11 @@ trait imagemagick { protected function build(): void { + $original_ldflags = $this->builder->arch_ld_flags; + if (str_contains($this->builder->arch_ld_flags, '-Wl,--as-needed')) { + $this->builder->arch_ld_flags = str_replace('-Wl,--as-needed', '', $original_ldflags); + } + $ac = UnixAutoconfExecutor::create($this) ->optionalLib('libzip', ...ac_with_args('zip')) ->optionalLib('libjpeg', ...ac_with_args('jpeg')) @@ -32,7 +37,7 @@ protected function build(): void ); // special: linux-static target needs `-static` - $ldflags = SPCTarget::isStatic() ? ('-static -ldl') : '-ldl'; + $ldflags = SPCTarget::isStatic() ? '-static -ldl' : '-ldl'; // special: macOS needs -iconv $libs = SPCTarget::getTargetOS() === 'Darwin' ? '-liconv' : ''; @@ -45,6 +50,8 @@ protected function build(): void $ac->configure()->make(); + $this->builder->arch_ld_flags = $original_ldflags; + $filelist = [ 'ImageMagick.pc', 'ImageMagick-7.Q16HDRI.pc', diff --git a/src/SPC/command/DownloadCommand.php b/src/SPC/command/DownloadCommand.php index b9bae6a57..00bcc1948 100644 --- a/src/SPC/command/DownloadCommand.php +++ b/src/SPC/command/DownloadCommand.php @@ -106,7 +106,7 @@ public function handle(): int } // retry - $retry = intval($this->getOption('retry')); + $retry = (int) $this->getOption('retry'); f_putenv('SPC_DOWNLOAD_RETRIES=' . $retry); // Use shallow-clone can reduce git resource download @@ -265,7 +265,7 @@ private function downloadFromZip(string $path): int f_passthru((PHP_OS_FAMILY === 'Windows' ? 'rmdir /s /q ' : 'rm -rf ') . DOWNLOAD_PATH); } // unzip command check - if (PHP_OS_FAMILY !== 'Windows' && !$this->findCommand('unzip')) { + if (PHP_OS_FAMILY !== 'Windows' && !self::findCommand('unzip')) { $this->output->writeln('Missing unzip command, you need to install it first !'); $this->output->writeln('You can use "bin/spc doctor" command to check and install required tools'); return static::FAILURE; diff --git a/src/SPC/store/Downloader.php b/src/SPC/store/Downloader.php index d659253e9..3e50c93d2 100644 --- a/src/SPC/store/Downloader.php +++ b/src/SPC/store/Downloader.php @@ -65,19 +65,19 @@ public static function getLatestGithubTarball(string $name, array $source, strin url: "https://api.github.com/repos/{$source['repo']}/{$type}", hooks: [[CurlHook::class, 'setupGithubToken']], retries: self::getRetryAttempts() - ), true); + ), true, 512, JSON_THROW_ON_ERROR); $url = null; - for ($i = 0; $i < count($data); ++$i) { - if (($data[$i]['prerelease'] ?? false) === true && ($source['prefer-stable'] ?? false)) { + foreach ($data as $rel) { + if (($rel['prerelease'] ?? false) === true && ($source['prefer-stable'] ?? false)) { continue; } if (!($source['match'] ?? null)) { - $url = $data[$i]['tarball_url'] ?? null; + $url = $rel['tarball_url'] ?? null; break; } - if (preg_match('|' . $source['match'] . '|', $data[$i]['tarball_url'])) { - $url = $data[$i]['tarball_url']; + if (preg_match('|' . $source['match'] . '|', $rel['tarball_url'])) { + $url = $rel['tarball_url']; break; } } @@ -232,7 +232,8 @@ public static function downloadGit(string $name, string $url, string $branch, ?a $quiet = !defined('DEBUG_MODE') ? '-q --quiet' : ''; $git = SPC_GIT_EXEC; $shallow = defined('GIT_SHALLOW_CLONE') ? '--depth 1 --single-branch' : ''; - $recursive = ($submodules === null) ? '--recursive' : ''; + $recursive = ($submodules === null && defined('GIT_SHALLOW_CLONE')) ? '--recursive --shallow-submodules' : null; + $recursive ??= $submodules === null ? '--recursive' : ''; try { self::registerCancelEvent(function () use ($download_path) { @@ -243,8 +244,9 @@ public static function downloadGit(string $name, string $url, string $branch, ?a }); f_passthru("{$git} clone {$quiet} --config core.autocrlf=false --branch \"{$branch}\" {$shallow} {$recursive} \"{$url}\" \"{$download_path}\""); if ($submodules !== null) { + $depth_flag = defined('GIT_SHALLOW_CLONE') ? '--depth 1' : ''; foreach ($submodules as $submodule) { - f_passthru("cd \"{$download_path}\" && {$git} submodule update --init " . escapeshellarg($submodule)); + f_passthru("cd \"{$download_path}\" && {$git} submodule update --init {$depth_flag} " . escapeshellarg($submodule)); } } } catch (SPCException $e) { @@ -399,7 +401,7 @@ public static function downloadPackage(string $name, ?array $pkg = null, bool $f * Download source * * @param string $name source name - * @param null|array{ + * @param null|array{ * type: string, * repo: ?string, * url: ?string, @@ -414,7 +416,7 @@ public static function downloadPackage(string $name, ?array $pkg = null, bool $f * path: ?string, * text: ?string * } - * } $source source meta info: [type, path, rev, url, filename, regex, license] + * } $source source meta info: [type, path, rev, url, filename, regex, license] * @param bool $force Whether to force download (default: false) * @param int $download_as Lock source type (default: SPC_LOCK_SOURCE) */ diff --git a/src/SPC/store/scripts/zig-cc.sh b/src/SPC/store/scripts/zig-cc.sh index d4090a0f3..6b340bc1d 100644 --- a/src/SPC/store/scripts/zig-cc.sh +++ b/src/SPC/store/scripts/zig-cc.sh @@ -19,9 +19,15 @@ while [[ $# -gt 0 ]]; do ARG_ABS="$(realpath "$ARG" 2>/dev/null || true)" [[ "$ARG_ABS" == "$BUILDROOT_ABS" ]] && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem$ARG") ;; - -march=*|-mcpu=*) # replace -march=x86-64 with -march=x86_64 + -march=*|-mcpu=*) OPT_NAME="${1%%=*}" OPT_VALUE="${1#*=}" + # Skip armv8- flags entirely as Zig doesn't support them + if [[ "$OPT_VALUE" == armv8-* ]]; then + shift + continue + fi + # replace -march=x86-64 with -march=x86_64 OPT_VALUE="${OPT_VALUE//-/_}" PARSED_ARGS+=("${OPT_NAME}=${OPT_VALUE}") shift diff --git a/src/SPC/store/source/PhpSource.php b/src/SPC/store/source/PhpSource.php index c33e172f3..377a8e1da 100644 --- a/src/SPC/store/source/PhpSource.php +++ b/src/SPC/store/source/PhpSource.php @@ -16,11 +16,11 @@ public function fetch(bool $force = false, ?array $config = null, int $lock_as = { $major = defined('SPC_BUILD_PHP_VERSION') ? SPC_BUILD_PHP_VERSION : '8.4'; if ($major === '8.5') { - Downloader::downloadSource('php-src', ['type' => 'url', 'url' => 'https://downloads.php.net/~edorian/php-8.5.0beta1.tar.xz'], $force); + Downloader::downloadSource('php-src', ['type' => 'url', 'url' => 'https://downloads.php.net/~edorian/php-8.5.0beta3.tar.xz'], $force); } elseif ($major === 'git') { Downloader::downloadSource('php-src', ['type' => 'git', 'url' => 'https://github.com/php/php-src.git', 'rev' => 'master'], $force); } else { - Downloader::downloadSource('php-src', self::getLatestPHPInfo($major), $force); + Downloader::downloadSource('php-src', $this->getLatestPHPInfo($major), $force); } } @@ -33,7 +33,7 @@ public function getLatestPHPInfo(string $major_version): array // 查找最新的小版本号 $info = json_decode(Downloader::curlExec( url: "https://www.php.net/releases/index.php?json&version={$major_version}", - retries: intval(getenv('SPC_DOWNLOAD_RETRIES') ?: 0) + retries: (int) getenv('SPC_DOWNLOAD_RETRIES') ?: 0 ), true); if (!isset($info['version'])) { throw new DownloaderException("Version {$major_version} not found."); diff --git a/src/SPC/toolchain/ZigToolchain.php b/src/SPC/toolchain/ZigToolchain.php index 8f2e261e9..bb423db20 100644 --- a/src/SPC/toolchain/ZigToolchain.php +++ b/src/SPC/toolchain/ZigToolchain.php @@ -64,6 +64,11 @@ public function afterInit(): void $extra_libs = trim($extra_libs . ' -lunwind'); GlobalEnvManager::putenv("SPC_EXTRA_LIBS={$extra_libs}"); } + $cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: getenv('CFLAGS') ?: ''; + $has_avx512 = str_contains($cflags, '-mavx512') || str_contains($cflags, '-march=x86-64-v4'); + if (!$has_avx512) { + GlobalEnvManager::putenv('SPC_EXTRA_PHP_VARS=php_cv_have_avx512=no php_cv_have_avx512vbmi=no'); + } } public function getCompilerInfo(): ?string diff --git a/src/SPC/util/GlobalEnvManager.php b/src/SPC/util/GlobalEnvManager.php index 9f74d2df7..4ce3384dd 100644 --- a/src/SPC/util/GlobalEnvManager.php +++ b/src/SPC/util/GlobalEnvManager.php @@ -40,7 +40,12 @@ public static function init(): void if (is_unix()) { self::addPathIfNotExists(BUILD_BIN_PATH); self::addPathIfNotExists(PKG_ROOT_PATH . '/bin'); - self::putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . '/pkgconfig'); + $pkgConfigPath = getenv('PKG_CONFIG_PATH'); + if ($pkgConfigPath !== false) { + self::putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . "/pkgconfig:{$pkgConfigPath}"); + } else { + self::putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . '/pkgconfig'); + } } $ini = self::readIniFile(); diff --git a/src/SPC/util/SPCConfigUtil.php b/src/SPC/util/SPCConfigUtil.php index d19cc8583..f321ca102 100644 --- a/src/SPC/util/SPCConfigUtil.php +++ b/src/SPC/util/SPCConfigUtil.php @@ -6,6 +6,7 @@ use SPC\builder\BuilderBase; use SPC\builder\BuilderProvider; +use SPC\builder\Extension; use SPC\exception\WrongUsageException; use SPC\store\Config; use Symfony\Component\Console\Input\ArgvInput; @@ -87,7 +88,7 @@ public function config(array $extensions = [], array $libraries = [], bool $incl if (SPCTarget::getTargetOS() === 'Darwin') { $libs .= " {$this->getFrameworksString($extensions)}"; } - if ($this->builder->hasCpp()) { + if ($this->hasCpp($extensions, $libraries)) { $libcpp = SPCTarget::getTargetOS() === 'Darwin' ? '-lc++' : '-lstdc++'; $libs = str_replace($libcpp, '', $libs) . " {$libcpp}"; } @@ -123,6 +124,27 @@ public function config(array $extensions = [], array $libraries = [], bool $incl ]; } + private function hasCpp(array $extensions, array $libraries): bool + { + // judge cpp-extension + $builderExtNames = array_keys($this->builder->getExts(false)); + $exts = array_unique([...$builderExtNames, ...$extensions]); + + foreach ($exts as $ext) { + if (Config::getExt($ext, 'cpp-extension', false) === true) { + return true; + } + } + $builderLibNames = array_keys($this->builder->getLibs()); + $libs = array_unique([...$builderLibNames, ...$libraries]); + foreach ($libs as $lib) { + if (Config::getLib($lib, 'cpp-library', false) === true) { + return true; + } + } + return false; + } + private function getIncludesString(array $libraries): string { $base = BUILD_INCLUDE_PATH; diff --git a/src/globals/functions.php b/src/globals/functions.php index 711279600..da2d177f5 100644 --- a/src/globals/functions.php +++ b/src/globals/functions.php @@ -245,6 +245,23 @@ function clean_spaces(string $string): string return trim(preg_replace('/\s+/', ' ', $string)); } +/** + * Deduplicate flags in a string. Only the last occurence of each flag will be kept. + * E.g. `-lintl -lstdc++ -lphp -lstdc++` becomes `-lintl -lphp -lstdc++` + * + * @param string $flags the string containing flags to deduplicate + * @return string the deduplicated string with no duplicate flags + */ +function deduplicate_flags(string $flags): string +{ + $tokens = preg_split('/\s+/', trim($flags)); + + // Reverse, unique, reverse back - keeps last occurrence of duplicates + $deduplicated = array_reverse(array_unique(array_reverse($tokens))); + + return implode(' ', $deduplicated); +} + /** * Register a callback function to handle keyboard interrupts (Ctrl+C). * diff --git a/src/globals/test-extensions.php b/src/globals/test-extensions.php index 764cfc7d3..954cb4743 100644 --- a/src/globals/test-extensions.php +++ b/src/globals/test-extensions.php @@ -23,10 +23,10 @@ // test os (macos-13, macos-14, macos-15, ubuntu-latest, windows-latest are available) $test_os = [ - 'macos-13', // bin/spc for x86_64 - // 'macos-14', // bin/spc for arm64 + // '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-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-arm', // bin/spc-gnu-docker for arm64 @@ -56,7 +56,7 @@ // If you want to test shared extensions, add them below (comma separated, example `bcmath,openssl`). $shared_extensions = match (PHP_OS_FAMILY) { - 'Linux' => 'zip', + 'Linux' => 'grpc,imagick', 'Darwin' => '', 'Windows' => '', }; diff --git a/tests/SPC/builder/BuilderTest.php b/tests/SPC/builder/BuilderTest.php index d28bd4d33..b4b6258fd 100644 --- a/tests/SPC/builder/BuilderTest.php +++ b/tests/SPC/builder/BuilderTest.php @@ -62,12 +62,6 @@ public function testGetExtAndGetExts() $this->assertInstanceOf(Extension::class, $this->builder->getExt('mbregex')); } - public function testHasCpp() - { - // mbregex doesn't have cpp - $this->assertFalse($this->builder->hasCpp()); - } - public function testMakeExtensionArgs() { $this->assertStringContainsString('--enable-mbstring', $this->builder->makeStaticExtensionArgs());