hatch-cpp 0.4.2__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/.gitignore +2 -0
  2. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/PKG-INFO +17 -1
  3. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/README.md +16 -0
  4. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/__init__.py +1 -1
  5. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/config.py +13 -4
  6. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/plugin.py +27 -19
  7. hatch_cpp-0.5.0/hatch_cpp/tests/test_project_pyodide/cpp/extension.cpp +9 -0
  8. hatch_cpp-0.5.0/hatch_cpp/tests/test_project_pyodide/pyodide_project/__init__.py +3 -0
  9. hatch_cpp-0.5.0/hatch_cpp/tests/test_project_pyodide/pyproject.toml +20 -0
  10. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_structs.py +49 -0
  11. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_vcpkg_ref.py +15 -0
  12. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/toolchains/common.py +20 -12
  13. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/toolchains/vcpkg.py +6 -2
  14. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/pyproject.toml +2 -2
  15. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/LICENSE +0 -0
  16. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/hooks.py +0 -0
  17. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_hatch_build.py +0 -0
  18. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_platform_specific.py +0 -0
  19. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_basic/cpp/project/basic.cpp +0 -0
  20. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_basic/cpp/project/basic.hpp +0 -0
  21. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_basic/project/__init__.py +0 -0
  22. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_basic/pyproject.toml +0 -0
  23. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake/CMakeLists.txt +0 -0
  24. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake/Makefile +0 -0
  25. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake/cpp/project/basic.cpp +0 -0
  26. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake/cpp/project/basic.hpp +0 -0
  27. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake/project/__init__.py +0 -0
  28. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake/project/include/project/basic.hpp +0 -0
  29. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake/pyproject.toml +0 -0
  30. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake_vcpkg/CMakeLists.txt +0 -0
  31. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake_vcpkg/Makefile +0 -0
  32. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake_vcpkg/cpp/project/basic.cpp +0 -0
  33. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake_vcpkg/cpp/project/basic.hpp +0 -0
  34. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake_vcpkg/project/__init__.py +0 -0
  35. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake_vcpkg/project/include/project/basic.hpp +0 -0
  36. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake_vcpkg/pyproject.toml +0 -0
  37. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_cmake_vcpkg/vcpkg.json +0 -0
  38. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_hatch_build/cpp/project/basic.cpp +0 -0
  39. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_hatch_build/cpp/project/basic.hpp +0 -0
  40. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_hatch_build/project/__init__.py +0 -0
  41. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_hatch_build/pyproject.toml +0 -0
  42. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_limited_api/cpp/project/basic.cpp +0 -0
  43. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_limited_api/cpp/project/basic.hpp +0 -0
  44. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_limited_api/project/__init__.py +0 -0
  45. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_limited_api/pyproject.toml +0 -0
  46. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_nanobind/cpp/project/basic.cpp +0 -0
  47. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_nanobind/cpp/project/basic.hpp +0 -0
  48. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_nanobind/project/__init__.py +0 -0
  49. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_nanobind/pyproject.toml +0 -0
  50. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_override_classes/cpp/project/basic.cpp +0 -0
  51. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_override_classes/cpp/project/basic.hpp +0 -0
  52. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_override_classes/project/__init__.py +0 -0
  53. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_override_classes/pyproject.toml +0 -0
  54. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_override_toolchain/cpp/project/basic.cpp +0 -0
  55. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_override_toolchain/cpp/project/basic.hpp +0 -0
  56. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_override_toolchain/project/__init__.py +0 -0
  57. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_override_toolchain/pyproject.toml +0 -0
  58. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_pybind/cpp/project/basic.cpp +0 -0
  59. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_pybind/cpp/project/basic.hpp +0 -0
  60. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_pybind/project/__init__.py +0 -0
  61. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_pybind/pyproject.toml +0 -0
  62. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_pybind_vcpkg/cpp/project/basic.cpp +0 -0
  63. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_pybind_vcpkg/cpp/project/basic.hpp +0 -0
  64. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_pybind_vcpkg/project/__init__.py +0 -0
  65. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_pybind_vcpkg/pyproject.toml +0 -0
  66. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_project_pybind_vcpkg/vcpkg.json +0 -0
  67. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/tests/test_projects.py +0 -0
  68. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/toolchains/__init__.py +0 -0
  69. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/toolchains/cmake.py +0 -0
  70. {hatch_cpp-0.4.2 → hatch_cpp-0.5.0}/hatch_cpp/utils.py +0 -0
