proj-flow 0.8.1__py3-none-any.whl

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 (126) hide show
  1. proj_flow/__init__.py +4 -0
  2. proj_flow/__main__.py +10 -0
  3. proj_flow/api/__init__.py +10 -0
  4. proj_flow/api/arg.py +134 -0
  5. proj_flow/api/completers.py +93 -0
  6. proj_flow/api/ctx.py +238 -0
  7. proj_flow/api/env.py +416 -0
  8. proj_flow/api/init.py +26 -0
  9. proj_flow/api/makefile.py +140 -0
  10. proj_flow/api/step.py +173 -0
  11. proj_flow/base/__init__.py +11 -0
  12. proj_flow/base/cmd.py +50 -0
  13. proj_flow/base/inspect.py +133 -0
  14. proj_flow/base/matrix.py +240 -0
  15. proj_flow/base/plugins.py +44 -0
  16. proj_flow/base/uname.py +71 -0
  17. proj_flow/flow/__init__.py +11 -0
  18. proj_flow/flow/cli/__init__.py +66 -0
  19. proj_flow/flow/cli/cmds.py +385 -0
  20. proj_flow/flow/cli/finder.py +59 -0
  21. proj_flow/flow/configs.py +162 -0
  22. proj_flow/flow/dependency.py +153 -0
  23. proj_flow/flow/init.py +65 -0
  24. proj_flow/flow/interact.py +134 -0
  25. proj_flow/flow/layer.py +176 -0
  26. proj_flow/flow/steps.py +104 -0
  27. proj_flow/log/__init__.py +10 -0
  28. proj_flow/log/commit.py +463 -0
  29. proj_flow/log/fmt.py +12 -0
  30. proj_flow/log/format.py +13 -0
  31. proj_flow/log/hosting/__init__.py +11 -0
  32. proj_flow/log/hosting/github.py +248 -0
  33. proj_flow/log/msg.py +201 -0
  34. proj_flow/log/release.py +34 -0
  35. proj_flow/log/rich_text/__init__.py +22 -0
  36. proj_flow/log/rich_text/api.py +126 -0
  37. proj_flow/log/rich_text/markdown.py +61 -0
  38. proj_flow/log/rich_text/re_structured_text.py +68 -0
  39. proj_flow/plugins/__init__.py +8 -0
  40. proj_flow/plugins/base.py +30 -0
  41. proj_flow/plugins/cmake/__init__.py +11 -0
  42. proj_flow/plugins/cmake/__version__.py +5 -0
  43. proj_flow/plugins/cmake/build.py +29 -0
  44. proj_flow/plugins/cmake/config.py +59 -0
  45. proj_flow/plugins/cmake/context.py +112 -0
  46. proj_flow/plugins/cmake/pack.py +37 -0
  47. proj_flow/plugins/cmake/parser.py +166 -0
  48. proj_flow/plugins/cmake/test.py +29 -0
  49. proj_flow/plugins/commands/__init__.py +12 -0
  50. proj_flow/plugins/commands/bootstrap.py +21 -0
  51. proj_flow/plugins/commands/ci/__init__.py +17 -0
  52. proj_flow/plugins/commands/ci/changelog.py +47 -0
  53. proj_flow/plugins/commands/ci/matrix.py +46 -0
  54. proj_flow/plugins/commands/ci/release.py +116 -0
  55. proj_flow/plugins/commands/init.py +75 -0
  56. proj_flow/plugins/commands/list.py +167 -0
  57. proj_flow/plugins/commands/run.py +147 -0
  58. proj_flow/plugins/commands/system.py +60 -0
  59. proj_flow/plugins/conan/__init__.py +66 -0
  60. proj_flow/plugins/conan/_conan.py +146 -0
  61. proj_flow/plugins/github.py +13 -0
  62. proj_flow/plugins/sign/__init__.py +130 -0
  63. proj_flow/plugins/sign/win32.py +191 -0
  64. proj_flow/plugins/store/__init__.py +11 -0
  65. proj_flow/plugins/store/store_both.py +22 -0
  66. proj_flow/plugins/store/store_packages.py +79 -0
  67. proj_flow/plugins/store/store_tests.py +21 -0
  68. proj_flow/template/layers/base/.clang-format +27 -0
  69. proj_flow/template/layers/base/.flow/config.yml +38 -0
  70. proj_flow/template/layers/base/.flow/flow.py.mustache +172 -0
  71. proj_flow/template/layers/base/.flow/matrix.yml +63 -0
  72. proj_flow/template/layers/base/.flow/official.yml +4 -0
  73. proj_flow/template/layers/base/.gitignore +39 -0
  74. proj_flow/template/layers/base/README.md.mustache +2 -0
  75. proj_flow/template/layers/base/flow +7 -0
  76. proj_flow/template/layers/base/flow.cmd +9 -0
  77. proj_flow/template/layers/base.json +2 -0
  78. proj_flow/template/layers/cmake/.flow/cmake/common.cmake.mustache +27 -0
  79. proj_flow/template/layers/cmake/.flow/extensions/wall/__init__.py.mustache +2 -0
  80. proj_flow/template/layers/cmake/.flow/extensions/wall/icons/__init__.py.mustache +54 -0
  81. proj_flow/template/layers/cmake/.flow/extensions/wall/icons/magick.py.mustache +97 -0
  82. proj_flow/template/layers/cmake/.flow/packages/base.cmake.mustache +33 -0
  83. proj_flow/template/layers/cmake/.flow/packages/config.cmake +12 -0
  84. proj_flow/template/layers/cmake/.flow/packages/cpack.cmake +3 -0
  85. proj_flow/template/layers/cmake/.flow/packages/wix/cpack.cmake.mustache +1 -0
  86. proj_flow/template/layers/cmake/.flow/packages/wix/patches.in.wix +10 -0
  87. proj_flow/template/layers/cmake/.flow/packages/wix.cmake.mustache +13 -0
  88. proj_flow/template/layers/cmake/CMakeGraphVizOptions.cmake +8 -0
  89. proj_flow/template/layers/cmake/CMakeLists.txt.mustache +213 -0
  90. proj_flow/template/layers/cmake/CMakePresets.json +196 -0
  91. proj_flow/template/layers/cmake/data/assets/appicon.ico +0 -0
  92. proj_flow/template/layers/cmake/data/assets/appicon.png +0 -0
  93. proj_flow/template/layers/cmake/data/assets/wix_banner.bmp +0 -0
  94. proj_flow/template/layers/cmake/data/assets/wix_dialog.bmp +0 -0
  95. proj_flow/template/layers/cmake/data/icons/.gitignore +3 -0
  96. proj_flow/template/layers/cmake/data/icons/appicon-mask.svg +6 -0
  97. proj_flow/template/layers/cmake/data/icons/appicon.svg +27 -0
  98. proj_flow/template/layers/cmake/src/main.cc.mustache +38 -0
  99. proj_flow/template/layers/cmake/src/version.hpp.in.mustache +36 -0
  100. proj_flow/template/layers/cmake/tests/test.cc.mustache +17 -0
  101. proj_flow/template/layers/cmake.json +15 -0
  102. proj_flow/template/layers/conan/.flow/cmake/libcxx_toolchain.cmake +4 -0
  103. proj_flow/template/layers/conan/.flow/cmake/output_dirs_setup.cmake +19 -0
  104. proj_flow/template/layers/conan/conanfile.txt +9 -0
  105. proj_flow/template/layers/conan.json +3 -0
  106. proj_flow/template/layers/github_actions/.github/linters/.isort.cfg +4 -0
  107. proj_flow/template/layers/github_actions/.github/linters/.mypy.ini +2 -0
  108. proj_flow/template/layers/github_actions/.github/workflows/build.yml +241 -0
  109. proj_flow/template/layers/github_actions/.github/workflows/linter.yml +59 -0
  110. proj_flow/template/layers/github_actions/CPPLINT.cfg +16 -0
  111. proj_flow/template/layers/github_actions.json +3 -0
  112. proj_flow/template/layers/github_social/.github/ISSUE_TEMPLATE/bug_report.md.mustache +42 -0
  113. proj_flow/template/layers/github_social/.github/ISSUE_TEMPLATE/feature_request.md.mustache +28 -0
  114. proj_flow/template/layers/github_social/CODE_OF_CONDUCT.md.mustache +76 -0
  115. proj_flow/template/layers/github_social/CONTRIBUTING.md +10 -0
  116. proj_flow/template/layers/github_social.json +3 -0
  117. proj_flow/template/licenses/0BSD.mustache +12 -0
  118. proj_flow/template/licenses/MIT.mustache +21 -0
  119. proj_flow/template/licenses/Unlicense.mustache +24 -0
  120. proj_flow/template/licenses/WTFPL.mustache +13 -0
  121. proj_flow/template/licenses/Zlib.mustache +19 -0
  122. proj_flow-0.8.1.dist-info/METADATA +81 -0
  123. proj_flow-0.8.1.dist-info/RECORD +126 -0
  124. proj_flow-0.8.1.dist-info/WHEEL +4 -0
  125. proj_flow-0.8.1.dist-info/entry_points.txt +2 -0
  126. proj_flow-0.8.1.dist-info/licenses/LICENSE +21 -0
