detectLlvmVersion() ?? throw new DownloaderException('Could not detect a clang version on host; install zig or clang first'); $tarball = "llvm-project-{$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, retries: $downloader->getRetry()); return DownloadResult::archive($tarball, ['url' => $url, 'version' => $llvmVersion], extract: '{pkg_root_path}/llvm-tools-src', verified: false, version: $llvmVersion); } #[CustomBinaryCheckUpdate('llvm-tools', [ 'linux-x86_64', 'linux-aarch64', 'macos-x86_64', 'macos-aarch64', ])] public function checkUpdateBinary(?string $old_version, ArtifactDownloader $downloader): CheckUpdateResult { $llvmVersion = $this->detectLlvmVersion() ?? throw new DownloaderException('Could not detect a clang version on host; install zig or clang first'); return new CheckUpdateResult( old: $old_version, new: $llvmVersion, needUpdate: $old_version === null || $llvmVersion !== $old_version, ); } #[AfterBinaryExtract('llvm-tools', [ 'linux-x86_64', 'linux-aarch64', 'macos-x86_64', 'macos-aarch64', ])] public function postExtract(string $target_path): void { $this->buildForHost($target_path); } public function buildForHost(?string $sourceRoot = null): bool { $sourceRoot ??= PKG_ROOT_PATH . '/llvm-tools-src'; $binDir = PKG_ROOT_PATH . '/llvm-tools/bin'; if ($this->allBuilt($binDir)) { return true; } $llvmDir = "{$sourceRoot}/llvm"; if (!is_dir($llvmDir)) { logger()->error("[llvm-tools] expected llvm/ subdir at {$llvmDir} (extraction layout changed?)"); return false; } $cmake = trim((string) shell_exec('command -v cmake 2>/dev/null')); if ($cmake === '') { logger()->error('[llvm-tools] cmake not found on PATH; install cmake first.'); return false; } $generator = trim((string) shell_exec('command -v ninja 2>/dev/null')) !== '' ? 'Ninja' : 'Unix Makefiles'; $cc = PKG_ROOT_PATH . '/zig/zig-cc'; $cxx = PKG_ROOT_PATH . '/zig/zig-c++'; $buildDir = "{$sourceRoot}/build"; $installDir = PKG_ROOT_PATH . '/llvm-tools'; f_mkdir($buildDir, recursive: true); $cmakeArgs = [ '-G', $generator, '-S', $llvmDir, '-B', $buildDir, '-DCMAKE_BUILD_TYPE=Release', '-DLLVM_ENABLE_PROJECTS=', '-DLLVM_TARGETS_TO_BUILD=', '-DLLVM_INCLUDE_BENCHMARKS=OFF', '-DLLVM_INCLUDE_TESTS=OFF', '-DLLVM_INCLUDE_EXAMPLES=OFF', '-DLLVM_INCLUDE_DOCS=OFF', '-DLLVM_ENABLE_ZLIB=OFF', '-DLLVM_ENABLE_ZSTD=OFF', '-DLLVM_ENABLE_LIBXML2=OFF', '-DLLVM_ENABLE_TERMINFO=OFF', '-DLLVM_ENABLE_LIBEDIT=OFF', '-DLLVM_ENABLE_LIBPFM=OFF', '-DLLVM_BUILD_LLVM_DYLIB=OFF', '-DLLVM_LINK_LLVM_DYLIB=OFF', '-DBUILD_SHARED_LIBS=OFF', '-DCMAKE_C_COMPILER=' . $cc, '-DCMAKE_CXX_COMPILER=' . $cxx, '-DCMAKE_INSTALL_PREFIX=' . $installDir, ]; $savedTarget = getenv('SPC_TARGET'); f_putenv('SPC_TARGET=' . GNU_ARCH . '-linux-musl'); try { $cmd = escapeshellarg($cmake) . ' ' . implode(' ', array_map('escapeshellarg', $cmakeArgs)) . ' 2>&1'; logger()->info("Configuring llvm-tools (generator: {$generator}, target: " . getenv('SPC_TARGET') . ')'); exec($cmd, $out, $rc); if ($rc !== 0) { logger()->error('[llvm-tools] cmake configure failed: ' . implode("\n", $out)); return false; } $jobs = (int) (getenv('SPC_CONCURRENCY') ?: CPU_COUNT); if ($jobs < 1) { $jobs = 1; } $targetArgs = ''; foreach (self::TOOLS as $t) { $targetArgs .= ' --target ' . escapeshellarg($t); } $buildCmd = 'cmake --build ' . escapeshellarg($buildDir) . $targetArgs . ($generator === 'Ninja' ? " -j{$jobs}" : " -- -j{$jobs}"); logger()->info('Building llvm-tools (' . implode(', ', self::TOOLS) . ')'); exec($buildCmd . ' 2>&1', $out2, $rc2); if ($rc2 !== 0) { logger()->error('[llvm-tools] build failed: ' . implode("\n", array_slice($out2, -40))); return false; } } finally { f_putenv('SPC_TARGET=' . ($savedTarget === false ? '' : $savedTarget)); } f_mkdir($binDir, recursive: true); foreach (self::TOOLS as $t) { $built = "{$buildDir}/bin/{$t}"; $dst = "{$binDir}/{$t}"; if (!is_file($built)) { logger()->error("[llvm-tools] expected output {$built} not found"); return false; } if (!copy($built, $dst)) { logger()->error("[llvm-tools] failed to copy {$built} → {$dst}"); return false; } chmod($dst, 0755); logger()->info("[llvm-tools] installed {$dst} (" . filesize($dst) . ' bytes)'); } return true; } public function allBuilt(string $binDir): bool { foreach (self::TOOLS as $t) { $p = "{$binDir}/{$t}"; if (!is_file($p) || !is_executable($p)) { return false; } } return true; } private function detectLlvmVersion(): ?string { $zig = PKG_ROOT_PATH . '/zig/zig'; $verLine = trim((string) shell_exec(escapeshellarg($zig) . ' cc --version 2>/dev/null')); if (preg_match('/clang version (\d+\.\d+\.\d+)/', $verLine, $m)) { return $m[1]; } return null; } }