diff --git a/docs/Config.md b/docs/Config.md
index 3b3eea8..08cc871 100644
--- a/docs/Config.md
+++ b/docs/Config.md
@@ -52,7 +52,14 @@ Defines how to build the project to package. If omitted, py-build-cmake will pro
| `install_args` | Extra arguments passed to the install step.
For example: `install_args = ["--strip"]` | list+ | `[]` |
| `install_components` | List of components to install, the install step is executed once for each component, with the option `--component >`.
Use an empty string to specify the default component. | list | `['']` |
| `env` | Environment variables to set when running CMake. Supports variable expansion using `${VAR}` (but not `$VAR`).
For example: `env = { "CMAKE_PREFIX_PATH" = "${HOME}/.local" }` | dict | `{}` |
-| `pure_python` | Indicate that this package contains no platform-specific binaries, only Python scripts and other platform-agnostic files. It causes the Wheel tags to be set to `py3-none-any`.
For example: `pure_python = true` | bool | `false` |
+
+## wheel
+Defines how to create the Wheel package.
+
+| Option | Description | Type | Default |
+|--------|-------------|------|---------|
+| `pure_python` | Indicate that this package contains no platform-specific binaries, only Python scripts and other platform-agnostic files. Setting this value to true causes the Wheel tags to be set to `py3-none-any`. If unset, the value depends on whether the `cmake` option is set.
For example: `pure_python = true` | bool | `none` |
+| `python_tag` | Override the default Python tag for the Wheel package.
If your package contains any Python extension modules, you want to set this to `auto`.
For details about platform compatibility tags, see the PyPA specification: https://packaging.python.org/en/latest/specifications/platform-compatibility-tags
For example: `python_tag = 'py2.py3'` | string | `'auto'` |
| `python_abi` | Override the default ABI tag for the Wheel package.
For packages with a Python extension module that make use of the full Python C API, this option should be set to `auto`.
If your package does not contain Python extension modules (e.g. because it only includes executables to run as a subprocess, or only shared library files to be loaded using `ctypes`), you can set this to `none`.
If your package only includes Python extension modules that use the CPython stable ABI, set this to `abi3` (see also `abi3_minimum_cpython_version` below).
For details about platform compatibility tags, see the PyPA specification: https://packaging.python.org/en/latest/specifications/platform-compatibility-tags
For example: `python_abi = 'none'` | `'auto'` \| `'none'` \| `'abi3'` | `'auto'` |
| `abi3_minimum_cpython_version` | If `python_abi` is set to `abi3`, only use the stable CPython API for CPython version that are newer than `abi3_minimum_version`. Useful for nanobind, which supports the stable ABI for CPython 12 and later.
The Python version is encoded as a single integer, consisting of the major and minor version numbers, without a dot.
For example: `abi3_minimum_cpython_version = 312` | int | `32` |
@@ -74,6 +81,7 @@ Override options for Linux.
| `editable` | Linux-specific editable options.
Inherits from: `/pyproject.toml/tool/py-build-cmake/editable` | | `none` |
| `sdist` | Linux-specific sdist options.
Inherits from: `/pyproject.toml/tool/py-build-cmake/sdist` | | `none` |
| `cmake` | Linux-specific CMake options.
Inherits from: `/pyproject.toml/tool/py-build-cmake/cmake` | | `none` |
+| `wheel` | Linux-specific Wheel options.
Inherits from: `/pyproject.toml/tool/py-build-cmake/wheel` | | `none` |
## windows
Override options for Windows.
@@ -83,6 +91,7 @@ Override options for Windows.
| `editable` | Windows-specific editable options.
Inherits from: `/pyproject.toml/tool/py-build-cmake/editable` | | `none` |
| `sdist` | Windows-specific sdist options.
Inherits from: `/pyproject.toml/tool/py-build-cmake/sdist` | | `none` |
| `cmake` | Windows-specific CMake options.
Inherits from: `/pyproject.toml/tool/py-build-cmake/cmake` | | `none` |
+| `wheel` | Windows-specific Wheel options.
Inherits from: `/pyproject.toml/tool/py-build-cmake/wheel` | | `none` |
## mac
Override options for Mac.
@@ -92,6 +101,7 @@ Override options for Mac.
| `editable` | Mac-specific editable options.
Inherits from: `/pyproject.toml/tool/py-build-cmake/editable` | | `none` |
| `sdist` | Mac-specific sdist options.
Inherits from: `/pyproject.toml/tool/py-build-cmake/sdist` | | `none` |
| `cmake` | Mac-specific CMake options.
Inherits from: `/pyproject.toml/tool/py-build-cmake/cmake` | | `none` |
+| `wheel` | Mac-specific Wheel options.
Inherits from: `/pyproject.toml/tool/py-build-cmake/wheel` | | `none` |
## cross
Causes py-build-cmake to cross-compile the project. See https://tttapa.github.io/py-build-cmake/Cross-compilation.html for more information.
@@ -110,6 +120,7 @@ Causes py-build-cmake to cross-compile the project. See https://tttapa.github.io
| `editable` | Override editable options when cross-compiling.
Inherits from: `/pyproject.toml/tool/py-build-cmake/editable` | | `none` |
| `sdist` | Override sdist options when cross-compiling.
Inherits from: `/pyproject.toml/tool/py-build-cmake/sdist` | | `none` |
| `cmake` | Override CMake options when cross-compiling.
Inherits from: `/pyproject.toml/tool/py-build-cmake/cmake` | | `none` |
+| `wheel` | Override Wheel options when cross-compiling.
Inherits from: `/pyproject.toml/tool/py-build-cmake/wheel` | | `none` |
# Local overrides
diff --git a/examples/minimal-program/README.md b/examples/minimal-program/README.md
index 0370c26..6d22d8d 100644
--- a/examples/minimal-program/README.md
+++ b/examples/minimal-program/README.md
@@ -8,7 +8,7 @@ should be in `${PY_BUILD_CMAKE_PACKAGE_NAME}-${PY_BUILD_CMAKE_PACKAGE_VERSION}.d
as per [PEP 427](https://peps.python.org/pep-0427/). Pip will then automatically
install it to a folder that's in the `PATH`.
Since there are no Python extension modules with specific ABI requirements for
-the Python interpreter, `tool.py-build-cmake.cmake.python_abi` is set
+the Python interpreter, `tool.py-build-cmake.wheel.python_abi` is set
to `'none'`.
For more information about the file structure and the configuration files,
diff --git a/examples/minimal-program/pyproject.toml b/examples/minimal-program/pyproject.toml
index 4d975b3..19d7806 100644
--- a/examples/minimal-program/pyproject.toml
+++ b/examples/minimal-program/pyproject.toml
@@ -41,6 +41,7 @@ build_args = ["-j"]
install_components = ["python_binaries"]
find_python = false
find_python3 = false
+[tool.py-build-cmake.wheel]
python_abi = "none"
[tool.pytest.ini_options]
diff --git a/examples/nanobind-project/pyproject.toml b/examples/nanobind-project/pyproject.toml
index 20812ef..ced18ae 100644
--- a/examples/nanobind-project/pyproject.toml
+++ b/examples/nanobind-project/pyproject.toml
@@ -49,6 +49,7 @@ build_tool_args = []
install_args = ["--verbose"]
install_components = ["python_modules"]
env = {}
+[tool.py-build-cmake.wheel]
python_abi = 'abi3'
abi3_minimum_cpython_version = 312
diff --git a/src/py_build_cmake/build.py b/src/py_build_cmake/build.py
index 516703c..e915571 100644
--- a/src/py_build_cmake/build.py
+++ b/src/py_build_cmake/build.py
@@ -327,7 +327,8 @@ def create_wheel(
whl = Wheel()
whl.name = package_info.norm_name
whl.version = package_info.version
- pure = is_pure(cmake_cfg)
+ wheel_cfg = _BuildBackend.get_wheel_config(cfg)
+ pure = is_pure(wheel_cfg, cmake_cfg)
libdir = "purelib" if pure else "platlib"
staging_dir = paths.pkg_staging_dir
whl_paths = {"prefix": str(staging_dir), libdir: str(staging_dir)}
@@ -336,10 +337,10 @@ def create_wheel(
tags = {"pyver": ["py3"]}
elif cfg.cross:
tags = get_cross_tags(cfg.cross)
- tags = convert_wheel_tags(tags, cmake_cfg)
+ tags = convert_wheel_tags(tags, wheel_cfg, cmake_cfg)
else:
tags = get_native_tags()
- tags = convert_wheel_tags(tags, cmake_cfg)
+ tags = convert_wheel_tags(tags, wheel_cfg, cmake_cfg)
wheel_path = whl.build(whl_paths, tags=tags, wheel_version=(1, 0))
logger.debug("Built Wheel: %s", wheel_path)
return str(Path(wheel_path).relative_to(paths.wheel_dir))
@@ -353,6 +354,13 @@ def get_cmake_config(cfg: Config):
else:
return cfg.cmake["cross"]
+ @staticmethod
+ def get_wheel_config(cfg: Config):
+ if cfg.cross is None:
+ return cfg.wheel[util.get_os_name()]
+ else:
+ return cfg.wheel["cross"]
+
# --- Building sdists -----------------------------------------------------
def do_build_sdist(self, sdist_directory, config_settings):
diff --git a/src/py_build_cmake/common/__init__.py b/src/py_build_cmake/common/__init__.py
index 4bf1f4f..b158a37 100644
--- a/src/py_build_cmake/common/__init__.py
+++ b/src/py_build_cmake/common/__init__.py
@@ -99,6 +99,7 @@ class Config:
editable: dict[str, dict[str, Any]] = field(default_factory=dict)
sdist: dict[str, dict[str, Any]] = field(default_factory=dict)
cmake: dict[str, dict[str, Any]] | None = field(default=None)
+ wheel: dict[str, dict[str, Any]] = field(default_factory=dict)
stubgen: dict[str, Any] | None = field(default=None)
cross: dict[str, Any] | None = field(default=None)
diff --git a/src/py_build_cmake/config/load.py b/src/py_build_cmake/config/load.py
index 8472e06..4792b6f 100644
--- a/src/py_build_cmake/config/load.py
+++ b/src/py_build_cmake/config/load.py
@@ -292,6 +292,13 @@ def get_sdist_cludes(v: ValueReference) -> dict[str, Any]:
}
cfg.cmake = cfg.cmake or None
+ # Store the Wheel configuration
+ cfg.wheel = {
+ os: cast(Dict[str, Any], pbc_value_ref.get_value(ConfPath((os, "wheel"))))
+ for os in ("linux", "windows", "mac", "cross")
+ if pbc_value_ref.is_value_set(ConfPath((os, "wheel")))
+ }
+
# Store stubgen configuration
s = "stubgen"
if pbc_value_ref.is_value_set(s):
@@ -302,7 +309,7 @@ def get_sdist_cludes(v: ValueReference) -> dict[str, Any]:
if pbc_value_ref.is_value_set(s):
cfg.cross = copy(cast(Optional[Dict[str, Any]], pbc_value_ref.get_value(s)))
if cfg.cross is not None:
- for k in ("cmake", "sdist", "editable"):
+ for k in ("cmake", "wheel", "sdist", "editable"):
cfg.cross.pop(k, None)
# Check for incompatible options
diff --git a/src/py_build_cmake/config/options/pyproject_options.py b/src/py_build_cmake/config/options/pyproject_options.py
index 15387c1..22532c1 100644
--- a/src/py_build_cmake/config/options/pyproject_options.py
+++ b/src/py_build_cmake/config/options/pyproject_options.py
@@ -234,13 +234,35 @@ def get_options(project_path: Path | PurePosixPath, *, test: bool = False):
"env = { \"CMAKE_PREFIX_PATH\" "
"= \"${HOME}/.local\" }",
default=DefaultValueValue({})),
+ ]) # fmt: skip
+
+ # [tool.py-build-cmake.wheel]
+ wheel = pbc.insert(
+ ConfigOption("wheel",
+ "Defines how to create the Wheel package.",
+ default=DefaultValueValue({}),
+ )) # fmt: skip
+ wheel_pth = ConfPath.from_string("pyproject.toml/tool/py-build-cmake/wheel")
+ wheel.insert_multiple([
BoolConfigOption("pure_python",
"Indicate that this package contains no platform-"
"specific binaries, only Python scripts and other "
- "platform-agnostic files. It causes the Wheel tags "
- "to be set to `py3-none-any`.",
- "pure_python = true",
- default=DefaultValueValue(False)),
+ "platform-agnostic files. Setting this value to true "
+ "causes the Wheel tags to be set to `py3-none-any`. "
+ "If unset, the value depends on whether the `cmake` "
+ "option is set.",
+ "pure_python = true"),
+ StringConfigOption("python_tag",
+ "Override the default Python tag for the Wheel "
+ "package.\n"
+ "If your package contains any Python extension "
+ "modules, you want to set this to `auto`.\n"
+ "For details about platform compatibility tags, "
+ "see the PyPA specification: "
+ "https://packaging.python.org/en/latest/"
+ "specifications/platform-compatibility-tags",
+ "python_tag = 'py2.py3'",
+ default=DefaultValueValue("auto")),
EnumConfigOption("python_abi",
"Override the default ABI tag for the Wheel package.\n"
"For packages with a Python extension module that "
@@ -318,6 +340,10 @@ def get_options(project_path: Path | PurePosixPath, *, test: bool = False):
f"{system}-specific CMake options.",
inherit_from=cmake_pth,
create_if_inheritance_target_exists=True),
+ ConfigOption("wheel",
+ f"{system}-specific Wheel options.",
+ inherit_from=wheel_pth,
+ create_if_inheritance_target_exists=True),
]) # fmt: skip
# [tool.py-build-cmake.cross]
@@ -413,6 +439,10 @@ def get_options(project_path: Path | PurePosixPath, *, test: bool = False):
"Override CMake options when cross-compiling.",
inherit_from=cmake_pth,
create_if_inheritance_target_exists=True),
+ ConfigOption("wheel",
+ "Override Wheel options when cross-compiling.",
+ inherit_from=wheel_pth,
+ create_if_inheritance_target_exists=True),
]) # fmt: skip
return root
diff --git a/src/py_build_cmake/export/tags.py b/src/py_build_cmake/export/tags.py
index 08eb827..70a6431 100644
--- a/src/py_build_cmake/export/tags.py
+++ b/src/py_build_cmake/export/tags.py
@@ -19,19 +19,19 @@ def get_cross_tags(crosscfg: dict[str, Any]) -> WheelTags:
return tags
-def convert_abi_tag(abi_tag: str, cmake_cfg: dict | None) -> str:
+def convert_abi_tag(abi_tag: str, wheel_cfg: dict, cmake_cfg: dict | None) -> str:
"""Set the ABI tag to 'none' or 'abi3', depending on the config options
specified by the user."""
if not cmake_cfg:
return "none"
- elif cmake_cfg["python_abi"] == "auto":
+ elif wheel_cfg["python_abi"] == "auto":
return abi_tag
- elif cmake_cfg["python_abi"] == "none":
+ elif wheel_cfg["python_abi"] == "none":
return "none"
- elif cmake_cfg["python_abi"] == "abi3":
+ elif wheel_cfg["python_abi"] == "abi3":
# Only use abi3 if we're actually building for CPython
m = re.match(r"^cp(\d+).*$", abi_tag)
- if m and int(m[1]) >= cmake_cfg["abi3_minimum_cpython_version"]:
+ if m and int(m[1]) >= wheel_cfg["abi3_minimum_cpython_version"]:
return "abi3"
return abi_tag
else:
@@ -39,19 +39,23 @@ def convert_abi_tag(abi_tag: str, cmake_cfg: dict | None) -> str:
raise AssertionError(msg)
-def convert_wheel_tags(tags: dict[str, list[str]], cmake_cfg: dict | None) -> WheelTags:
+def convert_wheel_tags(
+ tags: dict[str, list[str]], wheel_cfg: dict, cmake_cfg: dict | None
+) -> WheelTags:
"""Apply convert_abi_tag to each of the abi tags."""
tags = copy(tags)
- cvt_abi = lambda tag: convert_abi_tag(tag, cmake_cfg)
+ cvt_abi = lambda tag: convert_abi_tag(tag, wheel_cfg, cmake_cfg)
tags["abi"] = list(map(cvt_abi, tags["abi"]))
- if "none" in tags["abi"]:
+ if wheel_cfg["python_tag"] != "auto":
+ tags["pyver"] = wheel_cfg["python_tag"]
+ elif "none" in tags["abi"]:
tags["pyver"] = ["py3"]
return tags
-def is_pure(cmake_cfg: dict | None) -> bool:
+def is_pure(wheel_cfg: dict, cmake_cfg: dict | None) -> bool:
"""Check if the package is a pure-Python package without platform-
specific binaries."""
- if not cmake_cfg:
- return True
- return cmake_cfg["pure_python"]
+ if "pure_python" in wheel_cfg:
+ return wheel_cfg["pure_python"]
+ return not cmake_cfg
diff --git a/tests/test_config_load.py b/tests/test_config_load.py
index d884de8..4884e39 100644
--- a/tests/test_config_load.py
+++ b/tests/test_config_load.py
@@ -140,9 +140,6 @@ def test_inherit_cross_cmake():
"foo": "bar",
"crosscompiling": "true",
},
- "pure_python": False,
- "python_abi": "auto",
- "abi3_minimum_cpython_version": 32,
},
"linux": {
"build_type": "Release",
@@ -162,9 +159,6 @@ def test_inherit_cross_cmake():
"install_components": ["linux_install"],
"minimum_version": "3.15",
"env": {"foo": "bar"},
- "pure_python": False,
- "python_abi": "auto",
- "abi3_minimum_cpython_version": 32,
},
"windows": {
"build_type": "Release",
@@ -184,9 +178,6 @@ def test_inherit_cross_cmake():
"install_components": ["all_install", "win_install"],
"minimum_version": "3.15",
"env": {"foo": "bar"},
- "pure_python": False,
- "python_abi": "auto",
- "abi3_minimum_cpython_version": 32,
},
"mac": {
"build_type": "Release",
@@ -206,7 +197,26 @@ def test_inherit_cross_cmake():
"install_components": ["all_install"],
"minimum_version": "3.15",
"env": {"foo": "bar"},
- "pure_python": False,
+ },
+ }
+ assert conf.wheel == {
+ "cross": {
+ "python_tag": "auto",
+ "python_abi": "auto",
+ "abi3_minimum_cpython_version": 32,
+ },
+ "linux": {
+ "python_tag": "auto",
+ "python_abi": "auto",
+ "abi3_minimum_cpython_version": 32,
+ },
+ "windows": {
+ "python_tag": "auto",
+ "python_abi": "auto",
+ "abi3_minimum_cpython_version": 32,
+ },
+ "mac": {
+ "python_tag": "auto",
"python_abi": "auto",
"abi3_minimum_cpython_version": 32,
},
@@ -288,9 +298,6 @@ def test_real_config_no_cross():
"install_components": ["linux_install"],
"minimum_version": "3.15",
"env": {"foo": "bar"},
- "pure_python": False,
- "python_abi": "auto",
- "abi3_minimum_cpython_version": 32,
},
"windows": {
"build_type": "Release",
@@ -310,9 +317,6 @@ def test_real_config_no_cross():
"install_components": ["win_install"],
"minimum_version": "3.15",
"env": {"foo": "bar"},
- "pure_python": False,
- "python_abi": "auto",
- "abi3_minimum_cpython_version": 32,
},
"mac": {
"build_type": "Release",
@@ -332,7 +336,21 @@ def test_real_config_no_cross():
"install_components": [""],
"minimum_version": "3.15",
"env": {"foo": "bar"},
- "pure_python": False,
+ },
+ }
+ assert conf.wheel == {
+ "linux": {
+ "python_tag": "auto",
+ "python_abi": "auto",
+ "abi3_minimum_cpython_version": 32,
+ },
+ "windows": {
+ "python_tag": "auto",
+ "python_abi": "auto",
+ "abi3_minimum_cpython_version": 32,
+ },
+ "mac": {
+ "python_tag": "auto",
"python_abi": "auto",
"abi3_minimum_cpython_version": 32,
},
@@ -467,6 +485,8 @@ def test_real_config_cli_override():
"build_tool_args": ["-a", "-b"],
"find_python": False,
"find_python3": True,
+ },
+ "wheel": {
"python_abi": "abi3",
},
"linux": {
@@ -494,8 +514,8 @@ def test_real_config_cli_override():
linux.cmake.options.FOOBAR=+"xyz"
linux.cmake.options.FOOBAR-="def"
linux.cmake.args-=["arg3"]
- linux.cmake.python_abi=!
- mac.cmake.python_abi="none"
+ linux.wheel.python_abi=!
+ mac.wheel.python_abi="none"
mac.cmake.args=!
mac.cmake.env=!
mac.cmake.find_python=!
@@ -543,9 +563,6 @@ def test_real_config_cli_override():
"install_components": ["linux_install"],
"minimum_version": "3.15",
"env": {"PATH": "$HOME/opt" + os.pathsep + "/usr/bin", "foo": "bar"},
- "pure_python": False,
- "python_abi": "auto",
- "abi3_minimum_cpython_version": 32,
},
"windows": {
"build_type": "Release",
@@ -565,9 +582,6 @@ def test_real_config_cli_override():
"install_components": ["win_install"],
"minimum_version": "3.15",
"env": {"foo": "bar"},
- "pure_python": False,
- "python_abi": "abi3",
- "abi3_minimum_cpython_version": 32,
},
"mac": {
"build_type": "Release",
@@ -587,7 +601,21 @@ def test_real_config_cli_override():
"install_components": [""],
"minimum_version": "3.15",
"env": {},
- "pure_python": False,
+ },
+ }
+ assert conf.wheel == {
+ "linux": {
+ "python_tag": "auto",
+ "python_abi": "auto",
+ "abi3_minimum_cpython_version": 32,
+ },
+ "windows": {
+ "python_tag": "auto",
+ "python_abi": "abi3",
+ "abi3_minimum_cpython_version": 32,
+ },
+ "mac": {
+ "python_tag": "auto",
"python_abi": "none",
"abi3_minimum_cpython_version": 32,
},