From 745535ae60e5121292186bd72d264281c0ce215b Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 8 Dec 2025 09:49:11 +0800 Subject: [PATCH 01/10] Add LinuxToolCheck --- src/StaticPHP/Doctor/Item/LinuxToolCheck.php | 147 +++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 src/StaticPHP/Doctor/Item/LinuxToolCheck.php diff --git a/src/StaticPHP/Doctor/Item/LinuxToolCheck.php b/src/StaticPHP/Doctor/Item/LinuxToolCheck.php new file mode 100644 index 000000000..09161c74e --- /dev/null +++ b/src/StaticPHP/Doctor/Item/LinuxToolCheck.php @@ -0,0 +1,147 @@ + '/usr/share/perl5/FindBin.pm', + 'binutils-gold' => 'ld.gold', + 'base-devel' => 'automake', + 'gettext-devel' => 'gettextize', + 'gettext-dev' => 'gettextize', + 'perl-IPC-Cmd' => '/usr/share/perl5/vendor_perl/IPC/Cmd.pm', + 'perl-Time-Piece' => '/usr/lib64/perl5/Time/Piece.pm', + ]; + + /** @noinspection PhpUnused */ + #[CheckItem('if necessary tools are installed', limit_os: 'Linux', level: 999)] + public function checkCliTools(): ?CheckResult + { + $distro = LinuxUtil::getOSRelease(); + + $required = match ($distro['dist']) { + 'alpine' => self::TOOLS_ALPINE, + 'redhat' => self::TOOLS_RHEL, + 'centos' => array_merge(self::TOOLS_RHEL, ['perl-IPC-Cmd', 'perl-Time-Piece']), + 'arch' => self::TOOLS_ARCH, + default => self::TOOLS_DEBIAN, + }; + $missing = []; + foreach ($required as $package) { + if (LinuxUtil::findCommand(self::PROVIDED_COMMAND[$package] ?? $package) === null) { + $missing[] = $package; + } + } + if (!empty($missing)) { + return CheckResult::fail(implode(', ', $missing) . ' not installed on your system', 'install-linux-tools', [$distro, $missing]); + } + return CheckResult::ok(); + } + + #[CheckItem('if cmake version >= 3.22', limit_os: 'Linux')] + public function checkCMakeVersion(): ?CheckResult + { + $ver = get_cmake_version(); + if ($ver === null) { + return CheckResult::fail('Failed to get cmake version'); + } + if (version_compare($ver, '3.22.0') < 0) { + return CheckResult::fail('cmake version is too low (' . $ver . '), please update it manually!'); + } + return CheckResult::ok($ver); + } + + /** @noinspection PhpUnused */ + #[CheckItem('if necessary linux headers are installed', limit_os: 'Linux')] + public function checkSystemOSPackages(): ?CheckResult + { + if (LinuxUtil::isMuslDist()) { + // check linux-headers installation + if (!file_exists('/usr/include/linux/mman.h')) { + return CheckResult::fail('linux-headers not installed on your system', 'install-linux-tools', [LinuxUtil::getOSRelease(), ['linux-headers']]); + } + } + return CheckResult::ok(); + } + + #[FixItem('install-linux-tools')] + public function fixBuildTools(array $distro, array $missing): bool + { + $install_cmd = match ($distro['dist']) { + 'ubuntu', 'debian', 'Deepin', 'neon' => 'apt-get install -y', + 'alpine' => 'apk add', + 'redhat' => 'dnf install -y', + 'centos' => 'yum install -y', + 'arch' => 'pacman -S --noconfirm', + default => null, + }; + if ($install_cmd === null) { + // try family + $family = explode(' ', strtolower($distro['family'])); + if (in_array('debian', $family)) { + $install_cmd = 'apt-get install -y'; + } elseif (in_array('rhel', $family) || in_array('fedora', $family)) { + $install_cmd = 'dnf install -y'; + } else { + throw new EnvironmentException( + "Current linux distro [{$distro['dist']}] does not have an auto-install script for packages yet.", + 'You can submit an issue to request support: https://github.com/crazywhalecc/static-php-cli/issues' + ); + } + } + $prefix = ''; + if (($user = exec('whoami')) !== 'root') { + $prefix = 'sudo '; + logger()->warning("Current user ({$user}) is not root, using sudo for running command (may require password input)"); + } + + $is_debian = LinuxUtil::isDebianDist(); + $to_install = $is_debian ? str_replace('xz', 'xz-utils', $missing) : $missing; + // debian, alpine libtool -> libtoolize + $to_install = str_replace('libtoolize', 'libtool', $to_install); + shell()->exec($prefix . $install_cmd . ' ' . implode(' ', $to_install)); + + return true; + } +} From d3e29b51dd49ac2831a8b8bca2a33cd40d6491ec Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 8 Dec 2025 10:36:45 +0800 Subject: [PATCH 02/10] Add dev commands: is-installed, shell (for debugging package status) --- .../Command/Dev/IsInstalledCommand.php | 34 +++++++++++++++++++ src/StaticPHP/Command/Dev/ShellCommand.php | 33 ++++++++++++++++++ src/StaticPHP/ConsoleApplication.php | 6 ++++ 3 files changed, 73 insertions(+) create mode 100644 src/StaticPHP/Command/Dev/IsInstalledCommand.php create mode 100644 src/StaticPHP/Command/Dev/ShellCommand.php diff --git a/src/StaticPHP/Command/Dev/IsInstalledCommand.php b/src/StaticPHP/Command/Dev/IsInstalledCommand.php new file mode 100644 index 000000000..a3f693217 --- /dev/null +++ b/src/StaticPHP/Command/Dev/IsInstalledCommand.php @@ -0,0 +1,34 @@ +no_motd = true; + $this->addArgument('package', InputArgument::REQUIRED, 'The package name to check installation status'); + } + + public function handle(): int + { + $installer = new PackageInstaller(); + $package = $this->input->getArgument('package'); + $installer->addInstallPackage($package); + $installed = $installer->isPackageInstalled($package); + if ($installed) { + $this->output->writeln("Package [{$package}] is installed correctly."); + return static::SUCCESS; + } + $this->output->writeln("Package [{$package}] is not installed."); + return static::FAILURE; + } +} diff --git a/src/StaticPHP/Command/Dev/ShellCommand.php b/src/StaticPHP/Command/Dev/ShellCommand.php new file mode 100644 index 000000000..560cc7fed --- /dev/null +++ b/src/StaticPHP/Command/Dev/ShellCommand.php @@ -0,0 +1,33 @@ +output->writeln("Entering interactive shell. Type 'exit' to leave."); + + if (SystemTarget::isUnix()) { + passthru('PS1=\'[StaticPHP] > \' /bin/bash', $code); + return $code; + } + if (SystemTarget::getTargetOS() === 'Windows') { + passthru('cmd.exe', $code); + return $code; + } + $this->output->writeln('Unsupported OS for shell command.'); + return static::FAILURE; + } +} diff --git a/src/StaticPHP/ConsoleApplication.php b/src/StaticPHP/ConsoleApplication.php index 1f63190b3..0484c1114 100644 --- a/src/StaticPHP/ConsoleApplication.php +++ b/src/StaticPHP/ConsoleApplication.php @@ -6,6 +6,8 @@ use StaticPHP\Command\BuildLibsCommand; use StaticPHP\Command\BuildTargetCommand; +use StaticPHP\Command\Dev\IsInstalledCommand; +use StaticPHP\Command\Dev\ShellCommand; use StaticPHP\Command\DoctorCommand; use StaticPHP\Command\DownloadCommand; use StaticPHP\Command\ExtractCommand; @@ -47,6 +49,10 @@ public function __construct() new BuildLibsCommand(), new ExtractCommand(), new SPCConfigCommand(), + + // dev commands + new ShellCommand(), + new IsInstalledCommand(), ]); // add additional commands from registries From d3b98837b6415b0b7eef47224b9d26f171756afb Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 8 Dec 2025 10:57:51 +0800 Subject: [PATCH 03/10] Add Zig package support with downloader and installation checks --- config/pkg.target.json | 5 +- src/Package/Artifact/zig.php | 98 ++++++++++++++++++++++++++ src/StaticPHP/Doctor/Item/ZigCheck.php | 53 ++++++++++++++ src/globals/scripts/zig-cc.sh | 65 +++++++++++++++++ 4 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 src/Package/Artifact/zig.php create mode 100644 src/StaticPHP/Doctor/Item/ZigCheck.php create mode 100755 src/globals/scripts/zig-cc.sh diff --git a/config/pkg.target.json b/config/pkg.target.json index b5e4a7c8b..8f47502b5 100644 --- a/config/pkg.target.json +++ b/config/pkg.target.json @@ -82,7 +82,10 @@ }, "zig": { "type": "target", - "artifact": "zig" + "artifact": "zig", + "static-bins": [ + "{pkg_root_path}/zig/zig" + ] }, "nasm": { "type": "target", diff --git a/src/Package/Artifact/zig.php b/src/Package/Artifact/zig.php new file mode 100644 index 000000000..2ac7b454b --- /dev/null +++ b/src/Package/Artifact/zig.php @@ -0,0 +1,98 @@ +executeCurl('https://ziglang.org/download/index.json', retries: $downloader->getRetry()); + $index_json = json_decode($index_json ?: '', true); + $latest_version = null; + foreach ($index_json as $version => $data) { + $latest_version = $version; + break; + } + + if (!$latest_version) { + throw new DownloaderException('Could not determine latest Zig version'); + } + $zig_arch = SystemTarget::getTargetArch(); + $zig_os = match (SystemTarget::getTargetOS()) { + 'Windows' => 'win', + 'Darwin' => 'macos', + 'Linux' => 'linux', + default => throw new DownloaderException('Unsupported OS for Zig: ' . SystemTarget::getTargetOS()), + }; + $platform_key = "{$zig_arch}-{$zig_os}"; + if (!isset($index_json[$latest_version][$platform_key])) { + throw new DownloaderException("No download available for {$platform_key} in Zig version {$latest_version}"); + } + $download_info = $index_json[$latest_version][$platform_key]; + $url = $download_info['tarball']; + $sha256 = $download_info['shasum']; + $filename = basename($url); + $path = DOWNLOAD_PATH . DIRECTORY_SEPARATOR . $filename; + default_shell()->executeCurlDownload($url, $path, retries: $downloader->getRetry()); + // verify hash + $file_hash = hash_file('sha256', $path); + if ($file_hash !== $sha256) { + throw new DownloaderException("Hash mismatch for downloaded Zig binary. Expected {$sha256}, got {$file_hash}"); + } + return DownloadResult::archive(basename($path), ['url' => $url, 'version' => $latest_version], extract: PKG_ROOT_PATH . '/zig', verified: true, version: $latest_version); + } + + #[AfterBinaryExtract('zig', [ + 'linux-x86_64', + 'linux-aarch64', + 'macos-x86_64', + 'macos-aarch64', + ])] + 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; + } + + $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); + } +} diff --git a/src/StaticPHP/Doctor/Item/ZigCheck.php b/src/StaticPHP/Doctor/Item/ZigCheck.php new file mode 100644 index 000000000..c8d00574b --- /dev/null +++ b/src/StaticPHP/Doctor/Item/ZigCheck.php @@ -0,0 +1,53 @@ +addInstallPackage($package); + $installed = $installer->isPackageInstalled($package); + if ($installed) { + return CheckResult::ok(); + } + return CheckResult::fail('zig is not installed', 'install-zig'); + } + + #[FixItem('install-zig')] + public function installZig(): bool + { + $arch = arch2gnu(php_uname('m')); + $os = match (PHP_OS_FAMILY) { + 'Windows' => 'win', + 'Darwin' => 'macos', + 'BSD' => 'freebsd', + default => 'linux', + }; + $installer = new PackageInstaller(); + $installer->addInstallPackage('zig'); + $installer->run(false); + return $installer->isPackageInstalled('zig'); + } +} diff --git a/src/globals/scripts/zig-cc.sh b/src/globals/scripts/zig-cc.sh new file mode 100755 index 000000000..5c9017c7f --- /dev/null +++ b/src/globals/scripts/zig-cc.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + +if [ "$BUILD_ROOT_PATH" = "" ]; then + echo "The script must be run in the SPC build environment." + exit 1 +fi + +SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" +BUILDROOT_ABS=$BUILD_ROOT_PATH +PARSED_ARGS=() + +while [[ $# -gt 0 ]]; do + case "$1" in + -isystem) + shift + ARG="$1" + shift + ARG_ABS="$(realpath "$ARG" 2>/dev/null || true)" + [[ "$ARG_ABS" == "$BUILDROOT_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") + ;; + -march=*|-mcpu=*) + OPT_NAME="${1%%=*}" + OPT_VALUE="${1#*=}" + # Skip armv8- flags entirely as Zig doesn't support them + if [[ "$OPT_VALUE" == armv8-* ]]; then + shift + continue + fi + # replace -march=x86-64 with -march=x86_64 + OPT_VALUE="${OPT_VALUE//-/_}" + PARSED_ARGS+=("${OPT_NAME}=${OPT_VALUE}") + shift + ;; + *) + PARSED_ARGS+=("$1") + shift + ;; + esac +done + +[[ -n "$SPC_TARGET" ]] && TARGET="-target $SPC_TARGET" || TARGET="" + +if [[ "$SPC_TARGET" =~ \.[0-9]+\.[0-9]+ ]]; then + output=$(zig cc $TARGET $SPC_COMPILER_EXTRA "${PARSED_ARGS[@]}" 2>&1) + status=$? + + if [[ $status -eq 0 ]]; then + echo "$output" + exit 0 + fi + + if echo "$output" | grep -qE "version '.*' in target triple"; then + filtered_output=$(echo "$output" | grep -vE "version '.*' in target triple") + echo "$filtered_output" + exit 0 + fi +fi + +exec zig cc $TARGET $SPC_COMPILER_EXTRA "${PARSED_ARGS[@]}" From 77197f011832c047e816b4599746ea45f0f09062 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 8 Dec 2025 10:58:16 +0800 Subject: [PATCH 04/10] Fix artifact downloade does not accept boolean options bug --- src/StaticPHP/Artifact/ArtifactDownloader.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/StaticPHP/Artifact/ArtifactDownloader.php b/src/StaticPHP/Artifact/ArtifactDownloader.php index 4fb651cfb..2b7ac0de6 100644 --- a/src/StaticPHP/Artifact/ArtifactDownloader.php +++ b/src/StaticPHP/Artifact/ArtifactDownloader.php @@ -113,7 +113,7 @@ public function __construct(protected array $options = []) foreach ($ls as $name) { $this->fetch_prefs[$name] = Artifact::FETCH_PREFER_SOURCE; } - } elseif ($options['prefer-source'] === null) { + } elseif ($options['prefer-source'] === null || $options['prefer-source'] === true) { $this->default_fetch_pref = Artifact::FETCH_PREFER_SOURCE; } } @@ -124,7 +124,7 @@ public function __construct(protected array $options = []) foreach ($ls as $name) { $this->fetch_prefs[$name] = Artifact::FETCH_PREFER_BINARY; } - } elseif ($options['prefer-binary'] === null) { + } elseif ($options['prefer-binary'] === null || $options['prefer-binary'] === true) { $this->default_fetch_pref = Artifact::FETCH_PREFER_BINARY; } } @@ -134,7 +134,7 @@ public function __construct(protected array $options = []) foreach ($ls as $name) { $this->fetch_prefs[$name] = Artifact::FETCH_PREFER_BINARY; } - } elseif ($options['prefer-pre-built'] === null) { + } elseif ($options['prefer-pre-built'] === null || $options['prefer-pre-built'] === true) { $this->default_fetch_pref = Artifact::FETCH_PREFER_BINARY; } } @@ -145,7 +145,7 @@ public function __construct(protected array $options = []) foreach ($ls as $name) { $this->fetch_prefs[$name] = Artifact::FETCH_ONLY_SOURCE; } - } elseif ($options['source-only'] === null) { + } elseif ($options['source-only'] === null || $options['source-only'] === true) { $this->default_fetch_pref = Artifact::FETCH_ONLY_SOURCE; } } @@ -156,7 +156,7 @@ public function __construct(protected array $options = []) foreach ($ls as $name) { $this->fetch_prefs[$name] = Artifact::FETCH_ONLY_BINARY; } - } elseif ($options['binary-only'] === null) { + } elseif ($options['binary-only'] === null || $options['binary-only'] === true) { $this->default_fetch_pref = Artifact::FETCH_ONLY_BINARY; } } @@ -164,7 +164,7 @@ public function __construct(protected array $options = []) if (array_key_exists('ignore-cache', $options)) { if (is_string($options['ignore-cache'])) { $this->ignore_cache = parse_comma_list($options['ignore-cache']); - } elseif ($options['ignore-cache'] === null) { + } elseif ($options['ignore-cache'] === null || $options['ignore-cache'] === true) { $this->ignore_cache = true; } } @@ -172,7 +172,7 @@ public function __construct(protected array $options = []) if (array_key_exists('ignore-cache-sources', $options)) { if (is_string($options['ignore-cache-sources'])) { $this->ignore_cache = parse_comma_list($options['ignore-cache-sources']); - } elseif ($options['ignore-cache-sources'] === null) { + } elseif ($options['ignore-cache-sources'] === null || $options['ignore-cache-sources'] === true) { $this->ignore_cache = true; } } From cda2a8df76293229492b5f0081db8031b78bcbf2 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 8 Dec 2025 10:58:44 +0800 Subject: [PATCH 05/10] Fix pkg-config doctor fix using source bug --- src/StaticPHP/Doctor/Item/PkgConfigCheck.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StaticPHP/Doctor/Item/PkgConfigCheck.php b/src/StaticPHP/Doctor/Item/PkgConfigCheck.php index 757c14ed8..4a0ba498d 100644 --- a/src/StaticPHP/Doctor/Item/PkgConfigCheck.php +++ b/src/StaticPHP/Doctor/Item/PkgConfigCheck.php @@ -45,7 +45,7 @@ public function checkFunctional(): CheckResult public function fix(): bool { ApplicationContext::set('elephant', true); - $installer = new PackageInstaller(['dl-prefer-binary' => true]); + $installer = new PackageInstaller(['dl-binary-only' => true]); $installer->addInstallPackage('pkg-config'); $installer->run(false, true); return true; From 6a1b832b07b34c71e8a2fae838f6a863d502e4e7 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 8 Dec 2025 10:59:07 +0800 Subject: [PATCH 06/10] Fix wrong namespace in go-xcaddy package --- src/Package/Artifact/go_xcaddy.php | 2 +- src/Package/Target/go_xcaddy.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Package/Artifact/go_xcaddy.php b/src/Package/Artifact/go_xcaddy.php index 152dc2e5c..293f7c62b 100644 --- a/src/Package/Artifact/go_xcaddy.php +++ b/src/Package/Artifact/go_xcaddy.php @@ -4,13 +4,13 @@ namespace Package\Artifact; -use SPC\util\GlobalEnvManager; use StaticPHP\Artifact\ArtifactDownloader; use StaticPHP\Artifact\Downloader\DownloadResult; use StaticPHP\Attribute\Artifact\AfterBinaryExtract; use StaticPHP\Attribute\Artifact\CustomBinary; use StaticPHP\Exception\DownloaderException; use StaticPHP\Runtime\SystemTarget; +use StaticPHP\Util\GlobalEnvManager; use StaticPHP\Util\System\LinuxUtil; class go_xcaddy diff --git a/src/Package/Target/go_xcaddy.php b/src/Package/Target/go_xcaddy.php index cafaacf7e..01c4ade3d 100644 --- a/src/Package/Target/go_xcaddy.php +++ b/src/Package/Target/go_xcaddy.php @@ -4,9 +4,9 @@ namespace Package\Target; -use SPC\util\GlobalEnvManager; use StaticPHP\Attribute\Package\InitPackage; use StaticPHP\Attribute\Package\Target; +use StaticPHP\Util\GlobalEnvManager; #[Target('go-xcaddy')] class go_xcaddy From 0296026062433ebca648693e7f05042849ffb000 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 8 Dec 2025 10:59:25 +0800 Subject: [PATCH 07/10] Allow absolute paths for configs --- src/StaticPHP/Package/LibraryPackage.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/StaticPHP/Package/LibraryPackage.php b/src/StaticPHP/Package/LibraryPackage.php index c83985501..2063ee897 100644 --- a/src/StaticPHP/Package/LibraryPackage.php +++ b/src/StaticPHP/Package/LibraryPackage.php @@ -33,22 +33,28 @@ public function addBuildFunction(string $platform, callable $func): void public function isInstalled(): bool { foreach (PackageConfig::get($this->getName(), 'static-libs', []) as $lib) { - if (!file_exists("{$this->getLibDir()}/{$lib}")) { + $path = FileSystem::isRelativePath($lib) ? "{$this->getLibDir()}/{$lib}" : $lib; + if (!file_exists($path)) { return false; } } foreach (PackageConfig::get($this->getName(), 'headers', []) as $header) { - if (!file_exists("{$this->getIncludeDir()}/{$header}")) { + $path = FileSystem::isRelativePath($header) ? "{$this->getIncludeDir()}/{$header}" : $header; + if (!file_exists($path)) { return false; } } foreach (PackageConfig::get($this->getName(), 'pkg-configs', []) as $pc) { - if (!file_exists("{$this->getLibDir()}/pkgconfig/{$pc}.pc")) { + if (!str_ends_with($pc, '.pc')) { + $pc .= '.pc'; + } + if (!file_exists("{$this->getLibDir()}/pkgconfig/{$pc}")) { return false; } } foreach (PackageConfig::get($this->getName(), 'static-bins', []) as $bin) { - if (!file_exists("{$this->getBinDir()}/{$bin}")) { + $path = FileSystem::isRelativePath($bin) ? "{$this->getBinDir()}/{$bin}" : $bin; + if (!file_exists($path)) { return false; } } From 80dec1717d11446e5cf1ab708231c0a4f513da44 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 8 Dec 2025 11:01:36 +0800 Subject: [PATCH 08/10] Add PackageInstaller::isPackageInstalled() API --- src/StaticPHP/Package/PackageInstaller.php | 36 ++++++++++++++++++++-- src/StaticPHP/Util/SPCConfigUtil.php | 12 +++++--- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/StaticPHP/Package/PackageInstaller.php b/src/StaticPHP/Package/PackageInstaller.php index 34e246214..c6aa34213 100644 --- a/src/StaticPHP/Package/PackageInstaller.php +++ b/src/StaticPHP/Package/PackageInstaller.php @@ -108,8 +108,10 @@ public function setDownload(bool $download = true): static */ public function run(bool $interactive = true, bool $disable_delay_msg = false): void { - // resolve input, make dependency graph - $this->resolvePackages(); + if (empty($this->packages)) { + // resolve input, make dependency graph + $this->resolvePackages(); + } if ($interactive && !$disable_delay_msg) { // show install or build options in terminal with beautiful output @@ -148,7 +150,10 @@ public function run(bool $interactive = true, bool $disable_delay_msg = false): } $builder = ApplicationContext::get(PackageBuilder::class); foreach ($this->packages as $package) { - if ($this->isBuildPackage($package) || $package instanceof LibraryPackage && $package->hasStage('build')) { + if ( + $this->isBuildPackage($package) || + $package instanceof LibraryPackage && $package->hasStage('build') && !$package->getArtifact()->shouldUseBinary() + ) { if ($interactive) { InteractiveTerm::indicateProgress('Building package: ' . ConsoleColor::yellow($package->getName())); } @@ -213,6 +218,31 @@ public function isPackageResolved(string $package_name): bool return isset($this->packages[$package_name]); } + public function isPackageInstalled(Package|string $package_name): bool + { + if (empty($this->packages)) { + $this->resolvePackages(); + } + if (is_string($package_name)) { + $package = $this->getPackage($package_name); + if ($package === null) { + throw new WrongUsageException("Package '{$package_name}' is not resolved."); + } + } else { + $package = $package_name; + } + + // check if package is built/installed + if ($this->isBuildPackage($package)) { + return $package->isInstalled(); + } + if ($package instanceof LibraryPackage && $package->getArtifact()->shouldUseBinary()) { + $artifact = $package->getArtifact(); + return $artifact->isBinaryExtracted(); + } + return false; + } + /** * Returns the download status of all artifacts for the resolved packages. * diff --git a/src/StaticPHP/Util/SPCConfigUtil.php b/src/StaticPHP/Util/SPCConfigUtil.php index 317258d03..c525f8c79 100644 --- a/src/StaticPHP/Util/SPCConfigUtil.php +++ b/src/StaticPHP/Util/SPCConfigUtil.php @@ -225,11 +225,15 @@ private function getLibsString(array $packages, bool $use_short_libs = true): st // convert all static-libs to short names $libs = array_reverse(PackageConfig::get($package, 'static-libs', [])); foreach ($libs as $lib) { - // check file existence - if (!file_exists(BUILD_LIB_PATH . "/{$lib}")) { - throw new WrongUsageException("Library file '{$lib}' for lib [{$package}] does not exist in '" . BUILD_LIB_PATH . "'. Please build it first."); + if (FileSystem::isRelativePath($lib)) { + // check file existence + if (!file_exists(BUILD_LIB_PATH . "/{$lib}")) { + throw new WrongUsageException("Library file '{$lib}' for lib [{$package}] does not exist in '" . BUILD_LIB_PATH . "'. Please build it first."); + } + $lib_names[] = $this->getShortLibName($lib); + } else { + $lib_names[] = $lib; } - $lib_names[] = $this->getShortLibName($lib); } // add frameworks for macOS if (SystemTarget::getTargetOS() === 'Darwin') { From 1210af97373422b975d8b09fd8594fb1af4bd7e8 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 8 Dec 2025 11:04:11 +0800 Subject: [PATCH 09/10] Remove redundant path --- config/pkg.target.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/config/pkg.target.json b/config/pkg.target.json index 8f47502b5..b5e4a7c8b 100644 --- a/config/pkg.target.json +++ b/config/pkg.target.json @@ -82,10 +82,7 @@ }, "zig": { "type": "target", - "artifact": "zig", - "static-bins": [ - "{pkg_root_path}/zig/zig" - ] + "artifact": "zig" }, "nasm": { "type": "target", From e28670802125dc2e24c028d2571a5d19ea175f81 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 8 Dec 2025 12:32:03 +0800 Subject: [PATCH 10/10] Refactor BUILDROOT_ABS initialization to provide a default path --- src/globals/scripts/zig-cc.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/globals/scripts/zig-cc.sh b/src/globals/scripts/zig-cc.sh index 5c9017c7f..56ae95055 100755 --- a/src/globals/scripts/zig-cc.sh +++ b/src/globals/scripts/zig-cc.sh @@ -1,12 +1,7 @@ #!/usr/bin/env bash -if [ "$BUILD_ROOT_PATH" = "" ]; then - echo "The script must be run in the SPC build environment." - exit 1 -fi - SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" -BUILDROOT_ABS=$BUILD_ROOT_PATH +BUILDROOT_ABS="${BUILD_ROOT_PATH:-$(realpath "$SCRIPT_DIR/../../../buildroot/include" 2>/dev/null || true)}" PARSED_ARGS=() while [[ $# -gt 0 ]]; do