jolt 0.9.342__py3-none-any.whl → 0.9.429__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.

Potentially problematic release.


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

Files changed (158) hide show
  1. jolt/__init__.py +47 -0
  2. jolt/cache.py +358 -159
  3. jolt/cli.py +71 -104
  4. jolt/config.py +14 -26
  5. jolt/filesystem.py +2 -2
  6. jolt/graph.py +56 -28
  7. jolt/influence.py +67 -2
  8. jolt/loader.py +150 -186
  9. jolt/log.py +12 -2
  10. jolt/manifest.py +0 -46
  11. jolt/options.py +35 -12
  12. jolt/pkgs/abseil.py +42 -0
  13. jolt/pkgs/asio.py +25 -0
  14. jolt/pkgs/autoconf.py +41 -0
  15. jolt/pkgs/automake.py +41 -0
  16. jolt/pkgs/b2.py +31 -0
  17. jolt/pkgs/boost.py +111 -0
  18. jolt/pkgs/boringssl.py +32 -0
  19. jolt/pkgs/busybox.py +39 -0
  20. jolt/pkgs/bzip2.py +43 -0
  21. jolt/pkgs/cares.py +29 -0
  22. jolt/pkgs/catch2.py +36 -0
  23. jolt/pkgs/cbindgen.py +17 -0
  24. jolt/pkgs/cista.py +19 -0
  25. jolt/pkgs/clang.py +44 -0
  26. jolt/pkgs/cli11.py +23 -0
  27. jolt/pkgs/cmake.py +48 -0
  28. jolt/pkgs/cpython.py +196 -0
  29. jolt/pkgs/crun.py +29 -0
  30. jolt/pkgs/curl.py +38 -0
  31. jolt/pkgs/dbus.py +18 -0
  32. jolt/pkgs/double_conversion.py +24 -0
  33. jolt/pkgs/fastfloat.py +21 -0
  34. jolt/pkgs/ffmpeg.py +28 -0
  35. jolt/pkgs/flatbuffers.py +29 -0
  36. jolt/pkgs/fmt.py +27 -0
  37. jolt/pkgs/fstree.py +20 -0
  38. jolt/pkgs/gflags.py +18 -0
  39. jolt/pkgs/glib.py +18 -0
  40. jolt/pkgs/glog.py +25 -0
  41. jolt/pkgs/glslang.py +21 -0
  42. jolt/pkgs/golang.py +16 -11
  43. jolt/pkgs/googlebenchmark.py +18 -0
  44. jolt/pkgs/googletest.py +46 -0
  45. jolt/pkgs/gperf.py +15 -0
  46. jolt/pkgs/grpc.py +73 -0
  47. jolt/pkgs/hdf5.py +19 -0
  48. jolt/pkgs/help2man.py +14 -0
  49. jolt/pkgs/inja.py +28 -0
  50. jolt/pkgs/jsoncpp.py +31 -0
  51. jolt/pkgs/libarchive.py +43 -0
  52. jolt/pkgs/libcap.py +44 -0
  53. jolt/pkgs/libdrm.py +44 -0
  54. jolt/pkgs/libedit.py +42 -0
  55. jolt/pkgs/libevent.py +31 -0
  56. jolt/pkgs/libexpat.py +27 -0
  57. jolt/pkgs/libfastjson.py +21 -0
  58. jolt/pkgs/libffi.py +16 -0
  59. jolt/pkgs/libglvnd.py +30 -0
  60. jolt/pkgs/libogg.py +28 -0
  61. jolt/pkgs/libpciaccess.py +18 -0
  62. jolt/pkgs/libseccomp.py +21 -0
  63. jolt/pkgs/libtirpc.py +24 -0
  64. jolt/pkgs/libtool.py +42 -0
  65. jolt/pkgs/libunwind.py +35 -0
  66. jolt/pkgs/libva.py +18 -0
  67. jolt/pkgs/libvorbis.py +33 -0
  68. jolt/pkgs/libxml2.py +35 -0
  69. jolt/pkgs/libxslt.py +17 -0
  70. jolt/pkgs/libyajl.py +16 -0
  71. jolt/pkgs/llvm.py +81 -0
  72. jolt/pkgs/lua.py +54 -0
  73. jolt/pkgs/lz4.py +26 -0
  74. jolt/pkgs/m4.py +14 -0
  75. jolt/pkgs/make.py +17 -0
  76. jolt/pkgs/mesa.py +81 -0
  77. jolt/pkgs/meson.py +17 -0
  78. jolt/pkgs/mstch.py +28 -0
  79. jolt/pkgs/mysql.py +60 -0
  80. jolt/pkgs/nasm.py +49 -0
  81. jolt/pkgs/ncurses.py +30 -0
  82. jolt/pkgs/ng_log.py +25 -0
  83. jolt/pkgs/ninja.py +45 -0
  84. jolt/pkgs/nlohmann_json.py +25 -0
  85. jolt/pkgs/nodejs.py +19 -11
  86. jolt/pkgs/opencv.py +24 -0
  87. jolt/pkgs/openjdk.py +26 -0
  88. jolt/pkgs/openssl.py +103 -0
  89. jolt/pkgs/paho.py +76 -0
  90. jolt/pkgs/patchelf.py +16 -0
  91. jolt/pkgs/perl.py +42 -0
  92. jolt/pkgs/pkgconfig.py +64 -0
  93. jolt/pkgs/poco.py +39 -0
  94. jolt/pkgs/protobuf.py +77 -0
  95. jolt/pkgs/pugixml.py +27 -0
  96. jolt/pkgs/python.py +19 -0
  97. jolt/pkgs/qt.py +35 -0
  98. jolt/pkgs/rapidjson.py +26 -0
  99. jolt/pkgs/rapidyaml.py +28 -0
  100. jolt/pkgs/re2.py +30 -0
  101. jolt/pkgs/re2c.py +17 -0
  102. jolt/pkgs/readline.py +15 -0
  103. jolt/pkgs/rust.py +41 -0
  104. jolt/pkgs/sdl.py +28 -0
  105. jolt/pkgs/simdjson.py +27 -0
  106. jolt/pkgs/soci.py +46 -0
  107. jolt/pkgs/spdlog.py +29 -0
  108. jolt/pkgs/spirv_llvm.py +21 -0
  109. jolt/pkgs/spirv_tools.py +24 -0
  110. jolt/pkgs/sqlite.py +83 -0
  111. jolt/pkgs/ssl.py +12 -0
  112. jolt/pkgs/texinfo.py +15 -0
  113. jolt/pkgs/tomlplusplus.py +22 -0
  114. jolt/pkgs/wayland.py +26 -0
  115. jolt/pkgs/x11.py +58 -0
  116. jolt/pkgs/xerces_c.py +20 -0
  117. jolt/pkgs/xorg.py +360 -0
  118. jolt/pkgs/xz.py +29 -0
  119. jolt/pkgs/yamlcpp.py +30 -0
  120. jolt/pkgs/zeromq.py +47 -0
  121. jolt/pkgs/zlib.py +69 -0
  122. jolt/pkgs/zstd.py +33 -0
  123. jolt/plugins/autotools.py +66 -0
  124. jolt/plugins/cmake.py +74 -6
  125. jolt/plugins/conan.py +238 -0
  126. jolt/plugins/cxxinfo.py +7 -0
  127. jolt/plugins/docker.py +3 -3
  128. jolt/plugins/environ.py +11 -0
  129. jolt/plugins/fetch.py +141 -0
  130. jolt/plugins/gdb.py +10 -6
  131. jolt/plugins/git.py +60 -11
  132. jolt/plugins/libtool.py +63 -0
  133. jolt/plugins/linux.py +990 -0
  134. jolt/plugins/meson.py +61 -0
  135. jolt/plugins/ninja-compdb.py +11 -7
  136. jolt/plugins/ninja.py +245 -26
  137. jolt/plugins/paths.py +11 -1
  138. jolt/plugins/pkgconfig.py +219 -0
  139. jolt/plugins/podman.py +15 -41
  140. jolt/plugins/python.py +137 -0
  141. jolt/plugins/rust.py +25 -0
  142. jolt/plugins/scheduler.py +18 -14
  143. jolt/plugins/selfdeploy/setup.py +2 -1
  144. jolt/plugins/selfdeploy.py +21 -30
  145. jolt/plugins/strings.py +19 -10
  146. jolt/scheduler.py +428 -138
  147. jolt/tasks.py +159 -7
  148. jolt/tools.py +105 -51
  149. jolt/utils.py +16 -1
  150. jolt/version.py +1 -1
  151. {jolt-0.9.342.dist-info → jolt-0.9.429.dist-info}/METADATA +64 -9
  152. jolt-0.9.429.dist-info/RECORD +207 -0
  153. {jolt-0.9.342.dist-info → jolt-0.9.429.dist-info}/WHEEL +1 -1
  154. jolt/plugins/debian.py +0 -338
  155. jolt/plugins/repo.py +0 -253
  156. jolt-0.9.342.dist-info/RECORD +0 -93
  157. {jolt-0.9.342.dist-info → jolt-0.9.429.dist-info}/entry_points.txt +0 -0
  158. {jolt-0.9.342.dist-info → jolt-0.9.429.dist-info}/top_level.txt +0 -0
