proj-flow 0.9.3__py3-none-any.whl → 0.10.0__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 (66) hide show
  1. proj_flow/__init__.py +6 -1
  2. proj_flow/api/arg.py +47 -24
  3. proj_flow/api/ctx.py +43 -23
  4. proj_flow/api/env.py +7 -2
  5. proj_flow/api/makefile.py +1 -1
  6. proj_flow/api/step.py +3 -5
  7. proj_flow/base/name_list.py +19 -0
  8. proj_flow/base/plugins.py +1 -36
  9. proj_flow/base/registry.py +19 -4
  10. proj_flow/cli/__init__.py +2 -4
  11. proj_flow/cli/argument.py +3 -3
  12. proj_flow/{flow/dependency.py → dependency.py} +1 -1
  13. proj_flow/ext/cplusplus/__init__.py +10 -0
  14. proj_flow/ext/cplusplus/cmake/__init__.py +12 -0
  15. proj_flow/{plugins → ext/cplusplus}/cmake/__version__.py +5 -0
  16. proj_flow/{plugins → ext/cplusplus}/cmake/context.py +10 -8
  17. proj_flow/{plugins → ext/cplusplus}/cmake/parser.py +6 -28
  18. proj_flow/ext/cplusplus/cmake/steps.py +142 -0
  19. proj_flow/ext/cplusplus/cmake/version.py +35 -0
  20. proj_flow/{plugins → ext/cplusplus}/conan/__init__.py +7 -3
  21. proj_flow/{plugins → ext/cplusplus}/conan/_conan.py +8 -3
  22. proj_flow/ext/github/__init__.py +2 -2
  23. proj_flow/ext/github/cli.py +2 -11
  24. proj_flow/{plugins/github.py → ext/github/switches.py} +3 -3
  25. proj_flow/ext/{markdown_changelist.py → markdown_changelog.py} +2 -1
  26. proj_flow/ext/python/rtdocs.py +1 -1
  27. proj_flow/ext/python/version.py +1 -2
  28. proj_flow/ext/{re_structured_changelist.py → re_structured_changelog.py} +3 -1
  29. proj_flow/{plugins → ext}/sign/__init__.py +64 -44
  30. proj_flow/ext/sign/api.py +83 -0
  31. proj_flow/ext/sign/win32.py +152 -0
  32. proj_flow/{plugins/store/store_packages.py → ext/store.py} +51 -9
  33. proj_flow/flow/__init__.py +2 -2
  34. proj_flow/log/release.py +1 -1
  35. proj_flow/log/rich_text/markdown.py +1 -1
  36. proj_flow/log/rich_text/re_structured_text.py +1 -1
  37. proj_flow/minimal/__init__.py +2 -2
  38. proj_flow/{plugins → minimal}/base.py +3 -2
  39. proj_flow/{plugins/commands → minimal}/init.py +44 -11
  40. proj_flow/minimal/run.py +1 -2
  41. proj_flow/project/__init__.py +11 -0
  42. proj_flow/project/api.py +51 -0
  43. proj_flow/project/cplusplus.py +17 -0
  44. proj_flow/project/data.py +14 -0
  45. proj_flow/{flow → project}/interact.py +114 -13
  46. {proj_flow-0.9.3.dist-info → proj_flow-0.10.0.dist-info}/METADATA +3 -2
  47. {proj_flow-0.9.3.dist-info → proj_flow-0.10.0.dist-info}/RECORD +50 -55
  48. proj_flow/flow/init.py +0 -65
  49. proj_flow/plugins/__init__.py +0 -8
  50. proj_flow/plugins/cmake/__init__.py +0 -11
  51. proj_flow/plugins/cmake/build.py +0 -29
  52. proj_flow/plugins/cmake/config.py +0 -59
  53. proj_flow/plugins/cmake/pack.py +0 -37
  54. proj_flow/plugins/cmake/test.py +0 -29
  55. proj_flow/plugins/commands/__init__.py +0 -12
  56. proj_flow/plugins/commands/ci/__init__.py +0 -17
  57. proj_flow/plugins/commands/ci/changelog.py +0 -47
  58. proj_flow/plugins/commands/ci/matrix.py +0 -46
  59. proj_flow/plugins/commands/ci/release.py +0 -116
  60. proj_flow/plugins/sign/win32.py +0 -191
  61. proj_flow/plugins/store/__init__.py +0 -11
  62. proj_flow/plugins/store/store_both.py +0 -22
  63. proj_flow/plugins/store/store_tests.py +0 -21
  64. {proj_flow-0.9.3.dist-info → proj_flow-0.10.0.dist-info}/WHEEL +0 -0
  65. {proj_flow-0.9.3.dist-info → proj_flow-0.10.0.dist-info}/entry_points.txt +0 -0
  66. {proj_flow-0.9.3.dist-info → proj_flow-0.10.0.dist-info}/licenses/LICENSE +0 -0
