hatch-cpp 0.0.0__tar.gz → 0.1.6__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 (43) hide show
  1. hatch_cpp-0.1.6/.gitignore +169 -0
  2. {hatch_cpp-0.0.0 → hatch_cpp-0.1.6}/LICENSE +1 -1
  3. hatch_cpp-0.1.6/PKG-INFO +71 -0
  4. hatch_cpp-0.1.6/README.md +35 -0
  5. hatch_cpp-0.1.6/hatch_cpp/__init__.py +5 -0
  6. hatch_cpp-0.1.6/hatch_cpp/hooks.py +10 -0
  7. hatch_cpp-0.1.6/hatch_cpp/plugin.py +91 -0
  8. hatch_cpp-0.1.6/hatch_cpp/structs.py +252 -0
  9. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_basic/cpp/project/basic.cpp +5 -0
  10. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_basic/cpp/project/basic.hpp +17 -0
  11. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_basic/project/__init__.py +0 -0
  12. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_basic/pyproject.toml +35 -0
  13. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_limited_api/cpp/project/basic.cpp +5 -0
  14. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_limited_api/cpp/project/basic.hpp +17 -0
  15. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_limited_api/project/__init__.py +0 -0
  16. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_limited_api/pyproject.toml +35 -0
  17. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_nanobind/cpp/project/basic.cpp +2 -0
  18. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_nanobind/cpp/project/basic.hpp +7 -0
  19. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_nanobind/project/__init__.py +0 -0
  20. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_nanobind/pyproject.toml +35 -0
  21. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_override_classes/cpp/project/basic.cpp +5 -0
  22. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_override_classes/cpp/project/basic.hpp +17 -0
  23. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_override_classes/project/__init__.py +0 -0
  24. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_override_classes/pyproject.toml +37 -0
  25. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_pybind/cpp/project/basic.cpp +6 -0
  26. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_pybind/cpp/project/basic.hpp +9 -0
  27. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_pybind/project/__init__.py +0 -0
  28. hatch_cpp-0.1.6/hatch_cpp/tests/test_project_pybind/pyproject.toml +35 -0
  29. hatch_cpp-0.1.6/hatch_cpp/tests/test_projects.py +46 -0
  30. hatch_cpp-0.1.6/hatch_cpp/tests/test_structs.py +26 -0
  31. hatch_cpp-0.1.6/hatch_cpp/toolchains/__init__.py +0 -0
  32. hatch_cpp-0.1.6/hatch_cpp/toolchains/cmake.py +0 -0
  33. hatch_cpp-0.1.6/hatch_cpp/utils.py +132 -0
  34. hatch_cpp-0.1.6/pyproject.toml +131 -0
  35. hatch_cpp-0.0.0/PKG-INFO +0 -224
  36. hatch_cpp-0.0.0/README.md +0 -1
  37. hatch_cpp-0.0.0/hatch_cpp.egg-info/PKG-INFO +0 -224
  38. hatch_cpp-0.0.0/hatch_cpp.egg-info/SOURCES.txt +0 -8
  39. hatch_cpp-0.0.0/hatch_cpp.egg-info/dependency_links.txt +0 -1
  40. hatch_cpp-0.0.0/hatch_cpp.egg-info/top_level.txt +0 -1
  41. hatch_cpp-0.0.0/pyproject.toml +0 -29
  42. hatch_cpp-0.0.0/setup.cfg +0 -4
  43. hatch_cpp-0.0.0/setup.py +0 -1
