jolt 0.9.172__py3-none-any.whl → 0.9.435__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 (185) hide show
  1. jolt/__init__.py +80 -7
  2. jolt/__main__.py +9 -1
  3. jolt/bin/fstree-darwin-x86_64 +0 -0
  4. jolt/bin/fstree-linux-x86_64 +0 -0
  5. jolt/cache.py +596 -252
  6. jolt/chroot.py +36 -11
  7. jolt/cli.py +143 -130
  8. jolt/common_pb2.py +45 -45
  9. jolt/config.py +76 -40
  10. jolt/error.py +19 -4
  11. jolt/filesystem.py +2 -6
  12. jolt/graph.py +400 -82
  13. jolt/influence.py +110 -3
  14. jolt/loader.py +338 -174
  15. jolt/log.py +127 -31
  16. jolt/manifest.py +13 -46
  17. jolt/options.py +35 -11
  18. jolt/pkgs/abseil.py +42 -0
  19. jolt/pkgs/asio.py +25 -0
  20. jolt/pkgs/autoconf.py +41 -0
  21. jolt/pkgs/automake.py +41 -0
  22. jolt/pkgs/b2.py +31 -0
  23. jolt/pkgs/boost.py +111 -0
  24. jolt/pkgs/boringssl.py +32 -0
  25. jolt/pkgs/busybox.py +39 -0
  26. jolt/pkgs/bzip2.py +43 -0
  27. jolt/pkgs/cares.py +29 -0
  28. jolt/pkgs/catch2.py +36 -0
  29. jolt/pkgs/cbindgen.py +17 -0
  30. jolt/pkgs/cista.py +19 -0
  31. jolt/pkgs/clang.py +44 -0
  32. jolt/pkgs/cli11.py +24 -0
  33. jolt/pkgs/cmake.py +48 -0
  34. jolt/pkgs/cpython.py +196 -0
  35. jolt/pkgs/crun.py +29 -0
  36. jolt/pkgs/curl.py +38 -0
  37. jolt/pkgs/dbus.py +18 -0
  38. jolt/pkgs/double_conversion.py +24 -0
  39. jolt/pkgs/fastfloat.py +21 -0
  40. jolt/pkgs/ffmpeg.py +28 -0
  41. jolt/pkgs/flatbuffers.py +29 -0
  42. jolt/pkgs/fmt.py +27 -0
  43. jolt/pkgs/fstree.py +20 -0
  44. jolt/pkgs/gflags.py +18 -0
  45. jolt/pkgs/glib.py +18 -0
  46. jolt/pkgs/glog.py +25 -0
  47. jolt/pkgs/glslang.py +21 -0
  48. jolt/pkgs/golang.py +16 -11
  49. jolt/pkgs/googlebenchmark.py +18 -0
  50. jolt/pkgs/googletest.py +46 -0
  51. jolt/pkgs/gperf.py +15 -0
  52. jolt/pkgs/grpc.py +73 -0
  53. jolt/pkgs/hdf5.py +19 -0
  54. jolt/pkgs/help2man.py +14 -0
  55. jolt/pkgs/inja.py +28 -0
  56. jolt/pkgs/jsoncpp.py +31 -0
  57. jolt/pkgs/libarchive.py +43 -0
  58. jolt/pkgs/libcap.py +44 -0
  59. jolt/pkgs/libdrm.py +44 -0
  60. jolt/pkgs/libedit.py +42 -0
  61. jolt/pkgs/libevent.py +31 -0
  62. jolt/pkgs/libexpat.py +27 -0
  63. jolt/pkgs/libfastjson.py +21 -0
  64. jolt/pkgs/libffi.py +16 -0
  65. jolt/pkgs/libglvnd.py +30 -0
  66. jolt/pkgs/libogg.py +28 -0
  67. jolt/pkgs/libpciaccess.py +18 -0
  68. jolt/pkgs/libseccomp.py +21 -0
  69. jolt/pkgs/libtirpc.py +24 -0
  70. jolt/pkgs/libtool.py +42 -0
  71. jolt/pkgs/libunwind.py +35 -0
  72. jolt/pkgs/libva.py +18 -0
  73. jolt/pkgs/libvorbis.py +33 -0
  74. jolt/pkgs/libxml2.py +35 -0
  75. jolt/pkgs/libxslt.py +17 -0
  76. jolt/pkgs/libyajl.py +16 -0
  77. jolt/pkgs/llvm.py +81 -0
  78. jolt/pkgs/lua.py +54 -0
  79. jolt/pkgs/lz4.py +26 -0
  80. jolt/pkgs/m4.py +14 -0
  81. jolt/pkgs/make.py +17 -0
  82. jolt/pkgs/mesa.py +81 -0
  83. jolt/pkgs/meson.py +17 -0
  84. jolt/pkgs/mstch.py +28 -0
  85. jolt/pkgs/mysql.py +60 -0
  86. jolt/pkgs/nasm.py +49 -0
  87. jolt/pkgs/ncurses.py +30 -0
  88. jolt/pkgs/ng_log.py +25 -0
  89. jolt/pkgs/ninja.py +45 -0
  90. jolt/pkgs/nlohmann_json.py +25 -0
  91. jolt/pkgs/nodejs.py +19 -11
  92. jolt/pkgs/opencv.py +24 -0
  93. jolt/pkgs/openjdk.py +26 -0
  94. jolt/pkgs/openssl.py +103 -0
  95. jolt/pkgs/paho.py +76 -0
  96. jolt/pkgs/patchelf.py +16 -0
  97. jolt/pkgs/perl.py +42 -0
  98. jolt/pkgs/pkgconfig.py +64 -0
  99. jolt/pkgs/poco.py +39 -0
  100. jolt/pkgs/protobuf.py +77 -0
  101. jolt/pkgs/pugixml.py +27 -0
  102. jolt/pkgs/python.py +19 -0
  103. jolt/pkgs/qt.py +35 -0
  104. jolt/pkgs/rapidjson.py +26 -0
  105. jolt/pkgs/rapidyaml.py +28 -0
  106. jolt/pkgs/re2.py +30 -0
  107. jolt/pkgs/re2c.py +17 -0
  108. jolt/pkgs/readline.py +15 -0
  109. jolt/pkgs/rust.py +41 -0
  110. jolt/pkgs/sdl.py +28 -0
  111. jolt/pkgs/simdjson.py +27 -0
  112. jolt/pkgs/soci.py +46 -0
  113. jolt/pkgs/spdlog.py +29 -0
  114. jolt/pkgs/spirv_llvm.py +21 -0
  115. jolt/pkgs/spirv_tools.py +24 -0
  116. jolt/pkgs/sqlite.py +83 -0
  117. jolt/pkgs/ssl.py +12 -0
  118. jolt/pkgs/texinfo.py +15 -0
  119. jolt/pkgs/tomlplusplus.py +22 -0
  120. jolt/pkgs/wayland.py +26 -0
  121. jolt/pkgs/x11.py +58 -0
  122. jolt/pkgs/xerces_c.py +20 -0
  123. jolt/pkgs/xorg.py +360 -0
  124. jolt/pkgs/xz.py +29 -0
  125. jolt/pkgs/yamlcpp.py +30 -0
  126. jolt/pkgs/zeromq.py +47 -0
  127. jolt/pkgs/zlib.py +87 -0
  128. jolt/pkgs/zstd.py +33 -0
  129. jolt/plugins/alias.py +3 -0
  130. jolt/plugins/allure.py +2 -2
  131. jolt/plugins/autotools.py +66 -0
  132. jolt/plugins/cache.py +1 -1
  133. jolt/plugins/cmake.py +74 -6
  134. jolt/plugins/conan.py +238 -0
  135. jolt/plugins/cxxinfo.py +7 -0
  136. jolt/plugins/docker.py +76 -19
  137. jolt/plugins/email.xslt +141 -118
  138. jolt/plugins/environ.py +11 -0
  139. jolt/plugins/fetch.py +141 -0
  140. jolt/plugins/gdb.py +33 -14
  141. jolt/plugins/gerrit.py +0 -13
  142. jolt/plugins/git.py +248 -66
  143. jolt/plugins/googletest.py +1 -1
  144. jolt/plugins/http.py +1 -1
  145. jolt/plugins/libtool.py +63 -0
  146. jolt/plugins/linux.py +990 -0
  147. jolt/plugins/logstash.py +4 -4
  148. jolt/plugins/meson.py +61 -0
  149. jolt/plugins/ninja-compdb.py +96 -28
  150. jolt/plugins/ninja.py +424 -150
  151. jolt/plugins/paths.py +11 -1
  152. jolt/plugins/pkgconfig.py +219 -0
  153. jolt/plugins/podman.py +131 -87
  154. jolt/plugins/python.py +137 -0
  155. jolt/plugins/remote_execution/administration_pb2.py +27 -19
  156. jolt/plugins/remote_execution/log_pb2.py +12 -12
  157. jolt/plugins/remote_execution/scheduler_pb2.py +23 -23
  158. jolt/plugins/remote_execution/worker_pb2.py +19 -19
  159. jolt/plugins/report.py +7 -2
  160. jolt/plugins/rust.py +25 -0
  161. jolt/plugins/scheduler.py +135 -86
  162. jolt/plugins/selfdeploy/setup.py +6 -6
  163. jolt/plugins/selfdeploy.py +49 -31
  164. jolt/plugins/strings.py +35 -22
  165. jolt/plugins/symlinks.py +11 -4
  166. jolt/plugins/telemetry.py +1 -2
  167. jolt/plugins/timeline.py +13 -3
  168. jolt/scheduler.py +467 -165
  169. jolt/tasks.py +427 -111
  170. jolt/templates/timeline.html.template +44 -47
  171. jolt/timer.py +22 -0
  172. jolt/tools.py +527 -188
  173. jolt/utils.py +183 -3
  174. jolt/version.py +1 -1
  175. jolt/xmldom.py +12 -2
  176. {jolt-0.9.172.dist-info → jolt-0.9.435.dist-info}/METADATA +97 -41
  177. jolt-0.9.435.dist-info/RECORD +207 -0
  178. {jolt-0.9.172.dist-info → jolt-0.9.435.dist-info}/WHEEL +1 -1
  179. jolt/plugins/amqp.py +0 -855
  180. jolt/plugins/debian.py +0 -338
  181. jolt/plugins/repo.py +0 -253
  182. jolt/plugins/snap.py +0 -122
  183. jolt-0.9.172.dist-info/RECORD +0 -92
  184. {jolt-0.9.172.dist-info → jolt-0.9.435.dist-info}/entry_points.txt +0 -0
  185. {jolt-0.9.172.dist-info → jolt-0.9.435.dist-info}/top_level.txt +0 -0
