Skip to content

Commit b38c7b2

Browse files
committed
install runtime rt after zig init
1 parent efa7946 commit b38c7b2

4 files changed

Lines changed: 156 additions & 56 deletions

File tree

src/Package/Artifact/llvm_compiler_rt.php

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,23 @@
1414
use StaticPHP\Exception\DownloaderException;
1515
use StaticPHP\Runtime\SystemTarget;
1616

17+
/**
18+
* Builds the compiler-rt bits zig ships without — libclang_rt.profile.a (PGO instrumentation)
19+
* and clang_rt.crtbegin.o/crtend.o (__dso_handle for shared libs). Target-arch specific:
20+
* libs land in PKG_ROOT_PATH/zig/lib/{triple}.
21+
*/
1722
class llvm_compiler_rt
1823
{
1924
#[CustomBinary('llvm-compiler-rt', [
2025
'linux-x86_64',
2126
'linux-aarch64',
27+
'macos-x86_64',
28+
'macos-aarch64',
2229
])]
2330
public function downBinary(ArtifactDownloader $downloader): DownloadResult
2431
{
2532
$llvmVersion = $this->detectZigLlvmVersion()
26-
?? throw new DownloaderException('Could not detect bundled clang version from zig cc --version; ensure zig is installed');
33+
?? throw new DownloaderException('llvm-compiler-rt: could not detect bundled clang version from zig cc --version');
2734
$tarball = "compiler-rt-{$llvmVersion}.src.tar.xz";
2835
$url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-{$llvmVersion}/{$tarball}";
2936
$tarballPath = DOWNLOAD_PATH . '/' . $tarball;
@@ -34,11 +41,13 @@ public function downBinary(ArtifactDownloader $downloader): DownloadResult
3441
#[CustomBinaryCheckUpdate('llvm-compiler-rt', [
3542
'linux-x86_64',
3643
'linux-aarch64',
44+
'macos-x86_64',
45+
'macos-aarch64',
3746
])]
3847
public function checkUpdateBinary(?string $old_version, ArtifactDownloader $downloader): CheckUpdateResult
3948
{
4049
$llvmVersion = $this->detectZigLlvmVersion()
41-
?? throw new DownloaderException('Could not detect bundled clang version from zig cc --version; ensure zig is installed');
50+
?? throw new DownloaderException('llvm-compiler-rt: could not detect bundled clang version from zig cc --version');
4251
return new CheckUpdateResult(
4352
old: $old_version,
4453
new: $llvmVersion,
@@ -49,33 +58,34 @@ public function checkUpdateBinary(?string $old_version, ArtifactDownloader $down
4958
#[AfterBinaryExtract('llvm-compiler-rt', [
5059
'linux-x86_64',
5160
'linux-aarch64',
61+
'macos-x86_64',
62+
'macos-aarch64',
5263
])]
5364
public function postExtract(string $target_path): void
5465
{
55-
$this->buildForCurrentTarget($target_path);
66+
$this->buildForTriple($target_path);
5667
}
5768

