toolchain fixes forward port from v2-pgo

This commit is contained in:
henderkes
2026-05-10 19:27:36 +07:00
parent ca8a28a364
commit 4f7694267b
11 changed files with 260 additions and 35 deletions

View File

@@ -100,6 +100,7 @@ SPC_TARGET=${GNU_ARCH}-linux-musl
CC=${SPC_DEFAULT_CC}
CXX=${SPC_DEFAULT_CXX}
AR=${SPC_DEFAULT_AR}
RANLIB=${SPC_DEFAULT_RANLIB}
LD=${SPC_DEFAULT_LD}
; default compiler flags, used in CMake toolchain file, openssl and pkg-config build
SPC_DEFAULT_CFLAGS="-fPIC -O3 -pipe -fno-plt -fno-semantic-interposition -fstack-clash-protection -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffunction-sections -fdata-sections"
@@ -125,6 +126,8 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE
SPC_CMD_VAR_PHP_MAKE_EXTRA_CXXFLAGS="-g -fstack-protector-strong -fno-ident -fPIE -fvisibility=hidden -fvisibility-inlines-hidden ${SPC_DEFAULT_CXXFLAGS}"
; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS=""
; EXTRA_LDFLAGS_PROGRAM for `make` php; appended only to SAPI executable links (cli/fpm/cgi/micro/embed). Used by PGO to inject -fprofile-use= without polluting libphp.{a,so}.
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM=""
; optional, path to openssl conf. This affects where openssl will look for the default CA.
; default on Debian/Alpine: /etc/ssl, default on RHEL: /etc/pki/tls
@@ -140,6 +143,7 @@ SPC_USE_LLVM=system
CC=${SPC_DEFAULT_CC}
CXX=${SPC_DEFAULT_CXX}
AR=${SPC_DEFAULT_AR}
RANLIB=${SPC_DEFAULT_RANLIB}
LD=${SPC_DEFAULT_LD}
; default compiler flags, used in CMake toolchain file, openssl and pkg-config build
SPC_DEFAULT_CFLAGS="--target=${MAC_ARCH}-apple-darwin -O3 -fno-plt -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffunction-sections -fdata-sections"
@@ -163,5 +167,7 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -fvis
SPC_CMD_VAR_PHP_MAKE_EXTRA_CXXFLAGS="-g -fstack-protector-strong -fno-ident -fpie -fvisibility=hidden -fvisibility-inlines-hidden -Werror=unknown-warning-option ${SPC_DEFAULT_CXXFLAGS}"
; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.dylib
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS=""
; EXTRA_LDFLAGS_PROGRAM for `make` php; appended only to SAPI executable links (cli/fpm/cgi/micro/embed). Used by PGO to inject -fprofile-use= without polluting libphp.{a,dylib}.
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM=""
; minimum compatible macOS version (LLVM vars, availability not guaranteed)
MACOSX_DEPLOYMENT_TARGET=12.0

View File

@@ -109,7 +109,7 @@ class go_xcaddy
'GOROOT' => "{$target_path}",
'GOBIN' => "{$target_path}/bin",
'GOPATH' => "{$target_path}/go",
])->exec('CC=cc go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest');
])->exec('CGO_ENABLED=0 go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest');
GlobalEnvManager::addPathIfNotExists("{$target_path}/bin");
}
}

View File

