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.
- jolt/__init__.py +80 -7
- jolt/__main__.py +9 -1
- jolt/bin/fstree-darwin-x86_64 +0 -0
- jolt/bin/fstree-linux-x86_64 +0 -0
- jolt/cache.py +832 -362
- jolt/chroot.py +156 -0
- jolt/cli.py +281 -162
- jolt/common_pb2.py +63 -0
- jolt/common_pb2_grpc.py +4 -0
- jolt/config.py +98 -41
- jolt/error.py +19 -4
- jolt/filesystem.py +2 -6
- jolt/graph.py +705 -117
- jolt/hooks.py +43 -0
- jolt/influence.py +122 -3
- jolt/loader.py +369 -121
- jolt/log.py +225 -63
- jolt/manifest.py +28 -38
- jolt/options.py +35 -10
- 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 +24 -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 +87 -0
- jolt/pkgs/zstd.py +33 -0
- jolt/plugins/alias.py +3 -0
- jolt/plugins/allure.py +5 -2
- jolt/plugins/autotools.py +66 -0
- jolt/plugins/cache.py +133 -0
- jolt/plugins/cmake.py +74 -6
- jolt/plugins/conan.py +238 -0
- jolt/plugins/cxx.py +698 -0
- jolt/plugins/cxxinfo.py +7 -0
- jolt/plugins/dashboard.py +1 -1
- jolt/plugins/docker.py +80 -23
- jolt/plugins/email.py +2 -2
- jolt/plugins/email.xslt +144 -101
- jolt/plugins/environ.py +11 -0
- jolt/plugins/fetch.py +141 -0
- jolt/plugins/gdb.py +39 -19
- jolt/plugins/gerrit.py +1 -14
- jolt/plugins/git.py +283 -85
- jolt/plugins/googletest.py +2 -1
- jolt/plugins/http.py +36 -38
- jolt/plugins/libtool.py +63 -0
- jolt/plugins/linux.py +990 -0
- jolt/plugins/logstash.py +4 -4
- jolt/plugins/meson.py +61 -0
- jolt/plugins/ninja-compdb.py +99 -30
- jolt/plugins/ninja.py +468 -166
- jolt/plugins/paths.py +11 -1
- jolt/plugins/pkgconfig.py +219 -0
- jolt/plugins/podman.py +136 -92
- jolt/plugins/python.py +137 -0
- jolt/plugins/remote_execution/__init__.py +0 -0
- jolt/plugins/remote_execution/administration_pb2.py +46 -0
- jolt/plugins/remote_execution/administration_pb2_grpc.py +170 -0
- jolt/plugins/remote_execution/log_pb2.py +32 -0
- jolt/plugins/remote_execution/log_pb2_grpc.py +68 -0
- jolt/plugins/remote_execution/scheduler_pb2.py +41 -0
- jolt/plugins/remote_execution/scheduler_pb2_grpc.py +141 -0
- jolt/plugins/remote_execution/worker_pb2.py +38 -0
- jolt/plugins/remote_execution/worker_pb2_grpc.py +112 -0
- jolt/plugins/report.py +12 -2
- jolt/plugins/rust.py +25 -0
- jolt/plugins/scheduler.py +710 -0
- jolt/plugins/selfdeploy/setup.py +8 -4
- jolt/plugins/selfdeploy.py +138 -88
- jolt/plugins/strings.py +35 -22
- jolt/plugins/symlinks.py +26 -11
- jolt/plugins/telemetry.py +5 -2
- jolt/plugins/timeline.py +13 -3
- jolt/plugins/volume.py +46 -48
- jolt/scheduler.py +589 -192
- jolt/tasks.py +625 -121
- jolt/templates/timeline.html.template +44 -47
- jolt/timer.py +22 -0
- jolt/tools.py +638 -282
- jolt/utils.py +211 -7
- jolt/version.py +1 -1
- jolt/xmldom.py +12 -2
- {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/METADATA +97 -38
- jolt-0.9.435.dist-info/RECORD +207 -0
- {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/WHEEL +1 -1
- jolt/plugins/amqp.py +0 -834
- jolt/plugins/debian.py +0 -338
- jolt/plugins/ftp.py +0 -181
- jolt/plugins/repo.py +0 -253
- jolt-0.9.123.dist-info/RECORD +0 -77
- {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/entry_points.txt +0 -0
- {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/top_level.txt +0 -0
jolt/plugins/paths.py
CHANGED
|
@@ -16,6 +16,12 @@ class PathVariable(ArtifactStringAttribute):
|
|
|
16
16
|
def unapply(self, task, artifact):
|
|
17
17
|
pass
|
|
18
18
|
|
|
19
|
+
def __bool__(self):
|
|
20
|
+
return bool(str(self))
|
|
21
|
+
|
|
22
|
+
def __eq__(self, value: str) -> bool:
|
|
23
|
+
return str(self) == str(value)
|
|
24
|
+
|
|
19
25
|
def __str__(self):
|
|
20
26
|
if self._value is None:
|
|
21
27
|
return ""
|
|
@@ -30,6 +36,10 @@ class PathVariableSet(ArtifactAttributeSet):
|
|
|
30
36
|
def create(self, name):
|
|
31
37
|
return PathVariable(self._artifact, name)
|
|
32
38
|
|
|
39
|
+
def __getattr__(self, name):
|
|
40
|
+
attributes = self._get_attributes()
|
|
41
|
+
return attributes.get(name, None)
|
|
42
|
+
|
|
33
43
|
|
|
34
44
|
@ArtifactAttributeSetProvider.Register
|
|
35
45
|
class PathVariableSetProvider(ArtifactAttributeSetProvider):
|
|
@@ -41,7 +51,7 @@ class PathVariableSetProvider(ArtifactAttributeSetProvider):
|
|
|
41
51
|
return
|
|
42
52
|
|
|
43
53
|
for key, value in content["paths"].items():
|
|
44
|
-
|
|
54
|
+
setattr(artifact.paths, key, value)
|
|
45
55
|
|
|
46
56
|
def format(self, artifact, content):
|
|
47
57
|
if "paths" not in content:
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
from jolt import attributes
|
|
2
|
+
from jolt import filesystem as fs
|
|
3
|
+
from jolt import log
|
|
4
|
+
from jolt import utils
|
|
5
|
+
from jolt.error import raise_error_if
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PkgConfigHelper(object):
|
|
9
|
+
|
|
10
|
+
TEMPLATE_PKGCONFIG = """
|
|
11
|
+
prefix={{ artifact.final_path }}
|
|
12
|
+
|
|
13
|
+
Name: {{ pkgname }}
|
|
14
|
+
Description: {{ pkgname }} package
|
|
15
|
+
Version: {{ artifact.identity }}
|
|
16
|
+
|
|
17
|
+
Cflags: {% for flag in cxxflags %}{{ flag }} {% endfor %}{% for inc in incpaths %}-I${prefix}/{{ inc }} {% endfor %}{% for macro in macros %}-D{{ macro }} {% endfor %}
|
|
18
|
+
|
|
19
|
+
Libs: {% for flag in ldflags %}{{ flag }} {% endfor %}{% for libpath in libpaths %}-L${prefix}/{{ libpath }} {% endfor %}{% for library in libraries %}-l{{ library }} {% endfor %}
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, artifact, tools):
|
|
24
|
+
self.artifact = artifact
|
|
25
|
+
self.tools = tools
|
|
26
|
+
|
|
27
|
+
def _mkpath(self, path):
|
|
28
|
+
if fs.path.commonpath([path, self.artifact.path]) != self.artifact.path:
|
|
29
|
+
return path
|
|
30
|
+
return fs.path.relpath(path, self.artifact.path)
|
|
31
|
+
|
|
32
|
+
def cflags(self, package):
|
|
33
|
+
package = " ".join(utils.as_list(package))
|
|
34
|
+
try:
|
|
35
|
+
with self.tools.environ(**self.environ):
|
|
36
|
+
output = self.tools.run("{} --maximum-traverse-depth=1 --cflags-only-other {}", self.pkgconfig, package, output=False)
|
|
37
|
+
return output.strip().split()
|
|
38
|
+
except Exception as e:
|
|
39
|
+
log.debug("PkgConfig.cflags: {}", str(e))
|
|
40
|
+
return []
|
|
41
|
+
|
|
42
|
+
def incpaths(self, package):
|
|
43
|
+
package = " ".join(utils.as_list(package))
|
|
44
|
+
try:
|
|
45
|
+
with self.tools.environ(**self.environ):
|
|
46
|
+
output = self.tools.run("{} --maximum-traverse-depth=1 --cflags-only-I {}", self.pkgconfig, package, output=False)
|
|
47
|
+
return [self._mkpath(inc[2:]) for inc in output.strip().split()]
|
|
48
|
+
except Exception as e:
|
|
49
|
+
log.debug("PkgConfig.incpaths: {}", str(e))
|
|
50
|
+
return []
|
|
51
|
+
|
|
52
|
+
def linkflags(self, package):
|
|
53
|
+
package = " ".join(utils.as_list(package))
|
|
54
|
+
try:
|
|
55
|
+
with self.tools.environ(**self.environ):
|
|
56
|
+
output = self.tools.run("{} --maximum-traverse-depth=1 --libs-only-other {}", self.pkgconfig, package, output=False)
|
|
57
|
+
return output.strip().split()
|
|
58
|
+
except Exception as e:
|
|
59
|
+
log.debug("PkgConfig.linkflags: {}", str(e))
|
|
60
|
+
return []
|
|
61
|
+
|
|
62
|
+
def libpaths(self, package):
|
|
63
|
+
package = " ".join(utils.as_list(package))
|
|
64
|
+
try:
|
|
65
|
+
with self.tools.environ(**self.environ):
|
|
66
|
+
output = self.tools.run("{} --maximum-traverse-depth=1 --libs-only-L {}", self.pkgconfig, package, output=False)
|
|
67
|
+
return [self._mkpath(lib[2:]) for lib in output.strip().split()]
|
|
68
|
+
except Exception as e:
|
|
69
|
+
log.debug("PkgConfig.libpaths: {}", str(e))
|
|
70
|
+
return []
|
|
71
|
+
|
|
72
|
+
def libraries(self, package):
|
|
73
|
+
package = " ".join(utils.as_list(package))
|
|
74
|
+
try:
|
|
75
|
+
with self.tools.environ(**self.environ):
|
|
76
|
+
output = self.tools.run("{} --maximum-traverse-depth=1 --libs-only-l {}", self.pkgconfig, package, output=False)
|
|
77
|
+
return [lib[2:] for lib in output.strip().split()]
|
|
78
|
+
except Exception as e:
|
|
79
|
+
log.debug("PkgConfig.libraries: {}", str(e))
|
|
80
|
+
return []
|
|
81
|
+
|
|
82
|
+
def write_pc(self, package):
|
|
83
|
+
with self.tools.tmpdir() as tmpdir, self.tools.cwd(tmpdir):
|
|
84
|
+
content = self.tools.render(
|
|
85
|
+
self.TEMPLATE_PKGCONFIG,
|
|
86
|
+
artifact=self.artifact,
|
|
87
|
+
pkgname=package,
|
|
88
|
+
macros=list(self.artifact.cxxinfo.macros),
|
|
89
|
+
incpaths=list(self.artifact.cxxinfo.incpaths),
|
|
90
|
+
libpaths=list(self.artifact.cxxinfo.libpaths),
|
|
91
|
+
libraries=list(self.artifact.cxxinfo.libraries),
|
|
92
|
+
)
|
|
93
|
+
print(content)
|
|
94
|
+
self.tools.write_file(f"{package}.pc", content, expand=False)
|
|
95
|
+
self.artifact.collect(f"{package}.pc", "lib/pkgconfig/")
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def pkgconfig(self):
|
|
99
|
+
pkgconf = self.tools.which(self.tools.getenv("PKG_CONFIG", "pkg-config"))
|
|
100
|
+
raise_error_if(not pkgconf, "No pkg-config tool found in PATH")
|
|
101
|
+
return pkgconf
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def environ(self):
|
|
105
|
+
path = self.artifact.environ.get("PKG_CONFIG_PATH")
|
|
106
|
+
if path is None:
|
|
107
|
+
self.tools._task.verbose("No PKG_CONFIG_PATH in artifact environment")
|
|
108
|
+
|
|
109
|
+
# Path from the artifact environment
|
|
110
|
+
path = str(path).split(fs.pathsep)
|
|
111
|
+
path = fs.pathsep.join(fs.path.join(self.artifact.path, p) for p in path)
|
|
112
|
+
|
|
113
|
+
# Append existing PKG_CONFIG_PATH from the tools environment
|
|
114
|
+
if self.tools.getenv("PKG_CONFIG_PATH"):
|
|
115
|
+
path = path + fs.pathsep + self.tools.getenv("PKG_CONFIG_PATH")
|
|
116
|
+
|
|
117
|
+
return {"PKG_CONFIG_PATH": path}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def to_cxxinfo(
|
|
121
|
+
pkg: list | str,
|
|
122
|
+
cflags: bool = True,
|
|
123
|
+
cxxflags: bool = True,
|
|
124
|
+
incpaths: bool = True,
|
|
125
|
+
ldflags: bool = True,
|
|
126
|
+
libpaths: bool = True,
|
|
127
|
+
libraries: bool = True,
|
|
128
|
+
):
|
|
129
|
+
"""
|
|
130
|
+
Decorator to add pkg-config information to cxxinfo metadata of an artifact.
|
|
131
|
+
|
|
132
|
+
The decorator enables interoperability between libraries built with Jolt's
|
|
133
|
+
Ninja plugin and external packages that provide pkg-config files.
|
|
134
|
+
|
|
135
|
+
It uses the pkg-config tool to query for compiler and linker flags,
|
|
136
|
+
include paths, library paths, and libraries associated with a given package.
|
|
137
|
+
If the relevant flags are found, they are appended to the corresponding fields
|
|
138
|
+
in the artifact's cxxinfo metadata.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
pkg (str): The name of the pkg-config package to be added to cxxinfo.
|
|
142
|
+
cflags (bool): Whether to add C compiler flags from pkg-config.
|
|
143
|
+
cxxflags (bool): Whether to add C++ compiler flags from pkg-config.
|
|
144
|
+
incpaths (bool): Whether to add include paths from pkg-config.
|
|
145
|
+
ldflags (bool): Whether to add linker flags from pkg-config.
|
|
146
|
+
libpaths (bool): Whether to add library paths from pkg-config.
|
|
147
|
+
libraries (bool): Whether to add libraries from pkg-config.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
def decorate(cls):
|
|
151
|
+
original_publish = cls.publish
|
|
152
|
+
|
|
153
|
+
def publish(self, artifact, tools):
|
|
154
|
+
original_publish(self, artifact, tools)
|
|
155
|
+
|
|
156
|
+
pc = PkgConfigHelper(artifact, tools)
|
|
157
|
+
if not pc.environ:
|
|
158
|
+
self.verbose("Skipping pkg-config cxxinfo addition due to missing PKG_CONFIG_PATH.")
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
if cflags:
|
|
162
|
+
artifact.cxxinfo.cflags.extend(pc.cflags(pkg))
|
|
163
|
+
if cxxflags:
|
|
164
|
+
artifact.cxxinfo.cxxflags.extend(pc.cflags(pkg))
|
|
165
|
+
if incpaths:
|
|
166
|
+
artifact.cxxinfo.incpaths.extend(pc.incpaths(pkg))
|
|
167
|
+
if ldflags:
|
|
168
|
+
artifact.cxxinfo.ldflags.extend(pc.linkflags(pkg))
|
|
169
|
+
if libpaths:
|
|
170
|
+
artifact.cxxinfo.libpaths.extend(pc.libpaths(pkg))
|
|
171
|
+
if libraries:
|
|
172
|
+
artifact.cxxinfo.libraries.extend(pc.libraries(pkg))
|
|
173
|
+
|
|
174
|
+
cls.publish = publish
|
|
175
|
+
return cls
|
|
176
|
+
|
|
177
|
+
return decorate
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def from_cxxinfo(package):
|
|
181
|
+
"""
|
|
182
|
+
Decorator to write a pkg-config file for the given package
|
|
183
|
+
based on the cxxinfo metadata of the artifact.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
def decorate(cls):
|
|
187
|
+
original_publish = cls.publish
|
|
188
|
+
original_unpack = cls.unpack
|
|
189
|
+
|
|
190
|
+
def publish(self, artifact, tools):
|
|
191
|
+
original_publish(self, artifact, tools)
|
|
192
|
+
|
|
193
|
+
pc = PkgConfigHelper(artifact, tools)
|
|
194
|
+
pc.write_pc(package)
|
|
195
|
+
|
|
196
|
+
def unpack(self, artifact, tools):
|
|
197
|
+
original_unpack(self, artifact, tools)
|
|
198
|
+
|
|
199
|
+
pc = PkgConfigHelper(artifact, tools)
|
|
200
|
+
pc.write_pc(package)
|
|
201
|
+
|
|
202
|
+
cls.publish = publish
|
|
203
|
+
cls.unpack = unpack
|
|
204
|
+
return cls
|
|
205
|
+
|
|
206
|
+
return decorate
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def requires():
|
|
210
|
+
""" Decorator to add pkg-config requirements to a task. """
|
|
211
|
+
|
|
212
|
+
import jolt.pkgs.pkgconfig
|
|
213
|
+
|
|
214
|
+
def decorate(cls):
|
|
215
|
+
cls = attributes.requires("requires_pkgconf")(cls)
|
|
216
|
+
cls.requires_pkgconf = ["pkgconf"]
|
|
217
|
+
return cls
|
|
218
|
+
|
|
219
|
+
return decorate
|
jolt/plugins/podman.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from jolt import
|
|
1
|
+
from jolt import Parameter, Resource, Task
|
|
2
2
|
from jolt.error import raise_task_error_if
|
|
3
3
|
from jolt.tasks import TaskRegistry
|
|
4
4
|
from jolt import attributes
|
|
@@ -15,7 +15,6 @@ from jolt.cache import ArtifactAttributeSetProvider
|
|
|
15
15
|
import contextlib
|
|
16
16
|
import json
|
|
17
17
|
from os import path
|
|
18
|
-
from platform import system
|
|
19
18
|
import tarfile
|
|
20
19
|
|
|
21
20
|
|
|
@@ -25,24 +24,20 @@ class PodmanListVariable(ArtifactListAttribute):
|
|
|
25
24
|
|
|
26
25
|
class PodmanImportListVariable(PodmanListVariable):
|
|
27
26
|
def apply(self, task, artifact):
|
|
28
|
-
if isinstance(task, Resource):
|
|
29
|
-
return
|
|
30
27
|
for tar in self.items():
|
|
31
28
|
try:
|
|
32
29
|
tag = artifact.podman.tags[0]
|
|
33
30
|
except IndexError:
|
|
34
|
-
tag = "{}:{}"
|
|
31
|
+
tag = artifact.tools.expand("{canonical_name}:{identity}")
|
|
35
32
|
task.tools.run(
|
|
36
33
|
"podman import {} {}",
|
|
37
|
-
fs.path.join(artifact.path, tar, tag
|
|
34
|
+
fs.path.join(artifact.path, tar), tag, output_on_error=True)
|
|
38
35
|
for extra_tag in artifact.podman.tags[1:]:
|
|
39
36
|
task.tools.run("podman tag {} {}", tag, extra_tag, output_on_error=True)
|
|
40
37
|
|
|
41
38
|
|
|
42
39
|
class PodmanLoadListVariable(PodmanListVariable):
|
|
43
40
|
def apply(self, task, artifact):
|
|
44
|
-
if isinstance(task, Resource):
|
|
45
|
-
return
|
|
46
41
|
for image in self.items():
|
|
47
42
|
task.tools.run(
|
|
48
43
|
"podman load -i {}",
|
|
@@ -51,16 +46,12 @@ class PodmanLoadListVariable(PodmanListVariable):
|
|
|
51
46
|
|
|
52
47
|
class PodmanPullListVariable(PodmanListVariable):
|
|
53
48
|
def apply(self, task, artifact):
|
|
54
|
-
if isinstance(task, Resource):
|
|
55
|
-
return
|
|
56
49
|
for image in self.items():
|
|
57
50
|
task.tools.run("podman pull {}", image, output_on_error=True)
|
|
58
51
|
|
|
59
52
|
|
|
60
53
|
class PodmanRmiListVariable(PodmanListVariable):
|
|
61
54
|
def unapply(self, task, artifact):
|
|
62
|
-
if isinstance(task, Resource):
|
|
63
|
-
return
|
|
64
55
|
for image in self.items():
|
|
65
56
|
task.tools.run("podman rmi -f {}", image, output_on_error=True)
|
|
66
57
|
|
|
@@ -108,35 +99,6 @@ class PodmanAttributeProvider(ArtifactAttributeSetProvider):
|
|
|
108
99
|
artifact.podman.unapply(task, artifact)
|
|
109
100
|
|
|
110
101
|
|
|
111
|
-
class PodmanClient(Download):
|
|
112
|
-
""" Task: Downloads and publishes the Podman command line client.
|
|
113
|
-
|
|
114
|
-
The task will be automatically made available after importing
|
|
115
|
-
``jolt.plugins.podman``.
|
|
116
|
-
"""
|
|
117
|
-
|
|
118
|
-
name = "podman/cli"
|
|
119
|
-
""" Name of the task """
|
|
120
|
-
|
|
121
|
-
arch = Parameter("x86_64", help="Host architecture")
|
|
122
|
-
""" Host architecture [x86_64] """
|
|
123
|
-
|
|
124
|
-
collect = ["podman/podman"]
|
|
125
|
-
|
|
126
|
-
host = Parameter(system().lower(), help="Host operating system")
|
|
127
|
-
""" Host operating system [autodetected] """
|
|
128
|
-
|
|
129
|
-
url = "https://download.podman.com/{host}/static/stable/{arch}/podman-{version}.tgz"
|
|
130
|
-
""" URL of binaries """
|
|
131
|
-
|
|
132
|
-
version = Parameter("20.10.13", help="Podman version")
|
|
133
|
-
""" Podman version [20.10.13] """
|
|
134
|
-
|
|
135
|
-
def publish(self, artifact, tools):
|
|
136
|
-
super().publish(artifact, tools)
|
|
137
|
-
artifact.environ.PATH.append("podman")
|
|
138
|
-
|
|
139
|
-
|
|
140
102
|
@attributes.requires("_image")
|
|
141
103
|
class Container(Resource):
|
|
142
104
|
"""
|
|
@@ -193,6 +155,18 @@ class Container(Resource):
|
|
|
193
155
|
Alternatively, assign ``True`` to publish all exposed ports to random ports.
|
|
194
156
|
"""
|
|
195
157
|
|
|
158
|
+
release_on_error = True
|
|
159
|
+
""" Stop and remove container on error to avoid resource leaks. """
|
|
160
|
+
|
|
161
|
+
stop_timeout = 10
|
|
162
|
+
""" Timeout in seconds for stopping the container .
|
|
163
|
+
|
|
164
|
+
When stopping the container, the task will wait for the container to stop
|
|
165
|
+
for the specified number of seconds before forcefully killing it.
|
|
166
|
+
|
|
167
|
+
Default: 10 seconds.
|
|
168
|
+
"""
|
|
169
|
+
|
|
196
170
|
volumes = []
|
|
197
171
|
"""
|
|
198
172
|
A list of volumes to mount.
|
|
@@ -202,8 +176,8 @@ class Container(Resource):
|
|
|
202
176
|
"""
|
|
203
177
|
|
|
204
178
|
volumes_default = [
|
|
205
|
-
"{joltdir}
|
|
206
|
-
"{joltcachedir}
|
|
179
|
+
"{joltdir}",
|
|
180
|
+
"{joltcachedir}",
|
|
207
181
|
]
|
|
208
182
|
"""
|
|
209
183
|
A list of default volumes to mount.
|
|
@@ -243,7 +217,7 @@ class Container(Resource):
|
|
|
243
217
|
def _image(self):
|
|
244
218
|
registry = TaskRegistry.get()
|
|
245
219
|
tool = tools.Tools(self)
|
|
246
|
-
if registry.
|
|
220
|
+
if registry.has_task(tool.expand(self.image)):
|
|
247
221
|
return [self.image]
|
|
248
222
|
return []
|
|
249
223
|
|
|
@@ -280,10 +254,12 @@ class Container(Resource):
|
|
|
280
254
|
|
|
281
255
|
@property
|
|
282
256
|
def _volumes(self):
|
|
283
|
-
return " ".join([utils.option("-v ", self.tools.
|
|
257
|
+
return " ".join([utils.option("-v ", self.tools.expand_path(vol))
|
|
284
258
|
for vol in self.volumes_default + self.volumes])
|
|
285
259
|
|
|
286
260
|
def acquire(self, artifact, deps, tools, owner):
|
|
261
|
+
self._context_stack = None
|
|
262
|
+
self.container = None
|
|
287
263
|
self.joltcachedir = config.get_cachedir()
|
|
288
264
|
try:
|
|
289
265
|
image = deps[self.image]
|
|
@@ -292,9 +268,10 @@ class Container(Resource):
|
|
|
292
268
|
image = tools.expand(self.image)
|
|
293
269
|
|
|
294
270
|
self._info(f"Creating container from image '{image}'")
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
271
|
+
with utils.delayed_interrupt():
|
|
272
|
+
self.container = tools.run(
|
|
273
|
+
"podman run -i -d {_cap_adds} {_cap_drops} {_entrypoint} {_labels} {_ports} {_privileged} {_user} {_environment} {_volumes} {image} {_arguments}",
|
|
274
|
+
image=image, output_on_error=True)
|
|
298
275
|
|
|
299
276
|
self._info("Created container '{container}'")
|
|
300
277
|
info = tools.run("podman inspect {container}", output_on_error=True)
|
|
@@ -307,14 +284,18 @@ class Container(Resource):
|
|
|
307
284
|
owner.tools.runprefix(f"podman exec -i {artifact.container}"))
|
|
308
285
|
|
|
309
286
|
def release(self, artifact, deps, tools, owner):
|
|
310
|
-
if self.chroot:
|
|
287
|
+
if self.chroot and self._context_stack:
|
|
311
288
|
self._context_stack.close()
|
|
312
289
|
|
|
313
|
-
self.
|
|
314
|
-
|
|
290
|
+
if not self.container:
|
|
291
|
+
return
|
|
315
292
|
|
|
316
|
-
|
|
317
|
-
|
|
293
|
+
try:
|
|
294
|
+
self._info("Stopping container '{container}'")
|
|
295
|
+
tools.run("podman stop -t {stop_timeout} {container}", output_on_error=True)
|
|
296
|
+
finally:
|
|
297
|
+
self._info("Deleting container '{container}'")
|
|
298
|
+
tools.run("podman rm -f {container}", output_on_error=True)
|
|
318
299
|
|
|
319
300
|
|
|
320
301
|
class PodmanLogin(Resource):
|
|
@@ -333,8 +314,6 @@ class PodmanLogin(Resource):
|
|
|
333
314
|
name = "podman/login"
|
|
334
315
|
""" Name of the resource """
|
|
335
316
|
|
|
336
|
-
requires = ["podman/cli"]
|
|
337
|
-
|
|
338
317
|
user = Parameter("", help="Podman Registry username")
|
|
339
318
|
"""
|
|
340
319
|
Podman Registry username.
|
|
@@ -374,7 +353,6 @@ class PodmanLogin(Resource):
|
|
|
374
353
|
tools.run("podman logout {server}")
|
|
375
354
|
|
|
376
355
|
|
|
377
|
-
TaskRegistry.get().add_task_class(PodmanClient)
|
|
378
356
|
TaskRegistry.get().add_task_class(PodmanLogin)
|
|
379
357
|
|
|
380
358
|
|
|
@@ -434,7 +412,6 @@ class ContainerImage(Task):
|
|
|
434
412
|
|
|
435
413
|
Optionally add requirements to:
|
|
436
414
|
|
|
437
|
-
- ``podman/cli`` to provision the Podman client, if none is available on the host.
|
|
438
415
|
- ``podman/login`` to automatically login to the Podman registry.
|
|
439
416
|
|
|
440
417
|
This class must be subclassed.
|
|
@@ -457,12 +434,14 @@ class ContainerImage(Task):
|
|
|
457
434
|
class Busybox(ContainerImage):
|
|
458
435
|
\"\"\" Publishes Busybox image as gzip-compressed tarball \"\"\"
|
|
459
436
|
compression = "gz"
|
|
460
|
-
requires = ["podman/cli"]
|
|
461
437
|
tags = ["busybox:{identity}"]
|
|
462
438
|
|
|
463
439
|
"""
|
|
464
440
|
abstract = True
|
|
465
441
|
|
|
442
|
+
annotations = []
|
|
443
|
+
""" A list of image annotations """
|
|
444
|
+
|
|
466
445
|
autoload = True
|
|
467
446
|
"""
|
|
468
447
|
Automatically load image file into local registry when the artifact is
|
|
@@ -492,10 +471,26 @@ class ContainerImage(Task):
|
|
|
492
471
|
dockerfile = "Dockerfile"
|
|
493
472
|
""" Path to the Dockerfile to build, or the full source code of such a file. """
|
|
494
473
|
|
|
495
|
-
output = "oci-archive"
|
|
496
|
-
"""
|
|
474
|
+
output = ["oci-archive"]
|
|
475
|
+
"""
|
|
476
|
+
List of output formats.
|
|
477
|
+
|
|
478
|
+
If set to None, no output is produced and published.
|
|
479
|
+
|
|
480
|
+
Supported formats:
|
|
481
|
+
- archive
|
|
482
|
+
- cpio
|
|
483
|
+
- custom
|
|
484
|
+
- directory
|
|
485
|
+
- docker-archive
|
|
486
|
+
- ext4
|
|
487
|
+
- oci-archive
|
|
488
|
+
- oci-directory
|
|
489
|
+
- squashfs
|
|
497
490
|
|
|
498
|
-
|
|
491
|
+
"""
|
|
492
|
+
|
|
493
|
+
imagefile = "{canonical_name}"
|
|
499
494
|
"""
|
|
500
495
|
Name of the image tarball published by the task.
|
|
501
496
|
|
|
@@ -507,9 +502,6 @@ class ContainerImage(Task):
|
|
|
507
502
|
labels = []
|
|
508
503
|
""" A list of image metadata labels """
|
|
509
504
|
|
|
510
|
-
platform = None
|
|
511
|
-
""" Target platform, e.g. linux/arm/v7. """
|
|
512
|
-
|
|
513
505
|
pull = True
|
|
514
506
|
"""
|
|
515
507
|
Always pull images when building.
|
|
@@ -525,15 +517,25 @@ class ContainerImage(Task):
|
|
|
525
517
|
The ``podman/login`` Jolt resource can be used for that purpose.
|
|
526
518
|
"""
|
|
527
519
|
|
|
520
|
+
size = None
|
|
521
|
+
""" Size of the image, e.g. "64M" (for certain output formats). """
|
|
522
|
+
|
|
528
523
|
squash = False
|
|
529
524
|
""" Squash image layers """
|
|
530
525
|
|
|
531
526
|
tags = ["{canonical_name}:{identity}"]
|
|
532
527
|
""" Optional list of image tags. Defaults to task's canonical name. """
|
|
533
528
|
|
|
529
|
+
target = None
|
|
530
|
+
""" Target platform, e.g. linux/arm/v7. """
|
|
531
|
+
|
|
534
532
|
def __init__(self, *args, **kwargs):
|
|
535
533
|
super().__init__(*args, **kwargs)
|
|
536
534
|
|
|
535
|
+
@property
|
|
536
|
+
def _annotations(self):
|
|
537
|
+
return " ".join([utils.option("--annotation ", self.tools.expand(an)) for an in self.annotations])
|
|
538
|
+
|
|
537
539
|
@property
|
|
538
540
|
def _buildargs(self):
|
|
539
541
|
return " ".join([utils.option("--build-arg ", self.tools.expand(ba)) for ba in self.buildargs])
|
|
@@ -542,9 +544,13 @@ class ContainerImage(Task):
|
|
|
542
544
|
def _labels(self):
|
|
543
545
|
return " ".join([utils.option("-l ", self.tools.expand(label)) for label in self.labels])
|
|
544
546
|
|
|
547
|
+
@property
|
|
548
|
+
def _output(self):
|
|
549
|
+
return utils.as_list(self.output) if self.output else []
|
|
550
|
+
|
|
545
551
|
@property
|
|
546
552
|
def _platform(self):
|
|
547
|
-
platform = self.tools.expand(self.
|
|
553
|
+
platform = self.tools.expand(self.target) if self.target else None
|
|
548
554
|
return utils.option("--platform ", platform)
|
|
549
555
|
|
|
550
556
|
@property
|
|
@@ -575,7 +581,7 @@ class ContainerImage(Task):
|
|
|
575
581
|
tools.expand_relpath(context))
|
|
576
582
|
|
|
577
583
|
with tools.cwd(context):
|
|
578
|
-
tools.run("podman build {_platform} . -f {} {_buildargs} {_labels} {_tags} {pull}{squash}",
|
|
584
|
+
tools.run("podman build {_platform} . -f {} {_annotations} {_buildargs} {_labels} {_tags} {pull}{squash}",
|
|
579
585
|
utils.quote(dockerfile), pull=pull, squash=squash)
|
|
580
586
|
|
|
581
587
|
try:
|
|
@@ -584,22 +590,34 @@ class ContainerImage(Task):
|
|
|
584
590
|
for tag in self.tags:
|
|
585
591
|
tools.run("podman push {}", tag)
|
|
586
592
|
|
|
587
|
-
|
|
588
|
-
self.info("Saving image")
|
|
589
|
-
|
|
590
|
-
|
|
593
|
+
for output in self._output:
|
|
594
|
+
self.info("Saving image as {}", output)
|
|
595
|
+
outdir = tools.builddir(output)
|
|
596
|
+
with tools.cwd(outdir):
|
|
597
|
+
if output in ["oci-archive", "docker-archive"]:
|
|
591
598
|
tools.run("podman image save --format={output} {} -o {}", self.tags[0], "image.tar")
|
|
592
|
-
if
|
|
599
|
+
if output == "oci-directory":
|
|
593
600
|
tools.run("podman image save --format=oci-dir {} -o {}", self.tags[0], "image.dir")
|
|
594
|
-
if
|
|
601
|
+
if output in ["archive", "cpio", "custom", "directory", "ext4", "squashfs"]:
|
|
595
602
|
ctr = tools.run("podman create {}", self.tags[0])
|
|
596
603
|
try:
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
604
|
+
with tools.runprefix("podman unshare "):
|
|
605
|
+
mount_path = tools.run("podman mount {}", ctr, output_on_error=True)
|
|
606
|
+
if output == "custom":
|
|
607
|
+
self.run_custom(deps, tools, mount_path)
|
|
608
|
+
elif output == "archive":
|
|
609
|
+
tools.run("tar -C {} -cf image.tar .", mount_path, output_on_error=True)
|
|
610
|
+
elif output == "cpio":
|
|
611
|
+
with tools.cwd(mount_path):
|
|
612
|
+
tools.run("find | podman unshare cpio -o -F {}/image.cpio -H newc", outdir, output_on_error=True)
|
|
613
|
+
elif output == "ext4":
|
|
614
|
+
assert self.size, "Size must be set for ext4 output"
|
|
615
|
+
tools.run("mke2fs -t ext4 -F -L rootfs -d {} image.ext4 {size}", mount_path, output_on_error=True)
|
|
616
|
+
elif output == "squashfs":
|
|
617
|
+
tools.run("mksquashfs {} image.squashfs", mount_path, output_on_error=True)
|
|
618
|
+
else:
|
|
619
|
+
tools.mkdir("image.dir")
|
|
620
|
+
tools.run("tar c -C {} . | tar --no-same-permissions --no-same-owner --no-overwrite-dir -x -C ./image.dir/", mount_path, output_on_error=True)
|
|
603
621
|
finally:
|
|
604
622
|
utils.call_and_catch(tools.run, "podman rm {}", ctr)
|
|
605
623
|
finally:
|
|
@@ -608,26 +626,52 @@ class ContainerImage(Task):
|
|
|
608
626
|
for tag in self.tags:
|
|
609
627
|
utils.call_and_catch(tools.run("podman rmi -f {}", tag))
|
|
610
628
|
|
|
629
|
+
def run_custom(self, deps, tools, mount_path):
|
|
630
|
+
"""
|
|
631
|
+
Save image as custom output format.
|
|
632
|
+
|
|
633
|
+
The method is called when the output format is set to "custom".
|
|
634
|
+
The mount_path is the path to the mounted container root filesystem.
|
|
635
|
+
|
|
636
|
+
The default implementation does nothing.
|
|
637
|
+
"""
|
|
638
|
+
pass
|
|
639
|
+
|
|
611
640
|
def publish(self, artifact, tools):
|
|
641
|
+
""" Publish the image as different output formats """
|
|
642
|
+
|
|
612
643
|
artifact.strings.tag = tools.expand(self.tags[0])
|
|
613
644
|
|
|
614
645
|
for tag in self.tags:
|
|
615
646
|
artifact.podman.tags.append(tag)
|
|
616
647
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
648
|
+
for output in self._output:
|
|
649
|
+
outdir = tools.builddir(output)
|
|
650
|
+
with tools.cwd(outdir):
|
|
651
|
+
if output in ["oci-archive", "docker-archive"] and self._imagefile:
|
|
652
|
+
artifact.collect("image.tar", output + "/{_imagefile}.tar")
|
|
621
653
|
if self._autoload:
|
|
622
|
-
artifact.podman.load.append("{_imagefile}")
|
|
623
|
-
artifact.podman.rmi.append(artifact.strings.tag
|
|
624
|
-
if
|
|
625
|
-
artifact.collect("image.tar", "{_imagefile}")
|
|
654
|
+
artifact.podman.load.append(output + "/{_imagefile}.tar")
|
|
655
|
+
artifact.podman.rmi.append(artifact.strings.tag)
|
|
656
|
+
if output in ["archive"] and self._imagefile:
|
|
657
|
+
artifact.collect("image.tar", output + "/{_imagefile}.tar")
|
|
626
658
|
if self._autoload:
|
|
627
|
-
artifact.podman.imprt.append("{_imagefile}")
|
|
628
|
-
artifact.podman.rmi.append(artifact.strings.tag
|
|
629
|
-
if
|
|
659
|
+
artifact.podman.imprt.append(output + "/{_imagefile}.tar")
|
|
660
|
+
artifact.podman.rmi.append(artifact.strings.tag)
|
|
661
|
+
if output in ["directory", "oci-directory"]:
|
|
630
662
|
with tools.cwd("image.dir"):
|
|
631
|
-
artifact.collect("*", symlinks=True)
|
|
632
|
-
if
|
|
633
|
-
artifact.
|
|
663
|
+
artifact.collect("*", f"{output}/", symlinks=True)
|
|
664
|
+
if output in ["cpio"]:
|
|
665
|
+
artifact.collect("image.cpio", output + "/{_imagefile}.cpio")
|
|
666
|
+
if output in ["custom"]:
|
|
667
|
+
self.publish_custom(artifact, tools)
|
|
668
|
+
if output in ["directory"]:
|
|
669
|
+
artifact.paths.rootfs = output
|
|
670
|
+
if output in ["ext4"]:
|
|
671
|
+
artifact.collect("image.ext4", output + "/{_imagefile}.ext4")
|
|
672
|
+
if output in ["squashfs"]:
|
|
673
|
+
artifact.collect("image.squashfs", output + "/{_imagefile}.squashfs")
|
|
674
|
+
|
|
675
|
+
def publish_custom(self, artifact, tools):
|
|
676
|
+
""" Publish custom output as produced by run_custom """
|
|
677
|
+
pass
|