diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 227f029..c539e41 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -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 @@ -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 @@ -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 diff --git a/hatch_cpp/config.py b/hatch_cpp/config.py index 3bd7478..b5e276e 100644 --- a/hatch_cpp/config.py +++ b/hatch_cpp/config.py @@ -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: diff --git a/hatch_cpp/tests/test_projects.py b/hatch_cpp/tests/test_projects.py index 27f467a..a94edac 100644 --- a/hatch_cpp/tests/test_projects.py +++ b/hatch_cpp/tests/test_projects.py @@ -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", diff --git a/hatch_cpp/tests/test_vcpkg_ref.py b/hatch_cpp/tests/test_vcpkg_ref.py index 2cfecdc..2c0779b 100644 --- a/hatch_cpp/tests/test_vcpkg_ref.py +++ b/hatch_cpp/tests/test_vcpkg_ref.py @@ -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) @@ -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") @@ -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")) @@ -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() @@ -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() @@ -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() diff --git a/hatch_cpp/toolchains/cmake.py b/hatch_cpp/toolchains/cmake.py index 685a5ef..82c3fe7 100644 --- a/hatch_cpp/toolchains/cmake.py +++ b/hatch_cpp/toolchains/cmake.py @@ -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 diff --git a/hatch_cpp/toolchains/vcpkg.py b/hatch_cpp/toolchains/vcpkg.py index 5856e40..216007e 100644 --- a/hatch_cpp/toolchains/vcpkg.py +++ b/hatch_cpp/toolchains/vcpkg.py @@ -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 @@ -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":