Skip to content

[3.11] gh-101135: Add backwards compatibility to Windows launcher for older 32-bit versions (GH-101138) #101290

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.
155 changes: 121 additions & 34 deletions PC/launcher2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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)
{
Expand All @@ -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");
Expand All @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down