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.
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/plugins/cmake.py CHANGED
@@ -1,3 +1,4 @@
1
+ from jolt import attributes
1
2
  from jolt import influence
2
3
  from jolt import Task
3
4
  from jolt import utils
@@ -6,31 +7,98 @@ from jolt.error import raise_task_error_if
6
7
  import os
7
8
 
8
9
 
10
+ def options(attrib):
11
+ """
12
+ Decorates a CMake task with an alternative ``options`` attribute.
13
+
14
+ The new attribute will be concatenated with the regular
15
+ ``options`` attribute.
16
+
17
+ Args:
18
+ attrib (str): Name of alternative attribute.
19
+ Keywords are expanded.
20
+ """
21
+ return utils.concat_attributes("options", attrib)
22
+
23
+
24
+ def requires(version=None):
25
+ """ Decorator to add CMake requirements to a task. """
26
+
27
+ import jolt.pkgs.cmake
28
+
29
+ def decorate(cls):
30
+ cls = attributes.requires("requires_cmake")(cls)
31
+ cls.requires_cmake = ["cmake" + (f":version={version}" if version else "")]
32
+ return cls
33
+
34
+ return decorate
35
+
36
+
37
+ def use_ninja():
38
+ """
39
+ Decorator to add Ninja dependencies to CMake task.
40
+
41
+ It also selects Ninja as the CMake generator and builder.
42
+ """
43
+
44
+ import jolt.pkgs.ninja
45
+
46
+ def decorate(cls):
47
+ cls = attributes.requires("requires_ninja")(cls)
48
+ cls.generator = "Ninja"
49
+ cls.requires_ninja = ["ninja"]
50
+ return cls
51
+
52
+ return decorate
53
+
54
+
55
+ @attributes.common_metadata()
56
+ @options("options")
9
57
  class CMake(Task):
10
58
  """ Builds and publishes a CMake project """
11
-
12
59
  abstract = True
13
60
 
14
61
  cmakelists = "CMakeLists.txt"
15
62
  """ Path to CMakeLists.txt or directory containing CMakelists.txt """
16
63
 
64
+ config = "Release"
65
+ """ The default build configuration to use """
66
+
17
67
  generator = None
68
+ """ The build file generator that CMake should use """
18
69
 
19
70
  incremental = True
71
+ """ Keep build directories between Jolt invocations """
20
72
 
21
73
  options = []
22
74
  """ List of options and their values (``option[:type]=value``) """
23
75
 
76
+ srcdir = None
77
+ """ Source directory. If not specified, the task working directory is used. """
78
+
79
+ install = ["install"]
80
+ """ List of install build targets. If empty, the default install target is used. """
81
+
82
+ def clean(self, tools):
83
+ cmake = tools.cmake(incremental=self.incremental)
84
+ cmake.clean()
85
+
24
86
  def run(self, deps, tools):
87
+ self.deps = deps
25
88
  raise_task_error_if(not self.cmakelists, self, "cmakelists attribute has not been defined")
26
89
 
27
- cmake = tools.cmake(incremental=self.incremental)
28
- cmake.configure(tools.expand(self.cmakelists), *["-D" + tools.expand(option) for option in self.options], generator=self.generator)
29
- cmake.build()
30
- cmake.install()
90
+ config = tools.expand(str(self.config))
91
+ options = self._options()
92
+ options += ["CMAKE_BUILD_TYPE=" + config]
93
+
94
+ with tools.cwd(self.srcdir or self.joltdir):
95
+ cmake = tools.cmake(deps, incremental=self.incremental)
96
+ cmake.configure(tools.expand(self.cmakelists), *["-D" + tools.expand(option) for option in options], generator=self.generator)
97
+ for target in self.install or ["install"]:
98
+ cmake.install(config=config, target=target)
31
99
 
32
100
  def publish(self, artifact, tools):
33
- cmake = tools.cmake()
101
+ cmake = tools.cmake(incremental=self.incremental)
34
102
  cmake.publish(artifact)
35
103
 
36
104
 
jolt/plugins/conan.py CHANGED
@@ -3,6 +3,7 @@ from os import path
3
3
 
4
4
  from jolt import Task
5
5
  from jolt import influence
6
+ from jolt import utils
6
7
  from jolt.error import raise_task_error_if
7
8
 
8
9
 
