Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
root = true

[*]
indent_style = space
indent_size = 4
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"recommendations": [
"ms-python.mypy-type-checker",
"asciidoctor.asciidoctor-vscode",
"charliermarsh.ruff"
"charliermarsh.ruff",
"tamasfe.even-better-toml"
]
}
110 changes: 86 additions & 24 deletions cppython/plugins/cmake/builder.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
"""Plugin builder"""

import json
from copy import deepcopy
from pathlib import Path

from cppython.plugins.cmake.schema import CMakePresets, CMakeSyncData, ConfigurePreset
Expand All @@ -10,48 +8,85 @@
class Builder:
"""Aids in building the information needed for the CMake plugin"""

def __init__(self) -> None:
"""Initialize the builder"""

@staticmethod
def write_provider_preset(provider_directory: Path, data: CMakeSyncData) -> None:
def write_provider_preset(provider_directory: Path, provider_data: CMakeSyncData) -> None:
"""Writes a provider preset from input sync data

Args:
provider_directory: The base directory to place the preset files
data: The providers synchronization data
provider_data: The providers synchronization data
"""
configure_preset = ConfigurePreset(name=data.provider_name, cacheVariables=None)
presets = CMakePresets(configurePresets=[configure_preset])
generated_configure_preset = ConfigurePreset(name=provider_data.provider_name)

# Toss in that sync data from the provider
generated_configure_preset.cacheVariables = {
'CMAKE_PROJECT_TOP_LEVEL_INCLUDES': str(provider_data.top_level_includes.as_posix()),
}

generated_preset = CMakePresets(configurePresets=[generated_configure_preset])

provider_preset_file = provider_directory / f'{provider_data.provider_name}.json'

initial_preset = None

json_path = provider_directory / f'{data.provider_name}.json'
# If the file already exists, we need to compare it
if provider_preset_file.exists():
with open(provider_preset_file, encoding='utf-8') as file:
initial_json = file.read()
initial_preset = CMakePresets.model_validate_json(initial_json)

serialized = json.loads(presets.model_dump_json(exclude_none=True, by_alias=False))
with open(json_path, 'w', encoding='utf8') as file:
json.dump(serialized, file, ensure_ascii=False, indent=4)
if generated_preset != initial_preset:
serialized = generated_preset.model_dump_json(exclude_none=True, by_alias=False, indent=4)
with open(provider_preset_file, 'w', encoding='utf8') as file:
file.write(serialized)

@staticmethod
def write_cppython_preset(
cppython_preset_directory: Path, _provider_directory: Path, _provider_data: CMakeSyncData
cppython_preset_directory: Path, provider_directory: Path, provider_data: CMakeSyncData
) -> Path:
"""Write the cppython presets which inherit from the provider presets

Args:
cppython_preset_directory: The tool directory
provider_directory: The base directory containing provider presets
provider_data: The provider's synchronization data

Returns:
A file path to the written data
"""
configure_preset = ConfigurePreset(name='cppython', cacheVariables=None)
presets = CMakePresets(configurePresets=[configure_preset])
generated_configure_preset = ConfigurePreset(name='cppython', inherits=provider_data.provider_name)
generated_preset = CMakePresets(configurePresets=[generated_configure_preset])

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

serialized = json.loads(presets.model_dump_json(exclude_none=True, by_alias=False))
with open(cppython_json_path, 'w', encoding='utf8') as file:
json.dump(serialized, file, ensure_ascii=False, indent=4)
# Set the data
generated_preset.include = [relative_preset]

return cppython_json_path
cppython_preset_file = cppython_preset_directory / 'cppython.json'

initial_preset = None

# If the file already exists, we need to compare it
if cppython_preset_file.exists():
with open(cppython_preset_file, encoding='utf-8') as file:
initial_json = file.read()
initial_preset = CMakePresets.model_validate_json(initial_json)

# Only write the file if the data has changed
if generated_preset != initial_preset:
serialized = generated_preset.model_dump_json(exclude_none=True, by_alias=False, indent=4)
with open(cppython_preset_file, 'w', encoding='utf8') as file:
file.write(serialized)

return cppython_preset_file

@staticmethod
def write_root_presets(preset_file: Path, _: Path) -> None:
def write_root_presets(preset_file: Path, cppython_preset_file: Path) -> None:
"""Read the top level json file and insert the include reference.

Receives a relative path to the tool cmake json file
Expand All @@ -61,10 +96,37 @@ def write_root_presets(preset_file: Path, _: Path) -> None:

Args:
preset_file: Preset file to modify
cppython_preset_file: Path to the cppython preset file to include
"""
with open(preset_file, encoding='utf-8') as file:
initial_root_preset = json.load(file)

if (root_preset := deepcopy(initial_root_preset)) != initial_root_preset:
initial_root_preset = None