jolt/influence.py CHANGED
@@ -3,7 +3,7 @@ import hashlib
3
3
  import os
4
4
  from pathlib import Path, PurePath
5
5
 
6
- from jolt import config
6
+ from jolt import config as jolt_config
7
7
  from jolt import inspection
8
8
  from jolt import utils
9
9
  from jolt import filesystem as fs
@@ -202,6 +202,71 @@ def source(name, obj=None):
202
202
  return _decorate
203
203
 
204
204
 
205
+ class ConfigInfluence(HashInfluenceProvider):
206
+ name = "Config"
207
+
208
+ def __init__(self, section, key):
209
+ self.section = section
210
+ self.key = key
211
+
212
+ def get_influence(self, task):
213
+ value = jolt_config.get(self.section, self.key)
214
+ if value is None:
215
+ value = "<unset>"
216
+ return "{}.{}: {}".format(self.section, self.key, value)
217
+
218
+
219
+ def config(section, key):
220
+ """ Add configuration value as hash influence.
221
+
222
+ Note that the configuration value is read at the time
223
+ when the task is prepared, not when it is executed.
224
+ This means that if the configuration value changes after
225
+ influence has been calculated, the task will not be re-executed.
226
+ This is true also in distributed mode, where the task
227
+ is prepared locally and executed remotely on a worker
228
+ where the configuration value may be different.
229
+
230
+ Args:
231
+ section (str): Name of the configuration section.
232
+ key (str): Name of the configuration key.
233
+
234
+ Example:
235
+ .. code-block:: python
236
+
237
+ from jolt import influence
238
+
239
+ @influence.config("jolt", "task_timeout")
240
+ class Example(Task):
241
+
242
+ """
243
+ def _decorate(cls):
244
+ _old_influence = cls._influence
245
+
246
+ def _influence(self, *args, **kwargs):
247
+ influence = _old_influence(self, *args, **kwargs)
248
+ influence.append(ConfigInfluence(section, key))
249
+ return influence
250
+
251
+ cls._influence = _influence
252
+ return cls
253
+
254
+ return _decorate
255
+
256
+
257
+ def global_config(section, key):
258
+ """ Register a configuration influence globally.
259
+
260
+ See :py:func:`config` for more information.
261
+
262
+ Args:
263
+ section (str): Name of the configuration section.
264
+ key (str): Name of the configuration key.
265
+ """
266
+
267
+ HashInfluenceRegistry.get().register(ConfigInfluence(section, key))
268
+
269
+
205
270
  class TaskClassSourceInfluence(HashInfluenceProvider):
