Skip to content

Commit 1a5a2c4

Browse files
committed
Conditional headers
1 parent cbf5a78 commit 1a5a2c4

File tree

10 files changed

+73
-20
lines changed

10 files changed

+73
-20
lines changed

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,13 @@ dependencies = [
2525
"sphinxify >= 0.7.3",
2626
"validobj ~= 1.2",
2727
"cxxheaderparser[pcpp] ~= 1.5",
28+
"packaging",
2829
"tomli",
2930
"tomli_w",
3031
"toposort",
3132
"typing-extensions",
32-
"validobj",
3333
"pyyaml >= 5.1",
3434
"pybind11-stubgen ~= 2.5.1",
35-
"delocate; platform_system == 'Darwin'",
3635
"distro; platform_system == 'Linux'",
3736
]
3837

src/semiwrap/config/pyproject_toml.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import dataclasses
66
import re
7-
from typing import Dict, List, Optional
7+
from typing import Dict, List, Optional, Union
88

99
_arch_re = re.compile(r"\{\{\s*ARCH\s*\}\}")
1010
_os_re = re.compile(r"\{\{\s*OS\s*\}\}")
@@ -60,6 +60,28 @@ class TypeCasterConfig:
6060
headers: List[TypeCasterHeader] = dataclasses.field(default_factory=list)
6161

6262

63+
@dataclasses.dataclass
64+
class ConditionalHeader:
65+
"""
66+
Allows specifying that a header will only be autogenerated if the specified
67+
condition is true.
68+
69+
.. code-block:: toml
70+
71+
[tool.semiwrap.extension_modules."PACKAGE.NAME".headers]
72+
Name = { header="header.h", enable_if = "platform_machine == 'aarch64'" }
73+
74+
"""
75+
76+
#: Name of the header file
77+
header: str
78+
79+
#: This is a PEP 508 environment marker specification.
80+
#:
81+
#: The header will not be parsed if this does not evaluate to true
82+
enable_if: str
83+
84+
6385
@dataclasses.dataclass
6486
class ExtensionModuleConfig:
6587
"""
@@ -123,7 +145,9 @@ class ExtensionModuleConfig:
123145
#:
124146
#: .. seealso:: :ref:`autowrap`
125147
#:
126-
headers: Dict[str, str] = dataclasses.field(default_factory=dict)
148+
headers: Dict[str, Union[str, ConditionalHeader]] = dataclasses.field(
149+
default_factory=dict
150+
)
127151

128152
#: Path to a directory of yaml files. Generation data will be looked up
129153
#: using the key in the headers dictionary.

src/semiwrap/makeplan.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,14 +509,14 @@ def _process_headers(
509509
module_sources: T.List[BuildTarget] = []
510510
subpackages: T.Set[str] = set()
511511

512-
for yml, hdr in extension.headers.items():
512+
for yml, hdr in self.pyproject.get_extension_headers(extension):
513513
yml_input = InputFile(yaml_path / f"{yml}.yml")
514514

515515
try:
516516
ayml = AutowrapConfigYaml.from_file(self.project_root / yml_input.path)
517517
except FileNotFoundError:
518518
if not self.missing_yaml_ok:
519-
msg = f"{self.project_root / yml_input.path}: use `python3 -m semiwrap.cmd_creategen` to generate"
519+
msg = f"{self.project_root / yml_input.path}: use `python3 -m semiwrap create-gen` to generate"
520520
raise FileNotFoundError(msg) from None
521521
ayml = AutowrapConfigYaml()
522522

src/semiwrap/pyproject.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pathlib
22
import typing as T
33

4+
import packaging.markers
45
import tomli
56

67
from .config.util import parse_input
@@ -23,6 +24,19 @@ def __init__(self, pyproject_path: T.Optional[pathlib.Path] = None) -> None:
2324

2425
self._all_deps = None
2526

27+
self._evaluated_markers: T.Dict[str, bool] = {}
28+
29+
def _enable_if(self, condition: str) -> bool:
30+
"""
31+
Evaluates a string containing PEP 508 environment markers
32+
"""
33+
ok = self._evaluated_markers.get(condition)
34+
if ok is None:
35+
ok = packaging.markers.Marker(condition).evaluate()
36+
self._evaluated_markers[condition] = ok
37+
38+
return ok
39+
2640
@property
2741
def package_root(self) -> pathlib.Path:
2842
if self._package_root is None:
@@ -40,12 +54,6 @@ def package_root(self) -> pathlib.Path:
4054

4155
return self._package_root
4256

43-
@property
44-
def platform(self):
45-
if self._platform is None:
46-
self._platform = get_platform()
47-
return self._platform
48-
4957
@property
5058
def project(self):
5159
if self._project is None:
@@ -59,12 +67,6 @@ def project(self):
5967

6068
self.project_dict = self.pyproject.get("tool", {}).get("semiwrap", {})
6169

62-
# Overrides are applied before pydantic does processing, so that
63-
# we can easily override anything without needing to make the
64-
# pydantic schemas messy with needless details
65-
override_keys = get_platform_override_keys(self.platform)
66-
apply_overrides(self.project_dict, override_keys)
67-
6870
try:
6971
self._project = parse_input(
7072
self.project_dict, SemiwrapToolConfig, project_fname
@@ -85,3 +87,12 @@ def get_extension_deps(self, extension: ExtensionModuleConfig) -> T.List[str]:
8587
deps.append(wrap)
8688
deps.extend(extension.depends)
8789
return deps
90+
91+
def get_extension_headers(
92+
self, extension: ExtensionModuleConfig
93+
) -> T.Generator[T.Tuple[str, str], None, None]:
94+
for yml, hdr in extension.headers.items():
95+
if isinstance(hdr, str):
96+
yield yml, hdr
97+
elif self._enable_if(hdr.enable_if):
98+
yield yml, hdr.header

src/semiwrap/tool/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .__main__ import main

src/semiwrap/tool/scan_headers.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,15 @@ def _should_ignore(f):
7474
all_present.add(inc / h.header)
7575

7676
for name, ext in project.extension_modules.items():
77-
if not ext.headers:
77+
files = []
78+
for _, f in ext.headers.items():
79+
if isinstance(f, str):
80+
files.append(Path(f))
81+
else:
82+
files.append(Path(f.header))
83+
84+
if not files:
7885
continue
79-
files = [Path(f) for f in ext.headers.values()]
8086
for incdir in search_paths[name]:
8187
incdir = Path(incdir)
8288
for f in files:

tests/cpp/sw-test/pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ using2 = "using2.h"
6565
virtual_comma = "virtual_comma.h"
6666
virtual_xform = "virtual_xform.h"
6767

68+
# conditional headers
69+
cond_always_true = { header="cond_always_true.h", enable_if="python_version > '2'"}
70+
cond_never_true = { header="cond_never_true.h", enable_if="python_version < '2'"}
71+
6872
# Inheritance
6973
IBase = "inheritance/ibase.h"
7074
IChild = "inheritance/ichild.h"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#pragma once
2+
3+
inline int always_compiled() { return 1; }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#error "Should never be compiled"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
3+
functions:
4+
always_compiled:

0 commit comments

Comments
 (0)