Skip to content

Commit 83524cc

Browse files
authored
Merge pull request #93 from Point72/tkp/integ
Respect CMake args, enable forcing toolchains on/off
2 parents c518b27 + b7bc651 commit 83524cc

File tree

3 files changed

+144
-5
lines changed

3 files changed

+144
-5
lines changed

hatch_cpp/config.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from os import system as system_call
3+
from os import environ, system as system_call
44
from pathlib import Path
55
from typing import List, Optional
66

@@ -63,12 +63,27 @@ class HatchCppBuildPlan(HatchCppBuildConfig):
6363
def generate(self):
6464
self.commands = []
6565

66+
# Check for env var overrides
67+
vcpkg_override = environ.get("HATCH_CPP_VCPKG")
68+
cmake_override = environ.get("HATCH_CPP_CMAKE")
69+
6670
# Evaluate toolchains
67-
if self.vcpkg and Path(self.vcpkg.vcpkg).exists():
71+
if vcpkg_override == "1":
72+
if self.vcpkg:
73+
self._active_toolchains.append("vcpkg")
74+
else:
75+
log.warning("HATCH_CPP_VCPKG=1 set but no vcpkg configuration found; ignoring.")
76+
elif vcpkg_override != "0" and self.vcpkg and Path(self.vcpkg.vcpkg).exists():
6877
self._active_toolchains.append("vcpkg")
78+
6979
if self.libraries:
7080
self._active_toolchains.append("vanilla")
71-
elif self.cmake:
81+
elif cmake_override == "1":
82+
if self.cmake:
83+
self._active_toolchains.append("cmake")
84+
else:
85+
log.warning("HATCH_CPP_CMAKE=1 set but no cmake configuration found; ignoring.")
86+
elif cmake_override != "0" and self.cmake:
7287
self._active_toolchains.append("cmake")
7388

7489
# Collect toolchain commands

hatch_cpp/tests/test_structs.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
from os import environ
12
from pathlib import Path
23
from sys import version_info
4+
from unittest.mock import patch
35