jolt/plugins/git.py CHANGED
@@ -1,11 +1,13 @@
1
1
  import os
2
2
  import pygit2
3
3
  import re
4
+ from threading import RLock
5
+ import urllib.parse
4
6
 
5
- from jolt.tasks import BooleanParameter, Export, Parameter, TaskRegistry, WorkspaceResource
7
+ from jolt.tasks import BooleanParameter, Export, IntParameter, Parameter, TaskRegistry, WorkspaceResource
6
8
  from jolt.influence import FileInfluence, HashInfluenceRegistry
7
9
  from jolt.tools import Tools
8
- from jolt.loader import JoltLoader
10
+ from jolt.loader import JoltLoader, workspace_locked
9
11
  from jolt import config
10
12
  from jolt import filesystem as fs
11
13
  from jolt import log
@@ -19,6 +21,13 @@ from jolt.error import raise_task_error_if
19
21
  log.verbose("[Git] Loaded")
20
22
 
21
23
 
24
+ def locked(func):
25
+ def _f(self, *args, **kwargs):
26
+ with self._lock:
27
+ return func(self, *args, **kwargs)
28
+ return _f
29
+
30
+
22
31
  class GitRepository(object):
23
32
  def __init__(self, url, path, relpath, refspecs=None):
24
33
  self.path = path
