Skip to content

Commit af44b51

Browse files
committed
Meson Plugins
1 parent ff71935 commit af44b51

File tree

18 files changed

+959
-57
lines changed

18 files changed

+959
-57
lines changed

AGENTS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# AGENTS.md
2+
3+
This repository doesn't contain any agent specific instructions other than its README.md and its linked resources.

README.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,28 @@
11
# CPPython
2-
A Python management solution for C++ dependencies
2+
3+
A transparent Python management solution for C++ dependencies and building.
4+
5+
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE.md)
6+
[![PyPI version](https://img.shields.io/pypi/v/cppython.svg)](https://pypi.org/project/cppython/)
7+
8+
## Features
9+
10+
## Setup
11+
12+
See [Setup](https://synodic.github.io/cppython/setup) for setup instructions.
13+
14+
## Development
15+
16+
We use [pdm](https://pdm-project.org/en/latest/) as our build system and package manager. Scripts for development tasks are defined in `pyproject.toml` under the `[tool.pdm.scripts]` section.
17+
18+
See [Development](https://synodic.github.io/cppython/development) for additional build, test, and installation instructions.
19+
20+
For contribution guidelines, see [CONTRIBUTING.md](https://github.com/synodic/.github/blob/stable/CONTRIBUTING.md).
21+
22+
## Documentation
23+
24+
## License
25+
26+
This project is licensed under the MIT License — see [LICENSE.md](LICENSE.md) for details.
27+
28+
Copyright © 2026 Synodic Software

cppython/build/__init__.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
"""CPPython build backend wrapping scikit-build-core.
1+
"""CPPython build backend wrapping scikit-build-core and meson-python.
22
3-
This module provides PEP 517/518 build backend hooks that wrap scikit-build-core,
4-
automatically running CPPython's provider workflow before building
5-
to inject the generated toolchain file into the CMake configuration.
3+
This module provides PEP 517/518 build backend hooks that wrap scikit-build-core
4+
or meson-python depending on the active generator, automatically running
5+
CPPython's provider workflow before building to inject the generated
6+
toolchain or native/cross files into the build configuration.
67
78
Usage in pyproject.toml:
89
[build-system]

cppython/build/backend.py

Lines changed: 150 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,52 @@
1-
"""PEP 517 build backend implementation wrapping scikit-build-core.
1+
"""PEP 517 build backend implementation wrapping scikit-build-core and meson-python.
22
3-
This module provides the actual build hooks that delegate to scikit-build-core
3+
This module provides the actual build hooks that delegate to the appropriate
4+
underlying build backend (scikit-build-core for CMake, meson-python for Meson)
45
after running CPPython's preparation workflow.
56
"""
67

78
import logging
9+
import tomllib
810
from pathlib import Path
911
from typing import Any
1012

1113
from scikit_build_core import build as skbuild
1214

13-
from cppython.build.prepare import prepare_build
15+
from cppython.build.prepare import BuildPreparationResult, prepare_build
16+
from cppython.plugins.cmake.schema import CMakeSyncData
17+
from cppython.plugins.meson.schema import MesonSyncData
1418

1519
logger = logging.getLogger('cppython.build')
1620

1721

18-
def _inject_toolchain(config_settings: dict[str, Any] | None, toolchain_file: Path | None) -> dict[str, Any]:
22+
def _is_meson_project() -> bool:
23+
"""Detect if the current project uses Meson by checking pyproject.toml.
24+
25+
Looks for ``[tool.cppython.generator]`` containing "meson" or the
26+
presence of a ``meson.build`` file in the source directory.
27+
28+
Returns:
29+
True if the project appears to be Meson-based
30+
"""
31+
source_dir = Path.cwd()
32+
33+
# Check pyproject.toml for cppython generator configuration
34+
pyproject_path = source_dir / 'pyproject.toml'
35+
if pyproject_path.exists():
36+
with open(pyproject_path, 'rb') as f:
37+
data = tomllib.load(f)
38+
generator = data.get('tool', {}).get('cppython', {}).get('generator', '')
39+
if isinstance(generator, str) and 'meson' in generator.lower():
40+
return True
41+
42+
# Fallback: check for meson.build file
43+
if (source_dir / 'meson.build').exists():
44+
return True
45+
46+
return False
47+
48+
49+
def _inject_cmake_toolchain(config_settings: dict[str, Any] | None, toolchain_file: Path | None) -> dict[str, Any]:
1950
"""Inject the toolchain file into config settings for scikit-build-core.
2051
2152
Args:
@@ -49,28 +80,95 @@ def _inject_toolchain(config_settings: dict[str, Any] | None, toolchain_file: Pa
4980
return settings
5081

5182

52-
def _prepare_and_get_settings(
83+
def _inject_meson_files(
5384
config_settings: dict[str, Any] | None,
85+
native_file: Path | None,
86+
cross_file: Path | None,
5487
) -> dict[str, Any]:
55-
"""Run CPPython preparation and merge toolchain into config settings.
88+
"""Inject native/cross files into config settings for meson-python.
89+
90+
Args:
91+
config_settings: The original config settings (may be None)
92+
native_file: Path to the Meson native file to inject
93+
cross_file: Path to the Meson cross file to inject
94+
95+
Returns:
96+
Updated config settings with Meson files injected
97+
"""
98+
settings = dict(config_settings) if config_settings else {}
99+
100+
setup_args_key = 'setup-args'
101+
existing_args = settings.get(setup_args_key, '')
102+
103+
args_to_add: list[str] = []
104+
105+
if native_file and native_file.exists():
106+
native_arg = f'--native-file={native_file.absolute()}'
107+
if '--native-file' not in existing_args:
108+
args_to_add.append(native_arg)
109+
logger.info('CPPython: Injected --native-file=%s', native_file)
110+
else:
111+
logger.info('CPPython: User-specified native file takes precedence')
112+
113+
if cross_file and cross_file.exists():
114+
cross_arg = f'--cross-file={cross_file.absolute()}'
115+
if '--cross-file' not in existing_args:
116+
args_to_add.append(cross_arg)
117+
logger.info('CPPython: Injected --cross-file=%s', cross_file)
118+
else:
119+
logger.info('CPPython: User-specified cross file takes precedence')
120+
121+
if args_to_add:
122+
if existing_args:
123+
settings[setup_args_key] = f'{existing_args};' + ';'.join(args_to_add)
124+
else:
125+
settings[setup_args_key] = ';'.join(args_to_add)
126+
127+
return settings
128+
129+
130+
def _prepare_and_get_result(
131+
config_settings: dict[str, Any] | None,
132+
) -> tuple[BuildPreparationResult, dict[str, Any]]:
133+
"""Run CPPython preparation and merge config into settings.
56134
57135
Args:
58136
config_settings: The original config settings
59137
60138
Returns:
61-
Config settings with CPPython toolchain injected
139+
Tuple of (preparation result, updated config settings)
62140
"""
63141
# Determine source directory (current working directory during build)
64142
source_dir = Path.cwd()
65143

66144
# Run CPPython preparation
67-
toolchain_file = prepare_build(source_dir)
145+
result = prepare_build(source_dir)
146+
147+
# Inject settings based on sync data type
148+
settings = dict(config_settings) if config_settings else {}
68149

69-
# Inject toolchain into config settings
70-
return _inject_toolchain(config_settings, toolchain_file)
150+
if result.sync_data is not None:
151+
if isinstance(result.sync_data, CMakeSyncData):
152+
settings = _inject_cmake_toolchain(config_settings, result.sync_data.toolchain_file)
153+
elif isinstance(result.sync_data, MesonSyncData):
154+
settings = _inject_meson_files(config_settings, result.sync_data.native_file, result.sync_data.cross_file)
71155

156+
return result, settings
72157

73-
# PEP 517 Hooks - delegating to scikit-build-core after preparation
158+
159+
def _is_meson_build(result: BuildPreparationResult) -> bool:
160+
"""Determine if the build should use meson-python based on sync data.
161+
162+
Args:
163+
result: The build preparation result
164+
165+
Returns:
166+
True if meson-python should be used, False for scikit-build-core
167+
"""
168+
return isinstance(result.sync_data, MesonSyncData)
169+
170+
171+
# PEP 517 Hooks - dispatching to the appropriate backend after preparation
74172

75173

76174
def get_requires_for_build_wheel(
@@ -84,6 +182,10 @@ def get_requires_for_build_wheel(
84182
Returns:
85183
List of additional requirements
86184
"""
185+
if _is_meson_project():
186+
import mesonpy
187+
188+
return mesonpy.get_requires_for_build_wheel(config_settings)
87189
return skbuild.get_requires_for_build_wheel(config_settings)
88190

89191

@@ -98,6 +200,10 @@ def get_requires_for_build_sdist(
98200
Returns:
99201
List of additional requirements
100202
"""
203+
if _is_meson_project():
204+
import mesonpy
205+
206+
return mesonpy.get_requires_for_build_sdist(config_settings)
101207
return skbuild.get_requires_for_build_sdist(config_settings)
102208

103209

@@ -112,6 +218,10 @@ def get_requires_for_build_editable(
112218
Returns:
113219
List of additional requirements
114220
"""
221+
if _is_meson_project():
222+
import mesonpy
223+
224+
return mesonpy.get_requires_for_build_editable(config_settings)
115225
return skbuild.get_requires_for_build_editable(config_settings)
116226

117227

@@ -123,8 +233,8 @@ def build_wheel(
123233
"""Build a wheel from the source distribution.
124234
125235
This runs CPPython's provider workflow first to ensure C++ dependencies
126-
are installed and the toolchain file is generated, then delegates to
127-
scikit-build-core for the actual wheel build.
236+
are installed, then delegates to the appropriate build backend
237+
(scikit-build-core for CMake, meson-python for Meson).
128238
129239
Args:
130240
wheel_directory: Directory to place the built wheel
@@ -137,9 +247,14 @@ def build_wheel(
137247
logger.info('CPPython: Starting wheel build')
138248

139249
# Prepare CPPython and get updated settings
140-
settings = _prepare_and_get_settings(config_settings)
250+
result, settings = _prepare_and_get_result(config_settings)
251+
252+
# Delegate to the appropriate backend
253+
if _is_meson_build(result):
254+
import mesonpy
255+
256+
return mesonpy.build_wheel(wheel_directory, settings, metadata_directory)
141257

142-
# Delegate to scikit-build-core
143258
return skbuild.build_wheel(wheel_directory, settings, metadata_directory)
144259

145260

@@ -161,7 +276,11 @@ def build_sdist(
161276
"""
162277
logger.info('CPPython: Starting sdist build')
163278

164-
# Delegate directly to scikit-build-core (no preparation needed for sdist)
279+
# Delegate to the appropriate backend
280+
if _is_meson_project():
281+
import mesonpy
282+
283+
return mesonpy.build_sdist(sdist_directory, config_settings)
165284
return skbuild.build_sdist(sdist_directory, config_settings)
166285

167286

@@ -185,9 +304,14 @@ def build_editable(
185304
logger.info('CPPython: Starting editable build')
186305

187306
# Prepare CPPython and get updated settings
188-
settings = _prepare_and_get_settings(config_settings)
307+
result, settings = _prepare_and_get_result(config_settings)
308+
309+
# Delegate to the appropriate backend
310+
if _is_meson_build(result):
311+
import mesonpy
312+
313+
return mesonpy.build_editable(wheel_directory, settings, metadata_directory)
189314

190-
# Delegate to scikit-build-core
191315
return skbuild.build_editable(wheel_directory, settings, metadata_directory)
192316

193317

@@ -204,6 +328,10 @@ def prepare_metadata_for_build_wheel(
204328
Returns:
205329
The basename of the metadata directory
206330
"""
331+
if _is_meson_project():
332+
import mesonpy
333+
334+
return mesonpy.prepare_metadata_for_build_wheel(metadata_directory, config_settings)
207335
return skbuild.prepare_metadata_for_build_wheel(metadata_directory, config_settings)
208336

209337

@@ -220,4 +348,8 @@ def prepare_metadata_for_build_editable(
220348
Returns:
221349
The basename of the metadata directory
222350
"""
351+
if _is_meson_project():
352+
import mesonpy
353+
354+
return mesonpy.prepare_metadata_for_build_editable(metadata_directory, config_settings)
223355
return skbuild.prepare_metadata_for_build_editable(metadata_directory, config_settings)

0 commit comments

Comments
 (0)