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)
45after running CPPython's preparation workflow.
56"""
67
78import logging
9+ import tomllib
810from pathlib import Path
911from typing import Any
1012
1113from 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
1519logger = 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
76174def 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