46
import pytest
57
from pydantic import ValidationError
@@ -54,3 +56,115 @@ def test_platform_toolchain_override(self):
5456
assert "clang" in hatch_build_config.platform.cc
5557
assert "clang++" in hatch_build_config.platform.cxx
5658
assert hatch_build_config.platform.toolchain == "gcc"
59+
60+
def test_cmake_args_env_variable(self):
61+
"""Test that CMAKE_ARGS environment variable is respected."""
62+
txt = (Path(__file__).parent / "test_project_cmake" / "pyproject.toml").read_text()
63+
toml_data = loads(txt)
64+
hatch_build_config = HatchCppBuildConfig(name=toml_data["project"]["name"], **toml_data["tool"]["hatch"]["build"]["hooks"]["hatch-cpp"])
65+
hatch_build_plan = HatchCppBuildPlan(**hatch_build_config.model_dump())
66+
67+
with patch.dict(environ, {"CMAKE_ARGS": "-DFOO=bar -DBAZ=qux"}):
68+
hatch_build_plan.generate()
69+
assert "-DFOO=bar" in hatch_build_plan.commands[0]
70+
assert "-DBAZ=qux" in hatch_build_plan.commands[0]
71+
72+
def test_cmake_args_env_variable_empty(self):
73+
"""Test that an empty CMAKE_ARGS does not add extra whitespace."""
74+
txt = (Path(__file__).parent / "test_project_cmake" / "pyproject.toml").read_text()
75+
toml_data = loads(txt)
76+
hatch_build_config = HatchCppBuildConfig(name=toml_data["project"]["name"], **toml_data["tool"]["hatch"]["build"]["hooks"]["hatch-cpp"])
77+
hatch_build_plan = HatchCppBuildPlan(**hatch_build_config.model_dump())
78+
79+
with patch.dict(environ, {"CMAKE_ARGS": ""}):
80+
hatch_build_plan.generate()
81+
# Should not have trailing whitespace from empty CMAKE_ARGS
82+
assert not hatch_build_plan.commands[0].endswith(" ")
83+
84+
def test_cmake_generator_env_variable(self):
85+
"""Test that CMAKE_GENERATOR environment variable is respected on non-Windows platforms."""
86+
txt = (Path(__file__).parent / "test_project_cmake" / "pyproject.toml").read_text()
87+
toml_data = loads(txt)
88+
hatch_build_config = HatchCppBuildConfig(name=toml_data["project"]["name"], **toml_data["tool"]["hatch"]["build"]["hooks"]["hatch-cpp"])
89+
hatch_build_plan = HatchCppBuildPlan(**hatch_build_config.model_dump())
90+
91+
with patch.dict(environ, {"CMAKE_GENERATOR": "Ninja"}):
92+
hatch_build_plan.generate()
93+
assert '-G "Ninja"' in hatch_build_plan.commands[0]
94+
95+
def test_cmake_generator_env_variable_unset(self):
96+
"""Test that no -G flag is added on non-Windows when CMAKE_GENERATOR is not set."""
97+
txt = (Path(__file__).parent / "test_project_cmake" / "pyproject.toml").read_text()
98+
toml_data = loads(txt)
99+
hatch_build_config = HatchCppBuildConfig(name=toml_data["project"]["name"], **toml_data["tool"]["hatch"]["build"]["hooks"]["hatch-cpp"])
100+
hatch_build_plan = HatchCppBuildPlan(**hatch_build_config.model_dump())
101+
102+
with patch.dict(environ, {}, clear=False):
103+
# Remove CMAKE_GENERATOR if present
104+
environ.pop("CMAKE_GENERATOR", None)
105+
hatch_build_plan.generate()
106+
if hatch_build_plan.platform.platform != "win32":
107+
assert "-G " not in hatch_build_plan.commands[0]
108+
109+
def test_hatch_cpp_cmake_env_force_off(self):
110+
"""Test that HATCH_CPP_CMAKE=0 disables cmake even when cmake config is present."""
111+
txt = (Path(__file__).parent / "test_project_cmake" / "pyproject.toml").read_text()
112+
toml_data = loads(txt)
113+
hatch_build_config = HatchCppBuildConfig(name=toml_data["project"]["name"], **toml_data["tool"]["hatch"]["build"]["hooks"]["hatch-cpp"])
114+
hatch_build_plan = HatchCppBuildPlan(**hatch_build_config.model_dump())
115+
116+
assert hatch_build_plan.cmake is not None
117+
with patch.dict(environ, {"HATCH_CPP_CMAKE": "0"}):
118+
hatch_build_plan.generate()
119+
# cmake should not be active, so no cmake commands generated
120+
assert len(hatch_build_plan.commands) == 0
121+
assert "cmake" not in hatch_build_plan._active_toolchains
122+
123+
def test_hatch_cpp_cmake_env_force_on(self):
124+
"""Test that HATCH_CPP_CMAKE=1 enables cmake when cmake config is present."""
125+
txt = (Path(__file__).parent / "test_project_cmake" / "pyproject.toml").read_text()
126+
toml_data = loads(txt)
127+
hatch_build_config = HatchCppBuildConfig(name=toml_data["project"]["name"], **toml_data["tool"]["hatch"]["build"]["hooks"]["hatch-cpp"])
128+
hatch_build_plan = HatchCppBuildPlan(**hatch_build_config.model_dump())
129+
130+
assert hatch_build_plan.cmake is not None
131+
with patch.dict(environ, {"HATCH_CPP_CMAKE": "1"}):
132+
hatch_build_plan.generate()
133+
assert "cmake" in hatch_build_plan._active_toolchains
134+
135+
def test_hatch_cpp_cmake_env_force_on_no_config(self):
136+
"""Test that HATCH_CPP_CMAKE=1 warns and skips when no cmake config exists."""
137+
txt = (Path(__file__).parent / "test_project_cmake" / "pyproject.toml").read_text()
138+
toml_data = loads(txt)
139+
config_data = toml_data["tool"]["hatch"]["build"]["hooks"]["hatch-cpp"].copy()
140+
config_data.pop("cmake", None)
141+
hatch_build_config = HatchCppBuildConfig(name=toml_data["project"]["name"], **config_data)
142+
hatch_build_plan = HatchCppBuildPlan(**hatch_build_config.model_dump())
143+
144+
assert hatch_build_plan.cmake is None
145+
with patch.dict(environ, {"HATCH_CPP_CMAKE": "1"}):
146+
hatch_build_plan.generate()
147+
# cmake should NOT be activated when there's no config
148+
assert "cmake" not in hatch_build_plan._active_toolchains
149+
150+
def test_hatch_cpp_vcpkg_env_force_off(self):
151+
"""Test that HATCH_CPP_VCPKG=0 disables vcpkg even when vcpkg.json exists."""
152+
txt = (Path(__file__).parent / "test_project_cmake_vcpkg" / "pyproject.toml").read_text()
153+
toml_data = loads(txt)
154+
hatch_build_config = HatchCppBuildConfig(name=toml_data["project"]["name"], **toml_data["tool"]["hatch"]["build"]["hooks"]["hatch-cpp"])
155+
hatch_build_plan = HatchCppBuildPlan(**hatch_build_config.model_dump())
156+
157+
with patch.dict(environ, {"HATCH_CPP_VCPKG": "0"}):
158+
hatch_build_plan.generate()
159+
assert "vcpkg" not in hatch_build_plan._active_toolchains
160+
161+
def test_hatch_cpp_vcpkg_env_force_on(self):
162+
"""Test that HATCH_CPP_VCPKG=1 enables vcpkg even when vcpkg.json doesn't exist."""
163+
txt = (Path(__file__).parent / "test_project_cmake" / "pyproject.toml").read_text()
164+
toml_data = loads(txt)
165+
hatch_build_config = HatchCppBuildConfig(name=toml_data["project"]["name"], **toml_data["tool"]["hatch"]["build"]["hooks"]["hatch-cpp"])
166+
hatch_build_plan = HatchCppBuildPlan(**hatch_build_config.model_dump())
167+
168+
with patch.dict(environ, {"HATCH_CPP_VCPKG": "1"}):
169+
hatch_build_plan.generate()
170+
assert "vcpkg" in hatch_build_plan._active_toolchains

