jolt 0.9.123__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 (196) 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 +832 -362
  6. jolt/chroot.py +156 -0
  7. jolt/cli.py +281 -162
  8. jolt/common_pb2.py +63 -0
  9. jolt/common_pb2_grpc.py +4 -0
  10. jolt/config.py +98 -41
  11. jolt/error.py +19 -4
  12. jolt/filesystem.py +2 -6
  13. jolt/graph.py +705 -117
  14. jolt/hooks.py +43 -0
  15. jolt/influence.py +122 -3
  16. jolt/loader.py +369 -121
  17. jolt/log.py +225 -63
  18. jolt/manifest.py +28 -38
  19. jolt/options.py +35 -10
  20. jolt/pkgs/abseil.py +42 -0
  21. jolt/pkgs/asio.py +25 -0
  22. jolt/pkgs/autoconf.py +41 -0
  23. jolt/pkgs/automake.py +41 -0
  24. jolt/pkgs/b2.py +31 -0
  25. jolt/pkgs/boost.py +111 -0
  26. jolt/pkgs/boringssl.py +32 -0
  27. jolt/pkgs/busybox.py +39 -0
  28. jolt/pkgs/bzip2.py +43 -0
  29. jolt/pkgs/cares.py +29 -0
  30. jolt/pkgs/catch2.py +36 -0
  31. jolt/pkgs/cbindgen.py +17 -0
  32. jolt/pkgs/cista.py +19 -0
  33. jolt/pkgs/clang.py +44 -0
  34. jolt/pkgs/cli11.py +24 -0
  35. jolt/pkgs/cmake.py +48 -0
  36. jolt/pkgs/cpython.py +196 -0
  37. jolt/pkgs/crun.py +29 -0
  38. jolt/pkgs/curl.py +38 -0
  39. jolt/pkgs/dbus.py +18 -0
  40. jolt/pkgs/double_conversion.py +24 -0
  41. jolt/pkgs/fastfloat.py +21 -0
  42. jolt/pkgs/ffmpeg.py +28 -0
  43. jolt/pkgs/flatbuffers.py +29 -0
  44. jolt/pkgs/fmt.py +27 -0
  45. jolt/pkgs/fstree.py +20 -0
  46. jolt/pkgs/gflags.py +18 -0
  47. jolt/pkgs/glib.py +18 -0
  48. jolt/pkgs/glog.py +25 -0
  49. jolt/pkgs/glslang.py +21 -0
  50. jolt/pkgs/golang.py +16 -11
  51. jolt/pkgs/googlebenchmark.py +18 -0
  52. jolt/pkgs/googletest.py +46 -0
  53. jolt/pkgs/gperf.py +15 -0
  54. jolt/pkgs/grpc.py +73 -0
  55. jolt/pkgs/hdf5.py +19 -0
  56. jolt/pkgs/help2man.py +14 -0
  57. jolt/pkgs/inja.py +28 -0
  58. jolt/pkgs/jsoncpp.py +31 -0
  59. jolt/pkgs/libarchive.py +43 -0
  60. jolt/pkgs/libcap.py +44 -0
  61. jolt/pkgs/libdrm.py +44 -0
  62. jolt/pkgs/libedit.py +42 -0
  63. jolt/pkgs/libevent.py +31 -0
  64. jolt/pkgs/libexpat.py +27 -0
  65. jolt/pkgs/libfastjson.py +21 -0
  66. jolt/pkgs/libffi.py +16 -0
  67. jolt/pkgs/libglvnd.py +30 -0
  68. jolt/pkgs/libogg.py +28 -0
  69. jolt/pkgs/libpciaccess.py +18 -0
  70. jolt/pkgs/libseccomp.py +21 -0
  71. jolt/pkgs/libtirpc.py +24 -0
  72. jolt/pkgs/libtool.py +42 -0
  73. jolt/pkgs/libunwind.py +35 -0
  74. jolt/pkgs/libva.py +18 -0
  75. jolt/pkgs/libvorbis.py +33 -0
  76. jolt/pkgs/libxml2.py +35 -0
  77. jolt/pkgs/libxslt.py +17 -0
  78. jolt/pkgs/libyajl.py +16 -0
  79. jolt/pkgs/llvm.py +81 -0
  80. jolt/pkgs/lua.py +54 -0
  81. jolt/pkgs/lz4.py +26 -0
  82. jolt/pkgs/m4.py +14 -0
  83. jolt/pkgs/make.py +17 -0
  84. jolt/pkgs/mesa.py +81 -0
  85. jolt/pkgs/meson.py +17 -0
  86. jolt/pkgs/mstch.py +28 -0
  87. jolt/pkgs/mysql.py +60 -0
  88. jolt/pkgs/nasm.py +49 -0
  89. jolt/pkgs/ncurses.py +30 -0
  90. jolt/pkgs/ng_log.py +25 -0
  91. jolt/pkgs/ninja.py +45 -0
  92. jolt/pkgs/nlohmann_json.py +25 -0
  93. jolt/pkgs/nodejs.py +19 -11
  94. jolt/pkgs/opencv.py +24 -0
  95. jolt/pkgs/openjdk.py +26 -0
  96. jolt/pkgs/openssl.py +103 -0
  97. jolt/pkgs/paho.py +76 -0
  98. jolt/pkgs/patchelf.py +16 -0
  99. jolt/pkgs/perl.py +42 -0
  100. jolt/pkgs/pkgconfig.py +64 -0
  101. jolt/pkgs/poco.py +39 -0
  102. jolt/pkgs/protobuf.py +77 -0
  103. jolt/pkgs/pugixml.py +27 -0
  104. jolt/pkgs/python.py +19 -0
  105. jolt/pkgs/qt.py +35 -0
  106. jolt/pkgs/rapidjson.py +26 -0
  107. jolt/pkgs/rapidyaml.py +28 -0
  108. jolt/pkgs/re2.py +30 -0
  109. jolt/pkgs/re2c.py +17 -0
  110. jolt/pkgs/readline.py +15 -0
  111. jolt/pkgs/rust.py +41 -0
  112. jolt/pkgs/sdl.py +28 -0
  113. jolt/pkgs/simdjson.py +27 -0
  114. jolt/pkgs/soci.py +46 -0
  115. jolt/pkgs/spdlog.py +29 -0
  116. jolt/pkgs/spirv_llvm.py +21 -0
  117. jolt/pkgs/spirv_tools.py +24 -0
  118. jolt/pkgs/sqlite.py +83 -0
  119. jolt/pkgs/ssl.py +12 -0
  120. jolt/pkgs/texinfo.py +15 -0
  121. jolt/pkgs/tomlplusplus.py +22 -0
  122. jolt/pkgs/wayland.py +26 -0
  123. jolt/pkgs/x11.py +58 -0
  124. jolt/pkgs/xerces_c.py +20 -0
  125. jolt/pkgs/xorg.py +360 -0
  126. jolt/pkgs/xz.py +29 -0
  127. jolt/pkgs/yamlcpp.py +30 -0
  128. jolt/pkgs/zeromq.py +47 -0
  129. jolt/pkgs/zlib.py +87 -0
  130. jolt/pkgs/zstd.py +33 -0
  131. jolt/plugins/alias.py +3 -0
  132. jolt/plugins/allure.py +5 -2
  133. jolt/plugins/autotools.py +66 -0
  134. jolt/plugins/cache.py +133 -0
  135. jolt/plugins/cmake.py +74 -6
  136. jolt/plugins/conan.py +238 -0
  137. jolt/plugins/cxx.py +698 -0
  138. jolt/plugins/cxxinfo.py +7 -0
  139. jolt/plugins/dashboard.py +1 -1
  140. jolt/plugins/docker.py +80 -23
  141. jolt/plugins/email.py +2 -2
  142. jolt/plugins/email.xslt +144 -101
  143. jolt/plugins/environ.py +11 -0
  144. jolt/plugins/fetch.py +141 -0
  145. jolt/plugins/gdb.py +39 -19
  146. jolt/plugins/gerrit.py +1 -14
  147. jolt/plugins/git.py +283 -85
  148. jolt/plugins/googletest.py +2 -1
  149. jolt/plugins/http.py +36 -38
  150. jolt/plugins/libtool.py +63 -0
  151. jolt/plugins/linux.py +990 -0
  152. jolt/plugins/logstash.py +4 -4
  153. jolt/plugins/meson.py +61 -0
  154. jolt/plugins/ninja-compdb.py +99 -30
  155. jolt/plugins/ninja.py +468 -166
  156. jolt/plugins/paths.py +11 -1
  157. jolt/plugins/pkgconfig.py +219 -0
  158. jolt/plugins/podman.py +136 -92
  159. jolt/plugins/python.py +137 -0
  160. jolt/plugins/remote_execution/__init__.py +0 -0
  161. jolt/plugins/remote_execution/administration_pb2.py +46 -0
  162. jolt/plugins/remote_execution/administration_pb2_grpc.py +170 -0
  163. jolt/plugins/remote_execution/log_pb2.py +32 -0
  164. jolt/plugins/remote_execution/log_pb2_grpc.py +68 -0
  165. jolt/plugins/remote_execution/scheduler_pb2.py +41 -0
  166. jolt/plugins/remote_execution/scheduler_pb2_grpc.py +141 -0
  167. jolt/plugins/remote_execution/worker_pb2.py +38 -0
  168. jolt/plugins/remote_execution/worker_pb2_grpc.py +112 -0
  169. jolt/plugins/report.py +12 -2
  170. jolt/plugins/rust.py +25 -0
  171. jolt/plugins/scheduler.py +710 -0
  172. jolt/plugins/selfdeploy/setup.py +8 -4
  173. jolt/plugins/selfdeploy.py +138 -88
  174. jolt/plugins/strings.py +35 -22
  175. jolt/plugins/symlinks.py +26 -11
  176. jolt/plugins/telemetry.py +5 -2
  177. jolt/plugins/timeline.py +13 -3
  178. jolt/plugins/volume.py +46 -48
  179. jolt/scheduler.py +589 -192
  180. jolt/tasks.py +625 -121
  181. jolt/templates/timeline.html.template +44 -47
  182. jolt/timer.py +22 -0
  183. jolt/tools.py +638 -282
  184. jolt/utils.py +211 -7
  185. jolt/version.py +1 -1
  186. jolt/xmldom.py +12 -2
  187. {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/METADATA +97 -38
  188. jolt-0.9.435.dist-info/RECORD +207 -0
  189. {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/WHEEL +1 -1
  190. jolt/plugins/amqp.py +0 -834
  191. jolt/plugins/debian.py +0 -338
  192. jolt/plugins/ftp.py +0 -181
  193. jolt/plugins/repo.py +0 -253
  194. jolt-0.9.123.dist-info/RECORD +0 -77
  195. {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/entry_points.txt +0 -0
  196. {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/top_level.txt +0 -0
jolt/log.py CHANGED
@@ -1,7 +1,9 @@
1
1
  from __future__ import print_function
2
+ import glob
2
3
  import os
3
4
  import re
4
5
  import sys
6
+ import time
5
7
  import tqdm
6
8
  if os.name == "nt":
7
9
  # FIXME: Workaround to make tqdm behave correctly on Windows
@@ -23,34 +25,40 @@ from jolt import config
23
25
  from jolt.error import JoltError
24
26
  from jolt import filesystem as fs
25
27
  from jolt import colors
28
+ from jolt import common_pb2 as common_pb
29
+ from jolt import utils
26
30
 
27
31
 
28
- default_path = fs.path.join(config.get_logpath(), "jolt.log")
29
- logfile = config.get("jolt", "logfile", default_path)
30
- logsize = config.getsize("jolt", "logsize", os.environ.get("JOLT_LOGSIZE", 10 * 1024 ** 2)) # 10MiB
31
- logcount = config.getint("jolt", "logcount", os.environ.get("JOLT_LOGCOUNT", 1))
32
+ current_time = datetime.now().strftime("%Y-%m-%dT%H%M%S.%f")
33
+ logpath = config.get_logpath()
34
+ logfile = fs.path.join(logpath, f"{current_time}.log")
35
+ logcount = config.getint("jolt", "logcount", os.environ.get("JOLT_LOGCOUNT", 100))
36
+ logfiles = list(sorted(glob.glob(os.path.join(logpath, "*T*.log"))))
32
37
 
33
38
  dirpath = fs.path.dirname(logfile)
34
39
  if not fs.path.exists(dirpath):
35
40
  fs.makedirs(dirpath)
36
- with open(logfile, "a") as f:
37
- f.write("--------------------------------------------------------------------------------\n")
38
41
 
39
42
  ################################################################################
40
43
 
41
- ERROR = logging.ERROR
42
- WARNING = logging.WARNING
43
- INFO = logging.INFO
44
- VERBOSE = 15
45
- DEBUG = logging.DEBUG
46
- EXCEPTION = logging.DEBUG + 1
47
- STDOUT = logging.INFO + 1
48
- STDERR = logging.ERROR + 1
49
- SILENCE = STDERR + 1
44
+ ERROR = common_pb.LogLevel.ERROR + 1
45
+ WARNING = common_pb.LogLevel.WARNING + 1
46
+ INFO = common_pb.LogLevel.INFO + 1
47
+ VERBOSE = common_pb.LogLevel.VERBOSE + 1
48
+ DEBUG = common_pb.LogLevel.DEBUG + 1
49
+ EXCEPTION = common_pb.LogLevel.EXCEPTION + 1
50
+ STDOUT = common_pb.LogLevel.STDOUT + 1
51
+ STDERR = common_pb.LogLevel.STDERR + 1
52
+ SILENCE = STDERR + 2
53
+
50
54
  logging.addLevelName(VERBOSE, "VERBOSE")
51
55
  logging.addLevelName(STDOUT, "STDOUT")
52
56
  logging.addLevelName(STDERR, "STDERR")
53
57
  logging.addLevelName(EXCEPTION, "EXCEPT")
58
+ logging.addLevelName(WARNING, "WARNING")
59
+ logging.addLevelName(INFO, "INFO")
60
+ logging.addLevelName(ERROR, "ERROR")
61
+ logging.addLevelName(DEBUG, "DEBUG")
54
62
 
55
63
  logging.raiseExceptions = False
56
64
 
@@ -135,13 +143,37 @@ class TqdmStream(object):
135
143
  getattr(self.stream, 'flush', lambda: None)()
136
144
 
137
145
 
146
+ class ThreadPrefix(logging.LoggerAdapter):
147
+ thread_prefix = {}
148
+
149
+ def process(self, msg, kwargs):
150
+ tid = threading.current_thread()
151
+ if tid in self.thread_prefix:
152
+ msg = f"[{self.thread_prefix[tid]}] {msg}"
153
+ return msg, kwargs
154
+
155
+ def set_thread_prefix(self, prefix):
156
+ tid = threading.current_thread()
157
+ self.thread_prefix[tid] = prefix
158
+
159
+ def clear_thread_prefix(self):
160
+ tid = threading.current_thread()
161
+ if tid in self.thread_prefix:
162
+ del self.thread_prefix[tid]
163
+
164
+
138
165
  # silence root logger
139
166
  _root = logging.getLogger()
140
167
  _root.setLevel(logging.CRITICAL)
141
168
 
142
- # create jolt logger
169
+ # create jolt logger and protect it's methods against interrupts
143
170
  _logger = logging.getLogger('jolt')
144
- _logger.setLevel(logging.DEBUG)
171
+ _logger.setLevel(EXCEPTION)
172
+ _logger.handle = utils.delay_interrupt(_logger.handle)
173
+ _logger.log = utils.delay_interrupt(_logger.log)
174
+ _logger.addHandler = utils.delay_interrupt(_logger.addHandler)
175
+ _logger.removeHandler = utils.delay_interrupt(_logger.removeHandler)
176
+
145
177
 
146
178
  _console_formatter = ConsoleFormatter('[{levelname:>7}] {message}', '{message}')
147
179
 
@@ -149,91 +181,134 @@ if sys.stdout.isatty() and sys.stderr.isatty():
149
181
  _stdout = logging.StreamHandler(TqdmStream(sys.stdout))
150
182
  else:
151
183
  _stdout = logging.StreamHandler(sys.stdout)
152
- _stdout.setLevel(INFO)
153
184
  _stdout.setFormatter(_console_formatter)
154
185
  _stdout.addFilter(Filter(lambda r: r.levelno < ERROR))
186
+ _stdout.addFilter(Filter(lambda r: r.levelno != EXCEPTION))
155
187
 
156
188
  if sys.stdout.isatty() and sys.stderr.isatty():
157
189
  _stderr = logging.StreamHandler(TqdmStream(sys.stdout))
158
190
  else:
159
191
  _stderr = logging.StreamHandler(sys.stderr)
160
- _stderr.setLevel(INFO)
161
192
  _stderr.setFormatter(_console_formatter)
162
- _stderr.addFilter(Filter(lambda r: r.levelno >= ERROR))
163
- _stderr.addFilter(Filter(lambda r: r.levelno != EXCEPTION))
164
-
165
- _file = logging.handlers.RotatingFileHandler(logfile, maxBytes=logsize, backupCount=logcount)
166
- _file.setLevel(logging.DEBUG)
167
- _file_formatter = Formatter('{asctime} [{levelname:>7}] {message}')
168
- _file.setFormatter(_file_formatter)
193
+ _stderr.addFilter(Filter(lambda r: r.levelno >= ERROR or r.levelno == EXCEPTION))
169
194
 
170
195
  _logger.addHandler(_stdout)
171
196
  _logger.addHandler(_stderr)
172
- _logger.addHandler(_file)
197
+ _logger_frontend = ThreadPrefix(_logger, {})
198
+
199
+ _file_formatter = Formatter('{asctime} [{levelname:>7}] {message}')
200
+
201
+
202
+ def start_file_log():
203
+ global logfiles
204
+
205
+ if len(logfiles) >= logcount:
206
+ outdated = logfiles[:len(logfiles) - logcount + 1]
207
+ logfiles = logfiles[-logcount + 1:]
208
+ for file in outdated:
209
+ os.unlink(file)
210
+
211
+ _file = logging.FileHandler(logfile)
212
+ _file.setLevel(EXCEPTION)
213
+ _file.setFormatter(_file_formatter)
214
+ _logger.addHandler(_file)
215
+
216
+
217
+ def log(level, message, created=None, context=None, prefix=False):
218
+ created = created or time.time()
219
+ message = f"[{context}] {message}" if context else message
220
+ record = logging.LogRecord(
221
+ name="log",
222
+ level=level,
223
+ pathname=__file__,
224
+ lineno=0,
225
+ msg=message,
226
+ args={},
227
+ exc_info=None,
228
+ )
229
+ record.created = created
230
+ record.prefix = prefix
231
+ _logger.handle(record)
173
232
 
174
233
 
175
234
  def info(fmt, *args, **kwargs):
176
- _logger.info(fmt, *args, **kwargs)
235
+ _logger_frontend.log(INFO, fmt, *args, **kwargs)
177
236
 
178
237
 
179
238
  def warning(fmt, *args, **kwargs):
180
- _logger.warning(fmt, *args, **kwargs)
239
+ _logger_frontend.log(WARNING, fmt, *args, **kwargs)
181
240
 
182
241
 
183
242
  def verbose(fmt, *args, **kwargs):
184
- _logger.log(VERBOSE, fmt, *args, **kwargs)
243
+ _logger_frontend.log(VERBOSE, fmt, *args, **kwargs)
185
244
 
186
245
 
187
246
  def debug(fmt, *args, **kwargs):
188
- _logger.debug(fmt, *args, **kwargs)
247
+ _logger_frontend.log(DEBUG, fmt, *args, **kwargs)
189
248
 
190
249
 
191
250
  def error(fmt, *args, **kwargs):
192
- _logger.error(fmt, *args, **kwargs)
251
+ _logger_frontend.log(ERROR, fmt, *args, **kwargs)
193
252
 
194
253
 
195
254
  def stdout(line, **kwargs):
196
255
  line = line.replace("{", "{{")
197
256
  line = line.replace("}", "}}")
198
- _logger.log(STDOUT, line, extra=kwargs)
257
+ _logger_frontend.log(STDOUT, line, extra=kwargs)
199
258
 
200
259
 
201
260
  def stderr(line, **kwargs):
202
261
  line = line.replace("{", "{{")
203
262
  line = line.replace("}", "}}")
204
- _logger.log(STDERR, line, extra=kwargs)
263
+ _logger_frontend.log(STDERR, line, extra=kwargs)
264
+
265
+
266
+ def format_exception_msg(exc):
267
+ te = traceback.TracebackException.from_exception(exc)
205
268
 
269
+ if isinstance(exc, JoltError):
270
+ return str(exc)
206
271
 
207
- def exception(exc=None):
272
+ elif isinstance(exc, SyntaxError):
273
+ filename = fs.path.relpath(
274
+ te.filename,
275
+ fs.path.commonprefix([os.getcwd(), te.filename]))
276
+ return "SyntaxError: {} ({}, line {})".format(
277
+ te.text.strip(),
278
+ filename,
279
+ te.lineno)
280
+
281
+ else:
282
+ filename = fs.path.relpath(
283
+ te.stack[-1].filename,
284
+ fs.path.commonprefix([os.getcwd(), te.stack[-1].filename]))
285
+ return "{}: {} ({}, line {}, in {})".format(
286
+ type(exc).__name__,
287
+ str(exc) or te.stack[-1].line,
288
+ filename,
289
+ te.stack[-1].lineno,
290
+ te.stack[-1].name)
291
+
292
+
293
+ def exception(exc=None, error=True):
208
294
  if exc:
209
- te = traceback.TracebackException.from_exception(exc)
210
- if isinstance(exc, JoltError):
211
- _logger.error("{}", str(exc))
212
- elif isinstance(exc, SyntaxError):
213
- filename = fs.path.relpath(
214
- te.filename,
215
- fs.path.commonprefix([os.getcwd(), te.filename]))
216
- _logger.error("SyntaxError: {} ({}, line {})",
217
- te.text.strip(),
218
- filename,
219
- te.lineno)
220
- else:
221
- filename = fs.path.relpath(
222
- te.stack[-1].filename,
223
- fs.path.commonprefix([os.getcwd(), te.stack[-1].filename]))
224
- _logger.error("{}: {} ({}, line {}, in {})",
225
- type(exc).__name__,
226
- str(exc) or te.stack[-1].line,
227
- filename,
228
- te.stack[-1].lineno,
229
- te.stack[-1].name)
230
- backtrace = traceback.format_exc().splitlines()
295
+ if error:
296
+ _logger_frontend.log(ERROR, format_exception_msg(exc))
297
+
298
+ tb = traceback.format_exception(type(exc), value=exc, tb=exc.__traceback__)
299
+ installdir = fs.path.dirname(__file__)
300
+ if any(map(lambda frame: installdir not in frame, tb[1:-1])):
301
+ while len(tb) > 2 and installdir in tb[1]:
302
+ del tb[1]
303
+ backtrace = "".join(tb).splitlines()
231
304
  else:
232
305
  backtrace = traceback.format_exc().splitlines()
306
+
233
307
  for line in backtrace:
234
308
  line = line.replace("{", "{{")
235
309
  line = line.replace("}", "}}")
236
- _logger.log(EXCEPTION, line.strip())
310
+ line = line.strip()
311
+ _logger_frontend.log(EXCEPTION, line)
237
312
 
238
313
 
239
314
  def transfer(line, context):
@@ -242,6 +317,8 @@ def transfer(line, context):
242
317
  outline2 = context + line
243
318
  if line.startswith("[ ERROR]"):
244
319
  error(outline1)
320
+ elif line.startswith("[WARNING]"):
321
+ warning(outline1)
245
322
  elif line.startswith("[VERBOSE]"):
246
323
  verbose(outline1)
247
324
  elif line.startswith("[ DEBUG]"):
@@ -251,7 +328,7 @@ def transfer(line, context):
251
328
  elif line.startswith("[ EXCEPT]"):
252
329
  outline1 = outline1.replace("{", "{{")
253
330
  outline1 = outline1.replace("}", "}}")
254
- _logger.log(EXCEPTION, outline1)
331
+ _logger_frontend.log(EXCEPTION, outline1)
255
332
  elif line.startswith("[ STDERR]"):
256
333
  stderr(outline1, prefix=True)
257
334
  elif line.startswith("[ STDOUT]"):
@@ -281,26 +358,90 @@ class _Progress(object):
281
358
  def update(self, *args, **kwargs):
282
359
  pass
283
360
 
361
+ def refresh(self, *args, **kwargs):
362
+ pass
363
+
364
+ def reset(self, *args, **kwargs):
365
+ pass
366
+
367
+ def set_description(self, *args, **kwargs):
368
+ pass
369
+
284
370
 
285
371
  def progress_log(desc, count, unit):
286
372
  return _Progress(desc)
287
373
 
288
374
 
289
375
  def progress(desc, count, unit, estimates=True, debug=False):
290
- bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}]' \
291
- if not estimates else None
376
+ if count:
377
+ bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}]' \
378
+ if not estimates else None
379
+ else:
380
+ bar_format = '{desc}{n_fmt}{unit} [{elapsed}]'
292
381
  if not debug and is_interactive() and not is_verbose():