@@ -28,7 +37,7 @@ class GitRepository(object):
28
37
  self.url = url
29
38
  self.default_refspecs = [
30
39
  '+refs/heads/*:refs/remotes/origin/*',
31
- '+refs/tags/*:refs/remotes/origin/*',
40
+ '+refs/tags/*:refs/tags/*',
32
41
  ]
33
42
  self.refspecs = refspecs or []
34
43
  self._tree_hash = {}
@@ -67,19 +76,65 @@ class GitRepository(object):
67
76
  def is_indexed(self):
68
77
  return self.is_cloned() and fs.path.exists(self._git_index())
69
78
 
70
- def clone(self):
79
+ def clone(self, submodules=False):
71
80
  log.info("Cloning into {0}", self.path)
81
+
82
+ # Get reference repository path
83
+ refroot = config.get("git", "reference", None)
84
+ refpath = None
85
+ if refroot:
86
+ # Append <host>/<path> to the reference root
87
+ url = urllib.parse.urlparse(str(self.url))
88
+ refpath = fs.path.join(refroot, url.hostname, url.path.lstrip("/"))
89
+ refpath = fs.path.abspath(refpath)
90
+
91
+ # If the directory exists, initialize the repository instead of cloning
72
92
  if fs.path.exists(self.path):
73
93
  with self.tools.cwd(self.path):