hatch_cpp/toolchains/cmake.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,14 @@ def generate(self, config) -> Dict[str, Any]:
5454
commands[-1] += f" -DCMAKE_INSTALL_PREFIX={Path(self.root).parent}"
5555

5656
# TODO: CMAKE_CXX_COMPILER
57+
# Respect CMAKE_GENERATOR environment variable
58+
cmake_generator = environ.get("CMAKE_GENERATOR", "")
5759
if config.platform.platform == "win32":
58-
# TODO: prefix?
59-
commands[-1] += f' -G "{environ.get("CMAKE_GENERATOR", "Visual Studio 17 2022")}"'
60+
if not cmake_generator:
61+
cmake_generator = "Visual Studio 17 2022"
62+
commands[-1] += f' -G "{cmake_generator}"'
63+
elif cmake_generator:
64+
commands[-1] += f' -G "{cmake_generator}"'
6065

6166
# Put in CMake flags
6267
args = self.cmake_args.copy()
@@ -78,6 +83,11 @@ def generate(self, config) -> Dict[str, Any]:
7883
if config.platform.platform == "darwin":
7984
commands[-1] += f" -DCMAKE_OSX_DEPLOYMENT_TARGET={environ.get('OSX_DEPLOYMENT_TARGET', '11')}"
8085

86+
# Respect CMAKE_ARGS environment variable
87+
cmake_args_env = environ.get("CMAKE_ARGS", "").strip()
88+
if cmake_args_env:
89+
commands[-1] += " " + cmake_args_env
90+
8191
# Append build command
8292
commands.append(f"cmake --build {self.build} --config {config.build_type}")
8393

0 commit comments

Comments
 (0)