gh-101135: Add backwards compatibility to Windows launcher for older 32-bit versions (GH-101138)

Python 2.x and up to 3.4 did not contain the "-32" in their registry name, so the 32 and 64-bit installs were treated equal. Since 3.5/PEP 514 this is no longer true, but we still want to detect the EOL versions correctly in case people are still using them.

Additionally, the code to replace a node with one with a lower sort key was buggy (wrong node chosen, replace never happened since parent was always NULL, replaced node never freed, etc)
This commit is contained in:
Martin Boisvert 2023-01-24 11:35:16 -05:00 committed by GitHub
parent fee7a995a1
commit daec3a463c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 124 additions and 34 deletions

View File

@ -0,0 +1,3 @@
Restore ability to launch older 32-bit versions from the :file:`py.exe`
launcher when both 32-bit and 64-bit installs of the same version are
available.

View File

@ -1294,34 +1294,34 @@ _compareTag(const wchar_t *x, const wchar_t *y)
int
addEnvironmentInfo(EnvironmentInfo **root, EnvironmentInfo *node)
addEnvironmentInfo(EnvironmentInfo **root, EnvironmentInfo* parent, EnvironmentInfo *node)
{
EnvironmentInfo *r = *root;
if (!r) {
*root = node;
node->parent = NULL;
node->parent = parent;
return 0;
}
// Sort by company name
switch (_compareCompany(node->company, r->company)) {
case -1:
return addEnvironmentInfo(&r->prev, node);
return addEnvironmentInfo(&r->prev, r, node);
case 1:
return addEnvironmentInfo(&r->next, node);
return addEnvironmentInfo(&r->next, r, node);
case 0:
break;
}
// Then by tag (descending)
switch (_compareTag(node->tag, r->tag)) {
case -1:
return addEnvironmentInfo(&r->next, node);
return addEnvironmentInfo(&r->next, r, node);
case 1:
return addEnvironmentInfo(&r->prev, node);
return addEnvironmentInfo(&r->prev, r, node);
case 0:
break;
}
// Then keep the one with the lowest internal sort key
if (r->internalSortKey < node->internalSortKey) {
if (node->internalSortKey < r->internalSortKey) {
// Replace the current node
node->parent = r->parent;
if (node->parent) {
@ -1334,9 +1334,16 @@ addEnvironmentInfo(EnvironmentInfo **root, EnvironmentInfo *node)
freeEnvironmentInfo(node);
return RC_INTERNAL_ERROR;
}
} else {
// If node has no parent, then it is the root.
*root = node;
}
node->next = r->next;
node->prev = r->prev;
debug(L"# replaced %s/%s/%i in tree\n", node->company, node->tag, node->internalSortKey);
freeEnvironmentInfo(r);
} else {
debug(L"# not adding %s/%s/%i to tree\n", node->company, node->tag, node->internalSortKey);
return RC_DUPLICATE_ITEM;
@ -1392,6 +1399,100 @@ _combineWithInstallDir(const wchar_t **dest, const wchar_t *installDir, const wc
}
bool
_isLegacyVersion(EnvironmentInfo *env)
{
// Check if backwards-compatibility is required.
// Specifically PythonCore versions 2.X and 3.0 - 3.5 do not implement PEP 514.
if (0 != _compare(env->company, -1, L"PythonCore", -1)) {
return false;
}
int versionMajor, versionMinor;
int n = swscanf_s(env->tag, L"%d.%d", &versionMajor, &versionMinor);
if (n != 2) {
debug(L"# %s/%s has an invalid version tag\n", env->company, env->tag);
return false;
}
return versionMajor == 2
|| (versionMajor == 3 && versionMinor >= 0 && versionMinor <= 5);
}
int
_registryReadLegacyEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *env, const wchar_t *fallbackArch)
{
// Backwards-compatibility for PythonCore versions which do not implement PEP 514.
int exitCode = _combineWithInstallDir(
&env->executablePath,
env->installDir,
search->executable,
search->executableLength
);
if (exitCode) {
return exitCode;
}
if (search->windowed) {
exitCode = _registryReadString(&env->executableArgs, root, L"InstallPath", L"WindowedExecutableArguments");
}
else {
exitCode = _registryReadString(&env->executableArgs, root, L"InstallPath", L"ExecutableArguments");
}
if (exitCode) {
return exitCode;
}
if (fallbackArch) {
copyWstr(&env->architecture, fallbackArch);
} else {
DWORD binaryType;
BOOL success = GetBinaryTypeW(env->executablePath, &binaryType);
if (!success) {
return RC_NO_PYTHON;
}
switch (binaryType) {
case SCS_32BIT_BINARY:
copyWstr(&env->architecture, L"32bit");
break;
case SCS_64BIT_BINARY:
copyWstr(&env->architecture, L"64bit");
break;
default:
return RC_NO_PYTHON;
}
}
if (0 == _compare(env->architecture, -1, L"32bit", -1)) {
size_t tagLength = wcslen(env->tag);
if (tagLength <= 3 || 0 != _compare(&env->tag[tagLength - 3], 3, L"-32", 3)) {
const wchar_t *rawTag = env->tag;
wchar_t *realTag = (wchar_t*) malloc(sizeof(wchar_t) * (tagLength + 4));
if (!realTag) {
return RC_NO_MEMORY;
}
int count = swprintf_s(realTag, tagLength + 4, L"%s-32", env->tag);
if (count == -1) {
free(realTag);
return RC_INTERNAL_ERROR;
}
env->tag = realTag;
free((void*)rawTag);
}
}
wchar_t buffer[MAXLEN];
if (swprintf_s(buffer, MAXLEN, L"Python %s", env->tag)) {
copyWstr(&env->displayName, buffer);
}
return 0;
}
int
_registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *env, const wchar_t *fallbackArch)
{
@ -1403,6 +1504,10 @@ _registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *e
return RC_NO_PYTHON;
}
if (_isLegacyVersion(env)) {
return _registryReadLegacyEnvironment(search, root, env, fallbackArch);
}
// If pythonw.exe requested, check specific value
if (search->windowed) {
exitCode = _registryReadString(&env->executablePath, root, L"InstallPath", L"WindowedExecutablePath");
@ -1425,6 +1530,11 @@ _registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *e
return exitCode;
}
if (!env->executablePath) {
debug(L"# %s/%s has no executable path\n", env->company, env->tag);
return RC_NO_PYTHON;
}
exitCode = _registryReadString(&env->architecture, root, NULL, L"SysArchitecture");
if (exitCode) {
return exitCode;
@ -1435,29 +1545,6 @@ _registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *e
return exitCode;
}
// Only PythonCore entries will infer executablePath from installDir and architecture from the binary
if (0 == _compare(env->company, -1, L"PythonCore", -1)) {
if (!env->executablePath) {
exitCode = _combineWithInstallDir(
&env->executablePath,
env->installDir,
search->executable,
search->executableLength
);
if (exitCode) {
return exitCode;
}
}
if (!env->architecture && env->executablePath && fallbackArch) {
copyWstr(&env->architecture, fallbackArch);
}
}
if (!env->executablePath) {
debug(L"# %s/%s has no executable path\n", env->company, env->tag);
return RC_NO_PYTHON;
}
return 0;
}
@ -1486,7 +1573,7 @@ _registrySearchTags(const SearchInfo *search, EnvironmentInfo **result, HKEY roo
freeEnvironmentInfo(env);
exitCode = 0;
} else if (!exitCode) {
exitCode = addEnvironmentInfo(result, env);
exitCode = addEnvironmentInfo(result, NULL, env);
if (exitCode) {
freeEnvironmentInfo(env);
if (exitCode == RC_DUPLICATE_ITEM) {
@ -1574,7 +1661,7 @@ appxSearch(const SearchInfo *search, EnvironmentInfo **result, const wchar_t *pa
copyWstr(&env->displayName, buffer);
}
int exitCode = addEnvironmentInfo(result, env);
int exitCode = addEnvironmentInfo(result, NULL, env);
if (exitCode) {
freeEnvironmentInfo(env);
if (exitCode == RC_DUPLICATE_ITEM) {
@ -1612,7 +1699,7 @@ explicitOverrideSearch(const SearchInfo *search, EnvironmentInfo **result)
if (exitCode) {
goto abort;
}
exitCode = addEnvironmentInfo(result, env);
exitCode = addEnvironmentInfo(result, NULL, env);
if (exitCode) {
goto abort;
}
@ -1661,7 +1748,7 @@ virtualenvSearch(const SearchInfo *search, EnvironmentInfo **result)
if (exitCode) {
goto abort;
}
exitCode = addEnvironmentInfo(result, env);
exitCode = addEnvironmentInfo(result, NULL, env);
if (exitCode) {
goto abort;
}