206
271
  name = "Source"
207
272
 
@@ -250,7 +315,7 @@ class CacheLocationInfluence(HashInfluenceProvider):
250
315
  name = "Cache"
251
316
 
252
317
  def get_influence(self, task):
253
- return config.get_cachedir()
318
+ return jolt_config.get_cachedir()
254
319
 
255
320
 
256
321
  @HashInfluenceRegistry.Register
jolt/loader.py CHANGED
@@ -1,4 +1,3 @@
1
- from collections import OrderedDict
2
1
  from contextlib import contextmanager
3
2
  import fasteners
4
3
  import json
@@ -11,20 +10,34 @@ import sys
11
10
  from types import ModuleType
12
11
 
13
12
  from jolt import inspection
14
- from jolt.tasks import attributes
15
- from jolt.tasks import Alias, Task, TaskGenerator, TaskRegistry, WorkspaceResource
16
- from jolt.error import raise_error_if, raise_task_error_if
13
+ from jolt.tasks import Task, TaskGenerator
14
+ from jolt.error import raise_error_if
17
15
  from jolt import common_pb2 as common_pb
18
16
  from jolt import config
19
17
  from jolt import filesystem as fs
20
18
  from jolt import log
21
19
  from jolt import utils
22
- from jolt.manifest import ManifestExtension
23
- from jolt.manifest import ManifestExtensionRegistry
24
20
  from jolt.tools import Tools
25
21
 
26
22
 
27
23
  class Recipe(object):
24
+ """
25
+ Abstract representation a single recipe file.
26
+
27
+ Implementations of this class are responsible for reading the recipe source
28
+ from the filesystem. The recipe source is then parsed and the tasks are
29
+ extracted and made available for execution.
30
+
31
+ The format of the recipe source is implementation defined.
32
+ """
33
+
34
+ tasks = []
35
+ """
36
+ List of task classes defined in the recipe.
37
+
38
+ Available after the recipe has been loaded.
39
+ """
40
+
28
41
  def __init__(self, path, joltdir=None, project=None, source=None):