74
- self.tools.run("git init && git remote add origin {} && git fetch",
75
- self.url, output_on_error=True)
94
+ self.tools.run("git init", output_on_error=True)
95
+
96
+ # Set the reference repository if available
97
+ if refpath:
98
+ # Check if the reference repository is a git repository
99
+ objpath = os.path.join(refpath, ".git", "objects")
100
+ objpath_bare = os.path.join(refpath, "objects")
101
+ if os.path.isdir(objpath):
102
+ refpath = objpath
103
+ elif os.path.isdir(objpath_bare):
104
+ refpath = objpath_bare
105
+ else:
106
+ refpath = None
107
+
108
+ if refpath:
109
+ self.tools.mkdir(".git/objects/info")
110
+ self.tools.write_file(".git/objects/info/alternates", refpath)
111
+
112
+ utils.call_and_catch(self.tools.run, "git remote remove origin", output=False)
113
+ self.tools.run("git remote add origin {}", self.url, output_on_error=True)
114
+ self._fetch_origin(submodules=submodules)
115
+ self.tools.run("git checkout -f FETCH_HEAD", output_on_error=True)
76
116
  else:
77
- self.tools.run("git clone {0} {1}", self.url, self.path, output_on_error=True)
117
+ # Get configurable extra clone options
118
+ extra_clone_options = config.get("git", "clone_options", "")
119
+
120
+ if refpath and os.path.isdir(refpath):
121
+ self.tools.run("git clone --reference-if-able {0} {1} {2} {3}", refpath, extra_clone_options, self.url, self.path, output_on_error=True)
122
+ else:
123
+ self.tools.run("git clone {0} {1} {2}", extra_clone_options, self.url, self.path, output_on_error=True)
124
+
78
125
  self._init_repo()
79
126
  raise_error_if(
80
127
  self.repository is None,
81
128
  "Failed to clone repository '{0}'", self.relpath)
82
129
 
130
+ @utils.retried.on_exception(JoltCommandError, pattern="Command failed: git fetch", count=6, backoff=[2, 5, 10, 15, 20, 30])
131
+ def _fetch_origin(self, submodules=False):
132
+ # Get configurable extra clone options
133
+ extra_fetch_options = config.get("git", "fetch_options", "")
134
+
135
+ with self.tools.cwd(self.path):
136
+ self.tools.run("git fetch {0} {1} origin", "", extra_fetch_options, output_on_error=True)
137
+
83
138
  @utils.cached.instance
84
139
  def diff_unchecked(self):
85
140
  if not self.is_indexed():
@@ -96,7 +151,7 @@ class GitRepository(object):
96
151
 
97
152
  def diff(self):
98
153
  diff = self.diff_unchecked()
99
- dlim = config.getsize("git", "maxdiffsize", "1M")
154
+ dlim = config.getsize("git", "maxdiffsize", "1 MiB")
100
155
  raise_error_if(
101
156
  len(diff) > dlim,
102
157
  "Repository '{}' has uncommitted changes. Size of patch exceeds configured transfer limit ({} > {} bytes)."
@@ -106,8 +161,8 @@ class GitRepository(object):
106
161
  def patch(self, patch):
107
162
  if not patch:
108
163
  return
109
- with self.tools.cwd(self.path), self.tools.tmpdir("git") as t:
110
- patchfile = fs.path.join(t.path, "jolt.diff")
164
+ with self.tools.cwd(self.path), self.tools.tmpdir("git") as tmp:
165
+ patchfile = fs.path.join(tmp, "jolt.diff")
111
166
  with open(patchfile, "wb") as f:
112
167
  f.write(patch.encode())
113
168
  log.info("Applying patch to {0}", self.path)
@@ -145,6 +200,7 @@ class GitRepository(object):
145
200
  return str(commit)
146
201
 
147
202
  @utils.cached.instance
203
+ @workspace_locked
148
204
  def write_tree(self):
149
205
  tools = Tools()
150
206
  with tools.cwd(self._git_folder()):
@@ -155,30 +211,30 @@ class GitRepository(object):
155
211
  output_on_error=True)
156
212
  return tree
157
213
 
158
- def tree_hash(self, sha=None, path="/"):
159
- # When sha is None, the caller want the tree hash of the repository's
214
+ def tree_hash(self, rev=None, path="/"):
215
+ # When rev is None, the caller want the tree hash of the repository's
160
216
  # current workspace state. If no checkout has been made, that would be the
161
217
  # tree that was written upon initialization of the repository as it
162
218
  # includes any uncommitted changes. If a checkout has been made since
163
219
  # the repo was initialized, make this an explicit request for the current
164
220
  # head - there can be no local changes.
165
- if sha is None:
221
+ if rev is None:
166
222
  if self.is_original_head():
167
223
  tree = self.repository.get(self.write_tree())
168
224
  else:
169
- sha = self.head()
225
+ rev = self.head()
170
226
 
171
227
  path = fs.path.normpath(path)
172
228
  full_path = fs.path.join(self.path, path) if path != "/" else self.path
173
229
 