proj_flow/__init__.py CHANGED
@@ -1,4 +1,9 @@
1
1
  # Copyright (c) 2025 Marcin Zdun
2
2
  # This code is licensed under MIT license (see LICENSE for details)
3
3
 
4
- __version__ = "0.9.3"
4
+ """
5
+ The **proj_flow** contains only ``__version__`` to be updated, nothing more.
6
+ This is in an attempt to make this module easy to load initially.
7
+ """
8
+
9
+ __version__ = "0.10.0"
proj_flow/api/arg.py CHANGED
@@ -12,48 +12,71 @@ from dataclasses import dataclass, field
12
12
 
13
13
  from proj_flow.base import inspect as _inspect
14
14
 
15
+ T = typing.TypeVar("T")
16
+ LazyArgument = typing.Union[T, typing.Callable[[], T]]
17
+
18
+
19
+ def _eval(arg: LazyArgument[T]) -> T:
20
+ if callable(arg):
21
+ return typing.cast(T, arg())
22
+ return arg
23
+
24
+
25
+ class _Completable(typing.Protocol):
26
+ completer: _inspect.Function
27
+
15
28
 
16
29
  @dataclass
17
30
  class Argument:
18
- help: str = ""
31
+ help: LazyArgument[str] = ""
19
32
  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
33
+ names: LazyArgument[typing.List[str]] = field(default_factory=list)
34
+ nargs: LazyArgument[typing.Union[str, int, None]] = None
35
+ opt: LazyArgument[typing.Optional[bool]] = None
36
+ meta: LazyArgument[typing.Optional[str]] = None
37
+ action: LazyArgument[typing.Union[str, argparse.Action, None]] = None
38
+ default: LazyArgument[typing.Optional[typing.Any]] = None
39
+ choices: LazyArgument[typing.Optional[typing.List[str]]] = None
27
40
  completer: typing.Optional[_inspect.Function] = None
28
41
 
29
42
  def visit(self, parser: argparse.ArgumentParser, name: str):
30
43
  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
44
+
45
+ self_help = _eval(self.help)
46
+ self_names = _eval(self.names)
47
+ self_nargs = _eval(self.nargs)
48
+ self_opt = _eval(self.opt)
49
+ self_meta = _eval(self.meta)
50
+ self_action = _eval(self.action)
51
+ self_default = _eval(self.default)
52
+ self_choices = _eval(self.choices)
53
+
54
+ if self_help is not None:
55
+ kwargs["help"] = self_help
56
+ if self_nargs is not None:
57
+ kwargs["nargs"] = self_nargs
58
+ if self_meta is not None:
59
+ kwargs["metavar"] = self_meta
60
+ if self_default is not None:
61
+ kwargs["default"] = self_default
62
+ if self_action is not None:
63
+ kwargs["action"] = self_action
64
+ if self_choices is not None:
65
+ kwargs["choices"] = self_choices
43
66
 
44
67
  names = (
45
- [name] if self.pos else self.names if len(self.names) > 0 else [f"--{name}"]
68
+ [name] if self.pos else self_names if len(self_names) > 0 else [f"--{name}"]
46
69
  )
47
70
 
48
71
  if self.pos:
49
- kwargs["nargs"] = "?" if self.opt else 1
72
+ kwargs["nargs"] = "?" if self_opt else 1
50
73
  else:
51
74
  kwargs["dest"] = name
52
- kwargs["required"] = not self.opt
75
+ kwargs["required"] = not self_opt
53
76
 
54
77
  action = parser.add_argument(*names, **kwargs)
55
78
  if self.completer:
56
- action.completer = self.completer # type: ignore
79
+ typing.cast(_Completable, action).completer = self.completer
57
80
 
58
81
  return action
59
82
 
proj_flow/api/ctx.py CHANGED
@@ -9,9 +9,10 @@ import datetime
9
9
  import inspect