29
42
  self.path = path
30
43
  self.basepath = os.path.basename(path)
@@ -33,13 +46,15 @@ class Recipe(object):
33
46
  self.source = source
34
47
  self.tasks = []
35
48
 
36
- def load(self, joltdir=None):
49
+ def load(self):
50
+ """ Load the recipe source from the file system. """
37
51
  raise_error_if(self.source is not None, "recipe already loaded: {}", self.path)
38
52
 
39
53
  with open(self.path) as f:
40
54
  self.source = f.read()
41
55
 
42
56
  def save(self):
57
+ """ Save the recipe source to the file system. """
43
58
  raise_error_if(self.source is None, "recipe source unknown: {}", self.path)
44
59
 
45
60
  with open(self.path, "w") as f:
@@ -47,6 +62,8 @@ class Recipe(object):
47
62
 
48
63
 
49
64
  class NativeRecipe(Recipe):
65
+ """ Represents a Python recipe file (.jolt, .py). """
66
+
50
67
  @staticmethod
51
68
  def _is_abstract(cls):
52
69
  return cls.__dict__.get("abstract", False) or cls.__name__.startswith("_")
@@ -58,6 +75,13 @@ class NativeRecipe(Recipe):
58
75
  not NativeRecipe._is_abstract(cls)
59
76
 
60
77
  def load(self, joltdir=None):
78
+ """
79
+ Load the recipe source from the file system.
80
+
81
+ Python classes defined in the recipe source are extracted and made available
82
+ as tasks for execution. Task classes must be subclasses of Task or TaskGenerator.
83
+
84
+ """
61
85
  super(NativeRecipe, self).load()
62
86
 
63
87
  name = utils.canonical(self.path)
@@ -88,17 +112,42 @@ class NativeRecipe(Recipe):
88
112
 
89
113
 
90
114
  class Loader(object):
91
- def recipes(self):
115
+ """
116
+ Base class for recipe loaders.
117
+
118
+ A Loader is responsible for finding recipes in the file system providing a list
119
+ of Recipe:s from which tasks can be loaded.
120
+ """
121
+
122
+ def recipes(self) -> list:
123
+ """ Return a list of Recipe:s from which tasks can be loaded. """
92
124
  pass
93
125
 
94
126
 
95
127
  class LoaderFactory(object):
128
+ """
129
+ A factory for creating Loader instances.
130
+
131
+ Factories are registered with the JoltLoader where it is used to create Loader instances.
132
+ """
96
133
  def create(self):
97
134
  raise NotImplementedError()
98
135
 
99
136
 
100
137
  class NativeLoader(Loader):
138
+ """ A loader for Python recipe files (.jolt, .py). """
139
+
101
140
  def __init__(self, searchpath):
141
+ """
142
+ Create a new NativeLoader instance.
143
+
144
+ Args:
145
+ searchpath (str): The path to search for recipe files. If the path is a file,
146
+ only that file will be loaded. If the path is a directory, all files with
147
+ the .jolt or .py extension will be loaded.
148
+
149
+ """
150
+
102
151
  self._files = self._find_files(searchpath)
103
152
  self._recipes = self._load_files(self._files) if self._files else []
104
153
 
@@ -109,7 +158,7 @@ class NativeLoader(Loader):
109
158
 
110
159
  _, ext = fs.path.splitext(searchpath)
111
160
  raise_error_if(not fs.path.exists(searchpath), "File does not exist: {}", searchpath)
112
- raise_error_if(ext not in [".jolt", ".py"], "Invalid file extension: {}", ext)
161
+ raise_error_if(ext not in [".build", ".jolt", ".py"], "Invalid file extension: {}", ext)
113
162
 
114
163
  return [searchpath]
115
164
 
@@ -129,6 +178,7 @@ _loaders = []
129
178
 
130
179
 
131
180
  def register(factory):
181
+ """ Register a LoaderFactory with the JoltLoader. """
132
182
  raise_error_if(not issubclass(factory, LoaderFactory),
133
183
  "{} is not a LoaderFactory", factory.__name__)
134
184
  _loaders.append(factory)
@@ -136,12 +186,22 @@ def register(factory):
136
186
 
137
187
  @register
138
188
  class NativeLoaderFactory(LoaderFactory):
189
+ """ A factory for creating NativeLoader instances. """
139
190
  def create(self, searchpath):
140
191
  return NativeLoader(searchpath)
141
192
 
142
193
 
143
194
  @utils.Singleton
144
195
  class JoltLoader(object):