@@ -142,6 +142,8 @@ hatch_cpp/labextension
142
142
 
143
143
  # Emscripten SDK (locally installed)
144
144
  emsdk
145
+ .pyodide_build
146
+ .venv-pyodide
145
147
 
146
148
  # Mac
147
149
  .DS_Store
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hatch-cpp
3
- Version: 0.4.2
3
+ Version: 0.5.0
4
4
  Summary: Hatch plugin for C++ builds
5
5
  Project-URL: Repository, https://github.com/python-project-templates/hatch-cpp
6
6
  Project-URL: Homepage, https://github.com/python-project-templates/hatch-cpp
@@ -227,5 +227,21 @@ options:
227
227
 
228
228
  `hatch-cpp` will respect standard environment variables for compiler control, e.g. `CC`, `CXX`, `LD`, `CMAKE_GENERATOR`, `OSX_DEPLOYMENT_TARGET`, etc.
229
229
 
230
+ ### Pyodide
231
+
232
+ Pyodide builds are detected from `PYODIDE_ABI_VERSION`. The hook preserves Pyodide's Emscripten compiler wrappers, gives extension modules CPython's Emscripten suffix, and emits the corresponding `pyemscripten` wheel platform tag. No project-specific build hook is required.
233
+
234
+ ```toml
235
+ [tool.hatch.build.hooks.hatch-cpp]
236
+ libraries = [
237
+ {name = "project/extension", sources = ["cpp/extension.cpp"], binding = "pybind11"},
238
+ ]
239
+
240
+ [tool.cibuildwheel.pyodide]
241
+ test-command = "python -c 'from project.extension import answer; assert answer() == 42'"
242
+ ```
243
+
244
+ When a `vcpkg.json` manifest is active, Emscripten builds use vcpkg's `wasm32-emscripten` community triplet. Package support varies by port.
245
+
230
246
  > [!NOTE]
231
247
  > 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).
@@ -181,5 +181,21 @@ options:
181
181
 
182
182
  `hatch-cpp` will respect standard environment variables for compiler control, e.g. `CC`, `CXX`, `LD`, `CMAKE_GENERATOR`, `OSX_DEPLOYMENT_TARGET`, etc.
183
183
 
184
+ ### Pyodide
185
+
186
+ Pyodide builds are detected from `PYODIDE_ABI_VERSION`. The hook preserves Pyodide's Emscripten compiler wrappers, gives extension modules CPython's Emscripten suffix, and emits the corresponding `pyemscripten` wheel platform tag. No project-specific build hook is required.
187
+
188
+ ```toml
189
+ [tool.hatch.build.hooks.hatch-cpp]
190
+ libraries = [
191
+ {name = "project/extension", sources = ["cpp/extension.cpp"], binding = "pybind11"},
192
+ ]
193
+
194
+ [tool.cibuildwheel.pyodide]
195
+ test-command = "python -c 'from project.extension import answer; assert answer() == 42'"
196
+ ```
197
+
198
+ When a `vcpkg.json` manifest is active, Emscripten builds use vcpkg's `wasm32-emscripten` community triplet. Package support varies by port.
199
+
184
200
  > [!NOTE]
185
201
  > 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).
@@ -1,4 +1,4 @@
1
- __version__ = "0.4.2"
1
+ __version__ = "0.5.0"
2
2
 
3
3
  from .config import *
4
4
  from .hooks import *
@@ -93,12 +93,19 @@ class HatchCppBuildPlan(HatchCppBuildConfig):
93
93
  if "vcpkg" in self._active_toolchains:
94
94
  log.warning("vcpkg toolchain is active; ensure that your compiler is configured to use vcpkg includes and libs.")
95
95
 
