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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 81 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
7 changes: 4 additions & 3 deletions hatch_cpp/config.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
20 changes: 8 additions & 12 deletions hatch_cpp/plugin.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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",)
Expand All @@ -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."""
Expand All @@ -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

Expand All @@ -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()

Expand Down Expand Up @@ -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}")
34 changes: 21 additions & 13 deletions hatch_cpp/toolchains/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,40 +96,48 @@ 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:
toolchain = "clang"
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:
Expand Down