From 06864fc3f69dc2befd8eec059b9e501f7e9d7050 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sun, 21 Jun 2026 16:56:40 +0800 Subject: [PATCH] feat(windows): replace php-sdk-binary-tools with MSYS2 + 7za-win - Add msys2-build-essentials target: downloads the MSYS2 nightly sfx, extracts it, disables PGP keyring (CI-safe), runs two-pass pacman update, and installs autotools build essentials (make, autoconf, automake, libtool, pkgconf, perl). - Add 7za-win target: downloads 7za.exe to PKG_ROOT_PATH\bin. - Remove php-sdk-binary-tools target and all PHP_SDK_PATH references; replace with SPC_MSYS2_PATH throughout Artifact, ArtifactExtractor, FileSystem, DefaultShell and MSVCToolchain. - Replace {php_sdk_path} path placeholder with {spc_msys2_path}. - WindowsToolCheck: replace checkSDK/installSDK with checkMsys2, installMsys2 and check7zaWin/install7zaWin fix items. - nasm.yml: extract nasm.exe/ndisasm.exe to {pkg_root_path}/bin. - env.ini: rename PHP_SDK_PATH to SPC_MSYS2_PATH. - Docs: update Windows migration guide and package-model placeholder docs. --- config/env.ini | 4 +- config/pkg/target/7za-win.yml | 5 + config/pkg/target/msys2-build-essentials.yml | 8 ++ config/pkg/target/nasm.yml | 2 +- config/pkg/target/php-sdk-binary-tools.yml | 5 - docs/en/develop/package-model.md | 2 +- docs/en/guide/migrate-from-v2.md | 8 +- docs/zh/develop/package-model.md | 2 +- docs/zh/guide/migrate-from-v2.md | 8 +- .../Artifact/msys2_build_essentials.php | 93 +++++++++++++++++++ src/StaticPHP/Artifact/Artifact.php | 2 +- src/StaticPHP/Artifact/ArtifactExtractor.php | 2 +- .../Artifact/Downloader/DownloadResult.php | 3 +- .../Doctor/Item/WindowsToolCheck.php | 37 ++++++-- src/StaticPHP/Runtime/Shell/DefaultShell.php | 10 +- src/StaticPHP/Toolchain/MSVCToolchain.php | 12 ++- src/StaticPHP/Util/FileSystem.php | 2 +- 17 files changed, 172 insertions(+), 33 deletions(-) create mode 100644 config/pkg/target/7za-win.yml create mode 100644 config/pkg/target/msys2-build-essentials.yml delete mode 100644 config/pkg/target/php-sdk-binary-tools.yml create mode 100644 src/Package/Artifact/msys2_build_essentials.php diff --git a/config/env.ini b/config/env.ini index 483ae408..a3e9aa70 100644 --- a/config/env.ini +++ b/config/env.ini @@ -68,8 +68,8 @@ SPC_PRESERVE_LOGS="no" [windows] ; build target: win7-static SPC_TARGET=native-windows -; php-sdk-binary-tools path -PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools" +; MSYS2 root directory (msys64 subfolder), used by the Windows toolchain +SPC_MSYS2_PATH="${PKG_ROOT_PATH}\msys2-build-essentials\msys64" ; upx executable path UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe" ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches diff --git a/config/pkg/target/7za-win.yml b/config/pkg/target/7za-win.yml new file mode 100644 index 00000000..351fad8c --- /dev/null +++ b/config/pkg/target/7za-win.yml @@ -0,0 +1,5 @@ +7za-win: + type: target + artifact: + binary: + windows-x86_64: { type: url, url: 'https://dl.static-php.dev/v3/tools/7zip/7za.exe', extract: '{pkg_root_path}/bin/7za.exe' } diff --git a/config/pkg/target/msys2-build-essentials.yml b/config/pkg/target/msys2-build-essentials.yml new file mode 100644 index 00000000..39f10fe0 --- /dev/null +++ b/config/pkg/target/msys2-build-essentials.yml @@ -0,0 +1,8 @@ +msys2-build-essentials: + type: target + artifact: + binary: custom + env: + SPC_MSYS2_PATH: '{pkg_root_path}/msys2-build-essentials/msys64' + path@windows: + - '{pkg_root_path}/msys2-build-essentials/msys64/usr/bin' diff --git a/config/pkg/target/nasm.yml b/config/pkg/target/nasm.yml index 3f483e8b..3e6d9e43 100644 --- a/config/pkg/target/nasm.yml +++ b/config/pkg/target/nasm.yml @@ -2,4 +2,4 @@ nasm: type: target artifact: binary: - windows-x86_64: { type: url, url: 'https://dl.static-php.dev/static-php-cli/deps/nasm/nasm-2.16.01-win64.zip', extract: { nasm.exe: '{php_sdk_path}/bin/nasm.exe', ndisasm.exe: '{php_sdk_path}/bin/ndisasm.exe' } } + windows-x86_64: { type: url, url: 'https://dl.static-php.dev/static-php-cli/deps/nasm/nasm-2.16.01-win64.zip', extract: { nasm.exe: '{pkg_root_path}/bin/nasm.exe', ndisasm.exe: '{pkg_root_path}/bin/ndisasm.exe' } } diff --git a/config/pkg/target/php-sdk-binary-tools.yml b/config/pkg/target/php-sdk-binary-tools.yml deleted file mode 100644 index 81180007..00000000 --- a/config/pkg/target/php-sdk-binary-tools.yml +++ /dev/null @@ -1,5 +0,0 @@ -php-sdk-binary-tools: - type: target - artifact: - binary: - windows-x86_64: { type: git, rev: master, url: 'https://github.com/php/php-sdk-binary-tools.git', extract: '{php_sdk_path}' } diff --git a/docs/en/develop/package-model.md b/docs/en/develop/package-model.md index 2e9a290d..4bac3de2 100644 --- a/docs/en/develop/package-model.md +++ b/docs/en/develop/package-model.md @@ -229,7 +229,7 @@ The following path placeholders are supported in string values of the `path`, `e | `{working_dir}` | Working directory (project root) | | `{download_path}` | Download cache directory (`downloads/`) | | `{source_path}` | Extracted source directory (`source/`) | -| `{php_sdk_path}` | Windows PHP SDK directory | +| `{spc_msys2_path}` | MSYS2 root directory (`msys64/`) — Windows only | ## target Package Type diff --git a/docs/en/guide/migrate-from-v2.md b/docs/en/guide/migrate-from-v2.md index 54bfa163..3fee7bd3 100644 --- a/docs/en/guide/migrate-from-v2.md +++ b/docs/en/guide/migrate-from-v2.md @@ -58,7 +58,13 @@ A single-file hook API for lightweight patches may be provided in a future relea ### Windows-only: `--with-sdk-binary-dir` and `--vs-ver` -These options are no longer accepted on the command line. Instead, set the `PHP_SDK_PATH` environment variable to point to your PHP SDK binary tools directory. The Visual Studio version is now managed by the toolchain configuration. +These options are no longer accepted on the command line. In v3, the `php-sdk-binary-tools` dependency has been completely removed. v3 now manages its own **MSYS2** environment to support autotools-based library builds on Windows. Run `spc doctor --install` to download and configure MSYS2 automatically. + +If you need to point to a custom MSYS2 installation, set the `SPC_MSYS2_PATH` environment variable to the `msys64` directory (e.g. `C:\msys64`). Visual Studio is now auto-detected by the toolchain — no manual version flag needed. + +::: warning Migrating from v2 +v2 relied on `php-sdk-binary-tools` and required `--with-sdk-binary-dir` and `--vs-ver` on every build invocation. In v3 these options are gone. Remove them from all CI scripts and run `spc doctor --install` once to set up the Windows build environment. +::: ## Renamed / Deprecated Options diff --git a/docs/zh/develop/package-model.md b/docs/zh/develop/package-model.md index c7fa9dff..af87a8da 100644 --- a/docs/zh/develop/package-model.md +++ b/docs/zh/develop/package-model.md @@ -223,7 +223,7 @@ openssl: | `{working_dir}` | 工作目录(项目根目录) | | `{download_path}` | 下载缓存目录(`downloads/`) | | `{source_path}` | 解压源码目录(`source/`) | -| `{php_sdk_path}` | Windows PHP SDK 目录 | +| `{spc_msys2_path}` | MSYS2 根目录(`msys64/`)——仅 Windows | ## target 包类型 diff --git a/docs/zh/guide/migrate-from-v2.md b/docs/zh/guide/migrate-from-v2.md index 81297acf..805a8549 100644 --- a/docs/zh/guide/migrate-from-v2.md +++ b/docs/zh/guide/migrate-from-v2.md @@ -58,7 +58,13 @@ curl -o spc https://dl.static-php.dev/v3/spc-bin/nightly/spc-linux-x86_64 ### Windows 专有:`--with-sdk-binary-dir` 和 `--vs-ver` -这两个选项已不再被命令行接受。请改为设置 `PHP_SDK_PATH` 环境变量,指向你的 PHP SDK binary tools 目录。Visual Studio 版本现在由工具链配置统一管理。 +这两个选项已不再被命令行接受。在 v3 中,`php-sdk-binary-tools` 依赖已被完全移除。v3 现在通过管理自己的 **MSYS2** 环境来支持 Windows 上基于 autotools 的库构建。运行 `spc doctor --install` 即可自动下载并配置 MSYS2。 + +如需指向自定义 MSYS2 安装目录,请设置 `SPC_MSYS2_PATH` 环境变量,值为 `msys64` 目录路径(例如 `C:\msys64`)。Visual Studio 版本现在由工具链自动检测,无需手动指定版本号。 + +::: warning 从 v2 迁移 +v2 依赖 `php-sdk-binary-tools`,并在每次构建时需要传入 `--with-sdk-binary-dir` 和 `--vs-ver` 参数。在 v3 中这些选项已被移除。请从所有 CI 脚本中删除这些参数,并使用 `spc doctor --install` 一次性完成 Windows 构建环境的配置。 +::: ## 已重命名 / 已弃用的选项 diff --git a/src/Package/Artifact/msys2_build_essentials.php b/src/Package/Artifact/msys2_build_essentials.php new file mode 100644 index 00000000..920927bf --- /dev/null +++ b/src/Package/Artifact/msys2_build_essentials.php @@ -0,0 +1,93 @@ +executeCurlDownload($url, $path, retries: $downloader->getRetry()); + + return DownloadResult::file( + $filename, + ['url' => $url, 'version' => 'nightly'], + version: 'nightly', + extract: '{pkg_root_path}/msys2-build-essentials', + ); + } + + #[BinaryExtract('msys2-build-essentials', ['windows-x86_64'])] + public function extractBinary(string $source_file, string $target_path): void + { + $target_path = FileSystem::convertPath($target_path); + $source_file = FileSystem::convertPath($source_file); + + // Guard: skip re-extraction if already initialized (marker written at end of this method). + $marker = "{$target_path}\\.spc-msys2-initialized"; + if (file_exists($marker)) { + return; + } + + if (!is_dir($target_path)) { + FileSystem::createDir($target_path); + } + + cmd()->exec("\"{$source_file}\" -y -o\"{$target_path}\""); + + $msys2_bin = "{$target_path}\\msys64\\usr\\bin"; + if (!file_exists("{$msys2_bin}\\bash.exe")) { + throw new DownloaderException("MSYS2 extraction failed: bash.exe not found at {$msys2_bin}\\bash.exe"); + } + + // Add MSYS2 usr\bin to PATH so pacman.exe can load msys-2.0.dll. + GlobalEnvManager::addPathIfNotExists($msys2_bin); + GlobalEnvManager::putenv('CHERE_INVOKING=yes'); + GlobalEnvManager::putenv('MSYSTEM=MSYS'); + + // Disable PGP signature checking: pacman-key --init requires a pseudo-TTY which is unavailable + // from PHP. Patching pacman.conf is the standard approach for CI pipelines. + $pacman_conf = "{$target_path}\\msys64\\etc\\pacman.conf"; + FileSystem::replaceFileRegex($pacman_conf, '/^SigLevel\s*=.*$/m', 'SigLevel = Never'); + + $pacman = "{$target_path}\\msys64\\usr\\bin\\pacman.exe"; + + // Two-pass update as recommended by MSYS2 CI docs. + cmd()->exec("\"{$pacman}\" --noconfirm -Syuu"); + cmd()->exec("\"{$pacman}\" --noconfirm -Syuu"); + + $pkgs = implode(' ', self::REQUIRED_PACKAGES); + cmd()->exec("\"{$pacman}\" --noconfirm -S --needed {$pkgs}"); + + FileSystem::writeFile($marker, date('Y-m-d H:i:s')); + } + + #[AfterBinaryExtract('msys2-build-essentials', ['windows-x86_64'])] + public function afterExtract(string $target_path): void + { + $target_path = FileSystem::convertPath($target_path); + $msys2_root = "{$target_path}\\msys64"; + + GlobalEnvManager::putenv("SPC_MSYS2_PATH={$msys2_root}"); + GlobalEnvManager::addPathIfNotExists("{$msys2_root}\\usr\\bin"); + } +} diff --git a/src/StaticPHP/Artifact/Artifact.php b/src/StaticPHP/Artifact/Artifact.php index 224bcff3..666939fe 100644 --- a/src/StaticPHP/Artifact/Artifact.php +++ b/src/StaticPHP/Artifact/Artifact.php @@ -644,7 +644,7 @@ class Artifact '{artifact_name}' => $this->name, '{pkg_root_path}' => PKG_ROOT_PATH, '{build_root_path}' => BUILD_ROOT_PATH, - '{php_sdk_path}' => getenv('PHP_SDK_PATH') ?: WORKING_DIR . '/php-sdk-binary-tools', + '{spc_msys2_path}' => getenv('SPC_MSYS2_PATH'), '{working_dir}' => WORKING_DIR, '{download_path}' => DOWNLOAD_PATH, '{source_path}' => SOURCE_PATH, diff --git a/src/StaticPHP/Artifact/ArtifactExtractor.php b/src/StaticPHP/Artifact/ArtifactExtractor.php index 987ec554..3630981a 100644 --- a/src/StaticPHP/Artifact/ArtifactExtractor.php +++ b/src/StaticPHP/Artifact/ArtifactExtractor.php @@ -614,7 +614,7 @@ class ArtifactExtractor '{source_path}' => SOURCE_PATH, '{download_path}' => DOWNLOAD_PATH, '{working_dir}' => WORKING_DIR, - '{php_sdk_path}' => getenv('PHP_SDK_PATH') ?: '', + '{spc_msys2_path}' => getenv('SPC_MSYS2_PATH') ?: '', ]; return str_replace(array_keys($replacement), array_values($replacement), $path); } diff --git a/src/StaticPHP/Artifact/Downloader/DownloadResult.php b/src/StaticPHP/Artifact/Downloader/DownloadResult.php index 2efe6945..9115fb59 100644 --- a/src/StaticPHP/Artifact/Downloader/DownloadResult.php +++ b/src/StaticPHP/Artifact/Downloader/DownloadResult.php @@ -76,9 +76,10 @@ class DownloadResult ?string $version = null, array $metadata = [], ?string $downloader = null, + mixed $extract = null, ): DownloadResult { $cache_type = self::isArchiveFile($filename) ? 'archive' : 'file'; - return new self($cache_type, config: $config, filename: $filename, verified: $verified, version: $version, metadata: $metadata, downloader: $downloader); + return new self($cache_type, config: $config, filename: $filename, extract: $extract, verified: $verified, version: $version, metadata: $metadata, downloader: $downloader); } /** diff --git a/src/StaticPHP/Doctor/Item/WindowsToolCheck.php b/src/StaticPHP/Doctor/Item/WindowsToolCheck.php index 08e140f4..3fe1d312 100644 --- a/src/StaticPHP/Doctor/Item/WindowsToolCheck.php +++ b/src/StaticPHP/Doctor/Item/WindowsToolCheck.php @@ -54,13 +54,24 @@ class WindowsToolCheck return CheckResult::ok(); } - #[CheckItem('if php-sdk-binary-tools are downloaded', limit_os: 'Windows', level: 996)] - public function checkSDK(): ?CheckResult + #[CheckItem('if msys2-build-essentials is installed', limit_os: 'Windows', level: 996)] + public function checkMsys2(): ?CheckResult { - if (!file_exists(getenv('PHP_SDK_PATH') . DIRECTORY_SEPARATOR . 'phpsdk-starter.bat')) { - return CheckResult::fail('php-sdk-binary-tools not downloaded', 'install-php-sdk'); + $marker = PKG_ROOT_PATH . '\msys2-build-essentials\.spc-msys2-initialized'; + if (!file_exists($marker)) { + return CheckResult::fail('msys2-build-essentials not installed', 'install-msys2-build-essentials'); } - return CheckResult::ok(getenv('PHP_SDK_PATH')); + return CheckResult::ok(PKG_ROOT_PATH . '\msys2-build-essentials\msys64'); + } + + #[CheckItem('if 7za.exe is installed', limit_os: 'Windows', level: 999)] + public function check7zaWin(): ?CheckResult + { + $path = FileSystem::convertPath(PKG_ROOT_PATH . '\bin\7za.exe'); + if (!file_exists($path)) { + return CheckResult::fail('7za.exe not found', 'install-7za-win'); + } + return CheckResult::ok($path); } #[CheckItem('if nasm installed', level: 995)] @@ -112,12 +123,20 @@ class WindowsToolCheck return true; } - #[FixItem('install-php-sdk')] - public function installSDK(): bool + #[FixItem('install-msys2-build-essentials')] + public function installMsys2(): bool { - FileSystem::removeDir(getenv('PHP_SDK_PATH')); $installer = new PackageInstaller(interactive: false); - $installer->addInstallPackage('php-sdk-binary-tools'); + $installer->addInstallPackage('msys2-build-essentials'); + $installer->run(true); + return true; + } + + #[FixItem('install-7za-win')] + public function install7zaWin(): bool + { + $installer = new PackageInstaller(interactive: false); + $installer->addInstallPackage('7za-win'); $installer->run(true); return true; } diff --git a/src/StaticPHP/Runtime/Shell/DefaultShell.php b/src/StaticPHP/Runtime/Shell/DefaultShell.php index f20fca33..2761f32f 100644 --- a/src/StaticPHP/Runtime/Shell/DefaultShell.php +++ b/src/StaticPHP/Runtime/Shell/DefaultShell.php @@ -184,12 +184,14 @@ class DefaultShell extends Shell */ public function execute7zExtract(string $archive_path, string $target_path): bool { - $sdk_path = getenv('PHP_SDK_PATH'); - if ($sdk_path === false) { - throw new SPCInternalException('PHP_SDK_PATH environment variable is not set'); + // 7za.exe is installed by the 7za-win target package into PKG_ROOT_PATH\bin, + // which is added to PATH by MSVCToolchain::initEnv(). + $_7z_path = FileSystem::convertPath(PKG_ROOT_PATH . '\bin\7za.exe'); + if (!file_exists($_7z_path)) { + throw new SPCInternalException('7za.exe not found. Please install the 7za-win target package.'); } - $_7z = escapeshellarg(FileSystem::convertPath($sdk_path . '/bin/7za.exe')); + $_7z = escapeshellarg(FileSystem::convertPath($_7z_path)); $archive_arg = escapeshellarg(FileSystem::convertPath($archive_path)); $target_arg = escapeshellarg(FileSystem::convertPath($target_path)); diff --git a/src/StaticPHP/Toolchain/MSVCToolchain.php b/src/StaticPHP/Toolchain/MSVCToolchain.php index 1449db70..646564fa 100644 --- a/src/StaticPHP/Toolchain/MSVCToolchain.php +++ b/src/StaticPHP/Toolchain/MSVCToolchain.php @@ -14,10 +14,14 @@ class MSVCToolchain implements ToolchainInterface public function initEnv(): void { GlobalEnvManager::addPathIfNotExists(PKG_ROOT_PATH . '\bin'); - $sdk = getenv('PHP_SDK_PATH'); - if ($sdk !== false) { - GlobalEnvManager::addPathIfNotExists($sdk . '\bin'); - GlobalEnvManager::addPathIfNotExists($sdk . '\msys2\usr\bin'); + // msys2-build-essentials: add MSYS2 usr\bin to PATH so that 7za.exe, make, autoconf, etc. are available. + // This must be done here because msys2-build-essentials is not a dependency of any library package, + // so its path@windows entries are not automatically applied by the package installer at runtime. + $msys2_path = getenv('SPC_MSYS2_PATH') ?: (PKG_ROOT_PATH . '\msys2-build-essentials\msys64'); + if (is_dir($msys2_path)) { + GlobalEnvManager::putenv("SPC_MSYS2_PATH={$msys2_path}"); + GlobalEnvManager::addPathIfNotExists($msys2_path . '\usr\bin'); + GlobalEnvManager::addPathIfNotExists("{$msys2_path}\\usr\\lib\\p7zip"); } // strawberry-perl if (is_dir(PKG_ROOT_PATH . '\strawberry-perl')) { diff --git a/src/StaticPHP/Util/FileSystem.php b/src/StaticPHP/Util/FileSystem.php index 28a4facd..ad41425c 100644 --- a/src/StaticPHP/Util/FileSystem.php +++ b/src/StaticPHP/Util/FileSystem.php @@ -411,7 +411,7 @@ class FileSystem $replacement = [ '{build_root_path}' => BUILD_ROOT_PATH, '{pkg_root_path}' => PKG_ROOT_PATH, - '{php_sdk_path}' => getenv('PHP_SDK_PATH') ? getenv('PHP_SDK_PATH') : WORKING_DIR . '/php-sdk-binary-tools', + '{spc_msys2_path}' => getenv('SPC_MSYS2_PATH') ?: (PKG_ROOT_PATH . DIRECTORY_SEPARATOR . 'msys2-build-essentials' . DIRECTORY_SEPARATOR . 'msys64'), '{working_dir}' => WORKING_DIR, '{download_path}' => DOWNLOAD_PATH, '{source_path}' => SOURCE_PATH,