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
15 changes: 15 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,15 @@ jobs:
runs-on: ${{ matrix.os }}

strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04]
python-version: ["3.11", "3.12", "3.13"]
include:
- os: macos-26
python-version: "3.11"
- os: windows-2025
python-version: "3.11"

steps:
- name: Checkout
Expand All @@ -43,14 +49,22 @@ jobs:
with:
version: ${{ matrix.python-version }}

- name: Setup C++
uses: actions-ext/cpp/setup@7de57593fbcba72d513072bfb4b30b5a344aa547
with:
cache: 'false'
clang_format: 'false'

- name: Install dependencies
run: make develop

- name: Lint
run: make lint
if: runner.os == 'Linux'

- name: Checks
run: make checks
if: runner.os == 'Linux'

- name: Build
run: make build
Expand All @@ -69,6 +83,7 @@ jobs:
uses: EnricoMi/publish-unit-test-result-action@7622c5bf3d112a72ab4fa3951c2aa5723d8ab92d # v2.25.0
with:
files: '**/junit.xml'
if: runner.os == 'Linux'

- name: Upload coverage
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
Expand Down
4 changes: 2 additions & 2 deletions hatch_cpp/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@ def generate(self):
if self.platform.platform == "emscripten":
objects = []
for source_index, source in enumerate(library.sources):
obj = Path("build/hatch-cpp") / f"{library_index}-{source_index}-{Path(source).stem}.o"
objects.append(str(obj))
obj = f"build/hatch-cpp/{library_index}-{source_index}-{Path(source).stem}.o"
objects.append(obj)
self.commands.append(f"{compiler} -c {source} {compile_flags} -o {obj}")
self.commands.append(f"{compiler} {' '.join(objects)} {link_flags}")
else:
Expand Down
7 changes: 6 additions & 1 deletion hatch_cpp/tests/test_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ class TestProject:
[
"test_project_basic",
"test_project_override_classes",
"test_project_override_toolchain",
pytest.param(
"test_project_override_toolchain",
marks=pytest.mark.skipif(
platform == "win32", reason="the fixture selects the gcc toolchain, whose flags clang rejects for MSVC targets"
),
),
"test_project_pybind",
"test_project_pybind_vcpkg",
"test_project_nanobind",
Expand Down
10 changes: 10 additions & 0 deletions hatch_cpp/tests/test_vcpkg_ref.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ def _make_vcpkg_env(self, tmp_path):
"""Create a minimal vcpkg.json so generate() produces commands."""
(tmp_path / "vcpkg.json").write_text("{}")

def _pin_linux_host(self, monkeypatch):
"""Make generate() behave as on an x86_64 Linux host, whatever host runs the test."""
monkeypatch.setattr("hatch_cpp.toolchains.vcpkg.sys_platform", "linux")
monkeypatch.setattr("hatch_cpp.toolchains.vcpkg.platform_machine", lambda: "x86_64")

def test_generate_with_explicit_ref(self, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
self._make_vcpkg_env(tmp_path)
Expand Down Expand Up @@ -164,6 +169,7 @@ def test_generate_without_ref(self, tmp_path, monkeypatch):

def test_generate_uses_posix_command_paths(self, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
self._pin_linux_host(monkeypatch)
self._make_vcpkg_env(tmp_path)

cfg = HatchCppVcpkgConfiguration(vcpkg_triplet="x64-linux")
Expand All @@ -185,6 +191,7 @@ def test_generate_detects_linux_arm64_triplet(self, tmp_path, monkeypatch):

def test_generate_detects_emscripten_triplet(self, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
self._pin_linux_host(monkeypatch)
self._make_vcpkg_env(tmp_path)
build_plan = SimpleNamespace(platform=SimpleNamespace(platform="emscripten"))

Expand All @@ -206,6 +213,7 @@ def test_generate_uses_windows_command_paths(self, tmp_path, monkeypatch):

def test_generate_skips_clone_when_vcpkg_root_exists(self, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
self._pin_linux_host(monkeypatch)
self._make_vcpkg_env(tmp_path)
vcpkg_root = tmp_path / "vcpkg"
vcpkg_root.mkdir()
Expand All @@ -225,6 +233,7 @@ def test_generate_skips_clone_when_vcpkg_root_exists(self, tmp_path, monkeypatch

def test_generate_reclones_when_vcpkg_root_exists_but_empty(self, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
self._pin_linux_host(monkeypatch)
self._make_vcpkg_env(tmp_path)
(tmp_path / "vcpkg").mkdir()

Expand Down Expand Up @@ -253,6 +262,7 @@ def test_generate_bootstraps_when_vcpkg_executable_missing(self, tmp_path, monke

def test_generate_reclones_when_vcpkg_exists_but_not_working(self, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
self._pin_linux_host(monkeypatch)
self._make_vcpkg_env(tmp_path)
vcpkg_root = tmp_path / "vcpkg"
vcpkg_root.mkdir()
Expand Down
9 changes: 3 additions & 6 deletions hatch_cpp/toolchains/cmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,10 @@ def generate(self, config) -> list[str]:
commands[-1] += f" -DCMAKE_INSTALL_PREFIX={Path(self.root).parent}"

# TODO: CMAKE_CXX_COMPILER
# Respect CMAKE_GENERATOR environment variable
# Respect CMAKE_GENERATOR environment variable. Without it CMake picks its default,
# which on Windows is the newest installed Visual Studio.
cmake_generator = environ.get("CMAKE_GENERATOR", "")
if config.platform.platform == "win32":
if not cmake_generator:
cmake_generator = "Visual Studio 17 2022"
commands[-1] += f' -G "{cmake_generator}"'
elif cmake_generator:
if cmake_generator:
commands[-1] += f' -G "{cmake_generator}"'

# Put in CMake flags
Expand Down
4 changes: 2 additions & 2 deletions hatch_cpp/toolchains/vcpkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def _read_vcpkg_ref_from_gitmodules(vcpkg_root: Path) -> str | None:
parser.read(str(gitmodules_path))

for section in parser.sections():
if parser.get(section, "path", fallback=None) == str(vcpkg_root):
if parser.get(section, "path", fallback=None) == vcpkg_root.as_posix():
return parser.get(section, "branch", fallback=None)

return None
Expand Down Expand Up @@ -93,7 +93,7 @@ def _vcpkg_executable_path(self) -> Path:
def _command_path(self, path: Path) -> str:
if sys_platform == "win32":
return str(path).replace("/", "\\")
return f"./{path}"
return f"./{path.as_posix()}"

def _delete_dir_command(self, path: Path) -> str:
if sys_platform == "win32":
Expand Down
Loading