Skip to content

Commit

Permalink
getpath.py: add support for mingw
Browse files Browse the repository at this point in the history
- always normalize the PREFIX to an absolute path
- use `/` when MSYSTEM is defined
  • Loading branch information
naveen521kk authored and lazka committed Jul 19, 2023
1 parent 3973987 commit 401f138
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 30 deletions.
1 change: 1 addition & 0 deletions Include/pylifecycle.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ PyAPI_FUNC(int) Py_IsInitialized(void);
PyAPI_FUNC(PyThreadState *) Py_NewInterpreter(void);
PyAPI_FUNC(void) Py_EndInterpreter(PyThreadState *);

PyAPI_FUNC(wchar_t) Py_GetAltSepW(const wchar_t *);
PyAPI_FUNC(wchar_t) Py_GetSepW(const wchar_t *);
PyAPI_FUNC(char) Py_GetSepA(const char *);

Expand Down
9 changes: 9 additions & 0 deletions Modules/getpath.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ getpath_abspath(PyObject *Py_UNUSED(self), PyObject *args)
if (path) {
wchar_t *abs;
if (_Py_abspath((const wchar_t *)_Py_normpath(path, -1), &abs) == 0 && abs) {
abs = _Py_normpath(abs, -1);
r = PyUnicode_FromWideChar(abs, -1);
PyMem_RawFree((void *)abs);
} else {
Expand Down Expand Up @@ -884,6 +885,11 @@ _PyConfig_InitPathConfig(PyConfig *config, int compute_path_config)
#else
!decode_to_dict(dict, "os_name", "posix") ||
#endif
#ifdef __MINGW32__
!int_to_dict(dict, "is_mingw", 1) ||
#else
!int_to_dict(dict, "is_mingw", 0) ||
#endif
#ifdef WITH_NEXT_FRAMEWORK
!int_to_dict(dict, "WITH_NEXT_FRAMEWORK", 1) ||
#else
Expand All @@ -910,6 +916,9 @@ _PyConfig_InitPathConfig(PyConfig *config, int compute_path_config)
!funcs_to_dict(dict, config->pathconfig_warnings) ||
#ifndef MS_WINDOWS
PyDict_SetItemString(dict, "winreg", Py_None) < 0 ||
#endif
#ifdef __MINGW32__
!env_to_dict(dict, "ENV_MSYSTEM", 0) ||
#endif
PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()) < 0
) {
Expand Down
48 changes: 35 additions & 13 deletions Modules/getpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

# ** Values known at compile time **
# os_name -- [in] one of 'nt', 'posix', 'darwin'
# is_mingw -- [in] True if targeting MinGW
# PREFIX -- [in] sysconfig.get_config_var(...)
# EXEC_PREFIX -- [in] sysconfig.get_config_var(...)
# PYTHONPATH -- [in] sysconfig.get_config_var(...)
Expand All @@ -51,6 +52,7 @@
# ENV_PYTHONHOME -- [in] getenv(...)
# ENV_PYTHONEXECUTABLE -- [in] getenv(...)
# ENV___PYVENV_LAUNCHER__ -- [in] getenv(...)
# ENV_MSYSTEM -- [in] getenv(...)

# ** Values calculated at runtime **
# config -- [in/out] dict of the PyConfig structure
Expand Down Expand Up @@ -185,8 +187,27 @@
ZIP_LANDMARK = f'{platlibdir}/python{VERSION_MAJOR}{VERSION_MINOR}.zip'
DELIM = ':'
SEP = '/'
ALTSEP = None

elif os_name == 'nt':
elif os_name == 'nt' and is_mingw:
BUILDDIR_TXT = 'pybuilddir.txt'
BUILD_LANDMARK = 'Modules/Setup.local'
DEFAULT_PROGRAM_NAME = f'python{VERSION_MAJOR}'
STDLIB_SUBDIR = f'{platlibdir}/python{VERSION_MAJOR}.{VERSION_MINOR}'
STDLIB_LANDMARKS = [f'{STDLIB_SUBDIR}/os.py', f'{STDLIB_SUBDIR}/os.pyc']
PLATSTDLIB_LANDMARK = f'{platlibdir}/python{VERSION_MAJOR}.{VERSION_MINOR}/lib-dynload'
BUILDSTDLIB_LANDMARKS = ['Lib/os.py']
VENV_LANDMARK = 'pyvenv.cfg'
ZIP_LANDMARK = f'{platlibdir}/python{VERSION_MAJOR}{VERSION_MINOR}.zip'
DELIM = ';'
if ENV_MSYSTEM:
SEP = '/'
ALTSEP = '\\'
else:
SEP = '\\'
ALTSEP = '/'

elif os_name == 'nt': # MSVC
BUILDDIR_TXT = 'pybuilddir.txt'
BUILD_LANDMARK = f'{VPATH}\\Modules\\Setup.local'
DEFAULT_PROGRAM_NAME = f'python'
Expand All @@ -199,6 +220,7 @@
WINREG_KEY = f'SOFTWARE\\Python\\PythonCore\\{PYWINVER}\\PythonPath'
DELIM = ';'
SEP = '\\'
ALTSEP = '/'


# ******************************************************************************
Expand Down Expand Up @@ -263,10 +285,10 @@ def search_up(prefix, *landmarks, test=isfile):
if not executable:
executable = real_executable

if not executable and SEP in program_name:
if not executable and (SEP in program_name or
(ALTSEP and ALTSEP in program_name)):
# Resolve partial path program_name against current directory
executable = abspath(program_name)

if not executable:
# All platforms default to real_executable if known at this
# stage. POSIX does not set this value.
Expand Down Expand Up @@ -497,15 +519,15 @@ def search_up(prefix, *landmarks, test=isfile):
except (FileNotFoundError, PermissionError):
if isfile(joinpath(real_executable_dir, BUILD_LANDMARK)):
build_prefix = joinpath(real_executable_dir, VPATH)
if os_name == 'nt':
if os_name == 'nt' and not is_mingw:
# QUIRK: Windows builds need platstdlib_dir to be the executable
# dir. Normally the builddir marker handles this, but in this
# case we need to correct manually.
platstdlib_dir = real_executable_dir

if build_prefix:
if os_name == 'nt':
# QUIRK: No searching for more landmarks on Windows
if os_name == 'nt' and not is_mingw:
# QUIRK: No searching for more landmarks on MSVC
build_stdlib_prefix = build_prefix
else:
build_stdlib_prefix = search_up(build_prefix, *BUILDSTDLIB_LANDMARKS)
Expand Down Expand Up @@ -597,7 +619,7 @@ def search_up(prefix, *landmarks, test=isfile):

# Detect exec_prefix by searching from executable for the platstdlib_dir
if PLATSTDLIB_LANDMARK and not exec_prefix:
if os_name == 'nt':
if os_name == 'nt' and (not is_mingw):
# QUIRK: Windows always assumed these were the same
# gh-100320: Our PYDs are assumed to be relative to the Lib directory
# (that is, prefix) rather than the executable (that is, executable_dir)
Expand All @@ -607,7 +629,7 @@ def search_up(prefix, *landmarks, test=isfile):
if not exec_prefix and EXEC_PREFIX:
exec_prefix = EXEC_PREFIX
if not exec_prefix or not isdir(joinpath(exec_prefix, PLATSTDLIB_LANDMARK)):
if os_name == 'nt':
if os_name == 'nt' and (not is_mingw):
# QUIRK: If DLLs is missing on Windows, don't warn, just assume
# that they're in exec_prefix
if not platstdlib_dir:
Expand Down Expand Up @@ -660,7 +682,7 @@ def search_up(prefix, *landmarks, test=isfile):
pythonpath.append(abspath(p))

# Then add the default zip file
if os_name == 'nt':
if os_name == 'nt' and (not is_mingw):
# QUIRK: Windows uses the library directory rather than the prefix
if library:
library_dir = dirname(library)
Expand All @@ -673,7 +695,7 @@ def search_up(prefix, *landmarks, test=isfile):
else:
pythonpath.append(joinpath(prefix, ZIP_LANDMARK))

if os_name == 'nt' and use_environment and winreg:
if (not is_mingw) and os_name == 'nt' and use_environment and winreg:
# QUIRK: Windows also lists paths in the registry. Paths are stored
# as the default value of each subkey of
# {HKCU,HKLM}\Software\Python\PythonCore\{winver}\PythonPath
Expand Down Expand Up @@ -714,7 +736,7 @@ def search_up(prefix, *landmarks, test=isfile):
if not platstdlib_dir and exec_prefix:
platstdlib_dir = joinpath(exec_prefix, PLATSTDLIB_LANDMARK)

if os_name == 'nt':
if os_name == 'nt' and (not is_mingw):
# QUIRK: Windows generates paths differently
if platstdlib_dir:
pythonpath.append(platstdlib_dir)
Expand Down Expand Up @@ -742,8 +764,8 @@ def search_up(prefix, *landmarks, test=isfile):

# QUIRK: Non-Windows replaces prefix/exec_prefix with defaults when running
# in build directory. This happens after pythonpath calculation.
if os_name != 'nt' and build_prefix:
prefix = config.get('prefix') or PREFIX
if (os_name != 'nt' or is_mingw) and build_prefix:
prefix = config.get('prefix') or abspath(PREFIX)
exec_prefix = config.get('exec_prefix') or EXEC_PREFIX or prefix


Expand Down
41 changes: 25 additions & 16 deletions Python/fileutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -2050,7 +2050,11 @@ _Py_abspath(const wchar_t *path, wchar_t **abspath_p)
}

