Skip to content

Commit 5a8ed01

Browse files
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) (cherry picked from commit daec3a4) Co-authored-by: Martin Boisvert <[email protected]>
1 parent bab7994 commit 5a8ed01

File tree

2 files changed

+124
-34
lines changed

2 files changed

+124
-34
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Restore ability to launch older 32-bit versions from the :file:`py.exe`
2+
launcher when both 32-bit and 64-bit installs of the same version are
3+
available.

PC/launcher2.c

Lines changed: 121 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,34 +1294,34 @@ _compareTag(const wchar_t *x, const wchar_t *y)
12941294

12951295

12961296
int
1297-
addEnvironmentInfo(EnvironmentInfo **root, EnvironmentInfo *node)
1297+
addEnvironmentInfo(EnvironmentInfo **root, EnvironmentInfo* parent, EnvironmentInfo *node)
12981298
{
12991299
EnvironmentInfo *r = *root;
13001300
if (!r) {
13011301
*root = node;
1302-
node->parent = NULL;
1302+
node->parent = parent;
13031303
return 0;
13041304
}
13051305
// Sort by company name
13061306
switch (_compareCompany(node->company, r->company)) {
13071307
case -1:
1308-
return addEnvironmentInfo(&r->prev, node);
1308+
return addEnvironmentInfo(&r->prev, r, node);
13091309
case 1:
1310-
return addEnvironmentInfo(&r->next, node);
1310+
return addEnvironmentInfo(&r->next, r, node);
13111311
case 0:
13121312
break;
13131313
}
13141314
// Then by tag (descending)
13151315
switch (_compareTag(node->tag, r->tag)) {
13161316
case -1:
1317-
return addEnvironmentInfo(&r->next, node);
1317+
return addEnvironmentInfo(&r->next, r, node);
13181318
case 1:
1319-
return addEnvironmentInfo(&r->prev, node);
1319+
return addEnvironmentInfo(&r->prev, r, node);
13201320
case 0:
13211321
break;
13221322
}
13231323
// Then keep the one with the lowest internal sort key
1324-
if (r->internalSortKey < node->internalSortKey) {
1324+
if (node->internalSortKey < r->internalSortKey) {
13251325
// Replace the current node
13261326
node->parent = r->parent;
13271327
if (node->parent) {
@@ -1334,9 +1334,16 @@ addEnvironmentInfo(EnvironmentInfo **root, EnvironmentInfo *node)
13341334
freeEnvironmentInfo(node);
13351335
return RC_INTERNAL_ERROR;
13361336
}
1337+
} else {
1338+
// If node has no parent, then it is the root.
1339+
*root = node;
13371340
}
1341+
13381342
node->next = r->next;
13391343
node->prev = r->prev;
1344+
1345+
debug(L"# replaced %s/%s/%i in tree\n", node->company, node->tag, node->internalSortKey);
1346+
freeEnvironmentInfo(r);
13401347
} else {
13411348
debug(L"# not adding %s/%s/%i to tree\n", node->company, node->tag, node->internalSortKey);
13421349
return RC_DUPLICATE_ITEM;
@@ -1392,6 +1399,100 @@ _combineWithInstallDir(const wchar_t **dest, const wchar_t *installDir, const wc
13921399
}
13931400

13941401

1402+
bool
1403+
_isLegacyVersion(EnvironmentInfo *env)
1404+
{
1405+
// Check if backwards-compatibility is required.
1406+
// Specifically PythonCore versions 2.X and 3.0 - 3.5 do not implement PEP 514.
1407+
if (0 != _compare(env->company, -1, L"PythonCore", -1)) {
1408+
return false;
1409+
}
1410+
1411+
int versionMajor, versionMinor;
1412+
int n = swscanf_s(env->tag, L"%d.%d", &versionMajor, &versionMinor);
1413+
if (n != 2) {
1414+
debug(L"# %s/%s has an invalid version tag\n", env->company, env->tag);
1415+
return false;
1416+
}
1417+
1418+
return versionMajor == 2
1419+
|| (versionMajor == 3 && versionMinor >= 0 && versionMinor <= 5);
1420+
}
1421+
1422+
int
1423+
_registryReadLegacyEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *env, const wchar_t *fallbackArch)
1424+
{
1425+
// Backwards-compatibility for PythonCore versions which do not implement PEP 514.
1426+
int exitCode = _combineWithInstallDir(
1427+
&env->executablePath,
1428+
env->installDir,
1429+
search->executable,
1430+
search->executableLength
1431+
);
1432+
if (exitCode) {
1433+
return exitCode;
1434+
}
1435+
1436+
if (search->windowed) {
1437+
exitCode = _registryReadString(&env->executableArgs, root, L"InstallPath", L"WindowedExecutableArguments");
1438+
}
1439+
else {
1440+
exitCode = _registryReadString(&env->executableArgs, root, L"InstallPath", L"ExecutableArguments");
1441+
}
1442+
if (exitCode) {
1443+
return exitCode;
1444+
}
1445+
1446+
if (fallbackArch) {
1447+
copyWstr(&env->architecture, fallbackArch);
1448+
} else {
1449+
DWORD binaryType;
1450+
BOOL success = GetBinaryTypeW(env->executablePath, &binaryType);
1451+
if (!success) {
1452+
return RC_NO_PYTHON;
1453+
}
1454+
1455+
switch (binaryType) {
1456+
case SCS_32BIT_BINARY:
1457+
copyWstr(&env->architecture, L"32bit");
1458+
break;
1459+
case SCS_64BIT_BINARY:
1460+
copyWstr(&env->architecture, L"64bit");
1461+
break;
1462+
default:
1463+
return RC_NO_PYTHON;
1464+
}
1465+
}
1466+
1467+
if (0 == _compare(env->architecture, -1, L"32bit", -1)) {
1468+
size_t tagLength = wcslen(env->tag);
1469+
if (tagLength <= 3 || 0 != _compare(&env->tag[tagLength - 3], 3, L"-32", 3)) {
1470+
const wchar_t *rawTag = env->tag;
1471+
wchar_t *realTag = (wchar_t*) malloc(sizeof(wchar_t) * (tagLength + 4));
1472+
if (!realTag) {
1473+
return RC_NO_MEMORY;
1474+
}
1475+
1476+
int count = swprintf_s(realTag, tagLength + 4, L"%s-32", env->tag);
1477+
if (count == -1) {
1478+
free(realTag);
1479+
return RC_INTERNAL_ERROR;
1480+
}
1481+
1482+
env->tag = realTag;
1483+
free((void*)rawTag);
1484+
}
1485+
}
1486+
1487+
wchar_t buffer[MAXLEN];
1488+
if (swprintf_s(buffer, MAXLEN, L"Python %s", env->tag)) {
1489+
copyWstr(&env->displayName, buffer);
1490+
}
1491+
1492+
return 0;
1493+
}
1494+
1495+
13951496
int
13961497
_registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *env, const wchar_t *fallbackArch)
13971498
{
@@ -1403,6 +1504,10 @@ _registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *e
14031504
return RC_NO_PYTHON;
14041505
}
14051506