10
10
  import os
11
11
  from dataclasses import dataclass
12
- from typing import Callable, Dict, Iterable, List, Optional, Union
12
+ from typing import Any, Callable, Dict, Iterable, List, Optional, Union, cast
13
13
 
14
14
  from proj_flow.base import cmd
15
+ from proj_flow.base import inspect as _inspect
15
16
 
16
17
  package_root = os.path.dirname(os.path.dirname(__file__))
17
18
  template_dir = "template"
@@ -26,32 +27,44 @@ SettingsType = Dict[str, StrOrBool]
26
27
  class Setting:
27
28
  json_key: str
28
29
  prompt: str = ""
29
- value: Union[Values, Callable[[], Values]] = ""
30
+ value: Union[Values, Callable[[], Values], Callable[[SettingsType], Values]] = ""
30
31
  fix: Optional[str] = None
31
32
  force_fix: bool = False
33
+ project: Optional[str] = None
32
34
 
33
35
  def calc_value(self, previous: SettingsType):
34
- if isinstance(self.value, Callable):
36
+ if callable(self.value):
35
37
  kwargs = {}
36
38
 
37
39
  params = inspect.signature(self.value).parameters
38
40
  if "settings" in params:
39
41
  kwargs["settings"] = previous
40
42
 
41
- return self.value(**kwargs)
43
+ return cast(_inspect.Function, self.value)(**kwargs)
42
44
 
43
45
  return self.value
44
46
 
45
47
 
46
- def register_init_setting(*setting: Setting, is_hidden=False):
48
+ def register_init_setting(*settings: Setting, is_hidden=False, project: Optional[str]):
49
+ if project is not None:
50
+ for setting in settings:
51
+ setting.project = project
52
+ (hidden if is_hidden else defaults).extend(settings)
53
+
54
+
55
+ def register_common_init_setting(*setting: Setting, is_hidden=False):
47
56
  (hidden if is_hidden else defaults).extend(setting)
48
57
 
49
58
 
50
- def register_switch(key: str, prompt: str, enabled: bool):
59
+ def register_switch(key: str, prompt: str, enabled: bool, project: Optional[str]):
60
+ switches.append(Setting(key, prompt, value=enabled, project=project))
61
+
62
+
63
+ def register_common_switch(key: str, prompt: str, enabled: bool):
51
64
  switches.append(Setting(key, prompt, value=enabled))
52
65
 
53
66
 
54
- def register_internal(key: str, value: any):
67
+ def register_internal(key: str, value: Any):
55
68
  internals[key] = value
56
69
 
57
70
 
@@ -59,17 +72,20 @@ def _git_config(name: str):
59
72
  def wrap():
60
73
  proc = cmd.run("git", "config", name, capture_output=True)
61
74
  if proc is None or proc.returncode != 0:
62
- return None
75
+ return ""
63
76
  return proc.stdout.strip()
64
77
 
65
78
  return wrap
66
79
 
67
80
 
68
- def move_to_front(preferred: str, values: Iterable[str]):
81
+ def move_to_front(preferred: str, values: Iterable[Optional[str]]):
69
82
  result: List[str] = []
70
83
 
71
84
  has_preferred = False
72
85
  for value in values:
86
+ if value is None:
87
+ continue
88
+
73
89
  if value == preferred:
74
90
  has_preferred = True
75
91
  else:
@@ -94,7 +110,7 @@ def _enum_licenses():
94
110
  root = os.path.abspath(os.path.join(package_root, template_dir, "licenses"))
95
111
  for _, dirnames, filenames in os.walk(root):
96
112
  dirnames[:] = []
97
- iter = filter(lambda x: x is not None, map(_as_mustache, filenames))
113
+ iter = map(_as_mustache, filenames)
98
114
  return move_to_front("MIT", iter)
99
115
  return []
100
116
 
@@ -114,11 +130,11 @@ def _get_nothing(_: SettingsType) -> StrOrBool:
114
130
 
115
131
 
116
132
  def _map(internal_key: str):
117
- def impl(key: str):
133
+ def impl(key: StrOrBool) -> StrOrBool:
118
134
  mapped = internals.get(internal_key)
119
135
  if not isinstance(mapped, dict):
120
- return None
121
- return mapped.get(key)
136
+ return ""
137
+ return mapped.get(key, "")
122
138
 
123
139
  return impl
124
140
 