@@ -12,6 +12,7 @@ use StaticPHP\Attribute\Artifact\CustomBinary;
use StaticPHP\Attribute\Artifact\CustomBinaryCheckUpdate;
use StaticPHP\Exception\DownloaderException;
use StaticPHP\Runtime\SystemTarget;
use StaticPHP\Util\FileSystem;
class zig
{
@@ -131,5 +132,151 @@ class zig
chmod("{$target_path}/zig-ld.lld", 0755);
chmod("{$target_path}/zig-ranlib", 0755);
chmod("{$target_path}/zig-objcopy", 0755);
// Build the clang runtime bits zig 0.15+ doesn't ship: profile runtime
// (so -fprofile-generate actually emits .profraw) and crtbegin/crtend
// (so shared libs get __dso_handle and the __cxa_finalize atexit hook).
// These get auto-linked by the zig-cc wrapper when the right flags fly past.
$this->buildClangRuntimeBits($target_path);
}
/**
* Detect the bundled clang version and build the missing clang runtime
* archives into `<zig>/lib/`. Compiles from a 2 MB compiler-rt-<llvm>.src
* tarball — far cheaper than fetching the 2 GB prebuilt LLVM tarball.
*/
private function buildClangRuntimeBits(string $zig_bin_dir): void
{
if (PHP_OS_FAMILY !== 'Linux') {
return;
}
$libDir = "{$zig_bin_dir}/lib";
$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;
}
$zig = "{$zig_bin_dir}/zig";
$verLine = trim((string) shell_exec(escapeshellarg($zig) . ' cc --version 2>/dev/null'));
if (!preg_match('/clang version (\d+\.\d+\.\d+)/', $verLine, $m)) {
logger()->warning('[zig] could not detect bundled clang version; skipping runtime bit build (PGO + shared libs without __dso_handle will fail to link)');
return;
}
$llvmVersion = $m[1];
logger()->info("Building clang runtime bits for LLVM {$llvmVersion} (zig's bundled clang)");
$srcRoot = $this->fetchCompilerRtSource($llvmVersion);
if ($srcRoot === null) {
return;
}
f_mkdir($libDir, recursive: true);
if (!file_exists($profileLib)) {
$this->buildProfileRuntime($zig, $srcRoot, $profileLib);
}
if (!file_exists($crtBegin) || !file_exists($crtEnd)) {
$this->buildCrtObjects($zig, $srcRoot, $crtBegin, $crtEnd);
}
FileSystem::removeDir($srcRoot);
}
private function fetchCompilerRtSource(string $llvmVersion): ?string
{
$tarball = "compiler-rt-{$llvmVersion}.src.tar.xz";
$url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-{$llvmVersion}/{$tarball}";
$tarballPath = DOWNLOAD_PATH . '/' . $tarball;
if (!file_exists($tarballPath)) {
try {
default_shell()->executeCurlDownload($url, $tarballPath);
} catch (\Throwable $e) {
logger()->warning("[zig] failed to download {$tarball}: {$e->getMessage()}");
return null;
}
}
$srcRoot = PKG_ROOT_PATH . "/compiler-rt-src-{$llvmVersion}";
FileSystem::removeDir($srcRoot);
FileSystem::createDir($srcRoot);
try {
default_shell()->executeTarExtract($tarballPath, $srcRoot, 'xz');
} catch (\Throwable $e) {
logger()->warning("[zig] failed to extract {$tarball}: {$e->getMessage()}");
return null;
}
return $srcRoot;
}
private function buildProfileRuntime(string $zig, string $srcRoot, string $libPath): void
{
$profileSrc = "{$srcRoot}/lib/profile";
$profileInc = "{$srcRoot}/include";
if (!is_dir($profileSrc)) {
logger()->warning("[zig] profile src dir missing at {$profileSrc} — PGO will not work");
return;
}
$sources = array_merge(
glob("{$profileSrc}/*.c") ?: [],
glob("{$profileSrc}/*.cpp") ?: []
);
// Keep Linux-only compilation units; the others bring in OS-specific headers
// we can't satisfy without their respective SDKs.
$skip = ['/PlatformAIX', '/PlatformDarwin', '/PlatformFuchsia', '/PlatformOther', '/PlatformWindows', '/WindowsMMap'];
$sources = array_filter($sources, function ($f) use ($skip) {
foreach ($skip as $s) {
if (str_contains($f, $s)) {
return false;
}
}
return true;
});
$objDir = "{$srcRoot}/obj-profile";
f_mkdir($objDir, recursive: true);
$cflags = '-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';
$cmd = escapeshellarg($zig) . ' cc ' . $cflags . ' -o ' . escapeshellarg($obj) . ' ' . escapeshellarg($src) . ' 2>&1';
if (!$this->runZigCmd($cmd, $obj, "failed to compile {$src}")) {
return;
}
$objs[] = $obj;
}
$arCmd = escapeshellarg($zig) . ' ar rcs ' . escapeshellarg($libPath) . ' ' . implode(' ', array_map('escapeshellarg', $objs)) . ' 2>&1';
if (!$this->runZigCmd($arCmd, $libPath, 'zig ar failed')) {
return;
}
logger()->info('[zig] libclang_rt.profile.a installed (' . filesize($libPath) . ' bytes)');
}
private function buildCrtObjects(string $zig, string $srcRoot, string $crtBegin, string $crtEnd): void
{
$beginSrc = "{$srcRoot}/lib/builtins/crtbegin.c";
$endSrc = "{$srcRoot}/lib/builtins/crtend.c";
if (!is_file($beginSrc) || !is_file($endSrc)) {
logger()->error("[zig] 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';
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('[zig] 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("[zig] {$errPrefix}: " . implode("\n", $out));
return false;
}
return true;
}
}

View File

@@ -73,13 +73,20 @@ class LinuxMuslCheck
$prefix = 'sudo ';
logger()->warning('Current user is not root, using sudo for running command');
}
$sysEnv = ['CC' => 'gcc', 'CXX' => 'g++', 'AR' => 'ar', 'LD' => 'ld', 'RANLIB' => 'ranlib'];
$envFlags = '';
foreach ($sysEnv as $k => $v) {
$envFlags .= "{$k}={$v} ";
}
$envFlags = rtrim($envFlags);
$shell = shell()->cd(SOURCE_PATH . '/musl-wrapper')
->exec('CC=gcc CXX=g++ AR=ar LD=ld ./configure --disable-gcc-wrapper')
->exec('CC=gcc CXX=g++ AR=ar LD=ld make -j');
->setEnv($sysEnv)
->exec('./configure --disable-gcc-wrapper')
->exec('make -j');
if ($prefix !== '') {
f_passthru('cd ' . SOURCE_PATH . "/musl-wrapper && CC=gcc CXX=g++ AR=ar LD=ld {$prefix}make install");
f_passthru('cd ' . SOURCE_PATH . "/musl-wrapper && {$envFlags} {$prefix}make install");
} else {
$shell->exec("CC=gcc CXX=g++ AR=ar LD=ld {$prefix}make install");
$shell->exec("{$prefix}make install");
}
return true;
}