58-
public function buildForCurrentTarget(?string $sourceDir = null): void
69+
public function buildForTriple(?string $sourceDir = null, ?string $triple = null): void
5970
{
6071
$sourceDir ??= SOURCE_PATH . '/llvm-compiler-rt';
61-
$triple = SystemTarget::getCanonicalTriple();
72+
$triple ??= SystemTarget::getCanonicalTriple();
6273
$libDir = PKG_ROOT_PATH . '/zig/lib/' . $triple;
6374
if ($this->isBuilt($libDir)) {
6475
return;
6576
}
66-
if (!is_dir($sourceDir)) {
67-
throw new BuildFailureException("llvm-compiler-rt: missing source at {$sourceDir}");
77+
if (!is_dir($sourceDir) || !is_dir("{$sourceDir}/lib/profile")) {
78+
throw new BuildFailureException("llvm-compiler-rt: missing source at {$sourceDir} (extraction layout changed?)");
6879
}
69-
$zig = PKG_ROOT_PATH . '/zig/zig';
7080
f_mkdir($libDir, recursive: true);
7181
$profileLib = "{$libDir}/libclang_rt.profile.a";
7282
$crtBegin = "{$libDir}/clang_rt.crtbegin.o";
7383
$crtEnd = "{$libDir}/clang_rt.crtend.o";
7484
if (!file_exists($profileLib)) {
75-
$this->buildProfileRuntime($zig, $sourceDir, $profileLib, $triple);
85+
$this->buildProfileRuntime($sourceDir, $profileLib, $triple);
7686
}
7787
if (!file_exists($crtBegin) || !file_exists($crtEnd)) {
78-
$this->buildCrtObjects($zig, $sourceDir, $crtBegin, $crtEnd, $triple);
88+
$this->buildCrtObjects($sourceDir, $crtBegin, $crtEnd, $triple);
7989
}
8090
}
8191

@@ -86,13 +96,19 @@ public function isBuilt(string $libDir): bool
8696
&& file_exists("{$libDir}/clang_rt.crtend.o");
8797
}
8898

