From 2fd96ac1f9a0362329dd8b6fbedc9049be53a8e7 Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Sun, 8 May 2022 13:12:44 -0400 Subject: [PATCH 1/5] Store CMake Paths as FilePaths --- cppython/schema.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cppython/schema.py b/cppython/schema.py index 56b5989..b61054e 100644 --- a/cppython/schema.py +++ b/cppython/schema.py @@ -6,10 +6,11 @@ from abc import abstractmethod from dataclasses import dataclass -from pathlib import Path +from pathlib import PosixPath from typing import Any, Optional from pydantic import BaseModel, Extra, Field +from pydantic.types import FilePath class Preset(BaseModel): @@ -29,7 +30,7 @@ class ConfigurePreset(Preset): Partial Configure Preset specification """ - toolchainFile: Optional[Path] = None + toolchainFile: Optional[FilePath] = None class BuildPreset(Preset): @@ -67,7 +68,7 @@ class CMakePresets(BaseModel, extra=Extra.forbid): version: int = Field(default=4, const=True) cmakeMinimumRequired: CMakeVersion = CMakeVersion() # TODO: 'version' compatability validation - include: list[Path] = [] + include: list[FilePath] = [] vendor: Optional[Any] = None configurePresets: list[ConfigurePreset] = [] buildPresets: list[BuildPreset] = [] From 51a59650a5fefca458706d2b149eb259c91b5353 Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Sun, 8 May 2022 13:18:37 -0400 Subject: [PATCH 2/5] JSON: Return Filename, Not Directory --- cppython/project.py | 16 +++++++--------- cppython/utility.py | 12 ++++++++---- tests/unit/test_utility.py | 4 ++-- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/cppython/project.py b/cppython/project.py index bd36f21..ea6dc20 100644 --- a/cppython/project.py +++ b/cppython/project.py @@ -191,7 +191,7 @@ def download(self): def _write_generator_presets(self, tool_path: Path, generator: Generator, toolchain_path: Path) -> Path: """ Write a generator preset. - @returns - The written directory + @returns - The written json file """ generator_tool_path = tool_path / generator.name() generator_tool_path.mkdir(parents=True, exist_ok=True) @@ -199,9 +199,7 @@ def _write_generator_presets(self, tool_path: Path, generator: Generator, toolch 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 + return write_preset(generator.name(), generator_tool_path, presets) def _write_presets(self, tool_path: Path, names: list[str], includes: list[Path]) -> None: """ @@ -211,7 +209,7 @@ def _write_presets(self, tool_path: Path, names: list[str], includes: list[Path] configure_preset = ConfigurePreset(name="cppython", hidden=True, inherits=names) presets = CMakePresets(configurePresets=[configure_preset], include=includes) - write_preset(tool_path, presets) + write_preset("cppython", tool_path, presets) # API Contract def install(self) -> None: @@ -237,8 +235,8 @@ def install(self) -> None: toolchain_path = generator.install() - directory = self._write_generator_presets(tool_path, generator, toolchain_path) - includes.append(directory) + json = self._write_generator_presets(tool_path, generator, toolchain_path) + includes.append(json) names.append(generator.name()) self._write_presets(tool_path, names, includes) @@ -265,8 +263,8 @@ def update(self) -> None: toolchain_path = generator.update() - directory = self._write_generator_presets(tool_path, generator, toolchain_path) - includes.append(directory) + json = self._write_generator_presets(tool_path, generator, toolchain_path) + includes.append(json) names.append(generator.name()) self._write_presets(tool_path, names, includes) diff --git a/cppython/utility.py b/cppython/utility.py index ff9a39f..9e1cd9f 100644 --- a/cppython/utility.py +++ b/cppython/utility.py @@ -8,18 +8,22 @@ from cppython.schema import CMakePresets -def read_preset(path: Path) -> CMakePresets: +def read_preset(name: str, path: Path) -> CMakePresets: """ Reading routing """ - preset_path = path / "CMakePresets.json" + preset_path = path / f"{name}.json" return CMakePresets.parse_file(path=preset_path) -def write_preset(path: Path, presets: CMakePresets) -> None: +def write_preset(name: str, path: Path, presets: CMakePresets) -> Path: """ Writing routine """ - with open(path / "CMakePresets.json", "w", encoding="utf8") as json_file: + file = path / f"{name}.json" + + with open(file, "w", encoding="utf8") as json_file: json.dump(presets.dict(), json_file, ensure_ascii=False, indent=2) + + return file diff --git a/tests/unit/test_utility.py b/tests/unit/test_utility.py index 6b298e7..e43c3eb 100644 --- a/tests/unit/test_utility.py +++ b/tests/unit/test_utility.py @@ -18,7 +18,7 @@ def test_preset_read_write(self, tmpdir: Path): """ presets = CMakePresets() - write_preset(tmpdir, presets) - output = read_preset(tmpdir) + write_preset("test", tmpdir, presets) + output = read_preset("test", tmpdir) assert presets == output From 8e29502105bc44a981f0605baceb5137236e6926 Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Sun, 8 May 2022 15:49:08 -0400 Subject: [PATCH 3/5] Move Preset Writing to Utility --- cppython/project.py | 60 ++++++++++++-------------------------- cppython/utility.py | 36 ++++++++++++++++++++++- tests/unit/test_utility.py | 15 +++++++++- 3 files changed, 67 insertions(+), 44 deletions(-) diff --git a/cppython/project.py b/cppython/project.py index ea6dc20..9b956eb 100644 --- a/cppython/project.py +++ b/cppython/project.py @@ -4,7 +4,6 @@ import logging from importlib import metadata -from pathlib import Path from typing import Any, Type, TypeVar from cppython_core.core import cppython_logger @@ -19,8 +18,8 @@ ) from pydantic import create_model -from cppython.schema import API, CMakePresets, ConfigurePreset, ProjectConfiguration -from cppython.utility import write_preset +from cppython.schema import API, ProjectConfiguration +from cppython.utility import write_preset, write_presets class ProjectBuilder: @@ -188,29 +187,6 @@ def download(self): 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 json file - """ - 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]) - - return write_preset(generator.name(), generator_tool_path, presets) - - 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("cppython", tool_path, presets) - # API Contract def install(self) -> None: """ @@ -226,20 +202,20 @@ def install(self) -> None: tool_path = self.pyproject.tool.cppython.tool_path tool_path.mkdir(parents=True, exist_ok=True) - names = [] - includes = [] + generator_output = [] # TODO: Async for generator in self._generators: cppython_logger.info(f"Installing {generator.name()} generator") - toolchain_path = generator.install() - - json = self._write_generator_presets(tool_path, generator, toolchain_path) - includes.append(json) - names.append(generator.name()) + try: + toolchain_path = generator.install() + generator_output.append((generator.name(), toolchain_path)) + except Exception as exception: + cppython_logger.error(f"Generator {generator.name()} failed to install") + raise exception - self._write_presets(tool_path, names, includes) + write_presets(tool_path, generator_output) def update(self) -> None: """ @@ -254,17 +230,17 @@ def update(self) -> None: tool_path = self.pyproject.tool.cppython.tool_path tool_path.mkdir(parents=True, exist_ok=True) - names = [] - includes = [] + generator_output = [] # TODO: Async for generator in self._generators: cppython_logger.info(f"Updating {generator.name()} generator") - toolchain_path = generator.update() - - json = self._write_generator_presets(tool_path, generator, toolchain_path) - includes.append(json) - names.append(generator.name()) + try: + toolchain_path = generator.update() + generator_output.append((generator.name(), toolchain_path)) + except Exception as exception: + cppython_logger.error(f"Generator {generator.name()} failed to update") + raise exception - self._write_presets(tool_path, names, includes) + write_presets(tool_path, generator_output) diff --git a/cppython/utility.py b/cppython/utility.py index 9e1cd9f..c440a62 100644 --- a/cppython/utility.py +++ b/cppython/utility.py @@ -5,7 +5,7 @@ import json from pathlib import Path -from cppython.schema import CMakePresets +from cppython.schema import CMakePresets, ConfigurePreset def read_preset(name: str, path: Path) -> CMakePresets: @@ -27,3 +27,37 @@ def write_preset(name: str, path: Path, presets: CMakePresets) -> Path: json.dump(presets.dict(), json_file, ensure_ascii=False, indent=2) return file + + +def write_presets(tool_path: Path, generator_output: list[tuple[str, Path]]) -> None: + """ + Write the cppython presets + """ + + def write_generator_presets(tool_path: Path, generator_name: str, toolchain_path: Path) -> Path: + """ + Write a generator preset. + @returns - The written json file + """ + 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]) + + return write_preset(generator_name, generator_tool_path, presets) + + names = [] + includes = [] + + for generator_name, toolchain in generator_output: + + preset_file = write_generator_presets(tool_path, generator_name, toolchain) + + names.append(generator_name) + includes.append(preset_file) + + configure_preset = ConfigurePreset(name="cppython", hidden=True, inherits=names) + presets = CMakePresets(configurePresets=[configure_preset], include=includes) + + write_preset("cppython", tool_path, presets) diff --git a/tests/unit/test_utility.py b/tests/unit/test_utility.py index e43c3eb..3e89731 100644 --- a/tests/unit/test_utility.py +++ b/tests/unit/test_utility.py @@ -4,7 +4,7 @@ from pathlib import Path from cppython.schema import CMakePresets -from cppython.utility import read_preset, write_preset +from cppython.utility import read_preset, write_preset, write_presets class TestBuilder: @@ -22,3 +22,16 @@ def test_preset_read_write(self, tmpdir: Path): output = read_preset("test", tmpdir) assert presets == output + + def test_presets(self, tmpdir: Path): + """ + TODO + """ + + input_toolchain = tmpdir / "input.cmake" + + with open(input_toolchain, "w", encoding="utf8") as file: + file.write("") + + generator_output = [("test", input_toolchain)] + write_presets(tmpdir, generator_output) From a48fdf610cd1e6307d147772a31809db5a8255ea Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Tue, 10 May 2022 03:24:48 -0400 Subject: [PATCH 4/5] Fix Serialization --- cppython/utility.py | 10 +++++++--- tests/unit/test_utility.py | 8 +++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/cppython/utility.py b/cppython/utility.py index c440a62..435d3b9 100644 --- a/cppython/utility.py +++ b/cppython/utility.py @@ -5,6 +5,9 @@ import json from pathlib import Path +from pydantic import FilePath +from pydantic.types import DirectoryPath + from cppython.schema import CMakePresets, ConfigurePreset @@ -23,18 +26,19 @@ def write_preset(name: str, path: Path, presets: CMakePresets) -> Path: """ file = path / f"{name}.json" + serialized = json.loads(presets.json()) with open(file, "w", encoding="utf8") as json_file: - json.dump(presets.dict(), json_file, ensure_ascii=False, indent=2) + json.dump(serialized, json_file, ensure_ascii=False, indent=2) return file -def write_presets(tool_path: Path, generator_output: list[tuple[str, Path]]) -> None: +def write_presets(tool_path: DirectoryPath, generator_output: list[tuple[str, FilePath]]) -> None: """ Write the cppython presets """ - def write_generator_presets(tool_path: Path, generator_name: str, toolchain_path: Path) -> Path: + def write_generator_presets(tool_path: DirectoryPath, generator_name: str, toolchain_path: FilePath) -> FilePath: """ Write a generator preset. @returns - The written json file diff --git a/tests/unit/test_utility.py b/tests/unit/test_utility.py index 3e89731..216d748 100644 --- a/tests/unit/test_utility.py +++ b/tests/unit/test_utility.py @@ -23,15 +23,17 @@ def test_preset_read_write(self, tmpdir: Path): assert presets == output - def test_presets(self, tmpdir: Path): + def test_presets(self, tmpdir): """ TODO """ - input_toolchain = tmpdir / "input.cmake" + temporary_directory = Path(tmpdir) + + input_toolchain = temporary_directory / "input.cmake" with open(input_toolchain, "w", encoding="utf8") as file: file.write("") generator_output = [("test", input_toolchain)] - write_presets(tmpdir, generator_output) + write_presets(temporary_directory, generator_output) From 3fd5db054236850398ad20fda0c3fd8a60f93868 Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Tue, 10 May 2022 05:44:10 -0400 Subject: [PATCH 5/5] Test CMakePreset File Layout --- cppython/schema.py | 36 +++++++++++++++++++++++------------- cppython/utility.py | 17 +++++++++-------- tests/unit/test_utility.py | 20 +++++++++++++++++--- 3 files changed, 49 insertions(+), 24 deletions(-) diff --git a/cppython/schema.py b/cppython/schema.py index b61054e..d121b93 100644 --- a/cppython/schema.py +++ b/cppython/schema.py @@ -6,11 +6,10 @@ from abc import abstractmethod from dataclasses import dataclass -from pathlib import PosixPath +from pathlib import Path from typing import Any, Optional -from pydantic import BaseModel, Extra, Field -from pydantic.types import FilePath +from pydantic import BaseModel, Extra, Field, validator class Preset(BaseModel): @@ -19,10 +18,10 @@ class Preset(BaseModel): """ name: str - hidden: Optional[bool] = None + hidden: Optional[bool] inherits: list[str] = [] - displayName: Optional[str] = None - description: Optional[str] = None + displayName: Optional[str] + description: Optional[str] class ConfigurePreset(Preset): @@ -30,7 +29,11 @@ class ConfigurePreset(Preset): Partial Configure Preset specification """ - toolchainFile: Optional[FilePath] = None + toolchainFile: Optional[str] + + @validator("toolchainFile") + def validate_path(cls, v): + return Path(v).as_posix() class BuildPreset(Preset): @@ -38,8 +41,8 @@ class BuildPreset(Preset): Partial Build Preset specification """ - configurePreset: Optional[str] = None - inheritConfigureEnvironment: Optional[bool] = None + configurePreset: Optional[str] + inheritConfigureEnvironment: Optional[bool] class TestPreset(Preset): @@ -47,8 +50,8 @@ class TestPreset(Preset): Partial Test Preset specification """ - configurePreset: Optional[str] = None - inheritConfigureEnvironment: Optional[bool] = None + configurePreset: Optional[str] + inheritConfigureEnvironment: Optional[bool] class CMakeVersion(BaseModel, extra=Extra.forbid): @@ -68,12 +71,19 @@ class CMakePresets(BaseModel, extra=Extra.forbid): version: int = Field(default=4, const=True) cmakeMinimumRequired: CMakeVersion = CMakeVersion() # TODO: 'version' compatability validation - include: list[FilePath] = [] - vendor: Optional[Any] = None + include: list[str] = [] + vendor: Optional[Any] configurePresets: list[ConfigurePreset] = [] buildPresets: list[BuildPreset] = [] testPresets: list[TestPreset] = [] + @validator("include") + def validate_path(cls, v): + output = [] + for value in v: + output.append(Path(value).as_posix()) + return output + @dataclass class ProjectConfiguration: diff --git a/cppython/utility.py b/cppython/utility.py index 435d3b9..b7d601a 100644 --- a/cppython/utility.py +++ b/cppython/utility.py @@ -5,9 +5,6 @@ import json from pathlib import Path -from pydantic import FilePath -from pydantic.types import DirectoryPath - from cppython.schema import CMakePresets, ConfigurePreset @@ -26,19 +23,19 @@ def write_preset(name: str, path: Path, presets: CMakePresets) -> Path: """ file = path / f"{name}.json" - serialized = json.loads(presets.json()) + serialized = json.loads(presets.json(exclude_none=True)) with open(file, "w", encoding="utf8") as json_file: json.dump(serialized, json_file, ensure_ascii=False, indent=2) return file -def write_presets(tool_path: DirectoryPath, generator_output: list[tuple[str, FilePath]]) -> None: +def write_presets(tool_path: Path, generator_output: list[tuple[str, Path]]) -> None: """ Write the cppython presets """ - def write_generator_presets(tool_path: DirectoryPath, generator_name: str, toolchain_path: FilePath) -> FilePath: + def write_generator_presets(tool_path: Path, generator_name: str, toolchain_path: Path) -> Path: """ Write a generator preset. @returns - The written json file @@ -46,7 +43,7 @@ def write_generator_presets(tool_path: DirectoryPath, generator_name: str, toolc 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) + configure_preset = ConfigurePreset(name=generator_name, hidden=True, toolchainFile=str(toolchain_path)) presets = CMakePresets(configurePresets=[configure_preset]) return write_preset(generator_name, generator_tool_path, presets) @@ -54,12 +51,16 @@ def write_generator_presets(tool_path: DirectoryPath, generator_name: str, toolc names = [] includes = [] + tool_path = tool_path / "cppython" + for generator_name, toolchain in generator_output: preset_file = write_generator_presets(tool_path, generator_name, toolchain) + relative_file = preset_file.relative_to(tool_path) + names.append(generator_name) - includes.append(preset_file) + includes.append(str(relative_file)) configure_preset = ConfigurePreset(name="cppython", hidden=True, inherits=names) presets = CMakePresets(configurePresets=[configure_preset], include=includes) diff --git a/tests/unit/test_utility.py b/tests/unit/test_utility.py index 216d748..fde0f81 100644 --- a/tests/unit/test_utility.py +++ b/tests/unit/test_utility.py @@ -12,14 +12,16 @@ class TestBuilder: TODO """ - def test_preset_read_write(self, tmpdir: Path): + def test_preset_read_write(self, tmpdir): """ TODO """ + temporary_directory = Path(tmpdir) + presets = CMakePresets() - write_preset("test", tmpdir, presets) - output = read_preset("test", tmpdir) + write_preset("test", temporary_directory, presets) + output = read_preset("test", temporary_directory) assert presets == output @@ -37,3 +39,15 @@ def test_presets(self, tmpdir): generator_output = [("test", input_toolchain)] write_presets(temporary_directory, generator_output) + + cppython_tool = temporary_directory / "cppython" + assert cppython_tool.exists() + + cppython_file = cppython_tool / "cppython.json" + assert cppython_file.exists() + + test_tool = cppython_tool / "test" + assert test_tool.exists() + + test_file = test_tool / "test.json" + assert test_file.exists()