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/cli.py CHANGED
@@ -1,7 +1,7 @@
1
- from os import _exit, environ, getcwd
2
1
  import atexit
3
2
  import click
4
3
  import datetime
4
+ import os
5
5
  import platform
6
6
  import subprocess
7
7
  import sys
@@ -18,13 +18,12 @@ from jolt import log
18
18
  from jolt import __version__
19
19
  from jolt.log import logfile
20
20
  from jolt import config
21
- from jolt.loader import JoltLoader
21
+ from jolt.loader import JoltLoader, import_workspace
22
22
  from jolt import tools
23
23
  from jolt import utils
24
24
  from jolt.influence import HashInfluenceRegistry
25
25
  from jolt.options import JoltOptions
26
26
  from jolt import hooks
27
- from jolt.manifest import JoltManifest
28
27
  from jolt.error import JoltError
29
28
  from jolt.error import raise_error
30
29
  from jolt.error import raise_error_if
@@ -32,7 +31,7 @@ from jolt.error import raise_task_error_if
32
31
  from jolt.plugins import report
33
32
 
34
33
  debug_enabled = False
35
- workdir = getcwd()
34
+ workdir = os.getcwd()
36
35
 
37
36
 
38
37
  class ArgRequiredUnless(click.Argument):
@@ -56,10 +55,12 @@ class PluginGroup(click.Group):
56
55
 
57
56
  if cmd_name in ["export", "inspect"]:
58
57
  log.set_level(log.SILENCE)
59
- elif ctx.params.get("verbose", False):
60
- log.set_level(log.VERBOSE)
61
- elif ctx.params.get("extra_verbose", False):
58
+ elif ctx.params.get("verbose") >= 3:
59
+ log.set_level(log.EXCEPTION)
60
+ elif ctx.params.get("verbose") >= 2:
62
61
  log.set_level(log.DEBUG)
62
+ elif ctx.params.get("verbose") >= 1:
63
+ log.set_level(log.VERBOSE)
63
64
 
64
65
  config_files = ctx.params.get("config_file") or []
65
66
  for config_file in config_files:
@@ -74,10 +75,12 @@ class PluginGroup(click.Group):
74
75
 
75
76
  @click.group(cls=PluginGroup, invoke_without_command=True)
76
77
  @click.version_option(__version__)
77
- @click.option("-v", "--verbose", is_flag=True, help="Verbose output.")
78
- @click.option("-vv", "--extra-verbose", is_flag=True, help="Extra verbose output.")
78
+ @click.option("-v", "--verbose", count=True, help="Verbose output (repeat to raise verbosity).")
79
79
  @click.option("-c", "--config", "config_file", multiple=True, type=str,
80
80
  help="Load a configuration file or set a configuration key.")
81
+ @click.option("-C", "--chdir", type=str,
82
+ help="Change working directory before executing command.")
83
+ @click.option("--interpreter", "machine_interface", type=str, help="Used for debugging.", hidden=True)
81
84
  @click.option("-d", "--debugger", is_flag=True,
82
85
  help="Attach debugger on exception.")
83
86
  @click.option("-p", "--profile", is_flag=True, hidden=True,
@@ -88,6 +91,7 @@ class PluginGroup(click.Group):
88
91
  help="Add salt as task influence.")
89
92
  @click.option("-g", "--debug", is_flag=True, default=False, hidden=True,
90
93
  help="Start debug shell before executing task.")
94
+ @click.option("-m", "--mute", is_flag=True, help="Display task log only if it fails.")
91
95
  @click.option("-n", "--network", is_flag=True, default=False, hidden=True,
92
96
  help="Build on network.")
93
97
  @click.option("-l", "--local", is_flag=True, default=False, hidden=True,
@@ -98,8 +102,8 @@ class PluginGroup(click.Group):
98
102
  help="Number of tasks allowed to execute in parallel (1). ")
99
103
  @click.option("-h", "--help", is_flag=True, help="Show this message and exit.")
100
104
  @click.pass_context
