Skip to content

Commit a714544

Browse files
committed
Presets Instead of Details
1 parent 2411ee7 commit a714544

5 files changed

Lines changed: 154 additions & 139 deletions

File tree

cppython/plugins/cmake/builder.py

Lines changed: 50 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
from cppython.plugins.cmake.schema import (
66
BuildPreset,
7-
CacheVariable,
87
CMakeData,
98
CMakePresets,
109
CMakeSyncData,
@@ -18,136 +17,70 @@ class Builder:
1817
def __init__(self) -> None:
1918
"""Initialize the builder"""
2019

21-
@staticmethod
22-
def generate_provider_preset(provider_data: CMakeSyncData) -> CMakePresets:
23-
"""Generates a provider preset from input sync data
24-
25-
Args:
26-
provider_data: The providers synchronization data
27-
"""
28-
# Base hidden preset with common configuration
29-
base_preset = ConfigurePreset(name=f'{provider_data.provider_name}-base', hidden=True)
30-
31-
# Handle both top_level_includes and toolchain options
32-
cache_variables: dict[str, str | bool | CacheVariable | None] = {}
33-
34-
if provider_data.top_level_includes:
35-
cache_variables['CMAKE_PROJECT_TOP_LEVEL_INCLUDES'] = str(provider_data.top_level_includes.as_posix())
36-
37-
if provider_data.toolchain:
38-
# Use the toolchainFile field for better integration
39-
base_preset.toolchainFile = provider_data.toolchain.as_posix()
40-
41-
if cache_variables:
42-
base_preset.cacheVariables = cache_variables
43-
44-
# Create specific configuration presets
45-
default_preset = ConfigurePreset(
46-
name=f'{provider_data.provider_name}-default', hidden=True, inherits=f'{provider_data.provider_name}-base'
47-
)
48-
49-
release_preset = ConfigurePreset(
50-
name=f'{provider_data.provider_name}-release',
51-
hidden=True,
52-
inherits=f'{provider_data.provider_name}-base',
53-
cacheVariables={'CMAKE_BUILD_TYPE': 'Release'},
54-
)
55-
56-
debug_preset = ConfigurePreset(
57-
name=f'{provider_data.provider_name}-debug',
58-
hidden=True,
59-
inherits=f'{provider_data.provider_name}-base',
60-
cacheVariables={'CMAKE_BUILD_TYPE': 'Debug'},
61-
)
62-
63-
return CMakePresets(configurePresets=[base_preset, default_preset, release_preset, debug_preset])
64-
65-
@staticmethod
66-
def write_provider_preset(provider_directory: Path, provider_data: CMakeSyncData) -> None:
67-
"""Writes a provider preset from input sync data
68-
69-
Args:
70-
provider_directory: The base directory to place the preset files
71-
provider_data: The providers synchronization data
72-
"""
73-
generated_preset = Builder.generate_provider_preset(provider_data)
74-
75-
provider_preset_file = provider_directory / f'{provider_data.provider_name}.json'
76-
77-
initial_preset = None
78-
79-
# If the file already exists, we need to compare it
80-
if provider_preset_file.exists():
81-
with open(provider_preset_file, encoding='utf-8') as file:
82-
initial_json = file.read()
83-
initial_preset = CMakePresets.model_validate_json(initial_json)
84-
85-
if generated_preset != initial_preset:
86-
serialized = generated_preset.model_dump_json(exclude_none=True, by_alias=False, indent=4)
87-
with open(provider_preset_file, 'w', encoding='utf8') as file:
88-
file.write(serialized)
89-
9020
@staticmethod
9121
def generate_cppython_preset(
92-
cppython_preset_directory: Path, provider_directory: Path, provider_data: CMakeSyncData
22+
cppython_preset_directory: Path, provider_preset_file: Path, provider_data: CMakeSyncData
9323
) -> CMakePresets:
9424
"""Generates the cppython preset which inherits from the provider presets
9525
9626
Args:
9727
cppython_preset_directory: The tool directory
98-
provider_directory: The base directory containing provider presets
28+
provider_preset_file: Path to the provider's preset file
9929
provider_data: The provider's synchronization data
10030
10131
Returns:
10232
A CMakePresets object
10333
"""
104-
# Configure presets similar to Conan structure
105-
default_configure = ConfigurePreset(
106-
name='default', inherits=f'{provider_data.provider_name}-default', hidden=False
107-
)
108-
109-
release_configure = ConfigurePreset(
110-
name='release', inherits=f'{provider_data.provider_name}-release', hidden=False
111-
)
112-
113-
debug_configure = ConfigurePreset(name='debug', inherits=f'{provider_data.provider_name}-debug', hidden=False)
114-
115-
# Build presets for multi-config and single-config generators
116-
multi_release_build = BuildPreset(
117-
name='multi-release',
118-
configurePreset='default',
119-
configuration='Release',
120-
inherits=f'{provider_data.provider_name}-release',
121-
)
122-
123-
multi_debug_build = BuildPreset(
124-
name='multi-debug',
125-
configurePreset='default',
126-
configuration='Debug',
127-
inherits=f'{provider_data.provider_name}-debug',
128-
)
129-
130-
release_build = BuildPreset(
131-
name='release',
132-
configurePreset='release',
133-
configuration='Release',
134-
inherits=f'{provider_data.provider_name}-release',
135-
)
136-
137-
debug_build = BuildPreset(
138-
name='debug',
139-
configurePreset='debug',
140-
configuration='Debug',
141-
inherits=f'{provider_data.provider_name}-debug',
142-
)
34+
configure_presets = []
35+
36+
preset_name = 'cppython-default'
37+
parent_preset_name = f'{provider_data.provider_name}-default'
38+
39+
# Create a default preset that inherits from provider's default preset
40+
default_configure = ConfigurePreset(name=preset_name, inherits=parent_preset_name, hidden=True)
41+
configure_presets.append(default_configure)
42+
43+
# Create presets for each configuration
44+
for config in provider_data.configurations:
45+
config_name = config.lower()
46+
preset_name = f'cppython-{config_name}'
47+
parent_preset_name = f'{provider_data.provider_name}-{config_name}'
48+
preset = ConfigurePreset(name=preset_name, inherits=parent_preset_name, hidden=True)
49+
configure_presets.append(preset)
50+
51+
build_presets = []
52+
53+
# Multi-config build presets using the default configure preset.
54+
# Important: Do not use a configure preset here, the user will do that in their own presets.
55+
for config in provider_data.configurations:
56+
config_name = config.lower()
57+
preset_name = f'cppython-multi-{config_name}'
58+
parent_preset_name = f'{provider_data.provider_name}-multi-{config_name}'
59+
multi_build_preset = BuildPreset(
60+
name=preset_name,
61+
configuration=config,
62+
inherits=parent_preset_name,
63+
)
64+
build_presets.append(multi_build_preset)
65+
66+
# Single-config build presets using the config-specific configure presets
67+
# Important: Do not use a configure preset here, the user will do that in their own presets.
68+
for config in provider_data.configurations:
69+
config_name = config.lower()
70+
parent_config_name = f'{provider_data.provider_name}-{config_name}'
71+
single_build_preset = BuildPreset(
72+
name=config_name,
73+
configuration=config,
74+
inherits=parent_config_name,
75+
)
76+
build_presets.append(single_build_preset)
14377

14478
generated_preset = CMakePresets(
145-
configurePresets=[default_configure, release_configure, debug_configure],
146-
buildPresets=[multi_release_build, multi_debug_build, release_build, debug_build],
79+
configurePresets=configure_presets,
80+
buildPresets=build_presets,
14781
)
14882

14983
# Get the relative path to the provider preset file
150-
provider_preset_file = provider_directory / f'{provider_data.provider_name}.json'
15184
relative_preset = provider_preset_file.relative_to(cppython_preset_directory, walk_up=True).as_posix()
15285

15386
# Set the data
@@ -156,20 +89,20 @@ def generate_cppython_preset(
15689

15790
@staticmethod
15891
def write_cppython_preset(
159-
cppython_preset_directory: Path, provider_directory: Path, provider_data: CMakeSyncData
92+
cppython_preset_directory: Path, provider_preset_file: Path, provider_data: CMakeSyncData
16093
) -> Path:
16194
"""Write the cppython presets which inherit from the provider presets
16295
16396
Args:
16497
cppython_preset_directory: The tool directory
165-
provider_directory: The base directory containing provider presets
98+
provider_preset_file: Path to the provider's preset file
16699
provider_data: The provider's synchronization data
167100
168101
Returns:
169102
A file path to the written data
170103
"""
171104
generated_preset = Builder.generate_cppython_preset(
172-
cppython_preset_directory, provider_directory, provider_data
105+
cppython_preset_directory, provider_preset_file, provider_data
173106
)
174107
cppython_preset_file = cppython_preset_directory / 'cppython.json'
175108

cppython/plugins/cmake/plugin.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,10 @@ def sync(self, sync_data: SyncData) -> None:
6363
match sync_data:
6464
case CMakeSyncData():
6565
self._cppython_preset_directory.mkdir(parents=True, exist_ok=True)
66-
self._provider_directory.mkdir(parents=True, exist_ok=True)
67-
68-
self.builder.write_provider_preset(self._provider_directory, sync_data)
6966

67+
# Provider now provides the preset file path, we don't generate it
7068
cppython_preset_file = self.builder.write_cppython_preset(
71-
self._cppython_preset_directory, self._provider_directory, sync_data
69+
self._cppython_preset_directory, sync_data.preset_file, sync_data
7270
)
7371

7472
self.builder.write_root_presets(

cppython/plugins/cmake/schema.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,17 @@ class CMakePresets(CPPythonModel, extra='allow'):
100100
class CMakeSyncData(SyncData):
101101
"""The CMake sync data"""
102102

103-
top_level_includes: FilePath | None = None
104-
toolchain: Path | None = None # We don't resolve the toolchain until after we set it
105-
106-
def model_post_init(self, __context) -> None:
107-
"""Validate that at least one of top_level_includes or toolchain is provided."""
108-
if not self.top_level_includes and not self.toolchain:
109-
raise ValueError("Either 'top_level_includes' or 'toolchain' must be provided")
103+
preset_file: Annotated[FilePath, Field(description='Path to the CMakePresets.json file generated by the provider')]
104+
configurations: Annotated[
105+
list[str],
106+
Field(
107+
description='List of configurations to generate presets for. Preset names are generated via '
108+
'the `provider_name` field with an appended suffix.'
109+
),
110+
] = [
111+
'Release',
112+
'Debug',
113+
]
110114

111115

112116
class CMakeData(CPPythonModel):

cppython/plugins/conan/plugin.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,16 +198,25 @@ def sync_data(self, consumer: SyncConsumer) -> SyncData:
198198
"""
199199
for sync_type in consumer.sync_types():
200200
if sync_type == CMakeSyncData:
201-
# Use the CMakeToolchain file directly as the toolchain
202-
toolchain_path = self.core_data.cppython_data.build_path / 'generators' / 'conan_toolchain.cmake'
203-
204-
return CMakeSyncData(
205-
provider_name=TypeName('conan'),
206-
toolchain=toolchain_path,
207-
)
201+
return self._create_cmake_sync_data()
208202

209203
raise NotSupportedError(f'Unsupported sync types: {consumer.sync_types()}')
210204

205+
def _create_cmake_sync_data(self) -> CMakeSyncData:
206+
"""Creates CMake synchronization data with Conan toolchain configuration.
207+
208+
Returns:
209+
CMakeSyncData configured for Conan integration
210+
"""
211+
# Conan's CMakeToolchain generator automatically creates preset files
212+
# The preset file will be created by Conan in the build directory
213+
conan_preset_path = self.core_data.cppython_data.build_path / 'CMakePresets.json'
214+
215+
return CMakeSyncData(
216+
provider_name=TypeName('conan'),
217+
preset_file=conan_preset_path,
218+
)
219+
211220
@classmethod
212221
async def download_tooling(cls, directory: Path) -> None:
213222
"""Download external tooling required by the Conan provider.

cppython/plugins/vcpkg/plugin.py

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""The vcpkg provider implementation"""
22

3+
import json
34
import subprocess
45
from logging import getLogger
56
from os import name as system_name
@@ -110,13 +111,83 @@ def sync_data(self, consumer: SyncConsumer) -> SyncData:
110111
"""
111112
for sync_type in consumer.sync_types():
112113
if sync_type == CMakeSyncData:
113-
return CMakeSyncData(
114-
provider_name=TypeName('vcpkg'),
115-
top_level_includes=self.core_data.cppython_data.install_path / 'scripts/buildsystems/vcpkg.cmake',
116-
)
114+
return self._create_cmake_sync_data()
117115

118116
raise NotSupportedError('OOF')
119117

118+
def _create_cmake_sync_data(self) -> CMakeSyncData:
119+
"""Creates CMake synchronization data with vcpkg configuration.
120+
121+
Returns:
122+
CMakeSyncData configured for vcpkg integration
123+
"""
124+
# Providers are now responsible for generating their own preset file
125+
# Create provider sync data with vcpkg configuration
126+
provider_preset_path = self.core_data.cppython_data.tool_path / 'providers' / 'vcpkg.json'
127+
provider_preset_path.parent.mkdir(parents=True, exist_ok=True)
128+
129+
# Create CMakeSyncData with vcpkg configuration
130+
vcpkg_cmake_path = self.core_data.cppython_data.install_path / 'scripts/buildsystems/vcpkg.cmake'
131+
132+
# Create a custom provider preset with vcpkg configuration
133+
provider_preset = {
134+
'version': 9,
135+
'configurePresets': [
136+
{
137+
'name': 'vcpkg-base',
138+
'hidden': True,
139+
'cacheVariables': {'CMAKE_PROJECT_TOP_LEVEL_INCLUDES': str(vcpkg_cmake_path.as_posix())},
140+
},
141+
{
142+
'name': 'vcpkg-release',
143+
'hidden': True,
144+
'inherits': 'vcpkg-base',
145+
'cacheVariables': {'CMAKE_BUILD_TYPE': 'Release'},
146+
},
147+
{
148+
'name': 'vcpkg-debug',
149+
'hidden': True,
150+
'inherits': 'vcpkg-base',
151+
'cacheVariables': {'CMAKE_BUILD_TYPE': 'Debug'},
152+
},
153+
],
154+
'buildPresets': [
155+
{
156+
'name': 'vcpkg-multi-release',
157+
'configurePreset': 'vcpkg-base',
158+
'configuration': 'Release',
159+
'hidden': True,
160+
},
161+
{
162+
'name': 'vcpkg-multi-debug',
163+
'configurePreset': 'vcpkg-base',
164+
'configuration': 'Debug',
165+
'hidden': True,
166+
},
167+
{
168+
'name': 'vcpkg-release',
169+
'configurePreset': 'vcpkg-release',
170+
'configuration': 'Release',
171+
'hidden': True,
172+
},
173+
{
174+
'name': 'vcpkg-debug',
175+
'configurePreset': 'vcpkg-debug',
176+
'configuration': 'Debug',
177+
'hidden': True,
178+
},
179+
],
180+
}
181+
182+
# Write the preset file
183+
with open(provider_preset_path, 'w', encoding='utf-8') as f:
184+
json.dump(provider_preset, f, indent=4)
185+
186+
return CMakeSyncData(
187+
provider_name=TypeName('vcpkg'),
188+
preset_file=provider_preset_path,
189+
)
190+
120191
@classmethod
121192
def tooling_downloaded(cls, path: Path) -> bool:
122193
"""Returns whether the provider tooling needs to be downloaded

0 commit comments

Comments
 (0)