View File

@@ -274,14 +274,20 @@ class PhpExtensionPackage extends Package
$compiler_extra = trim($compiler_extra . ' -lcompiler_rt');
GlobalEnvManager::putenv("SPC_COMPILER_EXTRA={$compiler_extra}");
}
$config = (new SPCConfigUtil())->getExtensionConfig($this);
// include_suggests so transitive optional libs (e.g. librdkafka → lz4/zstd/ssl) are in the link line
$config = (new SPCConfigUtil())->getExtensionConfig($this, include_suggests: true);
[$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
$preStatic = PHP_OS_FAMILY === 'Darwin' ? '' : '-Wl,--start-group ';
$postStatic = PHP_OS_FAMILY === 'Darwin' ? '' : ' -Wl,--end-group ';
// -Wl,-Bsymbolic: bind zend_* refs to the .so's own copies, not via global lookup
$ldflags = (string) $config['ldflags'];
if (PHP_OS_FAMILY !== 'Darwin' && !str_contains($ldflags, '-Wl,-Bsymbolic')) {
$ldflags = clean_spaces($ldflags . ' -Wl,-Bsymbolic');
}
return [
'CFLAGS' => $config['cflags'],
'CXXFLAGS' => $config['cflags'],
'LDFLAGS' => $config['ldflags'],
'LDFLAGS' => $ldflags,
'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"),
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
];
@@ -303,10 +309,11 @@ class PhpExtensionPackage extends Package
public function configureForUnix(array $env, PhpExtensionPackage $package): void
{
$phpvars = getenv('SPC_EXTRA_PHP_VARS') ?: '';
// CustomPhpConfigureArg keys are OS names ('Linux'/'Darwin'), not platform strings
shell()->cd($package->getSourceDir())
->setEnv($env)
->exec(
'./configure ' . $this->getPhpConfigureArg(SystemTarget::getCurrentPlatformString(), true) .
'./configure ' . $this->getPhpConfigureArg(SystemTarget::getTargetOS(), true) .
' --with-php-config=' . BUILD_BIN_PATH . '/php-config ' .
"--enable-shared --disable-static {$phpvars}"
);
@@ -318,6 +325,8 @@ class PhpExtensionPackage extends Package
#[Stage]
public function makeForUnix(array $env, PhpExtensionPackage $package, PackageBuilder $builder): void
{
// phpize Makefile's _SHARED_LIBADD line misses our static archives — splice them in
$package->patchSharedLibAdd();
shell()->cd($package->getSourceDir())
->setEnv($env)
->exec('make clean')
@@ -325,6 +334,34 @@ class PhpExtensionPackage extends Package
->exec('make install');
}
public function patchSharedLibAdd(): void
{
// include_suggests so transitive optional libs (e.g. librdkafka → lz4/zstd/ssl) are in the link line
$config = (new SPCConfigUtil())->getExtensionConfig($this, include_suggests: true);
[$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
$lstdcpp = str_contains($sharedLibs, '-l:libstdc++.a')
? '-l:libstdc++.a'
: (str_contains($sharedLibs, '-lstdc++') ? '-lstdc++' : '');
$makefile = $this->getSourceDir() . '/Makefile';
if (!is_file($makefile)) {
return;
}
$content = (string) file_get_contents($makefile);
if (!preg_match('/^(.*_SHARED_LIBADD\s*=\s*)(.*)$/m', $content, $m)) {
return;
}
$prefix = $m[1];
$current = trim($m[2]);
$merged = clean_spaces("{$current} {$staticLibs} {$lstdcpp}");
$merged = deduplicate_flags($merged);
\StaticPHP\Util\FileSystem::replaceFileRegex(
$makefile,
'/^(.*_SHARED_LIBADD\s*=.*)$/m',
$prefix . $merged
);
}
/**
* Build shared extension on Unix-like systems.
* Only for internal calling. For external use, call buildShared() instead.
@@ -333,6 +370,21 @@ class PhpExtensionPackage extends Package
*/
public function buildSharedForUnix(PackageBuilder $builder): void
{
// skip virtual addons (arg-type=none + display-name → owning ext); the parent ext built it
$argType = $this->extension_config['arg-type'] ?? null;
$displayName = $this->extension_config['display-name'] ?? null;
if ($argType === 'none' && $displayName !== null && $displayName !== $this->getExtensionName()) {
logger()->info("Skipping virtual extension [{$this->getName()}] — it's part of [{$displayName}].");
return;
}
if (!is_dir($this->getSourceDir())) {
throw new ValidationException(
"Extension source directory not found: {$this->getSourceDir()}",
validation_module: "Extension {$this->getName()} source"
);
}
$env = $this->getSharedExtensionEnv();
$this->runStage([$this, 'phpizeForUnix'], ['env' => $env]);

View File

@@ -230,7 +230,7 @@ class UnixCMakeExecutor extends Executor
// EXE linker flags: base system libs + framework flags for target packages
$exeLinkerFlags = SystemTarget::getRuntimeLibs();
if ($this->package instanceof TargetPackage) {
if ($this->package instanceof TargetPackage && SystemTarget::getTargetOS() === 'Darwin') {
$resolvedNames = array_keys($this->installer->getResolvedPackages());
$resolvedNames[] = $this->package->getName();
$fwFlags = new SPCConfigUtil()->getFrameworksString($resolvedNames);
@@ -302,9 +302,12 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_C_STANDARD_INCLUDE_DIRECTORIES "{$include}")
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES "{$include}")
CMAKE;
// Whoops, linux may need CMAKE_AR sometimes
// pin AR/RANLIB so cmake uses zig-ar/zig-ranlib instead of system /usr/bin/ranlib (zig archives need it)
if (PHP_OS_FAMILY === 'Linux') {
$toolchain .= "\nSET(CMAKE_AR \"ar\")";
$ar = getenv('SPC_DEFAULT_AR') ?: getenv('AR') ?: 'ar';
$ranlib = getenv('SPC_DEFAULT_RANLIB') ?: (getenv('RANLIB') ?: 'ranlib');
$toolchain .= "\nSET(CMAKE_AR \"{$ar}\")";
$toolchain .= "\nSET(CMAKE_RANLIB \"{$ranlib}\")";
}
FileSystem::writeFile(SOURCE_PATH . '/toolchain.cmake', $toolchain);
return $created = realpath(SOURCE_PATH . '/toolchain.cmake');

View File

@@ -15,6 +15,7 @@ class ClangBrewToolchain extends ClangNativeToolchain
GlobalEnvManager::putenv("SPC_DEFAULT_CC={$homebrew_prefix}/opt/llvm/bin/clang");
GlobalEnvManager::putenv("SPC_DEFAULT_CXX={$homebrew_prefix}/opt/llvm/bin/clang++");
GlobalEnvManager::putenv("SPC_DEFAULT_AR={$homebrew_prefix}/opt/llvm/bin/llvm-ar");
GlobalEnvManager::putenv("SPC_DEFAULT_RANLIB={$homebrew_prefix}/opt/llvm/bin/llvm-ranlib");
GlobalEnvManager::putenv('SPC_DEFAULT_LD=ld');
GlobalEnvManager::addPathIfNotExists("{$homebrew_prefix}/opt/llvm/bin");
}

View File

@@ -21,6 +21,7 @@ class ClangNativeToolchain implements UnixToolchainInterface
GlobalEnvManager::putenv('SPC_DEFAULT_CC=clang');
GlobalEnvManager::putenv('SPC_DEFAULT_CXX=clang++');
GlobalEnvManager::putenv('SPC_DEFAULT_AR=ar');
GlobalEnvManager::putenv('SPC_DEFAULT_RANLIB=ranlib');
GlobalEnvManager::putenv('SPC_DEFAULT_LD=ld');
}

View File

@@ -18,6 +18,7 @@ class GccNativeToolchain implements UnixToolchainInterface
GlobalEnvManager::putenv('SPC_DEFAULT_CC=gcc');
GlobalEnvManager::putenv('SPC_DEFAULT_CXX=g++');
GlobalEnvManager::putenv('SPC_DEFAULT_AR=ar');
GlobalEnvManager::putenv('SPC_DEFAULT_RANLIB=ranlib');
GlobalEnvManager::putenv('SPC_DEFAULT_LD=ld');
}

View File

@@ -16,28 +16,8 @@ class ZigToolchain implements UnixToolchainInterface
GlobalEnvManager::putenv('SPC_DEFAULT_CC=zig-cc');
GlobalEnvManager::putenv('SPC_DEFAULT_CXX=zig-c++');
GlobalEnvManager::putenv('SPC_DEFAULT_AR=zig-ar');
GlobalEnvManager::putenv('SPC_DEFAULT_RANLIB=zig-ranlib');
GlobalEnvManager::putenv('SPC_DEFAULT_LD=zig-ld.lld');
// Generate additional objects needed for zig toolchain
$paths = ['/usr/lib/gcc', '/usr/local/lib/gcc'];
$objects = ['crtbeginS.o', 'crtendS.o'];
$found = [];
foreach ($objects as $obj) {
$located = null;
foreach ($paths as $base) {
$output = shell_exec("find {$base} -name {$obj} 2>/dev/null | grep -v '/32/' | head -n 1");
$line = trim((string) $output);
if ($line !== '') {
$located = $line;
break;
}
}
if ($located) {
$found[] = $located;
}
}
GlobalEnvManager::putenv('SPC_EXTRA_RUNTIME_OBJECTS=' . implode(' ', $found));
}
public function afterInit(): void

