diff --git a/config/pkg/lib/postgresql.yml b/config/pkg/lib/postgresql.yml index 290650df..b010be33 100644 --- a/config/pkg/lib/postgresql.yml +++ b/config/pkg/lib/postgresql.yml @@ -18,6 +18,7 @@ postgresql: - openssl - zlib tools@windows: + - python-win - meson - ninja - winflexbison diff --git a/config/pkg/tool/python-win.yml b/config/pkg/tool/python-win.yml new file mode 100644 index 00000000..388c1ace --- /dev/null +++ b/config/pkg/tool/python-win.yml @@ -0,0 +1,8 @@ +python-win: + type: tool + artifact: + binary: custom + tool: + provides: + - python.exe + binary-subdir: python-win/tools diff --git a/src/Package/Artifact/meson_win.php b/src/Package/Artifact/meson_win.php index d3a911be..1594579b 100644 --- a/src/Package/Artifact/meson_win.php +++ b/src/Package/Artifact/meson_win.php @@ -22,8 +22,11 @@ class meson_win $dir = dirname($target_path); $venv = "{$dir}\\venv"; + // Prefer the python-win tool package (installed alongside via tools@windows), + // fall back to whatever Python the machine already has. + $candidates = ['"' . PKG_ROOT_PATH . '\python-win\tools\python.exe"', 'python', 'py -3']; $python = null; - foreach (['python', 'py -3'] as $candidate) { + foreach ($candidates as $candidate) { [$code] = cmd()->execWithResult("{$candidate} --version", false); if ($code === 0) { $python = $candidate; @@ -31,7 +34,7 @@ class meson_win } } if ($python === null) { - throw new EnvironmentException('meson needs Python 3 on PATH. Install it from https://www.python.org or with `winget install Python.Python.3.13`.'); + throw new EnvironmentException('meson needs Python 3; the python-win tool package did not install and no system Python was found.'); } if (is_dir($venv)) { diff --git a/src/Package/Artifact/python_win.php b/src/Package/Artifact/python_win.php new file mode 100644 index 00000000..f3aa7135 --- /dev/null +++ b/src/Package/Artifact/python_win.php @@ -0,0 +1,66 @@ +getLatestVersion($downloader->getRetry()); + + $url = "https://api.nuget.org/v3-flatcontainer/python/{$version}/python.{$version}.nupkg"; + // .nupkg is a zip; name the cache file .zip so the extractor treats it as one + $path = DOWNLOAD_PATH . DIRECTORY_SEPARATOR . "python-win-{$version}.zip"; + default_shell()->executeCurlDownload($url, $path, retries: $downloader->getRetry()); + + return DownloadResult::archive(basename($path), ['url' => $url, 'version' => $version], extract: '{pkg_root_path}/python-win', version: $version); + } + + #[CustomBinaryCheckUpdate('python-win', ['windows-x86_64'])] + public function checkUpdateBinary(?string $old_version): CheckUpdateResult + { + $version = $this->getLatestVersion(); + return new CheckUpdateResult( + old: $old_version, + new: $version, + needUpdate: $old_version === null || $version !== $old_version, + ); + } + + #[AfterBinaryExtract('python-win', ['windows-x86_64'])] + public function afterExtract(string $target_path): void + { + if (!file_exists("{$target_path}\\tools\\python.exe")) { + throw new DownloaderException("Python installation appears incomplete: python.exe not found at {$target_path}\\tools\\python.exe"); + } + } + + private function getLatestVersion(int $retries = 0): string + { + $index = default_shell()->executeCurl('https://api.nuget.org/v3-flatcontainer/python/index.json', retries: $retries); + $versions = $index ? (json_decode($index, true)['versions'] ?? []) : []; + $stable = array_filter($versions, fn ($v) => preg_match('/^\d+\.\d+\.\d+$/', $v)); + if ($stable === []) { + throw new DownloaderException('Failed to get Python versions from the nuget index'); + } + return end($stable); + } +}