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.
- jolt/__init__.py +47 -0
- jolt/cache.py +358 -159
- jolt/cli.py +71 -104
- jolt/config.py +14 -26
- jolt/filesystem.py +2 -2
- jolt/graph.py +56 -28
- jolt/influence.py +67 -2
- jolt/loader.py +150 -186
- jolt/log.py +12 -2
- jolt/manifest.py +0 -46
- jolt/options.py +35 -12
- jolt/pkgs/abseil.py +42 -0
- jolt/pkgs/asio.py +25 -0
- jolt/pkgs/autoconf.py +41 -0
- jolt/pkgs/automake.py +41 -0
- jolt/pkgs/b2.py +31 -0
- jolt/pkgs/boost.py +111 -0
- jolt/pkgs/boringssl.py +32 -0
- jolt/pkgs/busybox.py +39 -0
- jolt/pkgs/bzip2.py +43 -0
- jolt/pkgs/cares.py +29 -0
- jolt/pkgs/catch2.py +36 -0
- jolt/pkgs/cbindgen.py +17 -0
- jolt/pkgs/cista.py +19 -0
- jolt/pkgs/clang.py +44 -0
- jolt/pkgs/cli11.py +23 -0
- jolt/pkgs/cmake.py +48 -0
- jolt/pkgs/cpython.py +196 -0
- jolt/pkgs/crun.py +29 -0
- jolt/pkgs/curl.py +38 -0
- jolt/pkgs/dbus.py +18 -0
- jolt/pkgs/double_conversion.py +24 -0
- jolt/pkgs/fastfloat.py +21 -0
- jolt/pkgs/ffmpeg.py +28 -0
- jolt/pkgs/flatbuffers.py +29 -0
- jolt/pkgs/fmt.py +27 -0
- jolt/pkgs/fstree.py +20 -0
- jolt/pkgs/gflags.py +18 -0
- jolt/pkgs/glib.py +18 -0
- jolt/pkgs/glog.py +25 -0
- jolt/pkgs/glslang.py +21 -0
- jolt/pkgs/golang.py +16 -11
- jolt/pkgs/googlebenchmark.py +18 -0
- jolt/pkgs/googletest.py +46 -0
- jolt/pkgs/gperf.py +15 -0
- jolt/pkgs/grpc.py +73 -0
- jolt/pkgs/hdf5.py +19 -0
- jolt/pkgs/help2man.py +14 -0
- jolt/pkgs/inja.py +28 -0
- jolt/pkgs/jsoncpp.py +31 -0
- jolt/pkgs/libarchive.py +43 -0
- jolt/pkgs/libcap.py +44 -0
- jolt/pkgs/libdrm.py +44 -0
- jolt/pkgs/libedit.py +42 -0
- jolt/pkgs/libevent.py +31 -0
- jolt/pkgs/libexpat.py +27 -0
- jolt/pkgs/libfastjson.py +21 -0
- jolt/pkgs/libffi.py +16 -0
- jolt/pkgs/libglvnd.py +30 -0
- jolt/pkgs/libogg.py +28 -0
- jolt/pkgs/libpciaccess.py +18 -0
- jolt/pkgs/libseccomp.py +21 -0
- jolt/pkgs/libtirpc.py +24 -0
- jolt/pkgs/libtool.py +42 -0
- jolt/pkgs/libunwind.py +35 -0
- jolt/pkgs/libva.py +18 -0
- jolt/pkgs/libvorbis.py +33 -0
- jolt/pkgs/libxml2.py +35 -0
- jolt/pkgs/libxslt.py +17 -0
- jolt/pkgs/libyajl.py +16 -0
- jolt/pkgs/llvm.py +81 -0
- jolt/pkgs/lua.py +54 -0
- jolt/pkgs/lz4.py +26 -0
- jolt/pkgs/m4.py +14 -0
- jolt/pkgs/make.py +17 -0
- jolt/pkgs/mesa.py +81 -0
- jolt/pkgs/meson.py +17 -0
- jolt/pkgs/mstch.py +28 -0
- jolt/pkgs/mysql.py +60 -0
- jolt/pkgs/nasm.py +49 -0
- jolt/pkgs/ncurses.py +30 -0
- jolt/pkgs/ng_log.py +25 -0
- jolt/pkgs/ninja.py +45 -0
- jolt/pkgs/nlohmann_json.py +25 -0
- jolt/pkgs/nodejs.py +19 -11
- jolt/pkgs/opencv.py +24 -0
- jolt/pkgs/openjdk.py +26 -0
- jolt/pkgs/openssl.py +103 -0
- jolt/pkgs/paho.py +76 -0
- jolt/pkgs/patchelf.py +16 -0
- jolt/pkgs/perl.py +42 -0
- jolt/pkgs/pkgconfig.py +64 -0
- jolt/pkgs/poco.py +39 -0
- jolt/pkgs/protobuf.py +77 -0
- jolt/pkgs/pugixml.py +27 -0
- jolt/pkgs/python.py +19 -0
- jolt/pkgs/qt.py +35 -0
- jolt/pkgs/rapidjson.py +26 -0
- jolt/pkgs/rapidyaml.py +28 -0
- jolt/pkgs/re2.py +30 -0
- jolt/pkgs/re2c.py +17 -0
- jolt/pkgs/readline.py +15 -0
- jolt/pkgs/rust.py +41 -0
- jolt/pkgs/sdl.py +28 -0
- jolt/pkgs/simdjson.py +27 -0
- jolt/pkgs/soci.py +46 -0
- jolt/pkgs/spdlog.py +29 -0
- jolt/pkgs/spirv_llvm.py +21 -0
- jolt/pkgs/spirv_tools.py +24 -0
- jolt/pkgs/sqlite.py +83 -0
- jolt/pkgs/ssl.py +12 -0
- jolt/pkgs/texinfo.py +15 -0
- jolt/pkgs/tomlplusplus.py +22 -0
- jolt/pkgs/wayland.py +26 -0
- jolt/pkgs/x11.py +58 -0
- jolt/pkgs/xerces_c.py +20 -0
- jolt/pkgs/xorg.py +360 -0
- jolt/pkgs/xz.py +29 -0
- jolt/pkgs/yamlcpp.py +30 -0
- jolt/pkgs/zeromq.py +47 -0
- jolt/pkgs/zlib.py +69 -0
- jolt/pkgs/zstd.py +33 -0
- jolt/plugins/autotools.py +66 -0
- jolt/plugins/cmake.py +74 -6
- jolt/plugins/conan.py +238 -0
- jolt/plugins/cxxinfo.py +7 -0
- jolt/plugins/docker.py +3 -3
- jolt/plugins/environ.py +11 -0
- jolt/plugins/fetch.py +141 -0
- jolt/plugins/gdb.py +10 -6
- jolt/plugins/git.py +60 -11
- jolt/plugins/libtool.py +63 -0
- jolt/plugins/linux.py +990 -0
- jolt/plugins/meson.py +61 -0
- jolt/plugins/ninja-compdb.py +11 -7
- jolt/plugins/ninja.py +245 -26
- jolt/plugins/paths.py +11 -1
- jolt/plugins/pkgconfig.py +219 -0
- jolt/plugins/podman.py +15 -41
- jolt/plugins/python.py +137 -0
- jolt/plugins/rust.py +25 -0
- jolt/plugins/scheduler.py +18 -14
- jolt/plugins/selfdeploy/setup.py +2 -1
- jolt/plugins/selfdeploy.py +21 -30
- jolt/plugins/strings.py +19 -10
- jolt/scheduler.py +428 -138
- jolt/tasks.py +159 -7
- jolt/tools.py +105 -51
- jolt/utils.py +16 -1
- jolt/version.py +1 -1
- {jolt-0.9.342.dist-info → jolt-0.9.429.dist-info}/METADATA +64 -9
- jolt-0.9.429.dist-info/RECORD +207 -0
- {jolt-0.9.342.dist-info → jolt-0.9.429.dist-info}/WHEEL +1 -1
- jolt/plugins/debian.py +0 -338
- jolt/plugins/repo.py +0 -253
- jolt-0.9.342.dist-info/RECORD +0 -93
- {jolt-0.9.342.dist-info → jolt-0.9.429.dist-info}/entry_points.txt +0 -0
- {jolt-0.9.342.dist-info → jolt-0.9.429.dist-info}/top_level.txt +0 -0
jolt/tasks.py
CHANGED
|
@@ -10,6 +10,7 @@ import platform
|
|
|
10
10
|
from threading import RLock
|
|
11
11
|
import subprocess
|
|
12
12
|
from os import environ
|
|
13
|
+
from os import sys as os_sys
|
|
13
14
|
import sys
|
|
14
15
|
import unittest as ut
|
|
15
16
|
from urllib.parse import urlparse
|
|
@@ -20,6 +21,7 @@ import traceback
|
|
|
20
21
|
from jolt import filesystem as fs
|
|
21
22
|
from jolt import log
|
|
22
23
|
from jolt import utils
|
|
24
|
+
from jolt.cache import ArtifactAttributeSetProvider
|
|
23
25
|
from jolt.error import raise_error_if, raise_task_error, raise_task_error_if
|
|
24
26
|
from jolt.error import raise_unreported_task_error_if
|
|
25
27
|
from jolt.error import JoltError, JoltCommandError, LoggedJoltError
|
|
@@ -135,7 +137,7 @@ class Parameter(object):
|
|
|
135
137
|
self.name = name
|
|
136
138
|
|
|
137
139
|
def __init__(self, default=None, values=None, required=True,
|
|
138
|
-
const=False, influence=True, help=None):
|
|
140
|
+
const=False, influence=True, help=None, valuesfn=None):
|
|
139
141
|
"""
|
|
140
142
|
Creates a new parameter.
|
|
141
143
|
|
|
@@ -143,6 +145,10 @@ class Parameter(object):
|
|
|
143
145
|
default (str, optional): An optional default value.
|
|
144
146
|
values (list, optional): A list of accepted values. An
|
|
145
147
|
assertion is raised if an unlisted value is assigned to the parameter.
|
|
148
|
+
valuesfn (func, optional); A function that validates the assigned value.
|
|
149
|
+
If both values and valuesfn are specified, the values list is validated
|
|
150
|
+
first. The function is passed the assigned value and should return.
|
|
151
|
+
boolean value. If the function returns False, a ParameterValueError is raised.
|
|
146
152
|
required (boolean, optional): If required, the parameter must be assigned
|
|
147
153
|
a value before the task can be executed. The default is ``True``.
|
|
148
154
|
const (boolean, optional): If const is True, the parameter is immutable
|
|
@@ -164,6 +170,7 @@ class Parameter(object):
|
|
|
164
170
|
self._default = default
|
|
165
171
|
self._value = default
|
|
166
172
|
self._accepted_values = values
|
|
173
|
+
self._accepted_values_fn = valuesfn
|
|
167
174
|
self._required = required
|
|
168
175
|
self._const = const
|
|
169
176
|
self._influence = influence
|
|
@@ -190,7 +197,7 @@ class Parameter(object):
|
|
|
190
197
|
def highlight(value):
|
|
191
198
|
return colors.bright(value) if self._is_default(value) else colors.dim(value)
|
|
192
199
|
|
|
193
|
-
return "[{}]".format(", ".join([highlight(value) for value in accepted])) if accepted else ""
|
|
200
|
+
return "[{}]".format(", ".join([highlight(str(value)) for value in accepted])) if accepted else ""
|
|
194
201
|
|
|
195
202
|
def __str__(self):
|
|
196
203
|
""" Returns the parameter value as a string """
|
|
@@ -199,6 +206,8 @@ class Parameter(object):
|
|
|
199
206
|
def _validate(self, value, what=None):
|
|
200
207
|
if self._accepted_values is not None and value not in self._accepted_values:
|
|
201
208
|
raise ParameterValueError(self, value, what=what)
|
|
209
|
+
if self._accepted_values_fn is not None and not self._accepted_values_fn(value):
|
|
210
|
+
raise ParameterValueError(self, value, what=what)
|
|
202
211
|
|
|
203
212
|
def get_default(self):
|
|
204
213
|
""" Get the default value of the parameter.
|
|
@@ -436,7 +445,7 @@ class IntParameter(Parameter):
|
|
|
436
445
|
"""
|
|
437
446
|
|
|
438
447
|
def __init__(self, default=None, min=None, max=None, values=None, required=True, const=False,
|
|
439
|
-
influence=True, help=None):
|
|
448
|
+
influence=True, help=None, valuesfn=None):
|
|
440
449
|
"""
|
|
441
450
|
Creates a new parameter.
|
|
442
451
|
|
|
@@ -484,7 +493,8 @@ class IntParameter(Parameter):
|
|
|
484
493
|
required=required,
|
|
485
494
|
const=const,
|
|
486
495
|
influence=influence,
|
|
487
|
-
help=help
|
|
496
|
+
help=help,
|
|
497
|
+
valuesfn=valuesfn)
|
|
488
498
|
|
|
489
499
|
def _validate(self, value, what=None):
|
|
490
500
|
if self._min is not None and value < self._min:
|
|
@@ -688,6 +698,10 @@ class ListParameter(Parameter):
|
|
|
688
698
|
for item in value:
|
|
689
699
|
if item not in self._accepted_values:
|
|
690
700
|
raise ParameterValueError(self, item, what=what)
|
|
701
|
+
if self._accepted_values_fn is not None:
|
|
702
|
+
for item in value:
|
|
703
|
+
if not self._accepted_values_fn(item):
|
|
704
|
+
raise ParameterValueError(self, item, what=what)
|
|
691
705
|
|
|
692
706
|
def get_value(self):
|
|
693
707
|
return "+".join(self._value)
|
|
@@ -769,6 +783,7 @@ class TaskRegistry(object):
|
|
|
769
783
|
self.env = env
|
|
770
784
|
self.tasks = {}
|
|
771
785
|
self.instances = {}
|
|
786
|
+
self._workspace_resources = []
|
|
772
787
|
|
|
773
788
|
@staticmethod
|
|
774
789
|
def get(*args, **kwargs):
|
|
@@ -777,6 +792,21 @@ class TaskRegistry(object):
|
|
|
777
792
|
return TaskRegistry._instance
|
|
778
793
|
|
|
779
794
|
def add_task_class(self, cls):
|
|
795
|
+
"""
|
|
796
|
+
Add a task class to the registry.
|
|
797
|
+
|
|
798
|
+
The class is decorated to require workspace resources.
|
|
799
|
+
"""
|
|
800
|
+
|
|
801
|
+
registry = self
|
|
802
|
+
|
|
803
|
+
def _workspace_resources(self):
|
|
804
|
+
return registry._workspace_resources
|
|
805
|
+
|
|
806
|
+
if not issubclass(cls, WorkspaceResource):
|
|
807
|
+
cls = attributes.requires("_workspace_resources")(cls)
|
|
808
|
+
cls._workspace_resources = property(_workspace_resources)
|
|
809
|
+
|
|
780
810
|
self.tasks[cls.name] = cls
|
|
781
811
|
|
|
782
812
|
def add_task(self, task, extra_params):
|
|
@@ -785,6 +815,13 @@ class TaskRegistry(object):
|
|
|
785
815
|
full_name = utils.format_task_name(name, params)
|
|
786
816
|
self.instances[full_name] = task
|
|
787
817
|
|
|
818
|
+
def require_workspace_resource(self, taskname):
|
|
819
|
+
name, _ = utils.parse_task_name(taskname)
|
|
820
|
+
task = self.get_task_class(name)
|
|
821
|
+
raise_task_error_if(task is None, name, "Resource not found")
|
|
822
|
+
raise_task_error_if(not issubclass(task, WorkspaceResource), name, "Not a workspace resource")
|
|
823
|
+
self._workspace_resources.append(taskname)
|
|
824
|
+
|
|
788
825
|
def get_task_class(self, name):
|
|
789
826
|
return self.tasks.get(name)
|
|
790
827
|
|
|
@@ -811,6 +848,10 @@ class TaskRegistry(object):
|
|
|
811
848
|
|
|
812
849
|
raise_task_error_if(not task, full_name, "No such task")
|
|
813
850
|
|
|
851
|
+
def has_task(self, name):
|
|
852
|
+
name, params = utils.parse_task_name(name)
|
|
853
|
+
return self.tasks.get(name) is not None
|
|
854
|
+
|
|
814
855
|
def set_default_parameters(self, task):
|
|
815
856
|
name, params = utils.parse_task_name(task)
|
|
816
857
|
|
|
@@ -882,6 +923,14 @@ class TaskGenerator(object):
|
|
|
882
923
|
|
|
883
924
|
|
|
884
925
|
class attributes:
|
|
926
|
+
@staticmethod
|
|
927
|
+
def arch(cls):
|
|
928
|
+
""" Return the architecture name (x86_64, arm64, etc.). """
|
|
929
|
+
|
|
930
|
+
cls._arch = Export(lambda t: platform.machine().lower().replace("amd64", "x86_64"))
|
|
931
|
+
cls.arch = property(lambda t: t._arch.value)
|
|
932
|
+
return cls
|
|
933
|
+
|
|
885
934
|
@staticmethod
|
|
886
935
|
def artifact(name, session=False):
|
|
887
936
|
"""Decorator adding an additional artifact to a task.
|
|
@@ -1167,6 +1216,65 @@ class attributes:
|
|
|
1167
1216
|
return utils.concat_attributes("_publish_files", attrib)(cls)
|
|
1168
1217
|
return decorate
|
|
1169
1218
|
|
|
1219
|
+
@staticmethod
|
|
1220
|
+
def common_metadata(aclocal=True, cmake=True, cxxinfo=True, path=True, pkgconfig=True):
|
|
1221
|
+
"""
|
|
1222
|
+
Decorator adding common metadata to published artifacts.
|
|
1223
|
+
|
|
1224
|
+
The decorator adds common environment variables and C/C++ build information
|
|
1225
|
+
to the published artifact:
|
|
1226
|
+
|
|
1227
|
+
- Adds `bin/` to `PATH` environment variable if it exists.
|
|
1228
|
+
- Adds `lib/` and `lib64/` to C++ library paths if they exist.
|
|
1229
|
+
- Adds `include/` to C++ include paths if it exists.
|
|
1230
|
+
- Adds `lib/pkgconfig/`, `lib64/pkgconfig/` and `share/pkgconfig/` to
|
|
1231
|
+
`PKG_CONFIG_PATH` environment variable if `.pc` files are found.
|
|
1232
|
+
The `prefix` variable in `.pc` files is relocated to allow
|
|
1233
|
+
installation in arbitrary locations.
|
|
1234
|
+
- Adds `lib/cmake/`, `lib64/cmake/` and `share/cmake/` to
|
|
1235
|
+
`CMAKE_PREFIX_PATH` environment variable if they exist.
|
|
1236
|
+
- Adds `share/aclocal/` to `ACLOCAL_PATH` environment variable if it exists.
|
|
1237
|
+
|
|
1238
|
+
"""
|
|
1239
|
+
def decorate(cls):
|
|
1240
|
+
_old_publish = cls.publish
|
|
1241
|
+
|
|
1242
|
+
@functools.wraps(cls.publish)
|
|
1243
|
+
def publish(self, artifact, tools):
|
|
1244
|
+
_old_publish(self, artifact, tools)
|
|
1245
|
+
|
|
1246
|
+
with tools.cwd(artifact.path):
|
|
1247
|
+
if path and tools.exists("bin"):
|
|
1248
|
+
artifact.environ.PATH.append("bin")
|
|
1249
|
+
|
|
1250
|
+
pcdirs = set()
|
|
1251
|
+
|
|
1252
|
+
for pcpath in tools.glob("lib/pkgconfig/*.pc") + tools.glob("lib64/pkgconfig/*.pc") + tools.glob("share/pkgconfig/*.pc"):
|
|
1253
|
+
pcdirs.add(fs.path.dirname(pcpath))
|
|
1254
|
+
tools.replace_in_file(pcpath, artifact.strings.install_prefix, "${{pcfiledir}}/../..")
|
|
1255
|
+
for pcpath in tools.glob("lib/*/pkgconfig/*.pc") + tools.glob("lib64/*/pkgconfig/*.pc"):
|
|
1256
|
+
pcdirs.add(fs.path.dirname(pcpath))
|
|
1257
|
+
tools.replace_in_file(pcpath, artifact.strings.install_prefix, "${{pcfiledir}}/../../..")
|
|
1258
|
+
|
|
1259
|
+
for pcdir in pcdirs:
|
|
1260
|
+
artifact.environ.PKG_CONFIG_PATH.append(pcdir)
|
|
1261
|
+
|
|
1262
|
+
if cmake and tools.exists("lib/cmake"):
|
|
1263
|
+
artifact.environ.CMAKE_PREFIX_PATH.append(".")
|
|
1264
|
+
if cmake and tools.exists("lib64/cmake"):
|
|
1265
|
+
artifact.environ.CMAKE_PREFIX_PATH.append(".")
|
|
1266
|
+
if cmake and tools.exists("share/cmake"):
|
|
1267
|
+
artifact.environ.CMAKE_PREFIX_PATH.append(".")
|
|
1268
|
+
|
|
1269
|
+
if aclocal and tools.exists("share/aclocal"):
|
|
1270
|
+
artifact.environ.ACLOCAL_PATH.append("share/aclocal")
|
|
1271
|
+
|
|
1272
|
+
cls.publish = publish
|
|
1273
|
+
|
|
1274
|
+
return cls
|
|
1275
|
+
|
|
1276
|
+
return decorate
|
|
1277
|
+
|
|
1170
1278
|
@staticmethod
|
|
1171
1279
|
def requires(attrib):
|
|
1172
1280
|
"""
|
|
@@ -2648,7 +2756,7 @@ class ReportProxy(object):
|
|
|
2648
2756
|
if not filterfn(error):
|
|
2649
2757
|
continue
|
|
2650
2758
|
if error["location"] not in errors_by_location:
|
|
2651
|
-
errors_by_location[error["location"]] = (error, [error["message"]], error
|
|
2759
|
+
errors_by_location[error["location"]] = (error, [error["message"]], error.get("details", ""))
|
|
2652
2760
|
else:
|
|
2653
2761
|
errors_by_location[error["location"]][1].append(error["message"])
|
|
2654
2762
|
|
|
@@ -2935,6 +3043,7 @@ class Download(Task):
|
|
|
2935
3043
|
Once downloaded, archives are extracted and all of their files are published.
|
|
2936
3044
|
If the file is not an archive it is published as is. Recognized archive extensions are:
|
|
2937
3045
|
|
|
3046
|
+
- .7z
|
|
2938
3047
|
- .tar
|
|
2939
3048
|
- .tar.bz2
|
|
2940
3049
|
- .tar.gz
|
|
@@ -2997,7 +3106,7 @@ class Download(Task):
|
|
|
2997
3106
|
return fs.posixpath.basename(url.path) or "file"
|
|
2998
3107
|
|
|
2999
3108
|
def run(self, deps, tools):
|
|
3000
|
-
supported_formats = [".tar", ".tar.bz2", ".tar.gz", ".tar.xz", ".tgz", ".zip"]
|
|
3109
|
+
supported_formats = [".7z", ".tar", ".tar.bz2", ".tar.gz", ".tar.xz", ".tgz", ".zip"]
|
|
3001
3110
|
|
|
3002
3111
|
raise_task_error_if(not self.url, self, "No URL(s) specified")
|
|
3003
3112
|
|
|
@@ -3094,7 +3203,8 @@ class Script(Task):
|
|
|
3094
3203
|
doc = self.__doc__.split("---", 1)
|
|
3095
3204
|
script = doc[1] if len(doc) > 1 else doc[0]
|
|
3096
3205
|
script = script.splitlines()
|
|
3097
|
-
|
|
3206
|
+
if os_sys.version_info < (3, 13):
|
|
3207
|
+
script = [line[4:] for line in script]
|
|
3098
3208
|
script = "\n".join(script)
|
|
3099
3209
|
script = script.lstrip()
|
|
3100
3210
|
if not script.startswith("#!"):
|
|
@@ -3394,3 +3504,45 @@ class Test(Task):
|
|
|
3394
3504
|
"{} tests out of {} were successful".format(
|
|
3395
3505
|
len(self.testresult.successes),
|
|
3396
3506
|
self.testresult.testsRun))
|
|
3507
|
+
|
|
3508
|
+
|
|
3509
|
+
@ArtifactAttributeSetProvider.Register
|
|
3510
|
+
class WorkspaceResourceAttributeSetProvider(ArtifactAttributeSetProvider):
|
|
3511
|
+
def create(self, artifact):
|
|
3512
|
+
pass
|
|
3513
|
+
|
|
3514
|
+
def parse(self, artifact, content):
|
|
3515
|
+
pass
|
|
3516
|
+
|
|
3517
|
+
def format(self, artifact, content):
|
|
3518
|
+
pass
|
|
3519
|
+
|
|
3520
|
+
def apply(self, task, artifact):
|
|
3521
|
+
resource = artifact.task
|
|
3522
|
+
node = artifact.get_node()
|
|
3523
|
+
if not node.is_workspace_resource():
|
|
3524
|
+
return
|
|
3525
|
+
|
|
3526
|
+
resource.deps = node.cache.get_context(node)
|
|
3527
|
+
resource.deps.__enter__()
|
|
3528
|
+
|
|
3529
|
+
try:
|
|
3530
|
+
resource.acquire(artifact=artifact, deps=resource.deps, tools=resource.tools, owner=task)
|
|
3531
|
+
except (KeyboardInterrupt, Exception) as e:
|
|
3532
|
+
if resource.release_on_error:
|
|
3533
|
+
with utils.ignore_exception():
|
|
3534
|
+
self.unapply(task, artifact)
|
|
3535
|
+
raise e
|
|
3536
|
+
|
|
3537
|
+
def unapply(self, task, artifact):
|
|
3538
|
+
resource = artifact.task
|
|
3539
|
+
node = artifact.get_node()
|
|
3540
|
+
if not node.is_workspace_resource():
|
|
3541
|
+
return
|
|
3542
|
+
|
|
3543
|
+
try:
|
|
3544
|
+
resource.release(artifact=artifact, deps=resource.deps, tools=resource.tools, owner=task)
|
|
3545
|
+
except Exception as e:
|
|
3546
|
+
raise e
|
|
3547
|
+
|
|
3548
|
+
resource.deps.__exit__(None, None, None)
|
jolt/tools.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import py7zr
|
|
1
2
|
import bz2
|
|
2
3
|
import copy
|
|
3
4
|
import getpass
|
|
@@ -42,6 +43,9 @@ from jolt.error import raise_error_if
|
|
|
42
43
|
from jolt.error import raise_task_error, raise_task_error_if
|
|
43
44
|
|
|
44
45
|
|
|
46
|
+
SUPPORTED_ARCHIVE_TYPES = [".tar", ".tar.bz2", ".tar.gz", ".tgz", ".tar.xz", ".tar.zst", ".zip"]
|
|
47
|
+
|
|
48
|
+
|
|
45
49
|
http_session = Session()
|
|
46
50
|
|
|
47
51
|
|
|
@@ -83,11 +87,13 @@ class Reader(threading.Thread):
|
|
|
83
87
|
self.logbuf.append((self, line))
|
|
84
88
|
|
|
85
89
|
|
|
86
|
-
def _run(cmd, cwd, env,
|
|
90
|
+
def _run(cmd, cwd, env, *args, **kwargs):
|
|
87
91
|
output = kwargs.get("output")
|
|
88
92
|
output_on_error = kwargs.get("output_on_error")
|
|
89
93
|
output_rstrip = kwargs.get("output_rstrip", True)
|
|
90
94
|
output_stdio = kwargs.get("output_stdio", False)
|
|
95
|
+
output_stderr = kwargs.get("output_stderr", True)
|
|
96
|
+
output_stdout = kwargs.get("output_stdout", True)
|
|
91
97
|
return_stderr = kwargs.get("return_stderr", False)
|
|
92
98
|
output = output if output is not None else True
|
|
93
99
|
output = False if output_on_error else output
|
|
@@ -130,11 +136,17 @@ def _run(cmd, cwd, env, preexec_fn, *args, **kwargs):
|
|
|
130
136
|
shell=shell,
|
|
131
137
|
cwd=cwd,
|
|
132
138
|
env=env,
|
|
133
|
-
preexec_fn=preexec_fn,
|
|
134
139
|
)
|
|
135
140
|
|
|
136
|
-
|
|
137
|
-
|
|
141
|
+
if output_stdout:
|
|
142
|
+
stdout_func = log.stdout if not output_stdio else stdout_write
|
|
143
|
+
else:
|
|
144
|
+
stdout_func = None
|
|
145
|
+
|
|
146
|
+
if output_stderr:
|
|
147
|
+
stderr_func = log.stderr if not output_stdio else stderr_write
|
|
148
|
+
else:
|
|
149
|
+
stderr_func = None
|
|
138
150
|
|
|
139
151
|
logbuf = []
|
|
140
152
|
stdout = Reader(
|
|
@@ -160,7 +172,7 @@ def _run(cmd, cwd, env, preexec_fn, *args, **kwargs):
|
|
|
160
172
|
p.wait(10)
|
|
161
173
|
except subprocess.TimeoutExpired:
|
|
162
174
|
kill(p.pid)
|
|
163
|
-
p.wait
|
|
175
|
+
utils.call_and_catch(p.wait, 10)
|
|
164
176
|
raise
|
|
165
177
|
|
|
166
178
|
except (subprocess.TimeoutExpired, JoltTimeoutError):
|
|
@@ -170,7 +182,7 @@ def _run(cmd, cwd, env, preexec_fn, *args, **kwargs):
|
|
|
170
182
|
p.wait(10)
|
|
171
183
|
except subprocess.TimeoutExpired:
|
|
172
184
|
kill(p.pid)
|
|
173
|
-
p.wait
|
|
185
|
+
utils.call_and_catch(p.wait, 10)
|
|
174
186
|
|
|
175
187
|
finally:
|
|
176
188
|
if stdout:
|
|
@@ -249,6 +261,10 @@ class _CMake(object):
|
|
|
249
261
|
self.builddir = self.tools.builddir(incremental=incremental)
|
|
250
262
|
self.installdir = self.tools.builddir("install", incremental=False)
|
|
251
263
|
|
|
264
|
+
def clean(self):
|
|
265
|
+
self.tools.rmtree(self.builddir, ignore_errors=True)
|
|
266
|
+
self.tools.rmtree(self.installdir, ignore_errors=True)
|
|
267
|
+
|
|
252
268
|
def configure(self, sourcedir, *args, generator=None, **kwargs):
|
|
253
269
|
sourcedir = self.tools.expand_path(sourcedir)
|
|
254
270
|
|
|
@@ -259,74 +275,85 @@ class _CMake(object):
|
|
|
259
275
|
|
|
260
276
|
with self.tools.cwd(self.builddir):
|
|
261
277
|
self.tools.run(
|
|
262
|
-
"cmake {0}
|
|
278
|
+
"cmake {0} {1} -DCMAKE_INSTALL_PREFIX=/jolt-prefix {1} {2} {3}",
|
|
263
279
|
sourcedir,
|
|
264
|
-
self.builddir,
|
|
265
|
-
self.installdir,
|
|
280
|
+
utils.option("-B", self.builddir),
|
|
266
281
|
utils.option("-G", generator),
|
|
267
282
|
extra_args,
|
|
268
283
|
output=True)
|
|
269
284
|
|
|
270
|
-
def build(self,
|
|
285
|
+
def build(self, *args, config="Release", **kwargs):
|
|
271
286
|
threading_args = ' -j {}'.format(kwargs.get("threads", self.tools.thread_count()))
|
|
272
287
|
with self.tools.cwd(self.builddir):
|
|
273
|
-
|
|
274
|
-
self.tools.run("cmake --build . {0}{1}", release, threading_args, output=True)
|
|
288
|
+
self.tools.run("cmake --build . --config {0} {1}", config, threading_args, output=True)
|
|
275
289
|
|
|
276
|
-
def install(self,
|
|
277
|
-
with self.tools.cwd(self.builddir):
|
|
278
|
-
|
|
279
|
-
self.tools.run("cmake --build . --target install {0}", release, output=True)
|
|
290
|
+
def install(self, target="install", config="Release", **kwargs):
|
|
291
|
+
with self.tools.cwd(self.builddir), self.tools.environ(DESTDIR=self.installdir):
|
|
292
|
+
self.tools.run("cmake --build . --config {0} --target {1}", config, target, output=True)
|
|
280
293
|
|
|
281
|
-
def publish(self, artifact, files='*', *args, **kwargs):
|
|
282
|
-
with self.tools.cwd(self.installdir):
|
|
283
|
-
artifact.collect(files, *args, **kwargs)
|
|
294
|
+
def publish(self, artifact, files='*', symlinks=True, *args, **kwargs):
|
|
295
|
+
with self.tools.cwd(self.installdir, "jolt-prefix"):
|
|
296
|
+
artifact.collect(files, *args, symlinks=symlinks, **kwargs)
|
|
297
|
+
artifact.strings.install_prefix = "/jolt-prefix"
|
|
284
298
|
|
|
285
299
|
|
|
286
300
|
class _Meson(object):
|
|
287
|
-
def __init__(self, deps, tools):
|
|
301
|
+
def __init__(self, deps, tools, incremental=False):
|
|
288
302
|
self.deps = deps
|
|
289
303
|
self.tools = tools
|
|
290
|
-
self.builddir = self.tools.builddir()
|
|
291
|
-
self.installdir = self.tools.builddir("install")
|
|
304
|
+
self.builddir = self.tools.builddir(incremental=incremental)
|
|
305
|
+
self.installdir = self.tools.builddir("install", incremental=False)
|
|
306
|
+
self.prefix = "/jolt-prefix" if os.name != "nt" else "C:\\jolt-prefix"
|
|
307
|
+
|
|
308
|
+
def clean(self):
|
|
309
|
+
self.tools.rmtree(self.builddir, ignore_errors=True)
|
|
310
|
+
self.tools.rmtree(self.installdir, ignore_errors=True)
|
|
292
311
|
|
|
293
312
|
def configure(self, sourcedir, *args, **kwargs):
|
|
294
313
|
sourcedir = self.tools.expand_path(sourcedir)
|
|
295
|
-
|
|
314
|
+
options = " ".join([f"-D{arg}" for arg in args]) + " "
|
|
315
|
+
options += " ".join(["-D{0}={1}".format(key, self.tools.expand(val)) for key, val in kwargs.items()])
|
|
316
|
+
self.tools.run("meson setup --prefix={0} {1} {2} {3}", self.prefix, sourcedir, self.builddir, options,
|
|
296
317
|
output=True)
|
|
297
318
|
|
|
298
319
|
def build(self, *args, **kwargs):
|
|
299
320
|
self.tools.run("ninja -C {0} ", self.builddir, output=True)
|
|
300
321
|
|
|
301
322
|
def install(self, *args, **kwargs):
|
|
302
|
-
self.tools.
|
|
303
|
-
|
|
304
|
-
output=True)
|
|
323
|
+
with self.tools.environ(DESTDIR=self.installdir):
|
|
324
|
+
self.tools.run("ninja -C {0} install", self.builddir, output=True)
|
|
305
325
|
|
|
306
|
-
def publish(self, artifact, files='*', *args, **kwargs):
|
|
307
|
-
with self.tools.cwd(self.installdir):
|
|
308
|
-
artifact.collect(files, *args, **kwargs)
|
|
326
|
+
def publish(self, artifact, files='*', symlinks=True, *args, **kwargs):
|
|
327
|
+
with self.tools.cwd(self.installdir, "jolt-prefix"):
|
|
328
|
+
artifact.collect(files, *args, symlinks=symlinks, **kwargs)
|
|
329
|
+
artifact.strings.install_prefix = self.prefix
|
|
309
330
|
|
|
310
331
|
|
|
311
332
|
class _AutoTools(object):
|
|
312
|
-
def __init__(self, deps, tools):
|
|
333
|
+
def __init__(self, deps, tools, incremental=False):
|
|
313
334
|
self.deps = deps
|
|
314
335
|
self.tools = tools
|
|
315
|
-
self.builddir = self.tools.builddir()
|
|
316
|
-
self.installdir = self.tools.builddir("install")
|
|
336
|
+
self.builddir = self.tools.builddir(incremental=incremental)
|
|
337
|
+
self.installdir = self.tools.builddir("install", incremental=False)
|
|
338
|
+
self.prefix = "jolt-prefix"
|
|
317
339
|
|
|
318
|
-
def
|
|
340
|
+
def clean(self):
|
|
341
|
+
self.tools.rmtree(self.builddir, ignore_errors=True)
|
|
342
|
+
self.tools.rmtree(self.installdir, ignore_errors=True)
|
|
343
|
+
|
|
344
|
+
def configure(self, sourcedir, *args):
|
|
319
345
|
sourcedir = self.tools.expand_path(sourcedir)
|
|
320
|
-
prefix = kwargs.get("prefix", "/")
|
|
321
346
|
|
|
322
347
|
if not fs.path.exists(fs.path.join(sourcedir, "configure")):
|
|
323
348
|
with self.tools.cwd(sourcedir):
|
|
324
349
|
self.tools.run("autoreconf -visf", output=True)
|
|
325
350
|
|
|
326
351
|
with self.tools.cwd(self.builddir), self.tools.environ(DESTDIR=self.installdir):
|
|
327
|
-
self.tools.run("{0}/configure --prefix
|
|
328
|
-
sourcedir,
|
|
352
|
+
self.tools.run("{0}/configure --prefix=/{1} {2} {3}",
|
|
353
|
+
sourcedir,
|
|
354
|
+
self.prefix,
|
|
329
355
|
self.tools.getenv("CONFIGURE_FLAGS", ""),
|
|
356
|
+
" ".join(args),
|
|
330
357
|
output=True)
|
|
331
358
|
|
|
332
359
|
def build(self, *args, **kwargs):
|
|
@@ -334,13 +361,14 @@ class _AutoTools(object):
|
|
|
334
361
|
self.tools.run("make VERBOSE=yes Q= V=1 -j{0}",
|
|
335
362
|
self.tools.cpu_count(), output=True)
|
|
336
363
|
|
|
337
|
-
def install(self, target="install"
|
|
338
|
-
with self.tools.cwd(self.builddir)
|
|
339
|
-
self.tools.run("make {}", target, output=True)
|
|
364
|
+
def install(self, target="install"):
|
|
365
|
+
with self.tools.cwd(self.builddir):
|
|
366
|
+
self.tools.run("make DESTDIR={} {}", self.installdir, target, output=True)
|
|
340
367
|
|
|
341
|
-
def publish(self, artifact, files='*', *args, **kwargs):
|
|
342
|
-
with self.tools.cwd(self.installdir):
|
|
343
|
-
artifact.collect(files, *args, **kwargs)
|
|
368
|
+
def publish(self, artifact, files='*', symlinks=True, *args, **kwargs):
|
|
369
|
+
with self.tools.cwd(self.installdir, self.prefix):
|
|
370
|
+
artifact.collect(files, *args, symlinks=symlinks, **kwargs)
|
|
371
|
+
artifact.strings.install_prefix = "/" + self.prefix
|
|
344
372
|
|
|
345
373
|
|
|
346
374
|
class ZipFile(zipfile.ZipFile):
|
|
@@ -471,7 +499,6 @@ class Tools(object):
|
|
|
471
499
|
self._chroot_path = []
|
|
472
500
|
self._deadline = None
|
|
473
501
|
self._run_prefix = []
|
|
474
|
-
self._preexec_fn = None
|
|
475
502
|
self._cwd = fs.path.normpath(fs.path.join(config.get_workdir(), cwd or config.get_workdir()))
|
|
476
503
|
self._env = copy.deepcopy(env or os.environ)
|
|
477
504
|
self._task = task
|
|
@@ -520,6 +547,12 @@ class Tools(object):
|
|
|
520
547
|
zf.write(path, zippath)
|
|
521
548
|
return filename
|
|
522
549
|
|
|
550
|
+
def _make_7zfile(self, filename, fmt, rootdir):
|
|
551
|
+
self.mkdirname(filename)
|
|
552
|
+
with py7zr.SevenZipFile(filename, 'w') as archive:
|
|
553
|
+
archive.writeall(rootdir, ".")
|
|
554
|
+
return filename
|
|
555
|
+
|
|
523
556
|
def _make_tarfile(self, filename, fmt, rootdir):
|
|
524
557
|
self.mkdirname(filename)
|
|
525
558
|
with tarfile.open(filename, 'w|%s' % fmt) as tar:
|
|
@@ -552,6 +585,7 @@ class Tools(object):
|
|
|
552
585
|
The type of archive to create is determined by the filename extension.
|
|
553
586
|
Supported formats are:
|
|
554
587
|
|
|
588
|
+
- 7z
|
|
555
589
|
- tar
|
|
556
590
|
- tar.bz2
|
|
557
591
|
- tar.gz
|
|
@@ -587,12 +621,16 @@ class Tools(object):
|
|
|
587
621
|
fmt = "tarbz2"
|
|
588
622
|
elif filename.endswith(".tar.xz"):
|
|
589
623
|
fmt = "tarxz"
|
|
624
|
+
elif filename.endswith(".7z"):
|
|
625
|
+
fmt = "7z"
|
|
590
626
|
raise_task_error_if(
|
|
591
627
|
not fmt, self._task,
|
|
592
628
|
"unknown archive type '{0}'", fs.path.basename(filename))
|
|
593
629
|
try:
|
|
594
630
|
if fmt == "zip":
|
|
595
631
|
outfile = self._make_zipfile(filename, fmt, rootdir=pathname)
|
|
632
|
+
elif fmt == "7z":
|
|
633
|
+
outfile = self._make_7zfile(filename, fmt, rootdir=pathname)
|
|
596
634
|
else:
|
|
597
635
|
outfile = self._make_tarfile(filename, fmt[3:], rootdir=pathname)
|
|
598
636
|
if outfile != filename:
|
|
@@ -601,9 +639,9 @@ class Tools(object):
|
|
|
601
639
|
except Exception:
|
|
602
640
|
raise_task_error(self._task, "failed to create archive from directory '{0}'", pathname)
|
|
603
641
|
|
|
604
|
-
def autotools(self, deps=None):
|
|
642
|
+
def autotools(self, deps=None, incremental=False):
|
|
605
643
|
""" Creates an AutoTools invokation helper """
|
|
606
|
-
return _AutoTools(deps, self)
|
|
644
|
+
return _AutoTools(deps, self, incremental=incremental)
|
|
607
645
|
|
|
608
646
|
@utils.locked(lock='_builddir_lock')
|
|
609
647
|
def builddir(self, name=None, incremental=False, unique=True):
|
|
@@ -888,7 +926,7 @@ class Tools(object):
|
|
|
888
926
|
pbar.update(len(data))
|
|
889
927
|
actual_size = self.file_size(pathname)
|
|
890
928
|
raise_error_if(
|
|
891
|
-
size != 0 and size
|
|
929
|
+
size != 0 and size > actual_size,
|
|
892
930
|
f"Downloaded file was truncated to {actual_size}/{size} bytes: {name}")
|
|
893
931
|
|
|
894
932
|
return response.status_code == 200
|
|
@@ -1047,6 +1085,7 @@ class Tools(object):
|
|
|
1047
1085
|
|
|
1048
1086
|
Supported formats are:
|
|
1049
1087
|
|
|
1088
|
+
- 7z
|
|
1050
1089
|
- tar
|
|
1051
1090
|
- tar.bz2
|
|
1052
1091
|
- tar.gz
|
|
@@ -1110,6 +1149,13 @@ class Tools(object):
|
|
|
1110
1149
|
self._extract_tarzstd(filename, filepath, files)
|
|
1111
1150
|
except tarfile.StreamError as e:
|
|
1112
1151
|
raise_task_error(self._task, "failed to extract archive '{0}': {1}", filename, str(e))
|
|
1152
|
+
elif filename.endswith(".7z"):
|
|
1153
|
+
with py7zr.SevenZipFile(filename, 'r') as archive:
|
|
1154
|
+
if files:
|
|
1155
|
+
for file in files:
|
|
1156
|
+
archive.extract(file, filepath)
|
|
1157
|
+
else:
|
|
1158
|
+
archive.extractall(filepath)
|
|
1113
1159
|
else:
|
|
1114
1160
|
raise_task_error(self._task, "unknown archive type '{0}'", fs.path.basename(filename))
|
|
1115
1161
|
except Exception:
|
|
@@ -1261,9 +1307,9 @@ class Tools(object):
|
|
|
1261
1307
|
"""
|
|
1262
1308
|
return utils.map_concurrent(callable, iterable, max_workers)
|
|
1263
1309
|
|
|
1264
|
-
def meson(self, deps=None):
|
|
1310
|
+
def meson(self, deps=None, incremental=False):
|
|
1265
1311
|
""" Creates a Meson invokation helper """
|
|
1266
|
-
return _Meson(deps, self)
|
|
1312
|
+
return _Meson(deps, self, incremental=incremental)
|
|
1267
1313
|
|
|
1268
1314
|
@contextmanager
|
|
1269
1315
|
def nixpkgs(self, nixfile=None, packages=None, pure=False, path=None, options=None):
|
|
@@ -1539,6 +1585,9 @@ class Tools(object):
|
|
|
1539
1585
|
refuses to terminate, it will be killed after an additional
|
|
1540
1586
|
10 seconds have passed. Default: None.
|
|
1541
1587
|
|
|
1588
|
+
Returns:
|
|
1589
|
+
str: stdout from command unless output=False
|
|
1590
|
+
|
|
1542
1591
|
Example:
|
|
1543
1592
|
|
|
1544
1593
|
.. code-block:: python
|
|
@@ -1578,7 +1627,7 @@ class Tools(object):
|
|
|
1578
1627
|
except Exception:
|
|
1579
1628
|
pass
|
|
1580
1629
|
|
|
1581
|
-
return _run(cmd, self._cwd, self._env,
|
|
1630
|
+
return _run(cmd, self._cwd, self._env, *args, **kwargs)
|
|
1582
1631
|
|
|
1583
1632
|
finally:
|
|
1584
1633
|
if stdi:
|
|
@@ -1692,11 +1741,15 @@ class Tools(object):
|
|
|
1692
1741
|
fs.makedirs(path)
|
|
1693
1742
|
for relsrcpath, reldstpath in artifact.files.items():
|
|
1694
1743
|
srcpath = fs.path.normpath(fs.path.join(artifact.task.joltdir, relsrcpath))
|
|
1744
|
+
srcpath = self.expand_path(srcpath)
|
|
1695
1745
|
dstpath = fs.path.normpath(fs.path.join(path, reldstpath))
|
|
1746
|
+
dstpath = self.expand_path(dstpath)
|
|
1747
|
+
|
|
1696
1748
|
if dstpath != fs.path.realpath(dstpath):
|
|
1697
1749
|
log.debug("Cannot symlink '{} -> {}', parent directory already symlinked",
|
|
1698
1750
|
srcpath, dstpath)
|
|
1699
1751
|
continue
|
|
1752
|
+
|
|
1700
1753
|
if fs.path.isdir(dstpath):
|
|
1701
1754
|
files = fs.scandir(srcpath)
|
|
1702
1755
|
for file in files:
|
|
@@ -1706,7 +1759,8 @@ class Tools(object):
|
|
|
1706
1759
|
self.symlink(srcpath, dstpath)
|
|
1707
1760
|
|
|
1708
1761
|
# Restore missing srcfiles if they resided in a build directory
|
|
1709
|
-
|
|
1762
|
+
buildroot_abs = self.expand_path(artifact.tools.buildroot)
|
|
1763
|
+
if srcpath.startswith(buildroot_abs) and \
|
|
1710
1764
|
not fs.path.exists(srcpath):
|
|
1711
1765
|
fs.copy(fs.path.join(artifact.path, reldstpath), srcpath, symlinks=True)
|
|
1712
1766
|
self.write_file(meta, artifact.path)
|
|
@@ -1865,7 +1919,7 @@ class Tools(object):
|
|
|
1865
1919
|
|
|
1866
1920
|
if type(chroot) in [cache.Artifact, cache.ArtifactToolsProxy]:
|
|
1867
1921
|
raise_task_error_if(
|
|
1868
|
-
not
|
|
1922
|
+
not chroot.paths.rootfs, self._task,
|
|
1869
1923
|
"No 'rootfs' path in artifact")
|
|
1870
1924
|
chroot = chroot.paths.rootfs
|
|
1871
1925
|
|