293
- p = tqdm.tqdm(total=count, unit=unit, unit_scale=True, bar_format=bar_format, dynamic_ncols=True)
294
- 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)
295
391
  return p
296
392
  return progress_log(desc, count, unit)
297
393
 
298
394
 
395
+ _level = INFO
396
+
397
+
398
+ def level_to_pb(level):
399
+ """ Convert a log level to a protobuf enum value. """
400
+ return level - 1
401
+
402
+
403
+ def pb_to_level(level):
404
+ """ Convert a protobuf enum value to a log level. """
405
+ return level + 1
406
+
407
+
299
408
  def set_level(level):
409
+ """ Set the log level for terminal output. """
410
+
411
+ if level not in [
412
+ DEBUG,
413
+ ERROR,
414
+ EXCEPTION,
415
+ INFO,
416
+ SILENCE,
417
+ STDERR,
418
+ STDOUT,
419
+ VERBOSE,
420
+ WARNING,
421
+ ]:
422
+ raise ValueError("invalid log level")
423
+
424
+ global _level
425
+ _level = level
300
426
  _stdout.setLevel(level)
301
427
  _stderr.setLevel(level)
302
428
 
303
429
 
430
+ def set_level_pb(level):
431
+ """ Set the log level from a protobuf enum value. """
432
+ set_level(pb_to_level(level))
433
+
434
+
435
+ def get_level():
436
+ global _level
437
+ return _level
438
+
439
+
440
+ def get_level_pb():
441
+ """ Get the log level as a protobuf enum value. """
442
+ return level_to_pb(_level)
443
+
444
+
304
445
  def set_worker():