@@ -239,3 +240,240 @@ class Conan(Task):
239
240
  artifact.cxxinfo.cflags.append(dep["cflags"])
240
241
  artifact.cxxinfo.cxxflags.append(dep["cxxflags"])
241
242
  artifact.cxxinfo.ldflags.append(dep["exelinkflags"])
243
+
244
+
245
+ @influence.attribute("conanfile")
246
+ @influence.attribute("generators")
247
+ @influence.attribute("incremental")
248
+ @influence.attribute("options")
249
+ @influence.attribute("packages")
250
+ class Conan2(Task):
251
+ """
252
+ Conan package installer task.
253
+
254
+ This task base class can be used to fetch, build and publish Conan packages
255
+ as Jolt artifacts. All package metadata is transfered from the Conan package
256
+ manifest to the Jolt artifact so that no manual configuration of include
257
+ paths, library paths, macros, etc is required.
258
+
259
+ An existing installation of Conan is required. Please visit https://conan.io/
260
+ for installation instructions and documentation.
261
+
262
+ A minimal task to download and publish the Boost C++ libraries can look like this:
263
+
264
+ .. code-block:: python
265
+
266
+ from jolt.plugins.conan import Conan
267
+
268
+ class Boost(Conan):
269
+ packages = ["boost/1.74.0"]
270
+
271
+ Boost may then be used from Ninja tasks by declaring a requirement:
272
+
273
+ .. code-block:: python
274
+
275
+ from jolt.plugins.ninja import CXXExecutable
276
+
277
+ class BoostApplication(CXXExecutable):
278
+ requires = ["boost"]
279
+ sources = ["main.cpp"]
280
+
281
+ The task supports using an existing conanfile.txt, but it is not required.
282
+ Packages are installed into and collected from Jolt build directories. The
283
+ user's regular Conan cache will not be affected.
284
+
285
+ """
286
+
287
+ abstract = True
288
+
289
+ conanfile = None
290
+ """
291
+ An existing conanfile.txt file to use.
292
+
293
+ Instead of generating the conanfile.txt file on-demand, an external
294
+ file may be used. If this attribute is set, the ``generators``, ``options``
295
+ and ``packages`` attributes must not be set.
296
+
297
+ See Conan documentation for further details.
298
+ """
299
+
300
+ packages = []
301
+ """
302
+ A list of Conan package references to collect and publish.
303
+
304
+ The reference format is ``PkgName/<version>@user/channel``. See Conan
305
+ documentation for further details.
306
+
307
+ Any {keyword} arguments, or macros, found in the strings are automatically
308
+ expanded to the value of the associated task's parameters and properties.
309
+
310
+ Example:
311
+
312
+ .. code-block:: python
313
+
314
+ sdl_version = Parameter("2.0.12")
315
+
316
+ packages = [
317
+ "boost/1.74.0",
318
+ "sdl2/{sdl_version}@bincrafters/stable",
319
+ ]
320
+
321
+ """
322
+
323
+ options = []
324
+ """
325
+ A list of Conan package options to apply
326
+
327
+ The option format is ``PkgName:Option=Value``. See Conan
328
+ documentation for further details.
329
+
330
+ Any {keyword} arguments, or macros, found in the strings are automatically
331
+ expanded to the value of the associated task's parameters and properties.
332
+
333
+ Example:
334
+
335
+ .. code-block:: python
336
+
337
+ options = [
338
+ "boost:shared=True",
339
+ "zlib:shared=True",
340
+ ]
341
+
342
+ """
343
+
344
+ settings = []
345
+ """
346
+ A list of Conan settings to apply
347
+
348
+ The settings format is ``Option=Value``. See Conan
349
+ documentation for further details.
350
+
351
+ Any {keyword} arguments, or macros, found in the strings are automatically
352
+ expanded to the value of the associated task's parameters and properties.
353
+
354
+ Example:
355
+
356
+ .. code-block:: python
357
+
358
+ settings = [
359
+ "compiler.libcxx=libstdc++11",
360
+ ]
361
+
362
+ """
363
+
364
+ remotes = {}
365
+ """
366
+ A dictionary with Conan remotes to use when fetching packages.
367
+
368
+ The dictionary key is the name of remote and its value is the URL.
369
+
370
+ Example:
371
+
372
+ .. code-block:: python
373
+
374
+ remotes = {
375
+ "bincrafters": "https://api.bintray.com/conan/bincrafters/public-conan"
376
+ }
377
+
378
+ """
379
+
380
+ incremental = True
381
+ """
382
+ Keep installed packages in the Conan cache between Jolt invokations.
383
+
384
+ If incremental build is disabled, the Jolt Conan cache is removed
385
+ before execution begins.
386
+ """
387
+
388
+ def __init__(self, *args, **kwargs):
389
+ super().__init__(*args, **kwargs)
390
+ if self.conanfile:
391
+ self.influence.append(influence.FileInfluence(self.conanfile))
392
+
393
+ def _options(self):
394
+ return [] + self.options
395
+
396
+ def _settings(self):
397
+ return [] + self.settings
398
+
399
+ def _packages(self):
400
+ return [] + self.packages
401
+
402
+ def _remotes(self):
403
+ return self.remotes
404
+
405
+ def run(self, deps, tools):
406
+ raise_task_error_if(
407
+ not tools.which("conan"), self,
408
+ "Conan: Conan is not installed in the PATH")
409
+ raise_task_error_if(
410
+ self.conanfile and (self._packages() or self._options()), self,
411
+ "Conan: 'conanfile' attribute cannot be used with other attributes")
412
+
413
+ conanfile = tools.expand_path(self.conanfile) if self.conanfile else None
414
+
415
+ with tools.cwd(tools.builddir()):
416
+ if conanfile is None or not path.exists(conanfile):
417
+ conanfile = "conanfile.txt"
418
+ self.info("Creating conanfile.txt")
419
+ self.tools.write_file(conanfile, "[requires]\n")
420
+ for pkg in self._packages():
421
+ self.tools.append_file(conanfile, pkg + "\n")
422
+
423
+ with tools.environ(CONAN_USER_HOME=tools.builddir("conan", incremental=self.incremental)):
424
+ for remote, url in self._remotes().items():
425
+ self.info("Registering remote '{}'", remote)
426
+ tools.run("conan remote add -f {} {}", remote, url, output_on_error=True)
427
+
428
+ self.info("Installing packages into the Conan cache")
429
+ options = " ".join(["-o " + opt for opt in self._options()])
430
+ settings = " ".join(["-s " + opt for opt in self._settings()])
431
+ output = tools.run("conan install --build=missing --output-folder . -u --format=json {} {} {}", options, settings, conanfile, output_stdout=False)
432
+
433
+ self.info("Parsing manifest")
434
+ self._manifest = json.loads(output)
435
+
436
+ for dep in self._manifest["graph"]["nodes"].values():
437
+ if dep["package_folder"]:
438
+ self.info("Collecting '{}' files from: {}", dep["name"], dep["package_folder"])
439
+ tools.copy(dep["package_folder"], dep["name"])
440
+
441
+ def publish(self, artifact, tools):
442
+ self.info("Publishing package files")
443
+ with tools.cwd(tools.builddir()):
444
+ artifact.collect("*")
445
+
446
+ self.info("Publishing metadata")
447
+ for dep in self._manifest["graph"]["nodes"].values():
448
+ if not dep["package_folder"]:
449
+ continue
450
+
451
+ for node in dep["cpp_info"]:
452
+ for incpath in dep["cpp_info"][node]["includedirs"]:
453
+ artifact.cxxinfo.incpaths.append(path.join(dep["name"], path.relpath(incpath, dep["package_folder"])))
454
+ for libpath in dep["cpp_info"][node]["libdirs"]:
455
+ artifact.cxxinfo.libpaths.append(path.join(dep["name"], path.relpath(libpath, dep["package_folder"])))
456
+ for binpath in dep["cpp_info"][node]["bindirs"]:
457
+ artifact.environ.PATH.append(path.join(dep["name"], path.relpath(binpath, dep["package_folder"])))
458
+ if dep["cpp_info"][node]["libs"]:
459
+ artifact.cxxinfo.libraries.extend(dep["cpp_info"][node]["libs"])
460
+ if dep["cpp_info"][node]["system_libs"]:
461
+ artifact.cxxinfo.libraries.extend(dep["cpp_info"][node]["system_libs"])
462
+ if dep["cpp_info"][node]["defines"]:
463
+ artifact.cxxinfo.macros.extend(dep["cpp_info"][node]["defines"])
464
+ if dep["cpp_info"][node]["cflags"]:
465
+ artifact.cxxinfo.cflags.extend(dep["cpp_info"][node]["cflags"])
466
+ if dep["cpp_info"][node]["cxxflags"]:
467
+ artifact.cxxinfo.cxxflags.extend(dep["cpp_info"][node]["cxxflags"])
468
+ if dep["cpp_info"][node]["exelinkflags"]:
469
+ artifact.cxxinfo.ldflags.extend(dep["cpp_info"][node]["exelinkflags"])
470
+
471
+ # Make list of unique values
472
+ artifact.cxxinfo.incpaths = utils.unique_list(artifact.cxxinfo.incpaths)
473
+ artifact.cxxinfo.libpaths = utils.unique_list(artifact.cxxinfo.libpaths)
474
+ artifact.cxxinfo.libraries = utils.unique_list(artifact.cxxinfo.libraries)
475
+ artifact.cxxinfo.macros = utils.unique_list(artifact.cxxinfo.macros)
476
+ artifact.cxxinfo.cflags = utils.unique_list(artifact.cxxinfo.cflags)
477
+ artifact.cxxinfo.cxxflags = utils.unique_list(artifact.cxxinfo.cxxflags)
478
+ artifact.cxxinfo.ldflags = utils.unique_list(artifact.cxxinfo.ldflags)
479
+ artifact.environ.PATH = path.pathsep.join(utils.unique_list(str(artifact.environ.PATH).split(path.pathsep)))
jolt/plugins/cxxinfo.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from jolt.cache import ArtifactListAttribute
2
+ from jolt.cache import ArtifactStringAttribute
2
3
  from jolt.cache import ArtifactAttributeSet