196
+ """
197
+ The JoltLoader is responsible for loading recipes from the file system.
198
+
199
+ The JoltLoader is a singleton and is used to load recipes from the file system.
200
+ The recipes are loaded from the workspace directory and any project directories
201
+ defined in the workspace. The recipes are then made available for execution.
202
+
203
+ """
204
+
145
205
  filename = "*.jolt"
146
206
 
147
207
  def __init__(self):
@@ -149,61 +209,14 @@ class JoltLoader(object):
149
209
  self._recipes = []
150
210
  self._tasks = []
151
211
  self._path = None
152
- self._project_modules = OrderedDict()
153
- self._project_recipes = OrderedDict()
154
- self._project_resources = OrderedDict()
155
212
  self._build_path = None
156
213
  self._workspace_name = None
157
214
 
158
- def _add_project_module(self, project, src):
159
- modules = self._project_modules.get(project, [])
160
- modules.append(src)
161
- self._project_modules[project] = modules
162
-
163
- def _get_project_modules(self, project):
164
- return self._project_modules.get(project, [])
165
-
166
- def _add_project_recipe(self, project, joltdir, src):
167
- recipes = self._project_recipes.get(project, [])
168
- recipes.append((joltdir, src))
169
- self._project_recipes[project] = recipes
170
-
171
- def _get_project_recipes(self, project):
172
- return self._project_recipes.get(project, [])
173
-
174
215
  def _get_first_recipe_path(self):
175
216
  for recipe in self.recipes:
176
217
  return recipe.path
177
- for project, recipes in self._project_recipes.items():
178
- for joltdir, src in recipes:
179
- return fs.path.join(self._path, src)
180
218
  return None
181
219
 
182
- def _add_project_resource(self, project, resource_name, resource_task):
183
- class ProjectResource(Alias):
184
- name = project + "/" + resource_name
185
- requires = [resource_task]
186
-
187
- self._tasks.append(ProjectResource)
188
- resources = self._project_resources.get(project, [])
189
- resources.append((resource_name, resource_task))
190
- self._project_resources[project] = resources
191
-
192
- def _get_project_resources(self, project):
193
- return self._project_resources.get(project, [])
194
-
195
- def _load_project_recipes(self):
196
- for project, recipes in self._project_recipes.items():
197
- resources = [resource for _, resource in self._get_project_resources(project)]
198
- for joltdir, src in recipes:
199
- joltdir = fs.path.join(self.joltdir, joltdir) if joltdir else self.joltdir
200
- recipe = NativeRecipe(fs.path.join(self._path, src), joltdir, project)
201
- recipe.load()
202
- for task in recipe.tasks:
203
- task._resources = resources
204
- attributes.requires("_resources")(task)
205
- self._tasks += recipe.tasks
206
-
207
220
  def _find_workspace_path(self, searchdir):
208
221
  for factory in _loaders:
209
222
  loader = factory().create(searchdir)
@@ -219,7 +232,16 @@ class JoltLoader(object):
219
232
  def _get_searchpaths(self):
220
233
  return [self.workspace_path]
221
234
 
222
- def load(self, manifest=None):
235
+ def load(self, registry=None):
236
+ """
237
+ Load all recipes from the workspace directory.
238
+
239
+ Optionally populate the task registry with the tasks found in the recipes.
240
+
241
+ Returns:
242
+ List of Task classes found in the recipes.
243
+ """
244
+
223
245
  if not self.workspace_path:
224
246
  self.set_workspace_path(self._find_workspace_path(os.getcwd()))
225
247
 
@@ -230,21 +252,27 @@ class JoltLoader(object):
230
252
  for factory in _loaders:
231
253
  loader = factory().create(searchpath)
232
254
  for recipe in loader.recipes:
255
+ recipe.workspace_path = os.path.relpath(recipe.path, self.workspace_path)
233
256
  recipe.load()
234
257
  self._recipes.append(recipe)
235
258
  self._tasks += recipe.tasks
236
259
 
237
- self._load_project_recipes()
238
-
239
260
  # Create workspace lock on the first loaded recipe
240
261
  if not self._lock:
241
262
  path = self._get_first_recipe_path()
242
263
  if path:
243
264
  self._lock = fasteners.InterProcessLock(path)
244
265
 
266
+ # Add tasks to the registry if provided
267
+ if registry is not None:
268
+ for task in self._tasks:
269
+ registry.add_task_class(task)
270
+
245
271
  return self._tasks
246
272
 
247
273
  def load_file(self, path, joltdir=None):
274
+ """ Load a single recipe file. """
275
+
248
276
  for factory in _loaders:
249
277
  loader = factory().create(path)
