Skip to content

CMakePresets Initial Implementation #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 8, 2022
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
144 changes: 92 additions & 52 deletions cppython/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
"""

import logging
from dataclasses import dataclass
from importlib import metadata
from pathlib import Path
from typing import Any, Type, TypeVar

from cppython_core.core import cppython_logger
from cppython_core.schema import (
API,
CPPythonData,
Generator,
GeneratorConfiguration,
Expand All @@ -20,28 +19,8 @@
)
from pydantic import create_model


@dataclass
class ProjectConfiguration:
"""
TODO
"""

_verbosity: int = 0

@property
def verbosity(self) -> int:
"""
TODO
"""
return self._verbosity

@verbosity.setter
def verbosity(self, value: int) -> None:
"""
TODO
"""
self._verbosity = min(max(value, 0), 2)
from cppython.schema import API, CMakePresets, ConfigurePreset, ProjectConfiguration
from cppython.utility import write_preset


class ProjectBuilder:
Expand Down Expand Up @@ -188,45 +167,106 @@ def download(self):
"""
Download the generator tooling if required
"""
if self._enabled:
base_path = self.pyproject.tool.cppython.install_path
if not self._enabled:
cppython_logger.info("Skipping download because the project is not enabled")
return

for generator in self._generators:
base_path = self.pyproject.tool.cppython.install_path

path = base_path / generator.name()
for generator in self._generators:

path.mkdir(parents=True, exist_ok=True)
path = base_path / generator.name()

if not generator.generator_downloaded(path):
cppython_logger.warning(f"Downloading the {generator.name()} requirements to {path}")
path.mkdir(parents=True, exist_ok=True)

# TODO: Make async with progress bar
generator.download_generator(path)
cppython_logger.warning("Download complete")
if not generator.generator_downloaded(path):
cppython_logger.warning(f"Downloading the {generator.name()} requirements to {path}")

# API Contract
# TODO: Make async with progress bar
generator.download_generator(path)
cppython_logger.warning("Download complete")
else:
cppython_logger.info(f"The {generator.name()} generator is already downloaded")

def _write_generator_presets(self, tool_path: Path, generator: Generator, toolchain_path: Path) -> Path:
"""
Write a generator preset.
@returns - The written directory
"""
generator_tool_path = tool_path / generator.name()
generator_tool_path.mkdir(parents=True, exist_ok=True)

configure_preset = ConfigurePreset(name=generator.name(), hidden=True, toolchainFile=toolchain_path)
presets = CMakePresets(configurePresets=[configure_preset])

write_preset(generator_tool_path, presets)

return generator_tool_path

def _write_presets(self, tool_path: Path, names: list[str], includes: list[Path]) -> None:
"""
Write the cppython main preset
"""

configure_preset = ConfigurePreset(name="cppython", hidden=True, inherits=names)
presets = CMakePresets(configurePresets=[configure_preset], include=includes)

write_preset(tool_path, presets)

# API Contract
def install(self) -> None:
if self._enabled:
cppython_logger.info("Installing project")
self.download()
"""
TODO
"""
if not self._enabled:
cppython_logger.info("Skipping install because the project is not enabled")
return

cppython_logger.info("Installing project")
self.download()

tool_path = self.pyproject.tool.cppython.tool_path
tool_path.mkdir(parents=True, exist_ok=True)

names = []
includes = []

for generator in self._generators:
cppython_logger.info(f"Installing {generator.name()} generator")
generator.install()
# TODO: Async
for generator in self._generators:
cppython_logger.info(f"Installing {generator.name()} generator")

toolchain_path = generator.install()

directory = self._write_generator_presets(tool_path, generator, toolchain_path)
includes.append(directory)
names.append(generator.name())

self._write_presets(tool_path, names, includes)

def update(self) -> None:
if self._enabled:
cppython_logger.info("Updating project")
"""
TODO
"""
if not self._enabled:
cppython_logger.info("Skipping update because the project is not enabled")
return

cppython_logger.info("Updating project")

tool_path = self.pyproject.tool.cppython.tool_path
tool_path.mkdir(parents=True, exist_ok=True)

names = []
includes = []

# TODO: Async
for generator in self._generators:
cppython_logger.info(f"Updating {generator.name()} generator")

for generator in self._generators:
cppython_logger.info(f"Updating {generator.name()} generator")
generator.update()
toolchain_path = generator.update()

def build(self) -> None:
if self._enabled:
cppython_logger.info("Building project")
directory = self._write_generator_presets(tool_path, generator, toolchain_path)
includes.append(directory)
names.append(generator.name())

for generator in self._generators:
cppython_logger.info(f"Building {generator.name()} generator")
generator.build()
self._write_presets(tool_path, names, includes)
118 changes: 118 additions & 0 deletions cppython/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"""
TODO
"""

from __future__ import annotations # Required for self-referenced pydantic types

from abc import abstractmethod
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Optional

from pydantic import BaseModel, Extra, Field


class Preset(BaseModel):
"""
Partial Preset specification
"""

name: str
hidden: Optional[bool] = None
inherits: list[str] = []
displayName: Optional[str] = None
description: Optional[str] = None


class ConfigurePreset(Preset):
"""
Partial Configure Preset specification
"""

toolchainFile: Optional[Path] = None


class BuildPreset(Preset):
"""
Partial Build Preset specification
"""

configurePreset: Optional[str] = None
inheritConfigureEnvironment: Optional[bool] = None


class TestPreset(Preset):
"""
Partial Test Preset specification
"""

configurePreset: Optional[str] = None
inheritConfigureEnvironment: Optional[bool] = None


class CMakeVersion(BaseModel, extra=Extra.forbid):
"""
The version specification for CMake
"""

major: int = 3
minor: int = 23
patch: int = 1


class CMakePresets(BaseModel, extra=Extra.forbid):
"""
The schema for the CMakePresets and CMakeUserPresets files
"""

version: int = Field(default=4, const=True)
cmakeMinimumRequired: CMakeVersion = CMakeVersion() # TODO: 'version' compatability validation
include: list[Path] = []
vendor: Optional[Any] = None
configurePresets: list[ConfigurePreset] = []
buildPresets: list[BuildPreset] = []
testPresets: list[TestPreset] = []


@dataclass
class ProjectConfiguration:
"""
TODO
"""

_verbosity: int = 0

@property
def verbosity(self) -> int:
"""
TODO
"""
return self._verbosity

@verbosity.setter
def verbosity(self, value: int) -> None:
"""
TODO
"""
self._verbosity = min(max(value, 0), 2)


class API:
"""
Project API
"""

@abstractmethod
def install(self) -> None:
"""
TODO
"""
raise NotImplementedError()

@abstractmethod
def update(self) -> None:
"""
TODO
"""

raise NotImplementedError()
25 changes: 25 additions & 0 deletions cppython/utility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""
TODO
"""