305
446
  _console_formatter.enable_prefixes()
306
447
 
@@ -361,6 +502,15 @@ def threadsink(level=DEBUG):
361
502
  _logger.removeHandler(handler)
362
503
 
363
504
 
505
+ @contextmanager
506
+ def handler(h):
507
+ _logger.addHandler(h)
508
+ try:
509
+ yield
510
+ finally:
511
+ _logger.removeHandler(h)
512
+
513
+
364
514
  @contextmanager
365
515
  def map_thread(thread_from, thread_to):
366
516
  tid = thread_from.ident
@@ -371,6 +521,15 @@ def map_thread(thread_from, thread_to):
371
521
  _thread_map.unmap(tid)
372
522
 
373
523
 
524
+ @contextmanager
525
+ def thread_prefix(prefix):
526
+ try:
527
+ _logger_frontend.set_thread_prefix(prefix)
528
+ yield
529
+ finally:
530
+ _logger_frontend.clear_thread_prefix()
531
+
532
+
374
533
  class _LogStream(object):
375
534
  def __init__(self):
376
535
  self.buf = ""
@@ -396,3 +555,6 @@ class _LogStream(object):
396
555
  @contextmanager
397
556
  def stream():
398
557
  yield _LogStream()
558
+
559
+
560
+ set_level(STDOUT)
jolt/manifest.py CHANGED
@@ -40,6 +40,14 @@ class _JoltProjectModule(SubElement):
40
40
  def __init__(self, elem=None):
