diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a04510c..f2be675 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,13 +15,26 @@ jobs: strategy: fail-fast: false matrix: - os: ["ubuntu-latest", "macos-latest"] - python-version: [ "3.8", "3.9", "3.10", "3.10", "3.11", "3.12", "3.13", "3.14" ] - name: Test (${{ matrix.python-version }}, ${{ matrix.os }}) + os: ["ubuntu-latest", "macos-latest", "windows-latest"] + python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14" ] + compiler: ["default", "mingw", "msvc"] + exclude: + # MinGW and MSVC are Windows-only + - os: "ubuntu-latest" + compiler: "mingw" + - os: "ubuntu-latest" + compiler: "msvc" + - os: "macos-latest" + compiler: "mingw" + - os: "macos-latest" + compiler: "msvc" + - os: "windows-latest" + compiler: "default" + name: Test (${{ matrix.python-version }}, ${{ matrix.os }}${{ matrix.os == 'windows-latest' && format(', {0}', matrix.compiler) || '' }}) runs-on: ${{ matrix.os }} defaults: run: - shell: bash -l {0} + shell: ${{ matrix.os == 'windows-latest' && 'pwsh' || 'bash -l {0}' }} steps: - uses: actions/checkout@v2 - uses: conda-incubator/setup-miniconda@v3 @@ -31,6 +44,10 @@ jobs: activate-environment: cppimport environment-file: environment.yml python-version: ${{ matrix.python-version }} + - name: Install MinGW-w64 compiler (Windows MinGW) + if: ${{ matrix.os == 'windows-latest' && matrix.compiler == 'mingw' }} + run: | + conda install -y -c conda-forge gxx_win-64 - name: Install cppimport run: | pip install --no-deps -e . @@ -43,12 +60,20 @@ jobs: - name: Check import ordering with isort run: | isort --check . - - name: Test + - name: Test (macOS) if: ${{ matrix.os == 'macos-latest' }} run: | CFLAGS='-stdlib=libc++' pytest --cov=./ --cov-report=xml - - name: Test - if: ${{ matrix.os != 'macos-latest' }} + - name: Test (Windows MinGW) + if: ${{ matrix.os == 'windows-latest' && matrix.compiler == 'mingw' }} + run: | + pytest --cov=./ --cov-report=xml + - name: Test (Windows MSVC) + if: ${{ matrix.os == 'windows-latest' && matrix.compiler == 'msvc' }} + run: | + pytest --cov=./ --cov-report=xml + - name: Test (Linux) + if: ${{ matrix.os == 'ubuntu-latest' }} run: | pytest --cov=./ --cov-report=xml build-and-publish: @@ -59,13 +84,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: mamba-version: "*" channels: conda-forge activate-environment: cppimport environment-file: environment.yml - python-version: ${{ matrix.python-version }} - name: Get history and tags for SCM versioning to work run: | git fetch --prune --unshallow diff --git a/.gitignore b/.gitignore index 79dfb27..d15c846 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ *.swp *.pyc +*.pyd +*.pyd.old *.so *.cppimporthash .rendered.* @@ -15,4 +17,5 @@ htmlcov **/.DS_Store .eggs cppimport/_version.py -*.lock \ No newline at end of file +*.lock +logs \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 14024e5..bc761e8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,15 +1,15 @@ repos: - repo: https://github.com/psf/black - rev: 20.8b1 + rev: 24.10.0 hooks: - id: black language_version: python3 - - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.4 + - repo: https://github.com/pycqa/flake8 + rev: 7.1.2 hooks: - id: flake8 - repo: https://github.com/pycqa/isort - rev: 5.7.0 + rev: 5.13.2 hooks: - id: isort name: isort (python) diff --git a/cppimport/build_module.py b/cppimport/build_module.py index 830e715..a09364e 100644 --- a/cppimport/build_module.py +++ b/cppimport/build_module.py @@ -131,6 +131,20 @@ def copy_extensions_to_source(self): src_filename = os.path.join(self.build_lib, filename) dest_filename = os.path.join(ext.libdest, os.path.basename(filename)) + # On Windows, a loaded .pyd cannot be deleted or overwritten, but + # it can be renamed. Move the old file out of the way first. + if sys.platform == "win32" and os.path.exists(dest_filename): + old_filename = dest_filename + ".old" + try: + if os.path.exists(old_filename): + os.remove(old_filename) + except OSError: + pass + try: + os.rename(dest_filename, old_filename) + except OSError: + pass + distutils.file_util.copy_file( src_filename, dest_filename, verbose=self.verbose ) diff --git a/cppimport/templating.py b/cppimport/templating.py index aaa9e20..0a8c918 100644 --- a/cppimport/templating.py +++ b/cppimport/templating.py @@ -65,25 +65,26 @@ def setup_pybind11(cfg): import pybind11 cfg["include_dirs"] += [pybind11.get_include(), pybind11.get_include(True)] - + # Prefix with c++11 arg instead of suffix so that if a user specifies c++14 # (or later!) then it won't be overridden. prefix_args = ["-std=c++11", "-fvisibility=hidden"] # MSVC compiler do not support "-std=c++11" nor "-fvisibility=hidden" if os.name == "nt": import sys - if sys.version_info[0] >=3 and sys.version_info[1] > 11: + + if sys.version_info[0] >= 3 and sys.version_info[1] > 11: from setuptools._distutils import ccompiler else: from distutils import ccompiler compiler = ccompiler.new_compiler() if compiler.compiler_type == "msvc": - for k in ["-std=c++11", "-fvisibility=hidden"]: # remove unsupported flags + for k in ["-std=c++11", "-fvisibility=hidden"]: # remove unsupported flags try: prefix_args.remove(k) except ValueError: pass - + cfg["compiler_args"] = prefix_args + cfg["compiler_args"]