3
4
  from jolt.cache import ArtifactAttributeSetProvider
4
5
 
@@ -7,6 +8,10 @@ class CppInfoListVariable(ArtifactListAttribute):
7
8
  pass
8
9
 
9
10
 
11
+ class CppInfoStringVariable(ArtifactStringAttribute):
12
+ pass
13
+
14
+
10
15
  class CppInfoDictVariable(ArtifactListAttribute):
11
16
  def __init__(self, artifact, name):
12
17
  super(CppInfoDictVariable, self).__init__(artifact, name)
@@ -26,6 +31,8 @@ class CppInfo(ArtifactAttributeSet):
26
31
  return CppInfoListVariable(self._artifact, "asflags")
27
32
  if name == "cflags":
28
33
  return CppInfoListVariable(self._artifact, "cflags")
34
+ if name == "crt":
35
+ return CppInfoStringVariable(self._artifact, "crt")
29
36
  if name == "cxxflags":
30
37
  return CppInfoListVariable(self._artifact, "cxxflags")
31
38
  if name == "incpaths":
jolt/plugins/docker.py CHANGED
@@ -103,8 +103,8 @@ class DockerClient(Download):
103
103
  url = "https://download.docker.com/{host}/static/stable/{arch}/docker-{version}.tgz"
104
104
  """ URL of binaries """
