omdev 0.0.0.dev16__tar.gz → 0.0.0.dev18__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.

Potentially problematic release.


This version of omdev might be problematic. Click here for more details.

Files changed (82) hide show
  1. {omdev-0.0.0.dev16/omdev.egg-info → omdev-0.0.0.dev18}/PKG-INFO +2 -2
  2. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/__about__.py +2 -0
  3. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/cmake.py +7 -23
  4. omdev-0.0.0.dev18/omdev/cexts/magic.py +7 -0
  5. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/scan.py +5 -6
  6. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/pyproject/cli.py +9 -7
  7. omdev-0.0.0.dev18/omdev/pyproject/pkg.py +380 -0
  8. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/scripts/pyproject.py +310 -90
  9. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/toml/writer.py +4 -1
  10. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18/omdev.egg-info}/PKG-INFO +2 -2
  11. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev.egg-info/SOURCES.txt +22 -21
  12. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev.egg-info/requires.txt +1 -1
  13. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/pyproject.toml +2 -2
  14. omdev-0.0.0.dev16/omdev/pyproject/pkg.py +0 -227
  15. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/LICENSE +0 -0
  16. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/MANIFEST.in +0 -0
  17. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/README.rst +0 -0
  18. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/__init__.py +0 -0
  19. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/amalg/__init__.py +0 -0
  20. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/amalg/__main__.py +0 -0
  21. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/amalg/amalg.py +0 -0
  22. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/bracepy.py +0 -0
  23. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/__init__.py +0 -0
  24. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/__init__.py +0 -0
  25. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/build_ext.py +0 -0
  26. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/compilers/__init__.py +0 -0
  27. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/compilers/ccompiler.py +0 -0
  28. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/compilers/options.py +0 -0
  29. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/compilers/unixccompiler.py +0 -0
  30. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/dir_util.py +0 -0
  31. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/errors.py +0 -0
  32. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/extension.py +0 -0
  33. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/file_util.py +0 -0
  34. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/modified.py +0 -0
  35. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/spawn.py +0 -0
  36. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/sysconfig.py +0 -0
  37. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/util.py +0 -0
  38. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/_distutils/version.py +0 -0
  39. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/build.py +0 -0
  40. {omdev-0.0.0.dev16/omdev/exts → omdev-0.0.0.dev18/omdev/cexts}/importhook.py +0 -0
  41. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/classdot.py +0 -0
  42. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/cmake.py +0 -0
  43. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/findimports.py +0 -0
  44. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/findmagic.py +0 -0
  45. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/interp/__init__.py +0 -0
  46. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/interp/__main__.py +0 -0
  47. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/interp/cli.py +0 -0
  48. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/interp/inspect.py +0 -0
  49. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/interp/providers.py +0 -0
  50. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/interp/pyenv.py +0 -0
  51. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/interp/resolvers.py +0 -0
  52. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/interp/standalone.py +0 -0
  53. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/interp/system.py +0 -0
  54. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/interp/types.py +0 -0
  55. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/mypy/__init__.py +0 -0
  56. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/mypy/debug.py +0 -0
  57. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/precheck/__init__.py +0 -0
  58. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/precheck/__main__.py +0 -0
  59. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/precheck/precheck.py +0 -0
  60. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/pyproject/__init__.py +0 -0
  61. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/pyproject/__main__.py +0 -0
  62. /omdev-0.0.0.dev16/omdev/pyproject/ext.py → /omdev-0.0.0.dev18/omdev/pyproject/cexts.py +0 -0
  63. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/pyproject/configs.py +0 -0
  64. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/scripts/__init__.py +0 -0
  65. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/scripts/execrss.py +0 -0
  66. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/scripts/interp.py +0 -0
  67. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/tokens.py +0 -0
  68. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/toml/__init__.py +0 -0
  69. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/toml/parser.py +0 -0
  70. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/tools/__init__.py +0 -0
  71. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/tools/dockertools.py +0 -0
  72. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/tools/gittools.py +0 -0
  73. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/tools/revisions.py +0 -0
  74. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/tools/sqlrepl.py +0 -0
  75. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/tools/traceimport.py +0 -0
  76. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/versioning/__init__.py +0 -0
  77. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/versioning/specifiers.py +0 -0
  78. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/versioning/versions.py +0 -0
  79. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev/wheelfile.py +0 -0
  80. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev.egg-info/dependency_links.txt +0 -0
  81. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/omdev.egg-info/top_level.txt +0 -0
  82. {omdev-0.0.0.dev16 → omdev-0.0.0.dev18}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omdev