101
- def cli(ctx, verbose, extra_verbose, config_file, debugger, profile,
102
- force, salt, debug, network, local, keep_going, jobs, help):
105
+ def cli(ctx, verbose, config_file, debugger, profile,
106
+ force, salt, debug, mute, network, local, keep_going, jobs, help, machine_interface, chdir):
103
107
  """
104
108
  A task execution tool.
105
109
 
@@ -123,14 +127,24 @@ def cli(ctx, verbose, extra_verbose, config_file, debugger, profile,
123
127
  global debug_enabled
124
128
  debug_enabled = debugger
125
129
 
130
+ if machine_interface:
131
+ log.enable_gdb()
132
+
126
133
  if ctx.invoked_subcommand not in ["log", "report"]:
127
134
  log.start_file_log()
128
135
 
136
+ if chdir:
137
+ global workdir
138
+ workdir = chdir
139
+ os.chdir(workdir)
140
+
141
+ log.verbose("Jolt version: {}", __version__)
129
142
  log.verbose("Jolt command: {}", " ".join([fs.path.basename(sys.argv[0])] + sys.argv[1:]))
130
- log.verbose("Jolt host: {}", environ.get("HOSTNAME", "localhost"))
143
+ log.verbose("Jolt host: {}", os.environ.get("HOSTNAME", "localhost"))
131
144
  log.verbose("Jolt install path: {}", fs.path.dirname(__file__))
145
+ log.verbose("Jolt workdir: {}", workdir)
132
146
 
133
- if ctx.invoked_subcommand in ["config", "log"]:
147
+ if ctx.invoked_subcommand in ["config", "executor", "log"]:
134
148
  # Don't attempt to load any task recipes as they might require
135
149
  # plugins that are not yet configured.
136
150
  return
@@ -142,27 +156,13 @@ def cli(ctx, verbose, extra_verbose, config_file, debugger, profile,
142
156
  print(ctx.get_help())
143
157
  sys.exit(0)
144
158
 
145
- manifest = JoltManifest()
146
- utils.call_and_catch(manifest.parse)
147
- manifest.process_import()
148
- ctx.obj["manifest"] = manifest
149
-
150
- if manifest.version:
151
- from jolt.version_utils import requirement, version
152
- req = requirement(manifest.version)
153
- ver = version(__version__)
154
- raise_error_if(not req.satisfied(ver),
155
- "This project requires Jolt version {} (running {})",
156
- req, __version__)
157
-
159
+ registry = TaskRegistry.get()
158
160
  loader = JoltLoader.get()
159
- tasks = loader.load()
160
- for cls in tasks:
161
- TaskRegistry.get().add_task_class(cls)
161
+ loader.load(registry)
162
162
 
163
163
  if ctx.invoked_subcommand in ["build", "clean"] and loader.joltdir:
164
164
  ctx.obj["workspace_lock"] = utils.LockFile(
165
- fs.path.join(loader.joltdir, "build"),
165
+ loader.build_path,
166
166
  log.info, "Workspace is locked by another process, please wait...")
167
167
  atexit.register(ctx.obj["workspace_lock"].close)
168
168
 
@@ -171,8 +171,8 @@ def cli(ctx, verbose, extra_verbose, config_file, debugger, profile,
171
171
  if ctx.invoked_subcommand is None:
172
172
  task = config.get("jolt", "default", "default")
173
173
  taskname, _ = utils.parse_task_name(task)
174
- if TaskRegistry.get().get_task_class(taskname) is not None:
175
- ctx.invoke(build, task=[task], force=force, salt=salt, debug=debug,
174
+ if registry.get_task_class(taskname) is not None:
175
+ ctx.invoke(build, task=[task], force=force, salt=salt, debug=debug, mute=mute,
176
176
  network=network, local=local, keep_going=keep_going, jobs=jobs)
177
177
  else:
178
178
  print(cli.get_help(ctx))
@@ -180,11 +180,8 @@ def cli(ctx, verbose, extra_verbose, config_file, debugger, profile,
180
180
 
181
181
 
182
182
  def _autocomplete_tasks(ctx, args, incomplete):
183
- manifest = JoltManifest()
184
- utils.call_and_catch(manifest.parse)
185
- manifest.process_import()
186
-
187
- tasks = JoltLoader.get().load()
183
+ loader = JoltLoader.get()
184
+ tasks = loader.load()
188
185
  tasks = [task.name for task in tasks if task.name.startswith(incomplete or '')]
189
186
  return sorted(tasks)
190
187
 
@@ -203,12 +200,13 @@ def _autocomplete_tasks(ctx, args, incomplete):
203
200
  @click.option("-n", "--network", is_flag=True, default=False, help="Distribute tasks to network workers.")
204
201
  @click.option("-s", "--salt", type=str, help="Add salt as hash influence for all tasks in dependency tree.", metavar="SALT")
205
202
  @click.option("-m", "--mute", is_flag=True, help="Display task log only if it fails.")
206
- @click.option("-v", "--verbose", is_flag=True, help="Verbose output.")
207
- @click.option("-vv", "--extra-verbose", is_flag=True, help="Extra verbose output.")
203
+ @click.option("-v", "--verbose", count=True, help="Verbose output (repeat to raise verbosity).")
208
204
  @click.option("--result", type=click.Path(), hidden=True,
209
205
  help="Write result manifest to this file.")
210
206
  @click.option("--no-download", is_flag=True, default=False,
211
- help="Don't download artifacts from remote storage")
207
+ help="Don't download any artifacts from remote storage")
208
+ @click.option("--no-download-persistent", is_flag=True, default=False,
209
+ help="Don't download persistent artifacts from remote storage (only session artifacts)")
212
210
  @click.option("--no-upload", is_flag=True, default=False,
213
211
  help="Don't upload artifacts to remote storage")
214
212
  @click.option("--download", is_flag=True, default=False,
@@ -219,12 +217,13 @@ def _autocomplete_tasks(ctx, args, incomplete):
219
217
  help="Don't prune cached artifacts from the build graph. This option can be used to populate the local cache with remotely cached dependency artifacts.")
220
218
  @click.option("--worker", is_flag=True, default=False,
221
219
  help="Run with the worker build strategy", hidden=True)
220
+ @click.option("--environ", type=click.Path(), help="Import build environment from protobuf", hidden=True)
222
221
  @click.pass_context
223
222
  @hooks.cli_build
224
223
  def build(ctx, task, network, keep_going, default, local,
225
- no_download, no_upload, download, upload, worker, force,
226
- salt, copy, debug, result, jobs, no_prune, verbose, extra_verbose,
227
- mute):
224
+ no_download, no_download_persistent, no_upload, download, upload, worker, force,
225
+ salt, copy, debug, result, jobs, no_prune, verbose,
226
+ mute, environ):
228
227
  """