89-
private function buildProfileRuntime(string $zig, string $srcRoot, string $libPath, string $triple): void
99+
private function detectZigLlvmVersion(): ?string
100+
{
101+
[$rc, $out] = shell()->execWithResult('zig cc --version', false);
102+
if ($rc !== 0) {
103+
return null;
104+
}
105+
return preg_match('/clang version (\d+\.\d+\.\d+)/', implode("\n", $out), $m) ? $m[1] : null;
106+
}
107+
108+
private function buildProfileRuntime(string $srcRoot, string $libPath, string $triple): void
90109
{
91110
$profileSrc = "{$srcRoot}/lib/profile";
92111
$profileInc = "{$srcRoot}/include";
93-
if (!is_dir($profileSrc)) {
94-
throw new BuildFailureException("llvm-compiler-rt: profile src dir missing at {$profileSrc}");
95-
}
96112
// Skip OS-specific sources we can't satisfy without their SDKs.
97113
$skip = ['/PlatformAIX', '/PlatformDarwin', '/PlatformFuchsia', '/PlatformOther', '/PlatformWindows', '/WindowsMMap'];
98114
$sources = array_filter(
@@ -105,16 +121,12 @@ private function buildProfileRuntime(string $zig, string $srcRoot, string $libPa
105121
$cflags = "-target {$triple} -c -O2 -fPIC -fvisibility=hidden "
106122
. '-I' . escapeshellarg($profileInc) . ' '
107123
. '-DCOMPILER_RT_HAS_ATOMICS=1 -DCOMPILER_RT_HAS_FCNTL_LCK=1 -DCOMPILER_RT_HAS_UNAME=1';
108-
$objs = [];
109-
foreach ($sources as $src) {
110-
$obj = $objDir . '/' . pathinfo($src, PATHINFO_FILENAME) . '.o';
111-
shell()->exec(escapeshellarg($zig) . ' cc ' . $cflags . ' -o ' . escapeshellarg($obj) . ' ' . escapeshellarg($src));
112-
$objs[] = $obj;
113-
}
114-
shell()->exec(escapeshellarg($zig) . ' ar rcs ' . escapeshellarg($libPath) . ' ' . implode(' ', array_map('escapeshellarg', $objs)));
124+
$srcArgs = implode(' ', array_map('escapeshellarg', $sources));
125+
shell()->cd($objDir)->exec("zig cc {$cflags} {$srcArgs}");
126+
shell()->cd($objDir)->exec('zig ar rcs ' . escapeshellarg($libPath) . ' *.o');
115127
}
116128

117-
private function buildCrtObjects(string $zig, string $srcRoot, string $crtBegin, string $crtEnd, string $triple): void
129+
private function buildCrtObjects(string $srcRoot, string $crtBegin, string $crtEnd, string $triple): void
118130
{
119131
$beginSrc = "{$srcRoot}/lib/builtins/crtbegin.c";
120132
$endSrc = "{$srcRoot}/lib/builtins/crtend.c";
@@ -123,20 +135,7 @@ private function buildCrtObjects(string $zig, string $srcRoot, string $crtBegin,
123135
}
124136
$cflags = "-target {$triple} -c -O2 -fPIC -fvisibility=hidden -DCRT_HAS_INITFINI_ARRAY";
125137
foreach ([[$beginSrc, $crtBegin], [$endSrc, $crtEnd]] as [$src, $dst]) {
126-
shell()->exec(escapeshellarg($zig) . ' cc ' . $cflags . ' -o ' . escapeshellarg($dst) . ' ' . escapeshellarg($src));
138+
shell()->exec("zig cc {$cflags} -o " . escapeshellarg($dst) . ' ' . escapeshellarg($src));
127139
}
128140
}
129-
130-
private function detectZigLlvmVersion(): ?string
131-
{
132-
$zig = PKG_ROOT_PATH . '/zig/zig';
133-
if (!is_file($zig)) {
134-
return null;
135-
}
136-
[$rc, $out] = shell()->execWithResult(escapeshellarg($zig) . ' cc --version', false);
137-
if ($rc !== 0) {
138-
return null;
139-
}
140-
return preg_match('/clang version (\d+\.\d+\.\d+)/', implode("\n", $out), $m) ? $m[1] : null;
141-
}
142141
}

src/Package/Artifact/zig.php

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -110,26 +110,24 @@ public function postExtractZig(string $target_path): void
110110
break;
111111
}
112112
}
113-
if ($all_exist) {
114-
return;
115-
}
116-
117-
$script_path = ROOT_DIR . '/src/globals/scripts/zig-cc.sh';
118-
$script_content = file_get_contents($script_path);
113+
if (!$all_exist) {
114+
$script_path = ROOT_DIR . '/src/globals/scripts/zig-cc.sh';
115+
$script_content = file_get_contents($script_path);
119116

120-
file_put_contents("{$target_path}/zig-cc", $script_content);
121-
chmod("{$target_path}/zig-cc", 0755);
117+
file_put_contents("{$target_path}/zig-cc", $script_content);
118+
chmod("{$target_path}/zig-cc", 0755);
122119

123-
$script_content = str_replace('zig cc', 'zig c++', $script_content);
124-
file_put_contents("{$target_path}/zig-c++", $script_content);
125-
file_put_contents("{$target_path}/zig-ar", "#!/usr/bin/env bash\nexec zig ar $@");
126-
file_put_contents("{$target_path}/zig-ld.lld", "#!/usr/bin/env bash\nexec zig ld.lld $@");
127-
file_put_contents("{$target_path}/zig-ranlib", "#!/usr/bin/env bash\nexec zig ranlib $@");
128-
file_put_contents("{$target_path}/zig-objcopy", "#!/usr/bin/env bash\nexec zig objcopy $@");
129-
chmod("{$target_path}/zig-c++", 0755);
130-
chmod("{$target_path}/zig-ar", 0755);
131-
chmod("{$target_path}/zig-ld.lld", 0755);
132-
chmod("{$target_path}/zig-ranlib", 0755);
133-
chmod("{$target_path}/zig-objcopy", 0755);
120+
$script_content = str_replace('zig cc', 'zig c++', $script_content);
121+
file_put_contents("{$target_path}/zig-c++", $script_content);
122+
file_put_contents("{$target_path}/zig-ar", "#!/usr/bin/env bash\nexec zig ar $@");
123+
file_put_contents("{$target_path}/zig-ld.lld", "#!/usr/bin/env bash\nexec zig ld.lld $@");
124+
file_put_contents("{$target_path}/zig-ranlib", "#!/usr/bin/env bash\nexec zig ranlib $@");
125+
file_put_contents("{$target_path}/zig-objcopy", "#!/usr/bin/env bash\nexec zig objcopy $@");
126+
chmod("{$target_path}/zig-c++", 0755);
127+
chmod("{$target_path}/zig-ar", 0755);
128+
chmod("{$target_path}/zig-ld.lld", 0755);
129+
chmod("{$target_path}/zig-ranlib", 0755);
130+
chmod("{$target_path}/zig-objcopy", 0755);
131+
}
134132
}
135133
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace StaticPHP\Doctor\Item;
6+
7+
use Package\Artifact\llvm_compiler_rt;
8+
use StaticPHP\Attribute\Doctor\CheckItem;
9+
use StaticPHP\Attribute\Doctor\FixItem;
10+
use StaticPHP\Attribute\Doctor\OptionalCheck;
11+
use StaticPHP\DI\ApplicationContext;
12+
use StaticPHP\Doctor\CheckResult;
13+
use StaticPHP\Package\PackageInstaller;
14+
use StaticPHP\Runtime\SystemTarget;
15+
use StaticPHP\Toolchain\Interface\ToolchainInterface;
16+
use StaticPHP\Toolchain\ZigToolchain;
17+
18+
#[OptionalCheck([self::class, 'optionalCheck'])]
19+
class LlvmCompilerRtCheck
20+
{
21+
public static function optionalCheck(): bool
22+
{
23+
return ApplicationContext::get(ToolchainInterface::class) instanceof ZigToolchain;
24+
}
25+
26+
/** @noinspection PhpUnused */
27+
#[CheckItem('if llvm-compiler-rt is built for current target', level: 799)]
28+
public function checkLlvmCompilerRt(): CheckResult
29+
{
30+
$libDir = PKG_ROOT_PATH . '/zig/lib/' . SystemTarget::getCanonicalTriple();
31+
if (new llvm_compiler_rt()->isBuilt($libDir)) {
32+
return CheckResult::ok($libDir);
33+
}
34+
return CheckResult::fail('llvm-compiler-rt is not built for ' . SystemTarget::getCanonicalTriple(), 'build-llvm-compiler-rt');
35+
}
36+
37+
#[FixItem('build-llvm-compiler-rt')]
38+
public function fixLlvmCompilerRt(): bool
39+
{
40+
$installer = new PackageInstaller(interactive: false);
41+
$installer->addInstallPackage('llvm-compiler-rt');
42+
$installer->run(true);
43+
new llvm_compiler_rt()->buildForTriple();
44+
$libDir = PKG_ROOT_PATH . '/zig/lib/' . SystemTarget::getCanonicalTriple();
45+
return new llvm_compiler_rt()->isBuilt($libDir);
46+
}
47+
}