41
41
  super(_JoltProjectModule, self).__init__('module', elem=elem)
42
42
 
43
+ @property
44
+ def path(self):
45
+ return self.src
46
+
47
+ @path.setter
48
+ def path(self, value):
49
+ self.src = value
50
+
43
51
 
44
52
  @Attribute('name')
45
53
  class _JoltProjectResource(SubElement):
@@ -70,6 +78,7 @@ class _JoltTaskError(SubElement):
70
78
  @Attribute('duration', child=True)
71
79
  @Attribute('goal', child=True)
72
80
  @Attribute('identity', child=True)
81
+ @Attribute('instance', child=True)
73
82
  @Attribute('logstash', child=True)
74
83
  @Attribute('result', child=True)
75
84
  @Composition(_JoltAttribute, "attribute")
@@ -104,7 +113,9 @@ class _JoltNetworkParameter(SubElement):
104
113
  @Attribute("stderr", child=True, zlib=True)
105
114
  @Attribute("result", child=True)
106
115
  @Attribute("duration", child=True)
116
+ @Attribute("name")
107
117
  @Attribute("workspace")
118
+ @Attribute("build")
108
119
  @Attribute("version")
109
120
  @Composition(_JoltRecipe, "recipe")
110
121
  @Composition(_JoltTask, "task")
