Skip to content

Commit 08d5b33

Browse files
committed
CMake Toolchain Sync
1 parent e9b4de0 commit 08d5b33

File tree

3 files changed

+68
-21
lines changed

3 files changed

+68
-21
lines changed

cppython/plugins/cmake/builder.py

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

33
from pathlib import Path
44

5-
from cppython.plugins.cmake.schema import CMakeData, CMakePresets, CMakeSyncData, ConfigurePreset
5+
from cppython.plugins.cmake.schema import CacheVariable, CMakeData, CMakePresets, CMakeSyncData, ConfigurePreset
66

77

88
class Builder:
@@ -21,10 +21,18 @@ def generate_provider_preset(provider_data: CMakeSyncData) -> CMakePresets:
2121
"""
2222
generated_configure_preset = ConfigurePreset(name=provider_data.provider_name, hidden=True)
2323

24-
# Toss in that sync data from the provider
25-
generated_configure_preset.cacheVariables = {
26-
'CMAKE_PROJECT_TOP_LEVEL_INCLUDES': str(provider_data.top_level_includes.as_posix()),
27-
}
24+
# Handle both top_level_includes and toolchain options
25+
cache_variables: dict[str, str | bool | CacheVariable | None] = {}
26+
27+
if provider_data.top_level_includes:
28+
cache_variables['CMAKE_PROJECT_TOP_LEVEL_INCLUDES'] = str(provider_data.top_level_includes.as_posix())
29+
30+
if provider_data.toolchain:
31+
# Use the toolchainFile field for better integration
32+
generated_configure_preset.toolchainFile = str(provider_data.toolchain.as_posix())
33+
34+
if cache_variables:
35+
generated_configure_preset.cacheVariables = cache_variables
2836

2937
return CMakePresets(configurePresets=[generated_configure_preset])
3038

cppython/plugins/cmake/schema.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ class ConfigurePreset(CPPythonModel, extra='allow'):
5858
str | None,
5959
Field(description='The path to the output binary directory.'),
6060
] = None
61+
toolchainFile: Annotated[
62+
FilePath | None,
63+
Field(description='Path to the toolchain file.'),
64+
] = None
6165
cacheVariables: dict[str, None | bool | str | CacheVariable] | None = None
6266

6367

@@ -77,7 +81,13 @@ class CMakePresets(CPPythonModel, extra='allow'):
7781
class CMakeSyncData(SyncData):
7882
"""The CMake sync data"""
7983

80-
top_level_includes: FilePath
84+
top_level_includes: FilePath | None = None
85+
toolchain: FilePath | None = None
86+
87+
def model_post_init(self, __context) -> None:
88+
"""Validate that at least one of top_level_includes or toolchain is provided."""
89+
if not self.top_level_includes and not self.toolchain:
90+
raise ValueError("Either 'top_level_includes' or 'toolchain' must be provided")
8191

8292

8393
class CMakeData(CPPythonModel):

cppython/plugins/conan/plugin.py

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from pathlib import Path
99
from typing import Any
1010

11-
import requests
1211
from conan.api.conan_api import ConanAPI
1312
from conan.api.model import ListPattern
1413

@@ -27,8 +26,6 @@
2726
class ConanProvider(Provider):
2827
"""Conan Provider"""
2928

30-
_provider_url = 'https://raw.githubusercontent.com/conan-io/cmake-conan/refs/heads/develop2/conan_provider.cmake'
31-
3229
def __init__(
3330
self, group_data: ProviderPluginGroupData, core_data: CorePluginData, configuration_data: dict[str, Any]
3431
) -> None:
@@ -39,15 +36,6 @@ def __init__(
3936

4037
self.builder = Builder()
4138

42-
@staticmethod
43-
def _download_file(url: str, file: Path) -> None:
44-
"""Replaces the given file with the contents of the url"""
45-
file.parent.mkdir(parents=True, exist_ok=True)
46-
47-
with open(file, 'wb') as out_file:
48-
content = requests.get(url, stream=True).content
49-
out_file.write(content)
50-
5139
@staticmethod
5240
def features(directory: Path) -> SupportedFeatures:
5341
"""Queries conan support
@@ -197,6 +185,13 @@ def _generate_consumer_files(self, conan_api: ConanAPI, deps_graph) -> None:
197185
output_folder=str(self.core_data.cppython_data.build_path),
198186
)
199187

188+
# Rename the generated toolchain file so our wrapper can include it
189+
original_toolchain = self.core_data.cppython_data.build_path / 'conan_toolchain.cmake'
190+
renamed_toolchain = self.core_data.cppython_data.build_path / 'conan_toolchain.cmake.real'
191+
192+
if original_toolchain.exists() and not renamed_toolchain.exists():
193+
original_toolchain.rename(renamed_toolchain)
194+
200195
def install(self) -> None:
201196
"""Installs the provider"""
202197
self._install_dependencies(update=False)
@@ -231,17 +226,51 @@ def sync_data(self, consumer: SyncConsumer) -> SyncData:
231226
"""
232227
for sync_type in consumer.sync_types():
233228
if sync_type == CMakeSyncData:
229+
# Use the CMakeToolchain file directly as the toolchain
230+
toolchain_path = self.core_data.cppython_data.build_path / 'conan_toolchain.cmake'
231+
232+
# Create the directory structure if it doesn't exist
233+
toolchain_path.parent.mkdir(parents=True, exist_ok=True)
234+
235+
# Always create a minimal toolchain file that includes dependencies when they exist
236+
toolchain_content = f'''# Conan CMake integration file
237+
# This file is managed by CPPython and integrates Conan dependencies with CMake
238+
239+
# Set the build directory for reference
240+
set(CONAN_BUILD_DIR "{self.core_data.cppython_data.build_path.as_posix()}")
241+
242+
# Include CMakeDeps generated dependency files if they exist
243+
file(GLOB CONAN_DEPS_FILES "${{CONAN_BUILD_DIR}}/*-config.cmake")
244+
foreach(DEPS_FILE ${{CONAN_DEPS_FILES}})
245+
include("${{DEPS_FILE}}")
246+
endforeach()
247+
248+
# Include any conan-generated toolchain files
249+
if(EXISTS "${{CONAN_BUILD_DIR}}/conan_toolchain.cmake.real")
250+
include("${{CONAN_BUILD_DIR}}/conan_toolchain.cmake.real")
251+
else()
252+
message(STATUS "Conan dependencies not installed yet - run conan install to install dependencies")
253+
endif()
254+
'''
255+
256+
toolchain_path.write_text(toolchain_content)
257+
234258
return CMakeSyncData(
235259
provider_name=TypeName('conan'),
236-
top_level_includes=self.core_data.cppython_data.install_path / 'conan_provider.cmake',
260+
toolchain=toolchain_path,
237261
)
238262

239263
raise NotSupportedError(f'Unsupported sync types: {consumer.sync_types()}')
240264

241265
@classmethod
242266
async def download_tooling(cls, directory: Path) -> None:
243-
"""Downloads the conan provider file"""
244-
cls._download_file(cls._provider_url, directory / 'conan_provider.cmake')
267+
"""Download external tooling required by the Conan provider.
268+
269+
Since we're using CMakeToolchain generator instead of cmake-conan provider,
270+
no external tooling needs to be downloaded.
271+
"""
272+
# No external tooling required when using CMakeToolchain
273+
pass
245274

246275
def publish(self) -> None:
247276
"""Publishes the package using conan create workflow."""

0 commit comments

Comments
 (0)