proj_flow/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ # Copyright (c) 2025 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ __version__ = "0.8.1"
proj_flow/__main__.py ADDED
@@ -0,0 +1,10 @@
1
+ # Copyright (c) 2025 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ """
5
+ The **proj_flow.__main__** allows *C++ flow* to be called as Python component.
6
+ """
7
+
8
+ from .flow.cli import main
9
+
10
+ main()
@@ -0,0 +1,10 @@
1
+ # Copyright (c) 2025 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ """
5
+ The **proj_flow.api** contains public APIs, usable in third-party plugins.
6
+ """
7
+
8
+ from . import arg, completers, ctx, env, init, makefile, step
9
+
10
+ __all__ = ["arg", "completers", "ctx", "env", "init", "makefile", "step"]
proj_flow/api/arg.py ADDED
@@ -0,0 +1,134 @@
1
+ # Copyright (c) 2025 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ """
5
+ The **proj_flow.api.arg** is used by various commands to declare CLI arguments.
6
+ """
7
+
8
+ import argparse
9
+ import inspect
10
+ import typing
11
+ from dataclasses import dataclass, field
12
+
13
+ from proj_flow.base import inspect as _inspect
14
+
15
+
16
+ @dataclass
17
+ class Argument:
18
+ help: str = ""
19
+ pos: bool = False
20
+ names: typing.List[str] = field(default_factory=list)
21
+ nargs: typing.Union[str, int, None] = None
22
+ opt: typing.Optional[bool] = None
23
+ meta: typing.Optional[str] = None
24
+ action: typing.Union[str, argparse.Action, None] = None
25
+ default: typing.Optional[typing.Any] = None
26
+ choices: typing.Optional[typing.List[str]] = None
27
+ completer: typing.Optional[callable] = None # type: ignore
28
+
29
+ def visit(self, parser: argparse.ArgumentParser, name: str):
30
+ kwargs = {}
31
+ if self.help is not None:
32
+ kwargs["help"] = self.help
33
+ if self.nargs is not None:
34
+ kwargs["nargs"] = self.nargs
35
+ if self.meta is not None:
36
+ kwargs["metavar"] = self.meta
37
+ if self.default is not None:
38
+ kwargs["default"] = self.default
39
+ if self.action is not None:
40
+ kwargs["action"] = self.action
41
+ if self.choices is not None:
42
+ kwargs["choices"] = self.choices
43
+
44
+ names = (
45
+ [name] if self.pos else self.names if len(self.names) > 0 else [f"--{name}"]
46
+ )
47
+
48
+ if self.pos:
49
+ kwargs["nargs"] = "?" if self.opt else 1
50
+ else:
51
+ kwargs["dest"] = name
52
+ kwargs["required"] = not self.opt
53
+
54
+ action = parser.add_argument(*names, **kwargs)
55
+ if self.completer:
56
+ action.completer = self.completer # type: ignore
57
+
58
+ return action
59
+
60
+
61
+ class FlagArgument(Argument):
62
+ def __init__(self, help: str = "", names: typing.List[str] = []):
63
+ super().__init__(
64
+ help=help, names=names, opt=True, action="store_true", default=False
65
+ )
66
+
67
+
68
+ @dataclass
69
+ class _Command:
70
+ name: str
71
+ entry: typing.Optional[callable] # type: ignore
72
+ doc: typing.Optional[str]
73
+ subs: typing.Dict[str, "_Command"]
74
+
75
+ def add(self, names: typing.List[str], entry: callable, doc: typing.Optional[str]): # type: ignore
76
+ name = names[0]
77
+ rest = names[1:]
78
+ if len(rest):
79
+ try:
80
+ child = self.subs[name]
81
+ except KeyError:
82
+ child = _Command(name, None, None, {})
83
+ self.subs[name] = child
84
+
85
+ child.add(rest, entry, doc)
86
+ return
87
+
88
+ try:
89
+ child = self.subs[name]
90
+ child.entry = entry
91
+ child.doc = doc
92
+ except KeyError:
93
+ self.subs[name] = _Command(name, entry, doc, {})
94
+
95
+
96
+ _known_commands = _Command("", None, None, {})
97
+ _autodoc = {
98
+ "proj_flow.flow.configs.Configs": "Current configuration list.",
99
+ "proj_flow.api.env.Runtime": "Tools and print messages, while respecting ``--dry-run``, ``--silent`` and ``--verbose``.",
100
+ }
101
+
102
+
103
+ def command(*name: str):
104
+ def wrap(entry: callable): # type: ignore
105
+ global _known_commands
106
+ _known_commands.add(list(name), entry, entry.__doc__)
107
+
108
+ doc = inspect.getdoc(entry) or ""
109
+ if doc:
110
+ doc += "\n\n"
111
+
112
+ for arg in _inspect.signature(entry):
113
+ help = ""
114
+ for meta in arg.metadata:
115
+ if isinstance(meta, Argument):
116
+ help = meta.help
117
+ if help:
118
+ break
119
+
120
+ if not help:
121
+ full_name = f"{arg.type.__module__}.{arg.type.__name__}"
122
+ help = _autodoc.get(full_name, "")
123
+
124
+ doc += f":param {_inspect.type_name(arg.type)} {arg.name}: {help}\n"
125
+
126
+ entry.__doc__ = doc
127
+
128
+ return entry
129
+
130
+ return wrap
131
+
132
+
133
+ def get_commands():
134
+ return _known_commands
@@ -0,0 +1,93 @@
1
+ # Copyright (c) 2025 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ """
5
+ The **proj_flow.api.completers** defines :py:mod:`argcomplete` functions for
6
+ ``-C``, ``--step`` and ``-D``.
7
+ """
8
+
9
+ import os
10
+ from typing import Any, Dict, List, Union, cast
11
+
12
+ import yaml
13
+
14
+ from proj_flow import api
15
+
16
+
17
+ def cd_completer(prefix, **kwargs):
18
+ target_dir = os.path.dirname(prefix)
19
+ incomplete_part = os.path.basename(prefix)
20
+ try:
21
+ names = os.listdir(target_dir or ".")
22
+ except Exception:
23
+ return # empty iterator
24
+
25
+ for name in names:
26
+ if not name.startswith(incomplete_part):
27
+ continue
28
+ full = os.path.join(target_dir, name)
29
+ if not os.path.isdir(full):
30
+ continue
31
+
32
+ yield f"{full}{os.sep}"
33
+
34
+
35
+ def step_completer(prefix: str, parser, **kwargs):
36
+ flow_cfg = cast(api.env.FlowConfig, parser.flow)
37
+
38
+ comma_sep = prefix.split(",")
39
+ start = ",".join(comma_sep[:-1])
40
+ if len(comma_sep) > 1:
41
+ start += ","
42
+
43
+ current = comma_sep[-1].lower()
44
+ for step in flow_cfg.steps:
45
+ name = cast(str, step.name)
46
+ if name.lower().startswith(current):
47
+ candidate = current + name[len(current) :]
48
+ yield start + candidate
49
+
50
+
51
+ def _str_arg(arg: Union[bool, str]):
52
+ if isinstance(arg, bool):
53
+ return "ON" if arg else "OFF"
54
+ return str(arg)
55
+
56
+
57
+ def matrix_completer(prefix: str, parser, **kwargs):
58
+ flow_cfg = cast(api.env.FlowConfig, parser.flow)
59
+
60
+ matrix_yml = os.path.join(flow_cfg.root, ".flow", "matrix.yml")
61
+ with open(matrix_yml, "r", encoding="UTF-8") as contents:
62
+ data: Dict[str, List[Any]] = yaml.load(contents, Loader=yaml.Loader).get(
63
+ "matrix", {}
64
+ )
65
+
66
+ comma_sep = prefix.split(",")
67
+ start = ",".join(comma_sep[:-1])
68
+ if len(comma_sep) > 1:
69
+ start += ","
70
+
71
+ current = comma_sep[-1].split("=", 1)
72
+
73
+ if len(current) == 1:
74
+ completions: List[str] = []
75
+ for key in data:
76
+ if key.startswith(current[0]):
77
+ completions.append(f"{start}{key}=")
78
+ if len(completions) != 1:
79
+ return completions
80
+
81
+ current = completions[0].split("=", 1)
82
+
83
+ try:
84
+ args = data[current[0]]
85
+ except KeyError:
86
+ return
87
+
88
+ completions: List[str] = []
89
+ for arg in map(_str_arg, args):
90
+ if arg.startswith(current[1]):
91
+ completions.append(f"{start}{current[0]}={arg}")
92
+
93
+ return completions
proj_flow/api/ctx.py ADDED
@@ -0,0 +1,238 @@
1
+ # Copyright (c) 2025 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ """
5
+ The **proj_flow.api.ctx** provides tools for preparing the project template.
6
+ """
7
+
8
+ import datetime
9
+ import inspect
10
+ import os
11
+ from dataclasses import dataclass
12
+ from typing import Callable, Dict, Iterable, List, Optional, Union
13
+
14
+ from proj_flow.base import cmd
15
+
16
+ package_root = os.path.dirname(os.path.dirname(__file__))
17
+ template_dir = "template"
18
+
19
+
20
+ StrOrBool = Union[str, bool]
21
+ Values = Union[StrOrBool, List[str]]
22
+ SettingsType = Dict[str, StrOrBool]
23
+
24
+
25
+ @dataclass
26
+ class Setting:
27
+ json_key: str
28
+ prompt: str = ""
29
+ value: Union[Values, Callable[[], Values]] = ""
30
+ fix: Optional[str] = None
31
+ force_fix: bool = False
32
+
33
+ def calc_value(self, previous: SettingsType):
34
+ if isinstance(self.value, Callable):
35
+ kwargs = {}
36
+
37
+ params = inspect.signature(self.value).parameters
38
+ if "settings" in params:
39
+ kwargs["settings"] = previous
40
+
41
+ return self.value(**kwargs)
42
+
43
+ return self.value
44
+
45
+
46
+ def register_init_setting(*setting: Setting, is_hidden=False):
47
+ (hidden if is_hidden else defaults).extend(setting)
48
+
49
+
50
+ def register_switch(key: str, prompt: str, enabled: bool):
51
+ switches.append(Setting(key, prompt, value=enabled))
52
+
53
+
54
+ def register_internal(key: str, value: any):
55
+ internals[key] = value
56
+
57
+
58
+ def _git_config(name: str):
59
+ def wrap():
60
+ proc = cmd.run("git", "config", name, capture_output=True)
61
+ if proc is None or proc.returncode != 0:
62
+ return None
63
+ return proc.stdout.strip()
64
+
65
+ return wrap
66
+
67
+
68
+ def move_to_front(preferred: str, values: Iterable[str]):
69
+ result: List[str] = []
70
+
71
+ has_preferred = False
72
+ for value in values:
73
+ if value == preferred:
74
+ has_preferred = True
75
+ else:
76
+ result.append(value)
77
+
78
+ if has_preferred:
79
+ result.insert(0, preferred)
80
+
81
+ return result
82
+
83
+
84
+ def _list_ext():
85
+ return move_to_front(".cpp", sorted(_fileext.keys()))
86
+
87
+
88
+ def _as_mustache(basename: str):
89
+ name, ext = os.path.splitext(basename)
90
+ return ext == ".mustache" and name or None
91
+
92
+
93
+ def _enum_licenses():
94
+ root = os.path.abspath(os.path.join(package_root, template_dir, "licenses"))
95
+ for _, dirnames, filenames in os.walk(root):
96
+ dirnames[:] = []
97
+ iter = filter(lambda x: x is not None, map(_as_mustache, filenames))
98
+ return move_to_front("MIT", iter)
99
+ return []
100
+
101
+
102
+ def _filter(
103
+ src: Callable[[SettingsType], StrOrBool],
104
+ flt: Callable[[StrOrBool], StrOrBool],
105
+ ):
106
+ def impl(settings: SettingsType):
107
+ return flt(src(settings))
108
+
109
+ return impl
110
+
111
+
112
+ def _get_nothing(_: SettingsType) -> StrOrBool:
113
+ return ""
114
+
115
+
116
+ def _map(internal_key: str):
117
+ def impl(key: str):
118
+ mapped = internals.get(internal_key)
119
+ if not isinstance(mapped, dict):
120
+ return None
121
+ return mapped.get(key)
122
+
123
+ return impl
124
+
125
+
126
+ def _get_key(key: str):
127
+ def impl(settings: SettingsType):
128
+ return settings.get(key, "")
129
+
130
+ return impl
131
+
132
+
133
+ def _build_fixup(settings: SettingsType, fixup: str):
134
+ fixup_refs = fixup.split("{")
135
+ result = fixup_refs.pop(0)
136
+ for ref_expr in fixup_refs:
137
+ ref_text, verbose = ref_expr.split("}")
138
+ filter_path = ref_text.split("$")
139
+
140
+ ref = filter_path.pop(0)
141
+ code = _get_nothing if ref == "" else _get_key(ref)
142
+
143
+ for filter_name in filter_path:
144
+ flt = _filters.get(filter_name)
145
+ if flt is None:
146
+ if filter_name.startswith("map:"):
147
+ internal_key = filter_name[4:]
148
+ code = _filter(code, _map(internal_key))
149
+ continue
150
+ code = _filter(code, flt)
151
+
152
+ value = code(settings)
153
+
154
+ if result:
155
+ result += value
156
+ else:
157
+ result = value
158
+
159
+ if verbose:
160
+ result += verbose
161
+
162
+ return result
163
+
164
+
165
+ def _fixed(fixup: str):
166
+ def wrap(settings: SettingsType):
167
+ return _build_fixup(settings, fixup)
168
+
169
+ return wrap
170
+
171
+
172
+ internals = {}
173
+
174
+ switches: List[Setting] = []
175
+
176
+ defaults: List[Setting] = [
177
+ Setting(
178
+ "PROJECT.NAME",
179
+ "Project name",
180
+ lambda: os.path.basename(os.path.abspath("")) or "?project-name?",
181
+ ),
182
+ Setting("PROJECT.DESCRIPTION", "Project description"),
183
+ Setting(
184
+ "PROJECT.EMAIL",
185
+ "Valid email, e.g. for CODE_OF_CONDUCT",
186
+ _git_config("user.email"),
187
+ ),
188
+ Setting(
189
+ "COPY.YEAR",
190
+ "Year for copyright notices",
191
+ str(datetime.date.today().year),
192
+ ),
193
+ Setting(
194
+ "COPY.HOLDER",
195
+ "Holder of the copyright",
196
+ _git_config("user.name"),
197
+ ),
198
+ Setting("COPY.LICENSE", "License", _enum_licenses),
199
+ Setting(
200
+ "INCLUDE_PREFIX",
201
+ 'Prefix for includes (as in #include "{PREFIX}/version.hpp")',
202
+ _fixed("{PROJECT.NAME}"),
203
+ ),
204
+ Setting(
205
+ "NAME_PREFIX",
206
+ "CMake variable name prefix",
207
+ _fixed("{PROJECT.NAME$safe$upper}"),
208
+ ),
209
+ Setting(
210
+ "NAMESPACE",
211
+ "C++ namespace for the project",
212
+ _fixed("{PROJECT.NAME$safe}"),
213
+ ),
214
+ Setting("EXT", "Extension for code files", _list_ext),
215
+ Setting("SRCDIR", "Directory for code files", "src"),
216
+ Setting(
217
+ "INCLUDEDIR",
218
+ "Directory for include files",
219
+ "include",
220
+ "{INCLUDEDIR}/{INCLUDE_PREFIX}",
221
+ force_fix=True,
222
+ ),
223
+ ]
224
+
225
+ hidden: List[Setting] = [
226
+ Setting("EXT.cxx", fix="{EXT}"),
227
+ Setting("EXT.hxx", fix="{EXT.cxx$header}"),
228
+ ]
229
+
230
+ _fileext = {".cc": ".hh", ".cxx": ".hxx", ".cpp": ".hpp"}
231
+
232
+
233
+ _filters: Dict[str, Callable[[StrOrBool], StrOrBool]] = {
234
+ "safe": lambda value: value.replace("-", "_"),
235
+ "upper": lambda value: value.upper(),
236
+ "lower": lambda value: value.lower(),
237
+ "header": lambda cxx_ext: _fileext.get(cxx_ext, ".hpp"),
238
+ }