@@ -118,8 +129,10 @@ class JoltManifest(ElementTree):
118
129
  self._elem = self.getroot()
119
130
  self.path = None
120
131
 
121
- @property
122
- def joltdir(self):
132
+ def is_valid(self):
133
+ return self.path is not None
134
+
135
+ def get_workspace_path(self):
123
136
  if self.path is None:
124
137
  return None
125
138
  joltdir = fs.path.dirname(self.path)
@@ -127,6 +140,13 @@ class JoltManifest(ElementTree):
127
140
  joltdir = fs.path.normpath(fs.path.join(joltdir, self.workspace))
128
141
  return joltdir
129
142
 
143
+ def get_workspace_name(self):
144
+ if self.name:
145
+ return self.name
146
+ if self.path is None:
147
+ return None
148
+ return fs.path.basename(fs.path.dirname(self.path))
149
+
130
150
  @property
131
151
  def attrib(self):
132
152
  return self.getroot().attrib
@@ -195,6 +215,12 @@ class JoltManifest(ElementTree):
195
215
  return True
196
216
  return self.find("./task[result='FAILED']") is not None
197
217
 
218
+ def has_unstable(self):
219
+ return self.find("./task[result='UNSTABLE']") is not None
220
+
221
+ def has_tasks(self):
222
+ return self.find("./task") is not None
223
+
198
224
  def find_task(self, task):
