diff --git a/.github/workflows/install-vs-components.py b/.github/workflows/install-vs-components.py index ed2deb62f..7c4342d04 100644 --- a/.github/workflows/install-vs-components.py +++ b/.github/workflows/install-vs-components.py @@ -21,10 +21,13 @@ text=True, shell=True, ).strip() -components_to_add = ( - ["Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64"] +components_to_add = ["Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL"] + ( + [ + "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM", + "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64", + ] if platform.machine() == "ARM64" - else ["Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL"] + else [] ) args = ( "vs_installer.exe", diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a3a4a9ab9..a8866a7e0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,13 +15,25 @@ concurrency: jobs: test: name: Build and test - runs-on: windows-2022 + runs-on: ${{ matrix.os }} timeout-minutes: 30 strategy: fail-fast: false matrix: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] - architecture: [x64, x86] + python-architecture: [x64, x86, arm64] + include: + - os: windows-2022 + - python-architecture: arm64 + os: windows-11-arm + exclude: + # actions/setup-python does not provide prebuilt arm64 Python before 3.11 + - python-architecture: arm64 + python-version: "3.8" + - python-architecture: arm64 + python-version: "3.9" + - python-architecture: arm64 + python-version: "3.10" env: # TODO: We can't yet run tests with PYTHONDEVMODE=1, let's emulated it as much as we can # https://docs.python.org/3/library/devmode.html#effects-of-the-python-development-mode @@ -37,7 +49,7 @@ jobs: uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - architecture: ${{ matrix.architecture }} + architecture: ${{ matrix.python-architecture }} cache: pip cache-dependency-path: .github/workflows/main.yml check-latest: true @@ -83,21 +95,20 @@ jobs: # Upload artifacts even if tests fail if: ${{ always() }} with: - name: artifacts-${{ matrix.python-version }}-${{ matrix.architecture }} + name: artifacts-${{ matrix.python-version }}-${{ matrix.python-architecture }} path: dist/*.whl if-no-files-found: error - # We cannot build and test on ARM64, so we cross-compile. - # Later, when available, we can add tests using this wheel on ARM64 VMs - build_arm64: - name: Cross-compile ARM + # actions/setup-python does not provide prebuilt arm64 Python before 3.11, so we cross-compile. + cross_compile_arm64: + name: Cross-compile ARM64 runs-on: windows-2022 timeout-minutes: 30 strategy: fail-fast: false matrix: - # pythonarm64 NuGet's has no download for Python ~=3.9.11 - python-version: ["3.9.10", "3.10", "3.11", "3.12", "3.13", "3.14"] + # pythonarm64 NuGet has no download for Python 3.8 and Python ~=3.9.11 + python-version: ["3.9.10", "3.10"] steps: - uses: actions/checkout@v4 @@ -115,10 +126,10 @@ jobs: run: pip install --upgrade build - name: Obtain ARM64 library files - run: python .github\workflows\download-arm64-libs.py .\arm64libs + run: python .github\workflows\download-arm64-libs.py ./arm64libs - name: Build wheels - run: python -m build --wheel --config-setting=--build-option=build_ext --config-setting=--build-option=-L.\arm64libs --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=build --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=bdist_wheel --config-setting=--build-option=--plat-name=win-arm64 + run: python -m build --wheel --config-setting=--build-option="build_ext -L./arm64libs --plat-name=win-arm64 build --plat-name=win-arm64 bdist_wheel --plat-name=win-arm64" - uses: actions/upload-artifact@v4 with: @@ -128,7 +139,7 @@ jobs: merge: runs-on: windows-latest - needs: [test, build_arm64] + needs: [test, cross_compile_arm64] steps: - name: Merge Artifacts uses: actions/upload-artifact/merge@v4 diff --git a/build_env.md b/build_env.md index 8000f04d9..742d972b3 100644 --- a/build_env.md +++ b/build_env.md @@ -137,12 +137,12 @@ configuration, please [open an issue](https://github.com/mhammond/pywin32/issues - Follow the `For Visual Studio XXXX` instructions above and pick the optional ARM64 build tools - Download prebuilt Python ARM64 binaries to a temporary location on your machine. You will need this location in a later step. + - This script downloads a Python ARM64 build [from NuGet](https://www.nuget.org/packages/pythonarm64/#versions-tab) that matches the version you used to run it. ```shell - python .github\workflows\download-arm64-libraries.py "" + python .github\workflows\download-arm64-libs.py ./arm64libs ``` - - This script downloads a Python ARM64 build [from NuGet](https://www.nuget.org/packages/pythonarm64/#versions-tab) that matches the version you used to run it. - Setup the cross-compilation environment: ```shell @@ -156,13 +156,12 @@ configuration, please [open an issue](https://github.com/mhammond/pywin32/issues ``` - Build the extensions, passing the directory from earlier. You may optionally add the `bdist_wheel` command to generate a wheel. + - If you are not using an initialized build environment, you will need to specify the `build_ext`, `build` and `bdist_wheel` commands and pass `--plat-name win-arm64` to *each* of them separately. Otherwise you may get a mixed platform build and/or linker errors. ```shell - python -m build --wheel --config-setting=--build-option=build_ext --config-setting=--build-option=-L.\arm64libs --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=bdist_wheel --config-setting=--build-option=--plat-name=win-arm64 + python -m build --wheel --config-setting=--build-option="build_ext -L./arm64libs --plat-name=win-arm64 bdist_wheel --plat-name=win-arm64" ``` - - If you are not using an initialized build environment, you will need to specify the `build_ext`, `build` and `bdist_wheel` commands and pass `--plat-name win-arm64` to *each* of them separately. Otherwise you may get a mixed platform build and/or linker errors. - - Copy the built wheel to the target machine and install directly: ```shell diff --git a/com/win32com/test/testPyComTest.py b/com/win32com/test/testPyComTest.py index 921a45630..d2ef00514 100644 --- a/com/win32com/test/testPyComTest.py +++ b/com/win32com/test/testPyComTest.py @@ -7,7 +7,9 @@ import datetime import decimal import os +import platform import time +from unittest import SkipTest import pythoncom import win32com @@ -25,6 +27,7 @@ gencache, register_record_class, ) +from win32com.universal import RegisterInterfaces from win32process import GetProcessMemoryInfo # This test uses a Python implemented COM server - ensure correctly registered. @@ -46,10 +49,6 @@ print(f"The PyCOMTest module can not be located or generated.\n{importMsg}\n") raise RuntimeError(importMsg) from error -# We had a bg where RegisterInterfaces would fail if gencache had -# already been run - exercise that here -universal.RegisterInterfaces("{6BCDCB60-5605-11D0-AE5F-CADD4C000000}", 0, 1, 1) - verbose = 0 @@ -910,15 +909,27 @@ def TestQueryInterface(long_lived_server=0, iterations=5): class Tester(win32com.test.util.TestCase): - def testVTableInProc(self): + def testRegisterInterfacesAfterGencache(self) -> None: + # We had a bug where RegisterInterfaces would fail if gencache had + # already been run - exercise that here + try: + RegisterInterfaces("{6BCDCB60-5605-11D0-AE5F-CADD4C000000}", 0, 1, 1) + except NotImplementedError: + if platform.machine() == "ARM64": + raise SkipTest( + "`win32com.universal.RegisterInterfaces` doesn't support ARM64 yet" + ) + raise + + def testVTableInProc(self) -> None: # We used to crash running this the second time - do it a few times for i in range(3): - progress("Testing VTables in-process #%d..." % (i + 1)) + progress(f"Testing VTables in-process #{(i + 1)}...") TestVTable(pythoncom.CLSCTX_INPROC_SERVER) - def testVTableLocalServer(self): + def testVTableLocalServer(self) -> None: for i in range(3): - progress("Testing VTables out-of-process #%d..." % (i + 1)) + progress(f"Testing VTables out-of-process #{(i + 1)}...") TestVTable(pythoncom.CLSCTX_LOCAL_SERVER) def testVTable2(self): diff --git a/make_all.bat b/make_all.bat index 7c35ab225..f19792693 100644 --- a/make_all.bat +++ b/make_all.bat @@ -28,12 +28,12 @@ py -3.13 -m build --wheel py -3.14-32 -m build --wheel py -3.14 -m build --wheel -rem Check /build_env.md#build-environment to make sure you have all the required ARM64 components installed -py -3.10 -m build --wheel --config-setting=--build-option=build_ext --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=build --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=bdist_wheel --config-setting=--build-option=--plat-name=win-arm64 -py -3.11 -m build --wheel --config-setting=--build-option=build_ext --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=build --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=bdist_wheel --config-setting=--build-option=--plat-name=win-arm64 -py -3.12 -m build --wheel --config-setting=--build-option=build_ext --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=build --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=bdist_wheel --config-setting=--build-option=--plat-name=win-arm64 -py -3.13 -m build --wheel --config-setting=--build-option=build_ext --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=build --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=bdist_wheel --config-setting=--build-option=--plat-name=win-arm64 -py -3.14 -m build --wheel --config-setting=--build-option=build_ext --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=build --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=bdist_wheel --config-setting=--build-option=--plat-name=win-arm64 +rem Check /build_env.md#cross-compiling-for-arm64-microsoft-visual-c-141-and-up to make sure you have all the required ARM64 components installed +py -3.10 -m build --wheel --config-setting=--build-option="build_ext --plat-name=win-arm64 build --plat-name=win-arm64 bdist_wheel --plat-name=win-arm64" +py -3.11 -m build --wheel --config-setting=--build-option="build_ext --plat-name=win-arm64 build --plat-name=win-arm64 bdist_wheel --plat-name=win-arm64" +py -3.12 -m build --wheel --config-setting=--build-option="build_ext --plat-name=win-arm64 build --plat-name=win-arm64 bdist_wheel --plat-name=win-arm64" +py -3.13 -m build --wheel --config-setting=--build-option="build_ext --plat-name=win-arm64 build --plat-name=win-arm64 bdist_wheel --plat-name=win-arm64" +py -3.14 -m build --wheel --config-setting=--build-option="build_ext --plat-name=win-arm64 build --plat-name=win-arm64 bdist_wheel --plat-name=win-arm64" @goto xit :couldnt_rm diff --git a/setup.py b/setup.py index 72e2be18a..71e2e0c19 100644 --- a/setup.py +++ b/setup.py @@ -13,10 +13,10 @@ For a debug (_d) version, you need a local debug build of Python, but must use the release version executable for the build. eg: - pip install . -v --config-setting=--build-option=build --config-setting=--build-option=--debug + pip install . -v --config-setting=--build-option="build --debug" Cross-compilation from x86 to ARM is well supported (assuming installed vs tools etc) - eg: - python -m build --wheel --config-setting=--build-option=build_ext --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=build --config-setting=--build-option=--plat-name=win-arm64 --config-setting=--build-option=bdist_wheel --config-setting=--build-option=--plat-name=win-arm64 + python -m build --wheel --config-setting=--build-option="build_ext --plat-name=win-arm64 build --plat-name=win-arm64 bdist_wheel --plat-name=win-arm64" Some modules require special SDKs or toolkits to build (eg, mapi/exchange), which often aren't available in CI. The build process treats them as optional -