View File

@@ -1,9 +1,14 @@
#!/usr/bin/env bash
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
BUILDROOT_ABS="${BUILD_ROOT_PATH:-$(realpath "$SCRIPT_DIR/../../../buildroot/include" 2>/dev/null || true)}"
BUILDROOT_INC="${BUILD_INCLUDE_PATH:-$SCRIPT_DIR/../../../buildroot/include}"
BUILDROOT_ABS="$(realpath "$BUILDROOT_INC" 2>/dev/null || true)"
PARSED_ARGS=()
is_buildroot_inc() {
[[ -n "$BUILDROOT_ABS" && "$1" == "$BUILDROOT_ABS" ]]
}
while [[ $# -gt 0 ]]; do
case "$1" in
-isystem)
@@ -11,13 +16,13 @@ while [[ $# -gt 0 ]]; do
ARG="$1"
shift
ARG_ABS="$(realpath "$ARG" 2>/dev/null || true)"
[[ "$ARG_ABS" == "$BUILDROOT_ABS" ]] && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem" "$ARG")
is_buildroot_inc "$ARG_ABS" && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem" "$ARG")
;;
-isystem*)
ARG="${1#-isystem}"
shift
ARG_ABS="$(realpath "$ARG" 2>/dev/null || true)"
[[ "$ARG_ABS" == "$BUILDROOT_ABS" ]] && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem$ARG")
is_buildroot_inc "$ARG_ABS" && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem$ARG")
;;
-march=*|-mcpu=*)
OPT_NAME="${1%%=*}"
@@ -32,6 +37,10 @@ while [[ $# -gt 0 ]]; do
PARSED_ARGS+=("${OPT_NAME}=${OPT_VALUE}")
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
;;
*)
PARSED_ARGS+=("$1")
shift
@@ -39,6 +48,24 @@ while [[ $# -gt 0 ]]; do
esac
done
IS_LINK=1
NEED_PROFILE_RT=0 # https://codeberg.org/ziglang/zig/issues/32066
NEED_CRT=0 # https://codeberg.org/ziglang/zig/issues/32064
for _a in "${PARSED_ARGS[@]}"; do
case "$_a" in
-c|-S|-E|-M|-MM) IS_LINK=0 ;;
-fprofile-generate*|-fprofile-instr-generate*|-fcs-profile-generate*) NEED_PROFILE_RT=1 ;;
-shared) NEED_CRT=1 ;;
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")
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")
fi
[[ -n "$SPC_TARGET" ]] && TARGET="-target $SPC_TARGET" || TARGET=""
if [[ "$SPC_TARGET" =~ \.[0-9]+\.[0-9]+ ]]; then