105
105
 
106
- version = Parameter("27.3.1", help="Docker version")
107
- """ Docker version [27.3.1] """
106
+ version = Parameter("27.4.1", help="Docker version")
107
+ """ Docker version [27.4.1] """
108
108
 
109
109
  def publish(self, artifact, tools):
110
110
  super().publish(artifact, tools)
@@ -254,7 +254,7 @@ class DockerContainer(Resource):
254
254
  def _image(self):
255
255
  registry = TaskRegistry.get()
256
256
  tool = tools.Tools(self)
257
- if registry.get_task_class(tool.expand(self.image)):
257
+ if registry.has_task(tool.expand(self.image)):
258
258
  return [self.image]
259
259
  return []
260
260
 
jolt/plugins/environ.py CHANGED
@@ -55,6 +55,17 @@ class PathEnvironmentVariable(EnvironmentVariable):
55
55
 
56
56
 
57
57
  class EnvironmentVariableSet(ArtifactAttributeSet):
58
+ """ A set of environment variables for an artifact.
59
+
60
+ Example:
61
+
62
+ .. code-block:: python
63
+
64
+ artifact.environ.FOO = "bar"
65
+ artifact.environ.PATH.append("bin")
66
+
67
+ """
68
+
58
69
  def __init__(self, artifact):
59
70
  super(EnvironmentVariableSet, self).__init__()