1507+
if (_isLegacyVersion(env)) {
1508+
return _registryReadLegacyEnvironment(search, root, env, fallbackArch);
1509+
}
1510+
14061511
// If pythonw.exe requested, check specific value
14071512
if (search->windowed) {
14081513
exitCode = _registryReadString(&env->executablePath, root, L"InstallPath", L"WindowedExecutablePath");
@@ -1425,6 +1530,11 @@ _registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *e
14251530
return exitCode;
14261531
}
14271532

1533+
if (!env->executablePath) {
1534+
debug(L"# %s/%s has no executable path\n", env->company, env->tag);
1535+
return RC_NO_PYTHON;
1536+
}
1537+
14281538
exitCode = _registryReadString(&env->architecture, root, NULL, L"SysArchitecture");
14291539
if (exitCode) {
14301540
return exitCode;
@@ -1435,29 +1545,6 @@ _registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *e
14351545
return exitCode;
14361546
}
14371547

1438-
// Only PythonCore entries will infer executablePath from installDir and architecture from the binary
1439-
if (0 == _compare(env->company, -1, L"PythonCore", -1)) {
1440-
if (!env->executablePath) {
1441-
exitCode = _combineWithInstallDir(
1442-
&env->executablePath,
1443-
env->installDir,
1444-
search->executable,
1445-
search->executableLength
1446-
);
1447-
if (exitCode) {
1448-
return exitCode;
1449-
}
1450-
}
1451-
if (!env->architecture && env->executablePath && fallbackArch) {
1452-
copyWstr(&env->architecture, fallbackArch);
1453-
}
1454-
}
1455-
1456-
if (!env->executablePath) {
1457-
debug(L"# %s/%s has no executable path\n", env->company, env->tag);
1458-
return RC_NO_PYTHON;
1459-
}
1460-
14611548
return 0;
14621549
}
14631550

@@ -1486,7 +1573,7 @@ _registrySearchTags(const SearchInfo *search, EnvironmentInfo **result, HKEY roo
14861573
freeEnvironmentInfo(env);
14871574
exitCode = 0;
14881575
} else if (!exitCode) {
1489-
exitCode = addEnvironmentInfo(result, env);
1576+
exitCode = addEnvironmentInfo(result, NULL, env);
14901577
if (exitCode) {
14911578
freeEnvironmentInfo(env);
14921579
if (exitCode == RC_DUPLICATE_ITEM) {
@@ -1574,7 +1661,7 @@ appxSearch(const SearchInfo *search, EnvironmentInfo **result, const wchar_t *pa
15741661
copyWstr(&env->displayName, buffer);
15751662
}
15761663

1577-
int exitCode = addEnvironmentInfo(result, env);
1664+
int exitCode = addEnvironmentInfo(result, NULL, env);
15781665
if (exitCode) {
15791666
freeEnvironmentInfo(env);
15801667
if (exitCode == RC_DUPLICATE_ITEM) {
@@ -1612,7 +1699,7 @@ explicitOverrideSearch(const SearchInfo *search, EnvironmentInfo **result)
16121699
if (exitCode) {
16131700
goto abort;
16141701
}
1615-
exitCode = addEnvironmentInfo(result, env);
1702+
exitCode = addEnvironmentInfo(result, NULL, env);
16161703
if (exitCode) {
16171704
goto abort;
16181705
}
@@ -1661,7 +1748,7 @@ virtualenvSearch(const SearchInfo *search, EnvironmentInfo **result)
16611748
if (exitCode) {
16621749
goto abort;
16631750
}
1664-
exitCode = addEnvironmentInfo(result, env);
1751+
exitCode = addEnvironmentInfo(result, NULL, env);
16651752
if (exitCode) {
16661753
goto abort;
16671754
}

0 commit comments

Comments
 (0)