From 4f7694267b919fbf411bd1f03147d9c4f62988ef Mon Sep 17 00:00:00 2001 From: henderkes Date: Sun, 10 May 2026 19:27:36 +0700 Subject: [PATCH] toolchain fixes forward port from v2-pgo --- config/env.ini | 6 + src/Package/Artifact/go_xcaddy.php | 2 +- src/Package/Artifact/zig.php | 147 ++++++++++++++++++ src/StaticPHP/Doctor/Item/LinuxMuslCheck.php | 15 +- src/StaticPHP/Package/PhpExtensionPackage.php | 58 ++++++- .../Runtime/Executor/UnixCMakeExecutor.php | 9 +- .../Toolchain/ClangBrewToolchain.php | 1 + .../Toolchain/ClangNativeToolchain.php | 1 + .../Toolchain/GccNativeToolchain.php | 1 + src/StaticPHP/Toolchain/ZigToolchain.php | 22 +-- src/globals/scripts/zig-cc.sh | 33 +++- 11 files changed, 260 insertions(+), 35 deletions(-) diff --git a/config/env.ini b/config/env.ini index acad2523..58668cc5 100644 --- a/config/env.ini +++ b/config/env.ini @@ -100,6 +100,7 @@ SPC_TARGET=${GNU_ARCH}-linux-musl CC=${SPC_DEFAULT_CC} CXX=${SPC_DEFAULT_CXX} AR=${SPC_DEFAULT_AR} +RANLIB=${SPC_DEFAULT_RANLIB} LD=${SPC_DEFAULT_LD} ; default compiler flags, used in CMake toolchain file, openssl and pkg-config build SPC_DEFAULT_CFLAGS="-fPIC -O3 -pipe -fno-plt -fno-semantic-interposition -fstack-clash-protection -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffunction-sections -fdata-sections" @@ -125,6 +126,8 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE SPC_CMD_VAR_PHP_MAKE_EXTRA_CXXFLAGS="-g -fstack-protector-strong -fno-ident -fPIE -fvisibility=hidden -fvisibility-inlines-hidden ${SPC_DEFAULT_CXXFLAGS}" ; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS="" +; EXTRA_LDFLAGS_PROGRAM for `make` php; appended only to SAPI executable links (cli/fpm/cgi/micro/embed). Used by PGO to inject -fprofile-use= without polluting libphp.{a,so}. +SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="" ; optional, path to openssl conf. This affects where openssl will look for the default CA. ; default on Debian/Alpine: /etc/ssl, default on RHEL: /etc/pki/tls @@ -140,6 +143,7 @@ SPC_USE_LLVM=system CC=${SPC_DEFAULT_CC} CXX=${SPC_DEFAULT_CXX} AR=${SPC_DEFAULT_AR} +RANLIB=${SPC_DEFAULT_RANLIB} LD=${SPC_DEFAULT_LD} ; default compiler flags, used in CMake toolchain file, openssl and pkg-config build SPC_DEFAULT_CFLAGS="--target=${MAC_ARCH}-apple-darwin -O3 -fno-plt -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffunction-sections -fdata-sections" @@ -163,5 +167,7 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -fvis SPC_CMD_VAR_PHP_MAKE_EXTRA_CXXFLAGS="-g -fstack-protector-strong -fno-ident -fpie -fvisibility=hidden -fvisibility-inlines-hidden -Werror=unknown-warning-option ${SPC_DEFAULT_CXXFLAGS}" ; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.dylib SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS="" +; EXTRA_LDFLAGS_PROGRAM for `make` php; appended only to SAPI executable links (cli/fpm/cgi/micro/embed). Used by PGO to inject -fprofile-use= without polluting libphp.{a,dylib}. +SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="" ; minimum compatible macOS version (LLVM vars, availability not guaranteed) MACOSX_DEPLOYMENT_TARGET=12.0 diff --git a/src/Package/Artifact/go_xcaddy.php b/src/Package/Artifact/go_xcaddy.php index ca61bd46..5a16712c 100644 --- a/src/Package/Artifact/go_xcaddy.php +++ b/src/Package/Artifact/go_xcaddy.php @@ -109,7 +109,7 @@ class go_xcaddy 'GOROOT' => "{$target_path}", 'GOBIN' => "{$target_path}/bin", 'GOPATH' => "{$target_path}/go", - ])->exec('CC=cc go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest'); + ])->exec('CGO_ENABLED=0 go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest'); GlobalEnvManager::addPathIfNotExists("{$target_path}/bin"); } } diff --git a/src/Package/Artifact/zig.php b/src/Package/Artifact/zig.php index 95520aa4..672c0cf7 100644 --- a/src/Package/Artifact/zig.php +++ b/src/Package/Artifact/zig.php @@ -12,6 +12,7 @@ use StaticPHP\Attribute\Artifact\CustomBinary; use StaticPHP\Attribute\Artifact\CustomBinaryCheckUpdate; use StaticPHP\Exception\DownloaderException; use StaticPHP\Runtime\SystemTarget; +use StaticPHP\Util\FileSystem; class zig { @@ -131,5 +132,151 @@ class zig chmod("{$target_path}/zig-ld.lld", 0755); chmod("{$target_path}/zig-ranlib", 0755); chmod("{$target_path}/zig-objcopy", 0755); + + // Build the clang runtime bits zig 0.15+ doesn't ship: profile runtime + // (so -fprofile-generate actually emits .profraw) and crtbegin/crtend + // (so shared libs get __dso_handle and the __cxa_finalize atexit hook). + // These get auto-linked by the zig-cc wrapper when the right flags fly past. + $this->buildClangRuntimeBits($target_path); + } + + /** + * Detect the bundled clang version and build the missing clang runtime + * archives into `/lib/`. Compiles from a 2 MB compiler-rt-.src + * tarball — far cheaper than fetching the 2 GB prebuilt LLVM tarball. + */ + private function buildClangRuntimeBits(string $zig_bin_dir): void + { + if (PHP_OS_FAMILY !== 'Linux') { + return; + } + $libDir = "{$zig_bin_dir}/lib"; + $profileLib = "{$libDir}/libclang_rt.profile.a"; + $crtBegin = "{$libDir}/clang_rt.crtbegin.o"; + $crtEnd = "{$libDir}/clang_rt.crtend.o"; + if (file_exists($profileLib) && file_exists($crtBegin) && file_exists($crtEnd)) { + return; + } + + $zig = "{$zig_bin_dir}/zig"; + $verLine = trim((string) shell_exec(escapeshellarg($zig) . ' cc --version 2>/dev/null')); + if (!preg_match('/clang version (\d+\.\d+\.\d+)/', $verLine, $m)) { + logger()->warning('[zig] could not detect bundled clang version; skipping runtime bit build (PGO + shared libs without __dso_handle will fail to link)'); + return; + } + $llvmVersion = $m[1]; + logger()->info("Building clang runtime bits for LLVM {$llvmVersion} (zig's bundled clang)"); + + $srcRoot = $this->fetchCompilerRtSource($llvmVersion); + if ($srcRoot === null) { + return; + } + + f_mkdir($libDir, recursive: true); + if (!file_exists($profileLib)) { + $this->buildProfileRuntime($zig, $srcRoot, $profileLib); + } + if (!file_exists($crtBegin) || !file_exists($crtEnd)) { + $this->buildCrtObjects($zig, $srcRoot, $crtBegin, $crtEnd); + } + FileSystem::removeDir($srcRoot); + } + + private function fetchCompilerRtSource(string $llvmVersion): ?string + { + $tarball = "compiler-rt-{$llvmVersion}.src.tar.xz"; + $url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-{$llvmVersion}/{$tarball}"; + $tarballPath = DOWNLOAD_PATH . '/' . $tarball; + if (!file_exists($tarballPath)) { + try { + default_shell()->executeCurlDownload($url, $tarballPath); + } catch (\Throwable $e) { + logger()->warning("[zig] failed to download {$tarball}: {$e->getMessage()}"); + return null; + } + } + $srcRoot = PKG_ROOT_PATH . "/compiler-rt-src-{$llvmVersion}"; + FileSystem::removeDir($srcRoot); + FileSystem::createDir($srcRoot); + try { + default_shell()->executeTarExtract($tarballPath, $srcRoot, 'xz'); + } catch (\Throwable $e) { + logger()->warning("[zig] failed to extract {$tarball}: {$e->getMessage()}"); + return null; + } + return $srcRoot; + } + + private function buildProfileRuntime(string $zig, string $srcRoot, string $libPath): void + { + $profileSrc = "{$srcRoot}/lib/profile"; + $profileInc = "{$srcRoot}/include"; + if (!is_dir($profileSrc)) { + logger()->warning("[zig] profile src dir missing at {$profileSrc} — PGO will not work"); + return; + } + $sources = array_merge( + glob("{$profileSrc}/*.c") ?: [], + glob("{$profileSrc}/*.cpp") ?: [] + ); + // Keep Linux-only compilation units; the others bring in OS-specific headers + // we can't satisfy without their respective SDKs. + $skip = ['/PlatformAIX', '/PlatformDarwin', '/PlatformFuchsia', '/PlatformOther', '/PlatformWindows', '/WindowsMMap']; + $sources = array_filter($sources, function ($f) use ($skip) { + foreach ($skip as $s) { + if (str_contains($f, $s)) { + return false; + } + } + return true; + }); + + $objDir = "{$srcRoot}/obj-profile"; + f_mkdir($objDir, recursive: true); + $cflags = '-c -O2 -fPIC -fvisibility=hidden ' . + '-I' . escapeshellarg($profileInc) . ' ' . + '-DCOMPILER_RT_HAS_ATOMICS=1 -DCOMPILER_RT_HAS_FCNTL_LCK=1 -DCOMPILER_RT_HAS_UNAME=1'; + $objs = []; + foreach ($sources as $src) { + $obj = $objDir . '/' . pathinfo($src, PATHINFO_FILENAME) . '.o'; + $cmd = escapeshellarg($zig) . ' cc ' . $cflags . ' -o ' . escapeshellarg($obj) . ' ' . escapeshellarg($src) . ' 2>&1'; + if (!$this->runZigCmd($cmd, $obj, "failed to compile {$src}")) { + return; + } + $objs[] = $obj; + } + $arCmd = escapeshellarg($zig) . ' ar rcs ' . escapeshellarg($libPath) . ' ' . implode(' ', array_map('escapeshellarg', $objs)) . ' 2>&1'; + if (!$this->runZigCmd($arCmd, $libPath, 'zig ar failed')) { + return; + } + logger()->info('[zig] libclang_rt.profile.a installed (' . filesize($libPath) . ' bytes)'); + } + + private function buildCrtObjects(string $zig, string $srcRoot, string $crtBegin, string $crtEnd): void + { + $beginSrc = "{$srcRoot}/lib/builtins/crtbegin.c"; + $endSrc = "{$srcRoot}/lib/builtins/crtend.c"; + if (!is_file($beginSrc) || !is_file($endSrc)) { + logger()->error("[zig] crtbegin/crtend source missing under {$srcRoot}/lib/builtins — shared libs will lack __dso_handle"); + return; + } + $cflags = '-c -O2 -fPIC -fvisibility=hidden -DCRT_HAS_INITFINI_ARRAY'; + foreach ([[$beginSrc, $crtBegin], [$endSrc, $crtEnd]] as [$src, $dst]) { + $cmd = escapeshellarg($zig) . ' cc ' . $cflags . ' -o ' . escapeshellarg($dst) . ' ' . escapeshellarg($src) . ' 2>&1'; + if (!$this->runZigCmd($cmd, $dst, "failed to compile {$src}")) { + return; + } + } + logger()->info('[zig] clang_rt.crtbegin.o + clang_rt.crtend.o installed (' . filesize($crtBegin) . ' + ' . filesize($crtEnd) . ' bytes)'); + } + + private function runZigCmd(string $cmd, string $dst, string $errPrefix): bool + { + exec($cmd, $out, $rc); + if ($rc !== 0 || !is_file($dst)) { + logger()->warning("[zig] {$errPrefix}: " . implode("\n", $out)); + return false; + } + return true; } } diff --git a/src/StaticPHP/Doctor/Item/LinuxMuslCheck.php b/src/StaticPHP/Doctor/Item/LinuxMuslCheck.php index df3b5241..1fe7ed00 100644 --- a/src/StaticPHP/Doctor/Item/LinuxMuslCheck.php +++ b/src/StaticPHP/Doctor/Item/LinuxMuslCheck.php @@ -73,13 +73,20 @@ class LinuxMuslCheck $prefix = 'sudo '; logger()->warning('Current user is not root, using sudo for running command'); } + $sysEnv = ['CC' => 'gcc', 'CXX' => 'g++', 'AR' => 'ar', 'LD' => 'ld', 'RANLIB' => 'ranlib']; + $envFlags = ''; + foreach ($sysEnv as $k => $v) { + $envFlags .= "{$k}={$v} "; + } + $envFlags = rtrim($envFlags); $shell = shell()->cd(SOURCE_PATH . '/musl-wrapper') - ->exec('CC=gcc CXX=g++ AR=ar LD=ld ./configure --disable-gcc-wrapper') - ->exec('CC=gcc CXX=g++ AR=ar LD=ld make -j'); + ->setEnv($sysEnv) + ->exec('./configure --disable-gcc-wrapper') + ->exec('make -j'); if ($prefix !== '') { - f_passthru('cd ' . SOURCE_PATH . "/musl-wrapper && CC=gcc CXX=g++ AR=ar LD=ld {$prefix}make install"); + f_passthru('cd ' . SOURCE_PATH . "/musl-wrapper && {$envFlags} {$prefix}make install"); } else { - $shell->exec("CC=gcc CXX=g++ AR=ar LD=ld {$prefix}make install"); + $shell->exec("{$prefix}make install"); } return true; } diff --git a/src/StaticPHP/Package/PhpExtensionPackage.php b/src/StaticPHP/Package/PhpExtensionPackage.php index 1201496d..6fc5757e 100644 --- a/src/StaticPHP/Package/PhpExtensionPackage.php +++ b/src/StaticPHP/Package/PhpExtensionPackage.php @@ -274,14 +274,20 @@ class PhpExtensionPackage extends Package $compiler_extra = trim($compiler_extra . ' -lcompiler_rt'); GlobalEnvManager::putenv("SPC_COMPILER_EXTRA={$compiler_extra}"); } - $config = (new SPCConfigUtil())->getExtensionConfig($this); + // include_suggests so transitive optional libs (e.g. librdkafka → lz4/zstd/ssl) are in the link line + $config = (new SPCConfigUtil())->getExtensionConfig($this, include_suggests: true); [$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']); $preStatic = PHP_OS_FAMILY === 'Darwin' ? '' : '-Wl,--start-group '; $postStatic = PHP_OS_FAMILY === 'Darwin' ? '' : ' -Wl,--end-group '; + // -Wl,-Bsymbolic: bind zend_* refs to the .so's own copies, not via global lookup + $ldflags = (string) $config['ldflags']; + if (PHP_OS_FAMILY !== 'Darwin' && !str_contains($ldflags, '-Wl,-Bsymbolic')) { + $ldflags = clean_spaces($ldflags . ' -Wl,-Bsymbolic'); + } return [ 'CFLAGS' => $config['cflags'], 'CXXFLAGS' => $config['cflags'], - 'LDFLAGS' => $config['ldflags'], + 'LDFLAGS' => $ldflags, 'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"), 'LD_LIBRARY_PATH' => BUILD_LIB_PATH, ]; @@ -303,10 +309,11 @@ class PhpExtensionPackage extends Package public function configureForUnix(array $env, PhpExtensionPackage $package): void { $phpvars = getenv('SPC_EXTRA_PHP_VARS') ?: ''; + // CustomPhpConfigureArg keys are OS names ('Linux'/'Darwin'), not platform strings shell()->cd($package->getSourceDir()) ->setEnv($env) ->exec( - './configure ' . $this->getPhpConfigureArg(SystemTarget::getCurrentPlatformString(), true) . + './configure ' . $this->getPhpConfigureArg(SystemTarget::getTargetOS(), true) . ' --with-php-config=' . BUILD_BIN_PATH . '/php-config ' . "--enable-shared --disable-static {$phpvars}" ); @@ -318,6 +325,8 @@ class PhpExtensionPackage extends Package #[Stage] public function makeForUnix(array $env, PhpExtensionPackage $package, PackageBuilder $builder): void { + // phpize Makefile's _SHARED_LIBADD line misses our static archives — splice them in + $package->patchSharedLibAdd(); shell()->cd($package->getSourceDir()) ->setEnv($env) ->exec('make clean') @@ -325,6 +334,34 @@ class PhpExtensionPackage extends Package ->exec('make install'); } + public function patchSharedLibAdd(): void + { + // include_suggests so transitive optional libs (e.g. librdkafka → lz4/zstd/ssl) are in the link line + $config = (new SPCConfigUtil())->getExtensionConfig($this, include_suggests: true); + [$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']); + $lstdcpp = str_contains($sharedLibs, '-l:libstdc++.a') + ? '-l:libstdc++.a' + : (str_contains($sharedLibs, '-lstdc++') ? '-lstdc++' : ''); + + $makefile = $this->getSourceDir() . '/Makefile'; + if (!is_file($makefile)) { + return; + } + $content = (string) file_get_contents($makefile); + if (!preg_match('/^(.*_SHARED_LIBADD\s*=\s*)(.*)$/m', $content, $m)) { + return; + } + $prefix = $m[1]; + $current = trim($m[2]); + $merged = clean_spaces("{$current} {$staticLibs} {$lstdcpp}"); + $merged = deduplicate_flags($merged); + \StaticPHP\Util\FileSystem::replaceFileRegex( + $makefile, + '/^(.*_SHARED_LIBADD\s*=.*)$/m', + $prefix . $merged + ); + } + /** * Build shared extension on Unix-like systems. * Only for internal calling. For external use, call buildShared() instead. @@ -333,6 +370,21 @@ class PhpExtensionPackage extends Package */ public function buildSharedForUnix(PackageBuilder $builder): void { + // skip virtual addons (arg-type=none + display-name → owning ext); the parent ext built it + $argType = $this->extension_config['arg-type'] ?? null; + $displayName = $this->extension_config['display-name'] ?? null; + if ($argType === 'none' && $displayName !== null && $displayName !== $this->getExtensionName()) { + logger()->info("Skipping virtual extension [{$this->getName()}] — it's part of [{$displayName}]."); + return; + } + + if (!is_dir($this->getSourceDir())) { + throw new ValidationException( + "Extension source directory not found: {$this->getSourceDir()}", + validation_module: "Extension {$this->getName()} source" + ); + } + $env = $this->getSharedExtensionEnv(); $this->runStage([$this, 'phpizeForUnix'], ['env' => $env]); diff --git a/src/StaticPHP/Runtime/Executor/UnixCMakeExecutor.php b/src/StaticPHP/Runtime/Executor/UnixCMakeExecutor.php index ecf17693..9fc00840 100644 --- a/src/StaticPHP/Runtime/Executor/UnixCMakeExecutor.php +++ b/src/StaticPHP/Runtime/Executor/UnixCMakeExecutor.php @@ -230,7 +230,7 @@ class UnixCMakeExecutor extends Executor // EXE linker flags: base system libs + framework flags for target packages $exeLinkerFlags = SystemTarget::getRuntimeLibs(); - if ($this->package instanceof TargetPackage) { + if ($this->package instanceof TargetPackage && SystemTarget::getTargetOS() === 'Darwin') { $resolvedNames = array_keys($this->installer->getResolvedPackages()); $resolvedNames[] = $this->package->getName(); $fwFlags = new SPCConfigUtil()->getFrameworksString($resolvedNames); @@ -302,9 +302,12 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) set(CMAKE_C_STANDARD_INCLUDE_DIRECTORIES "{$include}") set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES "{$include}") CMAKE; - // Whoops, linux may need CMAKE_AR sometimes + // pin AR/RANLIB so cmake uses zig-ar/zig-ranlib instead of system /usr/bin/ranlib (zig archives need it) if (PHP_OS_FAMILY === 'Linux') { - $toolchain .= "\nSET(CMAKE_AR \"ar\")"; + $ar = getenv('SPC_DEFAULT_AR') ?: getenv('AR') ?: 'ar'; + $ranlib = getenv('SPC_DEFAULT_RANLIB') ?: (getenv('RANLIB') ?: 'ranlib'); + $toolchain .= "\nSET(CMAKE_AR \"{$ar}\")"; + $toolchain .= "\nSET(CMAKE_RANLIB \"{$ranlib}\")"; } FileSystem::writeFile(SOURCE_PATH . '/toolchain.cmake', $toolchain); return $created = realpath(SOURCE_PATH . '/toolchain.cmake'); diff --git a/src/StaticPHP/Toolchain/ClangBrewToolchain.php b/src/StaticPHP/Toolchain/ClangBrewToolchain.php index 5d8963ef..23260954 100644 --- a/src/StaticPHP/Toolchain/ClangBrewToolchain.php +++ b/src/StaticPHP/Toolchain/ClangBrewToolchain.php @@ -15,6 +15,7 @@ class ClangBrewToolchain extends ClangNativeToolchain GlobalEnvManager::putenv("SPC_DEFAULT_CC={$homebrew_prefix}/opt/llvm/bin/clang"); GlobalEnvManager::putenv("SPC_DEFAULT_CXX={$homebrew_prefix}/opt/llvm/bin/clang++"); GlobalEnvManager::putenv("SPC_DEFAULT_AR={$homebrew_prefix}/opt/llvm/bin/llvm-ar"); + GlobalEnvManager::putenv("SPC_DEFAULT_RANLIB={$homebrew_prefix}/opt/llvm/bin/llvm-ranlib"); GlobalEnvManager::putenv('SPC_DEFAULT_LD=ld'); GlobalEnvManager::addPathIfNotExists("{$homebrew_prefix}/opt/llvm/bin"); } diff --git a/src/StaticPHP/Toolchain/ClangNativeToolchain.php b/src/StaticPHP/Toolchain/ClangNativeToolchain.php index 27ae2b65..7602c3ed 100644 --- a/src/StaticPHP/Toolchain/ClangNativeToolchain.php +++ b/src/StaticPHP/Toolchain/ClangNativeToolchain.php @@ -21,6 +21,7 @@ class ClangNativeToolchain implements UnixToolchainInterface GlobalEnvManager::putenv('SPC_DEFAULT_CC=clang'); GlobalEnvManager::putenv('SPC_DEFAULT_CXX=clang++'); GlobalEnvManager::putenv('SPC_DEFAULT_AR=ar'); + GlobalEnvManager::putenv('SPC_DEFAULT_RANLIB=ranlib'); GlobalEnvManager::putenv('SPC_DEFAULT_LD=ld'); } diff --git a/src/StaticPHP/Toolchain/GccNativeToolchain.php b/src/StaticPHP/Toolchain/GccNativeToolchain.php index 7c339e69..9aecb7e0 100644 --- a/src/StaticPHP/Toolchain/GccNativeToolchain.php +++ b/src/StaticPHP/Toolchain/GccNativeToolchain.php @@ -18,6 +18,7 @@ class GccNativeToolchain implements UnixToolchainInterface GlobalEnvManager::putenv('SPC_DEFAULT_CC=gcc'); GlobalEnvManager::putenv('SPC_DEFAULT_CXX=g++'); GlobalEnvManager::putenv('SPC_DEFAULT_AR=ar'); + GlobalEnvManager::putenv('SPC_DEFAULT_RANLIB=ranlib'); GlobalEnvManager::putenv('SPC_DEFAULT_LD=ld'); } diff --git a/src/StaticPHP/Toolchain/ZigToolchain.php b/src/StaticPHP/Toolchain/ZigToolchain.php index 36e42d04..63d2127c 100644 --- a/src/StaticPHP/Toolchain/ZigToolchain.php +++ b/src/StaticPHP/Toolchain/ZigToolchain.php @@ -16,28 +16,8 @@ class ZigToolchain implements UnixToolchainInterface GlobalEnvManager::putenv('SPC_DEFAULT_CC=zig-cc'); GlobalEnvManager::putenv('SPC_DEFAULT_CXX=zig-c++'); GlobalEnvManager::putenv('SPC_DEFAULT_AR=zig-ar'); + GlobalEnvManager::putenv('SPC_DEFAULT_RANLIB=zig-ranlib'); GlobalEnvManager::putenv('SPC_DEFAULT_LD=zig-ld.lld'); - - // Generate additional objects needed for zig toolchain - $paths = ['/usr/lib/gcc', '/usr/local/lib/gcc']; - $objects = ['crtbeginS.o', 'crtendS.o']; - $found = []; - - foreach ($objects as $obj) { - $located = null; - foreach ($paths as $base) { - $output = shell_exec("find {$base} -name {$obj} 2>/dev/null | grep -v '/32/' | head -n 1"); - $line = trim((string) $output); - if ($line !== '') { - $located = $line; - break; - } - } - if ($located) { - $found[] = $located; - } - } - GlobalEnvManager::putenv('SPC_EXTRA_RUNTIME_OBJECTS=' . implode(' ', $found)); } public function afterInit(): void diff --git a/src/globals/scripts/zig-cc.sh b/src/globals/scripts/zig-cc.sh index 56ae9505..fe09b466 100755 --- a/src/globals/scripts/zig-cc.sh +++ b/src/globals/scripts/zig-cc.sh @@ -1,9 +1,14 @@ #!/usr/bin/env bash SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" -BUILDROOT_ABS="${BUILD_ROOT_PATH:-$(realpath "$SCRIPT_DIR/../../../buildroot/include" 2>/dev/null || true)}" +BUILDROOT_INC="${BUILD_INCLUDE_PATH:-$SCRIPT_DIR/../../../buildroot/include}" +BUILDROOT_ABS="$(realpath "$BUILDROOT_INC" 2>/dev/null || true)" PARSED_ARGS=() +is_buildroot_inc() { + [[ -n "$BUILDROOT_ABS" && "$1" == "$BUILDROOT_ABS" ]] +} + while [[ $# -gt 0 ]]; do case "$1" in -isystem) @@ -11,13 +16,13 @@ while [[ $# -gt 0 ]]; do ARG="$1" shift ARG_ABS="$(realpath "$ARG" 2>/dev/null || true)" - [[ "$ARG_ABS" == "$BUILDROOT_ABS" ]] && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem" "$ARG") + is_buildroot_inc "$ARG_ABS" && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem" "$ARG") ;; -isystem*) ARG="${1#-isystem}" shift ARG_ABS="$(realpath "$ARG" 2>/dev/null || true)" - [[ "$ARG_ABS" == "$BUILDROOT_ABS" ]] && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem$ARG") + is_buildroot_inc "$ARG_ABS" && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem$ARG") ;; -march=*|-mcpu=*) OPT_NAME="${1%%=*}" @@ -32,6 +37,10 @@ while [[ $# -gt 0 ]]; do PARSED_ARGS+=("${OPT_NAME}=${OPT_VALUE}") shift ;; + -Wlogical-op|-Wduplicated-cond|-Wduplicated-branches|-Wno-clobbered|-Wjump-misses-init|-Wformat-truncation|-Warray-bounds=*|-Wimplicit-fallthrough=*) + # GCC-only warning flags that clang/zig doesn't recognize; drop to silence -Wunknown-warning-option noise + shift + ;; *) PARSED_ARGS+=("$1") shift @@ -39,6 +48,24 @@ while [[ $# -gt 0 ]]; do esac done +IS_LINK=1 +NEED_PROFILE_RT=0 # https://codeberg.org/ziglang/zig/issues/32066 +NEED_CRT=0 # https://codeberg.org/ziglang/zig/issues/32064 +for _a in "${PARSED_ARGS[@]}"; do + case "$_a" in + -c|-S|-E|-M|-MM) IS_LINK=0 ;; + -fprofile-generate*|-fprofile-instr-generate*|-fcs-profile-generate*) NEED_PROFILE_RT=1 ;; + -shared) NEED_CRT=1 ;; + esac +done +[[ "$SPC_COMPILER_EXTRA" == *-fprofile-generate* || "$SPC_COMPILER_EXTRA" == *-fcs-profile-generate* ]] && NEED_PROFILE_RT=1 +if [[ $IS_LINK -eq 1 && $NEED_PROFILE_RT -eq 1 && -f "$SCRIPT_DIR/lib/libclang_rt.profile.a" ]]; then + PARSED_ARGS+=("$SCRIPT_DIR/lib/libclang_rt.profile.a" "-Wl,-u,__llvm_profile_runtime") +fi +if [[ $IS_LINK -eq 1 && $NEED_CRT -eq 1 && -f "$SCRIPT_DIR/lib/clang_rt.crtbegin.o" && -f "$SCRIPT_DIR/lib/clang_rt.crtend.o" ]]; then + PARSED_ARGS+=("$SCRIPT_DIR/lib/clang_rt.crtbegin.o" "$SCRIPT_DIR/lib/clang_rt.crtend.o") +fi + [[ -n "$SPC_TARGET" ]] && TARGET="-target $SPC_TARGET" || TARGET="" if [[ "$SPC_TARGET" =~ \.[0-9]+\.[0-9]+ ]]; then