partis-pyproj 0.1.4__py3-none-any.whl → 0.1.6__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 (42) hide show
  1. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/__init__.py +9 -1
  2. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/_legacy_setup.py +11 -11
  3. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/_nonprintable.py +4 -3
  4. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/backend.py +44 -37
  5. partis_pyproj-0.1.6.data/purelib/partis/pyproj/builder/builder.py +351 -0
  6. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/builder/cargo.py +2 -2
  7. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/builder/cmake.py +9 -15
  8. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/builder/meson.py +5 -13
  9. partis_pyproj-0.1.6.data/purelib/partis/pyproj/builder/process.py +42 -0
  10. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/dist_file/__init__.py +1 -1
  11. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/dist_file/dist_base.py +75 -86
  12. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/dist_file/dist_binary.py +6 -24
  13. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/dist_file/dist_copy.py +7 -18
  14. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/dist_file/dist_source.py +4 -21
  15. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/dist_file/dist_targz.py +5 -12
  16. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/dist_file/dist_zip.py +5 -14
  17. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/file.py +2 -1
  18. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/legacy.py +3 -2
  19. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/load_module.py +7 -6
  20. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/norms.py +35 -31
  21. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/path/__init__.py +2 -1
  22. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/path/match.py +42 -35
  23. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/path/pattern.py +60 -54
  24. partis_pyproj-0.1.6.data/purelib/partis/pyproj/path/utils.py +94 -0
  25. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/pep.py +36 -35
  26. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/pkginfo.py +7 -16
  27. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/pptoml.py +125 -120
  28. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/pyproj.py +47 -36
  29. partis_pyproj-0.1.6.data/purelib/partis/pyproj/template.py +229 -0
  30. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/validate.py +273 -268
  31. partis_pyproj-0.1.6.dist-info/METADATA +500 -0
  32. partis_pyproj-0.1.6.dist-info/RECORD +37 -0
  33. partis_pyproj-0.1.4.data/purelib/partis/pyproj/builder/builder.py +0 -267
  34. partis_pyproj-0.1.4.data/purelib/partis/pyproj/builder/process.py +0 -75
  35. partis_pyproj-0.1.4.data/purelib/partis/pyproj/path/utils.py +0 -40
  36. partis_pyproj-0.1.4.dist-info/METADATA +0 -51
  37. partis_pyproj-0.1.4.dist-info/RECORD +0 -36
  38. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/builder/__init__.py +0 -0
  39. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.6.dist-info}/LICENSE.txt +0 -0
  40. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.6.dist-info}/WHEEL +0 -0
  41. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.6.dist-info}/entry_points.txt +0 -0
  42. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.6.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
1
 
