diff --git a/Include/win7compat.h b/Include/win7compat.h new file mode 100644 index 00000000000000..c8fb1ce477bbf2 --- /dev/null +++ b/Include/win7compat.h @@ -0,0 +1,12 @@ +#define STRSAFE_E_INSUFFICIENT_BUFFER ((HRESULT)0x8007007A) +#define PATHCCH_ALLOW_LONG_PATHS 0x01 +#define PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS 0x02 +#define PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS 0x04 +#define PATHCCH_DO_NOT_NORMALIZE_SEGMENTS 0x08 +#define PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH 0x10 +#define PATHCCH_ENSURE_TRAILING_SLASH 0x20 +#define PATHCCH_MAX_CCH 0x8000 + +HRESULT WINAPI PathCchCanonicalizeEx(WCHAR *out, SIZE_T size, const WCHAR *in, DWORD flags); +HRESULT WINAPI PathCchCombineEx(WCHAR *out, SIZE_T size, const WCHAR *path1, const WCHAR *path2, DWORD flags); +HRESULT WINAPI PathCchSkipRoot(const WCHAR *path, const WCHAR **root_end); diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index c984e2e93f3c04..5aae8cfd1b0e71 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -18,7 +18,7 @@ FSCTL_GET_REPARSE_POINT is not exported with WIN32_LEAN_AND_MEAN. */ # include -# include +# include "win7compat.h" #endif #include "pycore_ceval.h" // _PyEval_ReInitThreads() diff --git a/PC/getpathp.c b/PC/getpathp.c index 53da3a6d05faee..f1a83c16004fc5 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -90,7 +90,7 @@ #endif #include -#include +#include "win7compat.h" #include #ifdef HAVE_SYS_TYPES_H diff --git a/PC/pyconfig.h b/PC/pyconfig.h index d7d3cf081e0505..a2bc06a62a8453 100644 --- a/PC/pyconfig.h +++ b/PC/pyconfig.h @@ -135,9 +135,9 @@ WIN32 is still required for the locale module. #endif /* MS_WIN64 */ /* set the version macros for the windows headers */ -/* Python 3.9+ requires Windows 8 or greater */ -#define Py_WINVER 0x0602 /* _WIN32_WINNT_WIN8 */ -#define Py_NTDDI NTDDI_WIN8 +/* Python 3.9+ requires Windows 7 or greater */ +#define Py_WINVER 0x0601 /* _WIN32_WINNT_WIN7 */ +#define Py_NTDDI NTDDI_WIN7 /* We only set these values when building Python - we don't want to force these values on extensions, as that will affect the prototypes and diff --git a/PC/win7compat.c b/PC/win7compat.c new file mode 100644 index 00000000000000..7707321d54dfe5 --- /dev/null +++ b/PC/win7compat.c @@ -0,0 +1,522 @@ +#include +#include "win7compat.h" + +/* Get the next character beyond end of the segment. + Return TRUE if the last segment ends with a backslash */ +static BOOL get_next_segment(const WCHAR *next, const WCHAR **next_segment) +{ + while (*next && *next != '\\') next++; + if (*next == '\\') + { + *next_segment = next + 1; + return TRUE; + } + else + { + *next_segment = next; + return FALSE; + } +} + +static BOOL is_drive_spec( const WCHAR *str ) +{ + return ((str[0] >= 'A' && str[0] <= 'Z') || (str[0] >= 'a' && str[0] <= 'z')) && str[1] == ':'; +} + +static BOOL is_prefixed_unc(const WCHAR *string) +{ + return !_wcsnicmp(string, L"\\\\?\\UNC\\", 8 ); +} + +static BOOL is_prefixed_volume(const WCHAR *string) +{ + const WCHAR *guid; + INT i = 0; + + if (_wcsnicmp( string, L"\\\\?\\Volume", 10 )) return FALSE; + + guid = string + 10; + + while (i <= 37) + { + switch (i) + { + case 0: + if (guid[i] != '{') return FALSE; + break; + case 9: + case 14: + case 19: + case 24: + if (guid[i] != '-') return FALSE; + break; + case 37: + if (guid[i] != '}') return FALSE; + break; + default: + if (!isxdigit(guid[i])) return FALSE; + break; + } + i++; + } + + return TRUE; +} + +HRESULT WINAPI PathCchAddBackslashEx(WCHAR *path, SIZE_T size, WCHAR **endptr, SIZE_T *remaining) +{ + BOOL needs_termination; + SIZE_T length; + + length = lstrlenW(path); + needs_termination = size && length && path[length - 1] != '\\'; + + if (length >= (needs_termination ? size - 1 : size)) + { + if (endptr) *endptr = NULL; + if (remaining) *remaining = 0; + return STRSAFE_E_INSUFFICIENT_BUFFER; + } + + if (!needs_termination) + { + if (endptr) *endptr = path + length; + if (remaining) *remaining = size - length; + return S_FALSE; + } + + path[length++] = '\\'; + path[length] = 0; + + if (endptr) *endptr = path + length; + if (remaining) *remaining = size - length; + + return S_OK; +} + +static BOOL is_prefixed_disk(const WCHAR *string) +{ + return !wcsncmp(string, L"\\\\?\\", 4) && is_drive_spec( string + 4 ); +} + +HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size) +{ + return PathCchAddBackslashEx(path, size, NULL, NULL); +} + +/* Find the last character of the root in a path, if there is one, without any segments */ +static const WCHAR *get_root_end(const WCHAR *path) +{ + /* Find path root */ + if (is_prefixed_volume(path)) + return path[48] == '\\' ? path + 48 : path + 47; + else if (is_prefixed_unc(path)) + return path + 7; + else if (is_prefixed_disk(path)) + return path[6] == '\\' ? path + 6 : path + 5; + /* \\ */ + else if (path[0] == '\\' && path[1] == '\\') + return path + 1; + /* \ */ + else if (path[0] == '\\') + return path; + /* X:\ */ + else if (is_drive_spec( path )) + return path[2] == '\\' ? path + 2 : path + 1; + else + return NULL; +} + +HRESULT WINAPI PathCchStripPrefix(WCHAR *path, SIZE_T size) +{ + if (!path || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG; + + if (is_prefixed_unc(path)) + { + /* \\?\UNC\a -> \\a */ + if (size < (SIZE_T)lstrlenW(path + 8) + 3) return E_INVALIDARG; + lstrcpyW(path + 2, path + 8); + return S_OK; + } + else if (is_prefixed_disk(path)) + { + /* \\?\C:\ -> C:\ */ + if (size < (SIZE_T)lstrlenW(path + 4) + 1) return E_INVALIDARG; + lstrcpyW(path, path + 4); + return S_OK; + } + else + return S_FALSE; +} + +HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR **path_out) +{ + WCHAR *buffer, *dst; + const WCHAR *src; + const WCHAR *root_end; + SIZE_T buffer_size, length; + + if (!path_in || !path_out + || ((flags & PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS) && (flags & PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS)) + || (flags & (PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS | PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS) + && !(flags & PATHCCH_ALLOW_LONG_PATHS)) + || ((flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH) && (flags & PATHCCH_ALLOW_LONG_PATHS))) + { + if (path_out) *path_out = NULL; + return E_INVALIDARG; + } + + length = lstrlenW(path_in); + if ((length + 1 > MAX_PATH && !(flags & (PATHCCH_ALLOW_LONG_PATHS | PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH))) + || (length + 1 > PATHCCH_MAX_CCH)) + { + *path_out = NULL; + return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); + } + + /* PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH implies PATHCCH_DO_NOT_NORMALIZE_SEGMENTS */ + if (flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH) flags |= PATHCCH_DO_NOT_NORMALIZE_SEGMENTS; + + /* path length + possible \\?\ addition + possible \ addition + NUL */ + buffer_size = (length + 6) * sizeof(WCHAR); + buffer = LocalAlloc(LMEM_ZEROINIT, buffer_size); + if (!buffer) + { + *path_out = NULL; + return E_OUTOFMEMORY; + } + + src = path_in; + dst = buffer; + + root_end = get_root_end(path_in); + if (root_end) root_end = buffer + (root_end - path_in); + + /* Copy path root */ + if (root_end) + { + memcpy(dst, src, (root_end - buffer + 1) * sizeof(WCHAR)); + src += root_end - buffer + 1; + if(PathCchStripPrefix(dst, length + 6) == S_OK) + { + /* Fill in \ in X:\ if the \ is missing */ + if (is_drive_spec( dst ) && dst[2]!= '\\') + { + dst[2] = '\\'; + dst[3] = 0; + } + dst = buffer + lstrlenW(buffer); + root_end = dst; + } + else + dst += root_end - buffer + 1; + } + + while (*src) + { + if (src[0] == '.') + { + if (src[1] == '.') + { + /* Keep one . after * */ + if (dst > buffer && dst[-1] == '*') + { + *dst++ = *src++; + continue; + } + + /* Keep the .. if not surrounded by \ */ + if ((src[2] != '\\' && src[2]) || (dst > buffer && dst[-1] != '\\')) + { + *dst++ = *src++; + *dst++ = *src++; + continue; + } + + /* Remove the \ before .. if the \ is not part of root */ + if (dst > buffer && dst[-1] == '\\' && (!root_end || dst - 1 > root_end)) + { + *--dst = '\0'; + /* Remove characters until a \ is encountered */ + while (dst > buffer) + { + if (dst[-1] == '\\') + { + *--dst = 0; + break; + } + else + *--dst = 0; + } + } + /* Remove the extra \ after .. if the \ before .. wasn't deleted */ + else if (src[2] == '\\') + src++; + + src += 2; + } + else + { + /* Keep the . if not surrounded by \ */ + if ((src[1] != '\\' && src[1]) || (dst > buffer && dst[-1] != '\\')) + { + *dst++ = *src++; + continue; + } + + /* Remove the \ before . if the \ is not part of root */ + if (dst > buffer && dst[-1] == '\\' && (!root_end || dst - 1 > root_end)) dst--; + /* Remove the extra \ after . if the \ before . wasn't deleted */ + else if (src[1] == '\\') + src++; + + src++; + } + + /* If X:\ is not complete, then complete it */ + if (is_drive_spec( buffer ) && buffer[2] != '\\') + { + root_end = buffer + 2; + dst = buffer + 3; + buffer[2] = '\\'; + /* If next character is \, use the \ to fill in */ + if (src[0] == '\\') src++; + } + } + /* Copy over */ + else + *dst++ = *src++; + } + /* End the path */ + *dst = 0; + + /* Strip multiple trailing . */ + if (!(flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS)) + { + while (dst > buffer && dst[-1] == '.') + { + /* Keep a . after * */ + if (dst - 1 > buffer && dst[-2] == '*') + break; + /* If . follow a : at the second character, remove the . and add a \ */ + else if (dst - 1 > buffer && dst[-2] == ':' && dst - 2 == buffer + 1) + *--dst = '\\'; + else + *--dst = 0; + } + } + + /* If result path is empty, fill in \ */ + if (!*buffer) + { + buffer[0] = '\\'; + buffer[1] = 0; + } + + /* Extend the path if needed */ + length = lstrlenW(buffer); + if (((length + 1 > MAX_PATH && is_drive_spec( buffer )) + || (is_drive_spec( buffer ) && flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH)) + && !(flags & PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS)) + { + memmove(buffer + 4, buffer, (length + 1) * sizeof(WCHAR)); + buffer[0] = '\\'; + buffer[1] = '\\'; + buffer[2] = '?'; + buffer[3] = '\\'; + } + + /* Add a trailing backslash to the path if needed */ + if (flags & PATHCCH_ENSURE_TRAILING_SLASH) + PathCchAddBackslash(buffer, buffer_size); + + *path_out = buffer; + return S_OK; +} + +HRESULT WINAPI PathCchSkipRoot(const WCHAR *path, const WCHAR **root_end) +{ + if (!path || !path[0] || !root_end + || (!_wcsnicmp(path, L"\\\\?", 3) && !is_prefixed_volume(path) && !is_prefixed_unc(path) + && !is_prefixed_disk(path))) + return E_INVALIDARG; + + *root_end = get_root_end(path); + if (*root_end) + { + (*root_end)++; + if (is_prefixed_unc(path)) + { + get_next_segment(*root_end, root_end); + get_next_segment(*root_end, root_end); + } + else if (path[0] == '\\' && path[1] == '\\' && path[2] != '?') + { + /* Skip share server */ + get_next_segment(*root_end, root_end); + /* If mount point is empty, don't skip over mount point */ + if (**root_end != '\\') get_next_segment(*root_end, root_end); + } + } + + return *root_end ? S_OK : E_INVALIDARG; +} + +HRESULT WINAPI PathCchCanonicalizeEx(WCHAR *out, SIZE_T size, const WCHAR *in, DWORD flags) +{ + WCHAR *buffer; + SIZE_T length; + HRESULT hr; + + if (!size) return E_INVALIDARG; + + hr = PathAllocCanonicalize(in, flags, &buffer); + if (FAILED(hr)) return hr; + + length = lstrlenW(buffer); + if (size < length + 1) + { + /* No root and path > MAX_PATH - 4, return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) */ + if (length > MAX_PATH - 4 && !(in[0] == '\\' || (is_drive_spec( in ) && in[2] == '\\'))) + hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); + else + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + + if (SUCCEEDED(hr)) + { + memcpy(out, buffer, (length + 1) * sizeof(WCHAR)); + + /* Fill a backslash at the end of X: */ + if (is_drive_spec( out ) && !out[2] && size > 3) + { + out[2] = '\\'; + out[3] = 0; + } + } + + LocalFree(buffer); + return hr; +} + +HRESULT WINAPI PathCchStripToRoot(WCHAR *path, SIZE_T size) +{ + const WCHAR *root_end; + WCHAR *segment_end; + BOOL is_unc; + + if (!path || !*path || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG; + + /* \\\\?\\UNC\\* and \\\\* have to have at least two extra segments to be striped, + * e.g. \\\\?\\UNC\\a\\b\\c -> \\\\?\\UNC\\a\\b + * \\\\a\\b\\c -> \\\\a\\b */ + if ((is_unc = is_prefixed_unc(path)) || (path[0] == '\\' && path[1] == '\\' && path[2] != '?')) + { + root_end = is_unc ? path + 8 : path + 3; + if (!get_next_segment(root_end, &root_end)) return S_FALSE; + if (!get_next_segment(root_end, &root_end)) return S_FALSE; + + if ((SIZE_T)(root_end - path) >= size) return E_INVALIDARG; + + segment_end = path + (root_end - path) - 1; + *segment_end = 0; + return S_OK; + } + else if (PathCchSkipRoot(path, &root_end) == S_OK) + { + if ((SIZE_T)(root_end - path) >= size) return E_INVALIDARG; + + segment_end = path + (root_end - path); + if (!*segment_end) return S_FALSE; + + *segment_end = 0; + return S_OK; + } + else + return E_INVALIDARG; +} + +HRESULT WINAPI PathAllocCombine(const WCHAR *path1, const WCHAR *path2, DWORD flags, WCHAR **out) +{ + SIZE_T combined_length, length2; + WCHAR *combined_path; + BOOL from_path2 = FALSE; + HRESULT hr; + + if ((!path1 && !path2) || !out) + { + if (out) *out = NULL; + return E_INVALIDARG; + } + + if (!path1 || !path2) return PathAllocCanonicalize(path1 ? path1 : path2, flags, out); + + /* If path2 is fully qualified, use path2 only */ + if (is_drive_spec( path2 ) || (path2[0] == '\\' && path2[1] == '\\')) + { + path1 = path2; + path2 = NULL; + from_path2 = TRUE; + } + + length2 = path2 ? lstrlenW(path2) : 0; + /* path1 length + path2 length + possible backslash + NULL */ + combined_length = lstrlenW(path1) + length2 + 2; + + combined_path = HeapAlloc(GetProcessHeap(), 0, combined_length * sizeof(WCHAR)); + if (!combined_path) + { + *out = NULL; + return E_OUTOFMEMORY; + } + + lstrcpyW(combined_path, path1); + PathCchStripPrefix(combined_path, combined_length); + if (from_path2) PathCchAddBackslashEx(combined_path, combined_length, NULL, NULL); + + if (path2 && path2[0]) + { + if (path2[0] == '\\' && path2[1] != '\\') + { + PathCchStripToRoot(combined_path, combined_length); + path2++; + } + + PathCchAddBackslashEx(combined_path, combined_length, NULL, NULL); + lstrcatW(combined_path, path2); + } + + hr = PathAllocCanonicalize(combined_path, flags, out); + HeapFree(GetProcessHeap(), 0, combined_path); + return hr; +} + +HRESULT WINAPI PathCchCombineEx(WCHAR *out, SIZE_T size, const WCHAR *path1, const WCHAR *path2, DWORD flags) +{ + HRESULT hr; + WCHAR *buffer; + SIZE_T length; + + if (!out || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG; + + hr = PathAllocCombine(path1, path2, flags, &buffer); + if (FAILED(hr)) + { + out[0] = 0; + return hr; + } + + length = lstrlenW(buffer); + if (length + 1 > size) + { + out[0] = 0; + LocalFree(buffer); + return STRSAFE_E_INSUFFICIENT_BUFFER; + } + else + { + memcpy(out, buffer, (length + 1) * sizeof(WCHAR)); + LocalFree(buffer); + return S_OK; + } +} diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 2625d0293dde63..b1bba2d77f9ae1 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -106,7 +106,7 @@ _Py_HAVE_ZLIB;%(PreprocessorDefinitions) - version.lib;shlwapi.lib;ws2_32.lib;pathcch.lib;%(AdditionalDependencies) + version.lib;shlwapi.lib;ws2_32.lib;%(AdditionalDependencies) @@ -258,6 +258,7 @@ + @@ -435,6 +436,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 4b9eebde6a1763..93e1f00e7d9186 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -393,6 +393,9 @@ Include + + Include + Modules @@ -1010,6 +1013,9 @@ PC + + PC + PC diff --git a/README.rst b/README.rst index cb5bbd2776810e..f3a69f599eceef 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,9 @@ This is Python version 3.9.6 ============================ +This is a fork, intended to run on Windows 7, for details look at `Win7.rst +`_. + .. image:: https://travis-ci.org/python/cpython.svg?branch=3.9 :alt: CPython build status on Travis CI :target: https://travis-ci.org/python/cpython diff --git a/Tools/msi/buildrelease.bat b/Tools/msi/buildrelease.bat index b72eedecb23cf2..0e5c9c4b432683 100644 --- a/Tools/msi/buildrelease.bat +++ b/Tools/msi/buildrelease.bat @@ -177,9 +177,9 @@ if "%OUTDIR_PLAT%" EQU "win32" ( set BUILDOPTS=/p:Platform=%1 /p:BuildForRelease=true /p:DownloadUrl=%DOWNLOAD_URL% /p:DownloadUrlBase=%DOWNLOAD_URL_BASE% /p:ReleaseUri=%RELEASE_URI% if defined BUILDMSI ( - %MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true + %MSBUILD% "%D%bundle\full.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true if errorlevel 1 exit /B - %MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false + %MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false if errorlevel 1 exit /B ) diff --git a/Tools/msi/bundle/Default.wxl b/Tools/msi/bundle/Default.wxl index 791ce6eab74745..f56c386b1289c9 100644 --- a/Tools/msi/bundle/Default.wxl +++ b/Tools/msi/bundle/Default.wxl @@ -121,9 +121,17 @@ Feel free to email <a href="mailto:python-list@python.org">python-list@pyt &Restart Unable to install [WixBundleName] due to an existing install. Use Programs and Features to modify, repair or remove [WixBundleName]. - At least Windows 8.1 or Windows Server 2012 are required to install [WixBundleName] + Windows 7 Service Pack 1 and all applicable updates are required to install [WixBundleName]. + +Please <a href="https://www.bing.com/search?q=how%20to%20install%20windows%207%20service%20pack%201">update your machine</a> and then restart the installation. + Windows 7 SP1 or later is required to install and use [WixBundleName]. + +Visit <a href="https://www.python.org/">python.org</a> to download Python 3.8. + + Windows Server 2008 R2 Service Pack 1 and all applicable updates are required to install [WixBundleName]. + +Please <a href="https://www.bing.com/search?q=how%20to%20install%20windows%20server%202008%20r2%20service%20pack%201">update your machine</a> and then restart the installation. -Visit <a href="https://www.python.org/">python.org</a> to download an earlier version of Python. Disable path length limit Changes your machine configuration to allow programs, including Python, to bypass the 260 character "MAX_PATH" limitation. diff --git a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp index 3c54e401330cfd..061ec09d120837 100644 --- a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp +++ b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp @@ -3011,35 +3011,55 @@ class PythonBootstrapperApplication : public CBalBaseBootstrapperApplication { BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Target OS is Windows Server 2012 or later"); return; } else if (IsWindowsVersionOrGreater(6, 1, 1)) { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Detected Windows Server 2008 R2"); + HMODULE hKernel32 = GetModuleHandleW(L"kernel32"); + if (hKernel32 && !GetProcAddress(hKernel32, "AddDllDirectory")) { + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows Server 2008 R2 without KB2533623"); + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "KB2533623 update is required to continue."); + /* The "MissingSP1" error also specifies updates are required */ + LocGetString(_wixLoc, L"#(loc.FailureWS2K8R2MissingSP1)", &pLocString); + } else { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Target OS is Windows Server 2008 R2 or later"); + return; + } } else if (IsWindowsVersionOrGreater(6, 1, 0)) { BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows Server 2008 R2"); + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Service Pack 1 is required to continue installation"); + LocGetString(_wixLoc, L"#(loc.FailureWS2K8R2MissingSP1)", &pLocString); } else if (IsWindowsVersionOrGreater(6, 0, 0)) { BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows Server 2008"); + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Windows Server 2008 SP2 or later is required to continue installation"); + LocGetString(_wixLoc, L"#(loc.FailureWS2K8R2MissingSP1)", &pLocString); } else { BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows Server 2003 or earlier"); + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Windows Server 2008 SP2 or later is required to continue installation"); + LocGetString(_wixLoc, L"#(loc.FailureWS2K8R2MissingSP1)", &pLocString); } - BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Windows Server 2012 or later is required to continue installation"); } else { - if (IsWindows10OrGreater()) { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Target OS is Windows 10 or later"); + if (IsWindows8OrGreater()) { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Target OS is Windows 8 or later"); return; - } else if (IsWindows8Point1OrGreater()) { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Target OS is Windows 8.1"); + } else if (IsWindows7SP1OrGreater()) { + HMODULE hKernel32 = GetModuleHandleW(L"kernel32"); + if (hKernel32 && !GetProcAddress(hKernel32, "AddDllDirectory")) { + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows 7 SP1 without KB2533623"); + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "KB2533623 update is required to continue."); + /* The "MissingSP1" error also specifies updates are required */ + LocGetString(_wixLoc, L"#(loc.FailureWin7MissingSP1)", &pLocString); + } else { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Target OS is Windows 7 SP1 or later"); return; - } else if (IsWindows8OrGreater()) { - BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows 8"); + } } else if (IsWindows7OrGreater()) { - BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows 7"); - } else if (IsWindowsVistaOrGreater()) { - BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows Vista"); + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows 7 RTM"); + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Service Pack 1 is required to continue installation"); + LocGetString(_wixLoc, L"#(loc.FailureWin7MissingSP1)", &pLocString); } else { - BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows XP or earlier"); + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Vista or earlier"); + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Windows 7 SP1 or later is required to continue installation"); + LocGetString(_wixLoc, L"#(loc.FailureVistaOrEarlier)", &pLocString); } - BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Windows 8.1 or later is required to continue installation"); } - LocGetString(_wixLoc, L"#(loc.FailureOldOS)", &pLocString); if (pLocString && pLocString->wzText) { BalFormatString(pLocString->wzText, &_failedMessage); } diff --git a/Win7.rst b/Win7.rst new file mode 100644 index 00000000000000..4ab936fd249f43 --- /dev/null +++ b/Win7.rst @@ -0,0 +1,35 @@ +============ +Python 3.9.6 fork for Windows 7 support +============ + +A lot of people still use Windows 7. End date of ESU support - 2023-01-10. + +*************** +Build instructions: +*************** + +You need Visual Studio 2017 and Python 3.8. Switch off vcpkg if it's installed +(vcpkg integrate remove). Install everything related to Python in Visual Studio Installer. + + 1. Start your "x64 Native Tools Command Prompt for VS 2017" + 2. Change current directory to the place with enough free space. + 3. pip install -U sphinx==2.4.4 + 4. set PYTHON="C:\\Program Files\\Python38\\python.exe" + 5. set SPHINXBUILD="C:\\Program Files\\Python38\\Scripts\\sphinx-build.exe" + 6. git clone https://github.com/NulAsh/cpython.git + 7. cd cpython\\Tools\\msi + 8. buildrelease.bat -x64 + 9. cd ..\\..\\PCbuild\\amd64\\en-us + +And in this folder we can see installer python-3.9.6-amd64.exe, install it as usual. + +If you need debugging symbols and/or debug binaries, you need to use python-3.9.6-amd64-full.exe + +Usually Python is distributed in two forms: +- web-based installer (internally called "releaseweb") - very small executable that downloads everything before installation, will not work if you have no internet connection +- executable installer (internally called "releaselocal") - have standard Python distribution inside, but without debugging symbols (*.pdb) and debug binaries, +so if you need them, you still have to download them separately (installer will do this for you if you select appropriate checkboxes) + +But in case of my fork, if you need them, you have to download full release, it have them inside + +Thanks to developers of wine, I used some of their code to emulate functions absent in Windows 7.