250
278
  for recipe in loader.recipes:
@@ -254,6 +282,8 @@ class JoltLoader(object):
254
282
  self._tasks += recipe.tasks
255
283
 
256
284
  def load_plugin(self, filepath):
285
+ """ Load a single plugin file. """
286
+
257
287
  plugin, ext = os.path.splitext(fs.path.basename(filepath))
258
288
  loader = SourceFileLoader("jolt.plugins." + plugin, filepath)
259
289
  module = ModuleType(loader.name)
@@ -262,6 +292,15 @@ class JoltLoader(object):
262
292
  sys.modules[loader.name] = module
263
293
 
264
294
  def load_plugins(self):
295
+ """
296
+ Load all configured plugins.
297
+
298
+ Plugins are loaded from the plugin path configured in the Jolt configuration file
299
+ or from the default plugin path in the Jolt package.
300
+
301
+ If a plugin is already loaded, it will not be loaded again. If a plugin is not found
302
+ in the plugin path, it will not be loaded.
303
+ """
265
304
  searchpath = config.get("jolt", "pluginpath")
266
305
  searchpath = searchpath.split(":") if searchpath else []
267
306
  searchpath.append(fs.path.join(fs.path.dirname(__file__), "plugins"))
@@ -288,14 +327,17 @@ class JoltLoader(object):
288
327
 
289
328
  @property
290
329
  def tasks(self):
330
+ """ Returns a list of all loaded tasks. """
291
331
  return self._tasks
292
332
 
293
333
  @property
294
334
  def joltdir(self):
335
+ """ Returns the path to the workspace. """
295
336
  return self._path
296
337
 
297
338
  @property
298
339
  def workspace_name(self):
340
+ """ Returns the name of the workspace. """
299
341
  return self._workspace_name or os.path.basename(self.workspace_path)
300
342
 
301
343
  def set_workspace_name(self, name):
@@ -303,6 +345,7 @@ class JoltLoader(object):
303
345
 
304
346
  @property
305
347
  def workspace_path(self):
348
+ """ Returns the path to the workspace. """
306
349
  return self._path
307
350
 
308
351
  @contextmanager
@@ -319,10 +362,12 @@ class JoltLoader(object):
319
362
 
320
363
  @property
321
364
  def build_path(self):
365
+ """ Returns the path to the build directory. """
322
366
  return self._build_path or os.path.join(self.workspace_path, "build")
323
367
 
324
368
  @property
325
369
  def build_path_rel(self):
370
+ """" Returns the path to the build directory relative to the workspace. """
326
371
  return os.path.relpath(self.build_path, self.workspace_path)
327
372
 
328
373
  def set_build_path(self, path):
@@ -330,98 +375,6 @@ class JoltLoader(object):
330
375
  log.debug("Jolt build path: {}", self._build_path)
331
376
 
332
377
 
