Skip to content

gh-101135: Windows launcher: Add backwards compatibility for older 32-bit versions #101138

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 11 commits into from
Jan 24, 2023
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