3
- Version: 0.0.0.dev16
3
+ Version: 0.0.0.dev18
4
4
  Summary: omdev
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,7 +12,7 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Operating System :: POSIX
13
13
  Requires-Python: ~=3.12
14
14
  License-File: LICENSE
15
- Requires-Dist: omlish==0.0.0.dev16
15
+ Requires-Dist: omlish==0.0.0.dev18
16
16
  Provides-Extra: all
17
17
  Requires-Dist: pycparser~=2.22; extra == "all"
18
18
  Requires-Dist: cffi~=1.17; extra == "all"
@@ -33,6 +33,8 @@ class Project(ProjectBase):
33
33
 
34
34
 
35
35
  class Setuptools(SetuptoolsBase):
36
+ cexts = True
37
+
36
38
  find_packages = {
37
39
  'include': [Project.name, f'{Project.name}.*'],
38
40
  'exclude': [*SetuptoolsBase.find_packages['exclude']],
@@ -1,6 +1,7 @@
1
1
  """
2
2
  FIXME:
3
3
  - debug tables don't handle symlinks
4
+ - use relapths in cml.txt
4
5
 
5
6
  TODO:
6
7
  - symlink headers, included src files (hamt_impl, ...)
@@ -37,6 +38,7 @@ from omlish import logs
37
38
 
38
39
  from .. import cmake
39
40
  from .. import findmagic
41
+ from .magic import CextMagic
40
42
 
41
43
 
42
44
  log = logging.getLogger(__name__)
@@ -45,12 +47,6 @@ log = logging.getLogger(__name__)
45
47
  ##
46
48
 
47
49
 
48
- MAGIC = '@omdev-ext'
49
- MAGIC_COMMENT = f'// {MAGIC}'
50
-
51
- FILE_EXTENSIONS = ('c', 'cc', 'cpp')
52
-
53
-
54
50
  def _sep_str_grps(*ls: ta.Sequence[str]) -> list[str]:
55
51
  o = []
56
52
  for i, l in enumerate(ls):
@@ -87,7 +83,7 @@ class CmakeProjectGen:
87
83
 
88
84
  @lang.cached_function
89
85
  def cmake_dir(self) -> str:
90
- cmake_dir = os.path.join(self.prj_root, 'cmake')
86
+ cmake_dir = os.path.join(self.prj_root, 'cmake', self.prj_name())
91
87
  if os.path.exists(cmake_dir):
92
88
  for e in os.listdir(cmake_dir):
93
89
  if e == '.idea':
@@ -98,7 +94,7 @@ class CmakeProjectGen:
98
94
  else:
99
95
  shutil.rmtree(ep)
100
96
  else:
101
- os.mkdir(cmake_dir)
97
+ os.makedirs(cmake_dir)
102
98
  return cmake_dir
103
99
 
104
100
  #
@@ -109,17 +105,6 @@ class CmakeProjectGen:
109
105
 
110
106
  #
111
107
 
112
- def write_idea_name(self) -> None:
113
- idea_dir = os.path.join(self.cmake_dir(), '.idea')
114
- if not os.path.isdir(idea_dir):
115
- os.mkdir(idea_dir)
116
- idea_name_file = os.path.join(idea_dir, '.name')
117
- if not os.path.isfile(idea_name_file):
118
- with open(idea_name_file, 'w') as f:
119
- f.write(self.prj_name())
120
-
121
- #
122
-
123
108
  @dc.dataclass(frozen=True, kw_only=True)
124
109
  class PyInfo:
125
110
  venv_exe: str
@@ -153,8 +138,8 @@ class CmakeProjectGen:
153
138
  out.extend(
154
139
  findmagic.find_magic(
155
140
  [e],
156
- [MAGIC_COMMENT],
157
- FILE_EXTENSIONS,
141
+ [CextMagic.MAGIC_COMMENT],
142
+ CextMagic.FILE_EXTENSIONS,
158
143
  ),
159
144
  )
160
145
  else:
@@ -224,7 +209,7 @@ class CmakeProjectGen:
224
209
  [
225
210
  ' '.join([
226
211
  'COMMAND ${CMAKE_COMMAND} -E ',
227
- f'copy $<TARGET_FILE_NAME:{ext_name}> ../../{os.path.dirname(ext_src)}/{so_name}',
212
+ f'copy $<TARGET_FILE_NAME:{ext_name}> ../../../{os.path.dirname(ext_src)}/{so_name}',
228
213
  ]),
229
214
  'COMMAND_EXPAND_LISTS',
230
215
  ],
@@ -311,7 +296,6 @@ class CmakeProjectGen:
311
296
 
312
297
  self.cmake_dir()
313
298
  self.write_git_ignore()
314
- self.write_idea_name()
315
299
 
316
300
  out = io.StringIO()
317
301
  clg = self._CmakeListsGen(self, out)
@@ -0,0 +1,7 @@
1
+ # @omlish-lite
2
+
3
+ class CextMagic:
4
+ MAGIC = '@omdev-cext'
5
+ MAGIC_COMMENT = f'// {MAGIC}'
6
+
7
+ FILE_EXTENSIONS = ('c', 'cc', 'cpp')
@@ -5,18 +5,17 @@ import typing as ta
5
5
 
6
6
  from omlish import logs
7
7
 
8
-
9
- log = logging.getLogger(__name__)
8
+ from .magic import CextMagic
10
9
 
11
10
 
12
- SCAN_COMMENT = '// @omdev-ext'
11
+ log = logging.getLogger(__name__)
13
12
 
14
13
 
15
14
  def scan_one(
16
15
  input_path: str,
17
16
  **kwargs: ta.Any,
18
17
  ) -> None:
19
- if not any(input_path.endswith(fx) for fx in ('.c', '.cc', '.cpp')):
18
+ if not any(input_path.endswith('.' + fx) for fx in CextMagic.FILE_EXTENSIONS):
20
19
  return
21
20
 
22
21
  with open(input_path, 'rb') as f:
@@ -27,9 +26,9 @@ def scan_one(
27
26
  except UnicodeDecodeError:
28
27
  return
29
28
 
30
- sls = [l for l in src.splitlines() if l.startswith(SCAN_COMMENT)]
29
+ sls = [l for l in src.splitlines() if l.startswith(CextMagic.MAGIC_COMMENT)]
31
30
  for sl in sls:
32
- sas = sl[len(SCAN_COMMENT):].split() # noqa
31
+ sas = sl[len(CextMagic.MAGIC_COMMENT):].split() # noqa
33
32
 
34
33
  log.info('Found ext: %s', input_path)
35
34
 
@@ -301,10 +301,10 @@ def _pkg_cmd(args) -> None:
301
301
  raise Exception('must specify command')
302
302
 
303
303
  elif cmd == 'gen':
304
- build_root = os.path.join('.pkg')
304
+ pkgs_root = os.path.join('.pkg')
305
305
 
306
- if os.path.exists(build_root):
307
- shutil.rmtree(build_root)
306
+ if os.path.exists(pkgs_root):
307
+ shutil.rmtree(pkgs_root)
308
308
 
309
309
  build_output_dir = 'dist'
310
310
  run_build = bool(args.build)
@@ -319,11 +319,13 @@ def _pkg_cmd(args) -> None:
319
319
  ex.submit(functools.partial(
320
320
  PyprojectPackageGenerator(
321
321
  dir_name,
322
- build_root,
322
+ pkgs_root,
323
323
  ).gen,
324
- run_build=run_build,
325
- build_output_dir=build_output_dir,
326
- add_revision=add_revision,
324
+ PyprojectPackageGenerator.GenOpts(
325
+ run_build=run_build,
326
+ build_output_dir=build_output_dir,
327
+ add_revision=add_revision,
328
+ ),
327
329
  ))
328
330
  for dir_name in run.cfg().pkgs
329
331
  ]
@@ -0,0 +1,380 @@
1
+ """
2
+ TODO:
3
+ - ext scanning
4
+ - __revision__
5
+ - entry_points
6
+
7
+ https://setuptools.pypa.io/en/latest/references/keywords.html
8
+ https://packaging.python.org/en/latest/specifications/pyproject-toml
9
+
10
+ How to build a C extension in keeping with PEP 517, i.e. with pyproject.toml instead of setup.py?
11
+ https://stackoverflow.com/a/66479252
12
+
13
+ https://github.com/pypa/sampleproject/blob/db5806e0a3204034c51b1c00dde7d5eb3fa2532e/setup.py
14
+
15
+ https://pip.pypa.io/en/stable/cli/pip_install/#vcs-support
16
+ vcs+protocol://repo_url/#egg=pkg&subdirectory=pkg_dir
17
+ 'git+https://github.com/wrmsr/omlish@master#subdirectory=.pip/omlish'
18
+ """
19
+ # ruff: noqa: UP006 UP007
20
+ import abc
21
+ import dataclasses as dc
22
+ import importlib
23
+ import os.path
24
+ import shutil
25
+ import subprocess
26
+ import sys
27
+ import types
28
+ import typing as ta
29
+
30
+ from omlish.lite.cached import cached_nullary
31
+ from omlish.lite.logs import log
32
+
33
+ from ..cexts.magic import CextMagic
34
+ from ..findmagic import find_magic
35
+ from ..toml.writer import TomlWriter
36
+ from ..tools.revisions import GitRevisionAdder
37
+
38
+
39
+ #
40
+
41
+
42
+ class BasePyprojectPackageGenerator(abc.ABC):
43
+ def __init__(
44
+ self,
45
+ dir_name: str,
46
+ pkgs_root: str,
47
+ *,
48
+ pkg_suffix: str = '',
49
+ ) -> None:
50
+ super().__init__()
51
+ self._dir_name = dir_name
52
+ self._pkgs_root = pkgs_root
53
+ self._pkg_suffix = pkg_suffix
54
+
55
+ #
56
+
57
+ @cached_nullary
58
+ def about(self) -> types.ModuleType:
59
+ return importlib.import_module(f'{self._dir_name}.__about__')
60
+
61
+ #
62
+
63
+ @cached_nullary
64
+ def _pkg_dir(self) -> str:
65
+ pkg_dir: str = os.path.join(self._pkgs_root, self._dir_name + self._pkg_suffix)
66
+ if os.path.isdir(pkg_dir):
67
+ shutil.rmtree(pkg_dir)
68
+ os.makedirs(pkg_dir)
69
+ return pkg_dir
70
+
71
+ #
72
+
73
+ _GIT_IGNORE: ta.Sequence[str] = [
74
+ '/*.egg-info/',
75
+ '/dist',
76
+ ]
77
+
78
+ def _write_git_ignore(self) -> None:
79
+ with open(os.path.join(self._pkg_dir(), '.gitignore'), 'w') as f:
80
+ f.write('\n'.join(self._GIT_IGNORE))
81
+
82
+ #
83
+
84
+ def _symlink_source_dir(self) -> None:
85
+ os.symlink(
86
+ os.path.relpath(self._dir_name, self._pkg_dir()),
87
+ os.path.join(self._pkg_dir(), self._dir_name),
88
+ )
89
+
90
+ #
91
+
92
+ @cached_nullary
93
+ def project_cls(self) -> type:
94
+ return self.about().Project
95
+
96
+ @cached_nullary
97
+ def setuptools_cls(self) -> type:
98
+ return self.about().Setuptools
99
+
100
+ @staticmethod
101
+ def _build_cls_dct(cls: type) -> ta.Dict[str, ta.Any]: # noqa
102
+ dct = {}
103
+ for b in reversed(cls.__mro__):
104
+ for k, v in b.__dict__.items():
105
+ if k.startswith('_'):
106
+ continue
107
+ dct[k] = v
108
+ return dct
109
+
110
+ @staticmethod
111
+ def _move_dict_key(
112
+ sd: ta.Dict[str, ta.Any],
113
+ sk: str,
114
+ dd: ta.Dict[str, ta.Any],
115
+ dk: str,
116
+ ) -> None:
117
+ if sk in sd:
118
+ dd[dk] = sd.pop(sk)
119
+
120
+ @dc.dataclass(frozen=True)
121
+ class Specs:
122
+ pyproject: ta.Dict[str, ta.Any]
123
+ setuptools: ta.Dict[str, ta.Any]
124
+
125
+ def build_specs(self) -> Specs:
126
+ return self.Specs(
127
+ self._build_cls_dct(self.project_cls()),
128
+ self._build_cls_dct(self.setuptools_cls()),
129
+ )
130
+
131
+ #
132
+
133
+ @abc.abstractmethod
134
+ def _write_file_contents(self) -> None:
135
+ raise NotImplementedError
136
+
137
+ #
138
+
139
+ _STANDARD_FILES: ta.Sequence[str] = [
140
+ 'LICENSE',
141
+ 'README.rst',
142
+ ]
143
+
144
+ def _symlink_standard_files(self) -> None:
145
+ for fn in self._STANDARD_FILES:
146
+ if os.path.exists(fn):
147
+ os.symlink(os.path.relpath(fn, self._pkg_dir()), os.path.join(self._pkg_dir(), fn))
148
+
149
+ #
150
+
151
+ def _run_build(
152
+ self,
153
+ build_output_dir: ta.Optional[str] = None,
154
+ *,
155
+ add_revision: bool = False,
156
+ ) -> None:
157
+ subprocess.check_call(
158
+ [
159
+ sys.executable,
160
+ '-m',
161
+ 'build',
162
+ ],
163
+ cwd=self._pkg_dir(),
164
+ )
165
+
166
+ dist_dir = os.path.join(self._pkg_dir(), 'dist')
167
+
168
+ if add_revision:
169
+ GitRevisionAdder().add_to(dist_dir)
170
+
171
+ if build_output_dir is not None:
172
+ for fn in os.listdir(dist_dir):
173
+ shutil.copyfile(os.path.join(dist_dir, fn), os.path.join(build_output_dir, fn))
174
+
175
+ #
176
+
177
+ @dc.dataclass(frozen=True)
178
+ class GenOpts:
179
+ run_build: bool = False
180
+ build_output_dir: ta.Optional[str] = None
181
+ add_revision: bool = False
182
+
183
+ def gen(self, opts: GenOpts = GenOpts()) -> str:
184
+ log.info('Generating pyproject package: %s -> %s (%s)', self._dir_name, self._pkgs_root, self._pkg_suffix)
185
+
186
+ self._pkg_dir()
187
+ self._write_git_ignore()
188
+ self._symlink_source_dir()
189
+ self._write_file_contents()
190
+ self._symlink_standard_files()
191
+
192
+ if opts.run_build:
193
+ self._run_build(
194
+ opts.build_output_dir,
195
+ add_revision=opts.add_revision,
196
+ )
197
+
198
+ return self._pkg_dir()
199
+
200
+
201
+ #
202
+
203
+
204
+ class PyprojectPackageGenerator(BasePyprojectPackageGenerator):
205
+
206
+ #
207
+
208
+ @dc.dataclass(frozen=True)
209
+ class FileContents:
210
+ pyproject_dct: ta.Mapping[str, ta.Any]
211
+ manifest_in: ta.Optional[ta.Sequence[str]]
212
+
213
+ @cached_nullary
214
+ def file_contents(self) -> FileContents:
215
+ specs = self.build_specs()
216
+
217
+ #
218
+
219
+ pyp_dct = {}
220
+
221
+ pyp_dct['build-system'] = {
222
+ 'requires': ['setuptools'],
223
+ 'build-backend': 'setuptools.build_meta',
224
+ }
225
+
226
+ prj = specs.pyproject
227
+ prj['name'] += self._pkg_suffix
228
+
229
+ pyp_dct['project'] = prj
230
+
231
+ self._move_dict_key(prj, 'optional_dependencies', pyp_dct, extrask := 'project.optional-dependencies')
232
+ if (extras := pyp_dct.get(extrask)):
233
+ pyp_dct[extrask] = {
234
+ 'all': [
235
+ e
236
+ for lst in extras.values()
237
+ for e in lst
238
+ ],
239
+ **extras,
240
+ }
241
+
242
+ #
243
+
244
+ st = specs.setuptools
245
+ pyp_dct['tool.setuptools'] = st
246
+
247
+ st.pop('cexts', None)
248
+
249
+ self._move_dict_key(st, 'find_packages', pyp_dct, 'tool.setuptools.packages.find')
250
+
251
+ mani_in = st.pop('manifest_in', None)
252
+
253
+ #
254
+
255
+ return self.FileContents(
256
+ pyp_dct,
257
+ mani_in,
258
+ )
259
+
260
+ def _write_file_contents(self) -> None:
261
+ fc = self.file_contents()
262
+
263
+ with open(os.path.join(self._pkg_dir(), 'pyproject.toml'), 'w') as f:
264
+ TomlWriter(f).write_root(fc.pyproject_dct)
265
+
266
+ if fc.manifest_in:
267
+ with open(os.path.join(self._pkg_dir(), 'MANIFEST.in'), 'w') as f:
268
+ f.write('\n'.join(fc.manifest_in)) # noqa
269
+
270
+ #
271
+
272
+ def gen(self, opts: BasePyprojectPackageGenerator.GenOpts = BasePyprojectPackageGenerator.GenOpts()) -> str:
273
+ ret = super().gen(opts)
274
+
275
+ if self.build_specs().setuptools.get('cexts'):
276
+ _PyprojectCextPackageGenerator(
277
+ self._dir_name,
278
+ self._pkgs_root,
279
+ pkg_suffix='-cext',
280
+ ).gen(opts)
281
+
282
+ return ret
283
+
284
+
285
+ #
286
+
287
+
288
+ class _PyprojectCextPackageGenerator(BasePyprojectPackageGenerator):
289
+
290
+ #
291
+
292
+ @cached_nullary
293
+ def find_cext_srcs(self) -> ta.Sequence[str]:
294
+ return sorted(find_magic(
295
+ [self._dir_name],
296
+ [CextMagic.MAGIC_COMMENT],
297
+ CextMagic.FILE_EXTENSIONS,
298
+ ))
299
+
300
+ #
301
+
302
+ @dc.dataclass(frozen=True)
303
+ class FileContents:
304
+ pyproject_dct: ta.Mapping[str, ta.Any]
305
+ setup_py: str
306
+
307
+ @cached_nullary
308
+ def file_contents(self) -> FileContents:
309
+ specs = self.build_specs()
310
+
311
+ #
312
+
313
+ pyp_dct = {}
314
+
315
+ pyp_dct['build-system'] = {
316
+ 'requires': ['setuptools'],
317
+ 'build-backend': 'setuptools.build_meta',
318
+ }
319
+
320
+ prj = specs.pyproject
321
+ prj['dependencies'] = [f'{prj["name"]} == {prj["version"]}']
322
+ prj['name'] += self._pkg_suffix
323
+ prj.pop('optional_dependencies', None)
324
+
325
+ pyp_dct['project'] = prj
326
+
327
+ #
328
+
329
+ st = specs.setuptools
330
+ pyp_dct['tool.setuptools'] = st
331
+
332
+ st.pop('cexts', None)
333
+ st.pop('find_packages', None)
334
+ st.pop('manifest_in', None)
335
+
336
+ pyp_dct['tool.setuptools.packages.find'] = {
337
+ 'include': [],
338
+ }
339
+
340
+ #
341
+
342
+ ext_lines = []
343
+
344
+ for ext_src in self.find_cext_srcs():
345
+ ext_name = ext_src.rpartition('.')[0].replace(os.sep, '.')
346
+ ext_lines.extend([
347
+ 'st.Extension(',
348
+ f" name='{ext_name}',",
349
+ f" sources=['{ext_src}'],",
350
+ " extra_compile_args=['-std=c++20'],",
351
+ '),',
352
+ ])
353
+
354
+ src = '\n'.join([
355
+ 'import setuptools as st',
356
+ '',
357
+ '',
358
+ 'st.setup(',
359
+ ' ext_modules=[',
360
+ *[' ' + l for l in ext_lines],
361
+ ' ]',
362
+ ')',
363
+ '',
364
+ ])
365
+
366
+ #
367
+
368
+ return self.FileContents(
369
+ pyp_dct,
370
+ src,
371
+ )
372
+
373
+ def _write_file_contents(self) -> None:
374
+ fc = self.file_contents()
375
+
376
+ with open(os.path.join(self._pkg_dir(), 'pyproject.toml'), 'w') as f:
377
+ TomlWriter(f).write_root(fc.pyproject_dct)
378
+
379
+ with open(os.path.join(self._pkg_dir(), 'setup.py'), 'w') as f:
380
+ f.write(fc.setup_py)