@@ -152,12 +168,12 @@ def _build_fixup(settings: SettingsType, fixup: str):
152
168
  value = code(settings)
153
169
 
154
170
  if result:
155
- result += value
171
+ result = f"{result}{value}"
156
172
  else:
157
173
  result = value
158
174
 
159
175
  if verbose:
160
- result += verbose
176
+ result = f"{result}{verbose}"
161
177
 
162
178
  return result
163
179
 
@@ -169,7 +185,7 @@ def _fixed(fixup: str):
169
185
  return wrap
170
186
 
171
187
 
172
- internals = {}
188
+ internals: Dict[str, Any] = {}
173
189
 
174
190
  switches: List[Setting] = []
175
191
 
@@ -200,18 +216,21 @@ defaults: List[Setting] = [
200
216
  "INCLUDE_PREFIX",
201
217
  'Prefix for includes (as in #include "{PREFIX}/version.hpp")',
202
218
  _fixed("{PROJECT.NAME}"),
219
+ project="cxx",
203
220
  ),
204
221
  Setting(
205
222
  "NAME_PREFIX",
206
223
  "CMake variable name prefix",
207
224
  _fixed("{PROJECT.NAME$safe$upper}"),
225
+ project="cxx",
208
226
  ),
209
227
  Setting(
210
228
  "NAMESPACE",
211
229
  "C++ namespace for the project",
212
230
  _fixed("{PROJECT.NAME$safe}"),
231
+ project="cxx",
213
232
  ),
214
- Setting("EXT", "Extension for code files", _list_ext),
233
+ Setting("EXT", "Extension for code files", _list_ext, project="cxx"),
215
234
  Setting("SRCDIR", "Directory for code files", "src"),
216
235
  Setting(
217
236
  "INCLUDEDIR",
@@ -219,20 +238,21 @@ defaults: List[Setting] = [
219
238
  "include",
220
239
  "{INCLUDEDIR}/{INCLUDE_PREFIX}",
221
240
  force_fix=True,
241
+ project="cxx",
222
242
  ),
223
243
  ]
224
244
 
225
245
  hidden: List[Setting] = [
226
- Setting("EXT.cxx", fix="{EXT}"),
227
- Setting("EXT.hxx", fix="{EXT.cxx$header}"),
246
+ Setting("EXT.cxx", fix="{EXT}", project="cxx"),
247
+ Setting("EXT.hxx", fix="{EXT.cxx$header}", project="cxx"),
228
248
  ]
229
249
 
230
250
  _fileext = {".cc": ".hh", ".cxx": ".hxx", ".cpp": ".hpp"}
231
251
 
232
252
 
233
253
  _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"),
254
+ "safe": lambda value: str(value).replace("-", "_"),
255
+ "upper": lambda value: str(value).upper(),
256
+ "lower": lambda value: str(value).lower(),
257
+ "header": lambda cxx_ext: _fileext.get(str(cxx_ext), ".hpp"),
238
258
  }
proj_flow/api/env.py CHANGED
@@ -24,7 +24,6 @@ from enum import Enum
24
24
  from typing import Any, Callable, Dict, List, Optional, Union, cast
25
25
 
26
26
  from proj_flow.base import plugins, uname
27
- from proj_flow.base.plugins import load_module_plugins
28
27
 
29
28
  platform = uname.uname()[0]
30
29
 
@@ -152,7 +151,13 @@ class FlowConfig:
152
151
  sys.path.insert(0, local_extensions)
153
152
 
154
153
  for extension in extensions:
155
- importlib.import_module(extension)
154
+ try:
155
+ importlib.import_module(extension)
156
+ except ModuleNotFoundError:
157
+ print(
158
+ f"-- error: module `{extension}` was no found, ignoring",
159
+ file=sys.stderr,
160
+ )
156
161
 
157
162
  @property
158
163
  def entry(self) -> Dict[str, dict]:
proj_flow/api/makefile.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # This code is licensed under MIT license (see LICENSE for details)
3
3
 
4
4
  """
5
- The **proj_flow.api.makefiles** exposes simple makefiles APIs, so extensions can
5
+ The **proj_flow.api.makefile** exposes simple makefile APIs, so extensions can
6
6
  easily provide run steps with multiple scripts being called.
7
7
  """
8
8
 
proj_flow/api/step.py CHANGED
@@ -12,6 +12,7 @@ from typing import List, cast
12
12
  from proj_flow.api.env import Config, Runtime