174
230
  # Lookup tree hash value in cache
175
- value = self._tree_hash.get((full_path, sha))
231
+ value = self._tree_hash.get((full_path, rev))
176
232
  if value is not None:
177
233
  return value
178
234
 
179
- # Translate explicit sha to tree
180
- if sha is not None:
181
- commit = self.rev_parse(sha)
235
+ # Translate explicit rev to tree
236
+ if rev is not None:
237
+ commit = self.rev_parse(rev)
182
238
  obj = self.repository.get(commit)
183
239
  try:
184
240
  tree = obj.tree
@@ -190,7 +246,7 @@ class GitRepository(object):
190
246
  tree = tree[fs.as_posix(path)]
191
247
 
192
248
  # Update tree hash cache
193
- self._tree_hash[(full_path, sha)] = value = tree.id
249
+ self._tree_hash[(full_path, rev)] = value = tree.id
194
250
 
195
251
  return value
196
252
 
@@ -202,37 +258,50 @@ class GitRepository(object):
202
258
  with self.tools.cwd(self.path):
203
259
  return self.tools.run("git reset --hard", output_on_error=True)
204
260
 
261
+ @utils.retried.on_exception(JoltCommandError, pattern="Command failed: git fetch", count=6, backoff=[2, 5, 10, 15, 20, 30])
205
262
  def fetch(self, commit=None):
206
263
  if commit and not self.is_valid_sha(commit):
207
264
  commit = None
208
265
 
266
+ # Get configurable extra clone options
267
+ extra_fetch_options = config.get("git", "fetch_options", "")
268
+
209
269
  refspec = " ".join(self.default_refspecs + self.refspecs)
210
270
  with self.tools.cwd(self.path):
211
271
  log.info("Fetching {0} from {1}", commit or refspec or 'commits', self.url)
212
272
  self.tools.run(
213
- "git fetch {url} {what}",
273
+ "git fetch --force --prune {extra_fetch_options} {url} {what}",
274
+ extra_fetch_options=extra_fetch_options,
214
275
  url=self.url,
215
276
  what=commit or refspec or '',
216
277
  output_on_error=True)
217
278
 
218
- def checkout(self, rev, commit=None):
279
+ def checkout(self, rev, commit=None, submodules=False):
219
280
  if rev == self._last_rev:
220
281
  log.debug("Checkout skipped, already @ {}", rev)
221
282
  return False
222
- log.info("Checking out {0} in {1}", rev, self.path)
283
+ log.verbose("Checking out {0} in {1}", rev, self.path)
223
284
  with self.tools.cwd(self.path):
224
285
  try:
225
286
  self.tools.run("git checkout -f {rev}", rev=rev, output=False)
287
+ if submodules:
288
+ self._update_submodules()
226
289
  except Exception:
227
290
  self.fetch(commit=commit)
228
291
  try:
229
292
  self.tools.run("git checkout -f {rev}", rev=rev, output_on_error=True)
293
+ if submodules:
294
+ self._update_submodules()
230
295
  except Exception:
231
296
  raise_error("Commit does not exist in remote for '{}': {}", self.relpath, rev)
232
297
  self._original_head = False
233
298
  self._last_rev = rev
234
299
  return True
235
300
 
301
+ def _update_submodules(self):
302
+ with self.tools.cwd(self.path):
303
+ self.tools.run("git submodule update --init --recursive", output_on_error=True)
304
+
236
305
 
237
306
  _gits = {}
238
307
 
@@ -317,23 +386,101 @@ def influence(path, git_cls=GitInfluenceProvider):
317
386
  return _decorate
318
387
 
319
388
 