199
225
  match = self.find("./task[@name='{0}']".format(task))
200
226
  if match is None:
@@ -215,39 +241,3 @@ class JoltManifest(ElementTree):
215
241
  for manifest_task in self.tasks:
216
242
  self._identities[manifest_task.name] = manifest_task.identity
217
243
  return self._identities
218
-
219
- @staticmethod
220
- def export(task):
221
- manifest = JoltManifest()
222
- ManifestExtensionRegistry.export_manifest(manifest, task)
223
- return manifest
224
-
225
- def process_import(self):
226
- ManifestExtensionRegistry.import_manifest(self)
227
-
228
-
229
- class ManifestExtensionRegistry(object):
230
- extensions = []
231
-
232
- @staticmethod
233
- def add(extension, priority=0):
234
- ManifestExtensionRegistry.extensions.append((extension, priority))
235
- ManifestExtensionRegistry.extensions.sort(key=lambda x: x[1])
236
-
237
- @staticmethod
238
- def export_manifest(manifest, task):
239
- for extension, _ in ManifestExtensionRegistry.extensions:
240
- extension.export_manifest(manifest, task)
241
-
242
- @staticmethod
243
- def import_manifest(manifest):
244
- for extension, _ in ManifestExtensionRegistry.extensions:
245
- extension.import_manifest(manifest)
246
-
247
-
248
- class ManifestExtension(object):
249
- def export_manifest(self, manifest, task):
250
- pass
251
-
252
- def import_manifest(self, manifest):
253
- pass
jolt/options.py CHANGED
@@ -1,14 +1,39 @@
1
1
 