13
13
  from proj_flow.base import inspect as _inspect
14
14
  from proj_flow.base import matrix
15
+ from proj_flow.base.name_list import name_list
15
16
 
16
17
 
17
18
  class Step(ABC):
@@ -111,11 +112,8 @@ def _name_list(label: str, names: List[str], template="`{}`") -> str:
111
112
  if len(names) == 0:
112
113
  return ""
113
114
 
114
- em = [template.format(name) for name in names]
115
- prefix = ", ".join(em[:-1])
116
- if prefix:
117
- prefix += " and "
118
- return f"\n:{label}: {prefix}{em[-1]}"
115
+ joined = name_list([template.format(name) for name in names])
116
+ return f"\n:{label}: {joined}"
119
117
 
120
118
 
121
119
  def _make_private(f: _inspect.Function):
@@ -0,0 +1,19 @@
1
+ # Copyright (c) 2025 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ """
5
+ The **proj_flow.base.name_list** provides name list helper for arguments with
6
+ choices
7
+ """
8
+
9
+ from typing import List
10
+
11
+
12
+ def name_list(names: List[str]) -> str:
13
+ if len(names) == 0:
14
+ return ""
15
+
16
+ prefix = ", ".join(names[:-1])
17
+ if prefix:
18
+ prefix += " and "
19
+ return f"{prefix}{names[-1]}"
proj_flow/base/plugins.py CHANGED
@@ -5,11 +5,9 @@
5
5
  The **proj_flow.base.plugins** provide the plugin enumeration helpers.
6
6
  """
7
7
 
8
- import importlib
9
8
  import json
10
9
  import os
11
- from types import ModuleType
12
- from typing import Optional, cast
10
+ from typing import cast
13
11
 
14
12
  import yaml
15
13
 
@@ -48,36 +46,3 @@ def load_data(filename: str):
48
46
  pass
49
47
 
50
48
  return {}
51
-
52
-
53
- def _load_plugins(directory: str, package: Optional[str], can_fail=False):
54
- for _, dirnames, filenames in os.walk(directory):
55
- for dirname in dirnames:
56
- if dirname == "__pycache__":
57
- continue
58
-
59
- try:
60
- importlib.import_module(f".{dirname}", package=package)
61
- except ModuleNotFoundError as err:
62
- if not can_fail:
63
- raise err
64
- for filename in filenames:
65
- if filename == "__init__.py":
66
- continue
67
-
68
- try:
69
- importlib.import_module(
70
- f".{os.path.splitext(filename)[0]}", package=package
71
- )
72
- except ModuleNotFoundError as err:
73
- if not can_fail:
74
- raise err
75
- dirnames[:] = []
76
-
77
-
78
- def load_module_plugins(mod: ModuleType, can_fail=False):
79
- spec = mod.__spec__
80
- if not spec:
81
- return
82
- for location in spec.submodule_search_locations: # type: ignore
83
- _load_plugins(location, spec.name, can_fail)
@@ -2,10 +2,12 @@
2
2
  # This code is licensed under MIT license (see LICENSE for details)
3
3
 
4
4
  """
5
- The **proj_flow.base.registry**
5
+ The **proj_flow.base.registry** allows building extension points, with ability
6
+ to register the plugins with a decorator.
6
7
  """
7
8
 
8
9
  import typing
10
+ from collections import OrderedDict
9
11
 
10
12
  T = typing.TypeVar("T")
11
13
  K = typing.TypeVar("K")
@@ -100,6 +102,19 @@ _debug_copies: typing.List[Registry] = []
100
102
  def verbose_info():
101
103
  for registry in _debug_copies:
102
104
  for item in registry.container:
103
- print(
104
- f"-- {registry.name}: adding `{item.__module__}.{item.__class__.__name__}`"
105
- )
105
+ full_name = f"{item.__module__}.{item.__class__.__name__}"
106
+
107
+ kw = OrderedDict()
108
+
109
+ if hasattr(item, "name"):
110
+ kw["name"] = getattr(item, "name")
111
+ elif hasattr(item, "__name__"):
112
+ kw["name"] = getattr(item, "__name__")
113
+
114
+ if hasattr(item, "id"):
115
+ kw["id"] = getattr(item, "id")
116
+
117
+ items = ", ".join([f"{key}={value}" for key, value in kw.items()])
118
+ if len(items) > 0:
119
+ items = f" ({items})"
120
+ print(f"-- {registry.name}: adding `{full_name}`{items}")
proj_flow/cli/__init__.py CHANGED
@@ -10,10 +10,8 @@ The **proj_flow.cli** provides command-line entry for the *Project Flow*.
10
10
  import argparse