60
71
  super(ArtifactAttributeSet, self).__setattr__("_artifact", artifact)
jolt/plugins/fetch.py ADDED
@@ -0,0 +1,141 @@
1
+ import hashlib
2
+ from jolt import BooleanParameter, Parameter
3
+ from jolt.plugins.git import ErrorDict
4
+ from jolt.tasks import Resource, TaskRegistry
5
+ from jolt import filesystem as fs
6
+ from jolt import utils
7
+ from jolt.error import raise_error_if
8
+ from jolt.loader import JoltLoader
9
+ from jolt.tools import SUPPORTED_ARCHIVE_TYPES
10
+
11
+
12
+ class Fetch(Resource):
13
+ """
14
+ Fetch a file from a URL and extract it.
15
+
16
+ By default, the fetched file is extracted if it is a supported archive type.
17
+ Extraction may be disable by assigning the `extract` parameter to false.
18
+
19
+ The fetched content is placed in a build directory named after the resource.
20
+ The `path` parameter can be used to specify a different location relative to
21
+ the workspace root.
22
+
23
+ The path of the fetched content is made available to consuming tasks through
24
+ the `fetch` attribute. The `fetch` attribute is a dictionary where the key
25
+ is the name of the fetched file and the value is a path relative to the
26
+ workspace root. You can specify an alias for the filename / key through the
27
+ `alias` parameter.
28
+
29
+ The plugin must be loaded before it can be used. This is done by importing
30
+ the module, or by adding the following line to the configuration file:
31
+
32
+ .. code-block:: ini
33
+
34
+ [fetch]
35
+
36
+
37
+ Example:
38
+
39
+ .. code-block:: python
40
+
41
+ from jolt.plugins import fetch
42
+
43
+ class Example(Task):
44
+ requires = ["fetch:alias=zlib,url=https://zlib.net/zlib-1.3.1.tar.gz"]
45
+
46
+ def run(self, deps, tools):
47
+ self.info("The source tree is located at: {fetch[zlib]}")
48
+ with tools.cwd(self.fetch["zlib"]):
49
+ tools.run("make")
50
+
51
+ """
52
+
53
+ name = "fetch"
54
+ alias = Parameter(required=False, help="Name of the task used when referencing content. Defaults to the filename of the fetched file.")
55
+ extract = BooleanParameter(default=True, help="Whether to extract the fetched file.")
56
+ path = Parameter(required=False, help="Destination directory.")
57
+ url = Parameter(help="URL to fetch from.")
58
+ md5 = Parameter(required=False, help="Expected MD5 hash of the fetched file.")
59
+ sha256 = Parameter(required=False, help="Expected SHA256 hash of the fetched file.")
60
+
61
+ def __init__(self, *args, **kwargs):
62
+ super().__init__(*args, **kwargs)
63
+ self.joltdir = JoltLoader.get().joltdir
64
+
65
+ # Set the path to the extraction directory
66
+ if self.path.is_unset():
67
+ self.abspath = self.tools.builddir(utils.canonical(self.short_qualified_name), incremental="always", unique=False)
68
+ if not self._extract():
69
+ # Join with filename if not extracting
70
+ self.abspath = fs.path.join(self.abspath, self._get_filename())
71
+ else:
72
+ self.abspath = fs.path.join(self.joltdir, str(self.path) or self._get_name())
73
+
74
+ self.relpath = fs.path.relpath(self.abspath, self.tools.wsroot)
75
+
76
+ def _extract(self):
77
+ """ Check if the fetched file should/can be extracted. """
78
+ filename = self._get_filename()
79
+
80
+ if not any([filename.endswith(ext) for ext in SUPPORTED_ARCHIVE_TYPES]):
81
+ return False
82
+
83
+ return bool(self.extract)
84
+
85
+ def _acquire_ws(self):
86
+ # Create the destination directory if it does not exist
87
+ self.tools.rmtree(self.abspath, ignore_errors=True)
88
+ self.tools.mkdir(fs.path.dirname(self.abspath), recursively=True)
89
+
90
+ if self._extract():
91
+ with self.tools.tmpdir() as tmpdir, self.tools.cwd(tmpdir):
92
+ filename = self._get_filename()
93
+ self.tools.download(self.url, filename)
94
+ self._verify_sha256(filename)
95
+ self.tools.extract(filename, self.abspath)
96
+ else:
97
+ self.tools.download(self.url, self.abspath)
98
+ self._verify_sha256(self.abspath)
99
+
100
+ def acquire(self, artifact, deps, tools, owner):
101
+ self._acquire_ws()
102
+ self._assign_fetch(owner)
103
+ artifact.worktree = fs.path.relpath(self.abspath, owner.joltdir)
104
+
105
+ def _assign_fetch(self, task, none=False):
106
+ if not hasattr(task, "fetch"):
107
+ task.fetch = ErrorDict(self)
108
+ if none:
109
+ # None means the git repo is not cloned or checked out
110
+ # and should not be included in the git dictionary
111
+ # of the consuming task yet. If the consuming task
112
+ # requires the git repo for its influence collection,
113
+ # the dict will raise an error. The solution is to
114
+ # assign hash=true to the git requirement which
115
+ # will cause the git repo to be cloned and checked out
116
+ # before the influence collection is performed.
117
+ task.fetch[self._get_name()] = None
118
+ else:
119
+ # Assign the git repo to the consuming task.
120
+ # The git repo is cloned and checked out before
121
+ # any influence collection is performed.
122
+ task.fetch[self._get_name()] = fs.path.relpath(self.abspath, task.joltdir)
123
+
124
+ def _get_name(self):
125
+ return str(self.alias) if self.alias.is_set() else self._get_filename()
126
+
127
+ def _get_filename(self):
128
+ return fs.path.basename(str(self.url))
129
+
130
+ def _verify_sha256(self, filepath):
131
+ if not self.sha256.is_set():
132
+ return
133
+ actual_hash = self.tools.checksum_file(filepath, hashfn=hashlib.sha256)
134
+ expected_hash = str(self.sha256)
135
+ raise_error_if(
136
+ actual_hash != expected_hash,
137
+ f"SHA256 hash mismatch for fetched file '{filepath}': expected {expected_hash}, got {actual_hash}"
138
+ )
139
+
140
+
141
+ TaskRegistry.get().add_task_class(Fetch)
jolt/plugins/gdb.py CHANGED
@@ -70,31 +70,34 @@ def gdb(ctx, task, default, machine_interface, no_binary, gdb_args):
70
70
  if machine_interface:
71
71
  log.enable_gdb()
72
72
 
73
- manifest = ctx.obj["manifest"]
74
73
  options = JoltOptions(default=default)
75
74
  acache = cache.ArtifactCache.get(options)
76
75
  TaskHookRegistry.get(options)
77
76
  executors = scheduler.ExecutorRegistry.get(options)
78
77
  registry = TaskRegistry.get()
79
78
  strategy = scheduler.DownloadStrategy(executors, acache)
80
- queue = scheduler.TaskQueue(strategy, acache, {})
79
+ queue = scheduler.TaskQueue()
81
80
 
82
81
  for params in default:
83
82
  registry.set_default_parameters(params)
84
83
 
85
- gb = graph.GraphBuilder(registry, acache, manifest, options, progress=True)
84
+ gb = graph.GraphBuilder(registry, acache, options, progress=True)
86
85
  dag = gb.build([task])
87
86
 
88
87
  try:
89
88
  with log.progress("Progress", dag.number_of_tasks(), " tasks", estimates=False, debug=False) as p:
89
+ in_progress = set()
90
+
90
91
  while dag.has_tasks() or not queue.empty():
91
- leafs = dag.select(lambda graph, task: task.is_ready())
92
+ leafs = dag.select(lambda graph, task: task.is_ready() and task not in in_progress)
92
93
 
93
94
  while leafs:
94
95
  task = leafs.pop()
95
- queue.submit(task)
96
+ executor = strategy.create_executor({}, task)
97
+ queue.submit(executor)
98
+ in_progress.add(task)
96
99
 
97
- task, error = queue.wait()
100
+ task, _ = queue.wait()
98
101
 
99
102
  # Materialize workspace resources so that
100
103
  # source code is available to the debugger.
@@ -113,6 +116,7 @@ def gdb(ctx, task, default, machine_interface, no_binary, gdb_args):
113
116
  log.warning("Interrupted by user")
114
117
  try:
115
118
  queue.abort()
119
+ executors.shutdown()
116
120
  sys.exit(1)
117
121
  except KeyboardInterrupt:
118
122
  print()