#ifdef MS_WINDOWS
return _PyOS_getfullpathname(path, abspath_p);
if (_PyOS_getfullpathname(path, abspath_p) < 0){
return -1;
}
*abspath_p = _Py_normpath(*abspath_p, -1);
return 0;
#else
wchar_t cwd[MAXPATHLEN + 1];
cwd[Py_ARRAY_LENGTH(cwd) - 1] = 0;
Expand Down Expand Up @@ -2194,11 +2198,16 @@ _Py_normpath(wchar_t *path, Py_ssize_t size)
wchar_t *minP2 = path; // the beginning of the destination range
wchar_t lastC = L'\0'; // the last ljusted character, p2[-1] in most cases

const wchar_t sep = Py_GetSepW(NULL);
#ifdef ALTSEP
const wchar_t altsep = Py_GetAltSepW(NULL);
#endif

#define IS_END(x) (pEnd ? (x) == pEnd : !*(x))
#ifdef ALTSEP
#define IS_SEP(x) (*(x) == SEP || *(x) == ALTSEP)
#define IS_SEP(x) (*(x) == sep || *(x) == altsep)
#else
#define IS_SEP(x) (*(x) == SEP)
#define IS_SEP(x) (*(x) == sep)
#endif
#define SEP_OR_END(x) (IS_SEP(x) || IS_END(x))

