From b38c7b274fbd514c94fff381b5bd4afaddd11040 Mon Sep 17 00:00:00 2001 From: henderkes Date: Tue, 19 May 2026 21:43:18 +0700 Subject: [PATCH] install runtime rt after zig init --- src/Package/Artifact/llvm_compiler_rt.php | 71 +++++++++---------- src/Package/Artifact/zig.php | 38 +++++----- .../Doctor/Item/LlvmCompilerRtCheck.php | 47 ++++++++++++ src/StaticPHP/Toolchain/ZigToolchain.php | 58 ++++++++++++++- 4 files changed, 157 insertions(+), 57 deletions(-) create mode 100644 src/StaticPHP/Doctor/Item/LlvmCompilerRtCheck.php diff --git a/src/Package/Artifact/llvm_compiler_rt.php b/src/Package/Artifact/llvm_compiler_rt.php index d713cc72..974b9d57 100644 --- a/src/Package/Artifact/llvm_compiler_rt.php +++ b/src/Package/Artifact/llvm_compiler_rt.php @@ -14,16 +14,23 @@ use StaticPHP\Exception\BuildFailureException; use StaticPHP\Exception\DownloaderException; use StaticPHP\Runtime\SystemTarget; +/** + * Builds the compiler-rt bits zig ships without — libclang_rt.profile.a (PGO instrumentation) + * and clang_rt.crtbegin.o/crtend.o (__dso_handle for shared libs). Target-arch specific: + * libs land in PKG_ROOT_PATH/zig/lib/{triple}. + */ class llvm_compiler_rt { #[CustomBinary('llvm-compiler-rt', [ 'linux-x86_64', 'linux-aarch64', + 'macos-x86_64', + 'macos-aarch64', ])] public function downBinary(ArtifactDownloader $downloader): DownloadResult { $llvmVersion = $this->detectZigLlvmVersion() - ?? throw new DownloaderException('Could not detect bundled clang version from zig cc --version; ensure zig is installed'); + ?? throw new DownloaderException('llvm-compiler-rt: could not detect bundled clang version from zig cc --version'); $tarball = "compiler-rt-{$llvmVersion}.src.tar.xz"; $url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-{$llvmVersion}/{$tarball}"; $tarballPath = DOWNLOAD_PATH . '/' . $tarball; @@ -34,11 +41,13 @@ class llvm_compiler_rt #[CustomBinaryCheckUpdate('llvm-compiler-rt', [ 'linux-x86_64', 'linux-aarch64', + 'macos-x86_64', + 'macos-aarch64', ])] public function checkUpdateBinary(?string $old_version, ArtifactDownloader $downloader): CheckUpdateResult { $llvmVersion = $this->detectZigLlvmVersion() - ?? throw new DownloaderException('Could not detect bundled clang version from zig cc --version; ensure zig is installed'); + ?? throw new DownloaderException('llvm-compiler-rt: could not detect bundled clang version from zig cc --version'); return new CheckUpdateResult( old: $old_version, new: $llvmVersion, @@ -49,33 +58,34 @@ class llvm_compiler_rt #[AfterBinaryExtract('llvm-compiler-rt', [ 'linux-x86_64', 'linux-aarch64', + 'macos-x86_64', + 'macos-aarch64', ])] public function postExtract(string $target_path): void { - $this->buildForCurrentTarget($target_path); + $this->buildForTriple($target_path); } - public function buildForCurrentTarget(?string $sourceDir = null): void + public function buildForTriple(?string $sourceDir = null, ?string $triple = null): void { $sourceDir ??= SOURCE_PATH . '/llvm-compiler-rt'; - $triple = SystemTarget::getCanonicalTriple(); + $triple ??= SystemTarget::getCanonicalTriple(); $libDir = PKG_ROOT_PATH . '/zig/lib/' . $triple; if ($this->isBuilt($libDir)) { return; } - if (!is_dir($sourceDir)) { - throw new BuildFailureException("llvm-compiler-rt: missing source at {$sourceDir}"); + if (!is_dir($sourceDir) || !is_dir("{$sourceDir}/lib/profile")) { + throw new BuildFailureException("llvm-compiler-rt: missing source at {$sourceDir} (extraction layout changed?)"); } - $zig = PKG_ROOT_PATH . '/zig/zig'; f_mkdir($libDir, recursive: true); $profileLib = "{$libDir}/libclang_rt.profile.a"; $crtBegin = "{$libDir}/clang_rt.crtbegin.o"; $crtEnd = "{$libDir}/clang_rt.crtend.o"; if (!file_exists($profileLib)) { - $this->buildProfileRuntime($zig, $sourceDir, $profileLib, $triple); + $this->buildProfileRuntime($sourceDir, $profileLib, $triple); } if (!file_exists($crtBegin) || !file_exists($crtEnd)) { - $this->buildCrtObjects($zig, $sourceDir, $crtBegin, $crtEnd, $triple); + $this->buildCrtObjects($sourceDir, $crtBegin, $crtEnd, $triple); } } @@ -86,13 +96,19 @@ class llvm_compiler_rt && file_exists("{$libDir}/clang_rt.crtend.o"); } - private function buildProfileRuntime(string $zig, string $srcRoot, string $libPath, string $triple): void + private function detectZigLlvmVersion(): ?string + { + [$rc, $out] = shell()->execWithResult('zig cc --version', false); + if ($rc !== 0) { + return null; + } + return preg_match('/clang version (\d+\.\d+\.\d+)/', implode("\n", $out), $m) ? $m[1] : null; + } + + private function buildProfileRuntime(string $srcRoot, string $libPath, string $triple): void { $profileSrc = "{$srcRoot}/lib/profile"; $profileInc = "{$srcRoot}/include"; - if (!is_dir($profileSrc)) { - throw new BuildFailureException("llvm-compiler-rt: profile src dir missing at {$profileSrc}"); - } // Skip OS-specific sources we can't satisfy without their SDKs. $skip = ['/PlatformAIX', '/PlatformDarwin', '/PlatformFuchsia', '/PlatformOther', '/PlatformWindows', '/WindowsMMap']; $sources = array_filter( @@ -105,16 +121,12 @@ class llvm_compiler_rt $cflags = "-target {$triple} -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'; - shell()->exec(escapeshellarg($zig) . ' cc ' . $cflags . ' -o ' . escapeshellarg($obj) . ' ' . escapeshellarg($src)); - $objs[] = $obj; - } - shell()->exec(escapeshellarg($zig) . ' ar rcs ' . escapeshellarg($libPath) . ' ' . implode(' ', array_map('escapeshellarg', $objs))); + $srcArgs = implode(' ', array_map('escapeshellarg', $sources)); + shell()->cd($objDir)->exec("zig cc {$cflags} {$srcArgs}"); + shell()->cd($objDir)->exec('zig ar rcs ' . escapeshellarg($libPath) . ' *.o'); } - private function buildCrtObjects(string $zig, string $srcRoot, string $crtBegin, string $crtEnd, string $triple): void + private function buildCrtObjects(string $srcRoot, string $crtBegin, string $crtEnd, string $triple): void { $beginSrc = "{$srcRoot}/lib/builtins/crtbegin.c"; $endSrc = "{$srcRoot}/lib/builtins/crtend.c"; @@ -123,20 +135,7 @@ class llvm_compiler_rt } $cflags = "-target {$triple} -c -O2 -fPIC -fvisibility=hidden -DCRT_HAS_INITFINI_ARRAY"; foreach ([[$beginSrc, $crtBegin], [$endSrc, $crtEnd]] as [$src, $dst]) { - shell()->exec(escapeshellarg($zig) . ' cc ' . $cflags . ' -o ' . escapeshellarg($dst) . ' ' . escapeshellarg($src)); + shell()->exec("zig cc {$cflags} -o " . escapeshellarg($dst) . ' ' . escapeshellarg($src)); } } - - private function detectZigLlvmVersion(): ?string - { - $zig = PKG_ROOT_PATH . '/zig/zig'; - if (!is_file($zig)) { - return null; - } - [$rc, $out] = shell()->execWithResult(escapeshellarg($zig) . ' cc --version', false); - if ($rc !== 0) { - return null; - } - return preg_match('/clang version (\d+\.\d+\.\d+)/', implode("\n", $out), $m) ? $m[1] : null; - } } diff --git a/src/Package/Artifact/zig.php b/src/Package/Artifact/zig.php index 18a06c4d..04ae0b1c 100644 --- a/src/Package/Artifact/zig.php +++ b/src/Package/Artifact/zig.php @@ -110,26 +110,24 @@ class zig break; } } - if ($all_exist) { - return; + if (!$all_exist) { + $script_path = ROOT_DIR . '/src/globals/scripts/zig-cc.sh'; + $script_content = file_get_contents($script_path); + + file_put_contents("{$target_path}/zig-cc", $script_content); + chmod("{$target_path}/zig-cc", 0755); + + $script_content = str_replace('zig cc', 'zig c++', $script_content); + file_put_contents("{$target_path}/zig-c++", $script_content); + file_put_contents("{$target_path}/zig-ar", "#!/usr/bin/env bash\nexec zig ar $@"); + file_put_contents("{$target_path}/zig-ld.lld", "#!/usr/bin/env bash\nexec zig ld.lld $@"); + file_put_contents("{$target_path}/zig-ranlib", "#!/usr/bin/env bash\nexec zig ranlib $@"); + file_put_contents("{$target_path}/zig-objcopy", "#!/usr/bin/env bash\nexec zig objcopy $@"); + chmod("{$target_path}/zig-c++", 0755); + chmod("{$target_path}/zig-ar", 0755); + chmod("{$target_path}/zig-ld.lld", 0755); + chmod("{$target_path}/zig-ranlib", 0755); + chmod("{$target_path}/zig-objcopy", 0755); } - - $script_path = ROOT_DIR . '/src/globals/scripts/zig-cc.sh'; - $script_content = file_get_contents($script_path); - - file_put_contents("{$target_path}/zig-cc", $script_content); - chmod("{$target_path}/zig-cc", 0755); - - $script_content = str_replace('zig cc', 'zig c++', $script_content); - file_put_contents("{$target_path}/zig-c++", $script_content); - file_put_contents("{$target_path}/zig-ar", "#!/usr/bin/env bash\nexec zig ar $@"); - file_put_contents("{$target_path}/zig-ld.lld", "#!/usr/bin/env bash\nexec zig ld.lld $@"); - file_put_contents("{$target_path}/zig-ranlib", "#!/usr/bin/env bash\nexec zig ranlib $@"); - file_put_contents("{$target_path}/zig-objcopy", "#!/usr/bin/env bash\nexec zig objcopy $@"); - chmod("{$target_path}/zig-c++", 0755); - chmod("{$target_path}/zig-ar", 0755); - chmod("{$target_path}/zig-ld.lld", 0755); - chmod("{$target_path}/zig-ranlib", 0755); - chmod("{$target_path}/zig-objcopy", 0755); } } diff --git a/src/StaticPHP/Doctor/Item/LlvmCompilerRtCheck.php b/src/StaticPHP/Doctor/Item/LlvmCompilerRtCheck.php new file mode 100644 index 00000000..368e0267 --- /dev/null +++ b/src/StaticPHP/Doctor/Item/LlvmCompilerRtCheck.php @@ -0,0 +1,47 @@ +isBuilt($libDir)) { + return CheckResult::ok($libDir); + } + return CheckResult::fail('llvm-compiler-rt is not built for ' . SystemTarget::getCanonicalTriple(), 'build-llvm-compiler-rt'); + } + + #[FixItem('build-llvm-compiler-rt')] + public function fixLlvmCompilerRt(): bool + { + $installer = new PackageInstaller(interactive: false); + $installer->addInstallPackage('llvm-compiler-rt'); + $installer->run(true); + new llvm_compiler_rt()->buildForTriple(); + $libDir = PKG_ROOT_PATH . '/zig/lib/' . SystemTarget::getCanonicalTriple(); + return new llvm_compiler_rt()->isBuilt($libDir); + } +} diff --git a/src/StaticPHP/Toolchain/ZigToolchain.php b/src/StaticPHP/Toolchain/ZigToolchain.php index 799ce2e4..83e8a4fe 100644 --- a/src/StaticPHP/Toolchain/ZigToolchain.php +++ b/src/StaticPHP/Toolchain/ZigToolchain.php @@ -4,13 +4,21 @@ declare(strict_types=1); namespace StaticPHP\Toolchain; +use Package\Artifact\llvm_compiler_rt; +use StaticPHP\DI\ApplicationContext; +use StaticPHP\Package\PackageBuilder; +use StaticPHP\Package\PackageInstaller; use StaticPHP\Runtime\SystemTarget; use StaticPHP\Toolchain\Interface\UnixToolchainInterface; use StaticPHP\Util\GlobalEnvManager; +use StaticPHP\Util\InteractiveTerm; use StaticPHP\Util\System\LinuxUtil; +use ZM\Logger\ConsoleColor; class ZigToolchain implements UnixToolchainInterface { + private static bool $afterInitDone = false; + public function initEnv(): void { // Set environment variables for zig toolchain @@ -19,11 +27,15 @@ class ZigToolchain implements UnixToolchainInterface GlobalEnvManager::putenv('SPC_DEFAULT_AR=zig-ar'); GlobalEnvManager::putenv('SPC_DEFAULT_RANLIB=zig-ranlib'); GlobalEnvManager::putenv('SPC_DEFAULT_LD=zig-ld.lld'); + GlobalEnvManager::addPathIfNotExists($this->getPath()); } public function afterInit(): void { - GlobalEnvManager::addPathIfNotExists($this->getPath()); + if (self::$afterInitDone) { + return; + } + self::$afterInitDone = true; f_passthru('ulimit -n 2048'); // zig opens extra file descriptors, so when a lot of extensions are built statically, 1024 is not enough $cflags = getenv('SPC_DEFAULT_CFLAGS') ?: ''; $cxxflags = getenv('SPC_DEFAULT_CXXFLAGS') ?: ''; @@ -52,6 +64,8 @@ class ZigToolchain implements UnixToolchainInterface // zig-cc/clang treats strlcpy/strlcat as compiler builtins, so configure link tests pass (HAVE_STRLCPY=1) $extra_vars = getenv('SPC_EXTRA_PHP_VARS') ?: ''; GlobalEnvManager::putenv("SPC_EXTRA_PHP_VARS=ac_cv_func_strlcpy=no ac_cv_func_strlcat=no {$extra_vars}"); + + $this->ensureCompilerRt(); } public function getCompilerInfo(): ?string @@ -87,6 +101,48 @@ class ZigToolchain implements UnixToolchainInterface return false; } + private function ensureCompilerRt(): void + { + $rt = new llvm_compiler_rt(); + $triple = SystemTarget::getCanonicalTriple(); + $libDir = PKG_ROOT_PATH . '/zig/lib/' . $triple; + if ($rt->isBuilt($libDir)) { + return; + } + if (!is_dir(SOURCE_PATH . '/llvm-compiler-rt/lib/profile')) { + // Source not yet downloaded; install via nested PackageInstaller. The recursion guard + // on afterInit prevents the nested run from re-entering this method. Save the outer + // installer/builder in the container so executors keep seeing the outer one after. + // The PackageInstaller surfaces its own spinner for the install; AfterBinaryExtract + // builds for the current triple, so we're done after run(). + $outerInstaller = ApplicationContext::tryGet(PackageInstaller::class); + $outerBuilder = ApplicationContext::tryGet(PackageBuilder::class); + try { + new PackageInstaller() + ->addInstallPackage('llvm-compiler-rt') + ->run(true); + } finally { + if ($outerInstaller !== null) { + ApplicationContext::set(PackageInstaller::class, $outerInstaller); + } + if ($outerBuilder !== null) { + ApplicationContext::set(PackageBuilder::class, $outerBuilder); + } + } + return; + } + // Source already extracted from a previous run on a different triple; rebuild here with our + // own progress spinner since we're outside the PackageInstaller flow. + InteractiveTerm::indicateProgress('Building llvm-compiler-rt for ' . ConsoleColor::yellow($triple)); + try { + $rt->buildForTriple(); + } catch (\Throwable $e) { + InteractiveTerm::finish('Build llvm-compiler-rt for ' . ConsoleColor::red($triple) . ' failed', false); + throw $e; + } + InteractiveTerm::finish('Built llvm-compiler-rt for ' . ConsoleColor::green($triple)); + } + private function getPath(): string { return PKG_ROOT_PATH . '/zig';