11
11
  import os
12
12
  import sys
13
- from pprint import pprint
14
- from typing import Dict, Optional, Tuple
15
13
 
16
- from proj_flow.api import arg, env
14
+ from proj_flow.api import env
17
15
  from proj_flow.cli import argument, finder
18
16
  from proj_flow.flow import steps
19
17
 
@@ -31,7 +29,7 @@ def main():
31
29
  def _change_dir():
32
30
  root = argparse.ArgumentParser(
33
31
  prog="proj-flow",
34
- usage="proj-flow [-h] [--version] [-C [dir]] {command} ...",
32
+ usage="proj-flow [-h] [--version] [-C [dir]] command ...",
35
33
  add_help=False,
36
34
  )
37
35
  root.add_argument("-C", dest="cd", nargs="?")
proj_flow/cli/argument.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # This code is licensed under MIT license (see LICENSE for details)
3
3
 
4
4
  """
5
- The **proj_flow.cli.arguments** provides command-line builders and runners,
5
+ The **proj_flow.cli.argument** provides command-line builders and runners,
6
6
  supporting the functions decorated with :func:`@arg.command()
7
7
  <proj_flow.api.arg.command>`.
8
8
  """
@@ -192,7 +192,7 @@ class Command:
192
192
  if len(self.children):
193
193
  subparsers = parser.add_subparsers(
194
194
  dest=f"command_{level}",
195
- metavar="{command}",
195
+ metavar="command",
196
196
  help="Known command name, see below",
197
197
  )
198
198
  subparsers.parent = parser # type: ignore