import json
from pathlib import Path

from cppython.schema import CMakePresets


def read_preset(path: Path) -> CMakePresets:
"""
Reading routing
"""

preset_path = path / "CMakePresets.json"
return CMakePresets.parse_file(path=preset_path)


def write_preset(path: Path, presets: CMakePresets) -> None:
"""
Writing routine
"""
with open(path / "CMakePresets.json", "w", encoding="utf8") as json_file:
json.dump(presets.dict(), json_file, ensure_ascii=False, indent=2)
16 changes: 8 additions & 8 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions tests/unit/test_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import pytest
from click.testing import CliRunner
from cppython_core.schema import (
API,
PEP621,
CPPythonData,
InterfaceConfiguration,
Expand All @@ -17,11 +16,12 @@
from pytest_mock.plugin import MockerFixture

from cppython.console import Config, ConsoleInterface, cli
from cppython.schema import API

default_pep621 = PEP621(name="test_name", version="1.0")
default_cppython_data = CPPythonData(**{"target": TargetEnum.EXE})
default_tool_data = ToolData(**{"cppython": default_cppython_data})
default_pyproject = PyProject(**{"project": default_pep621, "tool": default_tool_data})
default_cppython_data = CPPythonData(target=TargetEnum.EXE)
default_tool_data = ToolData(cppython=default_cppython_data)
default_pyproject = PyProject(project=default_pep621, tool=default_tool_data)


class TestCLIInterface(InterfaceUnitTests):
Expand Down
Loading