2
2
  class JoltOptions(object):
3
+ """ Jolt options that control the behavior of builds. """
4
+
5
+ debug = False
6
+ """ Enable debug mode. Break into debugger on exceptions. """
7
+
8
+ download = True
9
+ """ Enable downloading of remote artifacts, both session and persistent. """
10
+
11
+ download_session = True
12
+ """ Enable downloading of remote session artifacts. """
13
+
14
+ keep_going = False
15
+ """ Keep going with the build after a task fails. """
16
+
17
+ local = False
18
+ """ Disable network access. """
19
+
20
+ network = False
21
+ """ Distribute tasks to workers. """
22
+
23
+ upload = True
24
+ """ Enable uploading of artifacts. """
25
+
26
+ worker = False
27
+ """ Running as a worker. """
28
+
29
+ salt = None
30
+ """ Salt for hashing (--salt). """
31
+
32
+ jobs = 1
33
+ """ Number of concurrent local tasks to run (1). """
34
+
35
+ mute = False
36
+ """ Mute task output, until a task fails. """
37
+
3
38
  def __init__(self, **kwargs):
4
- self.debug = False
5
- self.default = []
6
- self.download = True
7
- self.keep_going = False
8
- self.local = False
9
- self.network = False
10
- self.upload = True
11
- self.worker = False
12
- self.salt = None
13
- self.jobs = 1
14
39
  self.__dict__.update(kwargs)
