mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-02 22:35:43 +08:00
install runtime rt after zig init
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
47
src/StaticPHP/Doctor/Item/LlvmCompilerRtCheck.php
Normal file
47
src/StaticPHP/Doctor/Item/LlvmCompilerRtCheck.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace StaticPHP\Doctor\Item;
|
||||
|
||||
use Package\Artifact\llvm_compiler_rt;
|
||||
use StaticPHP\Attribute\Doctor\CheckItem;
|
||||
use StaticPHP\Attribute\Doctor\FixItem;
|
||||
use StaticPHP\Attribute\Doctor\OptionalCheck;
|
||||
use StaticPHP\DI\ApplicationContext;
|
||||
use StaticPHP\Doctor\CheckResult;
|
||||
use StaticPHP\Package\PackageInstaller;
|
||||
use StaticPHP\Runtime\SystemTarget;
|
||||
use StaticPHP\Toolchain\Interface\ToolchainInterface;
|
||||
use StaticPHP\Toolchain\ZigToolchain;
|
||||
|
||||
#[OptionalCheck([self::class, 'optionalCheck'])]
|
||||
class LlvmCompilerRtCheck
|
||||
{
|
||||
public static function optionalCheck(): bool
|
||||
{
|
||||
return ApplicationContext::get(ToolchainInterface::class) instanceof ZigToolchain;
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
#[CheckItem('if llvm-compiler-rt is built for current target', level: 799)]
|
||||
public function checkLlvmCompilerRt(): CheckResult
|
||||
{
|
||||
$libDir = PKG_ROOT_PATH . '/zig/lib/' . SystemTarget::getCanonicalTriple();
|
||||
if (new llvm_compiler_rt()->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);
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user