333
- class RecipeExtension(ManifestExtension):
334
- def export_manifest(self, manifest, tasks):
335
- loader = JoltLoader.get()
336
-
337
- for recipe in loader.recipes:
338
- manifest_recipe = manifest.create_recipe()
339
- manifest_recipe.path = recipe.basepath
340
- manifest_recipe.source = recipe.source
341
-
342
- projects = set([task.task.joltproject for task in tasks])
343
- for project in filter(lambda x: x is not None, projects):
344
- manifest_project = manifest.create_project()
345
- manifest_project.name = project
346
-
347
- for name, resource_task in loader._get_project_resources(project):
348
- resource = manifest_project.create_resource()
349
- resource.name = name
350
- resource.text = resource_task
351
-
352
- for joltdir, src in loader._get_project_recipes(project):
353
- recipe = manifest_project.create_recipe()
354
- recipe.src = src
355
- if joltdir:
356
- recipe.joltdir = joltdir
357
-
358
- for path in loader._get_project_modules(project):
359
- module = manifest_project.create_module()
360
- module.path = path
361
-
362
- def import_manifest(self, manifest):
363
- loader = JoltLoader.get()
364
- loader.set_workspace_path(manifest.get_workspace_path() or os.getcwd())
365
- loader.set_workspace_name(manifest.get_workspace_name())
366
- if manifest.build:
367
- loader.set_build_path(manifest.build)
368
-
369
- for recipe in manifest.recipes:
370
- recipe = Recipe(recipe.path, source=recipe.source)
371
- recipe.save()
372
-
373
- for project in manifest.projects:
374
- for recipe in project.recipes:
375
- loader._add_project_recipe(project.name, recipe.joltdir, recipe.src)
376
-
377
- for resource in project.resources:
378
- loader._add_project_resource(project.name, resource.name, resource.text)
379
-
380
- # Acquire resource immediately
381
- task = TaskRegistry.get().get_task(resource.text, manifest=manifest)
382
- raise_task_error_if(not isinstance(task, WorkspaceResource), task,
383
- "only workspace resources are allowed in manifest")
384
- task.acquire_ws()
385
-
386
- for module in project.modules:
387
- loader._add_project_module(project.name, module.src)
388
- sys.path.append(fs.path.join(manifest.get_workspace_path(), module.src))
389
-
390
- def import_protobuf(self, buildenv):
391
- loader = JoltLoader.get()
392
- loader.set_workspace_path(os.getcwd())
393
- loader.set_workspace_name(buildenv.workspace.name)
394
- if buildenv.workspace.builddir:
395
- loader.set_build_path(buildenv.workspace.builddir)
396
-
397
- # Write .jolt files into workspace
398
- for file in buildenv.workspace.files:
399
- if file.content:
400
- with open(file.path, "w") as f:
401
- f.write(file.content)
402
-
403
- for project in buildenv.workspace.projects:
404
- for recipe in project.recipes:
405
- loader._add_project_recipe(project.name, recipe.workdir, recipe.path)
406
-
407
- for resource in project.resources:
408
- loader._add_project_resource(project.name, resource.alias, resource.name)
409
-
410
- # Acquire resource immediately
411
- task = TaskRegistry.get().get_task(resource.name, buildenv=buildenv)
412
- raise_task_error_if(
413
- not isinstance(task, WorkspaceResource), task,
414
- "only workspace resources are allowed in manifest")
415
- task.acquire_ws()
416
-
417
- for path in project.paths:
418
- loader._add_project_module(project.name, path.path)
419
- sys.path.append(fs.path.join(loader.workspace_path, path.path))
420
-
421
-
422
- ManifestExtensionRegistry.add(RecipeExtension())
423
-
424
-
425
378
  def get_workspacedir():
426
379
  workspacedir = JoltLoader.get().workspace_path
427
380
  assert workspacedir is not None, "No workspace present"
@@ -441,8 +394,45 @@ def workspace_locked(func):
441
394
  return wrapper
442
395
 
443
396
 
397
+ def import_workspace(buildenv: common_pb.BuildEnvironment):
398
+ """
399
+ Import workspace from a BuildEnvironment protobuf message.
400
+
401
+ This function will create files, recipes, resources and modules in the workspace
402
+ based on the information in the BuildEnvironment message.
403
+
404
+ The workspace tree is not pulled and checked out here. This is done by the
405
+ worker before it starts the executor.
406
+
407
+ """
408
+ loader = JoltLoader.get()
409
+ loader.set_workspace_path(os.getcwd())
410
+ loader.set_workspace_name(buildenv.workspace.name)
411
+ if buildenv.workspace.builddir:
412
+ loader.set_build_path(buildenv.workspace.builddir)
413
+
414
+ # Write .jolt files into workspace
415
+ for file in buildenv.workspace.files:
416
+ if file.content:
417
+ with open(file.path, "w") as f:
418
+ f.write(file.content)
419
+
420
+
444
421
  @workspace_locked
445
- def export_workspace(tasks=None):
422
+ def export_workspace(tasks=None) -> common_pb.Workspace:
423
+ """
424
+ Export workspace to a Workspace protobuf message.
425
+
426
+ This function will create a Workspace protobuf message containing all the
427
+ recipes, resources and modules in the workspace. If tasks is provided, only
428
+ the projects associated with the tasks will be exported. Otherwise, all
429
+ projects will be exported.
430
+
431
+ If the workspace is configured to use a remote cache, the workspace will be
432
+ pushed to the remote cache using the fstree tool. The tree hash of the
433
+ workspace will be included in the returned Workspace message.
434
+
435
+ """
446
436
  loader = JoltLoader.get()
447
437
  tools = Tools()
448
438
  tree = None
@@ -461,6 +451,7 @@ def export_workspace(tasks=None):
461
451
  cache_grpc_uri = config.geturi("cache", "grpc_uri")
462
452
  if fstree_enabled:
463
453
  if not cache_grpc_uri:
454
+ fstree_enabled = False
464
455
  log.warning("No cache gRPC URI configured, will not push workspace to remote cache")
465
456
  else:
466
457
  raise_error_if(cache_grpc_uri.scheme not in ["tcp"], "Invalid scheme in cache gRPC URI config: {}", cache_grpc_uri.scheme)
@@ -553,40 +544,13 @@ def export_workspace(tasks=None):
553
544
  tree=tree,
554
545
  )
555
546
 