96
- for library in self.libraries:
96
+ for library_index, library in enumerate(self.libraries):
97
97
  compile_flags = self.platform.get_compile_flags(library, self.build_type)
98
98
  link_flags = self.platform.get_link_flags(library, self.build_type)
99
- self.commands.append(
100
- f"{self.platform.cc if library.language == 'c' else self.platform.cxx} {' '.join(library.sources)} {compile_flags} {link_flags}"
101
- )
99
+ compiler = self.platform.cc if library.language == "c" else self.platform.cxx
100
+ if self.platform.platform == "emscripten":
101
+ objects = []
102
+ for source_index, source in enumerate(library.sources):
103
+ obj = Path("build/hatch-cpp") / f"{library_index}-{source_index}-{Path(source).stem}.o"
104
+ objects.append(str(obj))
105
+ self.commands.append(f"{compiler} -c {source} {compile_flags} -o {obj}")
106
+ self.commands.append(f"{compiler} {' '.join(objects)} {link_flags}")
107
+ else:
108
+ self.commands.append(f"{compiler} {' '.join(library.sources)} {compile_flags} {link_flags}")
102
109
 
103
110
  if "cmake" in self._active_toolchains:
104
111
  self.commands.extend(self.cmake.generate(self))
@@ -106,6 +113,8 @@ class HatchCppBuildPlan(HatchCppBuildConfig):
106
113
  return self.commands
107
114
 
108
115
  def execute(self):
116
+ if self.platform.platform == "emscripten" and "vanilla" in self._active_toolchains:
117
+ Path("build/hatch-cpp").mkdir(parents=True, exist_ok=True)
109
118
  for command in self.commands:
110
119
  ret = system_call(command)
111
120
  if ret != 0:
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from os import environ
3
4
  from pathlib import Path
4
5
  from platform import machine as platform_machine
5
- from sys import platform as sys_platform, version_info
6
+ from sys import version_info
6
7
  from typing import Any
7
8
 
8
9
  from hatch_build import parse_extra_args_model
@@ -14,6 +15,23 @@ from .utils import import_string
14
15
  __all__ = ("HatchCppBuildHook",)
15
16
 
16
17
 
18
+ def _wheel_tag(platform: str, machine: str, version_major: int, version_minor: int, abi3: bool) -> str:
19
+ if platform == "emscripten":
20
+ abi_version = environ.get("PYODIDE_ABI_VERSION")
21
+ if not abi_version:
22
+ raise ValueError("PYODIDE_ABI_VERSION is required for Emscripten wheel tags.")
23
+ return f"cp{version_major}{version_minor}-cp{version_major}{version_minor}-pyemscripten_{abi_version}_wasm32"
24
+
25
+ if platform == "darwin":
26
+ os_name = "macosx_11_0"
27
+ elif platform == "linux":
28
+ os_name = "linux"
29
+ else:
30
+ os_name = "win"
31
+ abi = "abi3" if abi3 else f"cp{version_major}{version_minor}"
32
+ return f"cp{version_major}{version_minor}-{abi}-{os_name}_{machine}"
33
+
34
+
17
35
  class HatchCppBuildHook(BuildHookInterface[HatchCppBuildConfig]):
18
36
  """The hatch-cpp build hook."""
19
37
 
@@ -76,29 +94,19 @@ class HatchCppBuildHook(BuildHookInterface[HatchCppBuildConfig]):
76
94
  machine = platform_machine()
77
95
  version_major = version_info.major
78
96
  version_minor = version_info.minor
79
- if "darwin" in sys_platform:
80
- os_name = "macosx_11_0"
81
- elif "linux" in sys_platform:
82
- os_name = "linux"
83
- else:
84
- os_name = "win"
85
- if all(lib.py_limited_api for lib in build_plan.libraries):
86
- build_data["tag"] = f"cp{version_major}{version_minor}-abi3-{os_name}_{machine}"
87
- else:
88
- build_data["tag"] = f"cp{version_major}{version_minor}-cp{version_major}{version_minor}-{os_name}_{machine}"
97
+ build_data["tag"] = _wheel_tag(
98
+ build_plan.platform.platform,
99
+ machine,
100
+ version_major,
101
+ version_minor,
102
+ all(lib.py_limited_api for lib in build_plan.libraries),
103
+ )
89
104
  else:
90
105
  build_data["pure_python"] = False
91
106
  machine = platform_machine()
92
107
  version_major = version_info.major
93
108
  version_minor = version_info.minor
94
- # TODO abi3
95
- if "darwin" in sys_platform:
96
- os_name = "macosx_11_0"
97
- elif "linux" in sys_platform:
98
- os_name = "linux"
99
- else:
100
- os_name = "win"
101
- build_data["tag"] = f"cp{version_major}{version_minor}-cp{version_major}{version_minor}-{os_name}_{machine}"
109
+ build_data["tag"] = _wheel_tag(build_plan.platform.platform, machine, version_major, version_minor, False)
102
110
 
103
111
  # force include libraries
104
112
  for path in Path(".").rglob("*"):
@@ -0,0 +1,9 @@
1
+ #include <pybind11/pybind11.h>
2
+
3
+ int answer() {
4
+ return 42;
5
+ }
6
+
7
+ PYBIND11_MODULE(extension, module) {
8
+ module.def("answer", &answer);
9
+ }
@@ -0,0 +1,3 @@
1
+ from .extension import answer
2
+
3
+ __all__ = ("answer",)
@@ -0,0 +1,20 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.20"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "hatch-cpp-test-project-pyodide"
7
+ version = "0.1.0"
8
+ requires-python = ">=3.14"
9
+
10
+ [tool.hatch.build.targets.wheel]
11
+ packages = ["pyodide_project"]
12
+
13
+ [tool.hatch.build.hooks.hatch-cpp]
14
+ verbose = true
15
+ libraries = [
16
+ {name = "pyodide_project/extension", sources = ["cpp/extension.cpp"], binding = "pybind11"},
17
+ ]
18
+
19
+ [tool.cibuildwheel.pyodide]
20
+ test-command = "python -c 'from pyodide_project import answer; assert answer() == 42'"
@@ -8,6 +8,7 @@ from pydantic import ValidationError
8
8
  from toml import loads
9
9
 
10
10
  from hatch_cpp import HatchCppBuildConfig, HatchCppBuildPlan, HatchCppLibrary, HatchCppPlatform
11
+ from hatch_cpp.plugin import _wheel_tag
11
12
  from hatch_cpp.toolchains.common import _normalize_rpath
12
13
 
13
14
 
@@ -58,6 +59,54 @@ class TestStructs:
58
59
  assert "clang++" in hatch_build_config.platform.cxx
59
60
  assert hatch_build_config.platform.toolchain == "gcc"
60
61
 