@@ -0,0 +1,169 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.a
8
+ *.so
9
+ *.obj
10
+ *.dll
11
+ *.exp
12
+ *.lib
13
+
14
+ # Distribution / packaging
15
+ .Python
16
+ env/
17
+ build/
18
+ develop-eggs/
19
+ dist/
20
+ downloads/
21
+ eggs/
22
+ .eggs/
23
+ lib/
24
+ lib64/
25
+ parts/
26
+ sdist/
27
+ var/
28
+ *.egg-info/
29
+ .installed.cfg
30
+ *.egg
31
+
32
+ # PyInstaller
33
+ # Usually these files are written by a python script from a template
34
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
35
+ *.manifest
36
+ *.spec
37
+
38
+ # Installer logs
39
+ pip-log.txt
40
+ pip-delete-this-directory.txt
41
+
42
+ # Unit test / coverage reports
43
+ htmlcov/
44
+ .tox/
45
+ .coverage
46
+ .coverage.*
47
+ .cache
48
+ python_junit.xml
49
+ junit.xml
50
+ nosetests.xml
51
+ coverage.xml
52
+ *,cover
53
+ .hypothesis/
54
+ .pytest_cache
55
+ .ruff_cache
56
+ js/playwright-report
57
+
58
+ # Translations
59
+ *.mo
60
+ *.pot
61
+
62
+ # Django stuff:
63
+ *.log
64
+ local_settings.py
65
+
66
+ # Flask instance folder
67
+ instance/
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+ docs/source
75
+
76
+ # PyBuilder
77
+ target/
78
+
79
+ # IPython Notebook
80
+ .ipynb_checkpoints
81
+ *.ipynb
82
+ .autoversion
83
+
84
+ # pyenv
85
+ .python-version
86
+
87
+ # celery beat schedule file
88
+ celerybeat-schedule
89
+
90
+ # dotenv
91
+ .env
92
+
93
+ # virtualenv
94
+ venv/
95
+ ENV/
96
+
97
+ # Spyder project settings
98
+ .spyderproject
99
+
100
+ # Rope project settings
101
+ .ropeproject
102
+
103
+ # =========================
104
+ # Operating System Files
105
+ # =========================
106
+
107
+ # OSX
108
+ # =========================
109
+
110
+ .DS_Store
111
+ .AppleDouble
112
+ .LSOverride
113
+
114
+ # Thumbnails
115
+ ._*
116
+
117
+ # Files that might appear in the root of a volume
118
+ .DocumentRevisions-V100
119
+ .fseventsd
120
+ .Spotlight-V100
121
+ .TemporaryItems
122
+ .Trashes
123
+ .VolumeIcon.icns
124
+
125
+ # Directories potentially created on remote AFP share
126
+ .AppleDB
127
+ .AppleDesktop
128
+ Network Trash Folder
129
+ Temporary Items
130
+ .apdisk
131
+
132
+ # Windows
133
+ # =========================
134
+
135
+ # Windows image file caches
136
+ Thumbs.db
137
+ ehthumbs.db
138
+
139
+ # Folder config file
140
+ Desktop.ini
141
+
142
+ # Recycle Bin used on file shares
143
+ $RECYCLE.BIN/
144
+
145
+ # Windows Installer files
146
+ *.cab
147
+ *.msi
148
+ *.msm
149
+ *.msp
150
+
151
+ # Windows shortcuts
152
+ *.lnk
153
+
154
+
155
+ # NPM
156
+ # ----
157
+ **/node_modules/
158
+
159
+ # Coverage data
160
+ # -------------
161
+ **/coverage/
162
+
163
+ # Notebook and lab extensions
164
+
165
+ nbprint/extension/*
166
+ nbprint/templates/nbprint/static/*
167
+ nbprint/voila/static/*
168
+ tmp.html
169
+ examples/output/
@@ -186,7 +186,7 @@
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright 2024, the hatch-cpp authors
189
+ Copyright [yyyy] [name of copyright owner]
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: hatch-cpp
3
+ Version: 0.1.6
4
+ Summary: Hatch plugin for C++ builds
5
+ Project-URL: Repository, https://github.com/python-project-templates/hatch-cpp
6
+ Project-URL: Homepage, https://github.com/python-project-templates/hatch-cpp
7
+ Author-email: the hatch-cpp authors <t.paine154@gmail.com>
8
+ License: Apache-2.0
9
+ License-File: LICENSE
10
+ Keywords: build,c++,cmake,cpp,hatch,python
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: Implementation :: CPython
20
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
21
+ Requires-Python: >=3.9
22
+ Requires-Dist: hatchling>=1.20
23
+ Requires-Dist: pydantic
24
+ Provides-Extra: develop
25
+ Requires-Dist: build; extra == 'develop'
26
+ Requires-Dist: bump-my-version; extra == 'develop'
27
+ Requires-Dist: check-manifest; extra == 'develop'
28
+ Requires-Dist: nanobind; extra == 'develop'
29
+ Requires-Dist: pybind11; extra == 'develop'
30
+ Requires-Dist: pytest; extra == 'develop'
31
+ Requires-Dist: pytest-cov; extra == 'develop'
32
+ Requires-Dist: ruff<0.9,>=0.3; extra == 'develop'
33
+ Requires-Dist: twine; extra == 'develop'
34
+ Requires-Dist: wheel; extra == 'develop'
35
+ Description-Content-Type: text/markdown
36
+
37
+ # hatch-cpp
38
+
39
+ Hatch plugin for C++ builds
40
+
41
+ [![Build Status](https://github.com/python-project-templates/hatch-cpp/actions/workflows/build.yml/badge.svg?branch=main&event=push)](https://github.com/python-project-templates/hatch-cpp/actions/workflows/build.yml)
42
+ [![codecov](https://codecov.io/gh/python-project-templates/hatch-cpp/branch/main/graph/badge.svg)](https://codecov.io/gh/python-project-templates/hatch-cpp)
43
+ [![License](https://img.shields.io/github/license/python-project-templates/hatch-cpp)](https://github.com/python-project-templates/hatch-cpp)
44
+ [![PyPI](https://img.shields.io/pypi/v/hatch-cpp.svg)](https://pypi.python.org/pypi/hatch-cpp)
45
+
46
+ ## Overview
47
+
48
+ A simple, extensible C++ build plugin for [hatch](https://hatch.pypa.io/latest/).
49
+
50
+ ```toml
51
+ [tool.hatch.build.hooks.hatch-cpp]
52
+ libraries = [
53
+ {name = "project/extension", sources = ["cpp/project/basic.cpp"], include-dirs = ["cpp"]}
54
+ ]
55
+ ```
56
+
57
+ For more complete systems, see:
58
+ - [scikit-build-core](https://github.com/scikit-build/scikit-build-core)
59
+ - [setuptools](https://setuptools.pypa.io/en/latest/userguide/ext_modules.html)
60
+
61
+ ## Environment Variables
62
+ | Name | Default | Description |
63
+ |:-----|:--------|:------------|
64
+ |`CC`| | |
65
+ |`CXX`| | |
66
+ |`LD`| | |
67
+ |`HATCH_CPP_PLATFORM`| | |
68
+ |`HATCH_CPP_DISABLE_CCACHE`| | |
69
+
70
+ > [!NOTE]
71
+ > 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).
@@ -0,0 +1,35 @@
1
+ # hatch-cpp
2
+
3
+ Hatch plugin for C++ builds
4
+
5
+ [![Build Status](https://github.com/python-project-templates/hatch-cpp/actions/workflows/build.yml/badge.svg?branch=main&event=push)](https://github.com/python-project-templates/hatch-cpp/actions/workflows/build.yml)
6
+ [![codecov](https://codecov.io/gh/python-project-templates/hatch-cpp/branch/main/graph/badge.svg)](https://codecov.io/gh/python-project-templates/hatch-cpp)
7
+ [![License](https://img.shields.io/github/license/python-project-templates/hatch-cpp)](https://github.com/python-project-templates/hatch-cpp)
8
+ [![PyPI](https://img.shields.io/pypi/v/hatch-cpp.svg)](https://pypi.python.org/pypi/hatch-cpp)
9
+
10
+ ## Overview
11
+
12
+ A simple, extensible C++ build plugin for [hatch](https://hatch.pypa.io/latest/).
13
+
14
+ ```toml
15
+ [tool.hatch.build.hooks.hatch-cpp]
16
+ libraries = [
17
+ {name = "project/extension", sources = ["cpp/project/basic.cpp"], include-dirs = ["cpp"]}
18
+ ]
19
+ ```
20
+
21
+ For more complete systems, see:
22
+ - [scikit-build-core](https://github.com/scikit-build/scikit-build-core)
23
+ - [setuptools](https://setuptools.pypa.io/en/latest/userguide/ext_modules.html)
24
+
25
+ ## Environment Variables
26
+ | Name | Default | Description |
27
+ |:-----|:--------|:------------|
28
+ |`CC`| | |
29
+ |`CXX`| | |
30
+ |`LD`| | |
31
+ |`HATCH_CPP_PLATFORM`| | |
32
+ |`HATCH_CPP_DISABLE_CCACHE`| | |
33
+
34
+ > [!NOTE]
35
+ > 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).
@@ -0,0 +1,5 @@
1
+ __version__ = "0.1.6"
2
+
3
+ from .hooks import hatch_register_build_hook
4
+ from .plugin import HatchCppBuildHook
5
+ from .structs import *
@@ -0,0 +1,10 @@
1
+ from typing import Type
2
+
3
+ from hatchling.plugin import hookimpl
4
+
5
+ from .plugin import HatchCppBuildHook
6
+
7
+
8
+ @hookimpl
9
+ def hatch_register_build_hook() -> Type[HatchCppBuildHook]:
10
+ return HatchCppBuildHook
@@ -0,0 +1,91 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import os
5
+ import platform as sysplatform
6
+ import sys
7
+ import typing as t
8
+
9
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
10
+
11
+ from .structs import HatchCppBuildConfig, HatchCppBuildPlan
12
+ from .utils import import_string
13
+
14
+ __all__ = ("HatchCppBuildHook",)
15
+
16
+
17
+ class HatchCppBuildHook(BuildHookInterface[HatchCppBuildConfig]):
18
+ """The hatch-cpp build hook."""
19
+
20
+ PLUGIN_NAME = "hatch-cpp"
21
+ _logger = logging.getLogger(__name__)
22
+
23
+ def initialize(self, version: str, build_data: dict[str, t.Any]) -> None:
24
+ """Initialize the plugin."""
25
+ # Log some basic information
26
+ self._logger.info("Initializing hatch-cpp plugin version %s", version)
27
+ self._logger.info("Running hatch-cpp")
28
+
29
+ # Only run if creating wheel
30
+ # TODO: Add support for specify sdist-plan
31
+ if self.target_name != "wheel":
32
+ self._logger.info("ignoring target name %s", self.target_name)
33
+ return
34
+
35
+ # Skip if SKIP_HATCH_CPP is set
36
+ # TODO: Support CLI once https://github.com/pypa/hatch/pull/1743
37
+ if os.getenv("SKIP_HATCH_CPP"):
38
+ self._logger.info("Skipping the build hook since SKIP_HATCH_CPP was set")
39
+ return
40
+
41
+ # Get build config class or use default
42
+ build_config_class = import_string(self.config["build-config-class"]) if "build-config-class" in self.config else HatchCppBuildConfig
43
+
44
+ # Instantiate build config
45
+ config = build_config_class(**self.config)
46
+
47
+ # Grab libraries and platform
48
+ libraries = config.libraries
49
+ platform = config.platform
50
+
51
+ # Get build plan class or use default
52
+ build_plan_class = import_string(self.config["build-plan-class"]) if "build-plan-class" in self.config else HatchCppBuildPlan
53
+
54
+ # Instantiate builder
55
+ build_plan = build_plan_class(libraries=libraries, platform=platform)
56
+
57
+ # Generate commands
58
+ build_plan.generate()
59
+
60
+ # Log commands if in verbose mode
61
+ if config.verbose:
62
+ for command in build_plan.commands:
63
+ self._logger.warning(command)
64
+
65
+ # Execute build plan
66
+ build_plan.execute()
67
+
68
+ # Perform any cleanup actions
69
+ build_plan.cleanup()
70
+
71
+ # force include libraries
72
+ for library in libraries:
73
+ name = library.get_qualified_name(build_plan.platform.platform)
74
+ build_data["force_include"][name] = name
75
+
76
+ if libraries:
77
+ build_data["pure_python"] = False
78
+ machine = sysplatform.machine()
79
+ version_major = sys.version_info.major
80
+ version_minor = sys.version_info.minor
81
+ # TODO abi3
82
+ if "darwin" in sys.platform:
83
+ os_name = "macosx_11_0"
84
+ elif "linux" in sys.platform:
85
+ os_name = "linux"
86
+ else:
87
+ os_name = "win"
88
+ if all([lib.py_limited_api for lib in libraries]):
89
+ build_data["tag"] = f"cp{version_major}{version_minor}-abi3-{os_name}_{machine}"
90
+ else:
91
+ build_data["tag"] = f"cp{version_major}{version_minor}-cp{version_major}{version_minor}-{os_name}_{machine}"
@@ -0,0 +1,252 @@
1
+ from __future__ import annotations
2
+
3
+ from os import environ, system
4
+ from pathlib import Path
5
+ from re import match
6
+ from shutil import which
7
+ from sys import executable, platform as sys_platform
8
+ from sysconfig import get_path
9
+ from typing import Any, List, Literal, Optional
10
+
11
+ from pydantic import AliasChoices, BaseModel, Field, field_validator, model_validator
12
+
13
+ __all__ = (
14
+ "HatchCppBuildConfig",
15
+ "HatchCppLibrary",
16
+ "HatchCppPlatform",
17
+ "HatchCppBuildPlan",
18
+ )
19
+
20
+ BuildType = Literal["debug", "release"]
21
+ CompilerToolchain = Literal["gcc", "clang", "msvc"]
22
+ Language = Literal["c", "c++"]
23
+ Binding = Literal["cpython", "pybind11", "nanobind"]
24
+ Platform = Literal["linux", "darwin", "win32"]
25
+ PlatformDefaults = {
26
+ "linux": {"CC": "gcc", "CXX": "g++", "LD": "ld"},
27
+ "darwin": {"CC": "clang", "CXX": "clang++", "LD": "ld"},
28
+ "win32": {"CC": "cl", "CXX": "cl", "LD": "link"},
29
+ }
30
+
31
+
32
+ class HatchCppLibrary(BaseModel, validate_assignment=True):
33
+ """A C++ library."""
34
+
35
+ name: str
36
+ sources: List[str]
37
+ language: Language = "c++"
38
+
39
+ binding: Binding = "cpython"
40
+ std: Optional[str] = None
41
+
42
+ include_dirs: List[str] = Field(default_factory=list, alias=AliasChoices("include_dirs", "include-dirs"))
43
+ library_dirs: List[str] = Field(default_factory=list, alias=AliasChoices("library_dirs", "library-dirs"))
44
+ libraries: List[str] = Field(default_factory=list)
45
+
46
+ extra_compile_args: List[str] = Field(default_factory=list, alias=AliasChoices("extra_compile_args", "extra-compile-args"))
47
+ extra_link_args: List[str] = Field(default_factory=list, alias=AliasChoices("extra_link_args", "extra-link-args"))
48
+ extra_objects: List[str] = Field(default_factory=list, alias=AliasChoices("extra_objects", "extra-objects"))
49
+
50
+ define_macros: List[str] = Field(default_factory=list, alias=AliasChoices("define_macros", "define-macros"))
51
+ undef_macros: List[str] = Field(default_factory=list, alias=AliasChoices("undef_macros", "undef-macros"))
52
+
53
+ export_symbols: List[str] = Field(default_factory=list, alias=AliasChoices("export_symbols", "export-symbols"))
54
+ depends: List[str] = Field(default_factory=list)
55
+
56
+ py_limited_api: Optional[str] = Field(default="", alias=AliasChoices("py_limited_api", "py-limited-api"))
57
+
58
+ @field_validator("py_limited_api", mode="before")
59
+ @classmethod
60
+ def check_py_limited_api(cls, value: Any) -> Any:
61
+ if value:
62
+ if not match(r"cp3\d", value):
63
+ raise ValueError("py-limited-api must be in the form of cp3X")
64
+ return value
65
+
66
+ def get_qualified_name(self, platform):
67
+ if platform == "win32":
68
+ suffix = "dll" if self.binding == "none" else "pyd"
69
+ elif platform == "darwin":
70
+ suffix = "dylib" if self.binding == "none" else "so"
71
+ else:
72
+ suffix = "so"
73
+ if self.py_limited_api and platform != "win32":
74
+ return f"{self.name}.abi3.{suffix}"
75
+ return f"{self.name}.{suffix}"
76
+
77
+ @model_validator(mode="after")
78
+ def check_binding_and_py_limited_api(self):
79
+ if self.binding == "pybind11" and self.py_limited_api:
80
+ raise ValueError("pybind11 does not support Py_LIMITED_API")
81
+ return self
82
+
83
+
84
+ class HatchCppPlatform(BaseModel):
85
+ cc: str
86
+ cxx: str
87
+ ld: str
88
+ platform: Platform
89
+ toolchain: CompilerToolchain
90
+
91
+ @staticmethod
92
+ def default() -> HatchCppPlatform:
93
+ platform = environ.get("HATCH_CPP_PLATFORM", sys_platform)
94
+ CC = environ.get("CC", PlatformDefaults[platform]["CC"])
95
+ CXX = environ.get("CXX", PlatformDefaults[platform]["CXX"])
96
+ LD = environ.get("LD", PlatformDefaults[platform]["LD"])
97
+ if "gcc" in CC and "g++" in CXX:
98
+ toolchain = "gcc"
99
+ elif "clang" in CC and "clang++" in CXX:
100
+ toolchain = "clang"
101
+ elif "cl" in CC and "cl" in CXX:
102
+ toolchain = "msvc"
103
+ else:
104
+ raise Exception(f"Unrecognized toolchain: {CC}, {CXX}")
105
+
106
+ # Customizations
107
+ if which("ccache") and not environ.get("HATCH_CPP_DISABLE_CCACHE"):
108
+ CC = f"ccache {CC}"
109
+ CXX = f"ccache {CXX}"
110
+
111
+ # https://github.com/rui314/mold/issues/647
112
+ # if which("ld.mold"):
113
+ # LD = which("ld.mold")
114
+ # elif which("ld.lld"):
115
+ # LD = which("ld.lld")
116
+ return HatchCppPlatform(cc=CC, cxx=CXX, ld=LD, platform=platform, toolchain=toolchain)
117
+
118
+ def get_compile_flags(self, library: HatchCppLibrary, build_type: BuildType = "release") -> str:
119
+ flags = ""
120
+
121
+ # Python.h
122
+ library.include_dirs.append(get_path("include"))
123
+
124
+ if library.binding == "pybind11":
125
+ import pybind11
126
+
127
+ library.include_dirs.append(pybind11.get_include())
128
+ if not library.std:
129
+ library.std = "c++11"
130
+ elif library.binding == "nanobind":
131
+ import nanobind
132
+
133
+ library.include_dirs.append(nanobind.include_dir())
134
+ if not library.std:
135
+ library.std = "c++17"
136
+ library.sources.append(str(Path(nanobind.include_dir()).parent / "src" / "nb_combined.cpp"))
137
+ library.include_dirs.append(str((Path(nanobind.include_dir()).parent / "ext" / "robin_map" / "include")))
138
+
139
+ if library.py_limited_api:
140
+ if library.binding == "pybind11":
141
+ raise ValueError("pybind11 does not support Py_LIMITED_API")
142
+ library.define_macros.append(f"Py_LIMITED_API=0x0{library.py_limited_api[2]}0{hex(int(library.py_limited_api[3:]))[2:]}00f0")
143
+
144
+ # Toolchain-specific flags
145
+ if self.toolchain == "gcc":
146
+ flags += " " + " ".join(f"-I{d}" for d in library.include_dirs)
147
+ flags += " -fPIC"
148
+ flags += " " + " ".join(library.extra_compile_args)
149
+ flags += " " + " ".join(f"-D{macro}" for macro in library.define_macros)
150
+ flags += " " + " ".join(f"-U{macro}" for macro in library.undef_macros)
151
+ if library.std:
152
+ flags += f" -std={library.std}"
153
+ elif self.toolchain == "clang":
154
+ flags += " ".join(f"-I{d}" for d in library.include_dirs)
155
+ flags += " -fPIC"
156
+ flags += " " + " ".join(library.extra_compile_args)
157
+ flags += " " + " ".join(f"-D{macro}" for macro in library.define_macros)
158
+ flags += " " + " ".join(f"-U{macro}" for macro in library.undef_macros)
159
+ if library.std:
160
+ flags += f" -std={library.std}"
161
+ elif self.toolchain == "msvc":
162
+ flags += " ".join(f"/I{d}" for d in library.include_dirs)
163
+ flags += " " + " ".join(library.extra_compile_args)
164
+ flags += " " + " ".join(library.extra_link_args)
165
+ flags += " " + " ".join(library.extra_objects)
166
+ flags += " " + " ".join(f"/D{macro}" for macro in library.define_macros)
167
+ flags += " " + " ".join(f"/U{macro}" for macro in library.undef_macros)
168
+ flags += " /EHsc /DWIN32"
169
+ if library.std:
170
+ flags += f" /std:{library.std}"
171
+ # clean
172
+ while flags.count(" "):
173
+ flags = flags.replace(" ", " ")
174
+ return flags
175
+
176
+ def get_link_flags(self, library: HatchCppLibrary, build_type: BuildType = "release") -> str:
177
+ flags = ""
178
+ if self.toolchain == "gcc":
179
+ flags += " -shared"
180
+ flags += " " + " ".join(library.extra_link_args)
181
+ flags += " " + " ".join(library.extra_objects)
182
+ flags += " " + " ".join(f"-l{lib}" for lib in library.libraries)
183
+ flags += " " + " ".join(f"-L{lib}" for lib in library.library_dirs)
184
+ flags += f" -o {library.get_qualified_name(self.platform)}"
185
+ if self.platform == "darwin":
186
+ flags += " -undefined dynamic_lookup"
187
+ if "mold" in self.ld:
188
+ flags += f" -fuse-ld={self.ld}"
189
+ elif "lld" in self.ld:
190
+ flags += " -fuse-ld=lld"
191
+ elif self.toolchain == "clang":
192
+ flags += " -shared"
193
+ flags += " " + " ".join(library.extra_link_args)
194
+ flags += " " + " ".join(library.extra_objects)
195
+ flags += " " + " ".join(f"-l{lib}" for lib in library.libraries)
196
+ flags += " " + " ".join(f"-L{lib}" for lib in library.library_dirs)
197
+ flags += f" -o {library.get_qualified_name(self.platform)}"
198
+ if self.platform == "darwin":
199
+ flags += " -undefined dynamic_lookup"
200
+ if "mold" in self.ld:
201
+ flags += f" -fuse-ld={self.ld}"
202
+ elif "lld" in self.ld:
203
+ flags += " -fuse-ld=lld"
204
+ elif self.toolchain == "msvc":
205
+ flags += " " + " ".join(library.extra_link_args)
206
+ flags += " " + " ".join(library.extra_objects)
207
+ flags += " /LD"
208
+ flags += f" /Fe:{library.get_qualified_name(self.platform)}"
209
+ flags += " /link /DLL"
210
+ if (Path(executable).parent / "libs").exists():
211
+ flags += f" /LIBPATH:{str(Path(executable).parent / 'libs')}"
212
+ flags += " " + " ".join(f"{lib}.lib" for lib in library.libraries)
213
+ flags += " " + " ".join(f"/LIBPATH:{lib}" for lib in library.library_dirs)
214
+ # clean
215
+ while flags.count(" "):
216
+ flags = flags.replace(" ", " ")
217
+ return flags
218
+
219
+
220
+ class HatchCppBuildPlan(BaseModel):
221
+ build_type: BuildType = "release"
222
+ libraries: List[HatchCppLibrary] = Field(default_factory=list)
223
+ platform: HatchCppPlatform = Field(default_factory=HatchCppPlatform.default)
224
+ commands: List[str] = Field(default_factory=list)
225
+
226
+ def generate(self):
227
+ self.commands = []
228
+ for library in self.libraries:
229
+ compile_flags = self.platform.get_compile_flags(library, self.build_type)
230
+ link_flags = self.platform.get_link_flags(library, self.build_type)
231
+ self.commands.append(
232
+ f"{self.platform.cc if library.language == 'c' else self.platform.cxx} {' '.join(library.sources)} {compile_flags} {link_flags}"
233
+ )
234
+ return self.commands
235
+
236
+ def execute(self):
237
+ for command in self.commands:
238
+ system(command)
239
+ return self.commands
240
+
241
+ def cleanup(self):
242
+ if self.platform.platform == "win32":
243
+ for temp_obj in Path(".").glob("*.obj"):
244
+ temp_obj.unlink()
245
+
246
+
247
+ class HatchCppBuildConfig(BaseModel):
248
+ """Build config values for Hatch C++ Builder."""
249
+
250
+ verbose: Optional[bool] = Field(default=False)
251
+ libraries: List[HatchCppLibrary] = Field(default_factory=list)
252
+ platform: Optional[HatchCppPlatform] = Field(default_factory=HatchCppPlatform.default)
@@ -0,0 +1,5 @@
1
+ #include "project/basic.hpp"
2
+
3
+ PyObject* hello(PyObject*, PyObject*) {
4
+ return PyUnicode_FromString("A string");
5
+ }
@@ -0,0 +1,17 @@
1
+ #pragma once
2
+ #include "Python.h"
3
+
4
+ PyObject* hello(PyObject*, PyObject*);
5
+
6
+ static PyMethodDef extension_methods[] = {
7
+ {"hello", (PyCFunction)hello, METH_NOARGS},
8
+ {nullptr, nullptr, 0, nullptr}
9
+ };
10
+
11
+ static PyModuleDef extension_module = {
12
+ PyModuleDef_HEAD_INIT, "extension", "extension", -1, extension_methods};
13
+
14
+ PyMODINIT_FUNC PyInit_extension(void) {
15
+ Py_Initialize();
16
+ return PyModule_Create(&extension_module);
17
+ }