2
2
3
3
from os import environ , system
4
4
from pathlib import Path
5
+ from re import match
5
6
from shutil import which
6
7
from sys import executable , platform as sys_platform
7
8
from sysconfig import get_path
8
- from typing import List , Literal , Optional
9
+ from typing import Any , List , Literal , Optional
9
10
10
- from pydantic import BaseModel , Field
11
+ from pydantic import AliasChoices , BaseModel , Field , field_validator , model_validator
11
12
12
13
__all__ = (
13
14
"HatchCppBuildConfig" ,
28
29
}
29
30
30
31
31
- class HatchCppLibrary (BaseModel ):
32
+ class HatchCppLibrary (BaseModel , validate_assignment = True ):
32
33
"""A C++ library."""
33
34
34
35
name : str
@@ -38,29 +39,47 @@ class HatchCppLibrary(BaseModel):
38
39
binding : Binding = "cpython"
39
40
std : Optional [str ] = None
40
41
41
- include_dirs : List [str ] = Field (default_factory = list , alias = " include-dirs" )
42
- library_dirs : List [str ] = Field (default_factory = list , alias = " library-dirs" )
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") )
43
44
libraries : List [str ] = Field (default_factory = list )
44
45
45
- extra_compile_args : List [str ] = Field (default_factory = list , alias = " extra-compile-args" )
46
- extra_link_args : List [str ] = Field (default_factory = list , alias = " extra-link-args" )
47
- extra_objects : List [str ] = Field (default_factory = list , alias = " extra-objects" )
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") )
48
49
49
- define_macros : List [str ] = Field (default_factory = list , alias = " define-macros" )
50
- undef_macros : List [str ] = Field (default_factory = list , alias = " undef-macros" )
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") )
51
52
52
- export_symbols : List [str ] = Field (default_factory = list , alias = " export-symbols" )
53
+ export_symbols : List [str ] = Field (default_factory = list , alias = AliasChoices ( "export_symbols" , " export-symbols") )
53
54
depends : List [str ] = Field (default_factory = list )
54
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
+
55
66
def get_qualified_name (self , platform ):
56
67
if platform == "win32" :
57
68
suffix = "dll" if self .binding == "none" else "pyd"
58
- elif platform == "darwin" and self . binding == "none" :
59
- suffix = "dylib"
69
+ elif platform == "darwin" :
70
+ suffix = "dylib" if self . binding == "none" else "so"
60
71
else :
61
72
suffix = "so"
73
+ if self .py_limited_api and platform != "win32" :
74
+ return f"{ self .name } .abi3.{ suffix } "
62
75
return f"{ self .name } .{ suffix } "
63
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
+
64
83
65
84
class HatchCppPlatform (BaseModel ):
66
85
cc : str
@@ -117,6 +136,12 @@ def get_compile_flags(self, library: HatchCppLibrary, build_type: BuildType = "r
117
136
library .sources .append (str (Path (nanobind .include_dir ()).parent / "src" / "nb_combined.cpp" ))
118
137
library .include_dirs .append (str ((Path (nanobind .include_dir ()).parent / "ext" / "robin_map" / "include" )))
119
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
120
145
if self .toolchain == "gcc" :
121
146
flags += " " + " " .join (f"-I{ d } " for d in library .include_dirs )
122
147
flags += " -fPIC"
@@ -156,7 +181,7 @@ def get_link_flags(self, library: HatchCppLibrary, build_type: BuildType = "rele
156
181
flags += " " + " " .join (library .extra_objects )
157
182
flags += " " + " " .join (f"-l{ lib } " for lib in library .libraries )
158
183
flags += " " + " " .join (f"-L{ lib } " for lib in library .library_dirs )
159
- flags += f" -o { library .name } .so "
184
+ flags += f" -o { library .get_qualified_name ( self . platform ) } "
160
185
if self .platform == "darwin" :
161
186
flags += " -undefined dynamic_lookup"
162
187
if "mold" in self .ld :
@@ -169,7 +194,7 @@ def get_link_flags(self, library: HatchCppLibrary, build_type: BuildType = "rele
169
194
flags += " " + " " .join (library .extra_objects )
170
195
flags += " " + " " .join (f"-l{ lib } " for lib in library .libraries )
171
196
flags += " " + " " .join (f"-L{ lib } " for lib in library .library_dirs )
172
- flags += f" -o { library .name } .so "
197
+ flags += f" -o { library .get_qualified_name ( self . platform ) } "
173
198
if self .platform == "darwin" :
174
199
flags += " -undefined dynamic_lookup"
175
200
if "mold" in self .ld :
@@ -180,7 +205,7 @@ def get_link_flags(self, library: HatchCppLibrary, build_type: BuildType = "rele
180
205
flags += " " + " " .join (library .extra_link_args )
181
206
flags += " " + " " .join (library .extra_objects )
182
207
flags += " /LD"
183
- flags += f" /Fe:{ library .name } .pyd "
208
+ flags += f" /Fe:{ library .get_qualified_name ( self . platform ) } "
184
209
flags += " /link /DLL"
185
210
if (Path (executable ).parent / "libs" ).exists ():
186
211
flags += f" /LIBPATH:{ str (Path (executable ).parent / 'libs' )} "
0 commit comments