diff --git a/config/pkg/target/llvm-compiler-rt.yml b/config/pkg/target/llvm-compiler-rt.yml new file mode 100644 index 000000000..0d717459e --- /dev/null +++ b/config/pkg/target/llvm-compiler-rt.yml @@ -0,0 +1,10 @@ +llvm-compiler-rt: + type: target + artifact: + source: + type: custom + metadata: + license-files: [LICENSE.TXT] + license: Apache-2.0-with-LLVM-exception + tools: + - zig diff --git a/config/pkg/tool/llvm-tools.yml b/config/pkg/tool/llvm-tools.yml new file mode 100644 index 000000000..d80479434 --- /dev/null +++ b/config/pkg/tool/llvm-tools.yml @@ -0,0 +1,16 @@ +llvm-tools: + type: tool + artifact: + source: + type: custom + binary: custom + metadata: + license-files: [llvm/LICENSE.TXT] + license: Apache-2.0-with-LLVM-exception + tool: + provides: + - llvm-objcopy + - llvm-strip + - llvm-profdata + install-root: '{pkg_root_path}/llvm-tools' + binary-subdir: bin diff --git a/src/Package/Artifact/llvm_compiler_rt.php b/src/Package/Artifact/llvm_compiler_rt.php new file mode 100644 index 000000000..71d461514 --- /dev/null +++ b/src/Package/Artifact/llvm_compiler_rt.php @@ -0,0 +1,39 @@ +executeCurlDownload( + $url, + DOWNLOAD_PATH . DIRECTORY_SEPARATOR . $filename, + headers: $this->getGitHubTokenHeaders(), + retries: $downloader->getRetry(), + ); + return DownloadResult::archive($filename, ['url' => $url], version: $version); + } + + #[CustomSourceCheckUpdate('llvm-compiler-rt')] + public function checkUpdateSource(?string $old_version): CheckUpdateResult + { + $version = zig::getLlvmVersion(); + return new CheckUpdateResult(old: $old_version, new: $version, needUpdate: $old_version !== $version); + } +} diff --git a/src/Package/Artifact/llvm_tools.php b/src/Package/Artifact/llvm_tools.php new file mode 100644 index 000000000..b327782e7 --- /dev/null +++ b/src/Package/Artifact/llvm_tools.php @@ -0,0 +1,57 @@ +executeCurlDownload( + $url, + DOWNLOAD_PATH . DIRECTORY_SEPARATOR . $filename, + headers: $this->getGitHubTokenHeaders(), + retries: $downloader->getRetry(), + ); + return DownloadResult::archive($filename, ['url' => $url], version: $version); + } + + #[CustomBinary('llvm-tools', [ + 'linux-x86_64', + 'linux-aarch64', + ])] + public function downBinary(ArtifactDownloader $downloader): DownloadResult + { + $asset = 'llvm-tools-' . SystemTarget::getTargetArch() . '-linux-musl-' . zig::getLlvmVersion() . '.txz'; + return new GitHubRelease()->download('llvm-tools', [ + 'type' => 'ghrel', + 'repo' => 'static-php/hosted', + 'match' => preg_quote($asset, '|'), + 'extract' => '{pkg_root_path}/llvm-tools', + ], $downloader); + } + + #[CustomSourceCheckUpdate('llvm-tools')] + public function checkUpdateSource(?string $old_version): CheckUpdateResult + { + $version = zig::getLlvmVersion(); + return new CheckUpdateResult(old: $old_version, new: $version, needUpdate: $old_version !== $version); + } +} diff --git a/src/Package/Artifact/zig.php b/src/Package/Artifact/zig.php index 95520aa4b..daef4d7e6 100644 --- a/src/Package/Artifact/zig.php +++ b/src/Package/Artifact/zig.php @@ -11,10 +11,25 @@ use StaticPHP\Attribute\Artifact\CustomBinary; use StaticPHP\Attribute\Artifact\CustomBinaryCheckUpdate; use StaticPHP\Exception\DownloaderException; +use StaticPHP\Package\ToolPackage; +use StaticPHP\Registry\PackageLoader; use StaticPHP\Runtime\SystemTarget; class zig { + public static function getLlvmVersion(): string + { + $zig = PackageLoader::getPackage('zig'); + if (!$zig instanceof ToolPackage || !$zig->isInstalled()) { + throw new DownloaderException('zig is not installed, run `bin/spc doctor` first'); + } + [$code, $out] = shell()->execWithResult(escapeshellarg($zig->getBinary('zig')) . ' cc --version', false); + if ($code !== 0 || !preg_match('/clang version (\d+\.\d+\.\d+)/', implode("\n", $out), $match)) { + throw new DownloaderException('Could not determine the clang version shipped by zig'); + } + return $match[1]; + } + #[CustomBinary('zig', [ 'linux-x86_64', 'linux-aarch64', @@ -102,21 +117,20 @@ public function checkUpdateBinary(?string $old_version, ArtifactDownloader $down ])] public function postExtractZig(string $target_path): void { - $files = ['zig', 'zig-cc', 'zig-c++', 'zig-ar', 'zig-ld.lld', 'zig-ranlib', 'zig-objcopy']; - $all_exist = true; - foreach ($files as $file) { - if (!file_exists("{$target_path}/{$file}")) { - $all_exist = false; - break; - } - } - if ($all_exist) { - return; - } + self::writeWrappers($target_path); + } + public static function writeWrappers(string $target_path): void + { $script_path = ROOT_DIR . '/src/globals/scripts/zig-cc.sh'; $script_content = file_get_contents($script_path); + $files = ['zig-cc', 'zig-c++', 'zig-ar', 'zig-ld.lld', 'zig-ranlib', 'zig-objcopy']; + $current = @file_get_contents("{$target_path}/zig-cc"); + if ($current === $script_content && array_all($files, fn ($file) => file_exists("{$target_path}/{$file}"))) { + return; + } + file_put_contents("{$target_path}/zig-cc", $script_content); chmod("{$target_path}/zig-cc", 0755); diff --git a/src/Package/Target/llvm_compiler_rt.php b/src/Package/Target/llvm_compiler_rt.php new file mode 100644 index 000000000..4ffee77fb --- /dev/null +++ b/src/Package/Target/llvm_compiler_rt.php @@ -0,0 +1,139 @@ + file_exists("{$dir}/{$file}")); + } + + #[BuildFor('Linux')] + public function build(): void + { + $source = $this->getSourceRoot(); + $out = self::outputDir(); + $triple = SystemTarget::getCanonicalTriple(); + FileSystem::createDir($out); + + $this->buildProfileRuntime($source, "{$out}/libclang_rt.profile.a", $triple); + $this->buildCrtObjects($source, "{$out}/clang_rt.crtbegin.o", "{$out}/clang_rt.crtend.o", $triple); + $this->buildCpuModel($source, "{$out}/libclang_rt.cpu_model.a", $triple); + } + + private function buildProfileRuntime(string $source, string $lib, string $triple): void + { + $dir = "{$source}/lib/profile"; + if (!is_dir($dir)) { + throw new BuildFailureException("llvm-compiler-rt: missing profile sources at {$dir}"); + } + $sources = array_filter( + [...glob("{$dir}/*.c") ?: [], ...glob("{$dir}/*.cpp") ?: []], + fn ($file) => !array_any(self::SKIP_PLATFORMS, fn ($skip) => str_contains($file, "/{$skip}")), + ); + + $obj_dir = "{$source}/obj-profile-{$triple}"; + FileSystem::resetDir($obj_dir); + $cflags = "-target {$triple} -c -O2 -fPIC -fvisibility=hidden -I" . escapeshellarg("{$source}/include") + . ' -DCOMPILER_RT_HAS_ATOMICS=1 -DCOMPILER_RT_HAS_FCNTL_LCK=1 -DCOMPILER_RT_HAS_UNAME=1'; + shell()->cd($obj_dir) + ->exec($this->zig('cc') . " {$cflags} " . implode(' ', array_map('escapeshellarg', $sources))) + ->exec($this->zig('ar') . ' rcs ' . escapeshellarg($lib) . ' *.o'); + } + + private function buildCrtObjects(string $source, string $begin, string $end, string $triple): void + { + $cflags = "-target {$triple} -c -O2 -fPIC -fvisibility=hidden -DCRT_HAS_INITFINI_ARRAY"; + foreach ([['crtbegin.c', $begin], ['crtend.c', $end]] as [$name, $dst]) { + $src = "{$source}/lib/builtins/{$name}"; + if (!is_file($src)) { + throw new BuildFailureException("llvm-compiler-rt: missing {$src}"); + } + shell()->exec($this->zig('cc') . " {$cflags} -o " . escapeshellarg($dst) . ' ' . escapeshellarg($src)); + } + } + + private function buildCpuModel(string $source, string $lib, string $triple): void + { + $builtins = "{$source}/lib/builtins"; + $arch = explode('-', $triple)[0]; + $family = match (true) { + in_array($arch, ['x86_64', 'amd64', 'i386', 'i686', 'x86'], true) => 'x86', + in_array($arch, ['aarch64', 'arm64'], true) => 'aarch64', + str_starts_with($arch, 'riscv') => 'riscv', + default => null, + }; + if ($family === null) { + logger()->debug("llvm-compiler-rt: no cpu_model implementation for {$triple}, skipping"); + return; + } + [$src, $includes] = is_dir("{$builtins}/cpu_model") + ? ["{$builtins}/cpu_model/{$family}.c", '-I' . escapeshellarg($builtins) . ' -I' . escapeshellarg("{$builtins}/cpu_model")] + : ["{$builtins}/cpu_model.c", '-I' . escapeshellarg($builtins)]; + if (!is_file($src)) { + throw new BuildFailureException("llvm-compiler-rt: missing cpu_model source {$src}"); + } + + $obj_dir = "{$source}/obj-cpu-model-{$triple}"; + FileSystem::resetDir($obj_dir); + $obj = "{$obj_dir}/cpu_model.o"; + shell() + ->exec($this->zig('cc') . " -target {$triple} -c -O2 -fPIC {$includes} -o " . escapeshellarg($obj) . ' ' . escapeshellarg($src)) + ->exec($this->zig('ar') . ' rcs ' . escapeshellarg($lib) . ' ' . escapeshellarg($obj)); + } + + private function zig(string $subcommand): string + { + $zig = PackageLoader::getPackage('zig'); + if (!$zig instanceof ToolPackage) { + throw new BuildFailureException('llvm-compiler-rt requires the zig tool package'); + } + return escapeshellarg($zig->getBinary('zig')) . ' ' . $subcommand; + } +} diff --git a/src/Package/Tool/llvm_tools.php b/src/Package/Tool/llvm_tools.php new file mode 100644 index 000000000..aa55402ca --- /dev/null +++ b/src/Package/Tool/llvm_tools.php @@ -0,0 +1,104 @@ +getProvides(); + $concurrency = ApplicationContext::get(PackageBuilder::class)->concurrency; + $zlib = $this->buildPrivateZlib($package); + + UnixCMakeExecutor::create($package) + ->setCustomDefaultArgs( + '-DCMAKE_BUILD_TYPE=Release', + '-DLLVM_TARGETS_TO_BUILD=', + '-DLLVM_INCLUDE_BENCHMARKS=OFF', + '-DLLVM_INCLUDE_TESTS=OFF', + '-DLLVM_INCLUDE_EXAMPLES=OFF', + '-DLLVM_INCLUDE_DOCS=OFF', + '-DLLVM_ENABLE_ZLIB=FORCE_ON', + "-DZLIB_INCLUDE_DIR={$zlib}/include", + "-DZLIB_LIBRARY={$zlib}/lib/libz.a", + '-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', + ) + ->toStep(1) + ->build('../llvm') + ->exec("cmake --build . -j {$concurrency} " . implode(' ', array_map(fn ($tool) => "--target {$tool}", $tools))); + + $build_bin = "{$package->getSourceRoot()}/build/bin"; + FileSystem::createDir($package->getBinDir()); + foreach ($tools as $tool) { + $built = "{$build_bin}/{$tool}"; + if (!is_file($built)) { + throw new BuildFailureException("llvm-tools: missing build output {$built}"); + } + FileSystem::copy($built, $package->getBinary($tool)); + chmod($package->getBinary($tool), 0755); + shell()->exec(escapeshellarg("{$build_bin}/llvm-strip") . ' --strip-all ' . escapeshellarg($package->getBinary($tool))); + } + } + + private function buildPrivateZlib(ToolPackage $package): string + { + $source = $this->fetchZlibSource(); + $prefix = "{$package->getSourceRoot()}/zlib-prefix"; + + $obj_dir = "{$prefix}/obj"; + FileSystem::resetDir($obj_dir); + FileSystem::createDir("{$prefix}/lib"); + FileSystem::createDir("{$prefix}/include"); + $units = ['adler32', 'crc32', 'deflate', 'infback', 'inffast', 'inflate', 'inftrees', 'trees', 'zutil', + 'compress', 'uncompr', 'gzclose', 'gzlib', 'gzread', 'gzwrite']; + $sources = implode(' ', array_map(fn ($unit) => escapeshellarg("{$source}/{$unit}.c"), $units)); + $cc = getenv('CC') ?: 'cc'; + $ar = getenv('AR') ?: 'ar'; + shell()->cd($obj_dir) + ->exec("{$cc} -c -O2 -fPIC -DHAVE_HIDDEN -I" . escapeshellarg($source) . " {$sources}") + ->exec("{$ar} rcs " . escapeshellarg("{$prefix}/lib/libz.a") . ' *.o'); + + foreach (['zlib.h', 'zconf.h'] as $header) { + FileSystem::copy("{$source}/{$header}", "{$prefix}/include/{$header}"); + } + return $prefix; + } + + private function fetchZlibSource(): string + { + $artifact = PackageLoader::getPackage('zlib')->getArtifact() + ?? throw new BuildFailureException('llvm-tools: the zlib package has no artifact to take the source from'); + new ArtifactDownloader(['source-only' => true], interactive: false)->add($artifact)->download(); + new ArtifactExtractor(ApplicationContext::get(ArtifactCache::class))->extract($artifact, force_source: true); + + $source = $artifact->getSourceDir(); + if (!is_file("{$source}/zlib.h")) { + throw new BuildFailureException("llvm-tools: zlib source not found at {$source}"); + } + return $source; + } +} diff --git a/src/StaticPHP/Doctor/Item/LlvmToolsCheck.php b/src/StaticPHP/Doctor/Item/LlvmToolsCheck.php new file mode 100644 index 000000000..f248aa725 --- /dev/null +++ b/src/StaticPHP/Doctor/Item/LlvmToolsCheck.php @@ -0,0 +1,62 @@ +addInstallPackage('llvm-tools')->isPackageInstalled('llvm-tools')) { + return CheckResult::ok(); + } + return CheckResult::fail('llvm-tools are not built', 'build-llvm-tools'); + } + + /** @noinspection PhpUnused */ + #[CheckItem('if llvm-compiler-rt is built', level: 797)] + public function checkCompilerRt(): CheckResult + { + if (llvm_compiler_rt::isBuilt()) { + return CheckResult::ok(llvm_compiler_rt::outputDir()); + } + return CheckResult::fail('llvm-compiler-rt is not built', 'build-llvm-compiler-rt'); + } + + #[FixItem('build-llvm-compiler-rt')] + public function buildCompilerRt(): bool + { + $installer = new PackageInstaller(interactive: false); + $installer->addBuildPackage('llvm-compiler-rt'); + $installer->run(true); + return llvm_compiler_rt::isBuilt(); + } + + #[FixItem('build-llvm-tools')] + public function buildLlvmTools(): bool + { + $installer = new PackageInstaller(['dl-prefer-binary' => true], interactive: false); + $installer->addInstallPackage('llvm-tools'); + $installer->run(true); + return $installer->isPackageInstalled('llvm-tools'); + } +} diff --git a/src/StaticPHP/Package/PackageBuilder.php b/src/StaticPHP/Package/PackageBuilder.php index 943b3ce56..09b6a1293 100644 --- a/src/StaticPHP/Package/PackageBuilder.php +++ b/src/StaticPHP/Package/PackageBuilder.php @@ -10,12 +10,14 @@ use StaticPHP\Exception\SPCException; use StaticPHP\Exception\SPCInternalException; use StaticPHP\Exception\WrongUsageException; +use StaticPHP\Registry\PackageLoader; use StaticPHP\Runtime\Shell\Shell; use StaticPHP\Runtime\SystemTarget; +use StaticPHP\Toolchain\ToolchainManager; +use StaticPHP\Toolchain\ZigToolchain; use StaticPHP\Util\FileSystem; use StaticPHP\Util\GlobalPathTrait; use StaticPHP\Util\InteractiveTerm; -use StaticPHP\Util\System\LinuxUtil; class PackageBuilder { @@ -187,15 +189,10 @@ public function extractDebugInfo(string $binary_path): string if (SystemTarget::getTargetOS() === 'Darwin') { shell()->exec("dsymutil -f {$binary_path} -o {$debug_file}"); } elseif (SystemTarget::getTargetOS() === 'Linux') { - if ($eu_strip = LinuxUtil::findCommand('eu-strip')) { - shell() - ->exec("{$eu_strip} -f {$debug_file} {$binary_path}") - ->exec("objcopy --add-gnu-debuglink={$debug_file} {$binary_path}"); - } else { - shell() - ->exec("objcopy --only-keep-debug {$binary_path} {$debug_file}") - ->exec("objcopy --add-gnu-debuglink={$debug_file} {$binary_path}"); - } + $objcopy = $this->getBinUtil('objcopy'); + shell() + ->exec("{$objcopy} --only-keep-debug {$binary_path} {$debug_file}") + ->exec("{$objcopy} --add-gnu-debuglink={$debug_file} {$binary_path}"); } else { logger()->debug('extractDebugInfo is only supported on Linux and macOS'); return ''; @@ -208,14 +205,35 @@ public function extractDebugInfo(string $binary_path): string */ public function stripBinary(string $binary_path): void { + $strip = $this->getBinUtil('strip'); shell()->exec(match (SystemTarget::getTargetOS()) { - 'Darwin' => "strip -S {$binary_path}", - 'Linux' => "strip --strip-unneeded {$binary_path}", + 'Darwin' => "{$strip} -S {$binary_path}", + 'Linux' => "{$strip} --strip-unneeded {$binary_path}", 'Windows' => 'echo "Skip strip on Windows"', // Windows strip is not available for now default => throw new SPCInternalException('stripBinary is only supported on Linux and macOS'), }); } + private function getBinUtil(string $name): string + { + $env = getenv(strtoupper($name)) ?: ''; + $is_zig = ToolchainManager::getToolchainClass() === ZigToolchain::class; + + // Honor an explicit override, but don't let Zig's own default wrappers block llvm-tools. + if ($env !== '' && !($is_zig && $env === "zig-{$name}")) { + return $env; + } + + if ($is_zig) { + $llvm_tools = PackageLoader::getPackage('llvm-tools'); + if ($llvm_tools instanceof ToolPackage && $llvm_tools->isInstalled()) { + return $llvm_tools->getBinary("llvm-{$name}"); + } + } + + return $env !== '' ? $env : $name; + } + private function installLicense(Package $package, array $license): void { $dir = BUILD_ROOT_PATH . '/source-licenses/' . $package->getName(); diff --git a/src/StaticPHP/Runtime/SystemTarget.php b/src/StaticPHP/Runtime/SystemTarget.php index 489f84384..d1c16b38f 100644 --- a/src/StaticPHP/Runtime/SystemTarget.php +++ b/src/StaticPHP/Runtime/SystemTarget.php @@ -120,6 +120,24 @@ public static function getCurrentPlatformString(): string return "{$os}-{$arch}"; } + public static function getCanonicalTriple(): string + { + $target = (string) getenv('SPC_TARGET'); + if ($target !== '' && !str_contains($target, 'native')) { + $cleaned = preg_split('/\s+/', trim((string) preg_replace('/(-gnu|-musl)\.[\d.]+/', '$1', $target)))[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', + }; + } + /** * Check if the target OS is a Unix-like system. */ diff --git a/src/StaticPHP/Toolchain/ZigToolchain.php b/src/StaticPHP/Toolchain/ZigToolchain.php index f9048ad35..603d8e3cb 100644 --- a/src/StaticPHP/Toolchain/ZigToolchain.php +++ b/src/StaticPHP/Toolchain/ZigToolchain.php @@ -4,12 +4,20 @@ namespace StaticPHP\Toolchain; +use Package\Artifact\zig; +use Package\Target\llvm_compiler_rt; +use StaticPHP\Package\PackageInstaller; +use StaticPHP\Package\ToolPackage; +use StaticPHP\Registry\PackageLoader; +use StaticPHP\Runtime\SystemTarget; use StaticPHP\Toolchain\Interface\UnixToolchainInterface; use StaticPHP\Util\GlobalEnvManager; use StaticPHP\Util\System\LinuxUtil; class ZigToolchain implements UnixToolchainInterface { + private static bool $deps_ensured = false; + public function initEnv(): void { // Set environment variables for zig toolchain @@ -19,31 +27,16 @@ public function initEnv(): void 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)); + $rt_dir = $this->getPath() . '/lib/' . SystemTarget::getCanonicalTriple(); + GlobalEnvManager::putenv("SPC_COMPILER_RT_DIR={$rt_dir}"); } public function afterInit(): void { GlobalEnvManager::addPathIfNotExists($this->getPath()); + if (is_dir($this->getPath())) { + zig::writeWrappers($this->getPath()); + } 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') ?: ''; @@ -71,6 +64,8 @@ public function afterInit(): void // 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->ensureBuildDeps(); } public function getCompilerInfo(): ?string @@ -106,6 +101,25 @@ public function isStatic(): bool return false; } + private function ensureBuildDeps(): void + { + if (self::$deps_ensured) { + return; + } + self::$deps_ensured = true; + + if (!llvm_compiler_rt::isBuilt()) { + logger()->info('Building llvm-compiler-rt for ' . SystemTarget::getCanonicalTriple()); + new PackageInstaller(interactive: false)->addBuildPackage('llvm-compiler-rt')->run(true); + } + + $tools = PackageLoader::getPackage('llvm-tools'); + if ($tools instanceof ToolPackage && !$tools->isInstalled()) { + logger()->info('Installing llvm-tools'); + new PackageInstaller(['dl-prefer-binary' => true], interactive: false)->addInstallPackage('llvm-tools')->run(true); + } + } + private function getPath(): string { return PKG_ROOT_PATH . '/zig'; diff --git a/src/globals/scripts/zig-cc.sh b/src/globals/scripts/zig-cc.sh index a6a530f41..886253ea6 100755 --- a/src/globals/scripts/zig-cc.sh +++ b/src/globals/scripts/zig-cc.sh @@ -41,6 +41,31 @@ 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 _arg in "${PARSED_ARGS[@]}"; do + case "$_arg" 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" == *-fprofile-instr-generate* || "$SPC_COMPILER_EXTRA" == *-fcs-profile-generate* ]] && NEED_PROFILE_RT=1 + +RT_DIR="${SPC_COMPILER_RT_DIR:-}" +if [[ $IS_LINK -eq 1 && -n "$RT_DIR" ]]; then + if [[ $NEED_PROFILE_RT -eq 1 && -f "$RT_DIR/libclang_rt.profile.a" ]]; then + PARSED_ARGS+=("-x" "none" "$RT_DIR/libclang_rt.profile.a" "-Wl,-u,__llvm_profile_runtime") + fi + if [[ $NEED_CRT -eq 1 && -f "$RT_DIR/clang_rt.crtbegin.o" && -f "$RT_DIR/clang_rt.crtend.o" ]]; then + PARSED_ARGS+=("-x" "none" "$RT_DIR/clang_rt.crtbegin.o" "$RT_DIR/clang_rt.crtend.o") + fi + if [[ -f "$RT_DIR/libclang_rt.cpu_model.a" ]]; then + PARSED_ARGS+=("-x" "none" "$RT_DIR/libclang_rt.cpu_model.a") + fi +fi + [[ -n "$SPC_TARGET" ]] && TARGET="-target $SPC_TARGET" || TARGET="" if [[ "$SPC_TARGET" =~ \.[0-9]+\.[0-9]+ ]]; then