62
+ def test_pyodide_platform(self, monkeypatch: pytest.MonkeyPatch):
63
+ monkeypatch.setenv("PYODIDE_ABI_VERSION", "2026_0")
64
+ monkeypatch.setenv("CC", "/toolchain/cc")
65
+ monkeypatch.setenv("CXX", "/toolchain/c++")
66
+
67
+ platform = HatchCppPlatform.default()
68
+ library = HatchCppLibrary(name="project/extension", sources=["extension.cpp"], binding="pybind11")
69
+
70
+ assert platform.platform == "emscripten"
71
+ assert platform.toolchain == "clang"
72
+ assert platform.cc == "/toolchain/cc"
73
+ assert platform.cxx == "/toolchain/c++"
74
+ assert library.get_qualified_name(platform.platform) == (
75
+ f"project/extension.cpython-{version_info.major}{version_info.minor}-wasm32-emscripten.so"
76
+ )
77
+ assert "-undefined dynamic_lookup" not in platform.get_link_flags(library)
78
+
79
+ def test_pyodide_wheel_tag(self, monkeypatch: pytest.MonkeyPatch):
80
+ monkeypatch.setenv("PYODIDE_ABI_VERSION", "2026_0")
81
+
82
+ assert _wheel_tag("emscripten", "wasm32", 3, 14, False) == "cp314-cp314-pyemscripten_2026_0_wasm32"
83
+
84
+ def test_pyodide_wheel_tag_requires_abi_version(self, monkeypatch: pytest.MonkeyPatch):
85
+ monkeypatch.delenv("PYODIDE_ABI_VERSION", raising=False)
86
+
87
+ with pytest.raises(ValueError, match="PYODIDE_ABI_VERSION"):
88
+ _wheel_tag("emscripten", "wasm32", 3, 14, False)
89
+
90
+ def test_pyodide_build_plan_compiles_objects_before_linking(self, monkeypatch: pytest.MonkeyPatch):
91
+ monkeypatch.setenv("PYODIDE_ABI_VERSION", "2026_0")
92
+ monkeypatch.setenv("CC", "/toolchain/cc")
93
+ monkeypatch.setenv("CXX", "/toolchain/c++")
94
+ build_plan = HatchCppBuildPlan(
95
+ name="pyodide-project",
96
+ libraries=[HatchCppLibrary(name="pyodide_project/extension", sources=["cpp/extension.cpp"], binding="pybind11")],
97
+ vcpkg=None,
98
+ )
99
+
100
+ build_plan.generate()
101
+
102
+ assert len(build_plan.commands) == 2
103
+ assert build_plan.commands[0].startswith("/toolchain/c++ -c cpp/extension.cpp ")
104
+ assert build_plan.commands[0].endswith(" -o build/hatch-cpp/0-0-extension.o")
105
+ assert build_plan.commands[1].startswith("/toolchain/c++ build/hatch-cpp/0-0-extension.o ")
106
+ assert build_plan.commands[1].endswith(
107
+ f" -shared -o pyodide_project/extension.cpython-{version_info.major}{version_info.minor}-wasm32-emscripten.so"
108
+ )
109
+
61
110
  def test_cmake_args_env_variable(self):
62
111
  """Test that CMAKE_ARGS environment variable is respected."""
63
112
  txt = (Path(__file__).parent / "test_project_cmake" / "pyproject.toml").read_text()
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from pathlib import Path
6
+ from types import SimpleNamespace
6
7
 