320
- class GitSrc(WorkspaceResource, FileInfluence):
321
- """ Clones a Git repo.
389
+ class ErrorDict(dict):
390
+ """ A dict that raises an error if the value of a key is None. """
391
+
392
+ def __init__(self, repo):
393
+ self.repo = repo
394
+
395
+ def __getitem__(self, key):
396
+ value = super().__getitem__(key)
397
+ raise_task_error_if(value is None, self.repo, "Git repository '{0}' referenced in influence collection before being cloned/checked out. Assign hash=true to the git requirement.", key)
398
+ return value
399
+
400
+
401
+ class Git(WorkspaceResource, FileInfluence):
402
+ """
403
+ Resource that clones and monitors a Git repo.
404
+
405
+ By default, the repo is cloned into a build directory named after
406
+ the resource. The 'path' parameter can be used to specify a different
407
+ location relative to the workspace root.
408
+
409
+ The path of the cloned repo is made available to consuming tasks
410
+ through their 'git' attribute. The 'git' attribute is a dictionary
411
+ where the key is the name of the git repository and the value is
412
+ the relative path to the repository from the consuming task's
413
+ workspace.
414
+
415
+ The resource influences the hash of consuming tasks, causing tasks
416
+ to be re-executed if the cloned repo is modified.
417
+
418
+ The plugin must be loaded before it can be used. This is done by
419
+ importing the module, or by adding the following line to the
420
+ configuration file:
421
+
422
+ .. code-block:: ini
423
+
424
+ [git]
425
+
426
+ Example:
427
+
428
+ .. code-block:: python
429
+
430
+ from jolt.plugins import git
431
+
432
+ class Example(Task):
433
+ requires = ["git:url=https://github.com/user/repo.git"]
434
+
435
+ def run(self, deps, tools):
436
+ self.info("The git repo is located at: {git[repo]}")
437
+ with tools.cwd(self.git["repo"]):
438
+ tools.run("make")
439
+
322
440
  """
441
+ name = "git"
323
442
 
324
- name = "git-src"
325
443
  url = Parameter(help="URL to the git repo to be cloned. Required.")
326
- sha = Parameter(required=False, help="Specific commit or tag to be checked out. Optional.")
444
+ """ URL to the git repo to be cloned. Required. """
445
+
446
+ rev = Parameter(required=False, help="Specific commit or tag to be checked out. Optional.")
447
+ """ Specific commit or tag to be checked out. Optional. """
448
+
449
+ hash = BooleanParameter(required=False, help="Let repo content influence the hash of consuming tasks.")
450
+ """ Let repo content influence the hash of consuming tasks. Default ``True``. Optional. """
451
+
327
452
  path = Parameter(required=False, help="Local path where the repository should be cloned.")
328
- defer = BooleanParameter(False, help="Defer cloning until a consumer task must be built.")
453
+ """ Alternative path where the repository should be cloned. Relative to ``joltdir``. Optional. """
454
+
455
+ submodules = BooleanParameter(default=False, help="Initialize and update git submodules after cloning.")
456
+ """ Initialize and update git submodules after cloning. Default ``False``. Optional. """
457
+
458
+ clean = BooleanParameter(default=False, help="Clean repository after it has been used")
459
+ """ Removes untracked files from the repository. """
460
+
329
461
  _revision = Export(value=lambda t: t._export_revision())
462
+ """ To worker exported value of the revision to be checked out. """
463
+
330
464
  _diff = Export(value=lambda t: t.git.diff(), encoded=True)
465
+ """ To worker exported value of the diff of the repo. """
331
466
 
332
467
  def __init__(self, *args, **kwargs):
333
468
  super().__init__(*args, **kwargs)
469
+ self._lock = RLock()
334
470
  self.joltdir = JoltLoader.get().joltdir
335
- self.relpath = str(self.path) or self._get_name()
336
- self.abspath = fs.path.join(self.joltdir, self.relpath)
471
+
472
+ # Set the path to the repo
473
+ if self.path.is_unset():
474
+ self.abspath = self.tools.builddir(utils.canonical(self.short_qualified_name), incremental="always", unique=False)
475
+ self.relpath = fs.path.relpath(self.abspath, self.tools.wsroot)
476
+ else:
477
+ self.abspath = fs.path.join(self.joltdir, str(self.path) or self._get_name())
478
+ self.relpath = fs.path.relpath(self.abspath, self.tools.wsroot)
479
+
480
+ self.abspath = fs.path.normpath(self.abspath)
481
+ self.relpath = fs.path.normpath(self.relpath)
482
+
483
+ # Create the git repository
337
484
  self.refspecs = kwargs.get("refspecs", [])
338
485
  self.git = new_git(self.url, self.abspath, self.relpath, self.refspecs)
339
486
 
@@ -344,26 +491,69 @@ class GitSrc(WorkspaceResource, FileInfluence):
344
491
  return name
345
492
 
346
493
  def _export_revision(self):
347
- return self.sha.value or self.git.head()
494
+ return self.rev.value or self.git.head()
348
495
 
349
496
  def _get_revision(self):
350
497
  if self._revision.is_imported:
351
498
  return self._revision.value
352
- if not self.sha.is_unset():
353
- return self.sha.get_value()
499
+ if not self.rev.is_unset():
500
+ return self.rev.get_value()
354
501
  return None
355
502
 
356
- def acquire(self, **kwargs):
503
+ def _assign_git(self, task, none=False):
504
+ if not hasattr(task, "git"):
505
+ task.git = ErrorDict(self)
506
+ if none:
507
+ # None means the git repo is not cloned or checked out
508
+ # and should not be included in the git dictionary
509
+ # of the consuming task yet. If the consuming task
510
+ # requires the git repo for its influence collection,
511
+ # the dict will raise an error. The solution is to
512
+ # assign hash=true to the git requirement which
513
+ # will cause the git repo to be cloned and checked out
514
+ # before the influence collection is performed.
515
+ task.git[self._get_name()] = None
516
+ else:
517
+ # Assign the git repo to the consuming task.
518
+ # The git repo is cloned and checked out before
519
+ # any influence collection is performed.
520
+ task.git[self._get_name()] = fs.path.relpath(self.abspath, task.joltdir)
521
+
522
+ def acquire(self, artifact, deps, tools, owner):
357
523
  self._acquire_ws()
524
+ self._assign_git(owner)
525
+ artifact.worktree = fs.path.relpath(self.abspath, owner.joltdir)
526
+
527
+ def release(self, artifact, deps, tools, owner):
528
+ if self.clean:
529
+ self.git.clean()
530
+ self.git.reset()
531
+
532
+ def prepare_ws_for(self, task):
533
+ """ Prepare the workspace for the task.
534
+
535
+ :param task: The task to prepare the workspace for.
536
+ """
537
+ if not self._must_influence():
538
+ # The content of the git repo is not required to influence the hash of the
539
+ # consumer task. The repo is therefore not cloned or checked out
540
+ # until the consumer is executed. Raise an error if the git repo
541
+ # is required for the influence collection of the consumer task.
542
+ self._assign_git(task, none=True)
543
+ return
544
+ # The content of the git repo is required to influence the hash of the consumer task.
545
+ self._assign_git(task)
358
546
 
359
- def acquire_ws(self):
360
- if self.defer is None or self.defer.is_false:
547
+ def acquire_ws(self, force=False):
548
+ """ Clone and/or checkout the git repo if required """
549
+ if force or self._must_influence() or self._revision.is_imported:
361
550
  self._acquire_ws()
362
551
 
552
+ @locked
363
553
  def _acquire_ws(self):
364
554
  commit = None
365
555
  if not self.git.is_cloned():
366
- self.git.clone()
556
+ self.git.clone(submodules=bool(self.submodules))
367
557
  if not self._revision.is_imported:
368
558
  self.git.diff_unchecked()
369
559
  else:
@@ -371,48 +561,43 @@ class GitSrc(WorkspaceResource, FileInfluence):
371
561
  rev = self._get_revision()
372
562
  if rev is not None:
373
563
  raise_task_error_if(
374
- not self._revision.is_imported and not self.sha.is_unset() and self.git.diff(), self,
375
- "explicit sha requested but git repo '{0}' has local changes", self.git.relpath)
564
+ not self._revision.is_imported and not self.rev.is_unset() and self.git.diff(), self,
565
+ "Explicit revision requested but git repo '{0}' has local changes, refusing checkout", self.git.relpath)
376
566
  # Should be safe to do this now
377
567
  rev = self.git.rev_parse(rev)
378
568
  if not self.git.is_head(rev) or self._revision.is_imported:
379
- if self.git.checkout(rev, commit=commit):
569
+ if self.git.checkout(rev, commit=commit, submodules=bool(self.submodules)):
380
570
  self.git.clean()
381
571
  self.git.patch(self._diff.value)
382
572
 
383
- def get_influence(self, task):
384
- return None
385
-
386
- def is_influenced_by(self, task, path):
387
- return fs.is_relative_to(path, self.abspath) and self.sha.is_set()
388
-
573
+ def _must_influence(self):
574
+ """ Check if the git repo must influence the hash of the consumer task."""
389
575
 
390
- TaskRegistry.get().add_task_class(GitSrc)
576
+ # If the hash parameter is set, honor it
577
+ if self.hash.is_set():
578
+ return self.hash
391
579
 
580
+ # If the revision parameter is not set, the git repo must influence the hash
581
+ if self.rev.is_unset():
582
+ return True
392
583
 
393
- class Git(GitSrc):
394
- """ Clones a Git repo.
584
+ # If the revision parameter is set, no influence is needed since the
585
+ # revision is fixed and repository content will not change.
586
+ return False
395
587
 
