From 97d1a8073e15181fceae2b12f18c9abfa2405a14 Mon Sep 17 00:00:00 2001 From: luming Date: Fri, 9 May 2025 11:40:12 +0800 Subject: [PATCH] =?UTF-8?q?[=E6=96=B0=E9=9C=80=E6=B1=82]:=20Python?= =?UTF-8?q?=E6=94=AF=E6=8C=81window-x86=E5=BD=A2=E6=80=81=E7=BC=96?= =?UTF-8?q?=E8=AF=91=EF=BC=8C=E5=B9=B6=E6=94=AF=E6=8C=81ctypes=20https://g?= =?UTF-8?q?itee.com/openharmony/third=5Fparty=5Fpython/issues/IC6IUB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: luming --- .cid/build.py | 56 + .../cpython_mingw_v3.11.4_20250509.patch | 7112 +++++++++++++++++ .cid/python_builder.py | 468 ++ 3 files changed, 7636 insertions(+) create mode 100644 .cid/build.py create mode 100644 .cid/patches/cpython_mingw_v3.11.4_20250509.patch create mode 100644 .cid/python_builder.py diff --git a/.cid/build.py b/.cid/build.py new file mode 100644 index 0000000..5f05fbc --- /dev/null +++ b/.cid/build.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import argparse +import logging +import shutil +from pathlib import Path +from python_builder import MinGWPythonBuilder, BuildConfig + + +def main(): + parser = argparse.ArgumentParser(description='Python builder command line options') + parser.add_argument('--repo-root', default='.', help='Repository root directory') + parser.add_argument('--out-path', default='./out', help='Output directory') + parser.add_argument('--lldb-py-version', default='3.11.4', help='LLDB Python version') + parser.add_argument('--lldb-py-detailed-version', default='3.11.4_20250509', help='LLDB Python detailed version') + parser.add_argument('--mingw-triple', default='x86_64-w64-mingw32', help='MinGW triple') + + args = parser.parse_args() + build_config = BuildConfig(args) + + # 删除并重新创建 OUT_PATH + out_path = Path(build_config.OUT_PATH) + if out_path.exists(): + shutil.rmtree(out_path) + out_path.mkdir(parents=True, exist_ok=True) + + logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', + filename=build_config.OUT_PATH + '/build.log') + + try: + mingw_builder = MinGWPythonBuilder(build_config) + mingw_builder.build() + mingw_builder.prepare_for_package() + mingw_builder.package() + logging.info("MinGW Python 构建、准备和打包完成。") + except Exception as e: + logging.error(f"MinGW 构建过程中发生错误: {str(e)}") + + +if __name__ == "__main__": + main() diff --git a/.cid/patches/cpython_mingw_v3.11.4_20250509.patch b/.cid/patches/cpython_mingw_v3.11.4_20250509.patch new file mode 100644 index 0000000..fb694b8 --- /dev/null +++ b/.cid/patches/cpython_mingw_v3.11.4_20250509.patch @@ -0,0 +1,7112 @@ +From fda6ecc6446980906f83b53e4c8add6d6023a5ba Mon Sep 17 00:00:00 2001 +From: luming +Date: Fri, 9 May 2025 08:45:54 +0800 +Subject: [PATCH] cpython 3.11.4 for mingw + +Change-Id: I7ae3759608e3fc26eb2ebef1fb200e46b3add264 +--- + .github/workflows/build.yml | 418 +++++++++++ + .gitignore | 5 +- + Include/bytesobject.h | 4 +- + Include/fileobject.h | 2 +- + Include/internal/pycore_condvar.h | 10 + + Include/iscygpty.h | 41 + + Include/osdefs.h | 1 - + Include/py_curses.h | 7 + + Include/pyerrors.h | 4 +- + Include/pylifecycle.h | 9 + + Include/pyport.h | 41 +- + Include/pythread.h | 6 + + Include/sysmodule.h | 4 +- + Lib/compileall.py | 2 + + Lib/ctypes/__init__.py | 4 +- + Lib/ctypes/util.py | 9 + + Lib/distutils/ccompiler.py | 4 +- + Lib/distutils/command/build_ext.py | 27 +- + Lib/distutils/command/install.py | 10 +- + Lib/distutils/cygwinccompiler.py | 188 ++--- + Lib/distutils/msvc9compiler.py | 4 +- + Lib/distutils/sysconfig.py | 39 +- + Lib/distutils/tests/test_cygwinccompiler.py | 36 +- + Lib/distutils/unixccompiler.py | 8 +- + Lib/distutils/util.py | 42 +- + Lib/importlib/_bootstrap_external.py | 4 + + Lib/ntpath.py | 108 +-- + Lib/pathlib.py | 2 + + Lib/site.py | 38 +- + Lib/ssl.py | 4 +- + Lib/sysconfig.py | 107 ++- + Lib/test/__main__.py | 8 + + Lib/test/test_bytes.py | 2 +- + Lib/test/test_getpath.py | 9 + + Lib/test/test_httpservers.py | 4 +- + Lib/test/test_importlib/test_windows.py | 17 + + Lib/test/test_sysconfig.py | 8 +- + Lib/venv/__init__.py | 9 +- + Makefile.pre.in | 88 ++- + Misc/NEWS.d/3.11.4.rst | 785 ++++++++++++++++++++ + Misc/config_mingw | 15 + + Misc/cross_mingw32 | 11 + + Misc/python-config.sh.in | 62 +- + Misc/python.pc.in | 2 +- + Modules/Setup.bootstrap.in | 11 +- + Modules/_ctypes/_ctypes.c | 19 + + Modules/_gdbmmodule.c | 2 +- + Modules/_io/fileio.c | 3 +- + Modules/_localemodule.c | 7 + + Modules/_multiprocessing/multiprocessing.c | 2 +- + Modules/_multiprocessing/multiprocessing.h | 5 +- + Modules/_winapi.c | 4 +- + Modules/_xxsubinterpretersmodule.c | 2 +- + Modules/getpath.c | 40 + + Modules/getpath.py | 81 +- + Modules/main.c | 3 +- + Modules/posixmodule.c | 45 +- + Modules/selectmodule.c | 6 +- + Modules/socketmodule.c | 8 +- + Modules/socketmodule.h | 2 + + Modules/timemodule.c | 2 +- + Objects/fileobject.c | 3 +- + PC/_testconsole.c | 4 +- + PC/launcher.c | 33 +- + PC/msvcrtmodule.c | 2 + + PC/pylauncher.rc | 20 +- + PC/python3dll.c | 8 + + PC/python_exe.rc | 2 +- + PC/pythonw_exe.rc | 2 +- + PC/winreg.c | 20 + + Python/bltinmodule.c | 5 +- + Python/dynamic_annotations.c | 2 +- + Python/dynload_win.c | 24 +- + Python/fileutils.c | 70 +- + Python/frozenmain.c | 3 +- + Python/getcompiler.c | 34 +- + Python/initconfig.c | 2 +- + Python/iscygpty.c | 185 +++++ + Python/pathconfig.c | 157 +++- + Python/pylifecycle.c | 5 +- + Python/sysmodule.c | 4 +- + Python/thread_nt.h | 3 +- + Python/traceback.c | 2 +- + configure.ac | 545 +++++++++++++- + mingw_ignorefile.txt | 42 ++ + mingw_smoketests.py | 358 +++++++++ + pyconfig.h.in | 7 +- + setup.py | 112 ++- + 88 files changed, 3606 insertions(+), 498 deletions(-) + create mode 100644 .github/workflows/build.yml + create mode 100644 Include/iscygpty.h + create mode 100644 Misc/NEWS.d/3.11.4.rst + create mode 100644 Misc/config_mingw + create mode 100644 Misc/cross_mingw32 + create mode 100644 Python/iscygpty.c + create mode 100644 mingw_ignorefile.txt + create mode 100644 mingw_smoketests.py + +diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml +new file mode 100644 +index 0000000..c2293cb +--- /dev/null ++++ b/.github/workflows/build.yml +@@ -0,0 +1,418 @@ ++name: Tests ++ ++# gh-84728: "paths-ignore" is not used to skip documentation-only PRs, because ++# it prevents to mark a job as mandatory. A PR cannot be merged if a job is ++# mandatory but not scheduled because of "paths-ignore". ++on: ++ workflow_dispatch: ++ push: ++ branches: ++ - 'main' ++ - '3.11' ++ - '3.10' ++ - '3.9' ++ - '3.8' ++ - '3.7' ++ pull_request: ++ branches: ++ - 'main' ++ - '3.11' ++ - '3.10' ++ - '3.9' ++ - '3.8' ++ - '3.7' ++ ++permissions: ++ contents: read ++ ++concurrency: ++ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} ++ cancel-in-progress: true ++ ++jobs: ++ check_source: ++ name: 'Check for source changes' ++ runs-on: ubuntu-latest ++ timeout-minutes: 10 ++ outputs: ++ run_tests: ${{ steps.check.outputs.run_tests }} ++ run_ssl_tests: ${{ steps.check.outputs.run_ssl_tests }} ++ config_hash: ${{ steps.config_hash.outputs.hash }} ++ steps: ++ - uses: actions/checkout@v3 ++ - name: Check for source changes ++ id: check ++ run: | ++ if [ -z "$GITHUB_BASE_REF" ]; then ++ echo "run_tests=true" >> $GITHUB_OUTPUT ++ echo "run_ssl_tests=true" >> $GITHUB_OUTPUT ++ else ++ git fetch origin $GITHUB_BASE_REF --depth=1 ++ # git diff "origin/$GITHUB_BASE_REF..." (3 dots) may be more ++ # reliable than git diff "origin/$GITHUB_BASE_REF.." (2 dots), ++ # but it requires to download more commits (this job uses ++ # "git fetch --depth=1"). ++ # ++ # git diff "origin/$GITHUB_BASE_REF..." (3 dots) works with Git ++ # 2.26, but Git 2.28 is stricter and fails with "no merge base". ++ # ++ # git diff "origin/$GITHUB_BASE_REF.." (2 dots) should be enough on ++ # GitHub, since GitHub starts by merging origin/$GITHUB_BASE_REF ++ # into the PR branch anyway. ++ # ++ # https://github.com/python/core-workflow/issues/373 ++ git diff --name-only origin/$GITHUB_BASE_REF.. | grep -qvE '(\.rst$|^Doc|^Misc)' && echo "run_tests=true" >> $GITHUB_OUTPUT || true ++ git diff --name-only origin/$GITHUB_BASE_REF.. | grep -qE '(ssl|hashlib|hmac|^.github)' && echo "run_ssl_tests=true" >> $GITHUB_OUTPUT || true ++ fi ++ - name: Compute hash for config cache key ++ id: config_hash ++ run: | ++ echo "hash=${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }}" >> $GITHUB_OUTPUT ++ ++ check_abi: ++ name: 'Check if the ABI has changed' ++ runs-on: ubuntu-20.04 ++ needs: check_source ++ if: needs.check_source.outputs.run_tests == 'true' ++ steps: ++ - uses: actions/checkout@v2 ++ - uses: actions/setup-python@v2 ++ - name: Install Dependencies ++ run: | ++ sudo ./.github/workflows/posix-deps-apt.sh ++ sudo apt-get install -yq abigail-tools ++ - name: Build CPython ++ env: ++ CFLAGS: -g3 -O0 ++ run: | ++ # Build Python with the libpython dynamic library ++ ./configure --enable-shared ++ make -j4 ++ - name: Check for changes in the ABI ++ id: check ++ run: | ++ if ! make check-abidump; then ++ echo "Generated ABI file is not up to date." ++ echo "Please, add the release manager of this branch as a reviewer of this PR." ++ echo "" ++ echo "The up to date ABI file should be attached to this build as an artifact." ++ echo "" ++ echo "To learn more about this check: https://devguide.python.org/setup/#regenerate-the-abi-dump" ++ echo "" ++ exit 1 ++ fi ++ - name: Generate updated ABI files ++ if: ${{ failure() && steps.check.conclusion == 'failure' }} ++ run: | ++ make regen-abidump ++ - uses: actions/upload-artifact@v3 ++ name: Publish updated ABI files ++ if: ${{ failure() && steps.check.conclusion == 'failure' }} ++ with: ++ name: abi-data ++ path: ./Doc/data/*.abi ++ ++ check_generated_files: ++ name: 'Check if generated files are up to date' ++ runs-on: ubuntu-latest ++ timeout-minutes: 60 ++ needs: check_source ++ if: needs.check_source.outputs.run_tests == 'true' ++ steps: ++ - uses: actions/checkout@v3 ++ - name: Restore config.cache ++ uses: actions/cache@v3 ++ with: ++ path: config.cache ++ key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} ++ - uses: actions/setup-python@v3 ++ - name: Install Dependencies ++ run: sudo ./.github/workflows/posix-deps-apt.sh ++ - name: Add ccache to PATH ++ run: echo "PATH=/usr/lib/ccache:$PATH" >> $GITHUB_ENV ++ - name: Configure ccache action ++ uses: hendrikmuhs/ccache-action@v1.2 ++ - name: Check Autoconf version 2.69 and aclocal 1.16.3 ++ run: | ++ grep "Generated by GNU Autoconf 2.69" configure ++ grep "aclocal 1.16.3" aclocal.m4 ++ grep -q "runstatedir" configure ++ grep -q "PKG_PROG_PKG_CONFIG" aclocal.m4 ++ - name: Configure CPython ++ run: | ++ # Build Python with the libpython dynamic library ++ ./configure --config-cache --with-pydebug --enable-shared ++ - name: Regenerate autoconf files with container image ++ run: make regen-configure ++ - name: Build CPython ++ run: | ++ # Deepfreeze will usually cause global objects to be added or removed, ++ # so we run it before regen-global-objects gets rum (in regen-all). ++ make regen-deepfreeze ++ make -j4 regen-all ++ make regen-stdlib-module-names ++ - name: Check for changes ++ run: | ++ git add -u ++ changes=$(git status --porcelain) ++ # Check for changes in regenerated files ++ if test -n "$changes"; then ++ echo "Generated files not up to date." ++ echo "Perhaps you forgot to run make regen-all or build.bat --regen. ;)" ++ echo "configure files must be regenerated with a specific version of autoconf." ++ echo "$changes" ++ echo "" ++ git diff --staged || true ++ exit 1 ++ fi ++ - name: Check exported libpython symbols ++ run: make smelly ++ - name: Check limited ABI symbols ++ run: make check-limited-abi ++ ++ build_win32: ++ name: 'Windows (x86)' ++ runs-on: windows-latest ++ timeout-minutes: 60 ++ needs: check_source ++ if: needs.check_source.outputs.run_tests == 'true' ++ env: ++ IncludeUwp: 'true' ++ steps: ++ - uses: actions/checkout@v3 ++ - name: Build CPython ++ run: .\PCbuild\build.bat -e -d -p Win32 ++ - name: Display build info ++ run: .\python.bat -m test.pythoninfo ++ - name: Tests ++ run: .\PCbuild\rt.bat -p Win32 -d -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 ++ ++ build_win_amd64: ++ name: 'Windows (x64)' ++ runs-on: windows-latest ++ timeout-minutes: 60 ++ needs: check_source ++ if: needs.check_source.outputs.run_tests == 'true' ++ env: ++ IncludeUwp: 'true' ++ steps: ++ - uses: actions/checkout@v3 ++ - name: Register MSVC problem matcher ++ run: echo "::add-matcher::.github/problem-matchers/msvc.json" ++ - name: Build CPython ++ run: .\PCbuild\build.bat -e -d -p x64 ++ - name: Display build info ++ run: .\python.bat -m test.pythoninfo ++ - name: Tests ++ run: .\PCbuild\rt.bat -p x64 -d -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 ++ ++ build_macos: ++ name: 'macOS' ++ runs-on: macos-latest ++ timeout-minutes: 60 ++ needs: check_source ++ if: needs.check_source.outputs.run_tests == 'true' ++ env: ++ HOMEBREW_NO_ANALYTICS: 1 ++ HOMEBREW_NO_AUTO_UPDATE: 1 ++ HOMEBREW_NO_INSTALL_CLEANUP: 1 ++ PYTHONSTRICTEXTENSIONBUILD: 1 ++ steps: ++ - uses: actions/checkout@v3 ++ - name: Restore config.cache ++ uses: actions/cache@v3 ++ with: ++ path: config.cache ++ key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} ++ - name: Install Homebrew dependencies ++ run: brew install pkg-config openssl@1.1 xz gdbm tcl-tk ++ - name: Configure CPython ++ run: | ++ CFLAGS="-I$(brew --prefix gdbm)/include -I$(brew --prefix xz)/include" \ ++ LDFLAGS="-L$(brew --prefix gdbm)/lib -I$(brew --prefix xz)/lib" \ ++ PKG_CONFIG_PATH="$(brew --prefix tcl-tk)/lib/pkgconfig" \ ++ ./configure \ ++ --config-cache \ ++ --with-pydebug \ ++ --prefix=/opt/python-dev \ ++ --with-openssl="$(brew --prefix openssl@1.1)" ++ - name: Build CPython ++ run: make -j4 ++ - name: Display build info ++ run: make pythoninfo ++ - name: Tests ++ run: make buildbottest TESTOPTS="-j4 -uall,-cpu" ++ ++ build_ubuntu: ++ name: 'Ubuntu' ++ runs-on: ubuntu-20.04 ++ timeout-minutes: 60 ++ needs: check_source ++ if: needs.check_source.outputs.run_tests == 'true' ++ env: ++ OPENSSL_VER: 1.1.1u ++ PYTHONSTRICTEXTENSIONBUILD: 1 ++ steps: ++ - uses: actions/checkout@v3 ++ - name: Register gcc problem matcher ++ run: echo "::add-matcher::.github/problem-matchers/gcc.json" ++ - name: Install Dependencies ++ run: sudo ./.github/workflows/posix-deps-apt.sh ++ - name: Configure OpenSSL env vars ++ run: | ++ echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> $GITHUB_ENV ++ echo "OPENSSL_DIR=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}" >> $GITHUB_ENV ++ echo "LD_LIBRARY_PATH=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}/lib" >> $GITHUB_ENV ++ - name: 'Restore OpenSSL build' ++ id: cache-openssl ++ uses: actions/cache@v3 ++ with: ++ path: ./multissl/openssl/${{ env.OPENSSL_VER }} ++ key: ${{ runner.os }}-multissl-openssl-${{ env.OPENSSL_VER }} ++ - name: Install OpenSSL ++ if: steps.cache-openssl.outputs.cache-hit != 'true' ++ run: python3 Tools/ssl/multissltests.py --steps=library --base-directory $MULTISSL_DIR --openssl $OPENSSL_VER --system Linux ++ - name: Add ccache to PATH ++ run: | ++ echo "PATH=/usr/lib/ccache:$PATH" >> $GITHUB_ENV ++ - name: Configure ccache action ++ uses: hendrikmuhs/ccache-action@v1.2 ++ - name: Setup directory envs for out-of-tree builds ++ run: | ++ echo "CPYTHON_RO_SRCDIR=$(realpath -m ${GITHUB_WORKSPACE}/../cpython-ro-srcdir)" >> $GITHUB_ENV ++ echo "CPYTHON_BUILDDIR=$(realpath -m ${GITHUB_WORKSPACE}/../cpython-builddir)" >> $GITHUB_ENV ++ - name: Create directories for read-only out-of-tree builds ++ run: mkdir -p $CPYTHON_RO_SRCDIR $CPYTHON_BUILDDIR ++ - name: Bind mount sources read-only ++ run: sudo mount --bind -o ro $GITHUB_WORKSPACE $CPYTHON_RO_SRCDIR ++ - name: Restore config.cache ++ uses: actions/cache@v3 ++ with: ++ path: ${{ env.CPYTHON_BUILDDIR }}/config.cache ++ key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} ++ - name: Configure CPython out-of-tree ++ working-directory: ${{ env.CPYTHON_BUILDDIR }} ++ run: | ++ ../cpython-ro-srcdir/configure \ ++ --config-cache \ ++ --with-pydebug \ ++ --with-openssl=$OPENSSL_DIR ++ - name: Build CPython out-of-tree ++ working-directory: ${{ env.CPYTHON_BUILDDIR }} ++ run: make -j4 ++ - name: Display build info ++ working-directory: ${{ env.CPYTHON_BUILDDIR }} ++ run: make pythoninfo ++ - name: Remount sources writable for tests ++ # some tests write to srcdir, lack of pyc files slows down testing ++ run: sudo mount $CPYTHON_RO_SRCDIR -oremount,rw ++ - name: Tests ++ working-directory: ${{ env.CPYTHON_BUILDDIR }} ++ run: xvfb-run make buildbottest TESTOPTS="-j4 -uall,-cpu" ++ ++ build_ubuntu_ssltests: ++ name: 'Ubuntu SSL tests with OpenSSL' ++ runs-on: ubuntu-20.04 ++ timeout-minutes: 60 ++ needs: check_source ++ if: needs.check_source.outputs.run_tests == 'true' && needs.check_source.outputs.run_ssl_tests == 'true' ++ strategy: ++ fail-fast: false ++ matrix: ++ openssl_ver: [1.1.1u, 3.0.9, 3.1.1] ++ env: ++ OPENSSL_VER: ${{ matrix.openssl_ver }} ++ MULTISSL_DIR: ${{ github.workspace }}/multissl ++ OPENSSL_DIR: ${{ github.workspace }}/multissl/openssl/${{ matrix.openssl_ver }} ++ LD_LIBRARY_PATH: ${{ github.workspace }}/multissl/openssl/${{ matrix.openssl_ver }}/lib ++ steps: ++ - uses: actions/checkout@v3 ++ - name: Restore config.cache ++ uses: actions/cache@v3 ++ with: ++ path: config.cache ++ key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} ++ - name: Register gcc problem matcher ++ run: echo "::add-matcher::.github/problem-matchers/gcc.json" ++ - name: Install Dependencies ++ run: sudo ./.github/workflows/posix-deps-apt.sh ++ - name: Configure OpenSSL env vars ++ run: | ++ echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> $GITHUB_ENV ++ echo "OPENSSL_DIR=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}" >> $GITHUB_ENV ++ echo "LD_LIBRARY_PATH=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}/lib" >> $GITHUB_ENV ++ - name: 'Restore OpenSSL build' ++ id: cache-openssl ++ uses: actions/cache@v3 ++ with: ++ path: ./multissl/openssl/${{ env.OPENSSL_VER }} ++ key: ${{ runner.os }}-multissl-openssl-${{ env.OPENSSL_VER }} ++ - name: Install OpenSSL ++ if: steps.cache-openssl.outputs.cache-hit != 'true' ++ run: python3 Tools/ssl/multissltests.py --steps=library --base-directory $MULTISSL_DIR --openssl $OPENSSL_VER --system Linux ++ - name: Add ccache to PATH ++ run: | ++ echo "PATH=/usr/lib/ccache:$PATH" >> $GITHUB_ENV ++ - name: Configure ccache action ++ uses: hendrikmuhs/ccache-action@v1.2 ++ - name: Configure CPython ++ run: ./configure --config-cache --with-pydebug --with-openssl=$OPENSSL_DIR ++ - name: Build CPython ++ run: make -j4 ++ - name: Display build info ++ run: make pythoninfo ++ - name: SSL tests ++ run: ./python Lib/test/ssltests.py ++ ++ build_asan: ++ name: 'Address sanitizer' ++ runs-on: ubuntu-20.04 ++ timeout-minutes: 60 ++ needs: check_source ++ if: needs.check_source.outputs.run_tests == 'true' ++ env: ++ OPENSSL_VER: 1.1.1u ++ PYTHONSTRICTEXTENSIONBUILD: 1 ++ ASAN_OPTIONS: detect_leaks=0:allocator_may_return_null=1:handle_segv=0 ++ steps: ++ - uses: actions/checkout@v3 ++ - name: Restore config.cache ++ uses: actions/cache@v3 ++ with: ++ path: config.cache ++ key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} ++ - name: Register gcc problem matcher ++ run: echo "::add-matcher::.github/problem-matchers/gcc.json" ++ - name: Install Dependencies ++ run: sudo ./.github/workflows/posix-deps-apt.sh ++ - name: Set up GCC-10 for ASAN ++ uses: egor-tensin/setup-gcc@v1 ++ with: ++ version: 10 ++ - name: Configure OpenSSL env vars ++ run: | ++ echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> $GITHUB_ENV ++ echo "OPENSSL_DIR=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}" >> $GITHUB_ENV ++ echo "LD_LIBRARY_PATH=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}/lib" >> $GITHUB_ENV ++ - name: 'Restore OpenSSL build' ++ id: cache-openssl ++ uses: actions/cache@v3 ++ with: ++ path: ./multissl/openssl/${{ env.OPENSSL_VER }} ++ key: ${{ runner.os }}-multissl-openssl-${{ env.OPENSSL_VER }} ++ - name: Install OpenSSL ++ if: steps.cache-openssl.outputs.cache-hit != 'true' ++ run: python3 Tools/ssl/multissltests.py --steps=library --base-directory $MULTISSL_DIR --openssl $OPENSSL_VER --system Linux ++ - name: Add ccache to PATH ++ run: | ++ echo "PATH=/usr/lib/ccache:$PATH" >> $GITHUB_ENV ++ - name: Configure ccache action ++ uses: hendrikmuhs/ccache-action@v1.2 ++ - name: Configure CPython ++ run: ./configure --config-cache --with-address-sanitizer --without-pymalloc ++ - name: Build CPython ++ run: make -j4 ++ - name: Display build info ++ run: make pythoninfo ++ - name: Tests ++ run: xvfb-run make buildbottest TESTOPTS="-j4 -uall,-cpu" +diff --git a/.gitignore b/.gitignore +index 0ddfd71..ea6e3a8 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -114,6 +114,7 @@ PCbuild/win32/ + Tools/unicode/data/ + /autom4te.cache + /build/ ++/builddir/ + /config.cache + /config.log + /config.status +@@ -150,6 +151,4 @@ Python/frozen_modules/MANIFEST + # Ignore ./python binary on Unix but still look into ./Python/ directory. + /python + !/Python/ +- +-# main branch only: ABI files are not checked/maintained +-Doc/data/python*.abi ++.cid/* +diff --git a/Include/bytesobject.h b/Include/bytesobject.h +index 4c4dc40..cd88bed 100644 +--- a/Include/bytesobject.h ++++ b/Include/bytesobject.h +@@ -35,9 +35,9 @@ PyAPI_FUNC(PyObject *) PyBytes_FromStringAndSize(const char *, Py_ssize_t); + PyAPI_FUNC(PyObject *) PyBytes_FromString(const char *); + PyAPI_FUNC(PyObject *) PyBytes_FromObject(PyObject *); + PyAPI_FUNC(PyObject *) PyBytes_FromFormatV(const char*, va_list) +- Py_GCC_ATTRIBUTE((format(printf, 1, 0))); ++ Py_PRINTF(1, 0); + PyAPI_FUNC(PyObject *) PyBytes_FromFormat(const char*, ...) +- Py_GCC_ATTRIBUTE((format(printf, 1, 2))); ++ Py_PRINTF(1, 2); + PyAPI_FUNC(Py_ssize_t) PyBytes_Size(PyObject *); + PyAPI_FUNC(char *) PyBytes_AsString(PyObject *); + PyAPI_FUNC(PyObject *) PyBytes_Repr(PyObject *, int); +diff --git a/Include/fileobject.h b/Include/fileobject.h +index 4c983e7..260e4c1 100644 +--- a/Include/fileobject.h ++++ b/Include/fileobject.h +@@ -30,7 +30,7 @@ PyAPI_DATA(int) Py_UTF8Mode; + #endif + + /* A routine to check if a file descriptor can be select()-ed. */ +-#ifdef _MSC_VER ++#ifdef MS_WINDOWS + /* On Windows, any socket fd can be select()-ed, no matter how high */ + #define _PyIsSelectable_fd(FD) (1) + #else +diff --git a/Include/internal/pycore_condvar.h b/Include/internal/pycore_condvar.h +index 981c962..ed9e6a7 100644 +--- a/Include/internal/pycore_condvar.h ++++ b/Include/internal/pycore_condvar.h +@@ -5,6 +5,12 @@ + # error "this header requires Py_BUILD_CORE define" + #endif + ++#ifdef __MINGW32__ ++# if !defined(HAVE_PTHREAD_H) || defined(NT_THREADS) ++# undef _POSIX_THREADS ++# endif ++#endif ++ + #ifndef _POSIX_THREADS + /* This means pthreads are not implemented in libc headers, hence the macro + not present in unistd.h. But they still can be implemented as an external +@@ -39,6 +45,10 @@ + /* include windows if it hasn't been done before */ + #define WIN32_LEAN_AND_MEAN + #include ++/* winpthreads are involved via windows header, so need undef _POSIX_THREADS after header include */ ++#if defined(_POSIX_THREADS) ++#undef _POSIX_THREADS ++#endif + + /* options */ + /* non-emulated condition variables are provided for those that want +diff --git a/Include/iscygpty.h b/Include/iscygpty.h +new file mode 100644 +index 0000000..82fd0af +--- /dev/null ++++ b/Include/iscygpty.h +@@ -0,0 +1,41 @@ ++/* ++ * iscygpty.h -- part of ptycheck ++ * https://github.com/k-takata/ptycheck ++ * ++ * Copyright (c) 2015-2017 K.Takata ++ * ++ * You can redistribute it and/or modify it under the terms of either ++ * the MIT license (as described below) or the Vim license. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining ++ * a copy of this software and associated documentation files (the ++ * "Software"), to deal in the Software without restriction, including ++ * without limitation the rights to use, copy, modify, merge, publish, ++ * distribute, sublicense, and/or sell copies of the Software, and to ++ * permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ++ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ++ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ++ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ++ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ */ ++ ++#ifndef _ISCYGPTY_H ++#define _ISCYGPTY_H ++ ++#ifdef _WIN32 ++int is_cygpty(int fd); ++int is_cygpty_used(void); ++#else ++#define is_cygpty(fd) 0 ++#define is_cygpty_used() 0 ++#endif ++ ++#endif /* _ISCYGPTY_H */ +diff --git a/Include/osdefs.h b/Include/osdefs.h +index 3243944..99d4977 100644 +--- a/Include/osdefs.h ++++ b/Include/osdefs.h +@@ -10,7 +10,6 @@ extern "C" { + #ifdef MS_WINDOWS + #define SEP L'\\' + #define ALTSEP L'/' +-#define MAXPATHLEN 256 + #define DELIM L';' + #endif + +diff --git a/Include/py_curses.h b/Include/py_curses.h +index b2c7f1b..e6fc813 100644 +--- a/Include/py_curses.h ++++ b/Include/py_curses.h +@@ -36,6 +36,13 @@ + #include + #endif + ++#if defined(__MINGW32__) ++#include ++#if !defined(_ISPAD) ++#define _ISPAD 0x10 ++#endif ++#endif ++ + #ifdef HAVE_NCURSES_H + /* configure was checking , but we will + use , which has some or all these features. */ +diff --git a/Include/pyerrors.h b/Include/pyerrors.h +index 34e3de3..fb71fde 100644 +--- a/Include/pyerrors.h ++++ b/Include/pyerrors.h +@@ -315,9 +315,9 @@ PyAPI_FUNC(int) PyUnicodeTranslateError_SetReason( + ); + + PyAPI_FUNC(int) PyOS_snprintf(char *str, size_t size, const char *format, ...) +- Py_GCC_ATTRIBUTE((format(printf, 3, 4))); ++ Py_PRINTF(3, 4); + PyAPI_FUNC(int) PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) +- Py_GCC_ATTRIBUTE((format(printf, 3, 0))); ++ Py_PRINTF(3, 0); + + #ifndef Py_LIMITED_API + # define Py_CPYTHON_ERRORS_H +diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h +index e4c3b09..21346ba 100644 +--- a/Include/pylifecycle.h ++++ b/Include/pylifecycle.h +@@ -21,6 +21,15 @@ 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 *); ++ ++PyAPI_FUNC(void) Py_NormalizeSepsW(wchar_t *); ++PyAPI_FUNC(void) Py_NormalizeSepsA(char *); ++ ++PyAPI_FUNC(void) Py_NormalizeSepsPathcchW(wchar_t *); ++ + + /* Py_PyAtExit is for the atexit module, Py_AtExit is for low-level + * exit functions. +diff --git a/Include/pyport.h b/Include/pyport.h +index 93250f4..b816c90 100644 +--- a/Include/pyport.h ++++ b/Include/pyport.h +@@ -53,6 +53,21 @@ + #endif + + ++#ifdef __MINGW32__ ++/* Translate GCC[mingw*] platform specific defines to those ++ * used in python code. ++ */ ++#if !defined(MS_WIN64) && defined(_WIN64) ++# define MS_WIN64 ++#endif ++#if !defined(MS_WIN32) && defined(_WIN32) ++# define MS_WIN32 ++#endif ++#if !defined(MS_WINDOWS) && defined(MS_WIN32) ++# define MS_WINDOWS ++#endif ++#endif /* __MINGW32__*/ ++ + /************************************************************************** + Symbols and macros to supply platform-independent interfaces to basic + C language & library operations whose spellings vary across platforms. +@@ -509,12 +524,12 @@ extern char * _getpty(int *, int, mode_t, int); + */ + + /* +- All windows ports, except cygwin, are handled in PC/pyconfig.h. ++ Only MSVC windows ports is handled in PC/pyconfig.h. + +- Cygwin is the only other autoconf platform requiring special ++ Cygwin and Mingw is the only other autoconf platform requiring special + linkage handling and it uses __declspec(). + */ +-#if defined(__CYGWIN__) ++#if defined(__CYGWIN__) || defined(__MINGW32__) + # define HAVE_DECLSPEC_DLL + #endif + +@@ -527,21 +542,23 @@ extern char * _getpty(int *, int, mode_t, int); + # define PyAPI_FUNC(RTYPE) Py_EXPORTED_SYMBOL RTYPE + # define PyAPI_DATA(RTYPE) extern Py_EXPORTED_SYMBOL RTYPE + /* module init functions inside the core need no external linkage */ +- /* except for Cygwin to handle embedding */ +-# if defined(__CYGWIN__) ++ /* except for Cygwin/Mingw to handle embedding */ ++# if defined(__CYGWIN__) || defined(__MINGW32__) + # define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject* +-# else /* __CYGWIN__ */ ++# else /* __CYGWIN__ || __MINGW32__*/ + # define PyMODINIT_FUNC PyObject* +-# endif /* __CYGWIN__ */ ++# endif /* __CYGWIN__ || __MINGW32__*/ + # else /* Py_BUILD_CORE */ + /* Building an extension module, or an embedded situation */ + /* public Python functions and data are imported */ + /* Under Cygwin, auto-import functions to prevent compilation */ + /* failures similar to those described at the bottom of 4.1: */ + /* http://docs.python.org/extending/windows.html#a-cookbook-approach */ +-# if !defined(__CYGWIN__) ++# if defined(__CYGWIN__) || defined(__MINGW32__) ++# define PyAPI_FUNC(RTYPE) RTYPE ++# else + # define PyAPI_FUNC(RTYPE) Py_IMPORTED_SYMBOL RTYPE +-# endif /* !__CYGWIN__ */ ++# endif /* __CYGWIN__ || __MINGW32__*/ + # define PyAPI_DATA(RTYPE) extern Py_IMPORTED_SYMBOL RTYPE + /* module init functions outside the core must be exported */ + # if defined(__cplusplus) +@@ -641,6 +658,12 @@ extern char * _getpty(int *, int, mode_t, int); + + #define Py_VA_COPY va_copy + ++#if defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__>= 4) || __GNUC__ > 4) ++# define Py_PRINTF(X,Y) Py_GCC_ATTRIBUTE((format(gnu_printf,X,Y))) ++#else ++# define Py_PRINTF(X,Y) Py_GCC_ATTRIBUTE((format(printf,X,Y))) ++#endif ++ + /* + * Convenient macros to deal with endianness of the platform. WORDS_BIGENDIAN is + * detected by configure and defined in pyconfig.h. The code in pyconfig.h +diff --git a/Include/pythread.h b/Include/pythread.h +index a483290..9bf8bd6 100644 +--- a/Include/pythread.h ++++ b/Include/pythread.h +@@ -7,6 +7,12 @@ typedef void *PyThread_type_lock; + extern "C" { + #endif + ++#ifdef __MINGW32__ ++# if !defined(HAVE_PTHREAD_H) || defined(NT_THREADS) ++# undef _POSIX_THREADS ++# endif ++#endif ++ + /* Return status codes for Python lock acquisition. Chosen for maximum + * backwards compatibility, ie failure -> 0, success -> 1. */ + typedef enum PyLockStatus { +diff --git a/Include/sysmodule.h b/Include/sysmodule.h +index b508711..d6767dc 100644 +--- a/Include/sysmodule.h ++++ b/Include/sysmodule.h +@@ -15,9 +15,9 @@ Py_DEPRECATED(3.11) PyAPI_FUNC(void) PySys_SetArgvEx(int, wchar_t **, int); + Py_DEPRECATED(3.11) PyAPI_FUNC(void) PySys_SetPath(const wchar_t *); + + PyAPI_FUNC(void) PySys_WriteStdout(const char *format, ...) +- Py_GCC_ATTRIBUTE((format(printf, 1, 2))); ++ Py_PRINTF(1, 2); + PyAPI_FUNC(void) PySys_WriteStderr(const char *format, ...) +- Py_GCC_ATTRIBUTE((format(printf, 1, 2))); ++ Py_PRINTF(1, 2); + PyAPI_FUNC(void) PySys_FormatStdout(const char *format, ...); + PyAPI_FUNC(void) PySys_FormatStderr(const char *format, ...); + +diff --git a/Lib/compileall.py b/Lib/compileall.py +index a388931..069ea2b 100644 +--- a/Lib/compileall.py ++++ b/Lib/compileall.py +@@ -38,6 +38,8 @@ def _walk_dir(dir, maxlevels, quiet=0): + if name == '__pycache__': + continue + fullname = os.path.join(dir, name) ++ if sys.platform == "win32" and sys.version.find("GCC") >= 0: ++ fullname = fullname.replace('\\','/') + if not os.path.isdir(fullname): + yield fullname + elif (maxlevels > 0 and name != os.curdir and name != os.pardir and +diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py +index 26135ad..76d583b 100644 +--- a/Lib/ctypes/__init__.py ++++ b/Lib/ctypes/__init__.py +@@ -458,7 +458,9 @@ def LoadLibrary(self, name): + cdll = LibraryLoader(CDLL) + pydll = LibraryLoader(PyDLL) + +-if _os.name == "nt": ++if _os.name == "nt" and _sys.version.find('GCC') >= 0: ++ pythonapi = PyDLL("libpython%d.%d%s.dll" % (_sys.version_info[:2] + (_sys.abiflags,)), None) ++elif _os.name == "nt": + pythonapi = PyDLL("python dll", None, _sys.dllhandle) + elif _sys.platform == "cygwin": + pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2]) +diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py +index 0c2510e..48ddb3b 100644 +--- a/Lib/ctypes/util.py ++++ b/Lib/ctypes/util.py +@@ -31,6 +31,12 @@ def _get_build_version(): + # else we don't know what version of the compiler this is + return None + ++ def find_msvcrt_mingw(): ++ is_ucrt = 'clang' in sys.version.lower() or 'ucrt' in sys.version.lower() ++ if is_ucrt: ++ return None ++ return 'msvcrt.dll' ++ + def find_msvcrt(): + """Return the name of the VC runtime dll""" + version = _get_build_version() +@@ -54,6 +60,9 @@ def find_msvcrt(): + + def find_library(name): + if name in ('c', 'm'): ++ import sysconfig ++ if sysconfig.get_platform().startswith('mingw'): ++ return find_msvcrt_mingw() + return find_msvcrt() + # See MSDN for the REAL search order. + for directory in os.environ['PATH'].split(os.pathsep): +diff --git a/Lib/distutils/ccompiler.py b/Lib/distutils/ccompiler.py +index 4c47f2e..ab61a86 100644 +--- a/Lib/distutils/ccompiler.py ++++ b/Lib/distutils/ccompiler.py +@@ -9,7 +9,7 @@ + from distutils.file_util import move_file + from distutils.dir_util import mkpath + from distutils.dep_util import newer_group +-from distutils.util import split_quoted, execute ++from distutils.util import split_quoted, execute, get_platform + from distutils import log + + class CCompiler: +@@ -948,6 +948,8 @@ def get_default_compiler(osname=None, platform=None): + osname = os.name + if platform is None: + platform = sys.platform ++ if get_platform().startswith('mingw'): ++ return 'mingw32' + for pattern, compiler in _default_compilers: + if re.match(pattern, platform) is not None or \ + re.match(pattern, osname) is not None: +diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py +index f287b34..e87e3ad 100644 +--- a/Lib/distutils/command/build_ext.py ++++ b/Lib/distutils/command/build_ext.py +@@ -186,7 +186,7 @@ def finalize_options(self): + # for extensions under windows use different directories + # for Release and Debug builds. + # also Python's library directory must be appended to library_dirs +- if os.name == 'nt': ++ if os.name == 'nt' and not self.plat_name.startswith(('mingw')): + # the 'libs' directory is for binary installs - we assume that + # must be the *native* platform. But we don't really support + # cross-compiling via a binary install anyway, so we let it go. +@@ -218,15 +218,16 @@ def finalize_options(self): + + # For extensions under Cygwin, Python's library directory must be + # appended to library_dirs +- if sys.platform[:6] == 'cygwin': +- if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): ++ if sys.platform[:6] == 'cygwin' or self.plat_name.startswith(('mingw')): ++ if not sysconfig.python_build: + # building third party extensions ++ config_dir_name = os.path.basename(sysconfig.get_config_var('LIBPL')) + self.library_dirs.append(os.path.join(sys.prefix, "lib", + "python" + get_python_version(), +- "config")) ++ config_dir_name)) + else: + # building python standard extensions +- self.library_dirs.append('.') ++ self.library_dirs.append(sysconfig.project_base) + + # For building extensions with a shared Python library, + # Python's library directory must be appended to library_dirs +@@ -237,7 +238,7 @@ def finalize_options(self): + self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) + else: + # building python standard extensions +- self.library_dirs.append('.') ++ self.library_dirs.append(sysconfig.project_base) + + # The argument parsing will result in self.define being a string, but + # it has to be a list of 2-tuples. All the preprocessor symbols +@@ -712,6 +713,20 @@ def get_libraries(self, ext): + # pyconfig.h that MSVC groks. The other Windows compilers all seem + # to need it mentioned explicitly, though, so that's what we do. + # Append '_d' to the python import library on debug builds. ++ ++ # Use self.plat_name as it works even in case of ++ # cross-compilation (at least for mingw build). ++ if self.plat_name.startswith('mingw'): ++ from distutils import sysconfig ++ extra = [] ++ for lib in ( ++ sysconfig.get_config_var('BLDLIBRARY').split() ++ + sysconfig.get_config_var('SHLIBS').split() ++ ): ++ if lib.startswith('-l'): ++ extra.append(lib[2:]) ++ return ext.libraries + extra ++ + if sys.platform == "win32": + from distutils._msvccompiler import MSVCCompiler + if not isinstance(self.compiler, MSVCCompiler): +diff --git a/Lib/distutils/command/install.py b/Lib/distutils/command/install.py +index 01d5331..25eb3d8 100644 +--- a/Lib/distutils/command/install.py ++++ b/Lib/distutils/command/install.py +@@ -72,8 +72,8 @@ + INSTALL_SCHEMES['nt_user'] = { + 'purelib': '$usersite', + 'platlib': '$usersite', +- 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', +- 'scripts': '$userbase/Python$py_version_nodot/Scripts', ++ 'headers': '$userbase/include/python$py_version_short_plat$abiflags/$dist_name', ++ 'scripts': '$userbase/bin', + 'data' : '$userbase', + } + +@@ -81,7 +81,7 @@ + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': +- '$userbase/include/python$py_version_short$abiflags/$dist_name', ++ '$userbase/include/python$py_version_short_plat$abiflags/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + } +@@ -311,6 +311,7 @@ def finalize_options(self): + 'py_version': py_version, + 'py_version_short': '%d.%d' % sys.version_info[:2], + 'py_version_nodot': '%d%d' % sys.version_info[:2], ++ 'py_version_short_plat': f'{sys.version_info[0]}.{sys.version_info[1]}-{get_platform()}' if os.name == 'nt' and 'gcc' in sys.version.lower() else f'{sys.version_info[0]}.{sys.version_info[1]}', + 'sys_prefix': prefix, + 'prefix': prefix, + 'sys_exec_prefix': exec_prefix, +@@ -363,7 +364,8 @@ def finalize_options(self): + + # Convert directories from Unix /-separated syntax to the local + # convention. +- self.convert_paths('lib', 'purelib', 'platlib', ++ self.convert_paths('base', 'platbase', ++ 'lib', 'purelib', 'platlib', + 'scripts', 'data', 'headers') + if HAS_USER_SITE: + self.convert_paths('userbase', 'usersite') +diff --git a/Lib/distutils/cygwinccompiler.py b/Lib/distutils/cygwinccompiler.py +index 66c12dd..d8c8428 100644 +--- a/Lib/distutils/cygwinccompiler.py ++++ b/Lib/distutils/cygwinccompiler.py +@@ -44,12 +44,13 @@ + # (ld supports -shared) + # * mingw gcc 3.2/ld 2.13 works + # (ld supports -shared) ++# * llvm-mingw with Clang 11 works ++# (lld supports -shared) + + import os + import sys + import copy +-from subprocess import Popen, PIPE, check_output +-import re ++import shlex + + from distutils.unixccompiler import UnixCCompiler + from distutils.file_util import write_file +@@ -57,6 +58,7 @@ + CompileError, UnknownFileError) + from distutils.version import LooseVersion + from distutils.spawn import find_executable ++from subprocess import Popen, check_output + + def get_msvcr(): + """Include the appropriate MSVC runtime library if Python was built +@@ -91,6 +93,7 @@ class CygwinCCompiler(UnixCCompiler): + obj_extension = ".o" + static_lib_extension = ".a" + shared_lib_extension = ".dll" ++ dylib_lib_extension = ".dll.a" + static_lib_format = "lib%s%s" + shared_lib_format = "%s%s" + exe_extension = ".exe" +@@ -109,50 +112,28 @@ def __init__(self, verbose=0, dry_run=0, force=0): + "Compiling may fail because of undefined preprocessor macros." + % details) + +- self.gcc_version, self.ld_version, self.dllwrap_version = \ +- get_versions() +- self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % +- (self.gcc_version, +- self.ld_version, +- self.dllwrap_version) ) +- +- # ld_version >= "2.10.90" and < "2.13" should also be able to use +- # gcc -mdll instead of dllwrap +- # Older dllwraps had own version numbers, newer ones use the +- # same as the rest of binutils ( also ld ) +- # dllwrap 2.10.90 is buggy +- if self.ld_version >= "2.10.90": +- self.linker_dll = "gcc" +- else: +- self.linker_dll = "dllwrap" ++ self.cc = os.environ.get('CC', 'gcc') ++ self.cxx = os.environ.get('CXX', 'g++') + +- # ld_version >= "2.13" support -shared so use it instead of +- # -mdll -static +- if self.ld_version >= "2.13": +- shared_option = "-shared" +- else: +- shared_option = "-mdll -static" +- +- # Hard-code GCC because that's what this is all about. +- # XXX optimization, warnings etc. should be customizable. +- self.set_executables(compiler='gcc -mcygwin -O -Wall', +- compiler_so='gcc -mcygwin -mdll -O -Wall', +- compiler_cxx='g++ -mcygwin -O -Wall', +- linker_exe='gcc -mcygwin', ++ # Older numpy dependend on this existing to check for ancient ++ # gcc versions. This doesn't make much sense with clang etc so ++ # just hardcode to something recent. ++ # https://github.com/numpy/numpy/pull/20333 ++ self.gcc_version = LooseVersion("11.2.0") ++ ++ self.linker_dll = self.cc ++ shared_option = "-shared" ++ ++ self.set_executables(compiler='%s -mcygwin -O -Wall' % self.cc, ++ compiler_so='%s -mcygwin -mdll -O -Wall' % self.cc, ++ compiler_cxx='%s -mcygwin -O -Wall' % self.cxx, ++ linker_exe='%s -mcygwin' % self.cc, + linker_so=('%s -mcygwin %s' % + (self.linker_dll, shared_option))) + +- # cygwin and mingw32 need different sets of libraries +- if self.gcc_version == "2.91.57": +- # cygwin shouldn't need msvcrt, but without the dlls will crash +- # (gcc version 2.91.57) -- perhaps something about initialization +- self.dll_libraries=["msvcrt"] +- self.warn( +- "Consider upgrading to a newer version of gcc") +- else: +- # Include the appropriate MSVC runtime library if Python was built +- # with MSVC 7.0 or later. +- self.dll_libraries = get_msvcr() ++ # Include the appropriate MSVC runtime library if Python was built ++ # with MSVC 7.0 or later. ++ self.dll_libraries = get_msvcr() + + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + """Compiles the source by spawning GCC and windres if needed.""" +@@ -162,6 +143,28 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + self.spawn(["windres", "-i", src, "-o", obj]) + except DistutilsExecError as msg: + raise CompileError(msg) ++ elif ext == '.mc': ++ # Adapted from msvc9compiler: ++ # ++ # Compile .MC to .RC file to .RES file. ++ # * '-h dir' specifies the directory for the generated include file ++ # * '-r dir' specifies the target directory of the generated RC file and the binary message resource it includes ++ # ++ # For now (since there are no options to change this), ++ # we use the source-directory for the include file and ++ # the build directory for the RC file and message ++ # resources. This works at least for win32all. ++ h_dir = os.path.dirname(src) ++ rc_dir = os.path.dirname(obj) ++ try: ++ # first compile .MC to .RC and .H file ++ self.spawn(['windmc'] + ['-h', h_dir, '-r', rc_dir] + [src]) ++ base, _ = os.path.splitext(os.path.basename(src)) ++ rc_file = os.path.join(rc_dir, base + '.rc') ++ # then compile .RC to .RES file ++ self.spawn(['windres', '-i', rc_file, '-o', obj]) ++ except DistutilsExecError as msg: ++ raise CompileError(msg) + else: # for other files use the C-compiler + try: + self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + +@@ -214,28 +217,21 @@ def link(self, target_desc, objects, output_filename, output_dir=None, + + # next add options for def-file and to creating import libraries + +- # dllwrap uses different options than gcc/ld +- if self.linker_dll == "dllwrap": +- extra_preargs.extend(["--output-lib", lib_file]) +- # for dllwrap we have to use a special option +- extra_preargs.extend(["--def", def_file]) +- # we use gcc/ld here and can be sure ld is >= 2.9.10 +- else: +- # doesn't work: bfd_close build\...\libfoo.a: Invalid operation +- #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) +- # for gcc/ld the def-file is specified as any object files +- objects.append(def_file) ++ # doesn't work: bfd_close build\...\libfoo.a: Invalid operation ++ #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) ++ # for gcc/ld the def-file is specified as any object files ++ objects.append(def_file) + + #end: if ((export_symbols is not None) and + # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): + + # who wants symbols and a many times larger output file + # should explicitly switch the debug mode on +- # otherwise we let dllwrap/ld strip the output file ++ # otherwise we let ld strip the output file + # (On my machine: 10KiB < stripped_file < ??100KiB + # unstripped_file = stripped_file + XXX KiB + # ( XXX=254 for a typical python extension)) +- if not debug: ++ if not debug and not hasattr(sys, 'gettotalrefcount'): + extra_preargs.append("-s") + + UnixCCompiler.link(self, target_desc, objects, output_filename, +@@ -253,11 +249,16 @@ def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): + output_dir = '' + obj_names = [] + for src_name in source_filenames: +- # use normcase to make sure '.rc' is really '.rc' and not '.RC' +- base, ext = os.path.splitext(os.path.normcase(src_name)) +- if ext not in (self.src_extensions + ['.rc','.res']): ++ base, ext = os.path.splitext(src_name) ++ # use 'normcase' only for resource suffixes ++ ext_normcase = os.path.normcase(ext) ++ if ext_normcase in ['.rc', '.res', '.mc']: ++ ext = ext_normcase ++ if ext not in (self.src_extensions + ['.rc', '.res', '.mc']): + raise UnknownFileError("unknown file type '%s' (from '%s')" % \ + (ext, src_name)) ++ base = os.path.splitdrive(base)[1] # Chop off the drive ++ base = base[os.path.isabs(base):] # If abs, chop off leading / + if strip_dir: + base = os.path.basename (base) + if ext in ('.res', '.rc'): +@@ -279,31 +280,18 @@ def __init__(self, verbose=0, dry_run=0, force=0): + + CygwinCCompiler.__init__ (self, verbose, dry_run, force) + +- # ld_version >= "2.13" support -shared so use it instead of +- # -mdll -static +- if self.ld_version >= "2.13": +- shared_option = "-shared" +- else: +- shared_option = "-mdll -static" +- +- # A real mingw32 doesn't need to specify a different entry point, +- # but cygwin 2.91.57 in no-cygwin-mode needs it. +- if self.gcc_version <= "2.91.57": +- entry_point = '--entry _DllMain@12' +- else: +- entry_point = '' ++ shared_option = "-shared" + +- if is_cygwingcc(): ++ if is_cygwincc(self.cc): + raise CCompilerError( + 'Cygwin gcc cannot be used with --compiler=mingw32') + +- self.set_executables(compiler='gcc -O -Wall', +- compiler_so='gcc -mdll -O -Wall', +- compiler_cxx='g++ -O -Wall', +- linker_exe='gcc', +- linker_so='%s %s %s' +- % (self.linker_dll, shared_option, +- entry_point)) ++ self.set_executables(compiler='%s -O2 -Wall' % self.cc, ++ compiler_so='%s -mdll -O2 -Wall' % self.cc, ++ compiler_cxx='%s -O2 -Wall' % self.cxx, ++ linker_exe='%s' % self.cc, ++ linker_so='%s %s' ++ % (self.linker_dll, shared_option)) + # Maybe we should also append -mthreads, but then the finished + # dlls need another dll (mingwm10.dll see Mingw32 docs) + # (-mthreads: Support thread-safe exception handling on `Mingw32') +@@ -313,7 +301,7 @@ def __init__(self, verbose=0, dry_run=0, force=0): + + # Include the appropriate MSVC runtime library if Python was built + # with MSVC 7.0 or later. +- self.dll_libraries = get_msvcr() ++ self.dll_libraries = get_msvcr() or [] + + # Because these compilers aren't configured in Python's pyconfig.h file by + # default, we should at least warn the user if he is using an unmodified +@@ -351,6 +339,10 @@ def check_config_h(): + if "GCC" in sys.version: + return CONFIG_H_OK, "sys.version mentions 'GCC'" + ++ # Clang would also work ++ if "Clang" in sys.version: ++ return CONFIG_H_OK, "sys.version mentions 'Clang'" ++ + # let's see if __GNUC__ is mentioned in python.h + fn = sysconfig.get_config_h_filename() + try: +@@ -366,38 +358,8 @@ def check_config_h(): + return (CONFIG_H_UNCERTAIN, + "couldn't read '%s': %s" % (fn, exc.strerror)) + +-RE_VERSION = re.compile(br'(\d+\.\d+(\.\d+)*)') +- +-def _find_exe_version(cmd): +- """Find the version of an executable by running `cmd` in the shell. +- +- If the command is not found, or the output does not match +- `RE_VERSION`, returns None. +- """ +- executable = cmd.split()[0] +- if find_executable(executable) is None: +- return None +- out = Popen(cmd, shell=True, stdout=PIPE).stdout +- try: +- out_string = out.read() +- finally: +- out.close() +- result = RE_VERSION.search(out_string) +- if result is None: +- return None +- # LooseVersion works with strings +- # so we need to decode our bytes +- return LooseVersion(result.group(1).decode()) +- +-def get_versions(): +- """ Try to find out the versions of gcc, ld and dllwrap. +- +- If not possible it returns None for it. +- """ +- commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] +- return tuple([_find_exe_version(cmd) for cmd in commands]) + +-def is_cygwingcc(): +- '''Try to determine if the gcc that would be used is from cygwin.''' +- out_string = check_output(['gcc', '-dumpmachine']) ++def is_cygwincc(cc): ++ '''Try to determine if the compiler that would be used is from cygwin.''' ++ out_string = check_output(shlex.split(cc) + ['-dumpmachine']) + return out_string.strip().endswith(b'cygwin') +diff --git a/Lib/distutils/msvc9compiler.py b/Lib/distutils/msvc9compiler.py +index a7976fb..c341679 100644 +--- a/Lib/distutils/msvc9compiler.py ++++ b/Lib/distutils/msvc9compiler.py +@@ -291,8 +291,6 @@ def query_vcvarsall(version, arch="x86"): + + # More globals + VERSION = get_build_version() +-if VERSION < 8.0: +- raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION) + # MACROS = MacroExpander(VERSION) + + class MSVCCompiler(CCompiler) : +@@ -327,6 +325,8 @@ class MSVCCompiler(CCompiler) : + + def __init__(self, verbose=0, dry_run=0, force=0): + CCompiler.__init__ (self, verbose, dry_run, force) ++ if VERSION < 8.0: ++ raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION) + self.__version = VERSION + self.__root = r"Software\Microsoft\VisualStudio" + # self.__macros = MACROS +diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py +index 03b8558..024c15c 100644 +--- a/Lib/distutils/sysconfig.py ++++ b/Lib/distutils/sysconfig.py +@@ -64,8 +64,23 @@ def parse_config_h(fp, g=None): + + _python_build = partial(is_python_build, check_home=True) + _init_posix = partial(sysconfig_init_posix, _config_vars) +-_init_nt = partial(_init_non_posix, _config_vars) + ++def _posix_build(): ++ # GCC[mingw*] use posix build system ++ # Check for cross builds explicitly ++ host_platform = os.environ.get("_PYTHON_HOST_PLATFORM") ++ if host_platform: ++ if host_platform.startswith('mingw'): ++ return True ++ return os.name == 'posix' or \ ++ (os.name == "nt" and 'GCC' in sys.version) ++posix_build = _posix_build() ++ ++ ++def _init_nt(): ++ if posix_build: ++ return _init_posix(_config_vars) ++ return _init_non_posix(_config_vars) + + # Similar function is also implemented in sysconfig as _parse_makefile + # but without the parsing capabilities of distutils.text_file.TextFile. +@@ -196,7 +211,23 @@ def customize_compiler(compiler): + Mainly needed on Unix, so we can plug in the information that + varies across Unices and is stored in Python's Makefile. + """ +- if compiler.compiler_type == "unix": ++ global _config_vars ++ if compiler.compiler_type in ["cygwin", "mingw32"]: ++ # Note that cygwin use posix build and 'unix' compiler. ++ # If build is not based on posix then we must predefine ++ # some environment variables corresponding to posix ++ # build rules and defaults. ++ if not 'GCC' in sys.version: ++ _config_vars['CC'] = "gcc" ++ _config_vars['CXX'] = "g++" ++ _config_vars['OPT'] = "-fwrapv -O3 -Wall -Wstrict-prototypes" ++ _config_vars['CFLAGS'] = "" ++ _config_vars['CCSHARED'] = "" ++ _config_vars['LDSHARED'] = "gcc -shared -Wl,--enable-auto-image-base" ++ _config_vars['AR'] = "ar" ++ _config_vars['ARFLAGS'] = "rc" ++ ++ if compiler.compiler_type in ["unix", "cygwin", "mingw32"]: + if sys.platform == "darwin": + # Perform first-time customization of compiler-related + # config vars on OS X now that we know we need a compiler. +@@ -274,7 +305,7 @@ def get_python_inc(plat_specific=0, prefix=None): + """ + if prefix is None: + prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX +- if os.name == "posix": ++ if posix_build: + if python_build: + # Assume the executable is in the build directory. The + # pyconfig.h file should be in the same directory. Since +@@ -321,7 +352,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): + else: + prefix = plat_specific and EXEC_PREFIX or PREFIX + +- if os.name == "posix": ++ if posix_build: + if plat_specific or standard_lib: + # Platform-specific modules (any module from a non-pure-Python + # module distribution) or standard Python library modules. +diff --git a/Lib/distutils/tests/test_cygwinccompiler.py b/Lib/distutils/tests/test_cygwinccompiler.py +index 0912ffd..a93c174 100644 +--- a/Lib/distutils/tests/test_cygwinccompiler.py ++++ b/Lib/distutils/tests/test_cygwinccompiler.py +@@ -8,7 +8,7 @@ + from distutils import cygwinccompiler + from distutils.cygwinccompiler import (check_config_h, + CONFIG_H_OK, CONFIG_H_NOTOK, +- CONFIG_H_UNCERTAIN, get_versions, ++ CONFIG_H_UNCERTAIN, + get_msvcr) + from distutils.tests import support + +@@ -81,40 +81,6 @@ def test_check_config_h(self): + self.write_file(self.python_h, 'xxx __GNUC__ xxx') + self.assertEqual(check_config_h()[0], CONFIG_H_OK) + +- def test_get_versions(self): +- +- # get_versions calls distutils.spawn.find_executable on +- # 'gcc', 'ld' and 'dllwrap' +- self.assertEqual(get_versions(), (None, None, None)) +- +- # Let's fake we have 'gcc' and it returns '3.4.5' +- self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF' +- res = get_versions() +- self.assertEqual(str(res[0]), '3.4.5') +- +- # and let's see what happens when the version +- # doesn't match the regular expression +- # (\d+\.\d+(\.\d+)*) +- self._exes['gcc'] = b'very strange output' +- res = get_versions() +- self.assertEqual(res[0], None) +- +- # same thing for ld +- self._exes['ld'] = b'GNU ld version 2.17.50 20060824' +- res = get_versions() +- self.assertEqual(str(res[1]), '2.17.50') +- self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' +- res = get_versions() +- self.assertEqual(res[1], None) +- +- # and dllwrap +- self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF' +- res = get_versions() +- self.assertEqual(str(res[2]), '2.17.50') +- self._exes['dllwrap'] = b'Cheese Wrap' +- res = get_versions() +- self.assertEqual(res[2], None) +- + def test_get_msvcr(self): + + # none +diff --git a/Lib/distutils/unixccompiler.py b/Lib/distutils/unixccompiler.py +index d00c489..76a7d82 100644 +--- a/Lib/distutils/unixccompiler.py ++++ b/Lib/distutils/unixccompiler.py +@@ -249,9 +249,13 @@ def runtime_library_dir_option(self, dir): + # -Wl whenever gcc was used in the past it is probably + # safest to keep doing so. + if sysconfig.get_config_var("GNULD") == "yes": +- # GNU ld needs an extra option to get a RUNPATH ++ # GNU ELF ld needs an extra option to get a RUNPATH + # instead of just an RPATH. +- return "-Wl,--enable-new-dtags,-R" + dir ++ if sys.platform in ["win32", "cygwin"] or \ ++ "mingw" in compiler: ++ return [] ++ else: ++ return "-Wl,--enable-new-dtags,-R" + dir + else: + return "-Wl,-R" + dir + else: +diff --git a/Lib/distutils/util.py b/Lib/distutils/util.py +index 2ce5c5b..81626b3 100644 +--- a/Lib/distutils/util.py ++++ b/Lib/distutils/util.py +@@ -37,6 +37,22 @@ def get_host_platform(): + + """ + if os.name == 'nt': ++ if 'gcc' in sys.version.lower(): ++ if 'ucrt' in sys.version.lower(): ++ if 'amd64' in sys.version.lower(): ++ return 'mingw_x86_64_ucrt' ++ return 'mingw_i686_ucrt' ++ if 'clang' in sys.version.lower(): ++ if 'amd64' in sys.version.lower(): ++ return 'mingw_x86_64_clang' ++ if 'arm64' in sys.version.lower(): ++ return 'mingw_aarch64' ++ if 'arm' in sys.version.lower(): ++ return 'mingw_armv7' ++ return 'mingw_i686_clang' ++ if 'amd64' in sys.version.lower(): ++ return 'mingw_x86_64' ++ return 'mingw_i686' + if 'amd64' in sys.version.lower(): + return 'win-amd64' + if '(arm)' in sys.version.lower(): +@@ -130,6 +146,13 @@ def convert_path (pathname): + paths.remove('.') + if not paths: + return os.curdir ++ # On Windows, if paths is ['C:','folder','subfolder'] then ++ # os.path.join(*paths) will return 'C:folder\subfolder' which ++ # is thus relative to the CWD on that drive. So we work around ++ # this by adding a \ to path[0] ++ if (len(paths) > 0 and paths[0].endswith(':') and ++ sys.platform == "win32" and sys.version.find("GCC") >= 0): ++ paths[0] += '\\' + return os.path.join(*paths) + + # convert_path () +@@ -140,6 +163,10 @@ def change_root (new_root, pathname): + relative, this is equivalent to "os.path.join(new_root,pathname)". + Otherwise, it requires making 'pathname' relative and then joining the + two, which is tricky on DOS/Windows and Mac OS. ++ ++ If on Windows or OS/2 and both new_root and pathname are on different ++ drives, raises DistutilsChangeRootError as this is nonsensical, ++ otherwise use drive which can be in either of new_root or pathname. + """ + if os.name == 'posix': + if not os.path.isabs(pathname): +@@ -149,9 +176,20 @@ def change_root (new_root, pathname): + + elif os.name == 'nt': + (drive, path) = os.path.splitdrive(pathname) +- if path[0] == '\\': ++ if path[0] == os.sep: + path = path[1:] +- return os.path.join(new_root, path) ++ (drive_r, path_r) = os.path.splitdrive(new_root) ++ if path_r and path_r[0] == os.sep: ++ path_r = path_r[1:] ++ drive_used = '' ++ if len(drive) == 2 and len(drive_r) == 2 and drive != drive_r: ++ raise DistutilsChangeRootError("root and pathname not on same drive (%s, %s)" ++ % (drive_r,drive)) ++ elif len(drive_r) == 2: ++ drive_used = drive_r+os.sep ++ elif len(drive) == 2: ++ drive_used = drive+os.sep ++ return os.path.join(drive_used+path_r, path) + + else: + raise DistutilsPlatformError("nothing known about platform '%s'" % os.name) +diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py +index f603a89..7693d05 100644 +--- a/Lib/importlib/_bootstrap_external.py ++++ b/Lib/importlib/_bootstrap_external.py +@@ -42,6 +42,10 @@ + path_separators = ['\\', '/'] + else: + path_separators = ['/'] ++ ++if _os.environ.get('MSYSTEM', ''): ++ path_separators = path_separators[::-1] ++ + # Assumption made in _path_join() + assert all(len(sep) == 1 for sep in path_separators) + path_sep = path_separators[0] +diff --git a/Lib/ntpath.py b/Lib/ntpath.py +index 0246419..7d5c3a5 100644 +--- a/Lib/ntpath.py ++++ b/Lib/ntpath.py +@@ -11,9 +11,7 @@ + curdir = '.' + pardir = '..' + extsep = '.' +-sep = '\\' + pathsep = ';' +-altsep = '/' + defpath = '.;C:\\bin' + devnull = 'nul' + +@@ -23,6 +21,14 @@ + import genericpath + from genericpath import * + ++if sys.platform == "win32" and os.environ.get("MSYSTEM", ""): ++ sep = '/' ++ altsep = '\\' ++else: ++ sep = '\\' ++ altsep = '/' ++bsep = str.encode(sep) ++baltsep = str.encode(altsep) + + __all__ = ["normcase","isabs","join","splitdrive","split","splitext", + "basename","dirname","commonprefix","getsize","getmtime", +@@ -34,9 +40,33 @@ + + def _get_bothseps(path): + if isinstance(path, bytes): +- return b'\\/' ++ return bsep+baltsep ++ else: ++ return sep+altsep ++ ++def _get_sep(path): ++ if isinstance(path, bytes): ++ return bsep ++ else: ++ return sep ++ ++def _get_altsep(path): ++ if isinstance(path, bytes): ++ return baltsep ++ else: ++ return altsep ++ ++def _get_colon(path): ++ if isinstance(path, bytes): ++ return b':' ++ else: ++ return ':' ++ ++def _get_unc_prefix(path): ++ if isinstance(path, bytes): ++ return b'\\\\?\\UNC\\' + else: +- return '\\/' ++ return '\\\\?\\UNC\\' + + # Normalize the case of a pathname and map slashes to backslashes. + # Other normalizations (such as optimizing '../' away) are not done +@@ -58,14 +88,14 @@ def normcase(s): + return s + if isinstance(s, bytes): + encoding = sys.getfilesystemencoding() +- s = s.decode(encoding, 'surrogateescape').replace('/', '\\') ++ s = s.decode(encoding, 'surrogateescape').replace(altsep, sep) + s = _LCMapStringEx(_LOCALE_NAME_INVARIANT, + _LCMAP_LOWERCASE, s) + return s.encode(encoding, 'surrogateescape') + else: + return _LCMapStringEx(_LOCALE_NAME_INVARIANT, + _LCMAP_LOWERCASE, +- s.replace('/', '\\')) ++ s.replace(altsep, sep)) + except ImportError: + def normcase(s): + """Normalize case of pathname. +@@ -74,8 +104,8 @@ def normcase(s): + """ + s = os.fspath(s) + if isinstance(s, bytes): +- return os.fsencode(os.fsdecode(s).replace('/', '\\').lower()) +- return s.replace('/', '\\').lower() ++ return os.fsencode(os.fsdecode(s).replace(altsep, sep).lower()) ++ return s.replace(altsep, sep).lower() + + + # Return whether a path is absolute. +@@ -87,14 +117,9 @@ def normcase(s): + def isabs(s): + """Test whether a path is absolute""" + s = os.fspath(s) +- if isinstance(s, bytes): +- sep = b'\\' +- altsep = b'/' +- colon_sep = b':\\' +- else: +- sep = '\\' +- altsep = '/' +- colon_sep = ':\\' ++ sep = _get_sep(s) ++ altsep = _get_altsep(s) ++ colon_sep = _get_colon(s) + sep + s = s[:3].replace(altsep, sep) + # Absolute: UNC, device, and paths with a drive and root. + # LEGACY BUG: isabs("/x") should be false since the path has no drive. +@@ -106,14 +131,9 @@ def isabs(s): + # Join two (or more) paths. + def join(path, *paths): + path = os.fspath(path) +- if isinstance(path, bytes): +- sep = b'\\' +- seps = b'\\/' +- colon = b':' +- else: +- sep = '\\' +- seps = '\\/' +- colon = ':' ++ sep = _get_sep(path) ++ seps = _get_bothseps(path) ++ colon = _get_colon(path) + try: + if not paths: + path[:0] + sep #23780: Ensure compatible data type even if p is null. +@@ -172,16 +192,10 @@ def splitdrive(p): + """ + p = os.fspath(p) + if len(p) >= 2: +- if isinstance(p, bytes): +- sep = b'\\' +- altsep = b'/' +- colon = b':' +- unc_prefix = b'\\\\?\\UNC\\' +- else: +- sep = '\\' +- altsep = '/' +- colon = ':' +- unc_prefix = '\\\\?\\UNC\\' ++ sep = _get_sep(p) ++ altsep = _get_altsep(p) ++ colon = _get_colon(p) ++ unc_prefix = _get_unc_prefix(p) + normp = p.replace(altsep, sep) + if normp[0:2] == sep * 2: + # UNC drives, e.g. \\server\share or \\?\UNC\server\share +@@ -231,9 +245,9 @@ def split(p): + def splitext(p): + p = os.fspath(p) + if isinstance(p, bytes): +- return genericpath._splitext(p, b'\\', b'/', b'.') ++ return genericpath._splitext(p, bsep, baltsep, b'.') + else: +- return genericpath._splitext(p, '\\', '/', '.') ++ return genericpath._splitext(p, sep, altsep, '.') + splitext.__doc__ = genericpath._splitext.__doc__ + + +@@ -334,7 +348,7 @@ def expanduser(path): + if 'USERPROFILE' in os.environ: + userhome = os.environ['USERPROFILE'] + elif not 'HOMEPATH' in os.environ: +- return path ++ return os.path.normpath(path) + else: + try: + drive = os.environ['HOMEDRIVE'] +@@ -361,7 +375,7 @@ def expanduser(path): + if isinstance(path, bytes): + userhome = os.fsencode(userhome) + +- return userhome + path[i:] ++ return os.path.normpath(userhome) + path[i:] + + + # Expand paths containing shell variable substitutions. +@@ -496,14 +510,12 @@ def expandvars(path): + def normpath(path): + """Normalize path, eliminating double slashes, etc.""" + path = os.fspath(path) ++ sep = _get_sep(path) ++ altsep = _get_altsep(path) + if isinstance(path, bytes): +- sep = b'\\' +- altsep = b'/' + curdir = b'.' + pardir = b'..' + else: +- sep = '\\' +- altsep = '/' + curdir = '.' + pardir = '..' + path = path.replace(altsep, sep) +@@ -570,7 +582,7 @@ def _abspath_fallback(path): + def abspath(path): + """Return the absolute version of a path.""" + try: +- return _getfullpathname(normpath(path)) ++ return normpath(_getfullpathname(normpath(path))) + except (OSError, ValueError): + return _abspath_fallback(path) + +@@ -731,6 +743,7 @@ def realpath(path, *, strict=False): + # strip the prefix anyway. + if ex.winerror == initial_winerror: + path = spath ++ path = normpath(path) + return path + + +@@ -741,12 +754,11 @@ def realpath(path, *, strict=False): + def relpath(path, start=None): + """Return a relative version of a path""" + path = os.fspath(path) ++ sep = _get_sep(path) + if isinstance(path, bytes): +- sep = b'\\' + curdir = b'.' + pardir = b'..' + else: +- sep = '\\' + curdir = '.' + pardir = '..' + +@@ -801,13 +813,11 @@ def commonpath(paths): + raise ValueError('commonpath() arg is an empty sequence') + + paths = tuple(map(os.fspath, paths)) ++ sep = _get_sep(paths[0]) ++ altsep = _get_altsep(paths[0]) + if isinstance(paths[0], bytes): +- sep = b'\\' +- altsep = b'/' + curdir = b'.' + else: +- sep = '\\' +- altsep = '/' + curdir = '.' + + try: +diff --git a/Lib/pathlib.py b/Lib/pathlib.py +index ecb1e8a..eec8a9e 100644 +--- a/Lib/pathlib.py ++++ b/Lib/pathlib.py +@@ -115,6 +115,8 @@ class _WindowsFlavour(_Flavour): + + sep = '\\' + altsep = '/' ++ if os.environ.get('MSYSTEM', ''): ++ sep, altsep = altsep, sep + has_drv = True + pathmod = ntpath + +diff --git a/Lib/site.py b/Lib/site.py +index 69670d9..41b9cf1 100644 +--- a/Lib/site.py ++++ b/Lib/site.py +@@ -88,6 +88,12 @@ + USER_BASE = None + + ++# Same as defined in Lib/sysconfig.py ++# redeclared since sysconfig is large for site. ++# GCC[mingw*] use posix build system ++_POSIX_BUILD = os.name == 'posix' or \ ++ (os.name == "nt" and 'GCC' in sys.version) ++ + def _trace(message): + if sys.flags.verbose: + print(message, file=sys.stderr) +@@ -273,7 +279,7 @@ def _getuserbase(): + def joinuser(*args): + return os.path.expanduser(os.path.join(*args)) + +- if os.name == "nt": ++ if os.name == "nt" and not _POSIX_BUILD: + base = os.environ.get("APPDATA") or "~" + return joinuser(base, "Python") + +@@ -283,14 +289,36 @@ def joinuser(*args): + + return joinuser("~", ".local") + ++# Copy of sysconfig.get_platform() but only for MinGW ++def _get_platform(): ++ if os.name == 'nt': ++ if 'gcc' in sys.version.lower(): ++ if 'ucrt' in sys.version.lower(): ++ if 'amd64' in sys.version.lower(): ++ return 'mingw_x86_64_ucrt' ++ return 'mingw_i686_ucrt' ++ if 'clang' in sys.version.lower(): ++ if 'amd64' in sys.version.lower(): ++ return 'mingw_x86_64_clang' ++ if 'arm64' in sys.version.lower(): ++ return 'mingw_aarch64' ++ if 'arm' in sys.version.lower(): ++ return 'mingw_armv7' ++ return 'mingw_i686_clang' ++ if 'amd64' in sys.version.lower(): ++ return 'mingw_x86_64' ++ return 'mingw_i686' ++ return sys.platform + + # Same to sysconfig.get_path('purelib', os.name+'_user') + def _get_path(userbase): + version = sys.version_info + + if os.name == 'nt': +- ver_nodot = sys.winver.replace('.', '') +- return f'{userbase}\\Python{ver_nodot}\\site-packages' ++ if not _POSIX_BUILD: ++ ver_nodot = sys.winver.replace('.', '') ++ return f'{userbase}\\Python{ver_nodot}\\site-packages' ++ return f'{userbase}/lib/python{version[0]}.{version[1]}-{_get_platform()}/site-packages' + + if sys.platform == 'darwin' and sys._framework: + return f'{userbase}/lib/python/site-packages' +@@ -361,7 +389,7 @@ def getsitepackages(prefixes=None): + continue + seen.add(prefix) + +- if os.sep == '/': ++ if _POSIX_BUILD: + libdirs = [sys.platlibdir] + if sys.platlibdir != "lib": + libdirs.append("lib") +@@ -392,7 +420,7 @@ def setquit(): + The repr of each object contains a hint at how it works. + + """ +- if os.sep == '\\': ++ if sys.platform == 'win32': + eof = 'Ctrl-Z plus Return' + else: + eof = 'Ctrl-D (i.e. EOF)' +diff --git a/Lib/ssl.py b/Lib/ssl.py +index ced87d4..ebb6b6f 100644 +--- a/Lib/ssl.py ++++ b/Lib/ssl.py +@@ -254,7 +254,7 @@ class _TLSMessageType: + CHANGE_CIPHER_SPEC = 0x0101 + + +-if sys.platform == "win32": ++if sys.platform == "win32" and sys.version.find("GCC") == -1: + from _ssl import enum_certificates, enum_crls + + from socket import socket, SOCK_STREAM, create_connection +@@ -591,7 +591,7 @@ def _load_windows_store_certs(self, storename, purpose): + def load_default_certs(self, purpose=Purpose.SERVER_AUTH): + if not isinstance(purpose, _ASN1Object): + raise TypeError(purpose) +- if sys.platform == "win32": ++ if sys.platform == "win32" and sys.version.find("GCC") == -1: + for storename in self._windows_cert_stores: + self._load_windows_store_certs(storename, purpose) + self.set_default_verify_paths() +diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py +index ebe3711..8035d2e 100644 +--- a/Lib/sysconfig.py ++++ b/Lib/sysconfig.py +@@ -2,6 +2,7 @@ + + import os + import sys ++import textwrap + from os.path import pardir, realpath + + __all__ = [ +@@ -47,13 +48,13 @@ + 'data': '{base}', + }, + 'nt': { +- 'stdlib': '{installed_base}/Lib', +- 'platstdlib': '{base}/Lib', +- 'purelib': '{base}/Lib/site-packages', +- 'platlib': '{base}/Lib/site-packages', +- 'include': '{installed_base}/Include', +- 'platinclude': '{installed_base}/Include', +- 'scripts': '{base}/Scripts', ++ 'stdlib': '{installed_base}/lib/python{py_version_short}', ++ 'platstdlib': '{base}/lib/python{py_version_short}', ++ 'purelib': '{base}/lib/python{py_version_short}/site-packages', ++ 'platlib': '{base}/lib/python{py_version_short}/site-packages', ++ 'include': '{installed_base}/include/python{py_version_short}', ++ 'platinclude': '{installed_base}/include/python{py_version_short}', ++ 'scripts': '{base}/bin', + 'data': '{base}', + }, + # Downstream distributors can overwrite the default install scheme. +@@ -97,13 +98,18 @@ + }, + } + ++# GCC[mingw*] use posix build system ++_POSIX_BUILD = os.name == 'posix' or \ ++ (os.name == "nt" and 'GCC' in sys.version) ++ + # For the OS-native venv scheme, we essentially provide an alias: +-if os.name == 'nt': ++if os.name == 'nt' and not _POSIX_BUILD: + _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['nt_venv'] + else: + _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['posix_venv'] + + ++ + # NOTE: site.py has copy of this function. + # Sync it when modify this function. + def _getuserbase(): +@@ -118,7 +124,7 @@ def _getuserbase(): + def joinuser(*args): + return os.path.expanduser(os.path.join(*args)) + +- if os.name == "nt": ++ if os.name == "nt" and not _POSIX_BUILD: + base = os.environ.get("APPDATA") or "~" + return joinuser(base, "Python") + +@@ -134,20 +140,20 @@ def joinuser(*args): + _INSTALL_SCHEMES |= { + # NOTE: When modifying "purelib" scheme, update site._get_path() too. + 'nt_user': { +- 'stdlib': '{userbase}/Python{py_version_nodot_plat}', +- 'platstdlib': '{userbase}/Python{py_version_nodot_plat}', +- 'purelib': '{userbase}/Python{py_version_nodot_plat}/site-packages', +- 'platlib': '{userbase}/Python{py_version_nodot_plat}/site-packages', +- 'include': '{userbase}/Python{py_version_nodot_plat}/Include', +- 'scripts': '{userbase}/Python{py_version_nodot_plat}/Scripts', ++ 'stdlib': '{userbase}/lib/python{py_version_short_plat}', ++ 'platstdlib': '{userbase}/lib/python{py_version_short_plat}', ++ 'purelib': '{userbase}/lib/python{py_version_short_plat}/site-packages', ++ 'platlib': '{userbase}/lib/python{py_version_short_plat}/site-packages', ++ 'include': '{userbase}/include/python{py_version_short_plat}', ++ 'scripts': '{userbase}/bin', + 'data': '{userbase}', + }, + 'posix_user': { +- 'stdlib': '{userbase}/{platlibdir}/python{py_version_short}', +- 'platstdlib': '{userbase}/{platlibdir}/python{py_version_short}', +- 'purelib': '{userbase}/lib/python{py_version_short}/site-packages', +- 'platlib': '{userbase}/lib/python{py_version_short}/site-packages', +- 'include': '{userbase}/include/python{py_version_short}', ++ 'stdlib': '{userbase}/{platlibdir}/python{py_version_short_plat}', ++ 'platstdlib': '{userbase}/{platlibdir}/python{py_version_short_plat}', ++ 'purelib': '{userbase}/lib/python{py_version_short_plat}/site-packages', ++ 'platlib': '{userbase}/lib/python{py_version_short_plat}/site-packages', ++ 'include': '{userbase}/include/python{py_version_short_plat}', + 'scripts': '{userbase}/bin', + 'data': '{userbase}', + }, +@@ -277,7 +283,7 @@ def _expand_vars(scheme, vars): + + + def _get_preferred_schemes(): +- if os.name == 'nt': ++ if os.name == 'nt' and not _POSIX_BUILD: + return { + 'prefix': 'nt', + 'home': 'posix_home', +@@ -435,6 +441,14 @@ def _parse_makefile(filename, vars=None, keep_unresolved=True): + if isinstance(v, str): + done[k] = v.strip() + ++ # any keys that have one with the same name suffixed with _b2h ++ # need to be replaced with the value of the _b2h key. ++ # This converts from MSYS*/Cygwin paths to Windows paths. ++ for k, v in dict(done).items(): ++ if isinstance(k, str): ++ if k.endswith("_b2h"): ++ done[k[:-4]]=v ++ + # save the results in the global dictionary + vars.update(done) + return vars +@@ -514,11 +528,30 @@ def _generate_posix_vars(): + os.makedirs(pybuilddir, exist_ok=True) + destfile = os.path.join(pybuilddir, name + '.py') + ++ replacement = """ ++ keys_to_replace = [ ++ 'BINDIR', 'BINLIBDEST', 'CONFINCLUDEDIR', ++ 'CONFINCLUDEPY', 'DESTDIRS', 'DESTLIB', 'DESTSHARED', ++ 'INCLDIRSTOMAKE', 'INCLUDEDIR', 'INCLUDEPY', ++ 'LIBDEST', 'LIBDIR', 'LIBPC', 'LIBPL', 'MACHDESTLIB', ++ 'MANDIR', 'SCRIPTDIR', 'datarootdir', 'exec_prefix', ++ 'TZPATH', ++ ] ++ ++ prefix = build_time_vars['BINDIR'][:-4] ++ ++ for key in keys_to_replace: ++ value = build_time_vars[key] ++ build_time_vars[key] = value.replace(prefix, sys.prefix) ++ """ ++ + with open(destfile, 'w', encoding='utf8') as f: ++ f.write('import sys\n') + f.write('# system configuration generated and used by' + ' the sysconfig module\n') + f.write('build_time_vars = ') + pprint.pprint(vars, stream=f) ++ f.write('\n%s' % textwrap.dedent(replacement)) + + # Create file used for sys.path fixup -- see Modules/getpath.c + with open('pybuilddir.txt', 'w', encoding='utf8') as f: +@@ -541,7 +574,7 @@ def _init_non_posix(vars): + vars['INCLUDEPY'] = get_path('include') + vars['EXT_SUFFIX'] = _imp.extension_suffixes()[0] + vars['EXE'] = '.exe' +- vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT ++ vars['VERSION'] = _PY_VERSION_SHORT + vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) + vars['TZPATH'] = '' + +@@ -587,7 +620,7 @@ def parse_config_h(fp, vars=None): + def get_config_h_filename(): + """Return the path of pyconfig.h.""" + if _PYTHON_BUILD: +- if os.name == "nt": ++ if os.name == "nt" and not _POSIX_BUILD: + inc_dir = os.path.join(_PROJECT_BASE, "PC") + else: + inc_dir = _PROJECT_BASE +@@ -662,11 +695,15 @@ def get_config_vars(*args): + _CONFIG_VARS['py_version_nodot_plat'] = sys.winver.replace('.', '') + except AttributeError: + _CONFIG_VARS['py_version_nodot_plat'] = '' ++ if os.name == 'nt' and _POSIX_BUILD: ++ _CONFIG_VARS['py_version_short_plat'] = f'{_PY_VERSION_SHORT}-{get_platform()}' ++ else: ++ _CONFIG_VARS['py_version_short_plat'] = _PY_VERSION_SHORT + +- if os.name == 'nt': ++ if os.name == 'nt' and not _POSIX_BUILD: + _init_non_posix(_CONFIG_VARS) + _CONFIG_VARS['VPATH'] = sys._vpath +- if os.name == 'posix': ++ if _POSIX_BUILD: + _init_posix(_CONFIG_VARS) + if _HAS_USER_BASE: + # Setting 'userbase' is done below the call to the +@@ -676,7 +713,7 @@ def get_config_vars(*args): + + # Always convert srcdir to an absolute path + srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE) +- if os.name == 'posix': ++ if _POSIX_BUILD: + if _PYTHON_BUILD: + # If srcdir is a relative path (typically '.' or '..') + # then it should be interpreted relative to the directory +@@ -714,7 +751,7 @@ def get_config_var(name): + """ + return get_config_vars().get(name) + +- ++# make sure to change site._get_platform() while changing this function + def get_platform(): + """Return a string that identifies the current platform. + +@@ -737,6 +774,22 @@ def get_platform(): + + """ + if os.name == 'nt': ++ if 'gcc' in sys.version.lower(): ++ if 'ucrt' in sys.version.lower(): ++ if 'amd64' in sys.version.lower(): ++ return 'mingw_x86_64_ucrt' ++ return 'mingw_i686_ucrt' ++ if 'clang' in sys.version.lower(): ++ if 'amd64' in sys.version.lower(): ++ return 'mingw_x86_64_clang' ++ if 'arm64' in sys.version.lower(): ++ return 'mingw_aarch64' ++ if 'arm' in sys.version.lower(): ++ return 'mingw_armv7' ++ return 'mingw_i686_clang' ++ if 'amd64' in sys.version.lower(): ++ return 'mingw_x86_64' ++ return 'mingw_i686' + if 'amd64' in sys.version.lower(): + return 'win-amd64' + if '(arm)' in sys.version.lower(): +diff --git a/Lib/test/__main__.py b/Lib/test/__main__.py +index 19a6b2b..7641b32 100644 +--- a/Lib/test/__main__.py ++++ b/Lib/test/__main__.py +@@ -1,2 +1,10 @@ ++import os ++import sys ++ + from test.libregrtest import main ++ ++if sys.platform == "win32": ++ # Enable DLL loading from PATH. ++ os.environ["PYTHONLEGACYWINDOWSDLLLOADING"] = "1" ++ + main() +diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py +index 53ba1ad..3b6f4ab 100644 +--- a/Lib/test/test_bytes.py ++++ b/Lib/test/test_bytes.py +@@ -1105,7 +1105,7 @@ def test_from_format(self): + + if os.name == 'nt': + # Windows (MSCRT) +- ptr_format = '0x%0{}X'.format(2 * sizeof_ptr) ++ ptr_format = '0x%0{}x'.format(2 * sizeof_ptr) + def ptr_formatter(ptr): + return (ptr_format % ptr) + else: +diff --git a/Lib/test/test_getpath.py b/Lib/test/test_getpath.py +index 8d5b426..82ad71d 100644 +--- a/Lib/test/test_getpath.py ++++ b/Lib/test/test_getpath.py +@@ -838,6 +838,7 @@ def test_symlink_buildpath_macos(self): + ENV_PYTHONHOME="", + ENV_PYTHONEXECUTABLE="", + ENV___PYVENV_LAUNCHER__="", ++ ENV_MSYSTEM="", + argv0="", + py_setpath="", + real_executable="", +@@ -877,6 +878,7 @@ def __init__(self, *a, argv0=None, config=None, **kw): + self.update(DEFAULT_NAMESPACE) + self["config"] = DEFAULT_CONFIG.copy() + self["os_name"] = "nt" ++ self["is_mingw"] = 0 + self["PLATLIBDIR"] = "DLLs" + self["PYWINVER"] = "9.8-XY" + self["VPATH"] = r"..\.." +@@ -912,6 +914,9 @@ def __missing__(self, key): + except AttributeError: + raise KeyError(key) from None + ++ def normpath(self, path): ++ return ntpath.normpath(path) ++ + def abspath(self, path): + if self.isabs(path): + return path +@@ -1053,6 +1058,7 @@ def __init__(self, *a, argv0=None, config=None, **kw): + self.update(DEFAULT_NAMESPACE) + self["config"] = DEFAULT_CONFIG.copy() + self["os_name"] = "posix" ++ self["is_mingw"] = 0 + self["PLATLIBDIR"] = "lib" + self["WITH_NEXT_FRAMEWORK"] = 0 + super().__init__(*a, **kw) +@@ -1089,6 +1095,9 @@ def __missing__(self, key): + except AttributeError: + raise KeyError(key) from None + ++ def normpath(self, path): ++ return path ++ + def abspath(self, path): + if self.isabs(path): + return path +diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py +index cdd1bea..cfd8a10 100644 +--- a/Lib/test/test_httpservers.py ++++ b/Lib/test/test_httpservers.py +@@ -442,10 +442,10 @@ def test_undecodable_filename(self): + def test_undecodable_parameter(self): + # sanity check using a valid parameter + response = self.request(self.base_url + '/?x=123').read() +- self.assertRegex(response, f'listing for {self.base_url}/\?x=123'.encode('latin1')) ++ self.assertRegex(response, rf'listing for {self.base_url}/\?x=123'.encode('latin1')) + # now the bogus encoding + response = self.request(self.base_url + '/?x=%bb').read() +- self.assertRegex(response, f'listing for {self.base_url}/\?x=\xef\xbf\xbd'.encode('latin1')) ++ self.assertRegex(response, rf'listing for {self.base_url}/\?x=\xef\xbf\xbd'.encode('latin1')) + + def test_get_dir_redirect_location_domain_injection_bug(self): + """Ensure //evil.co/..%2f../../X does not put //evil.co/ in Location. +diff --git a/Lib/test/test_importlib/test_windows.py b/Lib/test/test_importlib/test_windows.py +index b7dfe86..58a8f5d 100644 +--- a/Lib/test/test_importlib/test_windows.py ++++ b/Lib/test/test_importlib/test_windows.py +@@ -24,6 +24,23 @@ def get_platform(): + 'x64' : 'win-amd64', + 'arm' : 'win-arm32', + } ++ if os.name == 'nt': ++ if 'gcc' in sys.version.lower(): ++ if 'ucrt' in sys.version.lower(): ++ if 'amd64' in sys.version.lower(): ++ return 'mingw_x86_64_ucrt' ++ return 'mingw_i686_ucrt' ++ if 'clang' in sys.version.lower(): ++ if 'amd64' in sys.version.lower(): ++ return 'mingw_x86_64_clang' ++ if 'arm64' in sys.version.lower(): ++ return 'mingw_aarch64' ++ if 'arm' in sys.version.lower(): ++ return 'mingw_armv7' ++ return 'mingw_i686_clang' ++ if 'amd64' in sys.version.lower(): ++ return 'mingw_x86_64' ++ return 'mingw_i686' + if ('VSCMD_ARG_TGT_ARCH' in os.environ and + os.environ['VSCMD_ARG_TGT_ARCH'] in TARGET_TO_PLAT): + return TARGET_TO_PLAT[os.environ['VSCMD_ARG_TGT_ARCH']] +diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py +index d96371d..a75a664 100644 +--- a/Lib/test/test_sysconfig.py ++++ b/Lib/test/test_sysconfig.py +@@ -17,7 +17,7 @@ + from sysconfig import (get_paths, get_platform, get_config_vars, + get_path, get_path_names, _INSTALL_SCHEMES, + get_default_scheme, get_scheme_names, get_config_var, +- _expand_vars, _get_preferred_schemes, _main) ++ _expand_vars, _get_preferred_schemes, _main, _POSIX_BUILD) + import _osx_support + + +@@ -180,7 +180,7 @@ def test_nt_venv_scheme(self): + self.assertEqual(libpath, sysconfig.get_path('purelib', scheme='nt_venv')) + + def test_venv_scheme(self): +- if sys.platform == 'win32': ++ if not _POSIX_BUILD and sys.platform == 'win32': + self.assertEqual( + sysconfig.get_path('scripts', scheme='venv'), + sysconfig.get_path('scripts', scheme='nt_venv') +@@ -371,6 +371,10 @@ def test_user_similar(self): + if HAS_USER_BASE: + user_path = get_path(name, 'posix_user') + expected = os.path.normpath(global_path.replace(base, user, 1)) ++ if os.name == 'nt' and _POSIX_BUILD: ++ expected = expected.replace( ++ f'python{sysconfig.get_python_version()}', ++ f'python{sysconfig.get_python_version()}-{get_platform()}') + # bpo-44860: platlib of posix_user doesn't use sys.platlibdir, + # whereas posix_prefix does. + if name == 'platlib': +diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py +index 4403f2b..ab5be51 100644 +--- a/Lib/venv/__init__.py ++++ b/Lib/venv/__init__.py +@@ -302,7 +302,7 @@ def setup_python(self, context): + if not os.path.islink(path): + os.chmod(path, 0o755) + else: +- if self.symlinks: ++ if self.symlinks and not _POSIX_BUILD: + # For symlinking, we need a complete copy of the root directory + # If symlinks fail, you'll get unnecessary copies of files, but + # we assume that if you've opted into symlinks on Windows then +@@ -326,6 +326,12 @@ def setup_python(self, context): + if os.path.lexists(src): + copier(src, os.path.join(binpath, suffix)) + ++ if _POSIX_BUILD: ++ # copy from python/pythonw so the venvlauncher magic in symlink_or_copy triggers ++ copier(os.path.join(dirname, 'python.exe'), os.path.join(binpath, 'python3.exe')) ++ copier(os.path.join(dirname, 'python.exe'), os.path.join(binpath, 'python%d.%d.exe' % sys.version_info[:2])) ++ copier(os.path.join(dirname, 'pythonw.exe'), os.path.join(binpath, 'python3w.exe')) ++ + if sysconfig.is_python_build(): + # copy init.tcl + for root, dirs, files in os.walk(context.python_dir): +@@ -350,6 +356,7 @@ def _call_new_python(self, context, *py_args, **kwargs): + env['VIRTUAL_ENV'] = context.env_dir + env.pop('PYTHONHOME', None) + env.pop('PYTHONPATH', None) ++ env.pop("MSYSTEM", None) + kwargs['cwd'] = context.env_dir + kwargs['executable'] = context.env_exec_cmd + subprocess.check_output(args, **kwargs) +diff --git a/Makefile.pre.in b/Makefile.pre.in +index 3ea8653..2707d04 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -39,6 +39,7 @@ CXX= @CXX@ + MAINCC= @MAINCC@ + LINKCC= @LINKCC@ + AR= @AR@ ++WINDRES= @WINDRES@ + READELF= @READELF@ + SOABI= @SOABI@ + LDVERSION= @LDVERSION@ +@@ -123,6 +124,7 @@ PY_CORE_CFLAGS= $(PY_STDMODULE_CFLAGS) -DPy_BUILD_CORE + PY_CORE_LDFLAGS=$(PY_LDFLAGS) $(PY_LDFLAGS_NODIST) + # Strict or non-strict aliasing flags used to compile dtoa.c, see above + CFLAGS_ALIASING=@CFLAGS_ALIASING@ ++RCFLAGS=@RCFLAGS@ + + + # Machine-dependent subdirectories +@@ -141,6 +143,12 @@ exec_prefix= @exec_prefix@ + # Install prefix for data files + datarootdir= @datarootdir@ + ++# Locations needed for semi-native fixup of sysconfig. ++srcdir_b2h= @srcdir_b2h@ ++abs_srcdir_b2h= @abs_srcdir_b2h@ ++abs_builddir_b2h= @abs_builddir_b2h@ ++prefix_b2h= @prefix_b2h@ ++ + # Expanded directories + BINDIR= @bindir@ + LIBDIR= @libdir@ +@@ -158,10 +166,12 @@ BINLIBDEST= @BINLIBDEST@ + LIBDEST= $(SCRIPTDIR)/python$(VERSION) + INCLUDEPY= $(INCLUDEDIR)/python$(LDVERSION) + CONFINCLUDEPY= $(CONFINCLUDEDIR)/python$(LDVERSION) ++VENVLAUNCHERDIR= $(BINLIBDEST)/venv/scripts/nt + + # Symbols used for using shared libraries + SHLIB_SUFFIX= @SHLIB_SUFFIX@ + EXT_SUFFIX= @EXT_SUFFIX@ ++PYD_PLATFORM_TAG = @PYD_PLATFORM_TAG@ + LDSHARED= @LDSHARED@ $(PY_LDFLAGS) + BLDSHARED= @BLDSHARED@ $(PY_CORE_LDFLAGS) + LDCXXSHARED= @LDCXXSHARED@ +@@ -268,6 +278,8 @@ LIBRARY_DEPS= @LIBRARY_DEPS@ + LINK_PYTHON_DEPS=@LINK_PYTHON_DEPS@ + PY_ENABLE_SHARED= @PY_ENABLE_SHARED@ + STATIC_LIBPYTHON= @STATIC_LIBPYTHON@ ++ABI3DLLLIBRARY= libpython3.dll ++ABI3LDLIBRARY= libpython3.dll.a + + + LIBS= @LIBS@ +@@ -284,6 +296,7 @@ LIBOBJS= @LIBOBJS@ + + PYTHON= python$(EXE) + BUILDPYTHON= python$(BUILDEXE) ++BUILDPYTHONW= pythonw$(BUILDEXE) + + HOSTRUNNER= @HOSTRUNNER@ + +@@ -344,6 +357,7 @@ IO_OBJS= \ + ########################################################################## + + LIBFFI_INCLUDEDIR= @LIBFFI_INCLUDEDIR@ ++NCURSESW_INCLUDEDIR= @NCURSESW_INCLUDEDIR@ + + ########################################################################## + # Parser +@@ -390,7 +404,7 @@ PYTHON_OBJS= \ + Python/dynamic_annotations.o \ + Python/errors.o \ + Python/frame.o \ +- Python/frozenmain.o \ ++ @PYTHON_OBJS_FROZENMAIN@ \ + Python/future.o \ + Python/getargs.o \ + Python/getcompiler.o \ +@@ -402,6 +416,7 @@ PYTHON_OBJS= \ + Python/import.o \ + Python/importdl.o \ + Python/initconfig.o \ ++ Python/iscygpty.o \ + Python/marshal.o \ + Python/modsupport.o \ + Python/mysnprintf.o \ +@@ -584,8 +599,8 @@ LIBEXPAT_HEADERS= \ + + # Default target + all: @DEF_MAKE_ALL_RULE@ +-build_all: check-clean-src $(BUILDPYTHON) platform oldsharedmods sharedmods \ +- gdbhooks Programs/_testembed python-config ++build_all: check-clean-src $(BUILDPYTHON) $(BUILDPYTHONW) platform oldsharedmods sharedmods \ ++ gdbhooks Programs/_testembed python-config $(ABI3DLLLIBRARY) $(ABI3LDLIBRARY) + build_wasm: check-clean-src $(BUILDPYTHON) platform oldsharedmods python-config + + # Check that the source is clean when building out of source. +@@ -700,9 +715,30 @@ coverage-report: regen-token regen-frozen + clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c + $(PYTHON_FOR_REGEN) $(srcdir)/Tools/clinic/clinic.py --make --srcdir $(srcdir) + ++python_exe.o: $(srcdir)/PC/python_exe.rc ++ $(WINDRES) $(RCFLAGS) -I$(srcdir)/Include -I$(srcdir)/PC -I. $(srcdir)/PC/python_exe.rc $@ ++ ++pythonw_exe.o: $(srcdir)/PC/pythonw_exe.rc ++ $(WINDRES) $(RCFLAGS) -I$(srcdir)/Include -I$(srcdir)/PC -I. $(srcdir)/PC/pythonw_exe.rc $@ ++ ++python_nt.o: $(srcdir)/PC/python_nt.rc ++ $(WINDRES) $(RCFLAGS) -DORIGINAL_FILENAME=\\\"$(DLLLIBRARY)\\\" -I$(srcdir)/Include -I$(srcdir)/PC -I. $(srcdir)/PC/python_nt.rc $@ ++ ++python3dll_nt.o: $(srcdir)/PC/python_nt.rc ++ $(WINDRES) $(RCFLAGS) -DORIGINAL_FILENAME=\\\"$(ABI3DLLLIBRARY)\\\" -I$(srcdir)/Include -I$(srcdir)/PC -I. $(srcdir)/PC/python_nt.rc $@ ++ ++venvlauncher.o: $(srcdir)/PC/pylauncher.rc ++ $(WINDRES) $(RCFLAGS) -DPY_ICON -I$(srcdir)/Include -I$(srcdir)/PC -I. $(srcdir)/PC/pylauncher.rc $@ ++ ++venvwlauncher.o: $(srcdir)/PC/pylauncher.rc ++ $(WINDRES) $(RCFLAGS) -DPYW_ICON -I$(srcdir)/Include -I$(srcdir)/PC -I. $(srcdir)/PC/pylauncher.rc $@ ++ ++$(BUILDPYTHONW): Programs/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) pythonw_exe.o ++ $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -municode -mwindows -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) pythonw_exe.o ++ + # Build the interpreter +-$(BUILDPYTHON): Programs/python.o $(LINK_PYTHON_DEPS) +- $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) ++$(BUILDPYTHON): Programs/python.o $(LINK_PYTHON_DEPS) python_exe.o ++ $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -municode -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) python_exe.o + + platform: $(PYTHON_FOR_BUILD_DEPS) pybuilddir.txt + $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform +@@ -806,13 +842,17 @@ $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK): \ + + # This rule builds the Cygwin Python DLL and import library if configured + # for a shared core library; otherwise, this rule is a noop. +-$(DLLLIBRARY) libpython$(LDVERSION).dll.a: $(LIBRARY_OBJS) ++$(DLLLIBRARY) libpython$(LDVERSION).dll.a: $(LIBRARY_OBJS) python_nt.o + if test -n "$(DLLLIBRARY)"; then \ + $(LDSHARED) -Wl,--out-implib=$@ -o $(DLLLIBRARY) $^ \ +- $(LIBS) $(MODLIBS) $(SYSLIBS); \ ++ $(LIBS) $(LDFLAGS_NODIST) $(MODLIBS) $(SYSLIBS) python_nt.o; \ + else true; \ + fi + ++$(ABI3DLLLIBRARY) $(ABI3LDLIBRARY): python3dll_nt.o $(srcdir)/PC/launcher.c ++ $(LDSHARED) -DPYTHON_DLL_NAME=\"$(DLLLIBRARY)\" $(srcdir)/PC/python3dll.c -Wl,--out-implib=$(ABI3LDLIBRARY) -o $(ABI3DLLLIBRARY) python3dll_nt.o \ ++ $(LDFLAGS_NODIST); ++ + # wasm32-emscripten browser build + # wasm assets directory is relative to current build dir, e.g. "./usr/local". + # --preload-file turns a relative asset path into an absolute path. +@@ -974,7 +1014,7 @@ BOOTSTRAP_HEADERS = \ + Programs/_bootstrap_python.o: Programs/_bootstrap_python.c $(BOOTSTRAP_HEADERS) $(PYTHON_HEADERS) + + _bootstrap_python: $(LIBRARY_OBJS_OMIT_FROZEN) Programs/_bootstrap_python.o Modules/getpath.o Modules/Setup.local +- $(LINKCC) $(PY_LDFLAGS_NOLTO) -o $@ $(LIBRARY_OBJS_OMIT_FROZEN) \ ++ $(LINKCC) $(PY_LDFLAGS_NOLTO) -o $@ -municode -mwindows $(LIBRARY_OBJS_OMIT_FROZEN) \ + Programs/_bootstrap_python.o Modules/getpath.o $(LIBS) $(MODLIBS) $(SYSLIBS) + + +@@ -1276,9 +1316,15 @@ Python/dynload_hpux.o: $(srcdir)/Python/dynload_hpux.c Makefile + -DSHLIB_EXT='"$(EXT_SUFFIX)"' \ + -o $@ $(srcdir)/Python/dynload_hpux.c + ++Python/dynload_win.o: $(srcdir)/Python/dynload_win.c Makefile ++ $(CC) -c $(PY_CORE_CFLAGS) \ ++ -DPYD_PLATFORM_TAG='"$(PYD_PLATFORM_TAG)"' \ ++ -o $@ $(srcdir)/Python/dynload_win.c ++ + Python/sysmodule.o: $(srcdir)/Python/sysmodule.c Makefile $(srcdir)/Include/pydtrace.h + $(CC) -c $(PY_CORE_CFLAGS) \ + -DABIFLAGS='"$(ABIFLAGS)"' \ ++ -DVPATH='"$(VPATH)"' \ + $(MULTIARCH_CPPFLAGS) \ + -o $@ $(srcdir)/Python/sysmodule.c + +@@ -1496,6 +1542,7 @@ PYTHON_HEADERS= \ + $(srcdir)/Include/frameobject.h \ + $(srcdir)/Include/import.h \ + $(srcdir)/Include/intrcheck.h \ ++ $(srcdir)/Include/iscygpty.h \ + $(srcdir)/Include/iterobject.h \ + $(srcdir)/Include/listobject.h \ + $(srcdir)/Include/longobject.h \ +@@ -1791,7 +1838,7 @@ $(DESTSHARED): + # Install the interpreter with $(VERSION) affixed + # This goes into $(exec_prefix) + altbininstall: $(BUILDPYTHON) @FRAMEWORKPYTHONW@ +- @for i in $(BINDIR) $(LIBDIR); \ ++ @for i in $(BINDIR) $(LIBDIR) $(VENVLAUNCHERDIR); \ + do \ + if test ! -d $(DESTDIR)$$i; then \ + echo "Creating directory $$i"; \ +@@ -1801,6 +1848,7 @@ altbininstall: $(BUILDPYTHON) @FRAMEWORKPYTHONW@ + done + if test "$(PYTHONFRAMEWORKDIR)" = "no-framework" ; then \ + $(INSTALL_PROGRAM) $(BUILDPYTHON) $(DESTDIR)$(BINDIR)/python$(LDVERSION)$(EXE); \ ++ $(INSTALL_PROGRAM) $(BUILDPYTHONW) $(DESTDIR)$(BINDIR)/python3w$(EXE); \ + else \ + $(INSTALL_PROGRAM) $(STRIPFLAG) Mac/pythonw $(DESTDIR)$(BINDIR)/python$(LDVERSION)$(EXE); \ + fi +@@ -1814,6 +1862,7 @@ altbininstall: $(BUILDPYTHON) @FRAMEWORKPYTHONW@ + if test -f $(LDLIBRARY) && test "$(PYTHONFRAMEWORKDIR)" = "no-framework" ; then \ + if test -n "$(DLLLIBRARY)" ; then \ + $(INSTALL_SHARED) $(DLLLIBRARY) $(DESTDIR)$(BINDIR); \ ++ $(INSTALL_SHARED) $(ABI3DLLLIBRARY) $(DESTDIR)$(BINDIR); \ + else \ + $(INSTALL_SHARED) $(LDLIBRARY) $(DESTDIR)$(LIBDIR)/$(INSTSONAME); \ + if test $(LDLIBRARY) != $(INSTSONAME); then \ +@@ -1924,6 +1973,7 @@ LIBSUBDIRS= asyncio \ + tkinter \ + tomllib \ + turtledemo \ ++ msilib \ + unittest \ + urllib \ + venv venv/scripts venv/scripts/common venv/scripts/posix \ +@@ -2223,8 +2273,9 @@ libainstall: all python-config + @if test "$(STATIC_LIBPYTHON)" = 1; then \ + if test -d $(LIBRARY); then :; else \ + if test "$(PYTHONFRAMEWORKDIR)" = no-framework; then \ +- if test "$(SHLIB_SUFFIX)" = .dll; then \ +- $(INSTALL_DATA) $(LDLIBRARY) $(DESTDIR)$(LIBPL) ; \ ++ if test "$(SHLIB_SUFFIX)" = .dll -o "$(SHLIB_SUFFIX)" = .pyd; then \ ++ $(INSTALL_DATA) $(LDLIBRARY) $(DESTDIR)$(LIBDIR) ; \ ++ $(INSTALL_DATA) $(ABI3LDLIBRARY) $(DESTDIR)$(LIBDIR) ; \ + else \ + $(INSTALL_DATA) $(LIBRARY) $(DESTDIR)$(LIBPL)/$(LIBRARY) ; \ + fi; \ +@@ -2263,16 +2314,23 @@ libainstall: all python-config + else true; \ + fi + ++ifeq ($(shell uname -o),Msys) ++DESTDIRFINAL=$(DESTDIR) ++else ++DESTDIRFINAL=$(DESTDIR)/ ++endif ++ + # Install the dynamically loadable modules + # This goes into $(exec_prefix) + sharedinstall: all ++ MSYS2_ARG_CONV_EXCL="--prefix=;--install-scripts=;--install-platlib=" \ + $(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/setup.py install \ + --prefix=$(prefix) \ + --install-scripts=$(BINDIR) \ + --install-platlib=$(DESTSHARED) \ +- --root=$(DESTDIR)/ +- -rm $(DESTDIR)$(DESTSHARED)/_sysconfigdata_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH).py +- -rm -r $(DESTDIR)$(DESTSHARED)/__pycache__ ++ --root=$(DESTDIRFINAL) ++ -rm $(DESTDIRFINAL)$(DESTSHARED)/_sysconfigdata_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH).py ++ -rm -r $(DESTDIRFINAL)$(DESTSHARED)/__pycache__ + + # Here are a couple of targets for MacOSX again, to install a full + # framework-based Python. frameworkinstall installs everything, the +@@ -2464,7 +2522,7 @@ clean: clean-retain-profile + fi + + clobber: clean +- -rm -f $(BUILDPYTHON) $(LIBRARY) $(LDLIBRARY) $(DLLLIBRARY) \ ++ -rm -f $(BUILDPYTHON) $(LIBRARY) $(LDLIBRARY) $(DLLLIBRARY)$(ABI3LDLIBRARY) $(ABI3DLLLIBRARY) \ + tags TAGS \ + config.cache config.log pyconfig.h Modules/config.c + -rm -rf build platform +diff --git a/Misc/NEWS.d/3.11.4.rst b/Misc/NEWS.d/3.11.4.rst +new file mode 100644 +index 0000000..71ed43d +--- /dev/null ++++ b/Misc/NEWS.d/3.11.4.rst +@@ -0,0 +1,785 @@ ++.. date: 2023-06-01-03-24-58 ++.. gh-issue: 103142 ++.. nonce: GLWDMX ++.. release date: 2023-06-06 ++.. section: Security ++ ++The version of OpenSSL used in our binary builds has been upgraded to 1.1.1u ++to address several CVEs. ++ ++.. ++ ++.. date: 2023-05-02-17-56-32 ++.. gh-issue: 99889 ++.. nonce: l664SU ++.. section: Security ++ ++Fixed a security in flaw in :func:`uu.decode` that could allow for directory ++traversal based on the input if no ``out_file`` was specified. ++ ++.. ++ ++.. date: 2023-05-01-15-03-25 ++.. gh-issue: 104049 ++.. nonce: b01Y3g ++.. section: Security ++ ++Do not expose the local on-disk location in directory indexes produced by ++:class:`http.client.SimpleHTTPRequestHandler`. ++ ++.. ++ ++.. date: 2023-03-07-20-59-17 ++.. gh-issue: 102153 ++.. nonce: 14CLSZ ++.. section: Security ++ ++:func:`urllib.parse.urlsplit` now strips leading C0 control and space ++characters following the specification for URLs defined by WHATWG in ++response to CVE-2023-24329. Patch by Illia Volochii. ++ ++.. ++ ++.. date: 2023-05-31-19-35-22 ++.. gh-issue: 105164 ++.. nonce: 6Wajph ++.. section: Core and Builtins ++ ++Ensure annotations are set up correctly if the only annotation in a block is ++within a :keyword:`match` block. Patch by Jelle Zijlstra. ++ ++.. ++ ++.. date: 2023-05-18-13-00-21 ++.. gh-issue: 104615 ++.. nonce: h_rtw2 ++.. section: Core and Builtins ++ ++Fix wrong ordering of assignments in code like ``a, a = x, y``. Contributed ++by Carl Meyer. ++ ++.. ++ ++.. date: 2023-05-14-18-56-54 ++.. gh-issue: 104482 ++.. nonce: yaQsv8 ++.. section: Core and Builtins ++ ++Fix three error handling bugs in ast.c's validation of pattern matching ++statements. ++ ++.. ++ ++.. date: 2023-05-13-06-22-52 ++.. gh-issue: 102818 ++.. nonce: HIX1Dr ++.. section: Core and Builtins ++ ++Do not add a frame to the traceback in the ``sys.setprofile`` and ++``sys.settrace`` trampoline functions. This ensures that frames are not ++duplicated if an exception is raised in the callback function, and ensures ++that frames are not omitted if a C callback is used and that does not add ++the frame. ++ ++.. ++ ++.. date: 2023-05-12-00-19-02 ++.. gh-issue: 104405 ++.. nonce: tXV5fn ++.. section: Core and Builtins ++ ++Fix an issue where some :term:`bytecode` instructions could ignore ++:pep:`523` when "inlining" calls. ++ ++.. ++ ++.. date: 2023-05-01-12-03-52 ++.. gh-issue: 104018 ++.. nonce: PFxGS4 ++.. section: Core and Builtins ++ ++Disallow the "z" format specifier in %-format of bytes objects. ++ ++.. ++ ++.. date: 2023-04-28-18-57-13 ++.. gh-issue: 103971 ++.. nonce: Q3U9lv ++.. section: Core and Builtins ++ ++Fix an issue where incorrect locations numbers could be assigned to code ++following ``case`` blocks. ++ ++.. ++ ++.. date: 2023-04-21-17-03-14 ++.. gh-issue: 102310 ++.. nonce: anLjDx ++.. section: Core and Builtins ++ ++Change the error range for invalid bytes literals. ++ ++.. ++ ++.. date: 2023-04-21-16-12-41 ++.. gh-issue: 103590 ++.. nonce: 7DHDOE ++.. section: Core and Builtins ++ ++Do not wrap a single exception raised from a ``try-except*`` construct in an ++:exc:`ExceptionGroup`. ++ ++.. ++ ++.. date: 2023-04-14-22-35-23 ++.. gh-issue: 101517 ++.. nonce: 5EqM-S ++.. section: Core and Builtins ++ ++Fix bug in line numbers of instructions emitted for :keyword:`except* ++`. ++ ++.. ++ ++.. date: 2023-04-08-17-13-07 ++.. gh-issue: 103242 ++.. nonce: ysI1b3 ++.. section: Core and Builtins ++ ++Migrate :meth:`~ssl.SSLContext.set_ecdh_curve` method not to use deprecated ++OpenSSL APIs. Patch by Dong-hee Na. ++ ++.. ++ ++.. date: 2023-04-01-00-46-31 ++.. gh-issue: 102700 ++.. nonce: 493NB4 ++.. section: Core and Builtins ++ ++Allow built-in modules to be submodules. This allows submodules to be ++statically linked into a CPython binary. ++ ++.. ++ ++.. date: 2023-02-12-22-40-22 ++.. gh-issue: 101857 ++.. nonce: _bribG ++.. section: Core and Builtins ++ ++Fix xattr support detection on Linux systems by widening the check to linux, ++not just glibc. This fixes support for musl. ++ ++.. ++ ++.. date: 2022-11-08-12-36-25 ++.. gh-issue: 99184 ++.. nonce: KIaqzz ++.. section: Core and Builtins ++ ++Bypass instance attribute access of ``__name__`` in ``repr`` of ++:class:`weakref.ref`. ++ ++.. ++ ++.. date: 2022-09-27-11-59-13 ++.. gh-issue: 96670 ++.. nonce: XrBBit ++.. section: Core and Builtins ++ ++The parser now raises :exc:`SyntaxError` when parsing source code containing ++null bytes. Backported from ``aab01e3``. Patch by Pablo Galindo ++ ++.. ++ ++.. bpo: 31821 ++.. date: 2019-12-01-12-58-31 ++.. nonce: 1FNmwk ++.. section: Core and Builtins ++ ++Fix :func:`!pause_reading` to work when called from :func:`!connection_made` ++in :mod:`asyncio`. ++ ++.. ++ ++.. date: 2023-06-02-02-38-26 ++.. gh-issue: 105080 ++.. nonce: 2imGMg ++.. section: Library ++ ++Fixed inconsistent signature on derived classes for ++:func:`inspect.signature` ++ ++.. ++ ++.. date: 2023-05-24-09-34-23 ++.. gh-issue: 104874 ++.. nonce: oqyJSy ++.. section: Library ++ ++Document the ``__name__`` and ``__supertype__`` attributes of ++:class:`typing.NewType`. Patch by Jelle Zijlstra. ++ ++.. ++ ++.. date: 2023-05-17-20-03-01 ++.. gh-issue: 104340 ++.. nonce: kp_XmX ++.. section: Library ++ ++When an ``asyncio`` pipe protocol loses its connection due to an error, and ++the caller doesn't await ``wait_closed()`` on the corresponding ++``StreamWriter``, don't log a warning about an exception that was never ++retrieved. After all, according to the ``StreamWriter.close()`` docs, the ++``wait_closed()`` call is optional ("not mandatory"). ++ ++.. ++ ++.. date: 2023-05-17-08-01-36 ++.. gh-issue: 104372 ++.. nonce: jpoWs6 ++.. section: Library ++ ++Refactored the ``_posixsubprocess`` internals to avoid Python C API usage ++between fork and exec when marking ``pass_fds=`` file descriptors ++inheritable. ++ ++.. ++ ++.. date: 2023-05-16-11-02-44 ++.. gh-issue: 75367 ++.. nonce: qLWR35 ++.. section: Library ++ ++Fix data descriptor detection in :func:`inspect.getattr_static`. ++ ++.. ++ ++.. date: 2023-05-16-10-07-16 ++.. gh-issue: 104536 ++.. nonce: hFWD8f ++.. section: Library ++ ++Fix a race condition in the internal :mod:`multiprocessing.process` cleanup ++logic that could manifest as an unintended ``AttributeError`` when calling ++``process.close()``. ++ ++.. ++ ++.. date: 2023-05-11-23-03-00 ++.. gh-issue: 104399 ++.. nonce: MMatTP ++.. section: Library ++ ++Prepare the ``_tkinter`` module for building with Tcl 9.0 and future ++libtommath by replacing usage of deprecated functions ++:c:func:`mp_to_unsigned_bin_n` and :c:func:`mp_unsigned_bin_size` when ++necessary. ++ ++.. ++ ++.. date: 2023-05-08-20-57-17 ++.. gh-issue: 104307 ++.. nonce: DSB93G ++.. section: Library ++ ++:func:`socket.getnameinfo` now releases the GIL while contacting the DNS ++server ++ ++.. ++ ++.. date: 2023-05-08-15-39-00 ++.. gh-issue: 87695 ++.. nonce: f6iO7v ++.. section: Library ++ ++Fix issue where :meth:`pathlib.Path.glob` raised :exc:`OSError` when it ++encountered a symlink to an overly long path. ++ ++.. ++ ++.. date: 2023-05-07-19-56-45 ++.. gh-issue: 104265 ++.. nonce: fVblry ++.. section: Library ++ ++Prevent possible crash by disallowing instantiation of the ++:class:`!_csv.Reader` and :class:`!_csv.Writer` types. The regression was ++introduced in 3.10.0a4 with PR 23224 (:issue:`14935`). Patch by Radislav ++Chugunov. ++ ++.. ++ ++.. date: 2023-05-01-16-43-28 ++.. gh-issue: 104035 ++.. nonce: MrJBw8 ++.. section: Library ++ ++Do not ignore user-defined ``__getstate__`` and ``__setstate__`` methods for ++slotted frozen dataclasses. ++ ++.. ++ ++.. date: 2023-04-29-18-23-16 ++.. gh-issue: 103987 ++.. nonce: sRgALL ++.. section: Library ++ ++In :mod:`mmap`, fix several bugs that could lead to access to memory-mapped ++files after they have been invalidated. ++ ++.. ++ ++.. date: 2023-04-27-20-03-08 ++.. gh-issue: 103935 ++.. nonce: Uaf2M0 ++.. section: Library ++ ++Use :func:`io.open_code` for files to be executed instead of raw ++:func:`open` ++ ++.. ++ ++.. date: 2023-04-27-00-45-41 ++.. gh-issue: 100370 ++.. nonce: MgZ3KY ++.. section: Library ++ ++Fix potential :exc:`OverflowError` in :meth:`sqlite3.Connection.blobopen` ++for 32-bit builds. Patch by Erlend E. Aasland. ++ ++.. ++ ++.. date: 2023-04-26-09-54-25 ++.. gh-issue: 103848 ++.. nonce: aDSnpR ++.. section: Library ++ ++Add checks to ensure that ``[`` bracketed ``]`` hosts found by ++:func:`urllib.parse.urlsplit` are of IPv6 or IPvFuture format. ++ ++.. ++ ++.. date: 2023-04-26-09-38-47 ++.. gh-issue: 103872 ++.. nonce: 8LBsDz ++.. section: Library ++ ++Update the bundled copy of pip to version 23.1.2. ++ ++.. ++ ++.. date: 2023-04-25-19-58-13 ++.. gh-issue: 103861 ++.. nonce: JeozgD ++.. section: Library ++ ++Fix ``zipfile.Zipfile`` creating invalid zip files when ``force_zip64`` was ++used to add files to them. Patch by Carey Metcalfe. ++ ++.. ++ ++.. date: 2023-04-24-00-34-23 ++.. gh-issue: 103685 ++.. nonce: U14jBM ++.. section: Library ++ ++Prepare :meth:`tkinter.Menu.index` for Tk 8.7 so that it does not raise ++``TclError: expected integer but got ""`` when it should return ``None``. ++ ++.. ++ ++.. date: 2023-04-22-22-14-09 ++.. gh-issue: 81403 ++.. nonce: zVz9Td ++.. section: Library ++ ++:class:`urllib.request.CacheFTPHandler` no longer raises :class:`URLError` ++if a cached FTP instance is reused. ftplib's endtransfer method calls ++voidresp to drain the connection to handle FTP instance reuse properly. ++ ++.. ++ ++.. date: 2023-04-16-18-29-04 ++.. gh-issue: 103578 ++.. nonce: fly1wc ++.. section: Library ++ ++Fixed a bug where :mod:`pdb` crashes when reading source file with different ++encoding by replacing :func:`io.open` with :func:`io.open_code`. The new ++method would also call into the hook set by :func:`PyFile_SetOpenCodeHook`. ++ ++.. ++ ++.. date: 2023-04-15-12-19-14 ++.. gh-issue: 103556 ++.. nonce: TEf-2m ++.. section: Library ++ ++Now creating :class:`inspect.Signature` objects with positional-only ++parameter with a default followed by a positional-or-keyword parameter ++without one is impossible. ++ ++.. ++ ++.. date: 2023-04-15-11-21-38 ++.. gh-issue: 103559 ++.. nonce: a9rYHG ++.. section: Library ++ ++Update the bundled copy of pip to version 23.1.1. ++ ++.. ++ ++.. date: 2023-04-12-17-59-55 ++.. gh-issue: 103365 ++.. nonce: UBEE0U ++.. section: Library ++ ++Set default Flag boundary to ``STRICT`` and fix bitwise operations. ++ ++.. ++ ++.. date: 2023-04-12-13-04-16 ++.. gh-issue: 103472 ++.. nonce: C6bOHv ++.. section: Library ++ ++Avoid a potential :exc:`ResourceWarning` in ++:class:`http.client.HTTPConnection` by closing the proxy / tunnel's CONNECT ++response explicitly. ++ ++.. ++ ++.. date: 2023-04-11-21-38-39 ++.. gh-issue: 103449 ++.. nonce: -nxmhb ++.. section: Library ++ ++Fix a bug in doc string generation in :func:`dataclasses.dataclass`. ++ ++.. ++ ++.. date: 2023-04-06-17-28-36 ++.. gh-issue: 103256 ++.. nonce: 1syxfs ++.. section: Library ++ ++Fixed a bug that caused :mod:`hmac` to raise an exception when the requested ++hash algorithm was not available in OpenSSL despite being available ++separately as part of ``hashlib`` itself. It now falls back properly to the ++built-in. This could happen when, for example, your OpenSSL does not include ++SHA3 support and you want to compute ``hmac.digest(b'K', b'M', ++'sha3_256')``. ++ ++.. ++ ++.. date: 2023-04-05-01-28-53 ++.. gh-issue: 103225 ++.. nonce: QD3JVU ++.. section: Library ++ ++Fix a bug in :mod:`pdb` when displaying line numbers of module-level source ++code. ++ ++.. ++ ++.. date: 2023-04-04-12-43-38 ++.. gh-issue: 93910 ++.. nonce: jurMzv ++.. section: Library ++ ++Remove deprecation of enum ``memmber.member`` access. ++ ++.. ++ ++.. date: 2023-04-03-23-44-34 ++.. gh-issue: 102978 ++.. nonce: gy9eVk ++.. section: Library ++ ++Fixes :func:`unittest.mock.patch` not enforcing function signatures for ++methods decorated with ``@classmethod`` or ``@staticmethod`` when patch is ++called with ``autospec=True``. ++ ++.. ++ ++.. date: 2023-04-02-23-05-22 ++.. gh-issue: 103204 ++.. nonce: bbDmu0 ++.. section: Library ++ ++Fixes :mod:`http.server` accepting HTTP requests with HTTP version numbers ++preceded by '+', or '-', or with digit-separating '_' characters. The ++length of the version numbers is also constrained. ++ ++.. ++ ++.. date: 2023-03-23-15-24-38 ++.. gh-issue: 102953 ++.. nonce: YR4KaK ++.. section: Library ++ ++The extraction methods in :mod:`tarfile`, and :func:`shutil.unpack_archive`, ++have a new a *filter* argument that allows limiting tar features than may be ++surprising or dangerous, such as creating files outside the destination ++directory. See :ref:`tarfile-extraction-filter` for details. ++ ++.. ++ ++.. date: 2023-02-09-22-24-34 ++.. gh-issue: 101640 ++.. nonce: oFuEpB ++.. section: Library ++ ++:class:`argparse.ArgumentParser` now catches errors when writing messages, ++such as when :data:`sys.stderr` is ``None``. Patch by Oleg Iarygin. ++ ++.. ++ ++.. date: 2022-09-07-09-32-07 ++.. gh-issue: 96522 ++.. nonce: t73oqp ++.. section: Library ++ ++Fix potential deadlock in pty.spawn() ++ ++.. ++ ++.. date: 2022-08-27-21-41-41 ++.. gh-issue: 87474 ++.. nonce: 9X-kxt ++.. section: Library ++ ++Fix potential file descriptor leaks in :class:`subprocess.Popen`. ++ ++.. ++ ++.. date: 2023-05-28-21-01-00 ++.. gh-issue: 89455 ++.. nonce: qAKRrA ++.. section: Documentation ++ ++Add missing documentation for the ``max_group_depth`` and ++``max_group_width`` parameters and the ``exceptions`` attribute of the ++:class:`traceback.TracebackException` class. ++ ++.. ++ ++.. date: 2023-05-28-19-08-42 ++.. gh-issue: 89412 ++.. nonce: j4cg7K ++.. section: Documentation ++ ++Add missing documentation for the ``end_lineno`` and ``end_offset`` ++attributes of the :class:`traceback.TracebackException` class. ++ ++.. ++ ++.. date: 2023-05-25-22-34-31 ++.. gh-issue: 104943 ++.. nonce: J2v1Pc ++.. section: Documentation ++ ++Remove mentions of old Python versions in :class:`typing.NamedTuple`. ++ ++.. ++ ++.. date: 2023-05-14-12-11-28 ++.. gh-issue: 67056 ++.. nonce: nVC2Rf ++.. section: Documentation ++ ++Document that the effect of registering or unregistering an :mod:`atexit` ++cleanup function from within a registered cleanup function is undefined. ++ ++.. ++ ++.. date: 2023-04-25-22-58-08 ++.. gh-issue: 48241 ++.. nonce: l1Gxxh ++.. section: Documentation ++ ++Clarifying documentation about the url parameter to urllib.request.urlopen ++and urllib.request.Requst needing to be encoded properly. ++ ++.. ++ ++.. date: 2023-05-15-02-22-44 ++.. gh-issue: 104494 ++.. nonce: Bkrbfn ++.. section: Tests ++ ++Update ``test_pack_configure_in`` and ``test_place_configure_in`` for ++changes to error message formatting in Tk 8.7. ++ ++.. ++ ++.. date: 2023-05-14-03-00-00 ++.. gh-issue: 104461 ++.. nonce: Rmex11 ++.. section: Tests ++ ++Run test_configure_screen on X11 only, since the ``DISPLAY`` environment ++variable and ``-screen`` option for toplevels are not useful on Tk for Win32 ++or Aqua. ++ ++.. ++ ++.. date: 2023-04-08-00-50-23 ++.. gh-issue: 103329 ++.. nonce: M38tqF ++.. section: Tests ++ ++Regression tests for the behaviour of ``unittest.mock.PropertyMock`` were ++added. ++ ++.. ++ ++.. date: 2023-02-11-22-36-10 ++.. gh-issue: 85984 ++.. nonce: EVXjT9 ++.. section: Tests ++ ++Utilize new "winsize" functions from termios in pty tests. ++ ++.. ++ ++.. date: 2022-11-06-18-42-38 ++.. gh-issue: 75729 ++.. nonce: uGYJrv ++.. section: Tests ++ ++Fix the :func:`os.spawn* ` tests failing on Windows when the ++working directory or interpreter path contains spaces. ++ ++.. ++ ++.. date: 2023-06-06-09-08-10 ++.. gh-issue: 90005 ++.. nonce: 8mmeJQ ++.. section: Build ++ ++Fix a regression in :file:`configure` where we could end up unintentionally ++linking with ``libbsd``. ++ ++.. ++ ++.. date: 2023-05-04-10-56-14 ++.. gh-issue: 104106 ++.. nonce: -W9BJS ++.. section: Build ++ ++Add gcc fallback of mkfifoat/mknodat for macOS. Patch by Dong-hee Na. ++ ++.. ++ ++.. date: 2023-02-11-05-31-05 ++.. gh-issue: 99069 ++.. nonce: X4LDvY ++.. section: Build ++ ++Extended workaround defining ``static_assert`` when missing from the libc ++headers to all clang and gcc builds. In particular, this fixes building on ++macOS <= 10.10. ++ ++.. ++ ++.. date: 2023-05-31-16-14-31 ++.. gh-issue: 105146 ++.. nonce: gNjqq8 ++.. section: Windows ++ ++Updated the links at the end of the installer to point to Discourse rather ++than the mailing lists. ++ ++.. ++ ++.. date: 2023-05-18-22-46-03 ++.. gh-issue: 104623 ++.. nonce: HJZhm1 ++.. section: Windows ++ ++Update Windows installer to use SQLite 3.42.0. ++ ++.. ++ ++.. date: 2023-03-24-11-25-28 ++.. gh-issue: 102997 ++.. nonce: dredy2 ++.. section: Windows ++ ++Update Windows installer to use SQLite 3.41.2. ++ ++.. ++ ++.. date: 2023-03-18-21-38-00 ++.. gh-issue: 88013 ++.. nonce: Z3loxC ++.. section: Windows ++ ++Fixed a bug where :exc:`TypeError` was raised when calling ++:func:`ntpath.realpath` with a bytes parameter in some cases. ++ ++.. ++ ++.. date: 2023-05-30-23-30-46 ++.. gh-issue: 103142 ++.. nonce: 55lMXQ ++.. section: macOS ++ ++Update macOS installer to use OpenSSL 1.1.1u. ++ ++.. ++ ++.. date: 2023-05-18-22-31-49 ++.. gh-issue: 104623 ++.. nonce: 6h7Xfx ++.. section: macOS ++ ++Update macOS installer to SQLite 3.42.0. ++ ++.. ++ ++.. date: 2023-03-24-11-20-47 ++.. gh-issue: 102997 ++.. nonce: ZgQkbq ++.. section: macOS ++ ++Update macOS installer to SQLite 3.41.2. ++ ++.. ++ ++.. date: 2023-05-23-17-19-49 ++.. gh-issue: 104719 ++.. nonce: rvYXH- ++.. section: IDLE ++ ++Remove IDLE's modification of tokenize.tabsize and test other uses of ++tokenize data and methods. ++ ++.. ++ ++.. date: 2023-05-17-17-32-21 ++.. gh-issue: 104499 ++.. nonce: hNeqV4 ++.. section: IDLE ++ ++Fix completions for Tk Aqua 8.7 (currently blank). ++ ++.. ++ ++.. date: 2023-05-17-15-11-11 ++.. gh-issue: 104496 ++.. nonce: wjav-y ++.. section: IDLE ++ ++About prints both tcl and tk versions if different (expected someday). ++ ++.. ++ ++.. date: 2023-04-30-20-01-18 ++.. gh-issue: 88496 ++.. nonce: y65vUb ++.. section: IDLE ++ ++Fix IDLE test hang on macOS. +diff --git a/Misc/config_mingw b/Misc/config_mingw +new file mode 100644 +index 0000000..9be43fd +--- /dev/null ++++ b/Misc/config_mingw +@@ -0,0 +1,15 @@ ++# configure defaults for mingw* hosts ++ ++# mingw functions to ignore ++ac_cv_func_ftruncate=ignore # implement it as _chsize ++ ++# mingw-w64 functions to ignore ++ac_cv_func_truncate=ignore ++ac_cv_func_alarm=ignore ++ ++# files to ignore ++ac_cv_file__dev_ptmx=ignore #NOTE: under MSYS environment device exist ++ac_cv_file__dev_ptc=no ++ ++# force detection of winsock2 functionality - require wxp or newer ++ac_cv_func_getpeername=yes +diff --git a/Misc/cross_mingw32 b/Misc/cross_mingw32 +new file mode 100644 +index 0000000..03fde9e +--- /dev/null ++++ b/Misc/cross_mingw32 +@@ -0,0 +1,11 @@ ++# configure defaults for mingw32 host if cross-build ++ ++ac_cv_little_endian_double=yes ++ac_cv_big_endian_double=no ++ac_cv_mixed_endian_double=no ++ ++ac_cv_tanh_preserves_zero_sign=yes ++ ++ac_cv_wchar_t_signed=no ++ ++ac_cv_have_size_t_format=no +diff --git a/Misc/python-config.sh.in b/Misc/python-config.sh.in +index 2602fe2..e0e048a 100644 +--- a/Misc/python-config.sh.in ++++ b/Misc/python-config.sh.in +@@ -1,32 +1,44 @@ + #!/bin/sh + +-# Keep this script in sync with python-config.in +- + exit_with_usage () + { + echo "Usage: $0 --prefix|--exec-prefix|--includes|--libs|--cflags|--ldflags|--extension-suffix|--help|--abiflags|--configdir|--embed" +- exit $1 ++ exit 1 + } + ++# Really, python-config.py (and thus .sh) should be called directly, but ++# sometimes software (e.g. GDB) calls python-config.sh as if it were the ++# Python executable, passing python-config.py as the first argument. ++# Work around that oddness by ignoring any .py passed as first arg. ++case "$1" in ++ *.py) ++ shift ++ ;; ++esac ++ + if [ "$1" = "" ] ; then +- exit_with_usage 1 ++ exit_with_usage + fi + + # Returns the actual prefix where this script was installed to. + installed_prefix () + { +- RESULT=$(dirname $(cd $(dirname "$1") && pwd -P)) +- if which readlink >/dev/null 2>&1 ; then +- if readlink -f "$RESULT" >/dev/null 2>&1; then +- RESULT=$(readlink -f "$RESULT") +- fi ++ local RESULT=$(dirname $(cd $(dirname "$1") && pwd -P)) ++ if [ $(which readlink) ] ; then ++ RESULT=$(readlink -f "$RESULT") ++ fi ++ # Since we don't know where the output from this script will end up ++ # we keep all paths in Windows-land since MSYS2 can handle that ++ # while native tools can't handle paths in MSYS2-land. ++ if [ "$OSTYPE" = "msys" ]; then ++ RESULT=$(cd "$RESULT" && pwd -W) + fi + echo $RESULT + } + + prefix_real=$(installed_prefix "$0") + +-# Use sed to fix paths from their built-to locations to their installed-to ++# Use sed to fix paths from their built-to locations to their installed to + # locations. Keep prefix & exec_prefix using their original values in case + # they are referenced in other configure variables, to prevent double + # substitution, issue #22140. +@@ -41,13 +53,17 @@ LIBM="@LIBM@" + LIBC="@LIBC@" + SYSLIBS="$LIBM $LIBC" + ABIFLAGS="@ABIFLAGS@" ++# Protect against lack of substitution. ++if [ "$ABIFLAGS" = "@""ABIFLAGS""@" ] ; then ++ ABIFLAGS= ++fi + LIBS="@LIBPYTHON@ @LIBS@ $SYSLIBS" + LIBS_EMBED="-lpython${VERSION}${ABIFLAGS} @LIBS@ $SYSLIBS" + BASECFLAGS="@BASECFLAGS@" +-LDLIBRARY="@LDLIBRARY@" + OPT="@OPT@" + PY_ENABLE_SHARED="@PY_ENABLE_SHARED@" + LDVERSION="@LDVERSION@" ++LDLIBRARY="@LDLIBRARY@" + LIBDEST=${prefix_real}/lib/python${VERSION} + LIBPL=$(echo "@LIBPL@" | sed "s#$prefix#$prefix_real#") + SO="@EXT_SUFFIX@" +@@ -61,7 +77,7 @@ for ARG in $* + do + case $ARG in + --help) +- exit_with_usage 0 ++ exit_with_usage + ;; + --embed) + PY_EMBED=1 +@@ -69,7 +85,7 @@ do + --prefix|--exec-prefix|--includes|--libs|--cflags|--ldflags|--extension-suffix|--abiflags|--configdir) + ;; + *) +- exit_with_usage 1 ++ exit_with_usage + ;; + esac + done +@@ -80,37 +96,37 @@ fi + + for ARG in "$@" + do +- case "$ARG" in ++ case $ARG in + --prefix) +- echo "$prefix_real" ++ echo -ne "$prefix_real" + ;; + --exec-prefix) +- echo "$exec_prefix_real" ++ echo -ne "$exec_prefix_real " + ;; + --includes) +- echo "$INCDIR $PLATINCDIR" ++ echo -ne "$INCDIR $PLATINCDIR" + ;; + --cflags) +- echo "$INCDIR $PLATINCDIR $BASECFLAGS $CFLAGS $OPT" ++ echo -ne "$INCDIR $PLATINCDIR $BASECFLAGS $CFLAGS $OPT" + ;; + --libs) +- echo "$LIBS" ++ echo -ne "$LIBS" + ;; + --ldflags) + LIBPLUSED= + if [ "$PY_ENABLE_SHARED" = "0" ] ; then + LIBPLUSED="-L$LIBPL" + fi +- echo "$LIBPLUSED -L$libdir $LIBS" ++ echo -ne "$LIBPLUSED -L$libdir $LIBS " + ;; + --extension-suffix) +- echo "$SO" ++ echo -ne "$SO " + ;; + --abiflags) +- echo "$ABIFLAGS" ++ echo -ne "$ABIFLAGS " + ;; + --configdir) +- echo "$LIBPL" ++ echo -ne "$LIBPL " + ;; + esac + done +diff --git a/Misc/python.pc.in b/Misc/python.pc.in +index 87e04de..3900190 100644 +--- a/Misc/python.pc.in ++++ b/Misc/python.pc.in +@@ -9,5 +9,5 @@ Description: Build a C extension for Python + Requires: + Version: @VERSION@ + Libs.private: @LIBS@ +-Libs: ++Libs: -L${libdir} -lpython@VERSION@@ABIFLAGS@ + Cflags: -I${includedir}/python@VERSION@@ABIFLAGS@ +diff --git a/Modules/Setup.bootstrap.in b/Modules/Setup.bootstrap.in +index e3e9b96..6986290 100644 +--- a/Modules/Setup.bootstrap.in ++++ b/Modules/Setup.bootstrap.in +@@ -8,15 +8,15 @@ + # module C APIs are used in core + atexit atexitmodule.c + faulthandler faulthandler.c +-posix posixmodule.c +-_signal signalmodule.c ++@INITSYS@ posixmodule.c ++_signal signalmodule.c -lws2_32 + _tracemalloc _tracemalloc.c + + # modules used by importlib, deepfreeze, freeze, runpy, and sysconfig + _codecs _codecsmodule.c + _collections _collectionsmodule.c + errno errnomodule.c +-_io _io/_iomodule.c _io/iobase.c _io/fileio.c _io/bytesio.c _io/bufferedio.c _io/textio.c _io/stringio.c ++_io _io/_iomodule.c _io/iobase.c _io/fileio.c _io/bytesio.c _io/bufferedio.c _io/textio.c _io/stringio.c _io/winconsoleio.c + itertools itertoolsmodule.c + _sre _sre/sre.c + _thread _threadmodule.c +@@ -33,3 +33,8 @@ _symtable symtablemodule.c + + # for systems without $HOME env, used by site._getuserbase() + @MODULE_PWD_TRUE@pwd pwdmodule.c ++ ++# build-in modules for windows platform: ++@USE_WIN32_MODULE@winreg ../PC/winreg.c ++@MODULE_MSVCRT_TRUE@msvcrt -DPy_BUILD_CORE ../PC/msvcrtmodule.c ++@MODULE__WINAPI_TRUE@_winapi _winapi.c +diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c +index fc73264..6ed71fa 100644 +--- a/Modules/_ctypes/_ctypes.c ++++ b/Modules/_ctypes/_ctypes.c +@@ -3403,6 +3403,18 @@ static PPROC FindAddress(void *handle, const char *name, PyObject *type) + mangled_name = alloca(strlen(name) + 1 + 1 + 1 + 3); /* \0 _ @ %d */ + if (!mangled_name) + return NULL; ++ /* Issue: for stdcall decorated export functions MSVC compiler adds ++ * underscore, but GCC compiler create them without. This is ++ * visible by example for _ctypes_test.pyd module. ++ * As well functions from system libraries are without underscore. ++ * Solutions: ++ * - If a python module is build with gcc option --add-stdcall-alias ++ * the module will contain XXX as alias for function XXX@ as result ++ * first search in this method will succeed. ++ * - Distutil may use compiler to create def-file, to modify it as ++ * add underscore alias and with new def file to create module. ++ * - Or may be just to search for function without underscore. ++ */ + for (i = 0; i < 32; ++i) { + sprintf(mangled_name, "_%s@%d", name, i*4); + Py_BEGIN_ALLOW_THREADS +@@ -3410,6 +3422,13 @@ static PPROC FindAddress(void *handle, const char *name, PyObject *type) + Py_END_ALLOW_THREADS + if (address) + return address; ++ /* search for function without underscore as weel */ ++ sprintf(mangled_name, "%s@%d", name, i*4); ++ Py_BEGIN_ALLOW_THREADS ++ address = (PPROC)GetProcAddress(handle, mangled_name); ++ Py_END_ALLOW_THREADS ++ if (address) ++ return address; + } + return NULL; + #endif +diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c +index e6440fa..f42714a 100644 +--- a/Modules/_gdbmmodule.c ++++ b/Modules/_gdbmmodule.c +@@ -12,7 +12,7 @@ + #include + #include + +-#if defined(WIN32) && !defined(__CYGWIN__) ++#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) + #include "gdbmerrno.h" + extern const char * gdbm_strerror(gdbm_error); + #endif +diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c +index 4496609..80cc6f9 100644 +--- a/Modules/_io/fileio.c ++++ b/Modules/_io/fileio.c +@@ -20,6 +20,7 @@ + #endif + #include /* For offsetof */ + #include "_iomodule.h" ++#include "iscygpty.h" + + /* + * Known likely problems: +@@ -1129,7 +1130,7 @@ _io_FileIO_isatty_impl(fileio *self) + return err_closed(); + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH +- res = isatty(self->fd); ++ res = isatty(self->fd) || is_cygpty(self->fd); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + return PyBool_FromLong(res); +diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c +index 23c38e1..dfb6c7e 100644 +--- a/Modules/_localemodule.c ++++ b/Modules/_localemodule.c +@@ -12,6 +12,13 @@ This software comes with no warranty. Use at your own risk. + #define PY_SSIZE_T_CLEAN + #include "Python.h" + #include "pycore_fileutils.h" ++#ifdef __MINGW32__ ++/* The header libintl.h and library libintl may exist on mingw host. ++ * To be compatible with MSVC build we has to undef some defines. ++ */ ++#undef HAVE_LIBINTL_H ++#undef HAVE_BIND_TEXTDOMAIN_CODESET ++#endif + + #include + #include +diff --git a/Modules/_multiprocessing/multiprocessing.c b/Modules/_multiprocessing/multiprocessing.c +index 0809c24..bbb1b38 100644 +--- a/Modules/_multiprocessing/multiprocessing.c ++++ b/Modules/_multiprocessing/multiprocessing.c +@@ -172,7 +172,7 @@ static PyMethodDef module_methods[] = { + _MULTIPROCESSING_RECV_METHODDEF + _MULTIPROCESSING_SEND_METHODDEF + #endif +-#if !defined(POSIX_SEMAPHORES_NOT_ENABLED) && !defined(__ANDROID__) ++#if defined(MS_WINDOWS) || (!defined(POSIX_SEMAPHORES_NOT_ENABLED) && !defined(__ANDROID__)) + _MULTIPROCESSING_SEM_UNLINK_METHODDEF + #endif + {NULL} +diff --git a/Modules/_multiprocessing/multiprocessing.h b/Modules/_multiprocessing/multiprocessing.h +index 3a8314b..c07cfe8 100644 +--- a/Modules/_multiprocessing/multiprocessing.h ++++ b/Modules/_multiprocessing/multiprocessing.h +@@ -21,7 +21,10 @@ + # endif + # define SEM_HANDLE HANDLE + # define SEM_VALUE_MAX LONG_MAX +-# define HAVE_MP_SEMAPHORE ++# define HAVE_MP_SEMAPHORE ++# if defined(HAVE_SEM_OPEN) && defined(_POSIX_THREADS) ++# include ++# endif + #else + # include /* O_CREAT and O_EXCL */ + # if defined(HAVE_SEM_OPEN) && !defined(POSIX_SEMAPHORES_NOT_ENABLED) +diff --git a/Modules/_winapi.c b/Modules/_winapi.c +index f6bb07f..56f3306 100644 +--- a/Modules/_winapi.c ++++ b/Modules/_winapi.c +@@ -41,7 +41,9 @@ + + #define WINDOWS_LEAN_AND_MEAN + #include "windows.h" ++#if defined(Py_DEBUG) + #include ++#endif + #include "winreparse.h" + + #if defined(MS_WIN32) && !defined(MS_WIN64) +@@ -957,7 +959,7 @@ getattributelist(PyObject *obj, const char *name, AttributeList *attribute_list) + DWORD err; + BOOL result; + PyObject *value; +- Py_ssize_t handle_list_size; ++ Py_ssize_t handle_list_size = 0; + DWORD attribute_count = 0; + SIZE_T attribute_list_size = 0; + +diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c +index e5b96be..0e5ab45 100644 +--- a/Modules/_xxsubinterpretersmodule.c ++++ b/Modules/_xxsubinterpretersmodule.c +@@ -1765,7 +1765,7 @@ PyDoc_STRVAR(channelid_doc, + "A channel ID identifies a channel and may be used as an int."); + + static PyTypeObject ChannelIDtype = { +- PyVarObject_HEAD_INIT(&PyType_Type, 0) ++ PyVarObject_HEAD_INIT(NULL, 0) + "_xxsubinterpreters.ChannelID", /* tp_name */ + sizeof(channelid), /* tp_basicsize */ + 0, /* tp_itemsize */ +diff --git a/Modules/getpath.c b/Modules/getpath.c +index bc730fc..0419c2a 100644 +--- a/Modules/getpath.c ++++ b/Modules/getpath.c +@@ -54,6 +54,25 @@ + + /* HELPER FUNCTIONS for getpath.py */ + ++static PyObject * ++getpath_normpath(PyObject *Py_UNUSED(self), PyObject *args) ++{ ++ PyObject *r = NULL; ++ PyObject *pathobj; ++ wchar_t *path; ++ if (!PyArg_ParseTuple(args, "U", &pathobj)) { ++ return NULL; ++ } ++ Py_ssize_t len; ++ wchar_t *buffer = PyUnicode_AsWideCharString(pathobj, &len); ++ if (!buffer) { ++ return NULL; ++ } ++ r = PyUnicode_FromWideChar(_Py_normpath(buffer, len), -1); ++ PyMem_Free(buffer); ++ return r; ++} ++ + static PyObject * + getpath_abspath(PyObject *Py_UNUSED(self), PyObject *args) + { +@@ -88,6 +107,12 @@ getpath_basename(PyObject *Py_UNUSED(self), PyObject *args) + } + Py_ssize_t end = PyUnicode_GET_LENGTH(path); + Py_ssize_t pos = PyUnicode_FindChar(path, SEP, 0, end, -1); ++#ifdef ALTSEP ++ if (pos < 0) { ++ // try using altsep ++ pos = PyUnicode_FindChar(path, ALTSEP, 0, end, -1); ++ } ++#endif + if (pos < 0) { + return Py_NewRef(path); + } +@@ -104,6 +129,12 @@ getpath_dirname(PyObject *Py_UNUSED(self), PyObject *args) + } + Py_ssize_t end = PyUnicode_GET_LENGTH(path); + Py_ssize_t pos = PyUnicode_FindChar(path, SEP, 0, end, -1); ++#ifdef ALTSEP ++ if (pos < 0) { ++ // try using altsep ++ pos = PyUnicode_FindChar(path, ALTSEP, 0, end, -1); ++ } ++#endif + if (pos < 0) { + return PyUnicode_FromStringAndSize(NULL, 0); + } +@@ -513,6 +544,7 @@ getpath_realpath(PyObject *Py_UNUSED(self) , PyObject *args) + + + static PyMethodDef getpath_methods[] = { ++ {"normpath", getpath_normpath, METH_VARARGS, NULL}, + {"abspath", getpath_abspath, METH_VARARGS, NULL}, + {"basename", getpath_basename, METH_VARARGS, NULL}, + {"dirname", getpath_dirname, METH_VARARGS, NULL}, +@@ -884,6 +916,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 +@@ -910,6 +947,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 + ) { +diff --git a/Modules/getpath.py b/Modules/getpath.py +index 74f918c..c98cb1f 100644 +--- a/Modules/getpath.py ++++ b/Modules/getpath.py +@@ -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(...) +@@ -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 +@@ -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' +@@ -199,6 +220,7 @@ + WINREG_KEY = f'SOFTWARE\\Python\\PythonCore\\{PYWINVER}\\PythonPath' + DELIM = ';' + SEP = '\\' ++ ALTSEP = '/' + + + # ****************************************************************************** +@@ -211,6 +233,8 @@ def search_up(prefix, *landmarks, test=isfile): + return prefix + prefix = dirname(prefix) + ++def _normpath(p): ++ return normpath(p) if p is not None else None + + # ****************************************************************************** + # READ VARIABLES FROM config +@@ -263,10 +287,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. +@@ -497,15 +521,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) +@@ -552,6 +576,9 @@ def search_up(prefix, *landmarks, test=isfile): + # First try to detect prefix by looking alongside our runtime library, if known + if library and not prefix: + library_dir = dirname(library) ++ if os_name == 'nt' and is_mingw: ++ # QUIRK: On Windows, mingw Python DLLs are in the bin directory ++ library_dir = joinpath(library_dir, '..') + if ZIP_LANDMARK: + if os_name == 'nt': + # QUIRK: Windows does not search up for ZIP file +@@ -597,7 +624,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) +@@ -607,7 +634,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: +@@ -645,7 +672,7 @@ def search_up(prefix, *landmarks, test=isfile): + + if py_setpath: + # If Py_SetPath was called then it overrides any existing search path +- config['module_search_paths'] = py_setpath.split(DELIM) ++ config['module_search_paths'] = [_normpath(p) for p in py_setpath.split(DELIM)] + config['module_search_paths_set'] = 1 + + elif not pythonpath_was_set: +@@ -660,7 +687,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) +@@ -673,7 +700,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 +@@ -714,7 +741,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) +@@ -732,7 +759,7 @@ def search_up(prefix, *landmarks, test=isfile): + if platstdlib_dir: + pythonpath.append(platstdlib_dir) + +- config['module_search_paths'] = pythonpath ++ config['module_search_paths'] = [_normpath(p) for p in pythonpath] + config['module_search_paths_set'] = 1 + + +@@ -742,8 +769,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 + + +@@ -767,23 +794,23 @@ def search_up(prefix, *landmarks, test=isfile): + warn("unsupported 'import' line in ._pth file") + else: + pythonpath.append(joinpath(pth_dir, line)) +- config['module_search_paths'] = pythonpath ++ config['module_search_paths'] = [_normpath(p) for p in pythonpath] + config['module_search_paths_set'] = 1 + + # ****************************************************************************** + # UPDATE config FROM CALCULATED VALUES + # ****************************************************************************** + +-config['program_name'] = program_name +-config['home'] = home +-config['executable'] = executable +-config['base_executable'] = base_executable +-config['prefix'] = prefix +-config['exec_prefix'] = exec_prefix +-config['base_prefix'] = base_prefix or prefix +-config['base_exec_prefix'] = base_exec_prefix or exec_prefix ++config['program_name'] = _normpath(program_name) ++config['home'] = _normpath(home) ++config['executable'] = _normpath(executable) ++config['base_executable'] = _normpath(base_executable) ++config['prefix'] = _normpath(prefix) ++config['exec_prefix'] = _normpath(exec_prefix) ++config['base_prefix'] = _normpath(base_prefix or prefix) ++config['base_exec_prefix'] = _normpath(base_exec_prefix or exec_prefix) + +-config['platlibdir'] = platlibdir ++config['platlibdir'] = _normpath(platlibdir) + # test_embed expects empty strings, not None +-config['stdlib_dir'] = stdlib_dir or '' +-config['platstdlib_dir'] = platstdlib_dir or '' ++config['stdlib_dir'] = _normpath(stdlib_dir or '') ++config['platstdlib_dir'] = _normpath(platstdlib_dir or '') +diff --git a/Modules/main.c b/Modules/main.c +index 6904e3f..5c8708e 100644 +--- a/Modules/main.c ++++ b/Modules/main.c +@@ -7,6 +7,7 @@ + #include "pycore_pathconfig.h" // _PyPathConfig_ComputeSysPath0() + #include "pycore_pylifecycle.h" // _Py_PreInitializeFromPyArgv() + #include "pycore_pystate.h" // _PyInterpreterState_GET() ++#include "iscygpty.h" + + /* Includes for exit_sigint() */ + #include // perror() +@@ -92,7 +93,7 @@ static inline int config_run_code(const PyConfig *config) + static int + stdin_is_interactive(const PyConfig *config) + { +- return (isatty(fileno(stdin)) || config->interactive); ++ return (isatty(fileno(stdin)) || config->interactive || is_cygpty(fileno(stdin))); + } + + +diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c +index 8d09f54..cc668df 100644 +--- a/Modules/posixmodule.c ++++ b/Modules/posixmodule.c +@@ -55,6 +55,7 @@ + #ifdef __ANDROID__ + # undef HAVE_FACCESSAT + #endif ++#include "iscygpty.h" + + #include // ctermid() + #include // system() +@@ -355,6 +356,32 @@ corresponding Unix manual entries for more information on calls."); + # define HAVE_CWAIT 1 + # define HAVE_FSYNC 1 + # define fsync _commit ++# elif defined(__MINGW32__) /* GCC for windows hosts */ ++/* getlogin is detected by configure on mingw-w64 */ ++# undef HAVE_GETLOGIN ++/* opendir is detected by configure on mingw-w64, and for some reason ++things don't work as expected. For example, os.listdir always returns ++the cwd's directory listing instead of the one specified. By ++un-defining, this, os.listdir will use the one which uses native ++windows API. */ ++# undef HAVE_OPENDIR ++/*# define HAVE_GETCWD 1 - detected by configure*/ ++# define HAVE_GETPPID 1 ++# define HAVE_GETLOGIN 1 ++# define HAVE_SPAWNV 1 ++# define HAVE_WSPAWNV 1 ++# define HAVE_WEXECV 1 ++/*# define HAVE_EXECV 1 - detected by configure*/ ++# define HAVE_PIPE 1 ++# define HAVE_POPEN 1 ++# define HAVE_SYSTEM 1 ++# define HAVE_CWAIT 1 ++# define HAVE_FSYNC 1 ++# define fsync _commit ++# include ++# ifndef _MAX_ENV ++# define _MAX_ENV 32767 ++# endif + # endif /* _MSC_VER */ + #endif /* ! __WATCOMC__ || __QNX__ */ + +@@ -433,7 +460,7 @@ extern char *ctermid_r(char *); + # endif + #endif + +-#ifdef _MSC_VER ++#ifdef MS_WINDOWS + # ifdef HAVE_DIRECT_H + # include + # endif +@@ -444,7 +471,7 @@ extern char *ctermid_r(char *); + # include + # endif + # include +-#endif /* _MSC_VER */ ++#endif /* MS_WINDOWS */ + + #ifndef MAXPATHLEN + # if defined(PATH_MAX) && PATH_MAX > 1024 +@@ -1598,9 +1625,9 @@ win32_get_reparse_tag(HANDLE reparse_point_handle, ULONG *reparse_tag) + ** man environ(7). + */ + #include +-#elif !defined(_MSC_VER) && (!defined(__WATCOMC__) || defined(__QNX__) || defined(__VXWORKS__)) ++#elif !defined(MS_WINDOWS) && (!defined(__WATCOMC__) || defined(__QNX__) || defined(__VXWORKS__)) + extern char **environ; +-#endif /* !_MSC_VER */ ++#endif /* !MS_WINDOWS */ + + static PyObject * + convertenviron(void) +@@ -3820,6 +3847,7 @@ posix_getcwd(int use_bytes) + return PyErr_SetFromWindowsErr(0); + } + ++ Py_NormalizeSepsW(wbuf2); + PyObject *resobj = PyUnicode_FromWideChar(wbuf2, len); + if (wbuf2 != wbuf) { + PyMem_RawFree(wbuf2); +@@ -4424,6 +4452,7 @@ os__getfinalpathname_impl(PyObject *module, path_t *path) + target_path = tmp; + } + ++ Py_NormalizeSepsW(target_path); + result = PyUnicode_FromWideChar(target_path, result_length); + if (result && path->narrow) { + Py_SETREF(result, PyUnicode_EncodeFSDefault(result)); +@@ -5507,7 +5536,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, + /*[clinic end generated code: output=cfcac69d027b82cf input=2fbd62a2f228f8f4]*/ + { + #ifdef MS_WINDOWS +- HANDLE hFile; ++ HANDLE hFile = 0; + FILETIME atime, mtime; + #else + int result; +@@ -10234,7 +10263,7 @@ os_isatty_impl(PyObject *module, int fd) + int return_value; + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH +- return_value = isatty(fd); ++ return_value = isatty(fd) || is_cygpty(fd); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + return return_value; +@@ -14722,7 +14751,7 @@ os__add_dll_directory_impl(PyObject *module, path_t *path) + loaded. */ + Py_BEGIN_ALLOW_THREADS + if (!(hKernel32 = GetModuleHandleW(L"kernel32")) || +- !(AddDllDirectory = (PAddDllDirectory)GetProcAddress( ++ !(AddDllDirectory = (PAddDllDirectory)(void *)GetProcAddress( + hKernel32, "AddDllDirectory")) || + !(cookie = (*AddDllDirectory)(path->wide))) { + err = GetLastError(); +@@ -14772,7 +14801,7 @@ os__remove_dll_directory_impl(PyObject *module, PyObject *cookie) + loaded. */ + Py_BEGIN_ALLOW_THREADS + if (!(hKernel32 = GetModuleHandleW(L"kernel32")) || +- !(RemoveDllDirectory = (PRemoveDllDirectory)GetProcAddress( ++ !(RemoveDllDirectory = (PRemoveDllDirectory)(void *)GetProcAddress( + hKernel32, "RemoveDllDirectory")) || + !(*RemoveDllDirectory)(cookieValue)) { + err = GetLastError(); +diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c +index 4eea928..6be3582 100644 +--- a/Modules/selectmodule.c ++++ b/Modules/selectmodule.c +@@ -146,9 +146,9 @@ seq2set(PyObject *seq, fd_set *set, pylist fd2obj[FD_SETSIZE + 1]) + v = PyObject_AsFileDescriptor( o ); + if (v == -1) goto finally; + +-#if defined(_MSC_VER) ++#if defined(MS_WIN32) + max = 0; /* not used for Win32 */ +-#else /* !_MSC_VER */ ++#else /* !MS_WIN32 */ + if (!_PyIsSelectable_fd(v)) { + PyErr_SetString(PyExc_ValueError, + "filedescriptor out of range in select()"); +@@ -156,7 +156,7 @@ seq2set(PyObject *seq, fd_set *set, pylist fd2obj[FD_SETSIZE + 1]) + } + if (v > max) + max = v; +-#endif /* _MSC_VER */ ++#endif /* MS_WIN32 */ + FD_SET(v, set); + + /* add object and its file descriptor to the list */ +diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c +index 65d0e10..d1bfe3d 100644 +--- a/Modules/socketmodule.c ++++ b/Modules/socketmodule.c +@@ -274,7 +274,7 @@ shutdown(how) -- shut down traffic in one or both directions\n\ + # endif + + /* Macros based on the IPPROTO enum, see: https://bugs.python.org/issue29515 */ +-#ifdef MS_WINDOWS ++#ifdef _MSC_VER + #define IPPROTO_ICMP IPPROTO_ICMP + #define IPPROTO_IGMP IPPROTO_IGMP + #define IPPROTO_GGP IPPROTO_GGP +@@ -305,7 +305,7 @@ shutdown(how) -- shut down traffic in one or both directions\n\ + #define IPPROTO_PGM IPPROTO_PGM // WinSock2 only + #define IPPROTO_L2TP IPPROTO_L2TP // WinSock2 only + #define IPPROTO_SCTP IPPROTO_SCTP // WinSock2 only +-#endif /* MS_WINDOWS */ ++#endif /* _MSC_VER */ + + /* Provides the IsWindows7SP1OrGreater() function */ + #include +@@ -404,6 +404,10 @@ remove_unusable_flags(PyObject *m) + /* Do not include addrinfo.h for MSVC7 or greater. 'addrinfo' and + * EAI_* constants are defined in (the already included) ws2tcpip.h. + */ ++#elif defined(__MINGW32__) ++ /* Do not include addrinfo.h as minimum supported version is ++ * _WIN32_WINNT >= WindowsXP(0x0501) ++ */ + #else + # include "addrinfo.h" + #endif +diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h +index 1b35b11..cff1f1d 100644 +--- a/Modules/socketmodule.h ++++ b/Modules/socketmodule.h +@@ -68,8 +68,10 @@ struct SOCKADDR_BTH_REDEF { + */ + # ifdef SIO_GET_MULTICAST_FILTER + # include /* for SIO_RCVALL */ ++#ifndef __MINGW32__ /* resolve by configure */ + # define HAVE_ADDRINFO + # define HAVE_SOCKADDR_STORAGE ++#endif + # define HAVE_GETADDRINFO + # define HAVE_GETNAMEINFO + # define ENABLE_IPV6 +diff --git a/Modules/timemodule.c b/Modules/timemodule.c +index 18f9ddb..4edc2e7 100644 +--- a/Modules/timemodule.c ++++ b/Modules/timemodule.c +@@ -783,7 +783,7 @@ time_strftime(PyObject *module, PyObject *args) + return NULL; + } + +-#if defined(_MSC_VER) || (defined(__sun) && defined(__SVR4)) || defined(_AIX) || defined(__VXWORKS__) ++#if defined(MS_WINDOWS) || (defined(__sun) && defined(__SVR4)) || defined(_AIX) || defined(__VXWORKS__) + if (buf.tm_year + 1900 < 1 || 9999 < buf.tm_year + 1900) { + PyErr_SetString(PyExc_ValueError, + "strftime() requires year in [1; 9999]"); +diff --git a/Objects/fileobject.c b/Objects/fileobject.c +index ffe55eb..1d73c09 100644 +--- a/Objects/fileobject.c ++++ b/Objects/fileobject.c +@@ -3,6 +3,7 @@ + #define PY_SSIZE_T_CLEAN + #include "Python.h" + #include "pycore_call.h" // _PyObject_CallNoArgs() ++#include "iscygpty.h" + #include "pycore_runtime.h" // _PyRuntime + + #if defined(HAVE_GETC_UNLOCKED) && !defined(_Py_MEMORY_SANITIZER) +@@ -387,7 +388,7 @@ stdprinter_isatty(PyStdPrinter_Object *self, PyObject *Py_UNUSED(ignored)) + } + + Py_BEGIN_ALLOW_THREADS +- res = isatty(self->fd); ++ res = isatty(self->fd) || is_cygpty(self->fd); + Py_END_ALLOW_THREADS + + return PyBool_FromLong(res); +diff --git a/PC/_testconsole.c b/PC/_testconsole.c +index a830883..52aca33 100644 +--- a/PC/_testconsole.c ++++ b/PC/_testconsole.c +@@ -10,7 +10,7 @@ + #ifdef MS_WINDOWS + + #include "pycore_fileutils.h" // _Py_get_osfhandle() +-#include "..\modules\_io\_iomodule.h" ++#include "../Modules/_io/_iomodule.h" + + #define WIN32_LEAN_AND_MEAN + #include +@@ -108,7 +108,7 @@ _testconsole_read_output_impl(PyObject *module, PyObject *file) + Py_RETURN_NONE; + } + +-#include "clinic\_testconsole.c.h" ++#include "clinic/_testconsole.c.h" + + PyMethodDef testconsole_methods[] = { + _TESTCONSOLE_WRITE_INPUT_METHODDEF +diff --git a/PC/launcher.c b/PC/launcher.c +index da566a1..09ac7d9 100644 +--- a/PC/launcher.c ++++ b/PC/launcher.c +@@ -155,12 +155,12 @@ static wchar_t * get_env(wchar_t * key) + #else + #if defined(_WINDOWS) + +-#define PYTHON_EXECUTABLE L"pythonw.exe" ++#define PYTHON_EXECUTABLE PYTHON_EXECUTABLE_WITH_VERSION + #define EXECUTABLEPATH_VALUE L"WindowedExecutablePath" + + #else + +-#define PYTHON_EXECUTABLE L"python.exe" ++#define PYTHON_EXECUTABLE PYTHON_EXECUTABLE_WITH_VERSION + #define EXECUTABLEPATH_VALUE L"ExecutablePath" + + #endif +@@ -925,7 +925,7 @@ static COMMAND path_command; + static COMMAND * find_on_path(wchar_t * name) + { + wchar_t * pathext; +- size_t varsize; ++ size_t requiredSize; + wchar_t * context = NULL; + wchar_t * extension; + COMMAND * result = NULL; +@@ -942,18 +942,23 @@ static COMMAND * find_on_path(wchar_t * name) + } + else { + /* No extension - search using registered extensions. */ +- rc = _wdupenv_s(&pathext, &varsize, L"PATHEXT"); +- if (rc == 0) { +- extension = wcstok_s(pathext, L";", &context); +- while (extension) { +- len = SearchPathW(NULL, name, extension, MSGSIZE, path_command.value, NULL); +- if (len) { +- result = &path_command; +- break; ++ _wgetenv_s(&requiredSize, NULL, 0, L"PATHEXT"); ++ if (requiredSize > 0) { ++ pathext = (wchar_t *)malloc(requiredSize * sizeof(wchar_t)); ++ /* No extension - search using registered extensions. */ ++ rc = _wgetenv_s(&requiredSize, pathext, requiredSize, L"PATHEXT"); ++ if (rc == 0) { ++ extension = wcstok_s(pathext, L";", &context); ++ while (extension) { ++ len = SearchPathW(NULL, name, extension, MSGSIZE, path_command.value, NULL); ++ if (len) { ++ result = &path_command; ++ break; ++ } ++ extension = wcstok_s(NULL, L";", &context); + } +- extension = wcstok_s(NULL, L";", &context); ++ free(pathext); + } +- free(pathext); + } + } + return result; +@@ -1913,7 +1918,7 @@ process(int argc, wchar_t ** argv) + if (_wfopen_s(&f, venv_cfg_path, L"r")) { + error(RC_BAD_VENV_CFG, L"Cannot read '%ls'", venv_cfg_path); + } +- cb = fread_s(buffer, sizeof(buffer), sizeof(buffer[0]), ++ cb = fread(buffer, sizeof(buffer[0]), + sizeof(buffer) / sizeof(buffer[0]), f); + fclose(f); + +diff --git a/PC/msvcrtmodule.c b/PC/msvcrtmodule.c +index 1f78d99..0f2da80 100644 +--- a/PC/msvcrtmodule.c ++++ b/PC/msvcrtmodule.c +@@ -22,7 +22,9 @@ + #include + #include + #include ++#ifdef _DEBUG + #include ++#endif + #include + + #ifdef _MSC_VER +diff --git a/PC/pylauncher.rc b/PC/pylauncher.rc +index 1186264..84b2917 100644 +--- a/PC/pylauncher.rc ++++ b/PC/pylauncher.rc +@@ -12,17 +12,17 @@ + 1 RT_MANIFEST "python.manifest" + + #if defined(PY_ICON) +-1 ICON DISCARDABLE "icons\python.ico" ++1 ICON DISCARDABLE "icons/python.ico" + #elif defined(PYW_ICON) +-1 ICON DISCARDABLE "icons\pythonw.ico" ++1 ICON DISCARDABLE "icons/pythonw.ico" + #else +-1 ICON DISCARDABLE "icons\launcher.ico" +-2 ICON DISCARDABLE "icons\py.ico" +-3 ICON DISCARDABLE "icons\pyc.ico" +-4 ICON DISCARDABLE "icons\pyd.ico" +-5 ICON DISCARDABLE "icons\python.ico" +-6 ICON DISCARDABLE "icons\pythonw.ico" +-7 ICON DISCARDABLE "icons\setup.ico" ++1 ICON DISCARDABLE "icons/launcher.ico" ++2 ICON DISCARDABLE "icons/py.ico" ++3 ICON DISCARDABLE "icons/pyc.ico" ++4 ICON DISCARDABLE "icons/pyd.ico" ++5 ICON DISCARDABLE "icons/python.ico" ++6 ICON DISCARDABLE "icons/pythonw.ico" ++7 ICON DISCARDABLE "icons/setup.ico" + #endif + + 1 USAGE "launcher-usage.txt" +@@ -64,4 +64,4 @@ BEGIN + BEGIN + VALUE "Translation", 0x0, 1200 + END +-END +\ No newline at end of file ++END +diff --git a/PC/python3dll.c b/PC/python3dll.c +index 50e7a96..8b524fd 100755 +--- a/PC/python3dll.c ++++ b/PC/python3dll.c +@@ -3,6 +3,7 @@ + + /* Generated by Tools/scripts/stable_abi.py */ + ++#ifdef _MSC_VER + #ifdef _M_IX86 + #define DECORATE "_" + #else +@@ -13,6 +14,13 @@ + __pragma(comment(linker, "/EXPORT:" DECORATE #name "=" PYTHON_DLL_NAME "." #name)) + #define EXPORT_DATA(name) \ + __pragma(comment(linker, "/EXPORT:" DECORATE #name "=" PYTHON_DLL_NAME "." #name ",DATA")) ++#else ++// XXX: Why do we need the .dll extension and no DECORATE compared to the MSVC case? ++#define EXPORT_FUNC(name) \ ++ asm(".section .drectve\n\t.ascii \" -export:" #name "=\\\"" PYTHON_DLL_NAME "." #name "\\\" \""); ++#define EXPORT_DATA(name) \ ++ asm(".section .drectve\n\t.ascii \" -export:" #name "=\\\"" PYTHON_DLL_NAME "." #name "\\\",DATA \""); ++#endif + + EXPORT_FUNC(_Py_BuildValue_SizeT) + EXPORT_FUNC(_Py_CheckRecursiveCall) +diff --git a/PC/python_exe.rc b/PC/python_exe.rc +index c3d3bff..dde0e53 100644 +--- a/PC/python_exe.rc ++++ b/PC/python_exe.rc +@@ -12,7 +12,7 @@ + // current versions of Windows. + 1 RT_MANIFEST "python.manifest" + +-1 ICON DISCARDABLE "icons\python.ico" ++1 ICON DISCARDABLE "icons/python.ico" + + + ///////////////////////////////////////////////////////////////////////////// +diff --git a/PC/pythonw_exe.rc b/PC/pythonw_exe.rc +index 38570b7..7ce1043 100644 +--- a/PC/pythonw_exe.rc ++++ b/PC/pythonw_exe.rc +@@ -12,7 +12,7 @@ + // current versions of Windows. + 1 RT_MANIFEST "python.manifest" + +-1 ICON DISCARDABLE "icons\pythonw.ico" ++1 ICON DISCARDABLE "icons/pythonw.ico" + + + ///////////////////////////////////////////////////////////////////////////// +diff --git a/PC/winreg.c b/PC/winreg.c +index f668cf3..08548a7 100644 +--- a/PC/winreg.c ++++ b/PC/winreg.c +@@ -18,6 +18,25 @@ + #include "structmember.h" // PyMemberDef + #include + ++#ifndef SIZEOF_HKEY ++/* used only here */ ++#if defined(MS_WIN64) ++# define SIZEOF_HKEY 8 ++#elif defined(MS_WIN32) ++# define SIZEOF_HKEY 4 ++#else ++# error "SIZEOF_HKEY is not defined" ++#endif ++#endif ++ ++#ifndef REG_LEGAL_CHANGE_FILTER ++#define REG_LEGAL_CHANGE_FILTER (\ ++ REG_NOTIFY_CHANGE_NAME |\ ++ REG_NOTIFY_CHANGE_ATTRIBUTES |\ ++ REG_NOTIFY_CHANGE_LAST_SET |\ ++ REG_NOTIFY_CHANGE_SECURITY ) ++#endif ++ + static BOOL PyHKEY_AsHKEY(PyObject *ob, HKEY *pRes, BOOL bNoneOK); + static BOOL clinic_HKEY_converter(PyObject *ob, void *p); + static PyObject *PyHKEY_FromHKEY(HKEY h); +@@ -806,6 +825,7 @@ Reg2Py(BYTE *retDataBuf, DWORD retDataSize, DWORD typ) + case REG_BINARY: + /* ALSO handle ALL unknown data types here. Even if we can't + support it natively, we should handle the bits. */ ++ /* fallthrough */ + default: + if (retDataSize == 0) { + Py_INCREF(Py_None); +diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c +index e20bd53..c035b6b 100644 +--- a/Python/bltinmodule.c ++++ b/Python/bltinmodule.c +@@ -1,6 +1,7 @@ + /* Built-in functions */ + + #include "Python.h" ++#include "iscygpty.h" + #include + #include "pycore_ast.h" // _PyAST_Validate() + #include "pycore_call.h" // _PyObject_CallNoArgs() +@@ -2135,7 +2136,7 @@ builtin_input_impl(PyObject *module, PyObject *prompt) + Py_DECREF(tmp); + if (fd < 0 && PyErr_Occurred()) + return NULL; +- tty = fd == fileno(stdin) && isatty(fd); ++ tty = fd == fileno(stdin) && (isatty(fd) || is_cygpty(fd)); + } + if (tty) { + tmp = PyObject_CallMethodNoArgs(fout, &_Py_ID(fileno)); +@@ -2148,7 +2149,7 @@ builtin_input_impl(PyObject *module, PyObject *prompt) + Py_DECREF(tmp); + if (fd < 0 && PyErr_Occurred()) + return NULL; +- tty = fd == fileno(stdout) && isatty(fd); ++ tty = fd == fileno(stdout) && (isatty(fd) || is_cygpty(fd)); + } + } + +diff --git a/Python/dynamic_annotations.c b/Python/dynamic_annotations.c +index 7febaa0..70d5b3d 100644 +--- a/Python/dynamic_annotations.c ++++ b/Python/dynamic_annotations.c +@@ -27,7 +27,7 @@ + * Author: Kostya Serebryany + */ + +-#ifdef _MSC_VER ++#ifdef MS_WINDOWS + # include + #endif + +diff --git a/Python/dynload_win.c b/Python/dynload_win.c +index 5dc4095..fcb7321 100644 +--- a/Python/dynload_win.c ++++ b/Python/dynload_win.c +@@ -4,6 +4,7 @@ + #include "Python.h" + #include "pycore_fileutils.h" // _Py_add_relfile() + #include "pycore_pystate.h" // _PyInterpreterState_GET() ++#include "pycore_initconfig.h" + + #ifdef HAVE_DIRECT_H + #include +@@ -170,8 +171,7 @@ static char *GetPythonImport (HINSTANCE hModule) + Return whether the DLL was found. + */ + extern HMODULE PyWin_DLLhModule; +-static int +-_Py_CheckPython3(void) ++int _Py_CheckPython3(void) + { + static int python3_checked = 0; + static HANDLE hPython3; +@@ -224,7 +224,21 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix, + dl_funcptr p; + char funcname[258], *import_python; + +- _Py_CheckPython3(); ++ int use_legacy = 0; ++ DWORD load_library_flags = 0; ++ ++ _Py_get_env_flag(1, &use_legacy, "PYTHONLEGACYWINDOWSDLLLOADING"); ++ ++ if (use_legacy) { ++ load_library_flags = LOAD_WITH_ALTERED_SEARCH_PATH; ++ } else { ++ load_library_flags = LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | ++ LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR; ++ } ++ ++#ifdef _MSC_VER ++ _Py_CheckPython3(); ++#endif + + #if USE_UNICODE_WCHAR_CACHE + const wchar_t *wpathname = _PyUnicode_AsUnicode(pathname); +@@ -248,9 +262,7 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix, + AddDllDirectory function. We add SEARCH_DLL_LOAD_DIR to + ensure DLLs adjacent to the PYD are preferred. */ + Py_BEGIN_ALLOW_THREADS +- hDLL = LoadLibraryExW(wpathname, NULL, +- LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | +- LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); ++ hDLL = LoadLibraryExW(wpathname, NULL, load_library_flags); + Py_END_ALLOW_THREADS + #if !USE_UNICODE_WCHAR_CACHE + PyMem_Free(wpathname); +diff --git a/Python/fileutils.c b/Python/fileutils.c +index b310a6c..c575e5a 100644 +--- a/Python/fileutils.c ++++ b/Python/fileutils.c +@@ -1,4 +1,5 @@ + #include "Python.h" ++#include "iscygpty.h" + #include "pycore_fileutils.h" // fileutils definitions + #include "pycore_runtime.h" // _PyRuntime + #include "osdefs.h" // SEP +@@ -71,7 +72,7 @@ _Py_device_encoding(int fd) + int valid; + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH +- valid = isatty(fd); ++ valid = isatty(fd) || is_cygpty(fd); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + if (!valid) +@@ -1809,12 +1810,12 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held) + depending on heap usage). */ + if (gil_held) { + Py_BEGIN_ALLOW_THREADS +- if (isatty(fd)) { ++ if (isatty(fd) || is_cygpty(fd)) { + count = 32767; + } + Py_END_ALLOW_THREADS + } else { +- if (isatty(fd)) { ++ if (isatty(fd) || is_cygpty(fd)) { + count = 32767; + } + } +@@ -2004,19 +2005,31 @@ int + _Py_isabs(const wchar_t *path) + { + #ifdef MS_WINDOWS ++ // create a copy of path and replace all forward slashes with backslashes ++ // pathccskiproot does not handle forward slashes ++ wchar_t *path_copy = _wcsdup(path); ++ if (path_copy == NULL) { ++ return 0; ++ } ++ Py_NormalizeSepsPathcchW(path_copy); ++ + const wchar_t *tail; +- HRESULT hr = PathCchSkipRoot(path, &tail); +- if (FAILED(hr) || path == tail) { ++ HRESULT hr = PathCchSkipRoot(path_copy, &tail); ++ if (FAILED(hr) || path_copy == tail) { ++ free(path_copy); + return 0; + } +- if (tail == &path[1] && (path[0] == SEP || path[0] == ALTSEP)) { ++ if (tail == &path_copy[1] && (path_copy[0] == SEP || path_copy[0] == ALTSEP)) { + // Exclude paths with leading SEP ++ free(path_copy); + return 0; + } +- if (tail == &path[2] && path[1] == L':') { ++ if (tail == &path_copy[2] && path_copy[1] == L':') { + // Exclude drive-relative paths (e.g. C:filename.ext) ++ free(path_copy); + return 0; + } ++ free(path_copy); + return 1; + #else + return (path[0] == SEP); +@@ -2049,7 +2062,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; +@@ -2093,6 +2110,8 @@ join_relfile(wchar_t *buffer, size_t bufsize, + const wchar_t *dirname, const wchar_t *relfile) + { + #ifdef MS_WINDOWS ++ Py_NormalizeSepsPathcchW(dirname); ++ Py_NormalizeSepsPathcchW(relfile); + if (FAILED(PathCchCombineEx(buffer, bufsize, dirname, relfile, + PATHCCH_ALLOW_LONG_PATHS))) { + return -1; +@@ -2195,11 +2214,16 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) + 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)) + +@@ -2210,7 +2234,7 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) + path++; + } + p1 = p2 = minP2 = path; +- lastC = SEP; ++ lastC = sep; + } + #ifdef MS_WINDOWS + // Skip past drive segment and update minP2 +@@ -2224,13 +2248,13 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) + // 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; + } +@@ -2243,7 +2267,7 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) + *p2++ = *p1++; + *p2++ = *p1++; + minP2 = p2 - 1; // Absolute path has SEP at minP2 +- lastC = SEP; ++ lastC = sep; + } + #endif /* MS_WINDOWS */ + +@@ -2251,18 +2275,18 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) + 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]))) + { +@@ -2271,7 +2295,7 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) + *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 { +@@ -2282,7 +2306,7 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) + } else { + *p2++ = lastC = c; + } +- } else if (c == SEP) { ++ } else if (c == sep) { + } else { + *p2++ = lastC = c; + } +@@ -2292,7 +2316,7 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) + } + *p2 = L'\0'; + if (p2 != minP2) { +- while (--p2 != minP2 && *p2 == SEP) { ++ while (--p2 != minP2 && *p2 == sep) { + *p2 = L'\0'; + } + } else { +diff --git a/Python/frozenmain.c b/Python/frozenmain.c +index 8743e08..c6f5f19 100644 +--- a/Python/frozenmain.c ++++ b/Python/frozenmain.c +@@ -3,6 +3,7 @@ + #include "Python.h" + #include "pycore_runtime.h" // _PyRuntime_Initialize() + #include ++#include "iscygpty.h" + + #ifdef MS_WINDOWS + extern void PyWinFreeze_ExeInit(void); +@@ -71,7 +72,7 @@ Py_FrozenMain(int argc, char **argv) + sts = 0; + } + +- if (inspect && isatty((int)fileno(stdin))) { ++ if (inspect && (isatty((int)fileno(stdin)) || is_cygpty((int)fileno(stdin)))) + sts = PyRun_AnyFile(stdin, "") != 0; + } + +diff --git a/Python/getcompiler.c b/Python/getcompiler.c +index a5d2623..4b0b9b3 100644 +--- a/Python/getcompiler.c ++++ b/Python/getcompiler.c +@@ -7,10 +7,40 @@ + + // Note the __clang__ conditional has to come before the __GNUC__ one because + // clang pretends to be GCC. +-#if defined(__clang__) ++#if defined(__clang__) && !defined(_WIN32) + #define COMPILER "[Clang " __clang_version__ "]" + #elif defined(__GNUC__) +-#define COMPILER "[GCC " __VERSION__ "]" ++/* To not break compatibility with things that determine ++ CPU arch by calling get_build_version in msvccompiler.py ++ (such as NumPy) add "32 bit" or "64 bit (AMD64)" on Windows ++ and also use a space as a separator rather than a newline. */ ++#if defined(_WIN32) ++#define COMP_SEP " " ++#if defined(__x86_64__) ++#define ARCH_SUFFIX " 64 bit (AMD64)" ++#elif defined(__aarch64__) ++#define ARCH_SUFFIX " 64 bit (ARM64)" ++#elif defined(__arm__) ++#define ARCH_SUFFIX " 32 bit (ARM)" ++#else ++#define ARCH_SUFFIX " 32 bit" ++#endif ++#else ++#define COMP_SEP "\n" ++#define ARCH_SUFFIX "" ++#endif ++#if defined(__clang__) ++#define str(x) #x ++#define xstr(x) str(x) ++#define COMPILER COMP_SEP "[GCC Clang " xstr(__clang_major__) "." \ ++ xstr(__clang_minor__) "." xstr(__clang_patchlevel__) ARCH_SUFFIX "]" ++#else ++#if defined(_UCRT) ++#define COMPILER COMP_SEP "[GCC UCRT " __VERSION__ ARCH_SUFFIX "]" ++#else ++#define COMPILER COMP_SEP "[GCC " __VERSION__ ARCH_SUFFIX "]" ++#endif ++#endif + // Generic fallbacks. + #elif defined(__cplusplus) + #define COMPILER "[C++]" +diff --git a/Python/initconfig.c b/Python/initconfig.c +index d81cbaf..c54d238 100644 +--- a/Python/initconfig.c ++++ b/Python/initconfig.c +@@ -176,7 +176,7 @@ static const char usage_envvars[] = + "PYTHONVERBOSE : trace import statements (-v)\n" + "PYTHONWARNINGS=arg : warning control (-W arg)\n"; + +-#if defined(MS_WINDOWS) ++#if defined(_MSC_VER) + # define PYTHONHOMEHELP "\\python{major}{minor}" + #else + # define PYTHONHOMEHELP "/lib/pythonX.X" +diff --git a/Python/iscygpty.c b/Python/iscygpty.c +new file mode 100644 +index 0000000..722f88f +--- /dev/null ++++ b/Python/iscygpty.c +@@ -0,0 +1,185 @@ ++/* ++ * iscygpty.c -- part of ptycheck ++ * https://github.com/k-takata/ptycheck ++ * ++ * Copyright (c) 2015-2017 K.Takata ++ * ++ * You can redistribute it and/or modify it under the terms of either ++ * the MIT license (as described below) or the Vim license. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining ++ * a copy of this software and associated documentation files (the ++ * "Software"), to deal in the Software without restriction, including ++ * without limitation the rights to use, copy, modify, merge, publish, ++ * distribute, sublicense, and/or sell copies of the Software, and to ++ * permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ++ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ++ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ++ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ++ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ */ ++ ++#ifdef _WIN32 ++ ++#include ++#include ++#include ++#include ++ ++#ifdef USE_FILEEXTD ++/* VC 7.1 or earlier doesn't support SAL. */ ++# if !defined(_MSC_VER) || (_MSC_VER < 1400) ++# define __out ++# define __in ++# define __in_opt ++# endif ++/* Win32 FileID API Library: ++ * http://www.microsoft.com/en-us/download/details.aspx?id=22599 ++ * Needed for WinXP. */ ++# include ++#else /* USE_FILEEXTD */ ++/* VC 8 or earlier. */ ++# if defined(_MSC_VER) && (_MSC_VER < 1500) ++# ifdef ENABLE_STUB_IMPL ++# define STUB_IMPL ++# else ++# error "Win32 FileID API Library is required for VC2005 or earlier." ++# endif ++# endif ++#endif /* USE_FILEEXTD */ ++ ++ ++#include "iscygpty.h" ++ ++//#define USE_DYNFILEID ++#ifdef USE_DYNFILEID ++typedef BOOL (WINAPI *pfnGetFileInformationByHandleEx)( ++ HANDLE hFile, ++ FILE_INFO_BY_HANDLE_CLASS FileInformationClass, ++ LPVOID lpFileInformation, ++ DWORD dwBufferSize ++); ++static pfnGetFileInformationByHandleEx pGetFileInformationByHandleEx = NULL; ++ ++# ifndef USE_FILEEXTD ++static BOOL WINAPI stub_GetFileInformationByHandleEx( ++ HANDLE hFile, ++ FILE_INFO_BY_HANDLE_CLASS FileInformationClass, ++ LPVOID lpFileInformation, ++ DWORD dwBufferSize ++ ) ++{ ++ return FALSE; ++} ++# endif ++ ++static void setup_fileid_api(void) ++{ ++ if (pGetFileInformationByHandleEx != NULL) { ++ return; ++ } ++ pGetFileInformationByHandleEx = (pfnGetFileInformationByHandleEx) ++ GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), ++ "GetFileInformationByHandleEx"); ++ if (pGetFileInformationByHandleEx == NULL) { ++# ifdef USE_FILEEXTD ++ pGetFileInformationByHandleEx = GetFileInformationByHandleEx; ++# else ++ pGetFileInformationByHandleEx = stub_GetFileInformationByHandleEx; ++# endif ++ } ++} ++#else ++# define pGetFileInformationByHandleEx GetFileInformationByHandleEx ++# define setup_fileid_api() ++#endif ++ ++ ++#define is_wprefix(s, prefix) \ ++ (wcsncmp((s), (prefix), sizeof(prefix) / sizeof(WCHAR) - 1) == 0) ++ ++/* Check if the fd is a cygwin/msys's pty. */ ++int is_cygpty(int fd) ++{ ++#ifdef STUB_IMPL ++ return 0; ++#else ++ HANDLE h; ++ int size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * (MAX_PATH - 1); ++ FILE_NAME_INFO *nameinfo; ++ WCHAR *p = NULL; ++ ++ setup_fileid_api(); ++ ++ h = (HANDLE) _get_osfhandle(fd); ++ if (h == INVALID_HANDLE_VALUE) { ++ return 0; ++ } ++ /* Cygwin/msys's pty is a pipe. */ ++ if (GetFileType(h) != FILE_TYPE_PIPE) { ++ return 0; ++ } ++ nameinfo = malloc(size + sizeof(WCHAR)); ++ if (nameinfo == NULL) { ++ return 0; ++ } ++ /* Check the name of the pipe: ++ * '\{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master' */ ++ if (pGetFileInformationByHandleEx(h, FileNameInfo, nameinfo, size)) { ++ nameinfo->FileName[nameinfo->FileNameLength / sizeof(WCHAR)] = L'\0'; ++ p = nameinfo->FileName; ++ if (is_wprefix(p, L"\\cygwin-")) { /* Cygwin */ ++ p += 8; ++ } else if (is_wprefix(p, L"\\msys-")) { /* MSYS and MSYS2 */ ++ p += 6; ++ } else { ++ p = NULL; ++ } ++ if (p != NULL) { ++ while (*p && isxdigit(*p)) /* Skip 16-digit hexadecimal. */ ++ ++p; ++ if (is_wprefix(p, L"-pty")) { ++ p += 4; ++ } else { ++ p = NULL; ++ } ++ } ++ if (p != NULL) { ++ while (*p && isdigit(*p)) /* Skip pty number. */ ++ ++p; ++ if (is_wprefix(p, L"-from-master")) { ++ //p += 12; ++ } else if (is_wprefix(p, L"-to-master")) { ++ //p += 10; ++ } else { ++ p = NULL; ++ } ++ } ++ } ++ free(nameinfo); ++ return (p != NULL); ++#endif /* STUB_IMPL */ ++} ++ ++/* Check if at least one cygwin/msys pty is used. */ ++int is_cygpty_used(void) ++{ ++ int fd, ret = 0; ++ ++ for (fd = 0; fd < 3; fd++) { ++ ret |= is_cygpty(fd); ++ } ++ return ret; ++} ++ ++#endif /* _WIN32 */ ++ ++/* vim: set ts=4 sw=4: */ +diff --git a/Python/pathconfig.c b/Python/pathconfig.c +index be0f97c..7eb9006 100644 +--- a/Python/pathconfig.c ++++ b/Python/pathconfig.c +@@ -2,7 +2,7 @@ + + #include "Python.h" + #include "marshal.h" // PyMarshal_ReadObjectFromString +-#include "osdefs.h" // DELIM ++#include "osdefs.h" // DELIM, SEP + #include "pycore_initconfig.h" + #include "pycore_fileutils.h" + #include "pycore_pathconfig.h" +@@ -18,6 +18,158 @@ + extern "C" { + #endif + ++#ifdef __MINGW32__ ++#define wcstok wcstok_s ++#include ++#endif ++ ++static int ++Py_StartsWithA(const char * str, const char * prefix) ++{ ++ while(*prefix) ++ { ++ if(*prefix++ != *str++) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static int ++Py_StartsWithW(const wchar_t * str, const wchar_t * prefix) ++{ ++ while(*prefix) ++ { ++ if(*prefix++ != *str++) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++char ++Py_GetSepA(const char *name) ++{ ++ static char sep = '\0'; ++#ifdef _WIN32 ++ /* https://msdn.microsoft.com/en-gb/library/windows/desktop/aa365247%28v=vs.85%29.aspx ++ * The "\\?\" prefix .. indicate that the path should be passed to the system with minimal ++ * modification, which means that you cannot use forward slashes to represent path separators ++ */ ++ if (name != NULL && Py_StartsWithA(name, "\\\\?\\") != 0) ++ { ++ return '\\'; ++ } ++#endif ++ if (sep != '\0') ++ return sep; ++#if defined(__MINGW32__) ++ char* msystem = getenv("MSYSTEM"); ++ if (msystem != NULL && strcmp(msystem, "") != 0) ++ sep = '/'; ++ else ++ sep = '\\'; ++#else ++ sep = SEP; ++#endif ++ return sep; ++} ++ ++static char ++Py_GetAltSepA(const char *name) ++{ ++ char sep = Py_GetSepA(name); ++ if (sep == '/') ++ return '\\'; ++ return '/'; ++} ++ ++void ++Py_NormalizeSepsA(char *name) ++{ ++ assert(name != NULL); ++ char sep = Py_GetSepA(name); ++ char altsep = Py_GetAltSepA(name); ++ char* seps; ++ if (name[0] != '\0' && name[1] == ':') { ++ name[0] = toupper(name[0]); ++ } ++ seps = strchr(name, altsep); ++ while(seps) { ++ *seps = sep; ++ seps = strchr(seps, altsep); ++ } ++} ++ ++wchar_t ++Py_GetSepW(const wchar_t *name) ++{ ++ static wchar_t sep = L'\0'; ++#ifdef _WIN32 ++ /* https://msdn.microsoft.com/en-gb/library/windows/desktop/aa365247%28v=vs.85%29.aspx ++ * The "\\?\" prefix .. indicate that the path should be passed to the system with minimal ++ * modification, which means that you cannot use forward slashes to represent path separators ++ */ ++ if (name != NULL && Py_StartsWithW(name, L"\\\\?\\") != 0) ++ { ++ return L'\\'; ++ } ++#endif ++ if (sep != L'\0') ++ return sep; ++#if defined(__MINGW32__) ++ char* msystem = getenv("MSYSTEM"); ++ if (msystem != NULL && strcmp(msystem, "") != 0) ++ sep = L'/'; ++ else ++ sep = L'\\'; ++#else ++ sep = SEP; ++#endif ++ return sep; ++} ++ ++wchar_t ++Py_GetAltSepW(const wchar_t *name) ++{ ++ char sep = Py_GetSepW(name); ++ if (sep == L'/') ++ return L'\\'; ++ return L'/'; ++} ++ ++void ++Py_NormalizeSepsW(wchar_t *name) ++{ ++ assert(name != NULL); ++ wchar_t sep = Py_GetSepW(name); ++ wchar_t altsep = Py_GetAltSepW(name); ++ wchar_t* seps; ++ if (name[0] != L'\0' && name[1] == L':') { ++ name[0] = towupper(name[0]); ++ } ++ seps = wcschr(name, altsep); ++ while(seps) { ++ *seps = sep; ++ seps = wcschr(seps, altsep); ++ } ++} ++ ++void ++Py_NormalizeSepsPathcchW(wchar_t *name) ++{ ++#ifdef MS_WINDOWS ++ assert(name != NULL); ++ wchar_t sep = '\\'; ++ wchar_t altsep = '/'; ++ wchar_t* seps; ++ seps = wcschr(name, altsep); ++ while(seps) { ++ *seps = sep; ++ seps = wcschr(seps, altsep); ++ } ++#endif ++} + + /* External interface */ + +@@ -317,6 +469,7 @@ _Py_SetProgramFullPath(const wchar_t *program_full_path) + if (has_value && _Py_path_config.program_full_path == NULL) { + path_out_of_memory(__func__); + } ++ Py_NormalizeSepsW(_Py_path_config.program_name); + } + + +@@ -509,7 +662,7 @@ _PyPathConfig_ComputeSysPath0(const PyWideStringList *argv, PyObject **path0_p) + } + #endif /* All others */ + +- PyObject *path0_obj = PyUnicode_FromWideChar(path0, n); ++ PyObject *path0_obj = PyUnicode_FromWideChar(_Py_normpath(path0, -1), n); + if (path0_obj == NULL) { + return -1; + } +diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c +index 9248e97..1d53e12 100644 +--- a/Python/pylifecycle.c ++++ b/Python/pylifecycle.c +@@ -31,6 +31,7 @@ + + extern void _PyIO_Fini(void); + ++#include "iscygpty.h" + #include // setlocale() + #include // getenv() + +@@ -2954,7 +2955,7 @@ Py_Exit(int sts) + int + Py_FdIsInteractive(FILE *fp, const char *filename) + { +- if (isatty((int)fileno(fp))) ++ if (isatty((int)fileno(fp)) || is_cygpty((int)fileno(fp))) + return 1; + if (!Py_InteractiveFlag) + return 0; +@@ -2967,7 +2968,7 @@ Py_FdIsInteractive(FILE *fp, const char *filename) + int + _Py_FdIsInteractive(FILE *fp, PyObject *filename) + { +- if (isatty((int)fileno(fp))) { ++ if (isatty((int)fileno(fp)) || is_cygpty((int)fileno(fp))) { + return 1; + } + if (!Py_InteractiveFlag) { +diff --git a/Python/sysmodule.c b/Python/sysmodule.c +index 8bab703..fed0adb 100644 +--- a/Python/sysmodule.c ++++ b/Python/sysmodule.c +@@ -43,7 +43,7 @@ Data members: + #include + #endif /* MS_WINDOWS */ + +-#ifdef MS_COREDLL ++#if defined(MS_WINDOWS) && defined(Py_ENABLE_SHARED) + extern void *PyWin_DLLhModule; + /* A string loaded from the DLL at startup: */ + extern const char *PyWin_DLLVersionString; +@@ -2923,7 +2923,7 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) + SET_SYS_FROM_STRING("byteorder", "little"); + #endif + +-#ifdef MS_COREDLL ++#if defined(MS_WINDOWS) && defined(Py_ENABLE_SHARED) + SET_SYS("dllhandle", PyLong_FromVoidPtr(PyWin_DLLhModule)); + SET_SYS_FROM_STRING("winver", PyWin_DLLVersionString); + #endif +diff --git a/Python/thread_nt.h b/Python/thread_nt.h +index 084bd58..f8a6765 100644 +--- a/Python/thread_nt.h ++++ b/Python/thread_nt.h +@@ -360,8 +360,9 @@ PyThread_release_lock(PyThread_type_lock aLock) + { + dprintf(("%lu: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); + +- if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock))) ++ if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock))) { + dprintf(("%lu: Could not PyThread_release_lock(%p) error: %ld\n", PyThread_get_thread_ident(), aLock, GetLastError())); ++ } + } + + /* minimum/maximum thread stack sizes supported */ +diff --git a/Python/traceback.c b/Python/traceback.c +index 7f47349..23fda62 100644 +--- a/Python/traceback.c ++++ b/Python/traceback.c +@@ -323,7 +323,7 @@ _Py_FindSourceFile(PyObject *filename, char* namebuf, size_t namelen, PyObject * + filepath = PyBytes_AS_STRING(filebytes); + + /* Search tail of filename in sys.path before giving up */ +- tail = strrchr(filepath, SEP); ++ tail = strrchr(filepath, Py_GetSepA(filepath)); + if (tail == NULL) + tail = filepath; + else +diff --git a/configure.ac b/configure.ac +index 1c25abd..5cf7085 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -202,9 +202,11 @@ AC_SUBST([FREEZE_MODULE]) + AC_SUBST([FREEZE_MODULE_DEPS]) + AC_SUBST([PYTHON_FOR_BUILD_DEPS]) + ++NATIVE_PYTHON_SEARCH_PATH_MINGW=`echo $host | grep -Eq 'mingw*' && echo "$MINGW_PREFIX/bin" || echo $PATH` + AC_CHECK_PROGS([PYTHON_FOR_REGEN], + [python$PACKAGE_VERSION python3.10 python3.9 python3.8 python3.7 python3.6 python3 python], +- [python3]) ++ [python3], ++ [$NATIVE_PYTHON_SEARCH_PATH_MINGW]) + AC_SUBST(PYTHON_FOR_REGEN) + + AC_MSG_CHECKING([Python for regen version]) +@@ -545,6 +547,9 @@ then + *-*-cygwin*) + ac_sys_system=Cygwin + ;; ++ *-*-mingw*) ++ ac_sys_system=MINGW ++ ;; + *-*-vxworks*) + ac_sys_system=VxWorks + ;; +@@ -580,6 +585,7 @@ then + linux*) MACHDEP="linux";; + cygwin*) MACHDEP="cygwin";; + darwin*) MACHDEP="darwin";; ++ mingw*) MACHDEP="win32";; + '') MACHDEP="unknown";; + esac + fi +@@ -605,6 +611,9 @@ if test "$cross_compiling" = yes; then + ;; + wasm32-*-* | wasm64-*-*) + _host_cpu=$host_cpu ++ ;; ++ *-*-mingw*) ++ _host_cpu= + ;; + *) + # for now, limit cross builds to known configurations +@@ -612,6 +621,14 @@ if test "$cross_compiling" = yes; then + AC_MSG_ERROR([cross build not supported for $host]) + esac + _PYTHON_HOST_PLATFORM="$MACHDEP${_host_cpu:+-$_host_cpu}" ++ ++ case "$host_os" in ++ mingw*) ++ # As sys.platform() return 'win32' to build python and extantions ++ # we will use 'mingw' (in setup.py and etc.) ++ _PYTHON_HOST_PLATFORM=mingw ++ ;; ++ esac + fi + + # Some systems cannot stand _XOPEN_SOURCE being defined at all; they +@@ -723,6 +740,65 @@ then + AC_DEFINE(_INCLUDE__STDC_A1_SOURCE, 1, Define to include mbstate_t for mbrtowc) + fi + ++# On 'semi-native' build systems (MSYS*/Cygwin targeting MinGW-w64) ++# _sysconfigdata.py will contain paths that are correct only in the ++# build environment. This means external modules will fail to build ++# without setting up the same env and also that the build of Python ++# itself will fail as the paths are not correct for the host tools. ++# ++# To work around these issues a set of _b2h variables are created: ++# prefix_b2h, srcdir_b2h, abs_srcdir_b2h ++# and abs_builddir_b2h ++# .. where b2h stands for build to host. sysconfig.py replaces path ++# prefixes matching the non-b2h versions with the b2h equivalents. ++# ++# (note this assumes the host compilers are native and *not* cross ++# - in the 'semi-native' scenario only that is.) ++ ++AC_DEFUN([ABS_PATH_HOST], ++[$1=$(cd $$2 && pwd) ++ case $build_os in ++ mingw*) ++ case $host_os in ++ mingw*) $1=$(cd $$2 && pwd -W) ;; ++ *) ;; ++ esac ++ ;; ++ cygwin*) ++ case $host_os in ++ mingw*) $1=$(cygpath -w -m $$2) ;; ++ *) ;; ++ esac ++ ;; ++ esac ++AC_SUBST([$1]) ++]) ++ ++AC_MSG_CHECKING(absolute host location of prefix) ++ABS_PATH_HOST([prefix_b2h],[prefix]) ++AC_MSG_RESULT([$prefix_b2h]) ++ ++AC_MSG_CHECKING(absolute host location of srcdir) ++ABS_PATH_HOST([srcdir_b2h],[srcdir]) ++AC_MSG_RESULT([$srcdir_b2h]) ++ ++AC_MSG_CHECKING(absolute host location of abs_srcdir) ++ABS_PATH_HOST([abs_srcdir_b2h],[srcdir]) ++AC_MSG_RESULT([$abs_srcdir_b2h]) ++ ++my_builddir=. ++AC_MSG_CHECKING(Absolute host location of abs_builddir) ++ABS_PATH_HOST([abs_builddir_b2h],[my_builddir]) ++AC_MSG_RESULT([$abs_builddir_b2h]) ++ ++AC_MSG_CHECKING([for init system calls]) ++AC_SUBST(INITSYS) ++case $host in ++ *-*-mingw*) INITSYS=nt;; ++ *) INITSYS=posix;; ++esac ++AC_MSG_RESULT([$INITSYS]) ++ + # Record the configure-time value of MACOSX_DEPLOYMENT_TARGET, + # it may influence the way we can build extensions, so distutils + # needs to check it +@@ -1163,6 +1239,28 @@ AC_CACHE_CHECK([for -Wl,--no-as-needed], [ac_cv_wl_no_as_needed], [ + ]) + AC_SUBST(NO_AS_NEEDED) + ++# initialize default configuration ++py_config= ++case $host in ++ *-*-mingw*) py_config=mingw ;; ++esac ++if test -n "$py_config" ; then ++ AC_MSG_NOTICE([loading configure defaults from .../Misc/config_$py_config"]) ++ . "$srcdir/Misc/config_$py_config" ++fi ++ ++# initialize defaults for cross-builds ++if test "$cross_compiling" = yes; then ++ py_config=$host_os ++ case $py_config in ++ mingw32*) py_config=mingw32 ;; ++ esac ++ if test -f "$srcdir/Misc/cross_$py_config" ; then ++ AC_MSG_NOTICE([loading cross defaults from .../Misc/cross_$py_config"]) ++ . "$srcdir/Misc/cross_$py_config" ++ fi ++fi ++ + AC_MSG_CHECKING([for the Android API level]) + cat > conftest.c <]],[[_beginthread(0, 0, 0);]]) ++ ], ++ [AC_MSG_RESULT([yes])], ++ [AC_MSG_ERROR([failed to link with nt-threads])]) ++fi ++ ++if test $with_nt_threads = yes ; then ++ dnl temporary default flag to avoid additional pthread checks ++ dnl and initilize other ac..thread flags to no ++ ac_cv_pthread_is_default=no ++ ac_cv_kthread=no ++ ac_cv_pthread=no ++ dnl ac_cv_kpthread is set to no if default is yes (see below) ++else + # On some compilers, pthreads are available without further options + # (e.g. MacOS X). On some of these systems, the compiler will not + # complain if unaccepted options are passed (e.g. gcc on Mac OS X). +@@ -2609,6 +2771,8 @@ int main(void){ + CC="$ac_save_cc"]) + fi + ++fi ++ + # If we have set a CC compiler flag for thread support then + # check if it works for CXX, too. + ac_cv_cxx_thread=no +@@ -2629,6 +2793,10 @@ elif test "$ac_cv_pthread" = "yes" + then + CXX="$CXX -pthread" + ac_cv_cxx_thread=yes ++elif test $with_nt_threads = yes ++then ++ dnl set to always to skip extra pthread check below ++ ac_cv_cxx_thread=always + fi + + if test $ac_cv_cxx_thread = yes +@@ -2663,11 +2831,11 @@ AC_DEFINE(STDC_HEADERS, 1, [Define to 1 if you have the ANSI C header files.]) + + # checks for header files + AC_CHECK_HEADERS([ \ +- alloca.h asm/types.h bluetooth.h conio.h crypt.h direct.h dlfcn.h endian.h errno.h fcntl.h grp.h \ ++ alloca.h asm/types.h bluetooth.h conio.h crypt.h direct.h endian.h errno.h fcntl.h grp.h \ + ieeefp.h io.h langinfo.h libintl.h libutil.h linux/auxvec.h sys/auxv.h linux/memfd.h \ + linux/random.h linux/soundcard.h \ +- linux/tipc.h linux/wait.h netdb.h netinet/in.h netpacket/packet.h poll.h process.h pthread.h pty.h \ +- sched.h setjmp.h shadow.h signal.h spawn.h stropts.h sys/audioio.h sys/bsdtty.h sys/devpoll.h \ ++ linux/tipc.h linux/wait.h netdb.h netinet/in.h netpacket/packet.h poll.h process.h pty.h \ ++ setjmp.h shadow.h signal.h spawn.h stropts.h sys/audioio.h sys/bsdtty.h sys/devpoll.h \ + sys/endian.h sys/epoll.h sys/event.h sys/eventfd.h sys/file.h sys/ioctl.h sys/kern_control.h \ + sys/loadavg.h sys/lock.h sys/memfd.h sys/mkdev.h sys/mman.h sys/modem.h sys/param.h sys/poll.h \ + sys/random.h sys/resource.h sys/select.h sys/sendfile.h sys/socket.h sys/soundcard.h sys/stat.h \ +@@ -2675,9 +2843,24 @@ AC_CHECK_HEADERS([ \ + sys/types.h sys/uio.h sys/un.h sys/utsname.h sys/wait.h sys/xattr.h sysexits.h syslog.h \ + termios.h util.h utime.h utmp.h \ + ]) ++ ++case $host in ++ *-*-mingw*) ;; ++ *) AC_CHECK_HEADERS([dlfcn.h]);; ++esac ++ ++ + AC_HEADER_DIRENT + AC_HEADER_MAJOR + ++# If using nt threads, don't look for pthread.h or thread.h ++if test "x$with_nt_threads" = xno ; then ++AC_HEADER_STDC ++AC_CHECK_HEADERS(pthread.h sched.h thread.h) ++AC_HEADER_DIRENT ++AC_HEADER_MAJOR ++fi ++ + # bluetooth/bluetooth.h has been known to not compile with -std=c99. + # http://permalink.gmane.org/gmane.linux.bluez.kernel/22294 + SAVE_CFLAGS=$CFLAGS +@@ -2852,6 +3035,10 @@ dnl LFS does not work with Emscripten 3.1 + AS_CASE([$ac_sys_system], + [Emscripten], [have_largefile_support="no"] + ) ++dnl Activate on windows platforms (32&64-bit) where off_t(4) < fpos_t(8) ++AS_CASE([$ac_sys_system], ++ [MINGW], [have_largefile_support="yes"] ++) + AS_VAR_IF([have_largefile_support], [yes], [ + AC_DEFINE(HAVE_LARGEFILE_SUPPORT, 1, + [Defined to enable large file support when an off_t is bigger than a long +@@ -2882,6 +3069,10 @@ elif test "$ac_cv_pthread" = "yes" + then CC="$CC -pthread" + fi + ++if test $with_nt_threads = yes ; then ++ dnl skip check for pthread_t if NT-thread model is enabled ++ ac_cv_have_pthread_t=skip ++else + AC_CACHE_CHECK([for pthread_t], [ac_cv_have_pthread_t], [ + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[#include ]], [[pthread_t x; x = *(pthread_t*)0;]]) +@@ -2913,7 +3104,7 @@ AS_VAR_IF([ac_cv_pthread_key_t_is_arithmetic_type], [yes], [ + AC_DEFINE(PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT, 1, + [Define if pthread_key_t is compatible with int.]) + ]) +- ++fi + CC="$ac_save_cc" + + AC_SUBST(OTHER_LIBTOOL_OPT) +@@ -3089,6 +3280,9 @@ if test -z "$SHLIB_SUFFIX"; then + CYGWIN*) SHLIB_SUFFIX=.dll;; + *) SHLIB_SUFFIX=.so;; + esac ++ case $host_os in ++ mingw*) SHLIB_SUFFIX=.pyd;; ++ esac + fi + AC_MSG_RESULT($SHLIB_SUFFIX) + +@@ -3218,6 +3412,10 @@ then + CYGWIN*) + LDSHARED="gcc -shared -Wl,--enable-auto-image-base" + LDCXXSHARED="g++ -shared -Wl,--enable-auto-image-base";; ++ MINGW*) ++ LDSHARED='$(CC) -shared -Wl,--enable-auto-image-base' ++ LDCXXSHARED='$(CXX) -shared -Wl,--enable-auto-image-base' ++ ;; + *) LDSHARED="ld";; + esac + fi +@@ -3341,6 +3539,11 @@ then + VxWorks*) + LINKFORSHARED='-Wl,-export-dynamic';; + esac ++ case $host in ++ *-*-mingw*) ++ # for https://bugs.python.org/issue40458 on MINGW ++ LINKFORSHARED="-Wl,--stack,2000000";; ++ esac + fi + AC_MSG_RESULT($LINKFORSHARED) + +@@ -3385,7 +3588,12 @@ AC_MSG_RESULT($SHLIBS) + + # checks for libraries + AC_CHECK_LIB(sendfile, sendfile) +-AC_CHECK_LIB(dl, dlopen) # Dynamic linking for SunOS/Solaris and SYSV ++ ++case $host in ++ *-*-mingw*) ;; ++ *) AC_CHECK_LIB(dl, dlopen) ;; # Dynamic linking for SunOS/Solaris and SYSV ++esac ++ + AC_CHECK_LIB(dld, shl_load) # Dynamic linking for HP-UX + + +@@ -3448,16 +3656,30 @@ AS_VAR_IF([have_uuid], [missing], [ + + AS_VAR_IF([have_uuid], [missing], [have_uuid=no]) + ++if test $with_nt_threads = yes ; then ++ dnl do not search for sem_init if NT-thread model is enabled ++ : ++else + # 'Real Time' functions on Solaris + # posix4 on Solaris 2.6 + # pthread (first!) on Linux + AC_SEARCH_LIBS(sem_init, pthread rt posix4) ++fi + + # check if we need libintl for locale functions ++case $host in ++ *-*-mingw*) ++ dnl Native windows build don't use libintl (see _localemodule.c). ++ dnl Also we don't like setup.py to add "intl" library to the list ++ dnl when build _locale module. ++ ;; ++ *) + AC_CHECK_LIB(intl, textdomain, + [AC_DEFINE(WITH_LIBINTL, 1, + [Define to 1 if libintl is needed for locale functions.]) + LIBS="-lintl $LIBS"]) ++ ;; ++esac + + # checks for system dependent C++ extensions support + case "$ac_sys_system" in +@@ -3645,7 +3867,7 @@ else + fi + + if test "$with_system_ffi" = "yes" && test -n "$PKG_CONFIG"; then +- LIBFFI_INCLUDEDIR="`"$PKG_CONFIG" libffi --cflags-only-I 2>/dev/null | sed -e 's/^-I//;s/ *$//'`" ++ LIBFFI_INCLUDEDIR="`"$PKG_CONFIG" libffi --cflags-only-I 2>/dev/null | sed -e 's/^-I//;s/ .*$//'`" + else + LIBFFI_INCLUDEDIR="" + fi +@@ -3780,6 +4002,12 @@ AS_CASE([$ac_sys_system], + [OSSAUDIODEV_LIBS=""] + ) + ++dnl On MINGW, you need to link against ws2_32 and iphlpapi for sockets to work ++AS_CASE([$ac_sys_system], ++ [MINGW], [SOCKET_LIBS="-lws2_32 -liphlpapi"], ++ [SOCKET_LIBS=""] ++) ++ + dnl detect sqlite3 from Emscripten emport + PY_CHECK_EMSCRIPTEN_PORT([LIBSQLITE3], [-sUSE_SQLITE3]) + +@@ -4042,6 +4270,18 @@ AS_VAR_IF([with_dbmliborder], [error], [ + ]) + AC_MSG_RESULT($with_dbmliborder) + ++case $host in ++ *-*-mingw*) ++ CFLAGS_NODIST="$CFLAGS_NODIST -D_WIN32_WINNT=0x0602";; ++esac ++ ++# Determine if windows modules should be used. ++AC_SUBST(USE_WIN32_MODULE) ++USE_WIN32_MODULE='#' ++case $host in ++ *-*-mingw*) USE_WIN32_MODULE=;; ++esac ++ + # Templates for things AC_DEFINEd more than once. + # For a single AC_DEFINE, no template is needed. + AH_TEMPLATE(_REENTRANT, +@@ -4076,6 +4316,11 @@ then + CXX="$CXX -pthread" + fi + posix_threads=yes ++elif test $with_nt_threads = yes ++then ++ posix_threads=no ++ AC_DEFINE(NT_THREADS, 1, ++ [Define to 1 if you want to use native NT threads]) + else + if test ! -z "$withval" -a -d "$withval" + then LDFLAGS="$LDFLAGS -L$withval" +@@ -4450,11 +4695,14 @@ AC_MSG_RESULT($with_freelists) + AC_MSG_CHECKING(for --with-c-locale-coercion) + AC_ARG_WITH(c-locale-coercion, + AS_HELP_STRING([--with-c-locale-coercion], +- [enable C locale coercion to a UTF-8 based locale (default is yes)])) ++ [enable C locale coercion to a UTF-8 based locale (default is yes on Unix, no on Windows)])) + + if test -z "$with_c_locale_coercion" + then +- with_c_locale_coercion="yes" ++ case $host in ++ *-*-mingw*) with_c_locale_coercion="no";; ++ *) with_c_locale_coercion="yes";; ++ esac + fi + if test "$with_c_locale_coercion" != "no" + then +@@ -4555,12 +4803,36 @@ then + fi + ;; + esac ++ case $host in ++ *-*-mingw*) ++ DYNLOADFILE="dynload_win.o" ++ extra_machdep_objs="$extra_machdep_objs PC/dl_nt.o" ++ CFLAGS_NODIST="$CFLAGS_NODIST -DPY3_DLLNAME='L\"$DLLLIBRARY\"'" ++ case $host in ++ i686*) ++ CFLAGS_NODIST="$CFLAGS_NODIST -DMS_DLL_ID='\"${VERSION}-32\"'" ++ ;; ++ armv7*) ++ CFLAGS_NODIST="$CFLAGS_NODIST -DMS_DLL_ID='\"${VERSION}-arm32\"'" ++ ;; ++ aarch64*) ++ CFLAGS_NODIST="$CFLAGS_NODIST -DMS_DLL_ID='\"${VERSION}-arm64\"'" ++ ;; ++ *) ++ CFLAGS_NODIST="$CFLAGS_NODIST -DMS_DLL_ID='\"$VERSION\"'" ++ ;; ++ esac ++ ;; ++ esac + fi + AC_MSG_RESULT($DYNLOADFILE) + if test "$DYNLOADFILE" != "dynload_stub.o" + then ++ have_dynamic_loading=yes + AC_DEFINE(HAVE_DYNAMIC_LOADING, 1, + [Defined when any dynamic module loading is enabled.]) ++else ++ have_dynamic_loading=no + fi + + # MACHDEP_OBJS can be set to platform-specific object files needed by Python +@@ -4580,13 +4852,22 @@ else + fi + + # checks for library functions ++if test $with_nt_threads = yes ; then ++ dnl GCC(mingw) 4.4+ require and use posix threads(pthreads-w32) ++ dnl and host may contain installed pthreads-w32. ++ dnl Skip checks for some functions declared in pthreads-w32 if ++ dnl NT-thread model is enabled. ++ ac_cv_func_pthread_kill=skip ++ ac_cv_func_sem_open=skip ++ ac_cv_func_sched_setscheduler=skip ++fi + AC_CHECK_FUNCS([ \ + accept4 alarm bind_textdomain_codeset chmod chown clock close_range confstr \ + copy_file_range ctermid dup dup3 execv explicit_bzero explicit_memset \ + faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \ + fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \ + gai_strerror getegid getentropy geteuid getgid getgrgid getgrgid_r \ +- getgrnam_r getgrouplist getgroups gethostname getitimer getloadavg getlogin \ ++ getgrnam_r getgrouplist getgroups getitimer getloadavg getlogin \ + getpeername getpgid getpid getppid getpriority _getpty \ + getpwent getpwnam_r getpwuid getpwuid_r getresgid getresuid getrusage getsid getspent \ + getspnam getuid getwd if_nameindex initgroups kill killpg lchown linkat \ +@@ -4599,7 +4880,7 @@ AC_CHECK_FUNCS([ \ + sched_setparam sched_setscheduler sem_clockwait sem_getvalue sem_open \ + sem_timedwait sem_unlink sendfile setegid seteuid setgid sethostname \ + setitimer setlocale setpgid setpgrp setpriority setregid setresgid \ +- setresuid setreuid setsid setuid setvbuf shutdown sigaction sigaltstack \ ++ setresuid setreuid setsid setuid setvbuf sigaction sigaltstack \ + sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ + sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ + sysconf system tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ +@@ -4829,7 +5110,13 @@ PKG_CHECK_MODULES([LIBLZMA], [liblzma], [have_liblzma=yes], [ + ]) + + dnl PY_CHECK_NETDB_FUNC(FUNCTION) +-AC_DEFUN([PY_CHECK_NETDB_FUNC], [PY_CHECK_FUNC([$1], [#include ])]) ++AC_DEFUN([PY_CHECK_NETDB_FUNC], [PY_CHECK_FUNC([$1], [ ++#ifdef _WIN32 ++ #include ++#else ++ #include ++#endif ++])]) + + PY_CHECK_NETDB_FUNC([hstrerror]) + dnl not available in WASI yet +@@ -4838,13 +5125,19 @@ PY_CHECK_NETDB_FUNC([getservbyport]) + PY_CHECK_NETDB_FUNC([gethostbyname]) + PY_CHECK_NETDB_FUNC([gethostbyaddr]) + PY_CHECK_NETDB_FUNC([getprotobyname]) ++PY_CHECK_NETDB_FUNC([gethostname]) ++PY_CHECK_NETDB_FUNC([shutdown]) + + dnl PY_CHECK_SOCKET_FUNC(FUNCTION) + AC_DEFUN([PY_CHECK_SOCKET_FUNC], [PY_CHECK_FUNC([$1], [ ++#ifdef _WIN32 ++#include ++#else + #include + #include + #include + #include ++#endif + ])]) + + PY_CHECK_SOCKET_FUNC([inet_aton]) +@@ -4945,6 +5238,9 @@ WITH_SAVE_ENV([ + ]) + ]) + ++case $host in ++ *-*-mingw*) ;; ++ *) + AC_CHECK_FUNCS(clock_gettime, [], [ + AC_CHECK_LIB(rt, clock_gettime, [ + LIBS="$LIBS -lrt" +@@ -4965,6 +5261,8 @@ AC_CHECK_FUNCS(clock_settime, [], [ + AC_DEFINE(HAVE_CLOCK_SETTIME, 1) + ]) + ]) ++ ;; ++esac + + AC_CHECK_FUNCS(clock_nanosleep, [], [ + AC_CHECK_LIB(rt, clock_nanosleep, [ +@@ -5163,18 +5461,33 @@ if test $ac_cv_header_time_altzone = yes; then + AC_DEFINE(HAVE_ALTZONE, 1, [Define this if your time.h defines altzone.]) + fi + ++AC_CHECK_HEADERS([ws2tcpip.h]) + AC_CACHE_CHECK([for addrinfo], [ac_cv_struct_addrinfo], +-AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[struct addrinfo a]])], ++AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ++#ifdef HAVE_WS2TCPIP_H ++# include ++#else ++# include ++#endif]], ++ [[struct addrinfo a]])], + [ac_cv_struct_addrinfo=yes], + [ac_cv_struct_addrinfo=no])) + if test $ac_cv_struct_addrinfo = yes; then +- AC_DEFINE(HAVE_ADDRINFO, 1, [struct addrinfo (netdb.h)]) ++ AC_DEFINE(HAVE_ADDRINFO, 1, [struct addrinfo]) + fi + + AC_CACHE_CHECK([for sockaddr_storage], [ac_cv_struct_sockaddr_storage], + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +-# include +-# include ]], [[struct sockaddr_storage s]])], ++#ifdef HAVE_WS2TCPIP_H ++#include ++#endif ++#ifdef HAVE_SYS_TYPES_H ++#include ++#endif ++#ifdef HAVE_SYS_SOCKET_H ++#include ++#endif]], ++ [[struct sockaddr_storage s]])], + [ac_cv_struct_sockaddr_storage=yes], + [ac_cv_struct_sockaddr_storage=no])) + if test $ac_cv_struct_sockaddr_storage = yes; then +@@ -5508,6 +5821,10 @@ dnl actually works. For FreeBSD versions <= 7.2, + dnl the kernel module that provides POSIX semaphores + dnl isn't loaded by default, so an attempt to call + dnl sem_open results in a 'Signal 12' error. ++if test $with_nt_threads = yes ; then ++ dnl skip posix semaphores test if NT-thread model is enabled ++ ac_cv_posix_semaphores_enabled=no ++fi + AC_CACHE_CHECK([whether POSIX semaphores are enabled], [ac_cv_posix_semaphores_enabled], + AC_RUN_IFELSE([ + AC_LANG_SOURCE([ +@@ -5541,6 +5858,14 @@ AS_VAR_IF([ac_cv_posix_semaphores_enabled], [no], [ + ]) + + dnl Multiprocessing check for broken sem_getvalue ++if test $with_nt_threads = yes ; then ++ dnl Skip test if NT-thread model is enabled. ++ dnl NOTE the test case below fail for pthreads-w32 as: ++ dnl - SEM_FAILED is not defined; ++ dnl - sem_open is a stub; ++ dnl - sem_getvalue work(!). ++ ac_cv_broken_sem_getvalue=skip ++fi + AC_CACHE_CHECK([for broken sem_getvalue], [ac_cv_broken_sem_getvalue], + AC_RUN_IFELSE([ + AC_LANG_SOURCE([ +@@ -5577,7 +5902,10 @@ AS_VAR_IF([ac_cv_broken_sem_getvalue], [yes], [ + ) + ]) + +-AC_CHECK_DECLS([RTLD_LAZY, RTLD_NOW, RTLD_GLOBAL, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_DEEPBIND, RTLD_MEMBER], [], [], [[#include ]]) ++case $host in ++ *-*-mingw*) ;; ++ *) AC_CHECK_DECLS([RTLD_LAZY, RTLD_NOW, RTLD_GLOBAL, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_DEEPBIND, RTLD_MEMBER], [], [], [[#include ]]);; ++esac + + # determine what size digit to use for Python's longs + AC_MSG_CHECKING([digit size for Python's longs]) +@@ -5664,6 +5992,71 @@ esac + # check for endianness + AC_C_BIGENDIAN + ++AC_SUBST(PYD_PLATFORM_TAG) ++# Special case of PYD_PLATFORM_TAG with python build with mingw. ++# Python can with compiled with clang or gcc and linked ++# to msvcrt or ucrt. To avoid conflicts between them ++# we are selecting the extension as based on the compiler ++# and the runtime they link to ++# gcc + x86_64 + msvcrt = cp{version number}-x86_64 ++# gcc + i686 + msvcrt = cp{version number}-i686 ++# gcc + x86_64 + ucrt = cp{version number}-x86_64-ucrt ++# clang + x86_64 + ucrt = cp{version number}-x86_64-clang ++# clang + i686 + ucrt = cp{version number}-i686-clang ++ ++PYD_PLATFORM_TAG="" ++case $host in ++ *-*-mingw*) ++ # check if we are linking to ucrt ++ AC_MSG_CHECKING(whether linking to ucrt) ++ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ ++ #include ++ #ifndef _UCRT ++ #error no ucrt ++ #endif ++ int main(){ return 0; } ++ ]])],[linking_to_ucrt=yes],[linking_to_ucrt=no]) ++ AC_MSG_RESULT($linking_to_ucrt) ++ ;; ++esac ++case $host_os in ++ mingw*) ++ AC_MSG_CHECKING(PYD_PLATFORM_TAG) ++ case $host in ++ i686-*-mingw*) ++ if test -n "${cc_is_clang}"; then ++ # it is CLANG32 ++ PYD_PLATFORM_TAG="mingw_i686_clang" ++ else ++ if test $linking_to_ucrt = no; then ++ PYD_PLATFORM_TAG="mingw_i686" ++ else ++ PYD_PLATFORM_TAG="mingw_i686_ucrt" ++ fi ++ fi ++ ;; ++ x86_64-*-mingw*) ++ if test -n "${cc_is_clang}"; then ++ # it is CLANG64 ++ PYD_PLATFORM_TAG="mingw_x86_64_clang" ++ else ++ if test $linking_to_ucrt = no; then ++ PYD_PLATFORM_TAG="mingw_x86_64" ++ else ++ PYD_PLATFORM_TAG="mingw_x86_64_ucrt" ++ fi ++ fi ++ ;; ++ aarch64-*-mingw*) ++ PYD_PLATFORM_TAG+="mingw_aarch64" ++ ;; ++ armv7-*-mingw*) ++ PYD_PLATFORM_TAG+="mingw_armv7" ++ ;; ++ esac ++ AC_MSG_RESULT($PYD_PLATFORM_TAG) ++esac ++ + # ABI version string for Python extension modules. This appears between the + # periods in shared library file names, e.g. foo..so. It is calculated + # from the following attributes which affect the ABI of this Python build (in +@@ -5696,7 +6089,12 @@ if test "$Py_DEBUG" = 'true' -a "$with_trace_refs" != "yes"; then + fi + + AC_SUBST(EXT_SUFFIX) +-EXT_SUFFIX=.${SOABI}${SHLIB_SUFFIX} ++VERSION_NO_DOTS=$(echo $LDVERSION | tr -d .) ++if test -n "${PYD_PLATFORM_TAG}"; then ++ EXT_SUFFIX=".cp${VERSION_NO_DOTS}-${PYD_PLATFORM_TAG}${SHLIB_SUFFIX}" ++else ++ EXT_SUFFIX=.${SOABI}${SHLIB_SUFFIX} ++fi + + AC_MSG_CHECKING(LDVERSION) + LDVERSION='$(VERSION)$(ABIFLAGS)' +@@ -5704,7 +6102,7 @@ AC_MSG_RESULT($LDVERSION) + + # On Android and Cygwin the shared libraries must be linked with libpython. + AC_SUBST(LIBPYTHON) +-if test -n "$ANDROID_API_LEVEL" -o "$MACHDEP" = "cygwin"; then ++if test -n "$ANDROID_API_LEVEL" -o "$MACHDEP" = "cygwin" -o "$MACHDEP" = "win32"; then + LIBPYTHON="-lpython${VERSION}${ABIFLAGS}" + else + LIBPYTHON='' +@@ -6073,11 +6471,16 @@ then + [Define if you have struct stat.st_mtimensec]) + fi + ++if test -n "$PKG_CONFIG"; then ++ NCURSESW_INCLUDEDIR="`"$PKG_CONFIG" ncursesw --cflags-only-I 2>/dev/null | sed -e 's/^-I//;s/ .*$//'`" ++else ++ NCURSESW_INCLUDEDIR="" ++fi ++AC_SUBST(NCURSESW_INCLUDEDIR) ++ + # first curses header check + ac_save_cppflags="$CPPFLAGS" +-if test "$cross_compiling" = no; then +- CPPFLAGS="$CPPFLAGS -I/usr/include/ncursesw" +-fi ++CPPFLAGS="$CPPFLAGS -I$NCURSESW_INCLUDEDIR" + + AC_CHECK_HEADERS(curses.h ncurses.h) + +@@ -6243,7 +6646,10 @@ fi + + AC_CHECK_TYPE(socklen_t,, + AC_DEFINE(socklen_t,int, +- [Define to `int' if does not define.]),[ ++ [Define to `int' if or does not define.]),[ ++#ifdef HAVE_WS2TCPIP_H ++#include ++#endif + #ifdef HAVE_SYS_TYPES_H + #include + #endif +@@ -6333,6 +6739,27 @@ do + THREADHEADERS="$THREADHEADERS \$(srcdir)/$h" + done + ++case $host in ++ *-*-mingw*) ++ dnl Required for windows builds as Objects/exceptions.c require ++ dnl "errmap.h" from $srcdir/PC. ++ dnl Note we cannot use BASECPPFLAGS as autogenerated pyconfig.h ++ dnl has to be before customized located in ../PC. ++ dnl (-I. at end is workaround for setup.py logic) ++ CPPFLAGS="-I\$(srcdir)/PC $CPPFLAGS -I." ++ ;; ++esac ++ ++dnl Python interpreter main program for frozen scripts ++AC_SUBST(PYTHON_OBJS_FROZENMAIN) ++PYTHON_OBJS_FROZENMAIN="Python/frozenmain.o" ++case $host in ++ *-*-mingw*) ++ dnl 'PC/frozen_dllmain.c' - not yet ++ PYTHON_OBJS_FROZENMAIN= ++ ;; ++esac ++ + AC_SUBST(SRCDIRS) + SRCDIRS="\ + Modules \ +@@ -6354,6 +6781,10 @@ SRCDIRS="\ + Python \ + Python/frozen_modules \ + Python/deepfreeze" ++case $host in ++ *-*-mingw*) SRCDIRS="$SRCDIRS PC";; ++esac ++ + AC_MSG_CHECKING(for build directories) + for dir in $SRCDIRS; do + if test ! -d $dir; then +@@ -6362,6 +6793,38 @@ for dir in $SRCDIRS; do + done + AC_MSG_RESULT(done) + ++# For mingw build need additional library for linking ++case $host in ++ *-*-mingw*) ++ LIBS="$LIBS -lversion -lshlwapi -lpathcch -lbcrypt" ++ AC_PROG_AWK ++ if test "$AWK" = "gawk"; then ++ awk_extra_flag="--non-decimal-data" ++ fi ++ AC_MSG_CHECKING([FIELD3]) ++ FIELD3=$($AWK $awk_extra_flag '\ ++ /^#define PY_RELEASE_LEVEL_/ {levels[$2]=$3} \ ++ /^#define PY_MICRO_VERSION[[:space:]]+/ {micro=$3} \ ++ /^#define PY_RELEASE_LEVEL[[:space:]]+/ {level=levels[$3]} \ ++ /^#define PY_RELEASE_SERIAL[[:space:]]+/ {serial=$3} \ ++ END {print micro * 1000 + level * 10 + serial}' \ ++ $srcdir/Include/patchlevel.h ++ ) ++ ++ AC_MSG_RESULT([${FIELD3}]) ++ RCFLAGS="$RCFLAGS -DFIELD3=$FIELD3 -O COFF" ++ ++ case $host in ++ i686*) RCFLAGS="$RCFLAGS --target=pe-i386" ;; ++ x86_64*) RCFLAGS="$RCFLAGS --target=pe-x86-64" ;; ++ *) ;; ++ esac ++ ;; ++ *) ++ ;; ++esac ++AC_SUBST(RCFLAGS) ++ + # Availability of -O2: + AC_CACHE_CHECK([for -O2], [ac_cv_compile_o2], [ + saved_cflags="$CFLAGS" +@@ -6971,7 +7434,6 @@ PY_STDLIB_MOD_SIMPLE([_json]) + PY_STDLIB_MOD_SIMPLE([_lsprof]) + PY_STDLIB_MOD_SIMPLE([_opcode]) + PY_STDLIB_MOD_SIMPLE([_pickle]) +-PY_STDLIB_MOD_SIMPLE([_posixsubprocess]) + PY_STDLIB_MOD_SIMPLE([_queue]) + PY_STDLIB_MOD_SIMPLE([_random]) + PY_STDLIB_MOD_SIMPLE([select]) +@@ -6982,7 +7444,7 @@ PY_STDLIB_MOD_SIMPLE([_zoneinfo]) + + dnl multiprocessing modules + PY_STDLIB_MOD([_multiprocessing], +- [], [test "$ac_cv_func_sem_unlink" = "yes"], ++ [], [test "$ac_cv_func_sem_unlink" = "yes" -o "$MACHDEP" = "win32"], + [-I\$(srcdir)/Modules/_multiprocessing]) + PY_STDLIB_MOD([_posixshmem], + [], [test "$have_posix_shmem" = "yes"], +@@ -7002,11 +7464,15 @@ PY_STDLIB_MOD([fcntl], + [], [test "$ac_cv_header_sys_ioctl_h" = "yes" -a "$ac_cv_header_fcntl_h" = "yes"], + [], [$FCNTL_LIBS]) + PY_STDLIB_MOD([mmap], +- [], [test "$ac_cv_header_sys_mman_h" = "yes" -a "$ac_cv_header_sys_stat_h" = "yes"]) ++ [], m4_flatten([test "$ac_cv_header_sys_mman_h" = "yes" ++ -a "$ac_cv_header_sys_stat_h" = "yes" ++ -o "$MACHDEP" = "win32"])) + PY_STDLIB_MOD([_socket], + [], m4_flatten([test "$ac_cv_header_sys_socket_h" = "yes" + -a "$ac_cv_header_sys_types_h" = "yes" +- -a "$ac_cv_header_netinet_in_h" = "yes"])) ++ -a "$ac_cv_header_netinet_in_h" = "yes" ++ -o "$MACHDEP" = "win32"]), ++ [], [$SOCKET_LIBS]) + + dnl platform specific extensions + PY_STDLIB_MOD([grp], [], [test "$ac_cv_func_getgrgid" = yes -o "$ac_cv_func_getgrgid_r" = yes]) +@@ -7021,6 +7487,7 @@ PY_STDLIB_MOD([_scproxy], + PY_STDLIB_MOD([spwd], [], [test "$ac_cv_func_getspent" = yes -o "$ac_cv_func_getspnam" = yes]) + PY_STDLIB_MOD([syslog], [], [test "$ac_cv_header_syslog_h" = yes]) + PY_STDLIB_MOD([termios], [], [test "$ac_cv_header_termios_h" = yes]) ++PY_STDLIB_MOD([_posixsubprocess], [], [test "$MACHDEP" != "win32"]) + + dnl _elementtree loads libexpat via CAPI hook in pyexpat + PY_STDLIB_MOD([pyexpat], [], [], [$LIBEXPAT_CFLAGS], [$LIBEXPAT_LDFLAGS]) +@@ -7083,25 +7550,35 @@ PY_STDLIB_MOD([_lzma], [], [test "$have_liblzma" = yes], + + dnl OpenSSL bindings + PY_STDLIB_MOD([_ssl], [], [test "$ac_cv_working_openssl_ssl" = yes], +- [$OPENSSL_INCLUDES], [$OPENSSL_LDFLAGS $OPENSSL_LDFLAGS_RPATH $OPENSSL_LIBS]) ++ [$OPENSSL_INCLUDES], [$OPENSSL_LDFLAGS $OPENSSL_LDFLAGS_RPATH $OPENSSL_LIBS -lws2_32]) + PY_STDLIB_MOD([_hashlib], [], [test "$ac_cv_working_openssl_hashlib" = yes], + [$OPENSSL_INCLUDES], [$OPENSSL_LDFLAGS $OPENSSL_LDFLAGS_RPATH $LIBCRYPTO_LIBS]) + ++dnl windows specific modules ++PY_STDLIB_MOD([msvcrt], [test "$MACHDEP" = "win32"]) ++PY_STDLIB_MOD([_winapi], [test "$MACHDEP" = "win32"]) ++PY_STDLIB_MOD([_msi], [test "$MACHDEP" = "win32"], [], [], ++ [-lmsi -lcabinet -lrpcrt4]) ++PY_STDLIB_MOD([winsound], [test "$MACHDEP" = "win32"], [], [], ++ [-lwinmm]) ++PY_STDLIB_MOD([_overlapped], [test "$MACHDEP" = "win32"], [], [], ++ [-lws2_32]) ++ + dnl test modules + PY_STDLIB_MOD([_testcapi], [test "$TEST_MODULES" = yes]) + PY_STDLIB_MOD([_testclinic], [test "$TEST_MODULES" = yes]) +-PY_STDLIB_MOD([_testinternalcapi], [test "$TEST_MODULES" = yes]) ++PY_STDLIB_MOD([_testinternalcapi], [test "$TEST_MODULES" = yes], [], [-DPY3_DLLNAME="\"$DLLLIBRARY\""], []) + PY_STDLIB_MOD([_testbuffer], [test "$TEST_MODULES" = yes]) +-PY_STDLIB_MOD([_testimportmultiple], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes]) +-PY_STDLIB_MOD([_testmultiphase], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes]) ++PY_STDLIB_MOD([_testimportmultiple], [test "$TEST_MODULES" = yes], [test "$have_dynamic_loading" = yes]) ++PY_STDLIB_MOD([_testmultiphase], [test "$TEST_MODULES" = yes], [test "$have_dynamic_loading" = yes]) + PY_STDLIB_MOD([_xxtestfuzz], [test "$TEST_MODULES" = yes]) +-PY_STDLIB_MOD([_ctypes_test], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes], [], [-lm]) ++PY_STDLIB_MOD([_ctypes_test], [test "$TEST_MODULES" = yes], [test "$have_dynamic_loading" = yes], [], [-lm]) + + dnl Limited API template modules. + dnl The limited C API is not compatible with the Py_TRACE_REFS macro. + dnl Emscripten does not support shared libraries yet. +-PY_STDLIB_MOD([xxlimited], [test "$with_trace_refs" = "no"], [test "$ac_cv_func_dlopen" = yes]) +-PY_STDLIB_MOD([xxlimited_35], [test "$with_trace_refs" = "no"], [test "$ac_cv_func_dlopen" = yes]) ++PY_STDLIB_MOD([xxlimited], [test "$with_trace_refs" = "no"], [test "$have_dynamic_loading" = yes]) ++PY_STDLIB_MOD([xxlimited_35], [test "$with_trace_refs" = "no"], [test "$have_dynamic_loading" = yes]) + + # substitute multiline block, must come after last PY_STDLIB_MOD() + AC_SUBST([MODULE_BLOCK]) +diff --git a/mingw_ignorefile.txt b/mingw_ignorefile.txt +new file mode 100644 +index 0000000..54093f1 +--- /dev/null ++++ b/mingw_ignorefile.txt +@@ -0,0 +1,42 @@ ++ctypes.test.test_loading.LoaderTest.test_load_dll_with_flags ++distutils.tests.test_bdist_dumb.BuildDumbTestCase.test_simple_built ++distutils.tests.test_cygwinccompiler.CygwinCCompilerTestCase.test_get_versions ++distutils.tests.test_util.UtilTestCase.test_change_root ++test.datetimetester.TestLocalTimeDisambiguation_Fast.* ++test.datetimetester.TestLocalTimeDisambiguation_Pure.* ++test.test_cmath.CMathTests.test_specific_values ++test.test_cmd_line_script.CmdLineTest.test_consistent_sys_path_for_direct_execution ++test.test_compileall.CommandLineTestsNoSourceEpoch.* ++test.test_compileall.CommandLineTestsWithSourceEpoch.* ++test.test_compileall.CompileallTestsWithoutSourceEpoch.* ++test.test_compileall.CompileallTestsWithSourceEpoch.* ++test.test_import.ImportTests.test_dll_dependency_import ++test.test_math.MathTests.* ++test.test_ntpath.NtCommonTest.test_import ++test.test_os.StatAttributeTests.test_stat_block_device ++test.test_os.TestScandir.test_attributes ++test.test_os.UtimeTests.test_large_time ++test.test_platform.PlatformTest.test_architecture_via_symlink ++test.test_regrtest.ProgramsTestCase.test_pcbuild_rt ++test.test_regrtest.ProgramsTestCase.test_tools_buildbot_test ++test.test_site._pthFileTests.* ++test.test_site.HelperFunctionsTests.* ++test.test_site.StartupImportTests.* ++test.test_ssl.* ++test.test_strptime.CalculationTests.* ++test.test_strptime.StrptimeTests.test_weekday ++test.test_strptime.TimeRETests.test_compile ++test.test_tools.test_i18n.Test_pygettext.test_POT_Creation_Date ++test.test_venv.BasicTest.* ++test.test_venv.EnsurePipTest.* ++test.test_sysconfig.TestSysConfig.test_user_similar ++test.test_tcl.TclTest.testLoadWithUNC ++# flaky ++test.test__xxsubinterpreters.* ++test.test_asyncio.test_subprocess.SubprocessProactorTests.test_stdin_broken_pipe ++test.test_asynchat.TestAsynchat.test_line_terminator2 ++test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_gc_aclose_09 ++test.test_concurrent_futures.ThreadPoolShutdownTest.test_interpreter_shutdown ++test.test_asynchat.TestNotConnected.test_disallow_negative_terminator ++test.test_logging.SysLogHandlerTest.* ++test.test_logging.IPv6SysLogHandlerTest.* +diff --git a/mingw_smoketests.py b/mingw_smoketests.py +new file mode 100644 +index 0000000..ca1f652 +--- /dev/null ++++ b/mingw_smoketests.py +@@ -0,0 +1,358 @@ ++#!/usr/bin/env python3 ++# Copyright 2017 Christoph Reiter ++# ++# Permission is hereby granted, free of charge, to any person obtaining ++# a copy of this software and associated documentation files (the ++# "Software"), to deal in the Software without restriction, including ++# without limitation the rights to use, copy, modify, merge, publish, ++# distribute, sublicense, and/or sell copies of the Software, and to ++# permit persons to whom the Software is furnished to do so, subject to ++# the following conditions: ++# ++# The above copyright notice and this permission notice shall be included ++# in all copies or substantial portions of the Software. ++# ++# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ++# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ++# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ++# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ++# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ ++"""The goal of this test suite is collect tests for update regressions ++and to test msys2 related modifications like for path handling. ++Feel free to extend. ++""" ++ ++import os ++import unittest ++import sysconfig ++ ++if os.environ.get("MSYSTEM", ""): ++ SEP = "/" ++else: ++ SEP = "\\" ++ ++if sysconfig.is_python_build(): ++ os.environ["PYTHONLEGACYWINDOWSDLLLOADING"] = "1" ++ ++_UCRT = sysconfig.get_platform() not in ('mingw_x86_64', 'mingw_i686') ++ ++ ++class Tests(unittest.TestCase): ++ ++ def test_zoneinfo(self): ++ # https://github.com/msys2-contrib/cpython-mingw/issues/32 ++ import zoneinfo ++ self.assertTrue(any(os.path.exists(p) for p in zoneinfo.TZPATH)) ++ zoneinfo.ZoneInfo("America/Sao_Paulo") ++ ++ def test_userdir_path_sep(self): ++ # Make sure os.path and pathlib use the same path separators ++ from unittest import mock ++ from os.path import expanduser ++ from pathlib import Path ++ ++ profiles = ["C:\\foo", "C:/foo"] ++ for profile in profiles: ++ with mock.patch.dict(os.environ, {"USERPROFILE": profile}): ++ self.assertEqual(expanduser("~"), os.path.normpath(expanduser("~"))) ++ self.assertEqual(str(Path("~").expanduser()), expanduser("~")) ++ self.assertEqual(str(Path.home()), expanduser("~")) ++ ++ def test_sysconfig_schemes(self): ++ # https://github.com/msys2/MINGW-packages/issues/9319 ++ import sysconfig ++ from distutils.dist import Distribution ++ from distutils.command.install import install ++ ++ names = ['scripts', 'purelib', 'platlib', 'data', 'include'] ++ for scheme in ["nt", "nt_user"]: ++ for name in names: ++ c = install(Distribution({"name": "foobar"})) ++ c.user = (scheme == "nt_user") ++ c.finalize_options() ++ if name == "include": ++ dist_path = os.path.dirname(getattr(c, "install_" + "headers")) ++ else: ++ dist_path = getattr(c, "install_" + name) ++ sys_path = sysconfig.get_path(name, scheme) ++ self.assertEqual(dist_path, sys_path, (scheme, name)) ++ ++ def test_ctypes_find_library(self): ++ from ctypes.util import find_library ++ from ctypes import cdll ++ self.assertTrue(cdll.msvcrt) ++ if _UCRT: ++ self.assertIsNone(find_library('c')) ++ else: ++ self.assertEqual(find_library('c'), 'msvcrt.dll') ++ ++ def test_ctypes_dlopen(self): ++ import ctypes ++ import sys ++ self.assertEqual(ctypes.RTLD_GLOBAL, 0) ++ self.assertEqual(ctypes.RTLD_GLOBAL, ctypes.RTLD_LOCAL) ++ self.assertFalse(hasattr(sys, 'setdlopenflags')) ++ self.assertFalse(hasattr(sys, 'getdlopenflags')) ++ self.assertFalse([n for n in dir(os) if n.startswith("RTLD_")]) ++ ++ def test_time_no_unix_stuff(self): ++ import time ++ self.assertFalse([n for n in dir(time) if n.startswith("clock_")]) ++ self.assertFalse([n for n in dir(time) if n.startswith("CLOCK_")]) ++ self.assertFalse([n for n in dir(time) if n.startswith("pthread_")]) ++ self.assertFalse(hasattr(time, 'tzset')) ++ ++ def test_strftime(self): ++ import time ++ with self.assertRaises(ValueError): ++ time.strftime('%Y', (12345,) + (0,) * 8) ++ ++ def test_sep(self): ++ self.assertEqual(os.sep, SEP) ++ ++ def test_module_file_path(self): ++ import asyncio ++ import zlib ++ self.assertEqual(zlib.__file__, os.path.normpath(zlib.__file__)) ++ self.assertEqual(asyncio.__file__, os.path.normpath(asyncio.__file__)) ++ ++ def test_importlib_frozen_path_sep(self): ++ import importlib._bootstrap_external ++ self.assertEqual(importlib._bootstrap_external.path_sep, SEP) ++ ++ def test_os_commonpath(self): ++ self.assertEqual( ++ os.path.commonpath( ++ [os.path.join("C:", os.sep, "foo", "bar"), ++ os.path.join("C:", os.sep, "foo")]), ++ os.path.join("C:", os.sep, "foo")) ++ ++ def test_pathlib(self): ++ import pathlib ++ ++ p = pathlib.Path("foo") / pathlib.Path("foo") ++ self.assertEqual(str(p), os.path.normpath(p)) ++ ++ def test_modules_import(self): ++ import sqlite3 ++ import ssl ++ import ctypes ++ import curses ++ ++ def test_c_modules_import(self): ++ import _decimal ++ ++ def test_socket_inet_ntop(self): ++ import socket ++ self.assertTrue(hasattr(socket, "inet_ntop")) ++ ++ def test_socket_inet_pton(self): ++ import socket ++ self.assertTrue(hasattr(socket, "inet_pton")) ++ ++ def test_multiprocessing_queue(self): ++ from multiprocessing import Queue ++ Queue(0) ++ ++ #def test_socket_timout_normal_error(self): ++ # import urllib.request ++ # from urllib.error import URLError ++ ++ # try: ++ # urllib.request.urlopen( ++ # 'http://localhost', timeout=0.0001).close() ++ # except URLError: ++ # pass ++ ++ def test_threads(self): ++ from concurrent.futures import ThreadPoolExecutor ++ ++ with ThreadPoolExecutor(1) as pool: ++ for res in pool.map(lambda *x: None, range(10000)): ++ pass ++ ++ def test_sysconfig(self): ++ import sysconfig ++ # This should be able to execute without exceptions ++ sysconfig.get_config_vars() ++ ++ def test_sqlite_enable_load_extension(self): ++ # Make sure --enable-loadable-sqlite-extensions is used ++ import sqlite3 ++ self.assertTrue(sqlite3.Connection.enable_load_extension) ++ ++ def test_venv_creation(self): ++ import tempfile ++ import venv ++ import subprocess ++ import shutil ++ with tempfile.TemporaryDirectory() as tmp: ++ builder = venv.EnvBuilder() ++ builder.create(tmp) ++ assert os.path.exists(os.path.join(tmp, "bin", "activate")) ++ assert os.path.exists(os.path.join(tmp, "bin", "python.exe")) ++ assert os.path.exists(os.path.join(tmp, "bin", "python3.exe")) ++ subprocess.check_call([shutil.which("bash.exe"), os.path.join(tmp, "bin", "activate")]) ++ ++ # This will not work in in-tree build ++ if not sysconfig.is_python_build(): ++ op = subprocess.check_output( ++ [ ++ os.path.join(tmp, "bin", "python.exe"), ++ "-c", ++ "print('Hello World')" ++ ], ++ cwd=tmp, ++ ) ++ assert op.decode().strip() == "Hello World" ++ ++ def test_has_mktime(self): ++ from time import mktime, gmtime ++ mktime(gmtime()) ++ ++ def test_platform_things(self): ++ import sys ++ import sysconfig ++ import platform ++ import importlib.machinery ++ self.assertEqual(sys.implementation.name, "cpython") ++ self.assertEqual(sys.platform, "win32") ++ self.assertTrue(sysconfig.get_platform().startswith("mingw")) ++ self.assertTrue(sysconfig.get_config_var('SOABI').startswith("cpython-")) ++ ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') ++ self.assertTrue(ext_suffix.endswith(".pyd")) ++ self.assertTrue("mingw" in ext_suffix) ++ self.assertEqual(sysconfig.get_config_var('SHLIB_SUFFIX'), ".pyd") ++ ext_suffixes = importlib.machinery.EXTENSION_SUFFIXES ++ self.assertTrue(ext_suffix in ext_suffixes) ++ self.assertTrue(".pyd" in ext_suffixes) ++ if sysconfig.get_platform().startswith('mingw_i686'): ++ self.assertEqual(sys.winver, ".".join(map(str, sys.version_info[:2])) + '-32') ++ elif sysconfig.get_platform().startswith('mingw_aarch64'): ++ self.assertEqual(sys.winver, ".".join(map(str, sys.version_info[:2])) + '-arm64') ++ elif sysconfig.get_platform().startswith('mingw_armv7'): ++ self.assertEqual(sys.winver, ".".join(map(str, sys.version_info[:2])) + '-arm32') ++ else: ++ self.assertEqual(sys.winver, ".".join(map(str, sys.version_info[:2]))) ++ self.assertEqual(platform.python_implementation(), "CPython") ++ self.assertEqual(platform.system(), "Windows") ++ self.assertTrue(isinstance(sys.api_version, int) and sys.api_version > 0) ++ ++ def test_sys_getpath(self): ++ # everything sourced from getpath.py ++ import sys ++ ++ def assertNormpath(path): ++ self.assertEqual(path, os.path.normpath(path)) ++ ++ assertNormpath(sys.executable) ++ assertNormpath(sys._base_executable) ++ assertNormpath(sys.prefix) ++ assertNormpath(sys.base_prefix) ++ assertNormpath(sys.exec_prefix) ++ assertNormpath(sys.base_exec_prefix) ++ assertNormpath(sys.platlibdir) ++ assertNormpath(sys._stdlib_dir) ++ for p in sys.path: ++ assertNormpath(p) ++ ++ def test_site(self): ++ import site ++ ++ self.assertEqual(len(site.getsitepackages()), 1) ++ ++ def test_c_ext_build(self): ++ import tempfile ++ import sys ++ import subprocess ++ import textwrap ++ from pathlib import Path ++ ++ with tempfile.TemporaryDirectory() as tmppro: ++ subprocess.check_call([sys.executable, "-m", "ensurepip", "--user"]) ++ with Path(tmppro, "setup.py").open("w") as f: ++ f.write( ++ textwrap.dedent( ++ """\ ++ from setuptools import setup, Extension ++ ++ setup( ++ name='cwrapper', ++ version='1.0', ++ ext_modules=[ ++ Extension( ++ 'cwrapper', ++ sources=['cwrapper.c']), ++ ], ++ ) ++ """ ++ ) ++ ) ++ with Path(tmppro, "cwrapper.c").open("w") as f: ++ f.write( ++ textwrap.dedent( ++ """\ ++ #include ++ static PyObject * ++ helloworld(PyObject *self, PyObject *args) ++ { ++ printf("Hello World\\n"); ++ Py_RETURN_NONE; ++ } ++ static PyMethodDef ++ myMethods[] = { ++ { "helloworld", helloworld, METH_NOARGS, "Prints Hello World" }, ++ { NULL, NULL, 0, NULL } ++ }; ++ static struct PyModuleDef cwrapper = { ++ PyModuleDef_HEAD_INIT, ++ "cwrapper", ++ "Test Module", ++ -1, ++ myMethods ++ }; ++ ++ PyMODINIT_FUNC ++ PyInit_cwrapper(void) ++ { ++ return PyModule_Create(&cwrapper); ++ } ++ """ ++ ) ++ ) ++ subprocess.check_call( ++ [sys.executable, "-c", "import struct"], ++ ) ++ subprocess.check_call( ++ [ ++ sys.executable, ++ "-m", ++ "pip", ++ "install", ++ "wheel", ++ ], ++ ) ++ subprocess.check_call( ++ [ ++ sys.executable, ++ "-m", ++ "pip", ++ "install", ++ tmppro, ++ ], ++ ) ++ subprocess.check_call( ++ [sys.executable, "-c", "import cwrapper"], ++ ) ++ ++ ++ ++def suite(): ++ return unittest.TestLoader().loadTestsFromName(__name__) ++ ++ ++if __name__ == '__main__': ++ unittest.main(defaultTest='suite') +diff --git a/pyconfig.h.in b/pyconfig.h.in +index 75f1d90..3b89b9e 100644 +--- a/pyconfig.h.in ++++ b/pyconfig.h.in +@@ -60,7 +60,7 @@ + /* Define to 1 if you have the `acosh' function. */ + #undef HAVE_ACOSH + +-/* struct addrinfo (netdb.h) */ ++/* struct addrinfo */ + #undef HAVE_ADDRINFO + + /* Define to 1 if you have the `alarm' function. */ +@@ -1521,6 +1521,9 @@ + /* Define if mvwdelch in curses.h is an expression. */ + #undef MVWDELCH_IS_EXPRESSION + ++/* Define to 1 if you want to use native NT threads */ ++#undef NT_THREADS ++ + /* Define to the address where bug reports for this package should be sent. */ + #undef PACKAGE_BUGREPORT + +@@ -1836,7 +1839,7 @@ + /* Define to `unsigned int' if does not define. */ + #undef size_t + +-/* Define to `int' if does not define. */ ++/* Define to `int' if or does not define. */ + #undef socklen_t + + /* Define to `int' if doesn't define. */ +diff --git a/setup.py b/setup.py +index 4f122b6..bf93153 100644 +--- a/setup.py ++++ b/setup.py +@@ -77,9 +77,29 @@ def get_platform(): + return sys.platform + + ++# On MSYS, os.system needs to be wrapped with sh.exe ++# as otherwise all the io redirection will fail. ++# Arguably, this could happen inside the real os.system ++# rather than this monkey patch. ++if sys.platform == "win32" and os.environ.get("MSYSTEM", ""): ++ os_system = os.system ++ def msys_system(command): ++ command_in_sh = 'sh.exe -c "%s"' % command.replace("\\", "\\\\") ++ return os_system(command_in_sh) ++ os.system = msys_system ++ ++ # set PYTHONLEGACYWINDOWSDLLLOADING to 1 to load DLLs from PATH ++ # This is needed while building core extensions of Python ++ os.environ["PYTHONLEGACYWINDOWSDLLLOADING"] = "1" ++ ++ + CROSS_COMPILING = ("_PYTHON_HOST_PLATFORM" in os.environ) + HOST_PLATFORM = get_platform() +-MS_WINDOWS = (HOST_PLATFORM == 'win32') ++MS_WINDOWS = (HOST_PLATFORM == 'win32' or HOST_PLATFORM == 'mingw') ++ ++if MS_WINDOWS: ++ os.environ["PYTHONLEGACYWINDOWSDLLLOADING"] = "1" ++ + CYGWIN = (HOST_PLATFORM == 'cygwin') + MACOS = (HOST_PLATFORM == 'darwin') + AIX = (HOST_PLATFORM.startswith('aix')) +@@ -687,7 +707,7 @@ def check_extension_import(self, ext): + def add_multiarch_paths(self): + # Debian/Ubuntu multiarch support. + # https://wiki.ubuntu.com/MultiarchSpec +- tmpfile = os.path.join(self.build_temp, 'multiarch') ++ tmpfile = os.path.join(self.build_temp, 'multiarch').replace('\\','/') + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + ret = run_command( +@@ -712,7 +732,7 @@ def add_multiarch_paths(self): + opt = '' + if CROSS_COMPILING: + opt = '-t' + sysconfig.get_config_var('HOST_GNU_TYPE') +- tmpfile = os.path.join(self.build_temp, 'multiarch') ++ tmpfile = os.path.join(self.build_temp, 'multiarch').replace('\\','/') + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + ret = run_command( +@@ -774,7 +794,7 @@ def add_search_path(line): + pass + + def add_cross_compiling_paths(self): +- tmpfile = os.path.join(self.build_temp, 'ccpaths') ++ tmpfile = os.path.join(self.build_temp, 'ccpaths').replace('\\','/') + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + # bpo-38472: With a German locale, GCC returns "gcc-Version 9.1.0 +@@ -796,14 +816,25 @@ def add_cross_compiling_paths(self): + elif line.startswith("End of search list"): + in_incdirs = False + elif (is_gcc or is_clang) and line.startswith("LIBRARY_PATH"): +- for d in line.strip().split("=")[1].split(":"): ++ for d in line.strip().split("=")[1].split(os.pathsep): + d = os.path.normpath(d) +- if '/gcc/' not in d: ++ if '/gcc/' not in d and '/clang/' not in d: + add_dir_to_list(self.compiler.library_dirs, + d) + elif (is_gcc or is_clang) and in_incdirs and '/gcc/' not in line and '/clang/' not in line: + add_dir_to_list(self.compiler.include_dirs, + line.strip()) ++ if is_clang: ++ ret = run_command('%s -print-search-dirs >%s' % (CC, tmpfile)) ++ if ret == 0: ++ with open(tmpfile) as fp: ++ for line in fp.readlines(): ++ if line.startswith("libraries:"): ++ for d in line.strip().split("=")[1].split(os.pathsep): ++ d = os.path.normpath(d) ++ if '/gcc/' not in d and '/clang/' not in d: ++ add_dir_to_list(self.compiler.library_dirs, ++ d) + finally: + os.unlink(tmpfile) + +@@ -849,6 +880,7 @@ def configure_compiler(self): + if not CROSS_COMPILING: + add_dir_to_list(self.compiler.library_dirs, '/usr/local/lib') + add_dir_to_list(self.compiler.include_dirs, '/usr/local/include') ++ self.add_multiarch_paths() + # only change this for cross builds for 3.3, issues on Mageia + if CROSS_COMPILING: + self.add_cross_compiling_paths() +@@ -867,7 +899,6 @@ def init_inc_lib_dirs(self): + sysconfig.get_config_var("LIBDIR")) + add_dir_to_list(self.compiler.include_dirs, + sysconfig.get_config_var("INCLUDEDIR")) +- + system_lib_dirs = ['/lib64', '/usr/lib64', '/lib', '/usr/lib'] + system_include_dirs = ['/usr/include'] + # lib_dirs and inc_dirs are used to search for files; +@@ -898,7 +929,7 @@ def init_inc_lib_dirs(self): + if HOST_PLATFORM == 'hp-ux11': + self.lib_dirs += ['/usr/lib/hpux64', '/usr/lib/hpux32'] + +- if MACOS: ++ if MACOS or MS_WINDOWS: + # This should work on any unixy platform ;-) + # If the user has bothered specifying additional -I and -L flags + # in OPT and LDFLAGS we might as well use them here. +@@ -915,7 +946,21 @@ def init_inc_lib_dirs(self): + for item in ldflags.split(): + if item.startswith('-L'): + self.lib_dirs.append(item[2:]) +- ++ to_remove_inc = ['/usr/include', '/usr/include/x86_64-linux-gnu', '/usr/local/include'] ++ for path in to_remove_inc: ++ if path in self.inc_dirs: ++ self.inc_dirs.remove(path) ++ self.compiler.include_dirs.remove(path) ++ to_remove_lib = ['/usr/lib', '/lib', '/usr/lib/x86_64-linux-gnu', '/lib64', '/lib/x86_64-linux-gnu', '/usr/lib64'] ++ for path in to_remove_lib: ++ if path in self.lib_dirs: ++ self.lib_dirs.remove(path) ++ self.compiler.library_dirs.remove(path) ++ ++ log.info(f'Print compiler.include_dirs:{self.compiler.include_dirs}') ++ log.info(f'Print compiler.library_dirs:{self.compiler.library_dirs}') ++ ++ + def detect_simple_extensions(self): + # + # The following modules are all pretty straightforward, and compile +@@ -970,11 +1015,15 @@ def detect_simple_extensions(self): + # grp(3) + self.addext(Extension('grp', ['grpmodule.c'])) + +- self.addext(Extension('_socket', ['socketmodule.c'])) ++ self.addext(Extension( ++ '_socket', ['socketmodule.c'], ++ libraries=(['ws2_32', 'iphlpapi'] if MS_WINDOWS else None))) + self.addext(Extension('spwd', ['spwdmodule.c'])) + + # select(2); not on ancient System V +- self.addext(Extension('select', ['selectmodule.c'])) ++ self.addext(Extension( ++ 'select', ['selectmodule.c'], ++ libraries=(['ws2_32'] if MS_WINDOWS else None))) + + # Memory-mapped files (also works on Win32). + self.addext(Extension('mmap', ['mmapmodule.c'])) +@@ -1033,12 +1082,15 @@ def detect_test_extensions(self): + ['_xxtestfuzz/_xxtestfuzz.c', '_xxtestfuzz/fuzzer.c'] + )) + ++ if MS_WINDOWS: ++ self.add(Extension('_testconsole', ['../PC/_testconsole.c'])) ++ + def detect_readline_curses(self): + # readline + readline_termcap_library = "" + curses_library = "" + # Cannot use os.popen here in py3k. +- tmpfile = os.path.join(self.build_temp, 'readline_termcap_lib') ++ tmpfile = os.path.join(self.build_temp, 'readline_termcap_lib').replace('\\','/') + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + # Determine if readline is already linked against curses or tinfo. +@@ -1101,6 +1153,8 @@ def detect_readline_curses(self): + # readline package + if find_file('readline/rlconf.h', self.inc_dirs, []) is None: + do_readline = False ++ if MS_WINDOWS: ++ do_readline = False + if do_readline: + readline_libs = [readline_lib] + if readline_termcap_library: +@@ -1124,8 +1178,7 @@ def detect_readline_curses(self): + panel_library = 'panel' + if curses_library == 'ncursesw': + curses_defines.append(('HAVE_NCURSESW', '1')) +- if not CROSS_COMPILING: +- curses_includes.append('/usr/include/ncursesw') ++ curses_includes.append(sysconfig.get_config_var("NCURSESW_INCLUDEDIR")) + # Bug 1464056: If _curses.so links with ncursesw, + # _curses_panel.so must link with panelw. + panel_library = 'panelw' +@@ -1209,7 +1262,7 @@ def detect_dbm_gdbm(self): + if dbm_args: + dbm_order = [arg.split('=')[-1] for arg in dbm_args][-1].split(":") + else: +- dbm_order = "gdbm:ndbm:bdb".split(":") ++ dbm_order = [] + dbmext = None + for cand in dbm_order: + if cand == "ndbm": +@@ -1282,6 +1335,19 @@ def detect_platform_specific_exts(self): + # macOS-only, needs SystemConfiguration and CoreFoundation framework + self.addext(Extension('_scproxy', ['_scproxy.c'])) + ++ # Windows-only modules ++ if MS_WINDOWS: ++ srcdir = sysconfig.get_config_var('srcdir') ++ pc_srcdir = os.path.abspath(os.path.join(srcdir, 'PC')) ++ ++ self.addext(Extension('_msi', ++ [os.path.join(pc_srcdir, '_msi.c')])) ++ ++ self.addext(Extension('winsound', ++ [os.path.join(pc_srcdir, 'winsound.c')])) ++ ++ self.addext(Extension('_overlapped', ['overlapped.c'])) ++ + def detect_compress_exts(self): + # Andrew Kuchling's zlib module. + self.addext(Extension('zlib', ['zlibmodule.c'])) +@@ -1329,9 +1395,10 @@ def detect_multiprocessing(self): + if ( + sysconfig.get_config_var('HAVE_SEM_OPEN') and not + sysconfig.get_config_var('POSIX_SEMAPHORES_NOT_ENABLED') +- ): ++ ) or MS_WINDOWS: + multiprocessing_srcs.append('_multiprocessing/semaphore.c') +- self.addext(Extension('_multiprocessing', multiprocessing_srcs)) ++ self.addext(Extension('_multiprocessing', multiprocessing_srcs, ++ libraries=(['ws2_32'] if MS_WINDOWS else None), include_dirs=["Modules/_multiprocessing"])) + self.addext(Extension('_posixshmem', ['_multiprocessing/posixshmem.c'])) + + def detect_uuid(self): +@@ -1414,11 +1481,16 @@ def detect_ctypes(self): + include_dirs=include_dirs, + extra_compile_args=extra_compile_args, + extra_link_args=extra_link_args, +- libraries=[], ++ libraries=(['ole32', 'oleaut32', 'uuid'] if MS_WINDOWS else []), ++ export_symbols=( ++ ['DllGetClassObject PRIVATE', 'DllCanUnloadNow PRIVATE'] ++ if MS_WINDOWS else None ++ ), + sources=sources) + self.add(ext) + # function my_sqrt() needs libm for sqrt() +- self.addext(Extension('_ctypes_test', ['_ctypes/_ctypes_test.c'])) ++ self.addext(Extension('_ctypes_test', ['_ctypes/_ctypes_test.c'], ++ libraries=(['oleaut32'] if MS_WINDOWS else []))) + + ffi_inc = sysconfig.get_config_var("LIBFFI_INCLUDEDIR") + ffi_lib = None +@@ -1568,7 +1640,7 @@ def copy_scripts(self): + else: + newfilename = filename + minoronly + log.info(f'renaming {filename} to {newfilename}') +- os.rename(filename, newfilename) ++ os.replace(filename, newfilename) + newoutfiles.append(newfilename) + if filename in updated_files: + newupdated_files.append(newfilename) +-- +2.34.1 + diff --git a/.cid/python_builder.py b/.cid/python_builder.py new file mode 100644 index 0000000..c7bd92d --- /dev/null +++ b/.cid/python_builder.py @@ -0,0 +1,468 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import logging +import os +from pathlib import Path +import shutil +import subprocess +from typing import List, Mapping +import binascii +import glob +import tarfile + + +# 配置日志记录 +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.StreamHandler() + ] +) + + +def run_command(cmd, cwd=None, env=None): + logger = logging.getLogger(__name__) + try: + logger.info(f"Command: {' '.join(cmd)}") + result = subprocess.run(cmd, cwd=cwd, env=env, capture_output=True, text=True, check=True) + if result.stdout: + logger.info(f"Command output: {result.stdout.strip()}") + if result.stderr: + logger.warning(f"Command error output: {result.stderr.strip()}") + except subprocess.CalledProcessError as e: + logger.error(f"Command failed: {' '.join(cmd)}. Error: {e.stderr.strip()}") + raise + +class BuildConfig: + def __init__(self, args): + self.REPOROOT_DIR = args.repo_root + self.OUT_PATH = args.out_path + self.LLDB_PY_VERSION = args.lldb_py_version + self.LLDB_PY_DETAILED_VERSION = args.lldb_py_detailed_version + self.MINGW_TRIPLE = args.mingw_triple + + +class PythonBuilder: + target_platform = "" + patches = [] + + + def __init__(self, build_config) -> None: + self.build_config = build_config + self.repo_root = Path(build_config.REPOROOT_DIR).resolve() + self._out_dir = Path(build_config.OUT_PATH).resolve() + self._lldb_py_version = build_config.LLDB_PY_VERSION + self._version = build_config.LLDB_PY_DETAILED_VERSION + version_parts = self._version.split('.') + self._major_version = version_parts[0] + self._source_dir = self.repo_root / 'third_party' / 'python' + self._patch_dir = self._source_dir / '.cid' / 'patches' + self._prebuilts_path = os.path.join(self.repo_root, 'prebuilts') + self._prebuilts_python_path = os.path.join(self._prebuilts_path, 'python', 'linux-x86', self._lldb_py_version, 'bin', + f'python{self._major_version}') + self._install_dir = "" + self._clean_patches() + logging.getLogger(__name__).addHandler(logging.FileHandler(self._out_dir / 'build.log')) + + @property + def _logger(self) -> logging.Logger: + return logging.getLogger(__name__) + + @property + def _cc(self) -> Path: + return self._clang_toolchain_dir / 'bin' / 'clang' + + @property + def _cflags(self) -> List[str]: + return [] + + @property + def _ldflags(self) -> List[str]: + return [] + + @property + def _cxx(self) -> Path: + return self._clang_toolchain_dir / 'bin' / 'clang++' + + @property + def _strip(self) -> Path: + return self._clang_toolchain_dir / 'bin' / 'llvm-strip' + + @property + def _cxxflags(self) -> List[str]: + return self._cflags.copy() + + @property + def _rcflags(self) -> List[str]: + return [] + + @property + def _env(self) -> Mapping[str, str]: + env = os.environ.copy() + clang_bin_dir = self._clang_toolchain_dir / 'bin' + + env.update({ + 'CC': str(self._cc), + 'CXX': str(self._cxx), + 'WINDRES': str(clang_bin_dir / 'llvm-windres'), + 'AR': str(clang_bin_dir / 'llvm-ar'), + 'READELF': str(clang_bin_dir / 'llvm-readelf'), + 'LD': str(clang_bin_dir / 'ld.lld'), + 'DLLTOOL': str(clang_bin_dir / 'llvm-dlltoo'), + 'RANLIB': str(clang_bin_dir / 'llvm-ranlib'), + 'STRIP': str(self._strip), + 'CFLAGS': ' '.join(self._cflags), + 'CXXFLAGS': ' '.join(self._cxxflags), + 'LDFLAGS': ' '.join(self._ldflags), + 'RCFLAGS': ' '.join(self._rcflags), + 'CPPFLAGS': ' '.join(self._cflags), + 'LIBS': '-lffi' + }) + return env + + def _configure(self) -> None: + self._logger.info("Starting configuration...") + return + + def _clean_patches(self) -> None: + self._logger.info("Cleaning patches...") + run_command(['git', 'reset', '--hard', 'HEAD'], cwd=self._source_dir) + run_command(['git', 'clean', '-df', '--exclude=.cid'], cwd=self._source_dir) + + def _pre_build(self) -> None: + self._deps_build() + self._apply_patches() + + def _apply_patches(self) -> None: + if hasattr(self, '_patch_ignore_file') and self._patch_ignore_file.is_file(): + self._logger.warning('Patches for Python have being applied, skip patching') + return + + if not self._patch_dir.is_dir(): + self._logger.warning('Patches are not found, skip patching') + return + + for patch in self._patch_dir.iterdir(): + if patch.is_file() and patch.name in self.patches: + cmd = ['git', 'apply', str(patch)] + self._logger.info(f"Applying patch: {patch.name}") + run_command(cmd, cwd=self._source_dir) + + + def _deps_build(self) -> None: + self._logger.info("Starting dependency build process...") + return + + def build(self) -> None: + self._logger.info("Starting build process...") + self._pre_build() + if hasattr(self, '_build_dir') and self._build_dir.exists(): + self._logger.info(f"Removing existing build directory: {self._build_dir}") + shutil.rmtree(self._build_dir) + if isinstance(self._install_dir, Path) and self._install_dir.exists(): + self._logger.info(f"Removing existing install directory: {self._install_dir}") + shutil.rmtree(self._install_dir) + if hasattr(self, '_build_dir'): + self._build_dir.mkdir(parents=True) + if isinstance(self._install_dir, Path): + self._install_dir.mkdir(parents=True) + self._configure() + self._install() + + def _install(self) -> None: + self._logger.info("Starting installation...") + num_jobs = os.cpu_count() or 8 + cmd = ['make', f'-j{num_jobs}', 'install'] + run_command(cmd, cwd=self._build_dir) + + def _strip_in_place(self, file: Path) -> None: + self._logger.info(f"Stripping file: {file}") + cmd = [ + str(self._strip), + str(file), + ] + run_command(cmd) + + def _clean_bin_dir(self) -> None: + self._logger.info("Cleaning bin directory...") + python_bin_dir = self._install_dir / 'bin' + if not python_bin_dir.is_dir(): + return + + windows_suffixes = ('.exe', '.dll') + for f in python_bin_dir.iterdir(): + if f.suffix not in windows_suffixes or f.is_symlink(): + self._logger.info(f"Removing file: {f}") + f.unlink() + continue + self._strip_in_place(f) + + def _remove_dir(self, dir_path: Path) -> None: + if dir_path.is_dir(): + self._logger.info(f"Removing directory: {dir_path}") + shutil.rmtree(dir_path) + + def _clean_share_dir(self) -> None: + self._logger.info("Cleaning share directory...") + share_dir = self._install_dir / 'share' + self._remove_dir(share_dir) + + def _clean_lib_dir(self) -> None: + self._logger.info("Cleaning lib directory...") + python_lib_dir = self._install_dir / 'lib' + pkgconfig_dir = python_lib_dir / 'pkgconfig' + self._remove_dir(pkgconfig_dir) + + def _remove_exclude(self) -> None: + self._logger.info("Removing excluded files and directories...") + exclude_dirs_tuple = ( + f'config-{self._major_version}', + '__pycache__', + 'idlelib', + 'tkinter', 'turtledemo', + 'test', 'tests' + ) + exclude_files_tuple = ( + 'bdist_wininst.py', + 'turtle.py', + '.whl', + '.pyc', '.pickle' + ) + + for root, dirs, files in os.walk(self._install_dir / 'lib'): + for item in dirs: + if item.startswith(exclude_dirs_tuple): + self._logger.info(f"Removing directory: {os.path.join(root, item)}") + shutil.rmtree(os.path.join(root, item)) + for item in files: + if item.endswith(exclude_files_tuple): + self._logger.info(f"Removing file: {os.path.join(root, item)}") + os.remove(os.path.join(root, item)) + + def _copy_external_libs(self) -> None: + self._logger.info("Copying external libraries...") + # 定义源文件路径 + _external_libs = [self._deps_dir / 'ffi' / 'bin' / 'libffi-8.dll', self._clang_toolchain_dir / self.build_config.MINGW_TRIPLE / 'bin' / 'libssp-0.dll'] + # 定义目标目录 + target_dir = self._install_dir / 'lib' / 'python3.11' / 'lib-dynload' + # 创建目标目录(如果不存在) + target_dir.mkdir(parents=True, exist_ok=True) + + try: + for lib in _external_libs : + # 调用提取的方法拷贝 libffi-8.dll + self._copy_file_if_exists(lib, target_dir) + except Exception as e: + self._logger.error(f"Error copying external libraries: {e}") + + def _copy_file_if_exists(self, src_path: Path, dest_dir: Path) -> None: + """ + 若源文件存在,则将其拷贝到目标目录,并记录相应日志;若不存在,则记录警告日志。 + + :param src_path: 源文件的路径 + :param dest_dir: 目标目录的路径 + """ + if src_path.exists(): + shutil.copy2(src_path, dest_dir) + self._logger.info(f"Copied {src_path} to {dest_dir}") + else: + self._logger.warning(f"{src_path} does not exist. Skipping.") + + def _is_elf_file(self, file_path: Path) -> bool: + with open(file_path, 'rb') as f: + magic_numbers = f.read(4) + hex_magic_number = binascii.hexlify(magic_numbers).decode('utf-8') + return hex_magic_number == '7f454c46' + + @property + def install_dir(self) -> str: + return str(self._install_dir) + + +class MinGWPythonBuilder(PythonBuilder): + def __init__(self, build_config) -> None: + super().__init__(build_config) + + self.target_platform = "x86_64-w64-mingw32" + self.patches = [f'cpython_mingw_v{self._version}.patch'] + self._clang_toolchain_dir = Path( + os.path.join(self._prebuilts_path, 'mingw-w64', 'ohos', 'linux-x86_64', 'clang-mingw')).resolve() + self._mingw_install_dir = self._clang_toolchain_dir / build_config.MINGW_TRIPLE + self._build_dir = self._out_dir / 'python-windows-build' + self._install_dir = self._out_dir / 'python-windows-install' + self._deps_dir = self._out_dir / 'python-windows-deps' + # This file is used to detect whether patches are applied + self._patch_ignore_file = self._source_dir / 'mingw_ignorefile.txt' + + for directory in (self._mingw_install_dir, self._source_dir): + if not directory.is_dir(): + raise ValueError(f'No such directory "{directory}"') + + def _extract_libffi(self): + """ + 定位 libffi-*.tar.gz 文件,清理输出目录后,将其直接解压到 out/libffi 目录。 + + Returns: + Path: 解压后的 libffi 内部目录的路径。 + + Raises: + FileNotFoundError: 若未找到 libffi-*.tar.gz 文件。 + Exception: 若无法获取 libffi 压缩包的内部目录。 + """ + # 找到 libffi-*.tar.gz 包 + libffi_tar_gz_files = glob.glob(str(self.repo_root / 'third_party' / 'libffi' / 'libffi-*.tar.gz')) + if not libffi_tar_gz_files: + self._logger.error("No libffi-*.tar.gz file found in third_party/libffi directory.") + raise FileNotFoundError("No libffi-*.tar.gz file found.") + libffi_tar_gz = libffi_tar_gz_files[0] + + # 清理 out/libffi 目录 + libffi_extract_dir = self._out_dir / 'libffi' + if libffi_extract_dir.exists(): + self._logger.info(f"Cleaning existing libffi directory: {libffi_extract_dir}") + shutil.rmtree(libffi_extract_dir) + libffi_extract_dir.mkdir(parents=True) + + # 直接解压 libffi-*.tar.gz 到 out/libffi 目录 + with tarfile.open(libffi_tar_gz, 'r:gz') as tar: + tar.extractall(path=libffi_extract_dir) + # 获取解压后的目录名 + members = tar.getmembers() + if members: + libffi_inner_dir = libffi_extract_dir / members[0].name + else: + self._logger.error("Failed to get inner directory of libffi tarball.") + raise Exception("Failed to get inner directory of libffi tarball.") + return libffi_inner_dir + + @property + def _cflags(self) -> List[str]: + cflags = [ + f'-target {self.target_platform}', + f'--sysroot={self._mingw_install_dir}', + f'-fstack-protector-strong', + f'-I{str(self._deps_dir / "ffi" / "include")}', + f'-nostdinc', + f'-I{str(self._mingw_install_dir / "include")}', + f'-I{str(self._clang_toolchain_dir / "lib" / "clang" / "15.0.4" / "include")}' + ] + return cflags + + @property + def _ldflags(self) -> List[str]: + ldflags = [ + f'--sysroot={self._mingw_install_dir}', + f'-rtlib=compiler-rt', + f'-target {self.target_platform}', + f'-lucrt', + f'-lucrtbase', + f'-fuse-ld=lld', + f'-L{str(self._deps_dir / "ffi" / "lib")}', + ] + return ldflags + + @property + def _rcflags(self) -> List[str]: + return [f'-I{self._mingw_install_dir}/include'] + + def _deps_build(self) -> None: + self._logger.info("Starting MinGW dependency build process...") + # 调用提取的方法 + libffi_inner_dir = self._extract_libffi() + + env = os.environ.copy() + env.update({ + 'CC': "/bin/x86_64-w64-mingw32-gcc", + 'CXX': "/bin/x86_64-w64-mingw32-g++", + 'WINDRES': "/bin/x86_64-w64-mingw32-windres", + 'AR': "/bin/x86_64-w64-mingw32-ar", + 'READELF': "/bin/x86_64-w64-mingw32-readelf", + 'LD': "/bin/x86_64-w64-mingw32-ld", + 'DLLTOOL': "/bin/x86_64-w64-mingw32-dlltool", + 'RANLIB': "/bin/x86_64-w64-mingw32-gcc-ranlib", + 'STRIP': "/bin/x86_64-w64-mingw32-strip", + 'CFLAGS': "--sysroot=/usr/x86_64-w64-mingw32 -fstack-protector-strong", + 'CXXFLAGS': "--sysroot=/usr/x86_64-w64-mingw32 -fstack-protector-strong", + 'LDFLAGS': "--sysroot=/usr/x86_64-w64-mingw32", + 'RCFLAGS': "-I/usr/x86_64-w64-mingw32/include", + 'CPPFLAGS': "--sysroot=/usr/x86_64-w64-mingw32 -fstack-protector-strong" + }) + + configure_cmd = [ + "./configure", + f"--prefix={self._deps_dir / 'ffi'}", + "--enable-shared", + "--build=x86_64-pc-linux-gnu", + "--host=x86_64-w64-mingw32", + "--disable-symvers", + "--disable-docs" + ] + run_command(configure_cmd, env=env, cwd=libffi_inner_dir) + + # 执行 make -j16 + make_cmd = ['make', '-j16'] + run_command(make_cmd, env=env, cwd=libffi_inner_dir) + + # 执行 make install + make_install_cmd = ['make', 'install'] + run_command(make_install_cmd, env=env, cwd=libffi_inner_dir) + + def _configure(self) -> None: + self._logger.info("Starting MinGW configuration...") + run_command(['autoreconf', '-vfi'], cwd=self._source_dir) + build_platform = subprocess.check_output( + ['./config.guess'], cwd=self._source_dir).decode().strip() + config_flags = [ + f'--prefix={self._install_dir}', + f'--build={build_platform}', + f'--host={self.target_platform}', + f'--with-build-python={self._prebuilts_python_path}', + '--enable-shared', + '--without-ensurepip', + '--enable-loadable-sqlite-extensions', + '--disable-ipv6', + '--with-pydebug', + '--with-system-ffi' + ] + cmd = [str(self._source_dir / 'configure')] + config_flags + run_command(cmd, env=self._env, cwd=self._build_dir) + + def prepare_for_package(self) -> None: + self._logger.info("Preparing MinGW build for packaging...") + self._clean_bin_dir() + self._clean_share_dir() + self._clean_lib_dir() + self._remove_exclude() + self._copy_external_libs() + + def package(self) -> None: + self._logger.info("Packaging MinGW build...") + archive = self._out_dir / f'python-mingw-x86-{self._version}.tar.gz' + if archive.exists(): + self._logger.info(f"Removing existing archive: {archive}") + archive.unlink() + cmd = [ + 'tar', + '-czf', + str(archive), + '--exclude=__pycache__', + '--transform', + f's,^,python/windows-x86/{self._lldb_py_version}/,', + ] + [f.name for f in self._install_dir.iterdir()] + run_command(cmd, cwd=self._install_dir) -- Gitee