7
8
  from hatch_cpp.toolchains.vcpkg import (
8
9
  HatchCppVcpkgConfiguration,
@@ -87,6 +88,10 @@ class TestVcpkgRefConfig:
87
88
  cfg = HatchCppVcpkgConfiguration(vcpkg_triplet="arm64-linux")
88
89
  assert cfg.vcpkg_triplet == "arm64-linux"
89
90
 
91
+ def test_emscripten_triplet(self):
92
+ cfg = HatchCppVcpkgConfiguration(vcpkg_triplet="wasm32-emscripten")
93
+ assert cfg.vcpkg_triplet == "wasm32-emscripten"
94
+
90
95
 
91
96
  class TestResolveVcpkgRef:
92
97
  """Tests for _resolve_vcpkg_ref priority logic."""
@@ -178,6 +183,16 @@ class TestVcpkgGenerate:
178
183
 
179
184
  assert "./vcpkg/vcpkg install --triplet arm64-linux" in commands
180
185
 
186
+ def test_generate_detects_emscripten_triplet(self, tmp_path, monkeypatch):
187
+ monkeypatch.chdir(tmp_path)
188
+ self._make_vcpkg_env(tmp_path)
189
+ build_plan = SimpleNamespace(platform=SimpleNamespace(platform="emscripten"))
190
+
191
+ cfg = HatchCppVcpkgConfiguration()
192
+ commands = cfg.generate(build_plan)
193
+
194
+ assert "./vcpkg/vcpkg install --triplet wasm32-emscripten" in commands
195
+
181
196
  def test_generate_uses_windows_command_paths(self, tmp_path, monkeypatch):
182
197
  monkeypatch.chdir(tmp_path)
183
198
  monkeypatch.setattr("hatch_cpp.toolchains.vcpkg.sys_platform", "win32")
@@ -4,7 +4,7 @@ from os import environ
4
4
  from pathlib import Path
5
5
  from re import match
6
6
  from shutil import which
7
- from sys import base_exec_prefix, exec_prefix, executable, platform as sys_platform
7
+ from sys import base_exec_prefix, exec_prefix, executable, platform as sys_platform, version_info
8
8
  from sysconfig import get_config_var, get_path
9
9
  from typing import Any, Literal
10
10
 
@@ -29,11 +29,12 @@ CompilerToolchain = Literal["gcc", "clang", "msvc"]
29
29
  Toolchain = Literal["vcpkg", "cmake", "vanilla"]
30
30
  Language = Literal["c", "c++"]
31
31
  Binding = Literal["cpython", "pybind11", "nanobind", "generic"]
32
- Platform = Literal["linux", "darwin", "win32"]
32
+ Platform = Literal["linux", "darwin", "win32", "emscripten"]
33
33
  PlatformDefaults = {
34
34
  "linux": {"CC": "gcc", "CXX": "g++", "LD": "ld"},
35
35
  "darwin": {"CC": "clang", "CXX": "clang++", "LD": "ld"},
36
36
  "win32": {"CC": "cl", "CXX": "cl", "LD": "link"},
37
+ "emscripten": {"CC": "emcc", "CXX": "em++", "LD": "wasm-ld"},
37
38
  }
38
39
 
39
40
 
@@ -100,6 +101,10 @@ class HatchCppLibrary(BaseModel, validate_assignment=True):
100
101
  return value
101
102
 
102
103
  def get_qualified_name(self, platform):
104
+ if platform == "emscripten":
105
+ if self.binding == "generic":
106
+ return f"{self.name}.wasm"
107
+ return f"{self.name}.cpython-{version_info.major}{version_info.minor}-wasm32-emscripten.so"
103
108
  if self.binding == "cpython" and not self.py_limited_api:
104
109
  suffix = get_config_var("EXT_SUFFIX")
105
110
  return f"{self.name}{suffix}"
@@ -242,9 +247,10 @@ class HatchCppPlatform(BaseModel):
242
247
 
243
248
  @staticmethod
244
249
  def default() -> HatchCppPlatform:
245
- CC = environ.get("CC", PlatformDefaults[sys_platform]["CC"])
246
- CXX = environ.get("CXX", PlatformDefaults[sys_platform]["CXX"])
247
- LD = environ.get("LD", PlatformDefaults[sys_platform]["LD"])
250
+ platform = "emscripten" if environ.get("PYODIDE_ABI_VERSION") else sys_platform
251
+ CC = environ.get("CC", PlatformDefaults[platform]["CC"])
252
+ CXX = environ.get("CXX", PlatformDefaults[platform]["CXX"])
253
+ LD = environ.get("LD", PlatformDefaults[platform]["LD"])
248
254
  if "gcc" in CC and "g++" in CXX:
249
255
  toolchain = "gcc"
250
256
  elif "clang" in CC and "clang++" in CXX:
@@ -252,11 +258,11 @@ class HatchCppPlatform(BaseModel):
252
258
  elif "cl" in CC and "cl" in CXX:
253
259
  toolchain = "msvc"
254
260
  # Fallback to platform defaults
255
- elif sys_platform == "linux":
261
+ elif platform == "linux":
256
262
  toolchain = "gcc"
257
- elif sys_platform == "darwin":
263
+ elif platform in ("darwin", "emscripten"):
258
264
  toolchain = "clang"
259
- elif sys_platform == "win32":
265
+ elif platform == "win32":
260
266
  toolchain = "msvc"
261
267
  else:
262
268
  toolchain = "gcc"
@@ -267,13 +273,13 @@ class HatchCppPlatform(BaseModel):
267
273
  # LD = which("ld.mold")
268
274
  # elif which("ld.lld"):
269
275
  # LD = which("ld.lld")
270
- return HatchCppPlatform(cc=CC, cxx=CXX, ld=LD, platform=sys_platform, toolchain=toolchain)
276
+ return HatchCppPlatform(cc=CC, cxx=CXX, ld=LD, platform=platform, toolchain=toolchain)
271
277
 
272
278
  @model_validator(mode="wrap")
273
279
  @classmethod
274
280
  def validate_model(cls, data, handler):
275
281
  model = handler(data)
276
- if which("ccache") and not model.disable_ccache and model.toolchain in ["gcc", "clang"]:
282
+ if which("ccache") and model.platform != "emscripten" and not model.disable_ccache and model.toolchain in ["gcc", "clang"]:
277
283
  if not model.cc.startswith("ccache "):
278
284
  model.cc = f"ccache {model.cc}"
279
285
  if not model.cxx.startswith("ccache "):
@@ -324,7 +330,8 @@ class HatchCppPlatform(BaseModel):
324
330
  # Toolchain-specific flags
325
331
  if self.toolchain == "gcc":
326
332
  flags += " " + " ".join(f"-I{d}" for d in effective_include_dirs)
327
- flags += " -fPIC"
333
+ if self.platform != "emscripten":
334
+ flags += " -fPIC"
328
335
  flags += " " + " ".join(effective_compile_args)
329
336
  flags += " " + " ".join(f"-D{macro}" for macro in effective_define_macros)
330
337
  flags += " " + " ".join(f"-U{macro}" for macro in effective_undef_macros)
@@ -332,7 +339,8 @@ class HatchCppPlatform(BaseModel):
332
339
  flags += f" -std={library.std}"
333
340
  elif self.toolchain == "clang":
334
341
  flags += " ".join(f"-I{d}" for d in effective_include_dirs)
335
- flags += " -fPIC"
342
+ if self.platform != "emscripten":
343
+ flags += " -fPIC"
336
344
  flags += " " + " ".join(effective_compile_args)
337
345
  flags += " " + " ".join(f"-D{macro}" for macro in effective_define_macros)
338
346
  flags += " " + " ".join(f"-U{macro}" for macro in effective_undef_macros)
@@ -29,6 +29,7 @@ VcpkgTriplet = Literal[
29
29
  "arm64-uwp",
30
30
  "arm64-windows",
31
31
  "arm64-windows-static-md",
32
+ "wasm32-emscripten",
32
33
  ]
33
34
  VcpkgPlatformDefaults = {
34
35
  ("linux", "x86_64"): "x64-linux",
@@ -38,6 +39,7 @@ VcpkgPlatformDefaults = {
38
39
  ("win32", "x86_64"): "x64-windows-static-md",
39
40
  ("win32", "AMD64"): "x64-windows-static-md",
40
41
  ("win32", "arm64"): "arm64-windows-static-md",
42
+ ("emscripten", "wasm32"): "wasm32-emscripten",
41
43
  }
42
44
 
43
45
 
@@ -122,9 +124,11 @@ class HatchCppVcpkgConfiguration(BaseModel):
122
124
  commands = []
123
125
 
124
126
  if self.vcpkg_triplet is None:
125
- self.vcpkg_triplet = VcpkgPlatformDefaults.get((sys_platform, platform_machine()))
127
+ platform = config.platform.platform if config is not None else sys_platform
128
+ machine = "wasm32" if platform == "emscripten" else platform_machine()
129
+ self.vcpkg_triplet = VcpkgPlatformDefaults.get((platform, machine))
126
130
  if self.vcpkg_triplet is None:
127
- raise ValueError(f"Could not determine vcpkg triplet for platform {sys_platform} and architecture {platform_machine()}")
131
+ raise ValueError(f"Could not determine vcpkg triplet for platform {platform} and architecture {machine}")
128
132
 
129
133
  if self.vcpkg and Path(self.vcpkg).exists():
130
134
  vcpkg_root = Path(self.vcpkg_root)
@@ -12,7 +12,7 @@ authors = [
12
12
  description = "Hatch plugin for C++ builds"
13
13
  readme = "README.md"
14
14
  license = { text = "Apache-2.0" }
15
- version = "0.4.2"
15
+ version = "0.5.0"
16
16
  requires-python = ">=3.10"
17
17
  keywords = [
18
18
  "hatch",
@@ -79,7 +79,7 @@ Repository = "https://github.com/python-project-templates/hatch-cpp"
79
79
  Homepage = "https://github.com/python-project-templates/hatch-cpp"
80
80
 
81
81
  [tool.bumpversion]
82
- current_version = "0.4.2"
82
+ current_version = "0.5.0"
83
83
  commit = true
84
84
  tag = true
85
85
  commit_args = "-s"
File without changes
File without changes
File without changes