556
- for recipe in loader.recipes:
557
- workspace.files.append(
558
- common_pb.File(
559
- path=recipe.basepath,
560
- content=recipe.source,
547
+ if not fstree_enabled:
548
+ for recipe in loader.recipes:
549
+ workspace.files.append(
550
+ common_pb.File(
551
+ path=recipe.workspace_path,
552
+ content=recipe.source,
553
+ )
561
554
  )
562
- )
563
-
564
- if tasks is None:
565
- projects = loader._project_recipes.keys()
566
- else:
567
- projects = set([task.task.joltproject for task in tasks])
568
-
569
- for project in filter(lambda x: x is not None, projects):
570
- pb_project = common_pb.Project()
571
- pb_project.name = project
572
-
573
- for name, resource_task in loader._get_project_resources(project):
574
- resource = common_pb.Project.Resource()
575
- resource.alias = name
576
- resource.name = resource_task
577
- pb_project.resources.append(resource)
578
-
579
- for joltdir, src in loader._get_project_recipes(project):
580
- recipe = common_pb.Project.Recipe()
581
- recipe.path = src
582
- if joltdir:
583
- recipe.workdir = joltdir
584
- pb_project.recipes.append(recipe)
585
-
586
- for path in loader._get_project_modules(project):
587
- syspath = common_pb.Project.SystemPath(path=path)
588
- pb_project.paths.append(syspath)
589
-
590
- workspace.projects.append(pb_project)
591
555
 
592
556
  return workspace
jolt/log.py CHANGED
@@ -364,6 +364,9 @@ class _Progress(object):
364
364
  def reset(self, *args, **kwargs):
365
365
  pass
366
366
 
367
+ def set_description(self, *args, **kwargs):
368
+ pass
369
+
367
370
 
368
371
  def progress_log(desc, count, unit):
369
372
  return _Progress(desc)
@@ -376,8 +379,15 @@ def progress(desc, count, unit, estimates=True, debug=False):
376
379
  else:
377
380
  bar_format = '{desc}{n_fmt}{unit} [{elapsed}]'
378
381
  if not debug and is_interactive() and not is_verbose():
379
- p = tqdm.tqdm(total=count, unit=unit, unit_scale=True, bar_format=bar_format, dynamic_ncols=True)
380
- p.set_description("[ INFO] " + desc)
382
+ class ProgressTqdm(tqdm.tqdm):
383
+ def __init__(self, *args, **kwargs):
384
+ super(ProgressTqdm, self).__init__(*args, **kwargs)
385
+
386
+ def set_description(self, desc=None, refresh=True):
387
+ return super().set_description("[ INFO] " + desc, refresh)
388
+
389
+ p = ProgressTqdm(total=count, unit=unit, unit_scale=True, bar_format=bar_format, dynamic_ncols=True)
390
+ p.set_description(desc)
381
391
  return p
382
392
  return progress_log(desc, count, unit)
383
393
 
jolt/manifest.py CHANGED
@@ -241,49 +241,3 @@ class JoltManifest(ElementTree):
241
241
  for manifest_task in self.tasks:
242
242
  self._identities[manifest_task.name] = manifest_task.identity
243
243
  return self._identities
244
-
245
- @staticmethod
246
- def export(task):
247
- manifest = JoltManifest()
248
- ManifestExtensionRegistry.export_manifest(manifest, task)
249
- return manifest
250
-
251
- def process_import(self):
252
- ManifestExtensionRegistry.import_manifest(self)
253
-
254
-
255
- class ManifestExtensionRegistry(object):
256
- extensions = []
257
-
258
- @staticmethod
259
- def add(extension, priority=0):
260
- ManifestExtensionRegistry.extensions.append((extension, priority))
261
- ManifestExtensionRegistry.extensions.sort(key=lambda x: x[1])
262
-
263
- @staticmethod
264
- def export_manifest(manifest, task):
265
- for extension, _ in ManifestExtensionRegistry.extensions:
266
- extension.export_manifest(manifest, task)
267
-
268
- @staticmethod
269
- def import_manifest(manifest):
270
- if not manifest.is_valid():
271
- return
272
- for extension, _ in ManifestExtensionRegistry.extensions:
273
- extension.import_manifest(manifest)
274
-
275
- @staticmethod
276
- def import_protobuf(pb):
277
- for extension, _ in ManifestExtensionRegistry.extensions:
278
- extension.import_protobuf(pb)
279
-
280
-
281
- class ManifestExtension(object):
282
- def export_manifest(self, manifest, task):
283
- pass
284
-
285
- def import_manifest(self, manifest):
286
- pass
287
-
288
- def import_protobuf(self, pb):
289
- pass