jolt 0.9.76__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 +88 -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 +839 -367
- jolt/chroot.py +156 -0
- jolt/cli.py +362 -143
- jolt/common_pb2.py +63 -0
- jolt/common_pb2_grpc.py +4 -0
- jolt/config.py +99 -42
- jolt/error.py +19 -4
- jolt/expires.py +2 -2
- jolt/filesystem.py +8 -6
- jolt/graph.py +705 -117
- jolt/hooks.py +63 -1
- jolt/influence.py +129 -6
- 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 +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/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 +91 -23
- jolt/plugins/email.py +5 -2
- jolt/plugins/email.xslt +144 -101
- jolt/plugins/environ.py +11 -0
- jolt/plugins/fetch.py +141 -0
- jolt/plugins/gdb.py +44 -21
- jolt/plugins/gerrit.py +1 -14
- jolt/plugins/git.py +316 -101
- jolt/plugins/googletest.py +522 -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 +107 -31
- jolt/plugins/ninja.py +929 -134
- jolt/plugins/paths.py +11 -1
- jolt/plugins/pkgconfig.py +219 -0
- jolt/plugins/podman.py +148 -91
- 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 +9 -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 +591 -191
- jolt/tasks.py +1783 -245
- jolt/templates/export.sh.template +12 -6
- jolt/templates/timeline.html.template +44 -47
- jolt/timer.py +22 -0
- jolt/tools.py +749 -302
- jolt/utils.py +245 -18
- jolt/version.py +1 -1
- jolt/version_utils.py +2 -2
- jolt/xmldom.py +12 -2
- {jolt-0.9.76.dist-info → jolt-0.9.429.dist-info}/METADATA +98 -38
- jolt-0.9.429.dist-info/RECORD +207 -0
- {jolt-0.9.76.dist-info → jolt-0.9.429.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/ninja-cache.py +0 -64
- jolt/plugins/ninjacli.py +0 -271
- jolt/plugins/repo.py +0 -253
- jolt-0.9.76.dist-info/RECORD +0 -79
- {jolt-0.9.76.dist-info → jolt-0.9.429.dist-info}/entry_points.txt +0 -0
- {jolt-0.9.76.dist-info → jolt-0.9.429.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
|