2
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2
+ #===============================================================================
3
3
  from .validate import (
4
4
  ValidationError,
5
5
  ValidationWarning,
@@ -10,6 +10,7 @@ from .validate import (
10
10
  as_list )
11
11
 
12
12
  from .norms import (
13
+ marker_evaluated,
13
14
  scalar,
14
15
  scalar_list,
15
16
  empty_str,
@@ -59,6 +60,13 @@ from .path import (
59
60
  combine_ignore_patterns,
60
61
  contains )
61
62
 
63
+ from .template import (
64
+ Template,
65
+ Namespace,
66
+ template_substitute,
67
+ TemplateError,
68
+ NamespaceError)
69
+
62
70
  from .dist_file import (
63
71
  dist_base,
64
72
  dist_zip,
@@ -15,7 +15,7 @@ import tempfile
15
15
  from argparse import RawTextHelpFormatter
16
16
  logger = logging.getLogger(__name__)
17
17
 
18
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
18
+ #===============================================================================
19
19
  def egg_info( args ):
20
20
 
21
21
  logger.warning(
@@ -26,13 +26,13 @@ def egg_info( args ):
26
26
  if not dir.exists():
27
27
  dir.mkdir(parents=True, exist_ok = True)
28
28
 
29
- with open(dir.joinpath('PKG-INFO'), 'wb' ) as fp:
29
+ with open(dir.joinpath('PKG-INFO'), 'wb' ) as fp:
30
30
  fp.write( PKG_INFO )
31
31
 
32
- with open( dir.joinpath('setup_requires.txt'), 'wb' ) as fp:
32
+ with open( dir.joinpath('setup_requires.txt'), 'wb' ) as fp:
33
33
  fp.write( b'' )
34
34
 
35
- with open( dir.joinpath('requires.txt'), 'wb' ) as fp:
35
+ with open( dir.joinpath('requires.txt'), 'wb' ) as fp:
36
36
  fp.write( REQUIRES )
37
37
 
38
38
  with open( dir.joinpath('SOURCES.txt'), 'wb' ) as fp:
@@ -50,7 +50,7 @@ def egg_info( args ):
50
50
  with open( dir.joinpath('not-zip-safe'), 'wb' ) as fp:
51
51
  fp.write( b'' )
52
52
 
53
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
53
+ #===============================================================================
54
54
  def bdist_wheel( args ):
55
55
 
56
56
  logger.warning(
@@ -63,7 +63,7 @@ def bdist_wheel( args ):
63
63
  backend.build_wheel(
64
64
  wheel_directory = args.dist_dir or args.bdist_dir or '.' )
65
65
 
66
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
66
+ #===============================================================================
67
67
  def install( args ):
68
68
 
69
69
  logger.warning(
@@ -91,13 +91,13 @@ def install( args ):
91
91
  '-m',
92
92
  'pip',
93
93
  'install',
94
- tmpdir.joinpath(wheel_name) ])
94
+ tmpdir.joinpath(wheel_name) ])
95
95
 
96
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
96
+ #===============================================================================
97
97
  def dummy( args ):
98
98
  pass
99
99
 
100
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
100
+ #===============================================================================
101
101
  def main():
102
102
 
103
103
  logging.basicConfig(
@@ -185,7 +185,7 @@ def main():
185
185
  args.func( args )
186
186
 
187
187
 
188
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
188
+ #===============================================================================
189
189
  # NOTE: these are templated literal values substituded by the backend when
190
190
  # building the source distribution
191
191
 
@@ -205,7 +205,7 @@ TOP_LEVEL = {top_level}
205
205
 
206
206
  ENTRY_POINTS = {entry_points}
207
207
 
208
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
208
+ #===============================================================================
209
209
 
210
210
  if __name__ == "__main__":
211
211
  exit( main() )
@@ -1,7 +1,8 @@
1
+ from __future__ import annotations
1
2
  import sys
2
3
  import re
3
4
 
4
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5
+ #===============================================================================
5
6
  def _gen_nonprintable():
6
7
  test = ''
7
8
 
@@ -28,7 +29,7 @@ def _gen_nonprintable():
28
29
 
29
30
  return ns, test
30
31
 
31
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
32
+ #===============================================================================
32
33
  def gen_nonprintable():
33
34
  """Method used to generate a regex for matchiing all non-printable unicode
34
35
  characters, except for newlines '\\n' and tabs '\\t'.
@@ -79,6 +80,6 @@ def gen_nonprintable():
79
80
 
80
81
  return nonprintable
81
82
 
82
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
83
+ #===============================================================================
83
84
  if __name__ == '__main__':
84
85
  print( gen_nonprintable() )
@@ -1,8 +1,13 @@
1
+ from __future__ import annotations
1
2
  import os
2
3
  import os.path as osp
3
4
  import sys
4
5
  import shutil
5
6
  import logging
7
+ from logging import (
8
+ basicConfig,
9
+ getLogger,
10
+ Logger)
6
11
  import tempfile
7
12
  import re
8
13
 
@@ -24,34 +29,18 @@ from . import (
24
29
  dist_binary_wheel,
25
30
  dist_source_targz )
26
31
 
27
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
28
- class UnsupportedOperation( Exception ):
29
- """
30
- Note
31
- ----
32
- If the backend cannot produce an dist because a dependency is missing,
33
- or for another well understood reason, it should raise an exception of a
34
- specific type which it makes available as UnsupportedOperation on the
35
- backend object.
36
-
37
- See Also
38
- --------
39
- * https://www.python.org/dev/peps/pep-0517/
40
- """
41
- pass
42
-
43
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
32
+ #===============================================================================
44
33
  def backend_init(
45
- root = '.',
46
- config_settings = None,
47
- logger = None ):
34
+ root: str|Path = '',
35
+ config_settings: dict|None = None,
36
+ logger: Logger|None = None ):
48
37
  """Called to inialialize the backend upon a call to one of the hooks
49
38
 
50
39
  Parameters
51
40
  ----------
52
- root : str | pathlib.Path
41
+ root :
53
42
  Directory containing 'pyproject.toml'
54
- logger : :class:`logging.Logger`
43
+ logger :
55
44
  Logger to use
56
45
 
57
46
  Returns
@@ -59,28 +48,30 @@ def backend_init(
59
48
  PyProjBase
60
49
  """
61
50
 
62
- logger = logger or logging.getLogger( __name__ )
63
-
64
- pyproj = PyProjBase(
65
- root = root,
66
- config_settings = config_settings,
67
- logger = logger )
68
-
69
51
  # NOTE: this is mainly used for debugging, since front-ends don't seem to have
70
52
  # an option to set logging level for the backend.
71
- root = logging.getLogger()
72
- if not root.handlers:
73
- logging.basicConfig(
53
+ root_logger = getLogger()
54
+
55
+ if not root_logger.handlers:
56
+ basicConfig(
74
57
  level = logging.INFO,
75
58
  format = "{message}",
76
59
  style = "{" )
77
60
 
61
+ root = Path(root)
62
+ logger = logger or getLogger( __name__ )
63
+
64
+ pyproj = PyProjBase(
65
+ root = root,
66
+ config_settings = config_settings,
67
+ logger = logger )
68
+
78
69
  return pyproj
79
70
 
80
71
 
81
72
  #-----------------------------------------------------------------------------
82
73
  def get_requires_for_build_sdist(
83
- config_settings = None ):
74
+ config_settings: dict|None = None ):
84
75
  """
85
76
  Note
86
77
  ----
@@ -99,7 +90,7 @@ def get_requires_for_build_sdist(
99
90
  #-----------------------------------------------------------------------------
100
91
  def build_sdist(
101
92
  dist_directory,
102
- config_settings = None ):
93
+ config_settings: dict|None = None ):
103
94
  """
104
95
  Note
105
96
  ----
@@ -130,7 +121,7 @@ def build_sdist(
130
121
 
131
122
  #-----------------------------------------------------------------------------
132
123
  def get_requires_for_build_wheel(
133
- config_settings = None ):
124
+ config_settings: dict|None = None ):
134
125
  """
135
126
  Note
136
127
  ----
@@ -169,7 +160,7 @@ def get_requires_for_build_wheel(
169
160
  #-----------------------------------------------------------------------------
170
161
  def prepare_metadata_for_build_wheel(
171
162
  metadata_directory,
172
- config_settings = None ):
163
+ config_settings: dict|None = None ):
173
164
  """
174
165
  Note
175
166
  ----
@@ -204,7 +195,7 @@ def prepare_metadata_for_build_wheel(
204
195
  #-----------------------------------------------------------------------------
205
196
  def build_wheel(
206
197
  wheel_directory,
207
- config_settings = None,
198
+ config_settings: dict|None = None,
208
199
  metadata_directory = None ):
209
200
  """
210
201
  Note
@@ -255,3 +246,19 @@ def build_wheel(
255
246
  # config_settings = None,
256
247
  # metadata_directory = None ):
257
248
  # pass
249
+
250
+ #===============================================================================
251
+ class UnsupportedOperation( Exception ):
252
+ """
253
+ Note
254
+ ----
255
+ If the backend cannot produce an dist because a dependency is missing,
256
+ or for another well understood reason, it should raise an exception of a
257
+ specific type which it makes available as UnsupportedOperation on the
258
+ backend object.
259
+
260
+ See Also
261
+ --------
262
+ * https://www.python.org/dev/peps/pep-0517/
263
+ """
264
+ pass
@@ -0,0 +1,351 @@
1
+ from __future__ import annotations
2
+ import os
3
+ import os.path as osp
4
+ import sys
5
+ import sysconfig
6
+ import re
7
+ from copy import copy
8
+ import shutil
9
+ import subprocess
10
+ from logging import Logger
11
+ from pathlib import Path
12
+
13
+ from ..file import tail
14
+ from ..validate import (
15
+ validating,
16
+ ValidationError,
17
+ ValidPathError,
18
+ FileOutsideRootError )
19
+
20
+ from ..load_module import EntryPoint
21
+
22
+ from ..path import (
23
+ subdir,
24
+ resolve)
25
+
26
+ from ..template import (
27
+ template_substitute,
28
+ Namespace)
29
+ from ..pptoml import pyproj_targets
30
+
31
+ ERROR_REC = re.compile(r"error:", re.I)
32
+
33
+ #===============================================================================
34
+ class BuildCommandError(ValidationError):
35
+ pass
36
+
37
+ #===============================================================================
38
+ class Builder:
39
+ """Run build setup, compile, install commands
40
+
41
+ Parameters
42
+ ----------
43
+ root:
44
+ Path to root project directory
45
+ targets:
46
+ logger:
47
+
48
+ """
49
+ #-----------------------------------------------------------------------------
50
+ def __init__(self,
51
+ pyproj,
52
+ root: str | Path,
53
+ targets: pyproj_targets,
54
+ logger: Logger):
55
+
56
+ root = resolve(Path(root))
57
+
58
+ self.pyproj = pyproj
59
+ self.root = root
60
+ # isolate (shallow) changes to targets
61
+ self.targets = [copy(v) for v in targets]
62
+ self.clean_dirs = [False]*len(self.targets)
63
+ self.logger = logger
64
+ self.namespace = Namespace({
65
+ 'root': root,
66
+ 'pptoml': pyproj.pptoml,
67
+ 'project': pyproj.project,
68
+ 'pyproj': pyproj.pyproj,
69
+ 'config_settings': pyproj.config_settings,
70
+ 'targets': targets,
71
+ 'env': os.environ,
72
+ 'config_vars': sysconfig.get_config_vars()},
73
+ root=root)
74
+
75
+ #-----------------------------------------------------------------------------
76
+ def __enter__(self):
77
+ return self
78
+
79
+ #-----------------------------------------------------------------------------
80
+ def __exit__(self, type, value, traceback):
81
+ self.build_clean()
82
+
83
+ # do not handle any exceptions here
84
+ return False
85
+
86
+ #-----------------------------------------------------------------------------
87
+ def build_targets(self):
88
+ for i, target in enumerate(self.targets):
89
+ if not target.enabled:
90
+ self.logger.info(f"Skipping targets[{i}], disabled for environment markers")
91
+ continue
92
+
93
+ # each target isolated (shallow) changes to namespace
94
+ namespace = copy(self.namespace)
95
+
96
+ # check paths
97
+ for k in ('work_dir', 'src_dir', 'build_dir', 'prefix'):
98
+ with validating(key = f"tool.pyproj.targets[{i}].{k}"):
99
+ rel_path = target[k]
100
+ rel_path = template_substitute(rel_path, namespace)
101
+
102
+ if rel_path.is_absolute():
103
+ abs_path = rel_path
104
+ else:
105
+ abs_path = self.root/rel_path
106
+
107
+ abs_path = resolve(abs_path)
108
+
109
+ if not subdir(self.root, abs_path, check=False):
110
+ raise FileOutsideRootError(
111
+ f"Must be within project root directory:"
112
+ f"file = \"{abs_path}\", root = \"{self.root}\"")
113
+
114
+ if k in ('build_dir', 'prefix') and subdir(abs_path, self.root, check=False):
115
+ raise ValidPathError(
116
+ f"'{k}' cannot be project root directory:"
117
+ f"file = \"{abs_path}\", root = \"{self.root}\"")
118
+
119
+ target[k] = abs_path
120
+ namespace[k] = abs_path
121
+
122
+ src_dir = target.src_dir
123
+ build_dir = target.build_dir
124
+ prefix = target.prefix
125
+ work_dir = target.work_dir
126
+
127
+ with validating(key = f"tool.pyproj.targets[{i}].src_dir"):
128
+ if not src_dir.exists():
129
+ raise ValidPathError(f"Source directory not found: {src_dir}")
130
+
131
+ if not src_dir.is_dir():
132
+ raise ValidPathError(f"Source directory not a directory: {src_dir}")
133
+
134
+ with validating(key = f"tool.pyproj.targets[{i}]"):
135
+ if subdir(build_dir, prefix, check=False):
136
+ raise ValidPathError(
137
+ f"'prefix' cannot be inside 'build_dir', which will be cleaned: {build_dir} > {prefix}")
138
+
139
+ build_dirty = build_dir.exists() and any(build_dir.iterdir())
140
+
141
+ if target.build_clean and build_dirty:
142
+ raise ValidPathError(
143
+ f"'build_dir' is not empty, please remove manually."
144
+ f" If this was intended, set 'build_clean = false': {build_dir}")
145
+
146
+ # create output directories
147
+ for k in ['build_dir', 'prefix']:
148
+ with validating(key = f"tool.pyproj.targets[{i}].{k}"):
149
+ target[k].mkdir(parents=True, exist_ok=True)
150
+
151
+ with validating(key = f"tool.pyproj.targets[{i}].options"):
152
+ # original target options remain until evaluated
153
+ options = target.options
154
+
155
+ # top-level options updated in order of appearance
156
+ _options = {}
157
+ namespace['options'] = _options
158
+
159
+ for k,v in options.items():
160
+ v = template_substitute(v, namespace)
161
+ # update target
162
+ options[k] = v
163
+ # update
164
+ _options[k] = v
165
+
166
+ with validating(key = f"tool.pyproj.targets[{i}].env"):
167
+ # original target options remain until evaluated
168
+ env = target.env
169
+
170
+ # top-level options updated in order of appearance
171
+ # copy of environment dict, each target isolated changes
172
+ _env = copy(namespace['env'])
173
+ namespace['env'] = _env
174
+
175
+ for k,v in env.items():
176
+ v = template_substitute(v, namespace)
177
+ env[k] = v
178
+ _env[k] = v
179
+
180
+ for attr in ['setup_args', 'compile_args', 'install_args']:
181
+ with validating(key = f"tool.pyproj.targets[{i}].{attr}"):
182
+ value = target[attr]
183
+ value = template_substitute(value, namespace)
184
+
185
+ target[attr] = value
186
+ namespace[attr] = value
187
+
188
+ entry_point = EntryPoint(
189
+ pyproj = self,
190
+ root = self.root,
191
+ name = f"tool.pyproj.targets[{i}]",
192
+ logger = self.logger,
193
+ entry = target.entry)
194
+
195
+ log_dir = self.root/'build'/'logs'
196
+
197
+ log_dir.mkdir(parents=True, exist_ok=True)
198
+
199
+ runner = ProcessRunner(
200
+ logger=self.logger,
201
+ log_dir=log_dir,
202
+ target_name=f"target_{i:02d}",
203
+ env=_env)
204
+
205
+ self.logger.info('\n'.join([
206
+ f"targets[{i}]:",
207
+ f" work_dir: {work_dir}",
208
+ f" src_dir: {src_dir}",
209
+ f" build_dir: {build_dir}",
210
+ f" prefix: {prefix}",
211
+ f" log_dir: {log_dir}",
212
+ " options: " + ('\n' if target.options else 'none') + '\n'.join([
213
+ f" {k}: {v}" for k,v in target.options.items()]),
214
+ " env: " + ('\n' if target.env else 'default') + '\n'.join([
215
+ f" {k}: {v}" for k,v in target.env.items()])]))
216
+
217
+ cwd = os.getcwd()
218
+
219
+ # allow cleaning once the target is validated
220
+ self.clean_dirs[i] = True
221
+
222
+ try:
223
+ os.chdir(work_dir)
224
+
225
+ entry_point(
226
+ options = target.options,
227
+ work_dir = work_dir,
228
+ src_dir = src_dir,
229
+ build_dir = build_dir,
230
+ prefix = prefix,
231
+ setup_args = target.setup_args,
232
+ compile_args = target.compile_args,
233
+ install_args = target.install_args,
234
+ build_clean = not build_dirty,
235
+ runner = runner)
236
+
237
+ finally:
238
+ os.chdir(cwd)
239
+
240
+ #-----------------------------------------------------------------------------
241
+ def build_clean(self):
242
+ for i, (target, clean) in enumerate(zip(self.targets, self.clean_dirs)):
243
+ if not clean:
244
+ continue
245
+
246
+ build_dir = target.build_dir
247
+
248
+ if build_dir is not None and build_dir.exists() and target.build_clean:
249
+ self.logger.info(f"Removing build dir: {build_dir}")
250
+ shutil.rmtree(build_dir)
251
+
252
+ #===============================================================================
253
+ class ProcessRunner:
254
+ #-----------------------------------------------------------------------------
255
+ def __init__(self,
256
+ logger,
257
+ log_dir: Path,
258
+ target_name: str,
259
+ env: dict):
260
+
261
+ self.logger = logger
262
+ self.log_dir = log_dir
263
+ self.target_name = target_name
264
+ self.commands = {}
265
+ self.env = env
266
+
267
+ #-----------------------------------------------------------------------------
268
+ def run(self, args: list, env: dict = None):
269
+ if len(args) == 0:
270
+ raise ValueError(f"Command for {self.target_name} is empty.")
271
+
272
+ cmd_exec = args[0]
273
+ cmd_exec_src = shutil.which(cmd_exec)
274
+
275
+ if cmd_exec_src is None:
276
+ raise ValueError(
277
+ f"Executable does not exist or has in-sufficient permissions: {cmd_exec}")
278
+
279
+ cmd_exec_src = resolve(Path(cmd_exec_src))
280
+ cmd_name = cmd_exec_src.name
281
+ args = [str(cmd_exec_src)]+args[1:]
282
+
283
+ cmd_hist = self.commands.setdefault(cmd_exec_src, [])
284
+ cmd_idx = len(cmd_hist)
285
+ cmd_hist.append(args)
286
+
287
+ run_name = re.sub(r'[^\w]+', "_", cmd_name)
288
+ run_id = f"{self.target_name}.{run_name}.{cmd_idx:02d}"
289
+
290
+ stdout_file = self.log_dir/f"{run_id}.log"
291
+
292
+ try:
293
+ self.logger.info(f"Running {run_id!r}: "+' '.join(args))
294
+
295
+ with open(stdout_file, 'wb') as fp:
296
+ subprocess.run(
297
+ args,
298
+ shell=False,
299
+ stdout=fp,
300
+ stderr=subprocess.STDOUT,
301
+ check=True,
302
+ env=self.env)
303
+
304
+ except subprocess.CalledProcessError as e:
305
+
306
+
307
+ num_windows = 20
308
+ window_size = 5
309
+ with open(stdout_file, 'rb') as fp:
310
+ lines = [
311
+ (lineno,line)
312
+ for lineno,line in enumerate(fp.read().decode('utf-8', errors='replace').splitlines())]
313
+
314
+ suspect_linenos = [
315
+ lineno
316
+ for lineno,line in lines
317
+ if ERROR_REC.search(line)]
318
+
319
+ # suspect_linenos = suspect_linenos[:num_windows]
320
+
321
+ extra = [
322
+ '\n'.join(
323
+ [f"{'':-<70}",f"{'':>4}⋮"]
324
+ +[f"{j:>4d}| {line}" for j,line in lines[i:i+window_size]]
325
+ +[f"{'':>4}⋮"])
326
+ for i in suspect_linenos]
327
+
328
+ m = len(lines)-num_windows
329
+
330
+ if suspect_linenos:
331
+ m = max(m, suspect_linenos[-1])
332
+
333
+ last_lines = lines[m:]
334
+
335
+ if last_lines:
336
+ extra += [
337
+ f"{'':-<70}",
338
+ f"Last {len(last_lines)} lines of command output:",
339
+ f"{'':>4}⋮"]
340
+
341
+ extra += [
342
+ f"{j:>4d}| {line}"
343
+ for j,line in last_lines]
344
+
345
+ extra += [
346
+ f"{'END':>4}| [See log file: {stdout_file}]",
347
+ f"{'':-<70}",]
348
+
349
+ raise BuildCommandError(
350
+ str(e),
351
+ extra='\n'.join(extra)) from None
@@ -13,7 +13,7 @@ from ..validate import (
13
13
  ValidPathError,
14
14
  FileOutsideRootError )
15
15
 
16
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
16
+ #===============================================================================
17
17
  def cargo(
18
18
  pyproj: PyProjBase,
19
19
  logger: Logger,
@@ -31,7 +31,7 @@ def cargo(
31
31
  """
32
32
 
33
33
  if not shutil.which('cargo'):
34
- raise ValueError(f"The 'cargo' program not found.")
34
+ raise ValueError("The 'cargo' program not found.")
35
35
 
36
36
  if setup_args or install_args:
37
37
  raise ValueError(
@@ -1,25 +1,19 @@
1
- import os
2
- import os.path as osp
3
- import tempfile
1
+ from __future__ import annotations
4
2
  import shutil
5
- import subprocess
6
3
 
7
- from ..validate import (
8
- validating,
9
- ValidationError,
10
- ValidPathError,
11
- FileOutsideRootError )
12
-
13
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4
+ #===============================================================================
14
5
  def cmake_option_arg(k, v):
15
6
  """Convert python key-value pair to cmake ``-Dkey=value`` option
16
7
  """
8
+ typename = ''
9
+
17
10
  if isinstance(v, bool):
11
+ typename = ':BOOL'
18
12
  v = ({True: 'ON', False: 'OFF'})[v]
19
13
 
20
- return f'-D{k}={v}'
14
+ return f'-D{k}{typename}={v}'
21
15
 
22
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
16
+ #===============================================================================
23
17
  def cmake(
24
18
  pyproj,
25
19
  logger,
@@ -50,10 +44,10 @@ def cmake(
50
44
  """
51
45
 
52
46
  if not shutil.which('cmake'):
53
- raise ValueError(f"The 'cmake' program not found.")
47
+ raise ValueError("The 'cmake' program not found.")
54
48
 
55
49
  if not shutil.which('ninja'):
56
- raise ValueError(f"The 'ninja' program not found.")
50
+ raise ValueError("The 'ninja' program not found.")
57
51
 
58
52
  # TODO: ensure any paths in setup_args are normalized
59
53
  if not build_clean: