diff --git a/dist/setup/index.js b/dist/setup/index.js index b53c01a8..1a27fe9d 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -82997,28 +82997,43 @@ async function useCpythonVersion(version, architecture, updateEnvironment, check installDir = tc.find('Python', semanticVersionSpec, architecture); } } - if (!installDir) { - // Try system Python as fallback (e.g., on architectures without pre-built binaries) - try { - const { exitCode, stdout } = await exec.getExecOutput('python3', [ - '-c', - 'import sys; print(sys.prefix)' - ]); - if (exitCode === 0) { - const systemPrefix = stdout.trim(); - const systemVersion = await exec.getExecOutput('python3', [ + if (!installDir && !freethreaded) { + // Try system Python as fallback, but only for architectures that have + // no pre-built binaries in the manifest at all. This prevents the + // fallback from firing when a specific version is missing on a + // supported architecture (e.g., requesting Python 3.99 on x86_64). + const baseArchitecture = architecture.replace('-freethreaded', ''); + if (!manifest) { + manifest = await installer.getManifest(); + } + const archHasManifestEntries = manifest?.some(release => release.files?.some((file) => file.arch === baseArchitecture)); + if (!archHasManifestEntries) { + try { + const sysInfo = await exec.getExecOutput('python3', [ '-c', - 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")' + 'import sys, os; print(sys.executable + "\\n" + f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}" + "\\n" + sys.prefix + "\\n" + os.path.dirname(sys.executable))' ]); - if (systemVersion.exitCode === 0 && - semver.satisfies(systemVersion.stdout.trim(), semanticVersionSpec)) { - installDir = systemPrefix; - core.warning(`Pre-built Python not available for architecture '${architecture}'. Using system Python ${systemVersion.stdout.trim()} at ${systemPrefix}.`); + if (sysInfo.exitCode === 0) { + const [sysExecutable, sysVersion, sysPrefix, sysBinDir] = sysInfo.stdout.trim().split('\n'); + if (semver.satisfies(sysVersion, semanticVersionSpec)) { + core.warning(`Pre-built Python not available for architecture '${baseArchitecture}'. Using system Python ${sysVersion} at ${sysExecutable}.`); + if (updateEnvironment) { + core.exportVariable('pythonLocation', sysPrefix); + core.exportVariable('PKG_CONFIG_PATH', sysPrefix + '/lib/pkgconfig'); + core.exportVariable('Python_ROOT_DIR', sysPrefix); + core.exportVariable('Python2_ROOT_DIR', sysPrefix); + core.exportVariable('Python3_ROOT_DIR', sysPrefix); + core.addPath(sysBinDir); + } + core.setOutput('python-version', sysVersion); + core.setOutput('python-path', sysExecutable); + return { impl: 'CPython', version: sysVersion }; + } } } - } - catch { - // System Python not available, fall through to error + catch { + // System Python not available, fall through to error + } } } if (!installDir) { diff --git a/src/find-python.ts b/src/find-python.ts index 9e8de0da..ed1187ce 100644 --- a/src/find-python.ts +++ b/src/find-python.ts @@ -122,31 +122,57 @@ export async function useCpythonVersion( } } - if (!installDir) { - // Try system Python as fallback (e.g., on architectures without pre-built binaries) - try { - const {exitCode, stdout} = await exec.getExecOutput('python3', [ - '-c', - 'import sys; print(sys.prefix)' - ]); - if (exitCode === 0) { - const systemPrefix = stdout.trim(); - const systemVersion = await exec.getExecOutput('python3', [ + if (!installDir && !freethreaded) { + // Try system Python as fallback, but only for architectures that have + // no pre-built binaries in the manifest at all. This prevents the + // fallback from firing when a specific version is missing on a + // supported architecture (e.g., requesting Python 3.99 on x86_64). + const baseArchitecture = architecture.replace('-freethreaded', ''); + if (!manifest) { + manifest = await installer.getManifest(); + } + const archHasManifestEntries = manifest?.some( + release => + release.files?.some( + (file: {arch: string}) => file.arch === baseArchitecture + ) + ); + + if (!archHasManifestEntries) { + try { + const sysInfo = await exec.getExecOutput('python3', [ '-c', - 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")' + 'import sys, os; print(sys.executable + "\\n" + f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}" + "\\n" + sys.prefix + "\\n" + os.path.dirname(sys.executable))' ]); - if ( - systemVersion.exitCode === 0 && - semver.satisfies(systemVersion.stdout.trim(), semanticVersionSpec) - ) { - installDir = systemPrefix; - core.warning( - `Pre-built Python not available for architecture '${architecture}'. Using system Python ${systemVersion.stdout.trim()} at ${systemPrefix}.` - ); + if (sysInfo.exitCode === 0) { + const [sysExecutable, sysVersion, sysPrefix, sysBinDir] = + sysInfo.stdout.trim().split('\n'); + if (semver.satisfies(sysVersion, semanticVersionSpec)) { + core.warning( + `Pre-built Python not available for architecture '${baseArchitecture}'. Using system Python ${sysVersion} at ${sysExecutable}.` + ); + + if (updateEnvironment) { + core.exportVariable('pythonLocation', sysPrefix); + core.exportVariable( + 'PKG_CONFIG_PATH', + sysPrefix + '/lib/pkgconfig' + ); + core.exportVariable('Python_ROOT_DIR', sysPrefix); + core.exportVariable('Python2_ROOT_DIR', sysPrefix); + core.exportVariable('Python3_ROOT_DIR', sysPrefix); + core.addPath(sysBinDir); + } + + core.setOutput('python-version', sysVersion); + core.setOutput('python-path', sysExecutable); + + return {impl: 'CPython', version: sysVersion}; + } } + } catch { + // System Python not available, fall through to error } - } catch { - // System Python not available, fall through to error } }