@@ -254,7 +254,7 @@ def _argparse_visit_all(
254
254
  parser.shortcuts = shortcut_configs
255
255
 
256
256
  subparsers = parser.add_subparsers(
257
- dest="command", metavar="{command}", help="Known command name, see below"
257
+ dest="command", metavar="command", help="Known command name, see below"
258
258
  )
259
259
 
260
260
  subparsers.parent = parser # type: ignore
@@ -2,7 +2,7 @@
2
2
  # This code is licensed under MIT license (see LICENSE for details)
3
3
 
4
4
  """
5
- The **proj_flow.flow.dependency** verifies availabilty of Step's external tools.
5
+ The **proj_flow.dependency** verifies availabilty of Step's external tools.
6
6
  """
7
7
 
8
8
  import re
@@ -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.ext.cplusplus** defines steps and commands for C++ projects.
6
+ """
7
+
8
+ from . import cmake, conan
9
+
10
+ __all__ = ["cmake", "conan"]
@@ -0,0 +1,12 @@
1
+ # Copyright (c) 2025 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ """
5
+ The **proj_flow.ext.cplusplus.cmake** provides ``"CMake"``, ``"Build"``,
6
+ ``"Pack"`` and ``"Test"`` steps, as well as CMake-specific initialization
7
+ context.
8
+ """
9
+
10
+ from . import context, parser, steps, version
11
+
12
+ __all__ = ["context", "parser", "steps", "version"]
@@ -1,5 +1,10 @@
1
1
  # Copyright (c) 2025 Marcin Zdun
2
2
  # This code is licensed under MIT license (see LICENSE for details)
3
3
 
4
+ """
5
+ The **proj_flow.ext.cplusplus.cmake.__version__** contains only the value of
6
+ expected version of CMake.
7
+ """
8
+
4
9
 
5
10
  CMAKE_VERSION = "3.28"
@@ -2,8 +2,8 @@
2
2
  # This code is licensed under MIT license (see LICENSE for details)
3
3
 
4
4
  """
5
- The **proj_flow.plugins.cmake.context** provides CMake-specific initialization
6
- context.
5
+ The **proj_flow.ext.cplusplus.cmake.context** provides CMake-specific
6
+ initialization context.
7
7
  """
8
8
 
9
9
  import re
@@ -11,7 +11,8 @@ import uuid
11
11
 
12
12
  import chevron
13
13
 
14
- from proj_flow import api, flow
14
+ from proj_flow import api, project
15
+ from proj_flow.project import cplusplus
15
16
 
16
17
  from .__version__ import CMAKE_VERSION
17
18
 
@@ -49,22 +50,23 @@ class CMakeInit(api.init.InitStep):
49
50
  def _list_cmake_types():
50
51
  return api.ctx.move_to_front(
51
52
  "console-application",
52
- sorted(key for key in flow.init.get_internal("cmake").keys() if key),
53
+ sorted(key for key in project.data.get_internal("cmake").keys() if key),
53
54
  )
54
55
 
55
56
 
56
57
  api.init.register_init_step(CMakeInit())
57
- api.ctx.register_init_setting(
58
+
59
+ cplusplus.project.register_init_setting(
58
60
  api.ctx.Setting("PROJECT.TYPE", "CMake project type", _list_cmake_types)
59
61
  )
60
- api.ctx.register_init_setting(
62
+ cplusplus.project.register_init_setting(
61
63
  api.ctx.Setting("cmake", fix="{PROJECT.TYPE$map:cmake}"),
62
64
  api.ctx.Setting("CMAKE_VERSION", value=CMAKE_VERSION),
63
65
  api.ctx.Setting("PROJECT.WIX.UPGRADE_GUID", value=lambda: str(uuid.uuid4())),
64
66
  is_hidden=True,
65
67
  )
66
- api.ctx.register_switch("with_cmake", "Use CMake", True)
67
- api.ctx.register_internal(
68
+ cplusplus.project.register_switch("with_cmake", "Use CMake", True)
69
+ cplusplus.project.register_internal(
68
70
  "cmake",
69
71
  {
70
72
  "": {"cmd": "add_executable", "type": ""},
@@ -2,13 +2,15 @@
2
2
  # This code is licensed under MIT license (see LICENSE for details)
3
3
 
4
4
  """
5
- The **proj_flow.plugins.cmake.parser** contains simple CMake parser.
5
+ The **proj_flow.ext.cplusplus.cmake.parser** contains simple CMake parser.
6
6
  """
7
7
 
8
8
  import os
9
9
  import re
10
10
  from typing import Iterator, List, NamedTuple, Optional
11
11
 
12
+ from proj_flow.api.release import NO_ARG, Arg
13
+
12
14
  TOKENS = [
13
15
  ("COMMENT", r"#.*"),
14
16
  ("STR", r'"[^"]*"'),
@@ -25,39 +27,18 @@ class Token(NamedTuple):
25
27
  offset: int
26
28
 
27
29
 
28
- class Arg(NamedTuple):
29
- value: str
30
- offset: int
31
-
32
-
33
30
  class Command(NamedTuple):
34
31
  name: str
35
32
  args: List[Arg]
36
33
  offset: int
37
34
 
38
35
 
39
- class Project(NamedTuple):
36
+ class CMakeProject(NamedTuple):
40
37
  name: Arg
41
38
  version: Arg
42
39
  stability: Arg
43
40
  description: Arg
44
41
 
45
- def set_version(self, directory: str, next_version: str):
46
- _patch(directory, self.version, next_version)
47
- return ["proj_flow/__init__.py"]
48
-
49
- @property
50
- def ver(self):
51
- return f"{self.version.value}{self.stability.value}"
52
-
53
- @property
54
- def pkg(self):
55
- return f"{self.name.value}-{self.ver}"
56
-
57
- @property
58
- def tag(self):
59
- return f"v{self.ver}"
60
-
61
42
 
62
43
  def _token_stream(text: str) -> Iterator[Token]:
63
44
  tok_regex = "|".join("(?P<%s>%s)" % pair for pair in TOKENS)
@@ -121,10 +102,7 @@ def _patch(directory: str, arg: Arg, value: str):
121
102
  input.write(patched)
122
103
 
123
104
 
124
- NO_ARG = Arg("", -1)
125
-
126
-
127
- def get_project(dirname: str) -> Optional[Project]:
105
+ def get_project(dirname: str) -> Optional[CMakeProject]:
128
106
  try:
129
107
  commands = _cmake(os.path.join(dirname, "CMakeLists.txt"))
130
108
  except FileNotFoundError:
@@ -158,7 +136,7 @@ def get_project(dirname: str) -> Optional[Project]:
158
136
  if version_stability is None:
159
137
  version_stability = NO_ARG
160
138
 
161
- return Project(
139
+ return CMakeProject(
162
140
  name=project_name,
163
141
  version=version,
164
142
  stability=version_stability,