jolt/pkgs/abseil.py ADDED
@@ -0,0 +1,42 @@
1
+ from jolt import attributes, BooleanParameter, Parameter, Task
2
+ from jolt.plugins import cmake, git
3
+ from jolt.tasks import TaskRegistry
4
+ import os
5
+
6
+
7
+ @attributes.requires("requires_git")
8
+ @attributes.system
9
+ @cmake.requires()
10
+ @cmake.use_ninja()
11
+ class Abseil(cmake.CMake):
12
+ """ Abseil C++ Common Libraries """
13
+
14
+ name = "abseil"
15
+ version = Parameter("20250814.1")
16
+ shared = BooleanParameter(False, help="Build shared libraries")
17
+ requires_git = ["git:url=https://github.com/abseil/abseil-cpp.git,rev={version}"]
18
+ srcdir = "{git[abseil-cpp]}"
19
+ options = [
20
+ "ABSL_MSVC_STATIC_RUNTIME=OFF",
21
+ "BUILD_SHARED_LIBS={shared[ON,OFF]}",
22
+ "CMAKE_CXX_STANDARD=17",
23
+ ]
24
+
25
+ def publish(self, artifact, tools):
26
+ super().publish(artifact, tools)
27
+ artifact.cxxinfo.crt = "Dynamic"
28
+ artifact.cxxinfo.incpaths.append("include")
29
+ artifact.cxxinfo.libpaths.append("lib")
30
+ if self.shared:
31
+ artifact.environ.LD_LIBRARY_PATH.append("lib")
32
+
33
+ with tools.cwd(artifact.path, "lib"):
34
+ for libfile in tools.glob("*.lib"):
35
+ libname, _ = os.path.splitext(libfile)
36
+ artifact.cxxinfo.libraries.append(libname)
37
+ for libfile in tools.glob("lib*.a"):
38
+ libname, _ = os.path.splitext(libfile)
39
+ artifact.cxxinfo.libraries.append(libname[3:])
40
+
41
+
42
+ TaskRegistry.get().add_task_class(Abseil)
jolt/pkgs/asio.py ADDED
@@ -0,0 +1,25 @@
1
+ from jolt import attributes, Parameter
2
+ from jolt.plugins import cmake, git, autotools, pkgconfig
3
+ from jolt.tasks import TaskRegistry
4
+
5
+
6
+ @attributes.requires("requires_git")
7
+ @autotools.requires()
8
+ @pkgconfig.requires()
9
+ @pkgconfig.to_cxxinfo("asio")
10
+ class Asio(autotools.Autotools):
11
+ name = "asio"
12
+ version = Parameter("1.36.0", help="asio version.")
13
+ requires_git = ["git:url=https://github.com/chriskohlhoff/asio.git,rev=asio-{_version}"]
14
+ srcdir = "{git[asio]}/asio"
15
+
16
+ @property
17
+ def _version(self):
18
+ return str(self.version).replace(".", "-")
19
+
20
+ def publish(self, artifact, tools):
21
+ super().publish(artifact, tools)
22
+ artifact.environ.CMAKE_PREFIX_PATH.append(".")
23
+
24
+
25
+ TaskRegistry.get().add_task_class(Asio)