src/StaticPHP/Toolchain/ZigToolchain.php

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,21 @@
44

55
namespace StaticPHP\Toolchain;
66

7+
use Package\Artifact\llvm_compiler_rt;
8+
use StaticPHP\DI\ApplicationContext;
9+
use StaticPHP\Package\PackageBuilder;
10+
use StaticPHP\Package\PackageInstaller;
711
use StaticPHP\Runtime\SystemTarget;
812
use StaticPHP\Toolchain\Interface\UnixToolchainInterface;
913
use StaticPHP\Util\GlobalEnvManager;
14+
use StaticPHP\Util\InteractiveTerm;
1015
use StaticPHP\Util\System\LinuxUtil;
16+
use ZM\Logger\ConsoleColor;
1117

1218
class ZigToolchain implements UnixToolchainInterface
1319
{
20+
private static bool $afterInitDone = false;
21+
1422
public function initEnv(): void
1523
{
1624
// Set environment variables for zig toolchain
@@ -19,11 +27,15 @@ public function initEnv(): void
1927
GlobalEnvManager::putenv('SPC_DEFAULT_AR=zig-ar');
2028
GlobalEnvManager::putenv('SPC_DEFAULT_RANLIB=zig-ranlib');
2129
GlobalEnvManager::putenv('SPC_DEFAULT_LD=zig-ld.lld');
30+
GlobalEnvManager::addPathIfNotExists($this->getPath());
2231
}
2332

2433
public function afterInit(): void
2534
{
26-
GlobalEnvManager::addPathIfNotExists($this->getPath());
35+
if (self::$afterInitDone) {
36+
return;
37+
}
38+
self::$afterInitDone = true;
2739
f_passthru('ulimit -n 2048'); // zig opens extra file descriptors, so when a lot of extensions are built statically, 1024 is not enough
2840
$cflags = getenv('SPC_DEFAULT_CFLAGS') ?: '';
2941
$cxxflags = getenv('SPC_DEFAULT_CXXFLAGS') ?: '';
@@ -52,6 +64,8 @@ public function afterInit(): void
5264
// zig-cc/clang treats strlcpy/strlcat as compiler builtins, so configure link tests pass (HAVE_STRLCPY=1)
5365
$extra_vars = getenv('SPC_EXTRA_PHP_VARS') ?: '';
5466
GlobalEnvManager::putenv("SPC_EXTRA_PHP_VARS=ac_cv_func_strlcpy=no ac_cv_func_strlcat=no {$extra_vars}");
67+
68+
$this->ensureCompilerRt();
5569
}
5670

5771
public function getCompilerInfo(): ?string
@@ -87,6 +101,48 @@ public function isStatic(): bool
87101
return false;
88102
}
89103