Expand All @@ -2209,7 +2218,7 @@ _Py_normpath(wchar_t *path, Py_ssize_t size)
path++;
}
p1 = p2 = minP2 = path;
lastC = SEP;
lastC = sep;
}
#ifdef MS_WINDOWS
// Skip past drive segment and update minP2
Expand All @@ -2223,13 +2232,13 @@ _Py_normpath(wchar_t *path, Py_ssize_t size)
// and network paths, including the first segment.
else if (IS_SEP(&p1[0]) && IS_SEP(&p1[1])) {
int sepCount = 2;
*p2++ = SEP;
*p2++ = SEP;
*p2++ = sep;
*p2++ = sep;
p1 += 2;
for (; !IS_END(p1) && sepCount; ++p1) {
if (IS_SEP(p1)) {
--sepCount;
*p2++ = lastC = SEP;
*p2++ = lastC = sep;
} else {
*p2++ = lastC = *p1;
}
Expand All @@ -2246,26 +2255,26 @@ _Py_normpath(wchar_t *path, Py_ssize_t size)
*p2++ = *p1++;
*p2++ = *p1++;
minP2 = p2 - 1; // Absolute path has SEP at minP2
lastC = SEP;
lastC = sep;
}
#endif /* MS_WINDOWS */

/* if pEnd is specified, check that. Else, check for null terminator */
for (; !IS_END(p1); ++p1) {
wchar_t c = *p1;
#ifdef ALTSEP
if (c == ALTSEP) {
c = SEP;
if (c == altsep) {
c = sep;
}
#endif
if (lastC == SEP) {
if (lastC == sep) {
if (c == L'.') {
int sep_at_1 = SEP_OR_END(&p1[1]);
int sep_at_2 = !sep_at_1 && SEP_OR_END(&p1[2]);
if (sep_at_2 && p1[1] == L'.') {
wchar_t *p3 = p2;
while (p3 != minP2 && *--p3 == SEP) { }
while (p3 != minP2 && *(p3 - 1) != SEP) { --p3; }
while (p3 != minP2 && *--p3 == sep) { }
while (p3 != minP2 && *(p3 - 1) != sep) { --p3; }
if (p2 == minP2
|| (p3[0] == L'.' && p3[1] == L'.' && IS_SEP(&p3[2])))
{
Expand All @@ -2274,7 +2283,7 @@ _Py_normpath(wchar_t *path, Py_ssize_t size)
*p2++ = L'.';
*p2++ = L'.';
lastC = L'.';
} else if (p3[0] == SEP) {
} else if (p3[0] == sep) {
// Absolute path, so absorb segment
p2 = p3 + 1;
} else {
Expand All @@ -2285,7 +2294,7 @@ _Py_normpath(wchar_t *path, Py_ssize_t size)
} else {
*p2++ = lastC = c;
}
} else if (c == SEP) {
} else if (c == sep) {
} else {
*p2++ = lastC = c;
}
Expand All @@ -2295,7 +2304,7 @@ _Py_normpath(wchar_t *path, Py_ssize_t size)
}
*p2 = L'\0';
if (p2 != minP2) {
while (--p2 != minP2 && *p2 == SEP) {
while (--p2 != minP2 && *p2 == sep) {
*p2 = L'\0';
}
}
Expand Down
2 changes: 1 addition & 1 deletion Python/pathconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ Py_GetSepW(const wchar_t *name)
return sep;
}

static wchar_t
wchar_t
Py_GetAltSepW(const wchar_t *name)
{
char sep = Py_GetSepW(name);
Expand Down

0 comments on commit 401f138

Please sign in to comment.