Skip to content

Commit ece534c

Browse files
authored
3.14 Support (#121)
1 parent 865882e commit ece534c

File tree

27 files changed

+592
-486
lines changed

27 files changed

+592
-486
lines changed

cppython/builder.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -375,10 +375,10 @@ def solve(
375375
return combos[0]
376376

377377
@staticmethod
378-
def create_scm(
378+
def create_scm[T: SCM](
379379
core_data: CoreData,
380-
scm_type: type[SCM],
381-
) -> SCM:
380+
scm_type: type[T],
381+
) -> T:
382382
"""Creates a source control manager from input configuration
383383
384384
Args:
@@ -391,17 +391,17 @@ def create_scm(
391391
cppython_plugin_data = resolve_cppython_plugin(core_data.cppython_data, scm_type)
392392
scm_data = resolve_scm(core_data.project_data, cppython_plugin_data)
393393

394-
plugin = cast(SCM, scm_type(scm_data))
394+
plugin = scm_type(scm_data)
395395

396396
return plugin
397397

398-
def create_generator(
398+
def create_generator[T: Generator](
399399
self,
400400
core_data: CoreData,
401401
pep621_data: PEP621Data,
402402
generator_configuration: dict[str, Any],
403-
generator_type: type[Generator],
404-
) -> Generator:
403+
generator_type: type[T],
404+
) -> T:
405405
"""Creates a generator from input configuration
406406
407407
Args:
@@ -428,15 +428,15 @@ def create_generator(
428428
cppython_data=cppython_plugin_data,
429429
)
430430

431-
return cast(Generator, generator_type(generator_data, core_plugin_data, generator_configuration))
431+
return generator_type(generator_data, core_plugin_data, generator_configuration)
432432

433-
def create_provider(
433+
def create_provider[T: Provider](
434434
self,
435435
core_data: CoreData,
436436
pep621_data: PEP621Data,
437437
provider_configuration: dict[str, Any],
438-
provider_type: type[Provider],
439-
) -> Provider:
438+
provider_type: type[T],
439+
) -> T:
440440
"""Creates Providers from input data
441441
442442
Args:
@@ -463,7 +463,7 @@ def create_provider(
463463
cppython_data=cppython_plugin_data,
464464
)
465465

466-
return cast(Provider, provider_type(provider_data, core_plugin_data, provider_configuration))
466+
return provider_type(provider_data, core_plugin_data, provider_configuration)
467467

468468

469469
class Builder:

cppython/core/schema.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from abc import abstractmethod
44
from pathlib import Path
5-
from typing import Annotated, Any, NewType, Protocol, runtime_checkable
5+
from typing import Annotated, Any, NewType, Protocol, Self, runtime_checkable
66

77
from packaging.requirements import Requirement
88
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
@@ -80,7 +80,7 @@ class PEP621Configuration(CPPythonModel):
8080

8181
@model_validator(mode='after') # type: ignore
8282
@classmethod
83-
def dynamic_data(cls, model: 'PEP621Configuration') -> 'PEP621Configuration':
83+
def dynamic_data(cls, model: Self) -> Self:
8484
"""Validates that dynamic data is represented correctly
8585
8686
Args:

cppython/plugins/cmake/resolution.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Builder to help resolve cmake state"""
22

3+
import os
4+
from pathlib import Path
35
from typing import Any
46

57
from cppython.core.schema import CorePluginData
@@ -24,4 +26,13 @@ def resolve_cmake_data(data: dict[str, Any], core_data: CorePluginData) -> CMake
2426
if not modified_preset_file.is_absolute():
2527
modified_preset_file = root_directory / modified_preset_file
2628

27-
return CMakeData(preset_file=modified_preset_file, configuration_name=parsed_data.configuration_name)
29+
# Resolve cmake binary: environment variable takes precedence over configuration
30+
cmake_binary: Path | None = None
31+
if env_binary := os.environ.get('CMAKE_BINARY'):
32+
cmake_binary = Path(env_binary)
33+
elif parsed_data.cmake_binary:
34+
cmake_binary = parsed_data.cmake_binary
35+
36+
return CMakeData(
37+
preset_file=modified_preset_file, configuration_name=parsed_data.configuration_name, cmake_binary=cmake_binary
38+
)

cppython/plugins/cmake/schema.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class CMakeData(CPPythonModel):
111111

112112
preset_file: Path
113113
configuration_name: str
114+
cmake_binary: Path | None
114115

115116

116117
class CMakeConfiguration(CPPythonModel):
@@ -131,3 +132,10 @@ class CMakeConfiguration(CPPythonModel):
131132
'"default-release" will also be written'
132133
),
133134
] = 'default'
135+
cmake_binary: Annotated[
136+
Path | None,
137+
Field(
138+
description='Path to a specific CMake binary to use. If not specified, uses "cmake" from PATH. '
139+
'Can be overridden via CMAKE_BINARY environment variable.'
140+
),
141+
] = None

cppython/plugins/conan/builder.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,6 @@ def package_info(self):
158158
159159
def export_sources(self):
160160
copy(self, "CMakeLists.txt", src=self.recipe_folder, dst=self.export_sources_folder)
161-
copy(self, "include/*", src=self.recipe_folder, dst=self.export_sources_folder)
162161
copy(self, "src/*", src=self.recipe_folder, dst=self.export_sources_folder)
163162
copy(self, "cmake/*", src=self.recipe_folder, dst=self.export_sources_folder)
164163
"""
@@ -177,7 +176,11 @@ def export_sources(self):
177176
file.write(result)
178177

179178
def generate_conanfile(
180-
self, directory: DirectoryPath, dependencies: list[ConanDependency], name: str, version: str
179+
self,
180+
directory: DirectoryPath,
181+
dependencies: list[ConanDependency],
182+
name: str,
183+
version: str,
181184
) -> None:
182185
"""Generate a conanfile.py file for the project."""
183186
conan_file = directory / self._filename

cppython/plugins/conan/plugin.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ def __init__(
4545

4646
self._ensure_default_profiles()
4747

48+
# Initialize cmake_binary with system default. It may be overridden during sync.
49+
self._cmake_binary = 'cmake'
50+
4851
@staticmethod
4952
def features(directory: Path) -> SupportedFeatures:
5053
"""Queries conan support
@@ -149,8 +152,11 @@ def _run_conan_install(self, conanfile_path: Path, update: bool, build_type: str
149152
if build_type:
150153
command_args.extend(['-s', f'build_type={build_type}'])
151154

152-
# Log the command being executed
153-
logger.info('Executing conan command: conan %s', ' '.join(command_args))
155+
# Add cmake binary configuration if specified
156+
if self._cmake_binary and self._cmake_binary != 'cmake':
157+
# Quote the path if it contains spaces
158+
cmake_path = f'"{self._cmake_binary}"' if ' ' in self._cmake_binary else self._cmake_binary
159+
command_args.extend(['-c', f'tools.cmake:cmake_program={cmake_path}'])
154160

155161
try:
156162
# Use reusable Conan API instance instead of subprocess
@@ -200,10 +206,28 @@ def sync_data(self, consumer: SyncConsumer) -> SyncData:
200206
"""
201207
for sync_type in consumer.sync_types():
202208
if sync_type == CMakeSyncData:
203-
return self._create_cmake_sync_data()
209+
return self._sync_with_cmake(consumer)
204210

205211
raise NotSupportedError(f'Unsupported sync types: {consumer.sync_types()}')
206212

213+
def _sync_with_cmake(self, consumer: SyncConsumer) -> CMakeSyncData:
214+
"""Synchronize with CMake generator and create sync data.
215+
216+
Args:
217+
consumer: The CMake generator consumer
218+
219+
Returns:
220+
CMakeSyncData configured for Conan integration
221+
"""
222+
# Extract cmake_binary from CMakeGenerator if available
223+
if isinstance(consumer, CMakeGenerator) and not os.environ.get('CMAKE_BINARY'):
224+
# Only override if not already set from environment variable
225+
# Convert Path to string, or use 'cmake' if None
226+
cmake_path = consumer.data.cmake_binary
227+
self._cmake_binary = str(cmake_path) if cmake_path else 'cmake'
228+
229+
return self._create_cmake_sync_data()
230+
207231
def _create_cmake_sync_data(self) -> CMakeSyncData:
208232
"""Creates CMake synchronization data with Conan toolchain configuration.
209233
@@ -243,8 +267,11 @@ def publish(self) -> None:
243267
# Add build mode (build everything for publishing)
244268
command_args.extend(['--build', 'missing'])
245269

246-
# Log the command being executed
247-
logger.info('Executing conan create command: conan %s', ' '.join(command_args))
270+
# Add cmake binary configuration if specified
271+
if self._cmake_binary and self._cmake_binary != 'cmake':
272+
# Quote the path if it contains spaces
273+
cmake_path = f'"{self._cmake_binary}"' if ' ' in self._cmake_binary else self._cmake_binary
274+
command_args.extend(['-c', f'tools.cmake:cmake_program={cmake_path}'])
248275

249276
# Run conan create using reusable Conan API instance
250277
# Change to project directory since Conan API might not handle cwd like subprocess

cppython/plugins/conan/schema.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def __str__(self) -> str:
5555
return version
5656

5757
@classmethod
58-
def from_string(cls, version_str: str) -> 'ConanVersion':
58+
def from_string(cls, version_str: str) -> ConanVersion:
5959
"""Parse a version string into a ConanVersion."""
6060
if '-' in version_str:
6161
version_part, prerelease = version_str.split('-', 1)
@@ -219,7 +219,7 @@ def requires(self) -> str:
219219
return result
220220

221221
@classmethod
222-
def from_conan_reference(cls, reference: str) -> 'ConanDependency':
222+
def from_conan_reference(cls, reference: str) -> ConanDependency:
223223
"""Parse a Conan reference string into a ConanDependency.
224224
225225
Examples:

cppython/project.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,11 @@ def install(self) -> None:
6868
self.logger.info('Installing project')
6969
self.logger.info('Installing %s provider', self._data.plugins.provider.name())
7070

71+
# Sync before install to allow provider to access generator's resolved configuration
72+
self._data.sync()
73+
7174
# Let provider handle its own exceptions for better error context
7275
self._data.plugins.provider.install()
73-
self._data.sync()
7476

7577
def update(self) -> None:
7678
"""Updates project dependencies
@@ -88,9 +90,11 @@ def update(self) -> None:
8890
self.logger.info('Updating project')
8991
self.logger.info('Updating %s provider', self._data.plugins.provider.name())
9092

93+
# Sync before update to allow provider to access generator's resolved configuration
94+
self._data.sync()
95+
9196
# Let provider handle its own exceptions for better error context
9297
self._data.plugins.provider.update()
93-
self._data.sync()
9498

9599
def publish(self) -> None:
96100
"""Publishes the project

cppython/test/pytest/mixins.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ def fixture_provider_type(plugin_type: type[T]) -> type[T]:
193193

194194
@staticmethod
195195
@pytest.fixture(name='generator_type', scope='session')
196-
def fixture_generator_type(request: 'pytest.FixtureRequest') -> type[Generator]:
196+
def fixture_generator_type(request: pytest.FixtureRequest) -> type[Generator]:
197197
"""Provide generator variants for cross-plugin testing
198198
199199
Args:
@@ -207,7 +207,7 @@ def fixture_generator_type(request: 'pytest.FixtureRequest') -> type[Generator]:
207207

208208
@staticmethod
209209
@pytest.fixture(name='scm_type', scope='session')
210-
def fixture_scm_type(request: 'pytest.FixtureRequest') -> type[SCM]:
210+
def fixture_scm_type(request: pytest.FixtureRequest) -> type[SCM]:
211211
"""Provide SCM variants for cross-plugin testing
212212
213213
Args:
@@ -252,7 +252,7 @@ def fixture_plugin_group_data(
252252
# Cross-plugin testing fixtures for ensuring compatibility
253253
@staticmethod
254254
@pytest.fixture(name='provider_type', scope='session')
255-
def fixture_provider_type(request: 'pytest.FixtureRequest') -> type[Provider]:
255+
def fixture_provider_type(request: pytest.FixtureRequest) -> type[Provider]:
256256
"""Provide provider variants for cross-plugin testing
257257
258258
Args:
@@ -279,7 +279,7 @@ def fixture_generator_type(plugin_type: type[T]) -> type[T]:
279279

280280
@staticmethod
281281
@pytest.fixture(name='scm_type', scope='session')
282-
def fixture_scm_type(request: 'pytest.FixtureRequest') -> type[SCM]:
282+
def fixture_scm_type(request: pytest.FixtureRequest) -> type[SCM]:
283283
"""Provide SCM variants for cross-plugin testing
284284
285285
Args:
@@ -324,7 +324,7 @@ def fixture_plugin_group_data(
324324
# Cross-plugin testing fixtures for ensuring compatibility
325325
@staticmethod
326326
@pytest.fixture(name='provider_type', scope='session')
327-
def fixture_provider_type(request: 'pytest.FixtureRequest') -> type[Provider]:
327+
def fixture_provider_type(request: pytest.FixtureRequest) -> type[Provider]:
328328
"""Provide provider variants for cross-plugin testing
329329
330330
Args:
@@ -338,7 +338,7 @@ def fixture_provider_type(request: 'pytest.FixtureRequest') -> type[Provider]:
338338

339339
@staticmethod
340340
@pytest.fixture(name='generator_type', scope='session')
341-
def fixture_generator_type(request: 'pytest.FixtureRequest') -> type[Generator]:
341+
def fixture_generator_type(request: pytest.FixtureRequest) -> type[Generator]:
342342
"""Provide generator variants for cross-plugin testing
343343
344344
Args:

examples/conan_cmake/library/CMakeLists.txt

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
cmake_minimum_required(VERSION 3.30)
1+
cmake_minimum_required(VERSION 4.0)
22

33
project(mathutils
44
VERSION 1.0.0
@@ -12,37 +12,22 @@ include(CMakePackageConfigHelpers)
1212
# Dependencies
1313
find_package(fmt REQUIRED)
1414

15-
# Library target
16-
add_library(mathutils src/mathutils.cpp)
15+
add_library(mathutils)
1716
add_library(mathutils::mathutils ALIAS mathutils)
1817

19-
target_compile_features(mathutils PUBLIC cxx_std_17)
20-
target_include_directories(mathutils PUBLIC
21-
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
22-
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
23-
)
24-
target_link_libraries(mathutils PUBLIC fmt::fmt)
25-
26-
# Installation
27-
install(TARGETS mathutils
28-
EXPORT mathutilsTargets
29-
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
30-
)
31-
32-
install(DIRECTORY include/
33-
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
34-
FILES_MATCHING PATTERN "*.h*"
18+
target_sources(mathutils
19+
PUBLIC
20+
FILE_SET CXX_MODULES FILES
21+
src/mathutils.ixx
3522
)
3623

37-
install(EXPORT mathutilsTargets
38-
FILE mathutilsTargets.cmake
39-
NAMESPACE mathutils::
40-
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mathutils
41-
)
24+
target_compile_features(mathutils PUBLIC cxx_std_23)
25+
target_link_libraries(mathutils PUBLIC fmt::fmt)
4226

43-
# Package configuration
27+
# Generate package config files
4428
write_basic_package_version_file(
4529
mathutilsConfigVersion.cmake
30+
VERSION ${PROJECT_VERSION}
4631
COMPATIBILITY SameMajorVersion
4732
)
4833

@@ -52,7 +37,29 @@ configure_package_config_file(
5237
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mathutils
5338
)
5439

55-
install(FILES
40+
install(
41+
TARGETS mathutils
42+
EXPORT mathutilsTargets
43+
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
44+
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
45+
FILE_SET CXX_MODULES
46+
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mathutils
47+
)
48+
49+
# Create empty include directory to satisfy CMake's exported target requirements
50+
# (module-only library with no headers, but CMake expects the directory to exist)
51+
install(DIRECTORY DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
52+
53+
# Export and package configuration
54+
install(
55+
EXPORT mathutilsTargets
56+
FILE mathutilsTargets.cmake
57+
NAMESPACE mathutils::
58+
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mathutils
59+
)
60+
61+
install(
62+
FILES
5663
${CMAKE_CURRENT_BINARY_DIR}/mathutilsConfig.cmake
5764
${CMAKE_CURRENT_BINARY_DIR}/mathutilsConfigVersion.cmake
5865
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mathutils

0 commit comments

Comments
 (0)