From dc77f0390132a487948b43f1601b26a6113e34f9 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 17 Sep 2020 21:34:31 -0400 Subject: [PATCH] feat: support setup helpers --- .appveyor.yml | 1 - .github/workflows/wheels.yml | 141 ++++++++++++++++++++++++++++++++++ .gitignore | 144 +++++++++++++++++++++++++++++++++-- .travis.yml | 1 - setup.py | 142 +++++++++------------------------- 5 files changed, 317 insertions(+), 112 deletions(-) create mode 100644 .github/workflows/wheels.yml diff --git a/.appveyor.yml b/.appveyor.yml index 770d9a5..5ed92a1 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -33,7 +33,6 @@ build_script: - ps: | if ($env:PYTHON) { python setup.py sdist - python -m pip install 'pybind11>=2.3' cd dist python -m pip install --verbose python_example-0.0.1.tar.gz cd .. diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml new file mode 100644 index 0000000..4b03e86 --- /dev/null +++ b/.github/workflows/wheels.yml @@ -0,0 +1,141 @@ +name: Wheels + +on: + workflow_dispatch: + pull_request: + branches: + - master + push: + branches: + - master + release: + types: + - published + +env: + CIBW_TEST_EXTRAS: test + CIBW_TEST_COMMAND: pytest {project}/tests + + +jobs: + build_sdist: + name: Build SDist + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + + - name: Install deps + run: python -m pip install "setuptools>=42" "setuptools_scm[toml]>=4.1.0" twine + + - name: Build SDist + run: python setup.py sdist + + - name: Check metadata + run: twine check dist/* + + - uses: actions/upload-artifact@v2 + with: + path: dist/*.tar.gz + + + build_wheels: + name: Wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-python@v2 + + - name: Install cibuildwheel + run: python -m pip install cibuildwheel==1.6.0 + + - name: Build wheel + run: python -m cibuildwheel --output-dir wheelhouse + env: + CIBW_SKIP: cp27-win* + + - name: Show files + run: ls -lh wheelhouse + shell: bash + + - name: Verify clean directory + run: git diff --exit-code + shell: bash + + - name: Upload wheels + uses: actions/upload-artifact@v2 + with: + path: wheelhouse/*.whl + + + # Windows 2.7 (requires workaround for MSVC 2008 replacement) + build_win27_wheels: + name: Py 2.7 wheels on Windows + runs-on: windows-latest + + steps: + - uses: actions/checkout@v1 + with: + submodules: true + + - uses: actions/setup-python@v2 + + - name: Install cibuildwheel + run: python -m pip install cibuildwheel==1.6.0 + + - uses: ilammy/msvc-dev-cmd@v1 + + - name: Build 64-bit wheel + run: python -m cibuildwheel --output-dir wheelhouse + env: + CIBW_BUILD: cp27-win_amd64 + DISTUTILS_USE_SDK: 1 + MSSdk: 1 + + - uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x86 + + - name: Build 32-bit wheel + run: python -m cibuildwheel --output-dir wheelhouse + env: + CIBW_BUILD: cp27-win32 + DISTUTILS_USE_SDK: 1 + MSSdk: 1 + + - name: Show files + run: ls -lh wheelhouse + shell: bash + + - name: Verify clean directory + run: git diff --exit-code + shell: bash + + - uses: actions/upload-artifact@v2 + with: + path: wheelhouse/*.whl + + + upload_all: + name: Upload if release + needs: [build_wheels, build_win27_wheels, build_sdist] + runs-on: ubuntu-latest + if: github.event_name == 'release' && github.event.action == 'published' + + steps: + - uses: actions/setup-python@v2 + + - uses: actions/download-artifact@v2 + with: + name: artifact + path: dist + + - uses: pypa/gh-action-pypi-publish@v1.3.1 + with: + user: __token__ + password: ${{ secrets.pypi_password }} diff --git a/.gitignore b/.gitignore index 839282b..78d45bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,140 @@ +# Using https://github.com/github/gitignore/blob/master/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions *.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg MANIFEST -*.py[cod] -*.egg-info -.DS_Store -_build -_generate -build + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ diff --git a/.travis.yml b/.travis.yml index 18c5e22..00b06da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,7 +47,6 @@ install: - | if [ -n "$PYTHON" ]; then python setup.py sdist - python -m pip install 'pybind11>=2.3' python -m pip install --verbose dist/*.tar.gz elif [ -n "$CONDA" ]; then conda build conda.recipe --python $CONDA diff --git a/setup.py b/setup.py index 94e1d02..5800803 100644 --- a/setup.py +++ b/setup.py @@ -1,117 +1,51 @@ -from setuptools import setup, Extension -from setuptools.command.build_ext import build_ext -import sys -import setuptools - -__version__ = '0.0.1' - +from setuptools import setup -class get_pybind_include(object): - """Helper class to determine the pybind11 include path +# With setup_requires, this runs twice - once without setup_requires, and once +# with. The build only happens the second time. +try: + from pybind11.setup_helpers import Pybind11Extension, build_ext + from pybind11 import get_cmake_dir +except ImportError: + from setuptools import Extension as Pybind11Extension + from setuptools.command.build_ext import build_ext - The purpose of this class is to postpone importing pybind11 - until it is actually installed, so that the ``get_include()`` - method can be invoked. """ +import sys - def __str__(self): - import pybind11 - return pybind11.get_include() +__version__ = "0.0.1" +# The main interface is through Pybind11Extension. +# * You can add cxx_std=11/14/17, and then build_ext can be removed. +# * You can set include_pybind11=false to add the include directory yourself, +# say from a submodule. +# +# Note: +# Sort input source files if you glob sources to ensure bit-for-bit +# reproducible builds (https://github.com/pybind/python_example/pull/53) ext_modules = [ - Extension( - 'python_example', - # Sort input source files to ensure bit-for-bit reproducible builds - # (https://github.com/pybind/python_example/pull/53) - sorted(['src/main.cpp']), - include_dirs=[ - # Path to pybind11 headers - get_pybind_include(), - ], - language='c++' - ), + Pybind11Extension("python_example", + ["src/main.cpp"], + # Example: passing in the version to the compiled code + define_macros = [('VERSION_INFO', '"{}"'.format(__version__))], + ), ] - -# cf http://bugs.python.org/issue26689 -def has_flag(compiler, flagname): - """Return a boolean indicating whether a flag name is supported on - the specified compiler. - """ - import tempfile - import os - with tempfile.NamedTemporaryFile('w', suffix='.cpp', delete=False) as f: - f.write('int main (int argc, char **argv) { return 0; }') - fname = f.name - try: - compiler.compile([fname], extra_postargs=[flagname]) - except setuptools.distutils.errors.CompileError: - return False - finally: - try: - os.remove(fname) - except OSError: - pass - return True - - -def cpp_flag(compiler): - """Return the -std=c++[11/14/17] compiler flag. - - The newer version is prefered over c++11 (when it is available). - """ - flags = ['-std=c++17', '-std=c++14', '-std=c++11'] - - for flag in flags: - if has_flag(compiler, flag): - return flag - - raise RuntimeError('Unsupported compiler -- at least C++11 support ' - 'is needed!') - - -class BuildExt(build_ext): - """A custom build extension for adding compiler-specific options.""" - c_opts = { - 'msvc': ['/EHsc'], - 'unix': [], - } - l_opts = { - 'msvc': [], - 'unix': [], - } - - if sys.platform == 'darwin': - darwin_opts = ['-stdlib=libc++', '-mmacosx-version-min=10.7'] - c_opts['unix'] += darwin_opts - l_opts['unix'] += darwin_opts - - def build_extensions(self): - ct = self.compiler.compiler_type - opts = self.c_opts.get(ct, []) - link_opts = self.l_opts.get(ct, []) - if ct == 'unix': - opts.append(cpp_flag(self.compiler)) - if has_flag(self.compiler, '-fvisibility=hidden'): - opts.append('-fvisibility=hidden') - - for ext in self.extensions: - ext.define_macros = [('VERSION_INFO', '"{}"'.format(self.distribution.get_version()))] - ext.extra_compile_args = opts - ext.extra_link_args = link_opts - build_ext.build_extensions(self) - - setup( - name='python_example', + name="python_example", version=__version__, - author='Sylvain Corlay', - author_email='sylvain.corlay@gmail.com', - url='https://github.com/pybind/python_example', - description='A test project using pybind11', - long_description='', + author="Sylvain Corlay", + author_email="sylvain.corlay@gmail.com", + url="https://github.com/pybind/python_example", + description="A test project using pybind11", + long_description="", ext_modules=ext_modules, - setup_requires=['pybind11>=2.5.0'], - cmdclass={'build_ext': BuildExt}, + # Note: You have to add pybind11 to both setup and install requires to make + # it available during the build. Using PEP 518's pyproject.toml is better! + setup_requires=["pybind11 @ git+https://github.com/pybind/pybind11.git@master"], + install_requires=["pybind11 @ git+https://github.com/pybind/pybind11.git@master"], + extras_require={"test": "pytest"}, + # Currently, build_ext only provides an optional "highest supported C++ + # level" feature, but in the future it may provide more features. + cmdclass={"build_ext": build_ext}, zip_safe=False, )