diff --git a/README.md b/README.md index 3d69c21..b68da15 100644 --- a/README.md +++ b/README.md @@ -97,17 +97,89 @@ hatch-build \ --libraries.0.include-dirs cpp,another-dir ``` -### Environment Variables +This CLI is aware of your `pyproject.toml`-configured setup. +To display help for this, run (note the passthrough `--`): + +```bash +hatch-build -- --help +``` -`hatch-cpp` will respect standard environment variables for compiler control. +For example, for the `test_project_basic` in this project's `tests` folder: + +```raw +hatch-build --hooks-only -- --help +[sdist] + +[wheel] +[2025-11-11T17:31:06-0500][p2a][WARNING]: Only dicts with str, int, float, bool, or enum values are supported - field `cmake_env_args` got value type typing.Dict[str, str] +usage: hatch-build-extras-model [-h] [--verbose] [--name NAME] [--libraries.0.name LIBRARIES.0.NAME] + [--libraries.0.sources.0 LIBRARIES.0.SOURCES.0] [--libraries.0.sources LIBRARIES.0.SOURCES] + [--libraries.0.language LIBRARIES.0.LANGUAGE] [--libraries.0.binding LIBRARIES.0.BINDING] + [--libraries.0.std LIBRARIES.0.STD] [--libraries.0.include-dirs.0 LIBRARIES.0.INCLUDE_DIRS.0] + [--libraries.0.include-dirs LIBRARIES.0.INCLUDE_DIRS] + [--libraries.0.library-dirs LIBRARIES.0.LIBRARY_DIRS] + [--libraries.0.libraries LIBRARIES.0.LIBRARIES] + [--libraries.0.extra-compile-args LIBRARIES.0.EXTRA_COMPILE_ARGS] + [--libraries.0.extra-link-args LIBRARIES.0.EXTRA_LINK_ARGS] + [--libraries.0.extra-objects LIBRARIES.0.EXTRA_OBJECTS] + [--libraries.0.define-macros LIBRARIES.0.DEFINE_MACROS] + [--libraries.0.undef-macros LIBRARIES.0.UNDEF_MACROS] + [--libraries.0.export-symbols LIBRARIES.0.EXPORT_SYMBOLS] + [--libraries.0.depends LIBRARIES.0.DEPENDS] + [--libraries.0.py-limited-api LIBRARIES.0.PY_LIMITED_API] [--cmake.root CMAKE.ROOT] + [--cmake.build CMAKE.BUILD] [--cmake.install CMAKE.INSTALL] + [--cmake.cmake-arg-prefix CMAKE.CMAKE_ARG_PREFIX] [--cmake.cmake-args CMAKE.CMAKE_ARGS] + [--cmake.include-flags CMAKE.INCLUDE_FLAGS] [--platform.cc PLATFORM.CC] + [--platform.cxx PLATFORM.CXX] [--platform.ld PLATFORM.LD] [--platform.platform PLATFORM.PLATFORM] + [--platform.toolchain PLATFORM.TOOLCHAIN] [--platform.disable-ccache] [--vcpkg.vcpkg VCPKG.VCPKG] + [--vcpkg.vcpkg-root VCPKG.VCPKG_ROOT] [--vcpkg.vcpkg-repo VCPKG.VCPKG_REPO] + [--vcpkg.vcpkg-triplet VCPKG.VCPKG_TRIPLET] [--build-type BUILD_TYPE] [--commands COMMANDS] + +options: + -h, --help show this help message and exit + --verbose + --name NAME + --libraries.0.name LIBRARIES.0.NAME + --libraries.0.sources.0 LIBRARIES.0.SOURCES.0 + --libraries.0.sources LIBRARIES.0.SOURCES + --libraries.0.language LIBRARIES.0.LANGUAGE + --libraries.0.binding LIBRARIES.0.BINDING + --libraries.0.std LIBRARIES.0.STD + --libraries.0.include-dirs.0 LIBRARIES.0.INCLUDE_DIRS.0 + --libraries.0.include-dirs LIBRARIES.0.INCLUDE_DIRS + --libraries.0.library-dirs LIBRARIES.0.LIBRARY_DIRS + --libraries.0.libraries LIBRARIES.0.LIBRARIES + --libraries.0.extra-compile-args LIBRARIES.0.EXTRA_COMPILE_ARGS + --libraries.0.extra-link-args LIBRARIES.0.EXTRA_LINK_ARGS + --libraries.0.extra-objects LIBRARIES.0.EXTRA_OBJECTS + --libraries.0.define-macros LIBRARIES.0.DEFINE_MACROS + --libraries.0.undef-macros LIBRARIES.0.UNDEF_MACROS + --libraries.0.export-symbols LIBRARIES.0.EXPORT_SYMBOLS + --libraries.0.depends LIBRARIES.0.DEPENDS + --libraries.0.py-limited-api LIBRARIES.0.PY_LIMITED_API + --cmake.root CMAKE.ROOT + --cmake.build CMAKE.BUILD + --cmake.install CMAKE.INSTALL + --cmake.cmake-arg-prefix CMAKE.CMAKE_ARG_PREFIX + --cmake.cmake-args CMAKE.CMAKE_ARGS + --cmake.include-flags CMAKE.INCLUDE_FLAGS + --platform.cc PLATFORM.CC + --platform.cxx PLATFORM.CXX + --platform.ld PLATFORM.LD + --platform.platform PLATFORM.PLATFORM + --platform.toolchain PLATFORM.TOOLCHAIN + --platform.disable-ccache + --vcpkg.vcpkg VCPKG.VCPKG + --vcpkg.vcpkg-root VCPKG.VCPKG_ROOT + --vcpkg.vcpkg-repo VCPKG.VCPKG_REPO + --vcpkg.vcpkg-triplet VCPKG.VCPKG_TRIPLET + --build-type BUILD_TYPE + --commands COMMANDS +``` + +### Environment Variables -| Name | Default | Description | -| :------------------------- | :------ | :-------------------- | -| `CC` | | C Compiler override | -| `CXX` | | C++ Compiler override | -| `LD` | | Linker override | -| `HATCH_CPP_PLATFORM` | | Platform to build | -| `HATCH_CPP_DISABLE_CCACHE` | | Disable CCache usage | +`hatch-cpp` will respect standard environment variables for compiler control, e.g. `CC`, `CXX`, `LD`, `CMAKE_GENERATOR`, `OSX_DEPLOYMENT_TARGET`, etc. > [!NOTE] > This library was generated using [copier](https://copier.readthedocs.io/en/stable/) from the [Base Python Project Template repository](https://github.com/python-project-templates/base). diff --git a/hatch_cpp/config.py b/hatch_cpp/config.py index 3d7ffe7..9e9c4d8 100644 --- a/hatch_cpp/config.py +++ b/hatch_cpp/config.py @@ -1,10 +1,10 @@ from __future__ import annotations -from logging import getLogger from os import system as system_call from pathlib import Path from typing import List, Optional +from pkn import getSimpleLogger from pydantic import BaseModel, Field, model_validator from .toolchains import BuildType, HatchCppCmakeConfiguration, HatchCppLibrary, HatchCppPlatform, HatchCppVcpkgConfiguration, Toolchain @@ -15,13 +15,14 @@ ) -_log = getLogger(__name__) +log = getSimpleLogger("hatch_cpp") class HatchCppBuildConfig(BaseModel): """Build config values for Hatch C++ Builder.""" verbose: Optional[bool] = Field(default=False) + skip: Optional[bool] = Field(default=False) name: Optional[str] = Field(default=None) libraries: List[HatchCppLibrary] = Field(default_factory=list) cmake: Optional[HatchCppCmakeConfiguration] = Field(default=None) @@ -76,7 +77,7 @@ def generate(self): if "vanilla" in self._active_toolchains: if "vcpkg" in self._active_toolchains: - _log.warning("vcpkg toolchain is active; ensure that your compiler is configured to use vcpkg includes and libs.") + log.warning("vcpkg toolchain is active; ensure that your compiler is configured to use vcpkg includes and libs.") for library in self.libraries: compile_flags = self.platform.get_compile_flags(library, self.build_type) diff --git a/hatch_cpp/plugin.py b/hatch_cpp/plugin.py index 3dfaec2..76914ac 100644 --- a/hatch_cpp/plugin.py +++ b/hatch_cpp/plugin.py @@ -1,7 +1,5 @@ from __future__ import annotations -from logging import getLogger -from os import getenv from pathlib import Path from platform import machine as platform_machine from sys import platform as sys_platform, version_info @@ -10,7 +8,7 @@ from hatch_build import parse_extra_args_model from hatchling.builders.hooks.plugin.interface import BuildHookInterface -from .config import HatchCppBuildConfig, HatchCppBuildPlan +from .config import HatchCppBuildConfig, HatchCppBuildPlan, log from .utils import import_string __all__ = ("HatchCppBuildHook",) @@ -20,7 +18,7 @@ class HatchCppBuildHook(BuildHookInterface[HatchCppBuildConfig]): """The hatch-cpp build hook.""" PLUGIN_NAME = "hatch-cpp" - _logger = getLogger(__name__) + _logger = log def initialize(self, version: str, build_data: dict[str, Any]) -> None: """Initialize the plugin.""" @@ -35,12 +33,6 @@ def initialize(self, version: str, build_data: dict[str, Any]) -> None: self._logger.info("ignoring target name %s", self.target_name) return - # Skip if SKIP_HATCH_CPP is set - # TODO: Support CLI once https://github.com/pypa/hatch/pull/1743 - if getenv("SKIP_HATCH_CPP"): - self._logger.info("Skipping the build hook since SKIP_HATCH_CPP was set") - return - # Get build config class or use default build_config_class = import_string(self.config["build-config-class"]) if "build-config-class" in self.config else HatchCppBuildConfig @@ -60,10 +52,14 @@ def initialize(self, version: str, build_data: dict[str, Any]) -> None: build_plan.generate() # Log commands if in verbose mode - if config.verbose: + if build_plan.verbose: for command in build_plan.commands: self._logger.warning(command) + if build_plan.skip: + self._logger.warning("Skipping build") + return + # Execute build plan build_plan.execute() @@ -114,4 +110,4 @@ def initialize(self, version: str, build_data: dict[str, Any]) -> None: build_data["force_include"][str(path)] = str(path) for path in build_data["force_include"]: - self._logger.warning(f"Force include: {path}") + self._logger.info(f"Force include: {path}") diff --git a/hatch_cpp/toolchains/common.py b/hatch_cpp/toolchains/common.py index 5922d64..57d65e6 100644 --- a/hatch_cpp/toolchains/common.py +++ b/hatch_cpp/toolchains/common.py @@ -96,13 +96,13 @@ class HatchCppPlatform(BaseModel): ld: str platform: Platform toolchain: CompilerToolchain + disable_ccache: bool = False @staticmethod def default() -> HatchCppPlatform: - platform = environ.get("HATCH_CPP_PLATFORM", sys_platform) - CC = environ.get("CC", PlatformDefaults[platform]["CC"]) - CXX = environ.get("CXX", PlatformDefaults[platform]["CXX"]) - LD = environ.get("LD", PlatformDefaults[platform]["LD"]) + CC = environ.get("CC", PlatformDefaults[sys_platform]["CC"]) + CXX = environ.get("CXX", PlatformDefaults[sys_platform]["CXX"]) + LD = environ.get("LD", PlatformDefaults[sys_platform]["LD"]) if "gcc" in CC and "g++" in CXX: toolchain = "gcc" elif "clang" in CC and "clang++" in CXX: @@ -110,26 +110,34 @@ def default() -> HatchCppPlatform: elif "cl" in CC and "cl" in CXX: toolchain = "msvc" # Fallback to platform defaults - elif platform == "linux": + elif sys_platform == "linux": toolchain = "gcc" - elif platform == "darwin": + elif sys_platform == "darwin": toolchain = "clang" - elif platform == "win32": + elif sys_platform == "win32": toolchain = "msvc" else: toolchain = "gcc" - # Customizations - if which("ccache") and not environ.get("HATCH_CPP_DISABLE_CCACHE"): - CC = f"ccache {CC}" - CXX = f"ccache {CXX}" - + # TODO: # https://github.com/rui314/mold/issues/647 # if which("ld.mold"): # LD = which("ld.mold") # elif which("ld.lld"): # LD = which("ld.lld") - return HatchCppPlatform(cc=CC, cxx=CXX, ld=LD, platform=platform, toolchain=toolchain) + return HatchCppPlatform(cc=CC, cxx=CXX, ld=LD, platform=sys_platform, toolchain=toolchain) + + @model_validator(mode="wrap") + @classmethod + def validate_model(cls, data, handler): + model = handler(data) + if which("ccache") and not model.disable_ccache: + if model.toolchain in ["gcc", "clang"]: + if not model.cc.startswith("ccache "): + model.cc = f"ccache {model.cc}" + if not model.cxx.startswith("ccache "): + model.cxx = f"ccache {model.cxx}" + return model @staticmethod def platform_for_toolchain(toolchain: CompilerToolchain) -> HatchCppPlatform: