detectZigLlvmVersion() ?? 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; default_shell()->executeCurlDownload($url, $tarballPath, headers: $this->getGitHubTokenHeaders(), retries: $downloader->getRetry()); return DownloadResult::archive($tarball, ['url' => $url, 'version' => $llvmVersion], extract: '{source_path}/llvm-compiler-rt', verified: false, version: $llvmVersion); } #[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('llvm-compiler-rt: could not detect bundled clang version from zig cc --version'); return new CheckUpdateResult( old: $old_version, new: $llvmVersion, needUpdate: $old_version === null || $llvmVersion !== $old_version, ); } #[AfterBinaryExtract('llvm-compiler-rt', [ 'linux-x86_64', 'linux-aarch64', 'macos-x86_64', 'macos-aarch64', ])] public function postExtract(string $target_path): void { $this->buildForTriple($target_path); } public function buildForTriple(?string $sourceDir = null, ?string $triple = null): void { $sourceDir ??= SOURCE_PATH . '/llvm-compiler-rt'; $triple ??= SystemTarget::getCanonicalTriple(); $libDir = zig::path() . '/lib/' . $triple; if ($this->isBuilt($libDir)) { return; } if (!is_dir($sourceDir) || !is_dir("{$sourceDir}/lib/profile")) { throw new BuildFailureException("llvm-compiler-rt: missing source at {$sourceDir} (extraction layout changed?)"); } 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($sourceDir, $profileLib, $triple); } if (!file_exists($crtBegin) || !file_exists($crtEnd)) { $this->buildCrtObjects($sourceDir, $crtBegin, $crtEnd, $triple); } } public function isBuilt(string $libDir): bool { return file_exists("{$libDir}/libclang_rt.profile.a") && file_exists("{$libDir}/clang_rt.crtbegin.o") && file_exists("{$libDir}/clang_rt.crtend.o"); } private function detectZigLlvmVersion(): ?string { if (!zig::isInstalled()) { return null; } [$rc, $out] = shell()->execWithResult(escapeshellarg(zig::binary()) . ' 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"; // Skip OS-specific sources we can't satisfy without their SDKs. $skip = ['/PlatformAIX', '/PlatformDarwin', '/PlatformFuchsia', '/PlatformOther', '/PlatformWindows', '/WindowsMMap']; $sources = array_filter( array_merge(glob("{$profileSrc}/*.c") ?: [], glob("{$profileSrc}/*.cpp") ?: []), fn ($f) => !array_any($skip, fn ($s) => str_contains($f, $s)), ); $objDir = "{$srcRoot}/obj-profile-{$triple}"; f_mkdir($objDir, recursive: true); $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'; $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 $srcRoot, string $crtBegin, string $crtEnd, string $triple): void { $beginSrc = "{$srcRoot}/lib/builtins/crtbegin.c"; $endSrc = "{$srcRoot}/lib/builtins/crtend.c"; if (!is_file($beginSrc) || !is_file($endSrc)) { throw new BuildFailureException("llvm-compiler-rt: crtbegin/crtend source missing under {$srcRoot}/lib/builtins"); } $cflags = "-target {$triple} -c -O2 -fPIC -fvisibility=hidden -DCRT_HAS_INITFINI_ARRAY"; foreach ([[$beginSrc, $crtBegin], [$endSrc, $crtEnd]] as [$src, $dst]) { shell()->exec("zig cc {$cflags} -o " . escapeshellarg($dst) . ' ' . escapeshellarg($src)); } } }