diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a95d9ad --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[*] +indent_style = space +indent_size = 4 \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index ac5b79c..bd12d5e 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,6 +2,7 @@ "recommendations": [ "ms-python.mypy-type-checker", "asciidoctor.asciidoctor-vscode", - "charliermarsh.ruff" + "charliermarsh.ruff", + "tamasfe.even-better-toml" ] } \ No newline at end of file diff --git a/cppython/plugins/cmake/builder.py b/cppython/plugins/cmake/builder.py index 15e7dcf..82e2f33 100644 --- a/cppython/plugins/cmake/builder.py +++ b/cppython/plugins/cmake/builder.py @@ -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 @@ -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 @@ -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) diff --git a/cppython/plugins/cmake/resolution.py b/cppython/plugins/cmake/resolution.py index c3ad547..a36f584 100644 --- a/cppython/plugins/cmake/resolution.py +++ b/cppython/plugins/cmake/resolution.py @@ -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: @@ -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) diff --git a/cppython/plugins/cmake/schema.py b/cppython/plugins/cmake/schema.py index b310fb7..ab20994 100644 --- a/cppython/plugins/cmake/schema.py +++ b/cppython/plugins/cmake/schema.py @@ -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 @@ -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. @@ -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 @@ -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'): @@ -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): @@ -58,7 +76,7 @@ class CMakeSyncData(SyncData): class CMakeData(CPPythonModel): """Resolved CMake data""" - preset_file: FilePath + preset_file: Path configuration_name: str diff --git a/cppython/plugins/conan/builder.py b/cppython/plugins/conan/builder.py new file mode 100644 index 0000000..99f2afb --- /dev/null +++ b/cppython/plugins/conan/builder.py @@ -0,0 +1,177 @@ +"""Construction of Conan data""" + +from pathlib import Path +from string import Template +from textwrap import dedent + +import libcst as cst +from pydantic import DirectoryPath + +from cppython.plugins.conan.schema import ConanDependency + + +class RequiresTransformer(cst.CSTTransformer): + """Transformer to add or update the `requires` attribute in a ConanFile class.""" + + def __init__(self, dependencies: list[ConanDependency]) -> None: + """Initialize the transformer with a list of dependencies.""" + self.dependencies = dependencies + + def _create_requires_assignment(self) -> cst.Assign: + """Create a `requires` assignment statement.""" + return cst.Assign( + targets=[cst.AssignTarget(cst.Name('requires'))], + value=cst.List([ + cst.Element(cst.SimpleString(f'"{dependency.requires()}"')) for dependency in self.dependencies + ]), + ) + + def leave_ClassDef(self, original_node: cst.ClassDef, updated_node: cst.ClassDef) -> cst.BaseStatement: + """Modify the class definition to include or update 'requires'. + + Args: + original_node: The original class definition. + updated_node: The updated class definition. + + Returns: The modified class definition. + """ + if self._is_conanfile_class(original_node): + updated_node = self._update_requires(updated_node) + return updated_node + + @staticmethod + def _is_conanfile_class(class_node: cst.ClassDef) -> bool: + """Check if the class inherits from ConanFile. + + Args: + class_node: The class definition to check. + + Returns: True if the class inherits from ConanFile, False otherwise. + """ + return any((isinstance(base.value, cst.Name) and base.value.value == 'ConanFile') for base in class_node.bases) + + def _update_requires(self, updated_node: cst.ClassDef) -> cst.ClassDef: + """Update or add a 'requires' assignment in a ConanFile class definition.""" + # Check if 'requires' is already defined + for body_statement_line in updated_node.body.body: + if not isinstance(body_statement_line, cst.SimpleStatementLine): + continue + + assignment_statement = body_statement_line.body[0] + if not isinstance(assignment_statement, cst.Assign): + continue + + for target in assignment_statement.targets: + if not isinstance(target.target, cst.Name) or target.target.value != 'requires': + continue + + return self._replace_requires(updated_node, body_statement_line, assignment_statement) + + # Find the last attribute assignment before methods + last_attribute = None + for body_statement_line in updated_node.body.body: + if not isinstance(body_statement_line, cst.SimpleStatementLine): + break + assignment_statement = body_statement_line.body[0] + if not isinstance(assignment_statement, cst.Assign): + break + last_attribute = body_statement_line + + # Construct a new statement for the 'requires' attribute + new_statement = cst.SimpleStatementLine( + body=[self._create_requires_assignment()], + ) + + # Insert the new statement after the last attribute assignment + if last_attribute is not None: + new_body = list(updated_node.body.body) + index = new_body.index(last_attribute) + new_body.insert(index + 1, new_statement) + else: + new_body = [new_statement] + list(updated_node.body.body) + + return updated_node.with_changes(body=updated_node.body.with_changes(body=new_body)) + + def _replace_requires( + self, updated_node: cst.ClassDef, body_statement_line: cst.SimpleStatementLine, assignment_statement: cst.Assign + ) -> cst.ClassDef: + """Replace the existing 'requires' assignment with a new one. + + Args: + updated_node (cst.ClassDef): The class definition to update. + body_statement_line (cst.SimpleStatementLine): The body item containing the assignment. + assignment_statement (cst.Assign): The existing assignment statement. + + Returns: + cst.ClassDef: The updated class definition. + """ + new_value = cst.List([ + cst.Element(cst.SimpleString(f'"{dependency.requires()}"')) for dependency in self.dependencies + ]) + new_assignment = assignment_statement.with_changes(value=new_value) + return updated_node.with_changes( + body=updated_node.body.with_changes( + body=[new_assignment if item is body_statement_line else item for item in updated_node.body.body] + ) + ) + + +class Builder: + """Aids in building the information needed for the Conan plugin""" + + def __init__(self) -> None: + """Initialize the builder""" + self._filename = 'conanfile.py' + + @staticmethod + def _create_conanfile(conan_file: Path, dependencies: list[ConanDependency]) -> None: + """Creates a conanfile.py file with the necessary content.""" + template_string = """ + from conan import ConanFile + from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout + + class MyProject(ConanFile): + name = "myproject" + version = "1.0" + settings = "os", "compiler", "build_type", "arch" + requires = ${dependencies} + generators = "CMakeDeps" + + def layout(self): + cmake_layout(self) + + def generate(self): + tc = CMakeToolchain(self) + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build()""" + + template = Template(dedent(template_string)) + + values = { + 'dependencies': [dependency.requires() for dependency in dependencies], + } + + result = template.substitute(values) + + with open(conan_file, 'w', encoding='utf-8') as file: + file.write(result) + + def generate_conanfile(self, directory: DirectoryPath, dependencies: list[ConanDependency]) -> None: + """Generate a conanfile.py file for the project.""" + conan_file = directory / self._filename + + if conan_file.exists(): + source_code = conan_file.read_text(encoding='utf-8') + + module = cst.parse_module(source_code) + transformer = RequiresTransformer(dependencies) + modified = module.visit(transformer) + + conan_file.write_text(modified.code, encoding='utf-8') + else: + directory.mkdir(parents=True, exist_ok=True) + self._create_conanfile(conan_file, dependencies) diff --git a/cppython/plugins/conan/plugin.py b/cppython/plugins/conan/plugin.py index db441d3..079e73a 100644 --- a/cppython/plugins/conan/plugin.py +++ b/cppython/plugins/conan/plugin.py @@ -1,4 +1,9 @@ -"""_summary_""" +"""Conan Provider Plugin + +This module implements the Conan provider plugin for CPPython. It handles +integration with the Conan package manager, including dependency resolution, +installation, and synchronization with other tools. +""" from pathlib import Path from typing import Any @@ -10,7 +15,8 @@ from cppython.core.schema import CorePluginData, Information, SyncData from cppython.plugins.cmake.plugin import CMakeGenerator from cppython.plugins.cmake.schema import CMakeSyncData -from cppython.plugins.conan.resolution import resolve_conan_data +from cppython.plugins.conan.builder import Builder +from cppython.plugins.conan.resolution import resolve_conan_data, resolve_conan_dependency from cppython.plugins.conan.schema import ConanData from cppython.utility.exception import NotSupportedError from cppython.utility.utility import TypeName @@ -29,6 +35,8 @@ def __init__( self.core_data: CorePluginData = core_data self.data: ConanData = resolve_conan_data(configuration_data, core_data) + self.builder = Builder() + @staticmethod def _download_file(url: str, file: Path) -> None: """Replaces the given file with the contents of the url""" @@ -61,36 +69,49 @@ def information() -> Information: def install(self) -> None: """Installs the provider""" + resolved_dependencies = [resolve_conan_dependency(req) for req in self.core_data.cppython_data.dependencies] + + self.builder.generate_conanfile(self.core_data.project_data.project_root, resolved_dependencies) + + self.core_data.cppython_data.build_path.mkdir(parents=True, exist_ok=True) def update(self) -> None: """Updates the provider""" + resolved_dependencies = [resolve_conan_dependency(req) for req in self.core_data.cppython_data.dependencies] + + self.builder.generate_conanfile(self.core_data.project_data.project_root, resolved_dependencies) + + self.core_data.cppython_data.build_path.mkdir(parents=True, exist_ok=True) @staticmethod def supported_sync_type(sync_type: type[SyncData]) -> bool: - """_summary_ + """Checks if the given sync type is supported by the Conan provider. Args: - sync_type: _description_ + sync_type: The type of synchronization data to check. Returns: - _description_ + True if the sync type is supported, False otherwise. """ return sync_type in CMakeGenerator.sync_types() def sync_data(self, consumer: SyncConsumer) -> SyncData: - """_summary_ + """Generates synchronization data for the given consumer. Args: - consumer: _description_ + consumer: The input consumer for which synchronization data is generated. Returns: - _description_ + The synchronization data object. + + Raises: + NotSupportedError: If the consumer's sync type is not supported. """ for sync_type in consumer.sync_types(): if sync_type == CMakeSyncData: return CMakeSyncData( provider_name=TypeName('conan'), - top_level_includes=self.core_data.cppython_data.tool_path / 'conan' / 'conan_provider.cmake', + top_level_includes=self.core_data.cppython_data.install_path / 'conan_provider.cmake', ) raise NotSupportedError('OOF') diff --git a/cppython/plugins/conan/resolution.py b/cppython/plugins/conan/resolution.py index b006062..0abcb59 100644 --- a/cppython/plugins/conan/resolution.py +++ b/cppython/plugins/conan/resolution.py @@ -1,9 +1,34 @@ -"""_summary_""" +"""Provides functionality to resolve Conan-specific data for the CPPython project.""" from typing import Any +from packaging.requirements import Requirement + +from cppython.core.exception import ConfigException from cppython.core.schema import CorePluginData -from cppython.plugins.conan.schema import ConanData +from cppython.plugins.conan.schema import ConanData, ConanDependency + + +def resolve_conan_dependency(requirement: Requirement) -> ConanDependency: + """Resolves a Conan dependency from a requirement""" + specifiers = requirement.specifier + + # If the length of specifiers is greater than one, raise a configuration error + if len(specifiers) > 1: + raise ConfigException('Multiple specifiers are not supported. Please provide a single specifier.', []) + + # Extract the version from the single specifier + min_version = None + if len(specifiers) == 1: + specifier = next(iter(specifiers)) + if specifier.operator != '>=': + raise ConfigException(f"Unsupported specifier '{specifier.operator}'. Only '>=' is supported.", []) + min_version = specifier.version + + return ConanDependency( + name=requirement.name, + version_ge=min_version, + ) def resolve_conan_data(data: dict[str, Any], core_data: CorePluginData) -> ConanData: diff --git a/cppython/plugins/conan/schema.py b/cppython/plugins/conan/schema.py index 6d751a2..a291938 100644 --- a/cppython/plugins/conan/schema.py +++ b/cppython/plugins/conan/schema.py @@ -1,8 +1,28 @@ -"""TODO""" +"""Conan plugin schema + +This module defines Pydantic models used for integrating the Conan +package manager with the CPPython environment. The classes within +provide structured configuration and data needed by the Conan Provider. +""" from cppython.core.schema import CPPythonModel +class ConanDependency(CPPythonModel): + """Dependency information""" + + name: str + version_ge: str | None = None + include_prerelease: bool | None = None + + def requires(self) -> str: + """Generate the requires attribute for Conan""" + # TODO: Implement lower and upper bounds per conan documentation + if self.version_ge: + return f'{self.name}/[>={self.version_ge}]' + return self.name + + class ConanData(CPPythonModel): """Resolved conan data""" diff --git a/cppython/plugins/git/plugin.py b/cppython/plugins/git/plugin.py index 5bb6d45..bbd6ac1 100644 --- a/cppython/plugins/git/plugin.py +++ b/cppython/plugins/git/plugin.py @@ -1,4 +1,9 @@ -"""Git SCM plugin""" +"""Git SCM Plugin + +This module implements the Git SCM plugin for CPPython. It provides +functionality for interacting with Git repositories, including feature +detection, version extraction, and project description retrieval. +""" from pathlib import Path diff --git a/cppython/plugins/vcpkg/plugin.py b/cppython/plugins/vcpkg/plugin.py index 1fef385..7c14ddb 100644 --- a/cppython/plugins/vcpkg/plugin.py +++ b/cppython/plugins/vcpkg/plugin.py @@ -1,6 +1,5 @@ """The vcpkg provider implementation""" -import json from logging import getLogger from os import name as system_name from pathlib import Path, PosixPath, WindowsPath @@ -47,13 +46,13 @@ def features(directory: Path) -> SupportedProviderFeatures: @staticmethod def supported_sync_type(sync_type: type[SyncData]) -> bool: - """_summary_ + """Checks if the given sync type is supported by the vcpkg provider. Args: - sync_type: _description_ + sync_type: The type of synchronization data to check. Returns: - _description_ + True if the sync type is supported, False otherwise. """ return sync_type in CMakeGenerator.sync_types() @@ -193,9 +192,9 @@ def install(self) -> None: manifest = generate_manifest(self.core_data, self.data) # Write out the manifest - serialized = json.loads(manifest.model_dump_json(exclude_none=True, by_alias=True)) + serialized = manifest.model_dump_json(exclude_none=True, by_alias=True, indent=4) with open(manifest_directory / 'vcpkg.json', 'w', encoding='utf8') as file: - json.dump(serialized, file, ensure_ascii=False, indent=4) + file.write(serialized) executable = self.core_data.cppython_data.install_path / 'vcpkg' logger = getLogger('cppython.vcpkg') @@ -224,9 +223,9 @@ def update(self) -> None: manifest = generate_manifest(self.core_data, self.data) # Write out the manifest - serialized = json.loads(manifest.model_dump_json(exclude_none=True, by_alias=True)) + serialized = manifest.model_dump_json(exclude_none=True, by_alias=True, indent=4) with open(manifest_directory / 'vcpkg.json', 'w', encoding='utf8') as file: - json.dump(serialized, file, ensure_ascii=False, indent=4) + file.write(serialized) executable = self.core_data.cppython_data.install_path / 'vcpkg' logger = getLogger('cppython.vcpkg') diff --git a/cppython/test/mock/generator.py b/cppython/test/mock/generator.py index e392ac3..6c99869 100644 --- a/cppython/test/mock/generator.py +++ b/cppython/test/mock/generator.py @@ -51,10 +51,10 @@ def information() -> Information: @staticmethod def sync_types() -> list[type[SyncData]]: - """_summary_ + """Returns the supported synchronization data types for the mock generator. Returns: - _description_ + A list of supported synchronization data types. """ return [MockSyncData] diff --git a/examples/conan_cmake/inject/CMakeLists.txt b/examples/conan_cmake/inject/CMakeLists.txt new file mode 100644 index 0000000..7cf1ecc --- /dev/null +++ b/examples/conan_cmake/inject/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.24) + +project(FormatOutput LANGUAGES CXX C) + +set(CMAKE_CXX_STANDARD 14) + +find_package(fmt REQUIRED) + +add_executable(main main.cpp) +target_link_libraries(main PRIVATE fmt::fmt) \ No newline at end of file diff --git a/examples/conan_cmake/inject/conanfile.py b/examples/conan_cmake/inject/conanfile.py new file mode 100644 index 0000000..b2f756c --- /dev/null +++ b/examples/conan_cmake/inject/conanfile.py @@ -0,0 +1,28 @@ +"""Preexisting CMake project with Conan integration.""" + +from conan import ConanFile # type: ignore +from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout # type: ignore + + +class MyProject(ConanFile): # type: ignore + """Conan file for a simple CMake project.""" + + name = 'myproject' + version = '1.0' + settings = 'os', 'compiler', 'build_type', 'arch' + generators = 'CMakeDeps' + + def layout(self) -> None: + """Define the layout of the project.""" + cmake_layout(self) + + def generate(self) -> None: + """Generate the CMake toolchain file.""" + tc = CMakeToolchain(self) + tc.generate() + + def build(self) -> None: + """Build the project using CMake.""" + cmake = CMake(self) + cmake.configure() + cmake.build() diff --git a/examples/conan_cmake/inject/main.cpp b/examples/conan_cmake/inject/main.cpp new file mode 100644 index 0000000..4de3567 --- /dev/null +++ b/examples/conan_cmake/inject/main.cpp @@ -0,0 +1,7 @@ +#include "fmt/color.h" + +int main() +{ + fmt::print(fg(fmt::terminal_color::cyan), "Hello fmt {}!\n", FMT_VERSION); + return 0; +} \ No newline at end of file diff --git a/examples/conan_cmake/inject/pyproject.toml b/examples/conan_cmake/inject/pyproject.toml new file mode 100644 index 0000000..7d320b8 --- /dev/null +++ b/examples/conan_cmake/inject/pyproject.toml @@ -0,0 +1,27 @@ +[project] +description = "A simple project showing how to use conan with CPPython" +name = "cppython-conan-cmake-simple" +version = "1.0.0" + +license = {text = "MIT"} + +authors = [{name = "Synodic Software", email = "contact@synodic.software"}] + +requires-python = ">=3.13" + +dependencies = ["cppython[conan, cmake]>=0.1.0"] + +[tool.cppython] +generator-name = "cmake" +provider-name = "conan" + +install-path = "install" + +dependencies = ["fmt>=11.0.1"] + +[tool.cppython.generator] + +[tool.cppython.provider] + +[tool.pdm] +distribution = false diff --git a/examples/conan_cmake/simple/CMakeLists.txt b/examples/conan_cmake/simple/CMakeLists.txt new file mode 100644 index 0000000..7cf1ecc --- /dev/null +++ b/examples/conan_cmake/simple/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.24) + +project(FormatOutput LANGUAGES CXX C) + +set(CMAKE_CXX_STANDARD 14) + +find_package(fmt REQUIRED) + +add_executable(main main.cpp) +target_link_libraries(main PRIVATE fmt::fmt) \ No newline at end of file diff --git a/examples/conan_cmake/simple/main.cpp b/examples/conan_cmake/simple/main.cpp new file mode 100644 index 0000000..4de3567 --- /dev/null +++ b/examples/conan_cmake/simple/main.cpp @@ -0,0 +1,7 @@ +#include "fmt/color.h" + +int main() +{ + fmt::print(fg(fmt::terminal_color::cyan), "Hello fmt {}!\n", FMT_VERSION); + return 0; +} \ No newline at end of file diff --git a/examples/conan_cmake/simple/pyproject.toml b/examples/conan_cmake/simple/pyproject.toml new file mode 100644 index 0000000..7d320b8 --- /dev/null +++ b/examples/conan_cmake/simple/pyproject.toml @@ -0,0 +1,27 @@ +[project] +description = "A simple project showing how to use conan with CPPython" +name = "cppython-conan-cmake-simple" +version = "1.0.0" + +license = {text = "MIT"} + +authors = [{name = "Synodic Software", email = "contact@synodic.software"}] + +requires-python = ">=3.13" + +dependencies = ["cppython[conan, cmake]>=0.1.0"] + +[tool.cppython] +generator-name = "cmake" +provider-name = "conan" + +install-path = "install" + +dependencies = ["fmt>=11.0.1"] + +[tool.cppython.generator] + +[tool.cppython.provider] + +[tool.pdm] +distribution = false diff --git a/examples/vcpkg_cmake/simple/CMakePresets.json b/examples/vcpkg_cmake/simple/CMakePresets.json deleted file mode 100644 index bdec0d0..0000000 --- a/examples/vcpkg_cmake/simple/CMakePresets.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": 2, - "configurePresets": [ - { - "name": "vcpkg", - "generator": "Ninja", - "binaryDir": "${sourceDir}/build", - "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" - } - } - ] -} \ No newline at end of file diff --git a/examples/vcpkg_cmake/simple/pyproject.toml b/examples/vcpkg_cmake/simple/pyproject.toml index a910566..c2bf184 100644 --- a/examples/vcpkg_cmake/simple/pyproject.toml +++ b/examples/vcpkg_cmake/simple/pyproject.toml @@ -5,15 +5,11 @@ version = "1.0.0" license = {text = "MIT"} -authors = [ - {name = "Synodic Software", email = "contact@synodic.software"}, -] +authors = [{name = "Synodic Software", email = "contact@synodic.software"}] requires-python = ">=3.13" -dependencies = [ - "cppython>=0.1.0", -] +dependencies = ["cppython[vcpkg, cmake]>=0.1.0"] [tool.cppython] generator-name = "cmake" @@ -21,9 +17,7 @@ provider-name = "vcpkg" install-path = "install" -dependencies = [ - "fmt>=11.0.2", -] +dependencies = ["fmt>=11.0.2"] [tool.cppython.generator] diff --git a/pdm.lock b/pdm.lock index 2056101..5c79235 100644 --- a/pdm.lock +++ b/pdm.lock @@ -2,10 +2,10 @@ # It is not intended for manual editing. [metadata] -groups = ["default", "git", "lint", "pdm", "pytest", "test"] +groups = ["default", "cmake", "conan", "git", "lint", "pdm", "pytest", "test"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:39486bd6dc7dafe2696c974da9b2f015a8f4d918670601b707214a9568f49052" +content_hash = "sha256:90f922a571d25ecf4ebcbf4f9f973332d3a18d433d2ced3a15230cb5f770d3fe" [[metadata.targets]] requires_python = ">=3.13" @@ -57,7 +57,7 @@ name = "certifi" version = "2025.1.31" requires_python = ">=3.6" summary = "Python package for providing Mozilla's CA Bundle." -groups = ["default", "pdm"] +groups = ["default", "conan", "pdm"] files = [ {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, @@ -68,7 +68,7 @@ name = "charset-normalizer" version = "3.4.1" requires_python = ">=3.7" summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -groups = ["default"] +groups = ["default", "conan", "pdm"] files = [ {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, @@ -102,18 +102,69 @@ files = [ {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, ] +[[package]] +name = "cmake" +version = "4.0.0" +requires_python = ">=3.7" +summary = "CMake is an open-source, cross-platform family of tools designed to build, test and package software" +groups = ["cmake"] +dependencies = [ + "importlib-metadata>=1.4; python_version < \"3.8\"", +] +files = [ + {file = "cmake-4.0.0-py3-none-macosx_10_10_universal2.whl", hash = "sha256:2f01499980f5824092c08e8fe0893c31d4f3783c8475ea2d7cd0706c7dc646a3"}, + {file = "cmake-4.0.0-py3-none-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2877636e057a8227b1792614bb0eebab37632c73bae3b4939b2b20416248cf76"}, + {file = "cmake-4.0.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:26d0e992eea03d7ad8a2f320884f8eaf8178c3cf23f2f5f004cdca8354adf137"}, + {file = "cmake-4.0.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9af3ef2931c84557d58383169cc3cad6852de625c1fd8883ee696ac436ab1eb3"}, + {file = "cmake-4.0.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:340558bf3b50876380ac036d9e8a0e8c30ef28b071097cbceb3929519c021d4a"}, + {file = "cmake-4.0.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a9b7b1da83219d563cae0685c989d8aedf58480de1e64f3de2f51364606272f0"}, + {file = "cmake-4.0.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f23eaade0cf683c938849962c09b3c752cf96cddc872288c80620466e6acf0ce"}, + {file = "cmake-4.0.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dda13b113de7dba00f20587011c1b9b90708a22fe8fef530a46bfb4a4ee2bd2"}, + {file = "cmake-4.0.0-py3-none-manylinux_2_31_armv7l.whl", hash = "sha256:8606d0228529d9cb688fc8e4e31ae14236526fad586680aa3e15f6dd69c76488"}, + {file = "cmake-4.0.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:ba414b29459526bb10be13ecd38d022f0b5ebed2fec33bdae66d1568ddcf2e2e"}, + {file = "cmake-4.0.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:6a89cf41a770763a2132b32514dfd8000e1147ecec8cb5ad3d5d83041faea790"}, + {file = "cmake-4.0.0-py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:02dd7305ed88d9c98930116fa66b327034e01080b6688886d478b099bd6bf7ba"}, + {file = "cmake-4.0.0-py3-none-musllinux_1_1_s390x.whl", hash = "sha256:e1092881c07c5c892448aad04bdc36357804f37ad2ff42272fd1a940f679aa1a"}, + {file = "cmake-4.0.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:098ceee569eedc7853792f8b4fc6cc5b2995c1481b8bc13074aa565c2ebbac0d"}, + {file = "cmake-4.0.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4b2e75474ee412ca6f7f224b14a883206adc423daff1745752ce815cc5fbb599"}, + {file = "cmake-4.0.0-py3-none-win32.whl", hash = "sha256:e27776fbb5a101a9c8b71f9f360918d0985dfd7d1f057fa90713f8f2125e0e73"}, + {file = "cmake-4.0.0-py3-none-win_amd64.whl", hash = "sha256:a7ec8b997f75da5310c571f79ce560bc8941013549e47144bfa49025ced79b00"}, + {file = "cmake-4.0.0-py3-none-win_arm64.whl", hash = "sha256:31c4a1a3490f142c965de644e65987d1c76a2b90dec71a9a001c85bf535a5e58"}, + {file = "cmake-4.0.0.tar.gz", hash = "sha256:b929ab7d2ebd6b9a81018b0248ea75edb3dc4ad69367e8c75fb0f6774bb6e962"}, +] + [[package]] name = "colorama" version = "0.4.6" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" summary = "Cross-platform colored terminal text." -groups = ["default", "pytest", "test"] -marker = "sys_platform == \"win32\" or platform_system == \"Windows\"" +groups = ["default", "conan", "pytest", "test"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "conan" +version = "2.15.0" +requires_python = ">=3.6" +summary = "Conan C/C++ package manager" +groups = ["conan"] +dependencies = [ + "Jinja2<4.0.0,>=3.0", + "PyYAML<7.0,>=6.0", + "colorama<0.5.0,>=0.4.3", + "distro<=1.8.0,>=1.4.0; platform_system == \"Linux\" or platform_system == \"FreeBSD\"", + "fasteners>=0.15", + "patch-ng<1.19,>=1.18.0", + "python-dateutil<3,>=2.8.0", + "requests<3.0.0,>=2.25", + "urllib3<2.1,>=1.26.6", +] +files = [ + {file = "conan-2.15.0.tar.gz", hash = "sha256:c984c5fb0623ea60b39c2feb351886a3e2df8f6a1f99e3f322125a24198f05da"}, +] + [[package]] name = "coverage" version = "7.8.0" @@ -205,6 +256,18 @@ files = [ {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, ] +[[package]] +name = "distro" +version = "1.8.0" +requires_python = ">=3.6" +summary = "Distro - an OS platform information API" +groups = ["conan"] +marker = "platform_system == \"Linux\" or platform_system == \"FreeBSD\"" +files = [ + {file = "distro-1.8.0-py3-none-any.whl", hash = "sha256:99522ca3e365cac527b44bde033f64c6945d90eb9f769703caaec52b09bbd3ff"}, + {file = "distro-1.8.0.tar.gz", hash = "sha256:02e111d1dc6a50abb8eed6bf31c3e48ed8b0830d1ea2a1b78c61765c2513fdd8"}, +] + [[package]] name = "dulwich" version = "0.22.8" @@ -225,6 +288,17 @@ files = [ {file = "dulwich-0.22.8.tar.gz", hash = "sha256:701547310415de300269331abe29cb5717aa1ea377af826bf513d0adfb1c209b"}, ] +[[package]] +name = "fasteners" +version = "0.19" +requires_python = ">=3.6" +summary = "A python package that provides useful locks" +groups = ["conan"] +files = [ + {file = "fasteners-0.19-py3-none-any.whl", hash = "sha256:758819cb5d94cdedf4e836988b74de396ceacb8e2794d21f82d131fd9ee77237"}, + {file = "fasteners-0.19.tar.gz", hash = "sha256:b4f37c3ac52d8a445af3a66bce57b33b5e90b97c696b7b984f530cf8f0ded09c"}, +] + [[package]] name = "filelock" version = "3.18.0" @@ -280,7 +354,7 @@ files = [ [[package]] name = "httpcore" -version = "1.0.7" +version = "1.0.8" requires_python = ">=3.8" summary = "A minimal low-level HTTP client." groups = ["pdm"] @@ -289,8 +363,8 @@ dependencies = [ "h11<0.15,>=0.13", ] files = [ - {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, - {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, + {file = "httpcore-1.0.8-py3-none-any.whl", hash = "sha256:5254cf149bcb5f75e9d1b2b9f729ea4a4b883d1ad7379fc632b727cec23674be"}, + {file = "httpcore-1.0.8.tar.gz", hash = "sha256:86e94505ed24ea06514883fd44d2bc02d90e77e7979c8eb71b90f41d364a1bad"}, ] [[package]] @@ -326,12 +400,26 @@ files = [ {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, ] +[[package]] +name = "id" +version = "1.5.0" +requires_python = ">=3.8" +summary = "A tool for generating OIDC identities" +groups = ["pdm"] +dependencies = [ + "requests", +] +files = [ + {file = "id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658"}, + {file = "id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d"}, +] + [[package]] name = "idna" version = "3.10" requires_python = ">=3.6" summary = "Internationalized Domain Names in Applications (IDNA)" -groups = ["default", "pdm"] +groups = ["default", "conan", "pdm"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -359,6 +447,40 @@ files = [ {file = "installer-0.7.0.tar.gz", hash = "sha256:a26d3e3116289bb08216e0d0f7d925fcef0b0194eedfa0c944bcaaa106c4b631"}, ] +[[package]] +name = "jinja2" +version = "3.1.6" +requires_python = ">=3.7" +summary = "A very fast and expressive template engine." +groups = ["conan"] +dependencies = [ + "MarkupSafe>=2.0", +] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[[package]] +name = "libcst" +version = "1.7.0" +requires_python = ">=3.9" +summary = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.13 programs." +groups = ["conan"] +dependencies = [ + "pyyaml>=5.2", +] +files = [ + {file = "libcst-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:93417d36c2a1b70d651d0e970ff73339e8dcd64d341672b68823fa0039665022"}, + {file = "libcst-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6523731bfbdbc045ff8649130fe14a46b31ad6925f67acdc0e0d80a0c61719fd"}, + {file = "libcst-1.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a252fa03ea00986f03100379f11e15d381103a09667900fb0fa2076cec19081a"}, + {file = "libcst-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09a5530b40a15dbe6fac842ef2ad87ad561760779380ccf3ade6850854d81406"}, + {file = "libcst-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0456381c939169c4f11caecdb30f7aca6f234640731f8f965849c1631930536b"}, + {file = "libcst-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c8d6176a667d2db0132d133dad6bbf965f915f3071559342ca2cdbbec537ed12"}, + {file = "libcst-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:6137fe549bfbb017283c3cf85419eb0dfaa20a211ad6d525538a2494e248a84b"}, + {file = "libcst-1.7.0.tar.gz", hash = "sha256:a63f44ffa81292f183656234c7f2848653ff45c17d867db83c9335119e28aafa"}, +] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -373,6 +495,36 @@ files = [ {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, ] +[[package]] +name = "markupsafe" +version = "3.0.2" +requires_python = ">=3.9" +summary = "Safely add untrusted strings to HTML/XML markup." +groups = ["conan"] +files = [ + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, +] + [[package]] name = "mdurl" version = "0.1.2" @@ -449,20 +601,30 @@ files = [ {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] +[[package]] +name = "patch-ng" +version = "1.18.1" +requires_python = ">=3.6" +summary = "Library to parse and apply unified diffs." +groups = ["conan"] +files = [ + {file = "patch-ng-1.18.1.tar.gz", hash = "sha256:52fd46ee46f6c8667692682c1fd7134edc65a2d2d084ebec1d295a6087fc0291"}, +] + [[package]] name = "pbs-installer" -version = "2025.3.17" +version = "2025.4.9" requires_python = ">=3.8" summary = "Installer for Python Build Standalone" groups = ["pdm"] files = [ - {file = "pbs_installer-2025.3.17-py3-none-any.whl", hash = "sha256:d2b0563b1d5d814e479f3c43d7aee019250f68a0a113d754714fa9a721f83b47"}, - {file = "pbs_installer-2025.3.17.tar.gz", hash = "sha256:dde058f925b989c1d3bd90739c16ffd0e68732f7716e4d1e01ca480d00a67560"}, + {file = "pbs_installer-2025.4.9-py3-none-any.whl", hash = "sha256:af110b398248584422f46760ce1e3793622fe3fbcde47aacd22e35baf8c3db1d"}, + {file = "pbs_installer-2025.4.9.tar.gz", hash = "sha256:15755bc94769a544af5dda155f973c70caf76f0e70b21f3c8a8ed506f102f88f"}, ] [[package]] name = "pdm" -version = "2.23.0" +version = "2.23.1" requires_python = ">=3.9" summary = "A modern Python package and dependency manager supporting the latest PEP standards" groups = ["pdm"] @@ -475,6 +637,7 @@ dependencies = [ "hishel>=0.0.32", "httpcore>=1.0.6", "httpx[socks]<1,>0.20", + "id>=1.5.0", "importlib-metadata>=3.6; python_version < \"3.10\"", "installer<0.8,>=0.7", "msgpack>=1.0", @@ -489,12 +652,12 @@ dependencies = [ "tomli>=1.1.0; python_version < \"3.11\"", "tomlkit<1,>=0.11.1", "truststore>=0.9; python_version >= \"3.10\"", - "unearth>=0.17.0", + "unearth>=0.17.5", "virtualenv>=20", ] files = [ - {file = "pdm-2.23.0-py3-none-any.whl", hash = "sha256:965087f3ce1ad1f9ef84dc11c499ef5d1c5e0a5f09a1437148d9beaf7502d6c9"}, - {file = "pdm-2.23.0.tar.gz", hash = "sha256:ec5ce439648b42b928f4afa91c2b6251e6a33f40cc83779d899ca970bd9eb131"}, + {file = "pdm-2.23.1-py3-none-any.whl", hash = "sha256:202c217eef140f7fe933f2025cc2a7a7cc0638abe43c51c4a74d3c1aa686d6f7"}, + {file = "pdm-2.23.1.tar.gz", hash = "sha256:cf12cd2201df78a6543a5485ac1d7015a73b3a6b10c98b66dfae6ebd481386fe"}, ] [[package]] @@ -636,6 +799,20 @@ files = [ {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, ] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +summary = "Extensions to the standard Python datetime module" +groups = ["conan"] +dependencies = [ + "six>=1.5", +] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + [[package]] name = "python-dotenv" version = "1.1.0" @@ -647,12 +824,31 @@ files = [ {file = "python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5"}, ] +[[package]] +name = "pyyaml" +version = "6.0.2" +requires_python = ">=3.8" +summary = "YAML parser and emitter for Python" +groups = ["conan"] +files = [ + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + [[package]] name = "requests" version = "2.32.3" requires_python = ">=3.8" summary = "Python HTTP for Humans." -groups = ["default"] +groups = ["default", "conan", "pdm"] dependencies = [ "certifi>=2017.4.17", "charset-normalizer<4,>=2", @@ -693,29 +889,29 @@ files = [ [[package]] name = "ruff" -version = "0.11.4" +version = "0.11.5" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["lint"] files = [ - {file = "ruff-0.11.4-py3-none-linux_armv6l.whl", hash = "sha256:d9f4a761ecbde448a2d3e12fb398647c7f0bf526dbc354a643ec505965824ed2"}, - {file = "ruff-0.11.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8c1747d903447d45ca3d40c794d1a56458c51e5cc1bc77b7b64bd2cf0b1626cc"}, - {file = "ruff-0.11.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:51a6494209cacca79e121e9b244dc30d3414dac8cc5afb93f852173a2ecfc906"}, - {file = "ruff-0.11.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f171605f65f4fc49c87f41b456e882cd0c89e4ac9d58e149a2b07930e1d466f"}, - {file = "ruff-0.11.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebf99ea9af918878e6ce42098981fc8c1db3850fef2f1ada69fb1dcdb0f8e79e"}, - {file = "ruff-0.11.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edad2eac42279df12e176564a23fc6f4aaeeb09abba840627780b1bb11a9d223"}, - {file = "ruff-0.11.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f103a848be9ff379fc19b5d656c1f911d0a0b4e3e0424f9532ececf319a4296e"}, - {file = "ruff-0.11.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:193e6fac6eb60cc97b9f728e953c21cc38a20077ed64f912e9d62b97487f3f2d"}, - {file = "ruff-0.11.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7af4e5f69b7c138be8dcffa5b4a061bf6ba6a3301f632a6bce25d45daff9bc99"}, - {file = "ruff-0.11.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:126b1bf13154aa18ae2d6c3c5efe144ec14b97c60844cfa6eb960c2a05188222"}, - {file = "ruff-0.11.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8806daaf9dfa881a0ed603f8a0e364e4f11b6ed461b56cae2b1c0cab0645304"}, - {file = "ruff-0.11.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5d94bb1cc2fc94a769b0eb975344f1b1f3d294da1da9ddbb5a77665feb3a3019"}, - {file = "ruff-0.11.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:995071203d0fe2183fc7a268766fd7603afb9996785f086b0d76edee8755c896"}, - {file = "ruff-0.11.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7a37ca937e307ea18156e775a6ac6e02f34b99e8c23fe63c1996185a4efe0751"}, - {file = "ruff-0.11.4-py3-none-win32.whl", hash = "sha256:0e9365a7dff9b93af933dab8aebce53b72d8f815e131796268709890b4a83270"}, - {file = "ruff-0.11.4-py3-none-win_amd64.whl", hash = "sha256:5a9fa1c69c7815e39fcfb3646bbfd7f528fa8e2d4bebdcf4c2bd0fa037a255fb"}, - {file = "ruff-0.11.4-py3-none-win_arm64.whl", hash = "sha256:d435db6b9b93d02934cf61ef332e66af82da6d8c69aefdea5994c89997c7a0fc"}, - {file = "ruff-0.11.4.tar.gz", hash = "sha256:f45bd2fb1a56a5a85fae3b95add03fb185a0b30cf47f5edc92aa0355ca1d7407"}, + {file = "ruff-0.11.5-py3-none-linux_armv6l.whl", hash = "sha256:2561294e108eb648e50f210671cc56aee590fb6167b594144401532138c66c7b"}, + {file = "ruff-0.11.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac12884b9e005c12d0bd121f56ccf8033e1614f736f766c118ad60780882a077"}, + {file = "ruff-0.11.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4bfd80a6ec559a5eeb96c33f832418bf0fb96752de0539905cf7b0cc1d31d779"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0947c0a1afa75dcb5db4b34b070ec2bccee869d40e6cc8ab25aca11a7d527794"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad871ff74b5ec9caa66cb725b85d4ef89b53f8170f47c3406e32ef040400b038"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6cf918390cfe46d240732d4d72fa6e18e528ca1f60e318a10835cf2fa3dc19f"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56145ee1478582f61c08f21076dc59153310d606ad663acc00ea3ab5b2125f82"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5f66f8f1e8c9fc594cbd66fbc5f246a8d91f916cb9667e80208663ec3728304"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80b4df4d335a80315ab9afc81ed1cff62be112bd165e162b5eed8ac55bfc8470"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3068befab73620b8a0cc2431bd46b3cd619bc17d6f7695a3e1bb166b652c382a"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5da2e710a9641828e09aa98b92c9ebbc60518fdf3921241326ca3e8f8e55b8b"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ef39f19cb8ec98cbc762344921e216f3857a06c47412030374fffd413fb8fd3a"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b2a7cedf47244f431fd11aa5a7e2806dda2e0c365873bda7834e8f7d785ae159"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:81be52e7519f3d1a0beadcf8e974715b2dfc808ae8ec729ecfc79bddf8dbb783"}, + {file = "ruff-0.11.5-py3-none-win32.whl", hash = "sha256:e268da7b40f56e3eca571508a7e567e794f9bfcc0f412c4b607931d3af9c4afe"}, + {file = "ruff-0.11.5-py3-none-win_amd64.whl", hash = "sha256:6c6dc38af3cfe2863213ea25b6dc616d679205732dc0fb673356c2d69608f800"}, + {file = "ruff-0.11.5-py3-none-win_arm64.whl", hash = "sha256:67e241b4314f4eacf14a601d586026a962f4002a475aa702c69980a38087aa4e"}, + {file = "ruff-0.11.5.tar.gz", hash = "sha256:cae2e2439cb88853e421901ec040a758960b576126dab520fa08e9de431d1bef"}, ] [[package]] @@ -729,6 +925,17 @@ files = [ {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, ] +[[package]] +name = "six" +version = "1.17.0" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +summary = "Python 2 and 3 compatibility utilities" +groups = ["conan"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + [[package]] name = "sniffio" version = "1.3.1" @@ -807,13 +1014,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.13.1" +version = "4.13.2" requires_python = ">=3.8" summary = "Backported and Experimental Type Hints for Python 3.8+" groups = ["default", "lint"] files = [ - {file = "typing_extensions-4.13.1-py3-none-any.whl", hash = "sha256:4b6cf02909eb5495cfbc3f6e8fd49217e6cc7944e145cdda8caa3734777f9e69"}, - {file = "typing_extensions-4.13.1.tar.gz", hash = "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff"}, + {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, + {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, ] [[package]] @@ -832,7 +1039,7 @@ files = [ [[package]] name = "unearth" -version = "0.17.3" +version = "0.17.5" requires_python = ">=3.8" summary = "A utility to fetch and download python packages" groups = ["pdm"] @@ -841,19 +1048,19 @@ dependencies = [ "packaging>=20", ] files = [ - {file = "unearth-0.17.3-py3-none-any.whl", hash = "sha256:654dba44ac6ea9e4a73acface11dafc342fb0a8095e7d63c2d6801a047e96dfc"}, - {file = "unearth-0.17.3.tar.gz", hash = "sha256:32e96c9df63c563a118d411dfb4f9c672f181a410977f6765c0ed430b0d32784"}, + {file = "unearth-0.17.5-py3-none-any.whl", hash = "sha256:9963e66b14f0484644c9b45b517e530befb2de6a8da4b06a9a38bed2d086dfe6"}, + {file = "unearth-0.17.5.tar.gz", hash = "sha256:a19e1c02e64b40518d088079c7416fc41b45a648b81a4128aac02597234ee6ba"}, ] [[package]] name = "urllib3" -version = "2.3.0" -requires_python = ">=3.9" +version = "2.0.7" +requires_python = ">=3.7" summary = "HTTP library with thread-safe connection pooling, file post, and more." -groups = ["default", "git"] +groups = ["default", "conan", "git", "pdm"] files = [ - {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, - {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, + {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, + {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 561a478..1e2ab2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,11 +2,9 @@ description = "A Python management solution for C++ dependencies" name = "cppython" -license = {text = "MIT"} +license = { text = "MIT" } -authors = [ - {name = "Synodic Software", email = "contact@synodic.software"}, -] +authors = [{ name = "Synodic Software", email = "contact@synodic.software" }] readme = "README.md" dynamic = ["version"] @@ -14,26 +12,23 @@ dynamic = ["version"] requires-python = ">=3.13" dependencies = [ - "typer>=0.15.2", - "pydantic>=2.11.3", - "packaging>=24.2", - "requests>=2.32.3", - "types-requests>=2.32.0.20250328", + "typer>=0.15.2", + "pydantic>=2.11.3", + "packaging>=24.2", + "requests>=2.32.3", + "types-requests>=2.32.0.20250328", ] [project.optional-dependencies] -pytest = [ - "pytest>=8.3.5", - "pytest-mock>=3.14.0", -] +pytest = ["pytest>=8.3.5", "pytest-mock>=3.14.0"] -git = [ - "dulwich>=0.22.8", -] +git = ["dulwich>=0.22.8"] -pdm = [ - "pdm>=2.23.0", -] +pdm = ["pdm>=2.23.1"] + +cmake = ["cmake>=4.0.0"] + +conan = ["conan>=2.15.0", "libcst>=1.7.0"] [project.urls] homepage = "https://github.com/Synodic-Software/CPPython" @@ -56,24 +51,16 @@ cppython = "cppython.plugins.pdm.plugin:CPPythonPlugin" cppython = "cppython.test.pytest.fixtures" [dependency-groups] -lint = [ - "ruff>=0.11.4", - "mypy>=1.15.0", -] -test = [ - "pytest>=8.3.5", - "pytest-cov>=6.1.1", - "pytest-mock>=3.14.0", -] +lint = ["ruff>=0.11.5", "mypy>=1.15.0"] +test = ["pytest>=8.3.5", "pytest-cov>=6.1.1", "pytest-mock>=3.14.0"] [project.scripts] cppython = "cppython.console.entry:app" [tool.pytest.ini_options] +addopts = ["--color=yes"] log_cli = true -testpaths = [ - "tests", -] +testpaths = ["tests"] [tool.mypy] exclude = "__pypackages__" @@ -85,24 +72,17 @@ line-length = 120 preview = true [tool.ruff.lint] -ignore = [ - "D206", - "D300", - "D415", - "E111", - "E114", - "E117", -] +ignore = ["D206", "D300", "D415", "E111", "E114", "E117"] select = [ - "D", # pydocstyle - "F", # Pyflakes - "I", # isort - "PL", # pylint - "UP", # pyupgrade - "E", # pycodestyle - "B", # flake8-bugbear - "SIM", # flake8-simplify - "PT", # flake8-pytest-style + "D", # pydocstyle + "F", # Pyflakes + "I", # isort + "PL", # pylint + "UP", # pyupgrade + "E", # pycodestyle + "B", # flake8-bugbear + "SIM", # flake8-simplify + "PT", # flake8-pytest-style ] [tool.ruff.lint.pydocstyle] @@ -117,9 +97,7 @@ quote-style = "single" skip_empty = true [tool.pdm] -plugins = [ - "-e file:///${PROJECT_ROOT}", -] +plugins = ["-e file:///${PROJECT_ROOT}"] [tool.pdm.options] update = ["--update-all", "--unconstrained"] @@ -128,11 +106,11 @@ update = ["--update-all", "--unconstrained"] source = "scm" [tool.pdm.scripts] -analyze = {shell = "ruff check cppython tests"} -format = {shell = "ruff format"} -lint = {composite = ["analyze", "format", "type-check"]} -test = {shell = "pytest --cov=cppython --verbose tests"} -type-check = {shell = "mypy ."} +analyze = { shell = "ruff check cppython tests" } +format = { shell = "ruff format" } +lint = { composite = ["analyze", "format", "type-check"] } +test = { shell = "pytest --cov=cppython --verbose tests" } +type-check = { shell = "mypy ." } [build-system] build-backend = "pdm.backend" diff --git a/tests/integration/examples/test_conan_cmake.py b/tests/integration/examples/test_conan_cmake.py new file mode 100644 index 0000000..13626f5 --- /dev/null +++ b/tests/integration/examples/test_conan_cmake.py @@ -0,0 +1,58 @@ +"""Integration tests for the conan and CMake project variation. + +This module contains integration tests for projects that use conan and CMake. +The tests ensure that the projects build, configure, and execute correctly. +""" + +import subprocess +from pathlib import Path + +from typer.testing import CliRunner + +from cppython.console.entry import app + +pytest_plugins = ['tests.fixtures.example'] + + +class TestConanCMake: + """Test project variation of conan and CMake""" + + @staticmethod + def test_simple(example_runner: CliRunner) -> None: + """Simple project""" + result = example_runner.invoke( + app, + [ + 'install', + ], + ) + + assert result.exit_code == 0, result.output + + # Run the CMake configuration command + cmake_result = subprocess.run(['cmake', '--preset=default'], capture_output=True, text=True, check=False) + + assert cmake_result.returncode == 0, f'CMake configuration failed: {cmake_result.stderr}' + + # Verify that the build directory contains the expected files + assert (Path('build') / 'CMakeCache.txt').exists(), 'build/CMakeCache.txt not found' + + @staticmethod + def test_inject(example_runner: CliRunner) -> None: + """Inject""" + result = example_runner.invoke( + app, + [ + 'install', + ], + ) + + assert result.exit_code == 0, result.output + + # Run the CMake configuration command + cmake_result = subprocess.run(['cmake', '--preset=default'], capture_output=True, text=True, check=False) + + assert cmake_result.returncode == 0, f'CMake configuration failed: {cmake_result.stderr}' + + # Verify that the build directory contains the expected files + assert (Path('build') / 'CMakeCache.txt').exists(), 'build/CMakeCache.txt not found' diff --git a/tests/integration/examples/test_vcpkg_cmake.py b/tests/integration/examples/test_vcpkg_cmake.py index e1a222c..724844f 100644 --- a/tests/integration/examples/test_vcpkg_cmake.py +++ b/tests/integration/examples/test_vcpkg_cmake.py @@ -1,6 +1,11 @@ -"""TODO""" +"""Integration tests for the vcpkg and CMake project variation. + +This module contains integration tests for projects that use vcpkg and CMake. +The tests ensure that the projects build, configure, and execute correctly. +""" import subprocess +from pathlib import Path import pytest from typer.testing import CliRunner @@ -27,19 +32,11 @@ def test_simple(example_runner: CliRunner) -> None: assert result.exit_code == 0, result.output # Run the CMake configuration command - cmake_result = subprocess.run(['cmake', '--preset=default'], capture_output=True, text=True, check=False) + cmake_result = subprocess.run( + ['cmake', '--preset=default', '-B', 'build'], capture_output=True, text=True, check=False + ) assert cmake_result.returncode == 0, f'CMake configuration failed: {cmake_result.stderr}' - # Run the CMake build command - build_result = subprocess.run(['cmake', '--build', 'build'], capture_output=True, text=True, check=False) - - assert build_result.returncode == 0, f'CMake build failed: {build_result.stderr}' - assert 'Build finished successfully' in build_result.stdout, 'CMake build did not finish successfully' - - # Execute the built program and verify the output - program_result = subprocess.run(['build/HelloWorld'], capture_output=True, text=True, check=False) - - assert program_result.returncode == 0, f'Program execution failed: {program_result.stderr}' - - assert 'Hello, World!' in program_result.stdout, 'Program output did not match expected output' + # Verify that the build directory contains the expected files + assert (Path('build') / 'CMakeCache.txt').exists(), 'build/CMakeCache.txt not found' diff --git a/tests/unit/plugins/cmake/test_generator.py b/tests/unit/plugins/cmake/test_generator.py index 64197fc..aaec88b 100644 --- a/tests/unit/plugins/cmake/test_generator.py +++ b/tests/unit/plugins/cmake/test_generator.py @@ -1,6 +1,5 @@ """Unit test the provider plugin""" -import json from pathlib import Path from typing import Any @@ -105,9 +104,9 @@ def test_root_write(tmp_path: Path) -> None: root_file = tmp_path / 'CMakePresets.json' presets = CMakePresets() - serialized = json.loads(presets.model_dump_json(exclude_none=True, by_alias=False)) + serialized = presets.model_dump_json(exclude_none=True, by_alias=False, indent=4) with open(root_file, 'w', encoding='utf8') as file: - json.dump(serialized, file, ensure_ascii=False, indent=4) + file.write(serialized) data = CMakeSyncData(provider_name=TypeName('test-provider'), top_level_includes=includes_file) builder.write_provider_preset(provider_directory, data) @@ -140,9 +139,9 @@ def test_relative_root_write(tmp_path: Path) -> None: root_file = relative_indirection / 'CMakePresets.json' presets = CMakePresets() - serialized = json.loads(presets.model_dump_json(exclude_none=True, by_alias=False)) + serialized = presets.model_dump_json(exclude_none=True, by_alias=False, indent=4) with open(root_file, 'w', encoding='utf8') as file: - json.dump(serialized, file, ensure_ascii=False, indent=4) + file.write(serialized) data = CMakeSyncData(provider_name=TypeName('test-provider'), top_level_includes=includes_file) builder.write_provider_preset(provider_directory, data)