104+
private function ensureCompilerRt(): void
105+
{
106+
$rt = new llvm_compiler_rt();
107+
$triple = SystemTarget::getCanonicalTriple();
108+
$libDir = PKG_ROOT_PATH . '/zig/lib/' . $triple;
109+
if ($rt->isBuilt($libDir)) {
110+
return;
111+
}
112+
if (!is_dir(SOURCE_PATH . '/llvm-compiler-rt/lib/profile')) {
113+
// Source not yet downloaded; install via nested PackageInstaller. The recursion guard
114+
// on afterInit prevents the nested run from re-entering this method. Save the outer
115+
// installer/builder in the container so executors keep seeing the outer one after.
116+
// The PackageInstaller surfaces its own spinner for the install; AfterBinaryExtract
117+
// builds for the current triple, so we're done after run().
118+
$outerInstaller = ApplicationContext::tryGet(PackageInstaller::class);
119+
$outerBuilder = ApplicationContext::tryGet(PackageBuilder::class);
120+
try {
121+
new PackageInstaller()
122+
->addInstallPackage('llvm-compiler-rt')
123+
->run(true);
124+
} finally {
125+
if ($outerInstaller !== null) {
126+
ApplicationContext::set(PackageInstaller::class, $outerInstaller);
127+
}
128+
if ($outerBuilder !== null) {
129+
ApplicationContext::set(PackageBuilder::class, $outerBuilder);
130+
}
131+
}
132+
return;
133+
}
134+
// Source already extracted from a previous run on a different triple; rebuild here with our
135+
// own progress spinner since we're outside the PackageInstaller flow.
136+
InteractiveTerm::indicateProgress('Building llvm-compiler-rt for ' . ConsoleColor::yellow($triple));
137+
try {
138+
$rt->buildForTriple();
139+
} catch (\Throwable $e) {
140+
InteractiveTerm::finish('Build llvm-compiler-rt for ' . ConsoleColor::red($triple) . ' failed', false);
141+
throw $e;
142+
}
143+
InteractiveTerm::finish('Built llvm-compiler-rt for ' . ConsoleColor::green($triple));
144+
}
145+
90146
private function getPath(): string
91147
{
92148
return PKG_ROOT_PATH . '/zig';

0 commit comments

Comments
 (0)