396
- Also influences the hash of consuming tasks, causing tasks to
397
- be re-executed if the cloned repo is modified.
398
-
399
- """
400
- name = "git"
401
- url = Parameter(help="URL to the git repo to be cloned. Required.")
402
- sha = Parameter(required=False, help="Specific commit or tag to be checked out. Optional.")
403
- path = Parameter(required=False, help="Local path where the repository should be cloned.")
404
- defer = None
405
- _revision = Export(value=lambda t: t._export_revision())
406
- _diff = Export(value=lambda t: t.git.diff(), encoded=True)
588
+ def is_influenced_by(self, task, path):
589
+ influencing = self._must_influence() or self.rev.is_set()
590
+ return influencing and fs.is_relative_to(path, self.abspath)
407
591
 
408
- def __init__(self, *args, **kwargs):
409
- super().__init__(*args, **kwargs)
410
- self.influence.append(self)
592
+ def _influence(self):
593
+ influence = super()._influence()
594
+ return influence + [self] if self._must_influence() else influence
411
595
 
596
+ @locked
412
597
  @utils.cached.instance
413
598
  def get_influence(self, task):
414
599
  if not self.git.is_cloned():
415
- self.git.clone()
600
+ self.git.clone(submodules=bool(self.submodules))
416
601
  if not self._revision.is_imported:
417
602
  self.git.diff_unchecked()
418
603
  rev = self._get_revision()
@@ -427,8 +612,5 @@ class Git(GitSrc):
427
612
 
428
613
  return "{0}: {1}".format(self.git.relpath, th)
429
614
 
430
- def is_influenced_by(self, task, path):
431
- return path.startswith(self.abspath + fs.sep)
432
-
433
615
 
434
616
  TaskRegistry.get().add_task_class(Git)
@@ -212,7 +212,7 @@ def filter(default: str = "*", param: bool = True, attr: str = "filter"):
212
212
  Task class decorator controlling the GTEST_FILTER environment variable.
213
213
 
214
214
  The variable instructs test applications to only run test-cases that matches
215
- a wildcard filter string (default: *).
215
+ a wildcard filter string (default: ``*``).
216
216
 
217
217
  The value is taken from the parameter ``filter`` which is created
218
218
  by default. If no parameter is created, the value is instead taken from the class
jolt/plugins/http.py CHANGED
@@ -23,7 +23,7 @@ class Http(cache.StorageProvider):
23
23
  def __init__(self, cache):
24
24
  super(Http, self).__init__()
25
25
  self._cache = cache
26
- self._uri = config.get(NAME, "uri", "http://cache.")
26
+ self._uri = config.get(NAME, "uri", "http://cache")
27
27
  self._uri = self._uri.rstrip("/")
28
28
  raise_error_if(not self._uri, "HTTP URI not configured")
29
29
  self._upload = config.getboolean(NAME, "upload", True)
@@ -0,0 +1,63 @@
1
+ from jolt import attributes
2
+
3
+
4
+ class Libtool(object):
5
+ def __init__(self, tools):
6
+ self.tools = tools
7
+
8
+ def relocate(self, artifact, dirs=["lib"], prefix=None):
9
+ prefix = str(artifact.strings.install_prefix) if prefix is None else prefix
10
+ for dir in dirs:
11
+ with self.tools.cwd(artifact.path, dir):
12
+ for file in self.tools.glob("*.la"):
13
+ self.tools.replace_in_file(file, prefix, artifact.final_path)
14
+
15
+
16
+ def relocate(dirs=["lib"]):
17
+ """
18
+ Relocate libtool archive files (.la) published by task.
19
+
20
+ When an artifact is published, all .la files found in the specified
21
+ directories will have their install prefix updated to the artifact's final path.
22
+ The original install prefix is stored in the artifact's strings metadata
23
+ under the key 'libtool_prefix' for use during unpacking.
24
+
25
+ :param dirs: List of directories inside the artifact to relocate .la files in.
26
+
27
+ """
28
+
29
+ def decorate(cls):
30
+ original_publish = cls.publish
31
+ original_unpack = cls.unpack
32
+
33
+ def publish(self, artifact, tools):
34
+ original_publish(self, artifact, tools)
35
+ lt = Libtool(tools)
36
+ lt.relocate(artifact, dirs, prefix=artifact.strings.install_prefix)
37
+ artifact.libtool_prefix = artifact.final_path
38
+
39
+ def unpack(self, artifact, tools):
40
+ original_unpack(self, artifact, tools)
41
+ lt = Libtool(tools)
42
+ lt.relocate(artifact, dirs, prefix=artifact.strings.libtool_prefix)
43
+ artifact.strings.libtool_prefix = None
44
+
45
+ cls.publish = publish
46
+ cls.unpack = unpack
47
+
48
+ return cls
49
+
50
+ return decorate
51
+
52
+
53
+ def requires():
54
+ """ Decorator to add Libtool requirement to a task. """
55
+
56
+ import jolt.pkgs.libtool
57
+
58
+ def decorate(cls):
59
+ cls = attributes.requires("requires_libtool")(cls)
60
+ cls.requires_libtool = ["libtool"]
61
+ return cls
62
+
63
+ return decorate