# If the file already exists, we need to compare it
if preset_file.exists():
with open(preset_file, encoding='utf-8') as file:
initial_json = file.read()
initial_root_preset = CMakePresets.model_validate_json(initial_json)
root_preset = initial_root_preset.model_copy(deep=True)
else:
# If the file doesn't exist, we need to default it for the user

# Forward the tool's build directory
default_configure_preset = ConfigurePreset(name='default', inherits='cppython', binaryDir='build')
root_preset = CMakePresets(configurePresets=[default_configure_preset])

# Get the relative path to the cppython preset file
preset_directory = preset_file.parent.absolute()
relative_preset = cppython_preset_file.relative_to(preset_directory, walk_up=True).as_posix()

# If the include key doesn't exist, we know we will write to disk afterwards
if not root_preset.include:
root_preset.include = []

# Only the included preset file if it doesn't exist. Implied by the above check
if str(relative_preset) not in root_preset.include:
root_preset.include.append(str(relative_preset))

# Only write the file if the data has changed
if root_preset != initial_root_preset:
with open(preset_file, 'w', encoding='utf-8') as file:
json.dump(root_preset, file, ensure_ascii=False, indent=4)
preset = root_preset.model_dump_json(exclude_none=True, indent=4)
file.write(preset)
10 changes: 1 addition & 9 deletions cppython/plugins/cmake/resolution.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
"""Builder to help resolve cmake state"""

import json
from typing import Any

from cppython.core.schema import CorePluginData
from cppython.plugins.cmake.schema import CMakeConfiguration, CMakeData, CMakePresets
from cppython.plugins.cmake.schema import CMakeConfiguration, CMakeData


def resolve_cmake_data(data: dict[str, Any], core_data: CorePluginData) -> CMakeData:
Expand All @@ -25,11 +24,4 @@ def resolve_cmake_data(data: dict[str, Any], core_data: CorePluginData) -> CMake
if not modified_preset_dir.is_absolute():
modified_preset_dir = root_directory / modified_preset_dir

# If the user hasn't specified a preset file, we need to create one
if not modified_preset_dir.exists():
modified_preset_dir.parent.mkdir(parents=True, exist_ok=True)
with modified_preset_dir.open('w', encoding='utf-8') as file:
presets_dict = CMakePresets().model_dump_json(exclude_none=True)
json.dump(presets_dict, file, ensure_ascii=False, indent=4)

return CMakeData(preset_file=modified_preset_dir, configuration_name=parsed_data.configuration_name)
32 changes: 25 additions & 7 deletions cppython/plugins/cmake/schema.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
"""CMake data definitions"""
"""CMake plugin schema

This module defines the schema and data models for integrating the CMake
generator with CPPython. It includes definitions for cache variables,
configuration presets, and synchronization data.
"""

from enum import Enum, auto
from pathlib import Path
Expand All @@ -11,10 +16,10 @@


class VariableType(Enum):
"""_summary_
"""Defines the types of variables that can be used in CMake cache.

Args:
Enum: _description_
Enum: Base class for creating enumerations.
"""

BOOL = (auto(),) # Boolean ON/OFF value.
Expand All @@ -27,7 +32,12 @@ class VariableType(Enum):


class CacheVariable(CPPythonModel, extra='forbid'):
"""_summary_"""
"""Represents a variable in the CMake cache.

Attributes:
type: The type of the variable (e.g., BOOL, PATH).
value: The value of the variable, which can be a boolean or string.
"""

type: None | VariableType
value: bool | str
Expand All @@ -37,7 +47,11 @@ class ConfigurePreset(CPPythonModel, extra='allow'):
"""Partial Configure Preset specification to allow cache variable injection"""

name: str
cacheVariables: dict[str, None | bool | str | CacheVariable] | None
inherits: Annotated[
str | list[str] | None, Field(description='The inherits field allows inheriting from other presets.')
] = None
binaryDir: Annotated[str | None, Field(description='The binary directory for the build output.')] = None
cacheVariables: dict[str, None | bool | str | CacheVariable] | None = None


class CMakePresets(CPPythonModel, extra='allow'):
Expand All @@ -46,7 +60,11 @@ class CMakePresets(CPPythonModel, extra='allow'):
The only information needed is the configure preset list for cache variable injection
"""

configurePresets: Annotated[list[ConfigurePreset], Field(description='The list of configure presets')] = []
version: Annotated[int, Field(description='The version of the JSON schema.')] = 9
include: Annotated[
list[str] | None, Field(description='The include field allows inheriting from another preset.')
] = None
configurePresets: Annotated[list[ConfigurePreset] | None, Field(description='The list of configure presets')] = None


class CMakeSyncData(SyncData):
Expand All @@ -58,7 +76,7 @@ class CMakeSyncData(SyncData):
class CMakeData(CPPythonModel):
"""Resolved CMake data"""

preset_file: FilePath
preset_file: Path
configuration_name: str


Expand Down
Loading
Loading