mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-02 14:25:41 +08:00
turn llvm-tools into a doctor check. llvm-runtime is a target now
This commit is contained in:
6
config/pkg/target/llvm-compiler-rt.yml
Normal file
6
config/pkg/target/llvm-compiler-rt.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
llvm-compiler-rt:
|
||||
type: target
|
||||
artifact:
|
||||
binary: custom
|
||||
depends:
|
||||
- zig
|
||||
@@ -1,4 +1,6 @@
|
||||
clang-runtime-bits:
|
||||
llvm-tools:
|
||||
type: target
|
||||
artifact:
|
||||
binary: custom
|
||||
depends:
|
||||
- zig
|
||||
@@ -11,10 +11,11 @@ use StaticPHP\Attribute\Artifact\AfterBinaryExtract;
|
||||
use StaticPHP\Attribute\Artifact\CustomBinary;
|
||||
use StaticPHP\Attribute\Artifact\CustomBinaryCheckUpdate;
|
||||
use StaticPHP\Exception\DownloaderException;
|
||||
use StaticPHP\Runtime\SystemTarget;
|
||||
|
||||
class clang_runtime_bits
|
||||
class llvm_compiler_rt
|
||||
{
|
||||
#[CustomBinary('clang-runtime-bits', [
|
||||
#[CustomBinary('llvm-compiler-rt', [
|
||||
'linux-x86_64',
|
||||
'linux-aarch64',
|
||||
])]
|
||||
@@ -26,10 +27,10 @@ class clang_runtime_bits
|
||||
$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}/clang-runtime-bits', verified: false, version: $llvmVersion);
|
||||
return DownloadResult::archive($tarball, ['url' => $url, 'version' => $llvmVersion], extract: '{pkg_root_path}/llvm-compiler-rt', verified: false, version: $llvmVersion);
|
||||
}
|
||||
|
||||
#[CustomBinaryCheckUpdate('clang-runtime-bits', [
|
||||
#[CustomBinaryCheckUpdate('llvm-compiler-rt', [
|
||||
'linux-x86_64',
|
||||
'linux-aarch64',
|
||||
])]
|
||||
@@ -44,43 +45,61 @@ class clang_runtime_bits
|
||||
);
|
||||
}
|
||||
|
||||
#[AfterBinaryExtract('clang-runtime-bits', [
|
||||
#[AfterBinaryExtract('llvm-compiler-rt', [
|
||||
'linux-x86_64',
|
||||
'linux-aarch64',
|
||||
])]
|
||||
public function postExtract(string $target_path): void
|
||||
{
|
||||
$this->buildForCurrentTarget($target_path);
|
||||
}
|
||||
|
||||
public function buildForCurrentTarget(?string $sourceDir = null): bool
|
||||
{
|
||||
$sourceDir ??= PKG_ROOT_PATH . '/llvm-compiler-rt';
|
||||
$triple = SystemTarget::getCanonicalTriple();
|
||||
$libDir = PKG_ROOT_PATH . '/zig/lib/' . $triple;
|
||||
if ($this->isBuilt($libDir)) {
|
||||
return true;
|
||||
}
|
||||
if (!is_dir($sourceDir)) {
|
||||
logger()->warning("[llvm-compiler-rt] source dir missing at {$sourceDir}; cannot build runtime bits for {$triple}");
|
||||
return false;
|
||||
}
|
||||
$llvmVersion = $this->detectZigLlvmVersion();
|
||||
if ($llvmVersion === null) {
|
||||
logger()->warning('[llvm-compiler-rt] could not detect bundled clang version; skipping runtime bit build (PGO + shared libs without __dso_handle will fail to link)');
|
||||
return false;
|
||||
}
|
||||
logger()->info("Building llvm compiler-rt bits for {$triple} (LLVM {$llvmVersion})");
|
||||
|
||||
$zig = PKG_ROOT_PATH . '/zig/zig';
|
||||
$libDir = PKG_ROOT_PATH . '/zig/lib';
|
||||
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) && file_exists($crtBegin) && file_exists($crtEnd)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$llvmVersion = $this->detectZigLlvmVersion();
|
||||
if ($llvmVersion === null) {
|
||||
logger()->warning('[clang-runtime-bits] could not detect bundled clang version; skipping runtime bit build (PGO + shared libs without __dso_handle will fail to link)');
|
||||
return;
|
||||
}
|
||||
logger()->info("Building clang runtime bits for LLVM {$llvmVersion} (zig's bundled clang)");
|
||||
|
||||
f_mkdir($libDir, recursive: true);
|
||||
if (!file_exists($profileLib)) {
|
||||
$this->buildProfileRuntime($zig, $target_path, $profileLib);
|
||||
$this->buildProfileRuntime($zig, $sourceDir, $profileLib, $triple);
|
||||
}
|
||||
if (!file_exists($crtBegin) || !file_exists($crtEnd)) {
|
||||
$this->buildCrtObjects($zig, $target_path, $crtBegin, $crtEnd);
|
||||
$this->buildCrtObjects($zig, $sourceDir, $crtBegin, $crtEnd, $triple);
|
||||
}
|
||||
return $this->isBuilt($libDir);
|
||||
}
|
||||
|
||||
private function buildProfileRuntime(string $zig, string $srcRoot, string $libPath): void
|
||||
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 buildProfileRuntime(string $zig, string $srcRoot, string $libPath, string $triple): void
|
||||
{
|
||||
$profileSrc = "{$srcRoot}/lib/profile";
|
||||
$profileInc = "{$srcRoot}/include";
|
||||
if (!is_dir($profileSrc)) {
|
||||
logger()->warning("[clang-runtime-bits] profile src dir missing at {$profileSrc} — PGO will not work");
|
||||
logger()->warning("[llvm-compiler-rt] profile src dir missing at {$profileSrc} — PGO will not work");
|
||||
return;
|
||||
}
|
||||
$sources = array_merge(
|
||||
@@ -99,9 +118,9 @@ class clang_runtime_bits
|
||||
return true;
|
||||
});
|
||||
|
||||
$objDir = "{$srcRoot}/obj-profile";
|
||||
$objDir = "{$srcRoot}/obj-profile-{$triple}";
|
||||
f_mkdir($objDir, recursive: true);
|
||||
$cflags = '-c -O2 -fPIC -fvisibility=hidden ' .
|
||||
$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 = [];
|
||||
@@ -117,32 +136,32 @@ class clang_runtime_bits
|
||||
if (!$this->runZigCmd($arCmd, $libPath, 'zig ar failed')) {
|
||||
return;
|
||||
}
|
||||
logger()->info('[clang-runtime-bits] libclang_rt.profile.a installed (' . filesize($libPath) . ' bytes)');
|
||||
logger()->info('[llvm-compiler-rt] libclang_rt.profile.a installed (' . filesize($libPath) . ' bytes)');
|
||||
}
|
||||
|
||||
private function buildCrtObjects(string $zig, string $srcRoot, string $crtBegin, string $crtEnd): void
|
||||
private function buildCrtObjects(string $zig, 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)) {
|
||||
logger()->error("[clang-runtime-bits] crtbegin/crtend source missing under {$srcRoot}/lib/builtins — shared libs will lack __dso_handle");
|
||||
logger()->error("[llvm-compiler-rt] crtbegin/crtend source missing under {$srcRoot}/lib/builtins — shared libs will lack __dso_handle");
|
||||
return;
|
||||
}
|
||||
$cflags = '-c -O2 -fPIC -fvisibility=hidden -DCRT_HAS_INITFINI_ARRAY';
|
||||
$cflags = "-target {$triple} -c -O2 -fPIC -fvisibility=hidden -DCRT_HAS_INITFINI_ARRAY";
|
||||
foreach ([[$beginSrc, $crtBegin], [$endSrc, $crtEnd]] as [$src, $dst]) {
|
||||
$cmd = escapeshellarg($zig) . ' cc ' . $cflags . ' -o ' . escapeshellarg($dst) . ' ' . escapeshellarg($src) . ' 2>&1';
|
||||
if (!$this->runZigCmd($cmd, $dst, "failed to compile {$src}")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
logger()->info('[clang-runtime-bits] clang_rt.crtbegin.o + clang_rt.crtend.o installed (' . filesize($crtBegin) . ' + ' . filesize($crtEnd) . ' bytes)');
|
||||
logger()->info('[llvm-compiler-rt] clang_rt.crtbegin.o + clang_rt.crtend.o installed (' . filesize($crtBegin) . ' + ' . filesize($crtEnd) . ' bytes)');
|
||||
}
|
||||
|
||||
private function runZigCmd(string $cmd, string $dst, string $errPrefix): bool
|
||||
{
|
||||
exec($cmd, $out, $rc);
|
||||
if ($rc !== 0 || !is_file($dst)) {
|
||||
logger()->warning("[clang-runtime-bits] {$errPrefix}: " . implode("\n", $out));
|
||||
logger()->warning("[llvm-compiler-rt] {$errPrefix}: " . implode("\n", $out));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
180
src/Package/Artifact/llvm_tools.php
Normal file
180
src/Package/Artifact/llvm_tools.php
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Package\Artifact;
|
||||
|
||||
use StaticPHP\Artifact\ArtifactDownloader;
|
||||
use StaticPHP\Artifact\Downloader\DownloadResult;
|
||||
use StaticPHP\Artifact\Downloader\Type\CheckUpdateResult;
|
||||
use StaticPHP\Attribute\Artifact\AfterBinaryExtract;
|
||||
use StaticPHP\Attribute\Artifact\CustomBinary;
|
||||
use StaticPHP\Attribute\Artifact\CustomBinaryCheckUpdate;
|
||||
use StaticPHP\Exception\DownloaderException;
|
||||
|
||||
class llvm_tools
|
||||
{
|
||||
public const TOOLS = ['llvm-objcopy', 'llvm-strip', 'llvm-profdata'];
|
||||
|
||||
#[CustomBinary('llvm-tools', [
|
||||
'linux-x86_64',
|
||||
'linux-aarch64',
|
||||
'macos-x86_64',
|
||||
'macos-aarch64',
|
||||
])]
|
||||
public function downBinary(ArtifactDownloader $downloader): DownloadResult
|
||||
{
|
||||
$llvmVersion = $this->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;
|
||||
}
|
||||
}
|
||||
35
src/StaticPHP/Doctor/Item/LlvmToolsCheck.php
Normal file
35
src/StaticPHP/Doctor/Item/LlvmToolsCheck.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace StaticPHP\Doctor\Item;
|
||||
|
||||
use Package\Artifact\llvm_tools;
|
||||
use StaticPHP\Attribute\Doctor\CheckItem;
|
||||
use StaticPHP\Attribute\Doctor\FixItem;
|
||||
use StaticPHP\Doctor\CheckResult;
|
||||
use StaticPHP\Package\PackageInstaller;
|
||||
|
||||
class LlvmToolsCheck
|
||||
{
|
||||
/** @noinspection PhpUnused */
|
||||
#[CheckItem('if llvm-tools (objcopy/strip/profdata) are built', limit_os: 'Linux', level: 798)]
|
||||
public function checkLlvmTools(): CheckResult
|
||||
{
|
||||
$binDir = PKG_ROOT_PATH . '/llvm-tools/bin';
|
||||
if (new llvm_tools()->allBuilt($binDir)) {
|
||||
return CheckResult::ok($binDir);
|
||||
}
|
||||
return CheckResult::fail('llvm-tools are not built', 'build-llvm-tools');
|
||||
}
|
||||
|
||||
#[FixItem('build-llvm-tools')]
|
||||
public function fixLlvmTools(): bool
|
||||
{
|
||||
$installer = new PackageInstaller(interactive: false);
|
||||
$installer->addInstallPackage('llvm-tools');
|
||||
$installer->run(true);
|
||||
new llvm_tools()->buildForHost();
|
||||
return new llvm_tools()->allBuilt(PKG_ROOT_PATH . '/llvm-tools/bin');
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ 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;
|
||||
|
||||
@@ -41,30 +42,31 @@ class ZigCheck
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
#[CheckItem('if clang runtime bits are built', limit_os: 'Linux', level: 799)]
|
||||
public function checkClangRuntimeBits(): ?CheckResult
|
||||
#[CheckItem('if llvm compiler-rt bits are built', limit_os: 'Linux', level: 799)]
|
||||
public function checkCompilerRtBits(): ?CheckResult
|
||||
{
|
||||
// Skip if zig is not installed yet (zig check runs at level 800)
|
||||
if (!new PackageInstaller()->addInstallPackage('zig')->isPackageInstalled('zig')) {
|
||||
return null;
|
||||
}
|
||||
$libDir = PKG_ROOT_PATH . '/zig/lib';
|
||||
$libDir = PKG_ROOT_PATH . '/zig/lib/' . SystemTarget::getCanonicalTriple();
|
||||
if (file_exists("{$libDir}/libclang_rt.profile.a")
|
||||
&& file_exists("{$libDir}/clang_rt.crtbegin.o")
|
||||
&& file_exists("{$libDir}/clang_rt.crtend.o")
|
||||
) {
|
||||
return CheckResult::ok("{$libDir}/libclang_rt.profile.a");
|
||||
}
|
||||
return CheckResult::fail('clang runtime bits are not built', 'build-clang-runtime-bits');
|
||||
return CheckResult::fail('llvm compiler-rt bits are not built for ' . SystemTarget::getCanonicalTriple(), 'build-llvm-compiler-rt');
|
||||
}
|
||||
|
||||
#[FixItem('build-clang-runtime-bits')]
|
||||
public function fixClangRuntimeBits(): bool
|
||||
#[FixItem('build-llvm-compiler-rt')]
|
||||
public function fixCompilerRtBits(): bool
|
||||
{
|
||||
$installer = new PackageInstaller(interactive: false);
|
||||
$installer->addInstallPackage('clang-runtime-bits');
|
||||
$installer->addInstallPackage('llvm-compiler-rt');
|
||||
$installer->run(true);
|
||||
$libDir = PKG_ROOT_PATH . '/zig/lib';
|
||||
new \Package\Artifact\llvm_compiler_rt()->buildForCurrentTarget();
|
||||
$libDir = PKG_ROOT_PATH . '/zig/lib/' . SystemTarget::getCanonicalTriple();
|
||||
return file_exists("{$libDir}/libclang_rt.profile.a")
|
||||
&& file_exists("{$libDir}/clang_rt.crtbegin.o")
|
||||
&& file_exists("{$libDir}/clang_rt.crtend.o");
|
||||
|
||||
@@ -15,7 +15,6 @@ use StaticPHP\Util\FileSystem;
|
||||
use StaticPHP\Util\GlobalPathTrait;
|
||||
use StaticPHP\Util\InteractiveTerm;
|
||||
use StaticPHP\Util\System\LinuxUtil;
|
||||
use StaticPHP\Util\System\UnixUtil;
|
||||
|
||||
class PackageBuilder
|
||||
{
|
||||
@@ -179,7 +178,7 @@ class PackageBuilder
|
||||
if (SystemTarget::getTargetOS() === 'Darwin') {
|
||||
shell()->exec("dsymutil -f {$binary_path} -o {$debug_file}");
|
||||
} elseif (SystemTarget::getTargetOS() === 'Linux') {
|
||||
$objcopy = LinuxUtil::findCommand('llvm-objcopy') ?: 'objcopy';
|
||||
$objcopy = getenv('OBJCOPY');
|
||||
if ($eu_strip = LinuxUtil::findCommand('eu-strip')) {
|
||||
shell()
|
||||
->exec("{$eu_strip} -f {$debug_file} {$binary_path}")
|
||||
@@ -201,7 +200,7 @@ class PackageBuilder
|
||||
*/
|
||||
public function stripBinary(string $binary_path): void
|
||||
{
|
||||
$strip = UnixUtil::findCommand('llvm-strip') ?: 'strip';
|
||||
$strip = PKG_ROOT_PATH . '/llvm-tools/bin/llvm-strip';
|
||||
shell()->exec(match (SystemTarget::getTargetOS()) {
|
||||
'Darwin' => "{$strip} -S {$binary_path}",
|
||||
'Linux' => "{$strip} --strip-unneeded {$binary_path}",
|
||||
|
||||
@@ -128,6 +128,31 @@ class SystemTarget
|
||||
return in_array(self::getTargetOS(), ['Linux', 'Darwin', 'BSD']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the canonical target triple (arch-os-abi) for per-target build
|
||||
* artifacts. Always returns a non-null triple, falling back to a host-derived
|
||||
* triple when SPC_TARGET is unset or names 'native'.
|
||||
* Strips libc version suffix (-gnu.2.17 → -gnu) and trailing flags (' -dynamic').
|
||||
*/
|
||||
public static function getCanonicalTriple(): string
|
||||
{
|
||||
$target = (string) getenv('SPC_TARGET');
|
||||
if ($target !== '' && !str_contains($target, 'native')) {
|
||||
$cleaned = (string) preg_replace('/(-gnu|-musl)\.[\d.]+/', '$1', $target);
|
||||
$cleaned = preg_split('/\s+/', trim($cleaned))[0] ?? '';
|
||||
if ($cleaned !== '') {
|
||||
return $cleaned;
|
||||
}
|
||||
}
|
||||
$arch = self::getTargetArch();
|
||||
return match (self::getTargetOS()) {
|
||||
'Linux' => $arch . '-linux-' . (self::getLibc() === 'musl' ? 'musl' : 'gnu'),
|
||||
'Darwin' => $arch . '-macos-none',
|
||||
'Windows' => $arch . '-windows-gnu',
|
||||
default => $arch . '-unknown-unknown',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a GNU host triple for autoconf --host= when SPC_TARGET names an
|
||||
* architecture different from the build host (true cross-compile).
|
||||
|
||||
@@ -38,6 +38,7 @@ class ClangNativeToolchain implements UnixToolchainInterface
|
||||
default => throw new EnvironmentException(__CLASS__ . ' is not supported on ' . PHP_OS_FAMILY . '.'),
|
||||
};
|
||||
}
|
||||
GlobalEnvManager::putenv('OBJCOPY=' . PKG_ROOT_PATH . '/llvm-tools/bin/llvm-objcopy');
|
||||
}
|
||||
|
||||
public function getCompilerInfo(): ?string
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace StaticPHP\Toolchain;
|
||||
|
||||
use StaticPHP\Runtime\SystemTarget;
|
||||
use StaticPHP\Toolchain\Interface\UnixToolchainInterface;
|
||||
use StaticPHP\Util\GlobalEnvManager;
|
||||
use StaticPHP\Util\System\LinuxUtil;
|
||||
@@ -34,7 +35,8 @@ class ZigToolchain implements UnixToolchainInterface
|
||||
GlobalEnvManager::putenv("SPC_DEFAULT_CXXFLAGS={$cxxflags}");
|
||||
GlobalEnvManager::putenv("SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS={$extraCflags}");
|
||||
GlobalEnvManager::putenv('RANLIB=zig-ranlib');
|
||||
GlobalEnvManager::putenv('OBJCOPY=zig-objcopy');
|
||||
GlobalEnvManager::putenv('SPC_COMPILER_RT_DIR=' . PKG_ROOT_PATH . '/zig/lib/' . SystemTarget::getCanonicalTriple());
|
||||
GlobalEnvManager::putenv('OBJCOPY=' . PKG_ROOT_PATH . '/llvm-tools/bin/llvm-objcopy');
|
||||
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
|
||||
if (!str_contains($extra_libs, '-lunwind')) {
|
||||
// Add unwind library if not already present
|
||||
|
||||
@@ -221,9 +221,6 @@ final class PgoContext
|
||||
|
||||
public function mergeProfiles(): void
|
||||
{
|
||||
if (trim((string) shell_exec('command -v llvm-profdata 2>/dev/null')) === '') {
|
||||
throw new WrongUsageException('PGO --phase=use: llvm-profdata not on PATH');
|
||||
}
|
||||
foreach ($this->trainableSapis as $sapi) {
|
||||
$this->mergeSapi($sapi);
|
||||
}
|
||||
@@ -248,7 +245,8 @@ final class PgoContext
|
||||
$out = $this->profDataFile($sapi);
|
||||
$inputs = array_merge($raws, $csRaws);
|
||||
$argv = implode(' ', array_map('escapeshellarg', $inputs));
|
||||
shell()->exec('llvm-profdata merge --failure-mode=warn -output=' . escapeshellarg($out) . ' ' . $argv);
|
||||
$profdata = PKG_ROOT_PATH . '/llvm-tools/bin/llvm-profdata';
|
||||
shell()->exec(escapeshellarg($profdata) . ' merge --failure-mode=warn -output=' . escapeshellarg($out) . ' ' . $argv);
|
||||
if (!is_file($out) || filesize($out) === 0) {
|
||||
throw new WrongUsageException("PGO --phase=use: empty merge output for {$sapi}");
|
||||
}
|
||||
|
||||
@@ -37,6 +37,10 @@ while [[ $# -gt 0 ]]; do
|
||||
PARSED_ARGS+=("${OPT_NAME}=${OPT_VALUE}")
|
||||
shift
|
||||
;;
|
||||
-mtune=generic)
|
||||
PARSED_ARGS+=("-mtune=baseline")
|
||||
shift
|
||||
;;
|
||||
-Wlogical-op|-Wduplicated-cond|-Wduplicated-branches|-Wno-clobbered|-Wjump-misses-init|-Wformat-truncation|-Warray-bounds=*|-Wimplicit-fallthrough=*)
|
||||
# GCC-only warning flags that clang/zig doesn't recognize; drop to silence -Wunknown-warning-option noise
|
||||
shift
|
||||
@@ -59,11 +63,13 @@ for _a in "${PARSED_ARGS[@]}"; do
|
||||
esac
|
||||
done
|
||||
[[ "$SPC_COMPILER_EXTRA" == *-fprofile-generate* || "$SPC_COMPILER_EXTRA" == *-fcs-profile-generate* ]] && NEED_PROFILE_RT=1
|
||||
if [[ $IS_LINK -eq 1 && $NEED_PROFILE_RT -eq 1 && -f "$SCRIPT_DIR/lib/libclang_rt.profile.a" ]]; then
|
||||
PARSED_ARGS+=("$SCRIPT_DIR/lib/libclang_rt.profile.a" "-Wl,-u,__llvm_profile_runtime")
|
||||
|
||||
RT_DIR="${SPC_COMPILER_RT_DIR:-}"
|
||||
if [[ $IS_LINK -eq 1 && $NEED_PROFILE_RT -eq 1 && -n "$RT_DIR" && -f "$RT_DIR/libclang_rt.profile.a" ]]; then
|
||||
PARSED_ARGS+=("$RT_DIR/libclang_rt.profile.a" "-Wl,-u,__llvm_profile_runtime")
|
||||
fi
|
||||
if [[ $IS_LINK -eq 1 && $NEED_CRT -eq 1 && -f "$SCRIPT_DIR/lib/clang_rt.crtbegin.o" && -f "$SCRIPT_DIR/lib/clang_rt.crtend.o" ]]; then
|
||||
PARSED_ARGS+=("$SCRIPT_DIR/lib/clang_rt.crtbegin.o" "$SCRIPT_DIR/lib/clang_rt.crtend.o")
|
||||
if [[ $IS_LINK -eq 1 && $NEED_CRT -eq 1 && -n "$RT_DIR" && -f "$RT_DIR/clang_rt.crtbegin.o" && -f "$RT_DIR/clang_rt.crtend.o" ]]; then
|
||||
PARSED_ARGS+=("$RT_DIR/clang_rt.crtbegin.o" "$RT_DIR/clang_rt.crtend.o")
|
||||
fi
|
||||
|
||||
[[ -n "$SPC_TARGET" ]] && TARGET="-target $SPC_TARGET" || TARGET=""
|
||||
|
||||
Reference in New Issue
Block a user