229
228
  Build task artifact.
230
229
 
@@ -256,6 +255,7 @@ def build(ctx, task, network, keep_going, default, local,
256
255
  are removed before execution starts.
257
256
 
258
257
  """
258
+
259
259
  raise_error_if(network and local,
260
260
  "The -n and -l flags are mutually exclusive")
261
261
 
@@ -268,10 +268,12 @@ def build(ctx, task, network, keep_going, default, local,
268
268
  raise_error_if(no_upload and upload,
269
269
  "The --upload and --no-upload flags are mutually exclusive")
270
270
 
271
- if verbose:
272
- log.set_level(log.VERBOSE)
273
- elif extra_verbose:
271
+ if verbose >= 3:
272
+ log.set_level(log.EXCEPTION)
273
+ elif verbose >= 2:
274
274
  log.set_level(log.DEBUG)
275
+ elif verbose >= 1:
276
+ log.set_level(log.VERBOSE)
275
277
 
276
278
  ts_start = utils.duration()
277
279
  task = list(task)
@@ -283,6 +285,7 @@ def build(ctx, task, network, keep_going, default, local,
283
285
  else:
284
286
  _download = config.getboolean("jolt", "download", True)
285
287
  _upload = config.getboolean("jolt", "upload", True)
288
+ _download_session = _download
286
289
 
287
290
  if local:
288
291
  _download = False
@@ -290,17 +293,52 @@ def build(ctx, task, network, keep_going, default, local,
290
293
  else:
291
294
  if no_download:
292
295
  _download = False
296
+ _download_session = False
297
+ if no_download_persistent:
298
+ _download = False
293
299
  if no_upload:
294
300
  _upload = False
295
301
  if download:
296
302
  _download = True
303
+ _download_session = True
297
304
  if upload:
298
305
  _upload = True
299
306
 
307
+ if keep_going:
308
+ config.set_keep_going(True)
309
+
310
+ # Import build environment from protobuf if provided
311
+ buildenv = None
312
+ if environ:
313
+ with open(environ, "rb") as f:
314
+ from jolt import common_pb2 as common_pb
315
+ buildenv = common_pb.BuildEnvironment()
316
+ try:
317
+ buildenv.ParseFromString(f.read())
318
+ except Exception as e:
319
+ raise_error("Failed to parse build environment protobuf: {}", e)
320
+
321
+ # Import log level
322
+ log.set_level_pb(buildenv.loglevel)
323
+
324
+ # Import workspace
325
+ import_workspace(buildenv)
326
+
327
+ # Import configuration snippet
328
+ config.import_config(buildenv.config)
329
+
330
+ # Import configuration parameters (-c params.key)
331
+ config.import_params({param.key: param.value for param in buildenv.parameters})
332
+
333
+ # Import default parameters (-d taskname:param=value)
334
+ default = utils.as_list(default)
335
+ default += buildenv.task_default_parameters
336
+
300
337
  options = JoltOptions(
301
338
  network=network,
302
339
  local=local,
303
340
  download=_download,
341
+ download_session=_download_session,
304
342
  upload=_upload,
305
343
  keep_going=keep_going,
306
344
  default=default,
@@ -330,26 +368,20 @@ def build(ctx, task, network, keep_going, default, local,
330
368
  for params in default:
331
369
  registry.set_default_parameters(params)
332
370
 
333
- manifest = ctx.obj["manifest"]
334
-
335
- for mb in manifest.builds:
336
- for mt in mb.tasks:
337
- task.append(mt.name)
338
- for mt in mb.defaults:
339
- registry.set_default_parameters(mt.name)
340
-
341
- if force:
342
- for goal in task:
343
- registry.get_task(goal, manifest=manifest).taint = uuid.uuid4()
344
-
345
371
  log.info("Started: {}", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
346
372
 
347
- gb = graph.GraphBuilder(registry, acache, manifest, options, progress=True)
373
+ gb = graph.GraphBuilder(registry, acache, options, progress=True, buildenv=buildenv)
348
374
  dag = gb.build(task)
349
375
 
376
+ # If asked to force rebuild, taint all goal tasks
377
+ if force:
378
+ for goal in dag.goals:
379
+ goal.get_extended_task().taint()
380
+
350
381
  # Collect information about artifact presence before starting prune or build
351
- acache.precheck(dag.persistent_artifacts)
382
+ acache.precheck(dag.persistent_artifacts, remote=not local)
352
383
 
384
+ # Prune the graph to remove tasks that are already available locally or remotely
353
385
  if not no_prune:
354
386
  gp = graph.GraphPruner(acache, strategy)
355
387
  dag = gp.prune(dag)
@@ -358,7 +390,7 @@ def build(ctx, task, network, keep_going, default, local,
358
390
  goal_task_duration = 0
359
391
 
360
392
  session = executors.create_session(dag) if options.network else {}
361
- queue = scheduler.TaskQueue(strategy, acache, session)
393
+ queue = scheduler.TaskQueue()
362
394
 
363
395
  try:
364
396
  if not dag.has_tasks():
@@ -372,16 +404,20 @@ def build(ctx, task, network, keep_going, default, local,
372
404
  debug=debug)
373
405
 
374
406
  with progress:
407
+ in_progress = set()
408
+
375
409
  while dag.has_tasks() or not queue.empty():
376
410
  # Find all tasks ready to be executed
377
- leafs = dag.select(lambda graph, task: task.is_ready())
411
+ leafs = dag.select(lambda graph, task: task.is_ready() and task not in in_progress)
378
412
 
379
413
  # Order the tasks by their weights to improve build times
380
414
  leafs.sort(key=lambda x: x.weight)
381
415
 
382
416
  while leafs:
383
417
  task = leafs.pop()
384
- queue.submit(task)
418
+ executor = strategy.create_executor(session, task)
419
+ queue.submit(executor)
420
+ in_progress.add(task)
385
421
 
386
422
  task, error = queue.wait()
387
423
 
@@ -391,6 +427,7 @@ def build(ctx, task, network, keep_going, default, local,
391
427
  elif task.is_goal() and task.duration_running:
392
428
  goal_task_duration += task.duration_running.seconds
393
429
 
430
+ # Unpack tasks with overridden unpack() method
394
431
  if not task.is_resource():
395
432
  if no_prune and task.task.unpack.__func__ is not Task.unpack:
396
433
  with acache.get_context(task):
@@ -398,8 +435,13 @@ def build(ctx, task, network, keep_going, default, local,
398
435
 
399
436
  progress.update(1)
400
437
 
438
+ if no_prune and task.is_workspace_resource():
439
+ task.task.acquire_ws(force=True)
440
+
401
441
  if not keep_going and error is not None:
402
442
  queue.abort()
443
+ executors.shutdown()
444
+ task.raise_for_status()
403
445
  raise error
404
446
 
405
447
  if dag.failed or dag.unstable:
@@ -407,6 +449,8 @@ def build(ctx, task, network, keep_going, default, local,
407
449
  for failed in dag.failed + dag.unstable:
408
450
  log.error("- {}", failed.log_name.strip("()"))
409
451
 
452
+ for failed_task in dag.failed:
453
+ failed_task.raise_for_status()
410
454
  if dag.failed:
411
455
  raise_error("No more tasks could be executed")
412
456
 
@@ -415,12 +459,15 @@ def build(ctx, task, network, keep_going, default, local,
415
459
  log.warning("Interrupted by user")
416
460
  try:
417
461
  queue.abort()
462
+ executors.shutdown()
418
463
  sys.exit(1)
419
464
  except KeyboardInterrupt:
420
465
  print()
421
466
  log.warning("Interrupted again, exiting")
422
- _exit(1)
467
+ os._exit(1)
423
468
  finally:
469
+ queue.shutdown()
470
+
424
471
  for task in goal_tasks:
425
472
  for artifact in task.artifacts:
426
473
  if acache.is_available_locally(artifact):
@@ -467,7 +514,7 @@ def clean(ctx, task, deps, expired):
467
514
  if task:
468
515
  task = [utils.stable_task_name(t) for t in task]
469
516
  registry = TaskRegistry.get()
470
- dag = graph.GraphBuilder(registry, acache, ctx.obj["manifest"]).build(task)
517
+ dag = graph.GraphBuilder(registry, acache).build(task)
471
518
  if deps:
472
519
  tasks = dag.tasks
473
520
  else:
@@ -614,7 +661,7 @@ def display(ctx, task, reverse=None, show_cache=False, prune=False):
614
661
  registry = TaskRegistry.get()
615
662
  options = JoltOptions()
616
663
  acache = cache.ArtifactCache.get(options)
617
- gb = graph.GraphBuilder(registry, acache, ctx.obj["manifest"])
664
+ gb = graph.GraphBuilder(registry, acache)
618
665
  dag = gb.build(task, influence=show_cache)
619
666
 
620
667
  if reverse:
@@ -679,7 +726,14 @@ def docs():
679
726
  """
680
727
  Opens the Jolt documentation in the default webbrowser.
681
728
  """
682
- webbrowser.open(config.get("jolt", "docs", "http://jolt.readthedocs.io/"))
729
+ url = config.get("jolt", "docs", "http://jolt.readthedocs.io/")
730
+ success = False
731
+ try:
732
+ success = webbrowser.open(url)
733
+ except Exception:
734
+ pass
735
+ if not success:
736
+ print(f"Failed to open web browser. Visit {url} manually.")
683
737
 
684
738
 
685
739
  @cli.command()
@@ -704,15 +758,14 @@ def download(ctx, task, deps, copy, copy_all):
704
758
  if copy_all:
705
759
  deps = True
706
760
 
707
- manifest = ctx.obj["manifest"]
708
761
  options = JoltOptions()
709
762
  acache = cache.ArtifactCache.get(options)
710
763
  hooks.TaskHookRegistry.get(options)
711
764
  executors = scheduler.ExecutorRegistry.get(options)
712
765
  registry = TaskRegistry.get()
713
766
  strategy = scheduler.DownloadStrategy(executors, acache)
714
- queue = scheduler.TaskQueue(strategy, acache, {})
715
- gb = graph.GraphBuilder(registry, acache, manifest, options, progress=True)
767
+ queue = scheduler.TaskQueue()
768
+ gb = graph.GraphBuilder(registry, acache, options, progress=True)
716
769
  dag = gb.build(task)
717
770
 
718
771
  if not deps:
@@ -725,12 +778,16 @@ def download(ctx, task, deps, copy, copy_all):
725
778
 
726
779
  try:
727
780
  with log.progress("Progress", dag.number_of_tasks(), " tasks", estimates=False, debug=False) as p:
781
+ in_progress = set()
782
+
728
783
  while dag.has_tasks() or not queue.empty():
729
- leafs = dag.select(lambda graph, task: task.is_ready())
784
+ leafs = dag.select(lambda graph, task: task.is_ready() and task not in in_progress)
730
785
 
731
786
  while leafs:
732
787
  task = leafs.pop()
733
- queue.submit(task)
788
+ executor = strategy.create_executor({}, task)
789
+ queue.submit(executor)
790
+ in_progress.add(task)
734
791
 
735
792
  task, error = queue.wait()
736
793
  p.update(1)
@@ -753,62 +810,19 @@ def download(ctx, task, deps, copy, copy_all):
753
810
  log.warning("Interrupted by user")
754
811
  try:
755
812
  queue.abort()
813
+ executors.shutdown()
756
814
  sys.exit(1)
757
815
  except KeyboardInterrupt:
758
816
  print()
759
817
  log.warning("Interrupted again, exiting")
760
- _exit(1)
818
+ os._exit(1)
819
+
761
820
  except Exception as e:
762
821
  log.set_interactive(True)
763
822
  raise e
764
823
 
765
-
766
- @cli.command(hidden=True)
767
- @click.argument("task", type=str, nargs=-1, required=True)
768
- @click.option("-r", "--remove", is_flag=True, help="Remove tasks from existing manifest.")
769
- @click.option("-d", "--default", type=str, multiple=True, help="Override default parameter values.")
770
- @click.option("-o", "--output", type=str, default="default.joltxmanifest", help="Manifest filename.")
771
- @click.pass_context
772
- def freeze(ctx, task, default, output, remove):
773
- """
774
- Freeze the identity of a task.
775
-
776
- <WIP>
777
- """
778
- manifest = ctx.obj["manifest"]
779
-
780
- options = JoltOptions(default=default)
781
- acache = cache.ArtifactCache.get(options)
782
- scheduler.ExecutorRegistry.get(options)
783
- registry = TaskRegistry.get()
784
-
785
- for params in default:
786
- registry.set_default_parameters(params)
787
-
788
- gb = graph.GraphBuilder(registry, acache, manifest)
789
- dag = gb.build(task)
790
-
791
- available, missing = acache.availability(dag.persistent_artifacts)
792
-
793
- for artifact in missing:
794
- raise_task_error_if(
795
- not remove, artifact.get_task(),
796
- "Task artifact is not available in any cache, build it first")
797
-
798
- for task in dag.tasks:
799
- if task.is_resource() or not task.is_cacheable():
800
- continue
801
- manifest_task = manifest.find_task(task)
802
- if remove and manifest_task:
803
- manifest.remove_task(manifest_task)
804
- continue
805
- if not remove:
806
- if not manifest_task:
807
- manifest_task = manifest.create_task()
808
- manifest_task.name = task.qualified_name
809
- manifest_task.identity = task.identity
810
-
811
- manifest.write(fs.path.join(JoltLoader.get().joltdir, output))
824
+ finally:
825
+ queue.shutdown()
812
826
 
813
827
 
814
828
  @cli.command(name="list")
@@ -846,7 +860,7 @@ def _list(ctx, task=None, all=False, reverse=None):
846
860
  reverse = [utils.stable_task_name(t) for t in utils.as_list(reverse or [])]
847
861
 
848
862
  try:
849
- dag = graph.GraphBuilder(registry, acache, ctx.obj["manifest"]).build(task, influence=False)
863
+ dag = graph.GraphBuilder(registry, acache).build(task, influence=False)
850
864
  except JoltError as e:
851
865
  raise e
852
866
  except Exception:
@@ -891,7 +905,7 @@ def _log(follow, delete):
891
905
  fs.unlink(file)
892
906
  else:
893
907
  t = tools.Tools()
894
- configured_pager = config.get("jolt", "pager", environ.get("PAGER", None))
908
+ configured_pager = config.get("jolt", "pager", os.environ.get("PAGER", None))
895
909
  for pager in [configured_pager, "less", "more", "cat"]:
896
910
  if pager and t.which(pager):
897
911
  return subprocess.call("{1} {0}".format(log.logfiles[-1], pager), shell=True)
@@ -946,9 +960,8 @@ def inspect(ctx, task, influence=False, artifact=False, salt=None):
946
960
 
947
961
  print()
948
962
  print(" Requirements")
949
- manifest = ctx.obj["manifest"]
950
963
  try:
951
- task = task_registry.get_task(task_name, manifest=manifest)
964
+ task = task_registry.get_task(task_name)
952
965
  for req in sorted(utils.as_list(utils.call_or_return(task, task.requires))):
953
966
  print(" {0}".format(task.tools.expand(req)))
954
967
  if not task.requires:
@@ -970,7 +983,7 @@ def inspect(ctx, task, influence=False, artifact=False, salt=None):
970
983
  if artifact:
971
984
  options = JoltOptions(salt=salt)
972
985
  acache = cache.ArtifactCache.get()
973
- builder = graph.GraphBuilder(task_registry, acache, manifest, options)
986
+ builder = graph.GraphBuilder(task_registry, acache, options)
974
987
  dag = builder.build([task.qualified_name])
975
988
  tasks = dag.select(lambda graph, node: node.task is task)
976
989
  assert len(tasks) == 1, "graph produced multiple tasks, one expected"
@@ -1034,7 +1047,7 @@ def _export(ctx, task):
1034
1047
  executors = scheduler.ExecutorRegistry.get()
1035
1048
  strategy = scheduler.LocalStrategy(executors, acache)
1036
1049
 
1037
- dag = graph.GraphBuilder(registry, acache, ctx.obj["manifest"])
1050
+ dag = graph.GraphBuilder(registry, acache)
1038
1051
  dag = dag.build(task)
1039
1052
 
1040
1053
  gp = graph.GraphPruner(acache, strategy)
@@ -1064,7 +1077,7 @@ def _export(ctx, task):
1064
1077
  for task in context.tasks:
1065
1078
  for artifact in task.artifacts:
1066
1079
  raise_task_error_if(
1067
- artifact.is_temporary(), task,
1080
+ not task.is_resource() and artifact.is_temporary(), task,
1068
1081
  "Task artifact not found in local cache, build it first")
1069
1082
 
1070
1083
  visitor = Export()
@@ -1105,15 +1118,15 @@ def _report(ctx):
1105
1118
  session=True,
1106
1119
  tools=t)
1107
1120
 
1108
- with t.tmpdir("report") as tmp, t.cwd(tmp.path):
1121
+ with t.tmpdir("report") as tmp, t.cwd(tmp):
1109
1122
  log.info("Collecting environment")
1110
1123
  env = ""
1111
- for key, val in environ.items():
1124
+ for key, val in os.environ.items():
1112
1125
  env += f"{key} = {val}\n"
1113
1126
  t.write_file("environ.txt", env, expand=False)
1114
1127
 
1115
1128
  log.info("Collecting configuration")
1116
- config.save(tmp.path)
1129
+ config.save(tmp)
1117
1130
  artifact.collect("*.conf", "configs/")
1118
1131
 
1119
1132
  log.info("Collecting platform information")