Frequently asked questions#
Doesn’t my project need a setup.py
or setup.cfg
file?#
No, having a pyproject.toml
file is enough.
The setup.py
script and the setup.cfg
file are specific to the
Setuptools
build backend. It is not needed for other build systems, such as
py-build-cmake
,
Flit
, Poetry
etc.
Even when using Setuptools, it’s best to avoid setup.py
if you can, and to use
declarative configuration files like setup.cfg
and pyproject.toml
instead.
From the Setuptools quickstart guide:
The landscape of Python packaging is shifting and
Setuptools
has evolved to only provide backend support, no longer being the de-facto packaging tool in the market. Every python package must provide apyproject.toml
and specify the backend (build system) it wants to use. The distribution can then be generated with whatever tool that provides abuild sdist
-like functionality. While this may appear cumbersome, given the added pieces, it in fact tremendously enhances the portability of your package. The change is driven under PEP 517.
The
setup.py
file should be used only when custom scripting during the build is necessary. Examples are kept in this document to help people interested in maintaining or contributing to existing packages that usesetup.py
. Note that you can still keep most of configuration declarative insetup.cfg
orpyproject.toml
and usesetup.py
only for the parts not supported in those files (e.g. C extensions).
For more information about setup.py
and Python packaging in general, see:
The build fails. How can I find out what’s going on?#
To enable py-build-cmake’s verbose mode, pass the verbose
configuration setting,
or set the PY_BUILD_CMAKE_VERBOSE
environment variable:
python -m build . -C verbose # Option (1)
PY_BUILD_CMAKE_VERBOSE=1 python -m build . # Option (2)
Please see Verbose output and detailed information about the configuration and build process for more details.
How can I perform a clean rebuild?#
To remove all (temporary) build files and editable installations, simply remove
the hidden .py-build-cmake_cache
directory in the root of your project.
Please see Performing a clean rebuild for more details.
How to upload my package to PyPI?#
You’ll have to upload a single source distribution, and one binary wheel for each combination of Python version and platform you wish to support (see next section).
For the actual upload of the packages to PyPI, it is highly recommended to use trusted publishing:
The tttapa/py-build-cmake-example project includes a GitHub Actions workflow run that uses trusted publishing to upload the packages to PyPI.
How to build my package for many Python versions, operating systems, and architectures?#
In short: have a look at py-build-cmake-example: .github/workflows.
You can use a tool like cibuildwheel to automate the build process for this large matrix of platform/version combinations. Continuous integration providers like GitHub Actions also provide job matrix capabilities. For example, the py-build-cmake-example project uses the job matrices to build the package for multiple architectures and Python versions: tttapa/py-build-cmake-example
build-linux:
name: Build (${{ matrix.host }})
needs: [build-sdist]
runs-on: ubuntu-latest
strategy:
matrix:
host: [x86_64-bionic-linux-gnu, aarch64-rpi3-linux-gnu, armv7-neon-linux-gnueabihf, armv6-rpi-linux-gnueabihf]
build-macos-windows:
name: Build (${{ matrix.cibw-arch }}-${{ matrix.os }})
needs: [build-sdist]
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- {os: windows-latest, cibw-arch: AMD64, arch: amd64}
- {os: windows-latest, cibw-arch: x86, arch: amd64_x86}
- {os: windows-latest, cibw-arch: ARM64, arch: amd64_arm64}
- {os: macos-latest, cibw-arch: universal2}
- {os: macos-latest, cibw-arch: x86_64}
- {os: macos-latest, cibw-arch: arm64}
The same workflow file also contains some steps to test the resulting wheels and upload them to to PyPI. It’s a good idea to check that the package version matches the Git tag before uploading anything.
To build Universal Wheels for macOS (that work on both Intel- and ARM-based machines), you can set the following environment variables:
export MACOSX_DEPLOYMENT_TARGET='10.13'
export _PYTHON_HOST_PLATFORM='macosx-10.13-universal2'
export ARCHFLAGS='-arch arm64 -arch x86_64'
To build packages for multiple architectures on Linux, I recommend cross-compilation. This ensures that your package doesn’t depend on any libraries (including GLIBC) from the build server. You can use the modern GCC cross-compilers from tttapa/python-dev, which also include pre-built cross-compiled versions of Python. The py-build-cmake-example project simplifies the process even further by using the Conan package manager to automate the installation of the appropriate cross-compilation toolchains and cross-compiled Python libraries. See tttapa/py-build-cmake-example for details.
How to set the minimum supported macOS version#
py-build-cmake respects the standard MACOSX_DEPLOYMENT_TARGET
environment
variable, and uses its value to determine the appropriate Wheel platform tag.
It also passes the value on to CMake, which will in turn pass it on to the
compiler, determining the compatibility of the generated binaries. If
MACOSX_DEPLOYMENT_TARGET
is not set, py-build-cmake uses the value from
sysconfig.get_config_var("MACOSX_DEPLOYMENT_TARGET")
.
Note that the variable needs to be set in the environment used when invoking
py-build-cmake. Setting MACOSX_DEPLOYMENT_TARGET
or CMAKE_OSX_DEPLOYMENT_TARGET
using the options in the [tool.py-build-cmake.cmake]
section of pyproject.toml
is not supported. py-build-cmake ignores any MACOSX_DEPLOYMENT_TARGET
values in the config, and overwrites them with the value from its own
environment or sysconfig
.
If you’re using cibuildwheel, you can
specify the value of MACOSX_DEPLOYMENT_TARGET
in your pyproject.toml
file
as follows:
[tool.cibuildwheel.macos.environment]
MACOSX_DEPLOYMENT_TARGET = "11.0"