xref: /OK3568_Linux_fs/yocto/poky/meta/classes/toaster.bbclass (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#
2*4882a593Smuzhiyun# Toaster helper class
3*4882a593Smuzhiyun#
4*4882a593Smuzhiyun# Copyright (C) 2013 Intel Corporation
5*4882a593Smuzhiyun#
6*4882a593Smuzhiyun# Released under the MIT license (see COPYING.MIT)
7*4882a593Smuzhiyun#
8*4882a593Smuzhiyun# This bbclass is designed to extract data used by OE-Core during the build process,
9*4882a593Smuzhiyun# for recording in the Toaster system.
10*4882a593Smuzhiyun# The data access is synchronous, preserving the build data integrity across
11*4882a593Smuzhiyun# different builds.
12*4882a593Smuzhiyun#
13*4882a593Smuzhiyun# The data is transferred through the event system, using the MetadataEvent objects.
14*4882a593Smuzhiyun#
15*4882a593Smuzhiyun# The model is to enable the datadump functions as postfuncs, and have the dump
16*4882a593Smuzhiyun# executed after the real taskfunc has been executed. This prevents task signature changing
17*4882a593Smuzhiyun# is toaster is enabled or not. Build performance is not affected if Toaster is not enabled.
18*4882a593Smuzhiyun#
19*4882a593Smuzhiyun# To enable, use INHERIT in local.conf:
20*4882a593Smuzhiyun#
21*4882a593Smuzhiyun#       INHERIT += "toaster"
22*4882a593Smuzhiyun#
23*4882a593Smuzhiyun#
24*4882a593Smuzhiyun#
25*4882a593Smuzhiyun#
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun# Find and dump layer info when we got the layers parsed
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun
31*4882a593Smuzhiyunpython toaster_layerinfo_dumpdata() {
32*4882a593Smuzhiyun    import subprocess
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun    def _get_git_branch(layer_path):
35*4882a593Smuzhiyun        branch = subprocess.Popen("git symbolic-ref HEAD 2>/dev/null ", cwd=layer_path, shell=True, stdout=subprocess.PIPE).communicate()[0]
36*4882a593Smuzhiyun        branch = branch.decode('utf-8')
37*4882a593Smuzhiyun        branch = branch.replace('refs/heads/', '').rstrip()
38*4882a593Smuzhiyun        return branch
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun    def _get_git_revision(layer_path):
41*4882a593Smuzhiyun        revision = subprocess.Popen("git rev-parse HEAD 2>/dev/null ", cwd=layer_path, shell=True, stdout=subprocess.PIPE).communicate()[0].rstrip()
42*4882a593Smuzhiyun        return revision
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun    def _get_url_map_name(layer_name):
45*4882a593Smuzhiyun        """ Some layers have a different name on openembedded.org site,
46*4882a593Smuzhiyun            this method returns the correct name to use in the URL
47*4882a593Smuzhiyun        """
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun        url_name = layer_name
50*4882a593Smuzhiyun        url_mapping = {'meta': 'openembedded-core'}
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun        for key in url_mapping.keys():
53*4882a593Smuzhiyun            if key == layer_name:
54*4882a593Smuzhiyun                url_name = url_mapping[key]
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun        return url_name
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun    def _get_layer_version_information(layer_path):
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun        layer_version_info = {}
61*4882a593Smuzhiyun        layer_version_info['branch'] = _get_git_branch(layer_path)
62*4882a593Smuzhiyun        layer_version_info['commit'] = _get_git_revision(layer_path)
63*4882a593Smuzhiyun        layer_version_info['priority'] = 0
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun        return layer_version_info
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun    def _get_layer_dict(layer_path):
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun        layer_info = {}
71*4882a593Smuzhiyun        layer_name = layer_path.split('/')[-1]
72*4882a593Smuzhiyun        layer_url = 'http://layers.openembedded.org/layerindex/layer/{layer}/'
73*4882a593Smuzhiyun        layer_url_name = _get_url_map_name(layer_name)
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun        layer_info['name'] = layer_url_name
76*4882a593Smuzhiyun        layer_info['local_path'] = layer_path
77*4882a593Smuzhiyun        layer_info['layer_index_url'] = layer_url.format(layer=layer_url_name)
78*4882a593Smuzhiyun        layer_info['version'] = _get_layer_version_information(layer_path)
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun        return layer_info
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun    bblayers = e.data.getVar("BBLAYERS")
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun    llayerinfo = {}
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun    for layer in { l for l in bblayers.strip().split(" ") if len(l) }:
88*4882a593Smuzhiyun        llayerinfo[layer] = _get_layer_dict(layer)
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun    bb.event.fire(bb.event.MetadataEvent("LayerInfo", llayerinfo), e.data)
92*4882a593Smuzhiyun}
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun# Dump package file info data
95*4882a593Smuzhiyun
96*4882a593Smuzhiyundef _toaster_load_pkgdatafile(dirpath, filepath):
97*4882a593Smuzhiyun    import json
98*4882a593Smuzhiyun    import re
99*4882a593Smuzhiyun    pkgdata = {}
100*4882a593Smuzhiyun    with open(os.path.join(dirpath, filepath), "r") as fin:
101*4882a593Smuzhiyun        for line in fin:
102*4882a593Smuzhiyun            try:
103*4882a593Smuzhiyun                kn, kv = line.strip().split(": ", 1)
104*4882a593Smuzhiyun                m = re.match(r"^PKG:([^A-Z:]*)", kn)
105*4882a593Smuzhiyun                if m:
106*4882a593Smuzhiyun                    pkgdata['OPKGN'] = m.group(1)
107*4882a593Smuzhiyun                kn = kn.split(":")[0]
108*4882a593Smuzhiyun                pkgdata[kn] = kv
109*4882a593Smuzhiyun                if kn.startswith('FILES_INFO'):
110*4882a593Smuzhiyun                    pkgdata[kn] = json.loads(kv)
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun            except ValueError:
113*4882a593Smuzhiyun                pass    # ignore lines without valid key: value pairs
114*4882a593Smuzhiyun    return pkgdata
115*4882a593Smuzhiyun
116*4882a593Smuzhiyundef _toaster_dumpdata(pkgdatadir, d):
117*4882a593Smuzhiyun    """
118*4882a593Smuzhiyun    Dumps the data about the packages created by a recipe
119*4882a593Smuzhiyun    """
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun    # No need to try and dumpdata if the recipe isn't generating packages
122*4882a593Smuzhiyun    if not d.getVar('PACKAGES'):
123*4882a593Smuzhiyun        return
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun    lpkgdata = {}
126*4882a593Smuzhiyun    datadir = os.path.join(pkgdatadir, 'runtime')
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun    # scan and send data for each generated package
129*4882a593Smuzhiyun    if os.path.exists(datadir):
130*4882a593Smuzhiyun        for datafile in os.listdir(datadir):
131*4882a593Smuzhiyun            if not datafile.endswith('.packaged'):
132*4882a593Smuzhiyun                lpkgdata = _toaster_load_pkgdatafile(datadir, datafile)
133*4882a593Smuzhiyun                # Fire an event containing the pkg data
134*4882a593Smuzhiyun                bb.event.fire(bb.event.MetadataEvent("SinglePackageInfo", lpkgdata), d)
135*4882a593Smuzhiyun
136*4882a593Smuzhiyunpython toaster_package_dumpdata() {
137*4882a593Smuzhiyun    _toaster_dumpdata(d.getVar('PKGDESTWORK'), d)
138*4882a593Smuzhiyun}
139*4882a593Smuzhiyun
140*4882a593Smuzhiyunpython toaster_packagedata_dumpdata() {
141*4882a593Smuzhiyun    # This path needs to match do_packagedata[sstate-inputdirs]
142*4882a593Smuzhiyun    _toaster_dumpdata(os.path.join(d.getVar('WORKDIR'), 'pkgdata-pdata-input'), d)
143*4882a593Smuzhiyun}
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun# 2. Dump output image files information
146*4882a593Smuzhiyun
147*4882a593Smuzhiyunpython toaster_artifact_dumpdata() {
148*4882a593Smuzhiyun    """
149*4882a593Smuzhiyun    Dump data about SDK variables
150*4882a593Smuzhiyun    """
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun    event_data = {
153*4882a593Smuzhiyun      "TOOLCHAIN_OUTPUTNAME": d.getVar("TOOLCHAIN_OUTPUTNAME")
154*4882a593Smuzhiyun    }
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun    bb.event.fire(bb.event.MetadataEvent("SDKArtifactInfo", event_data), d)
157*4882a593Smuzhiyun}
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun# collect list of buildstats files based on fired events; when the build completes, collect all stats and fire an event with collected data
160*4882a593Smuzhiyun
161*4882a593Smuzhiyunpython toaster_collect_task_stats() {
162*4882a593Smuzhiyun    import bb.build
163*4882a593Smuzhiyun    import bb.event
164*4882a593Smuzhiyun    import bb.data
165*4882a593Smuzhiyun    import bb.utils
166*4882a593Smuzhiyun    import os
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun    if not e.data.getVar('BUILDSTATS_BASE'):
169*4882a593Smuzhiyun        return  # if we don't have buildstats, we cannot collect stats
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun    toaster_statlist_file = os.path.join(e.data.getVar('BUILDSTATS_BASE'), "toasterstatlist")
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun    def stat_to_float(value):
174*4882a593Smuzhiyun        return float(value.strip('% \n\r'))
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun    def _append_read_list(v):
177*4882a593Smuzhiyun        lock = bb.utils.lockfile(e.data.expand("${TOPDIR}/toaster.lock"), False, True)
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun        with open(toaster_statlist_file, "a") as fout:
180*4882a593Smuzhiyun            taskdir = e.data.expand("${BUILDSTATS_BASE}/${BUILDNAME}/${PF}")
181*4882a593Smuzhiyun            fout.write("%s::%s::%s::%s\n" % (e.taskfile, e.taskname, os.path.join(taskdir, e.task), e.data.expand("${PN}")))
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun        bb.utils.unlockfile(lock)
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun    def _read_stats(filename):
186*4882a593Smuzhiyun        # seconds
187*4882a593Smuzhiyun        cpu_time_user = 0
188*4882a593Smuzhiyun        cpu_time_system = 0
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun        # bytes
191*4882a593Smuzhiyun        disk_io_read = 0
192*4882a593Smuzhiyun        disk_io_write = 0
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun        started = 0
195*4882a593Smuzhiyun        ended = 0
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun        taskname = ''
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun        statinfo = {}
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun        with open(filename, 'r') as task_bs:
202*4882a593Smuzhiyun            for line in task_bs.readlines():
203*4882a593Smuzhiyun                k,v = line.strip().split(": ", 1)
204*4882a593Smuzhiyun                statinfo[k] = v
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun        if "Started" in statinfo:
207*4882a593Smuzhiyun            started = stat_to_float(statinfo["Started"])
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun        if "Ended" in statinfo:
210*4882a593Smuzhiyun            ended = stat_to_float(statinfo["Ended"])
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun        if "Child rusage ru_utime" in statinfo:
213*4882a593Smuzhiyun            cpu_time_user = cpu_time_user + stat_to_float(statinfo["Child rusage ru_utime"])
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun        if "Child rusage ru_stime" in statinfo:
216*4882a593Smuzhiyun            cpu_time_system = cpu_time_system + stat_to_float(statinfo["Child rusage ru_stime"])
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun        if "IO write_bytes" in statinfo:
219*4882a593Smuzhiyun            write_bytes = int(statinfo["IO write_bytes"].strip('% \n\r'))
220*4882a593Smuzhiyun            disk_io_write = disk_io_write + write_bytes
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun        if "IO read_bytes" in statinfo:
223*4882a593Smuzhiyun            read_bytes = int(statinfo["IO read_bytes"].strip('% \n\r'))
224*4882a593Smuzhiyun            disk_io_read = disk_io_read + read_bytes
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun        return {
227*4882a593Smuzhiyun            'stat_file': filename,
228*4882a593Smuzhiyun            'cpu_time_user': cpu_time_user,
229*4882a593Smuzhiyun            'cpu_time_system': cpu_time_system,
230*4882a593Smuzhiyun            'disk_io_read': disk_io_read,
231*4882a593Smuzhiyun            'disk_io_write': disk_io_write,
232*4882a593Smuzhiyun            'started': started,
233*4882a593Smuzhiyun            'ended': ended
234*4882a593Smuzhiyun        }
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun    if isinstance(e, (bb.build.TaskSucceeded, bb.build.TaskFailed)):
237*4882a593Smuzhiyun        _append_read_list(e)
238*4882a593Smuzhiyun        pass
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun    if isinstance(e, bb.event.BuildCompleted) and os.path.exists(toaster_statlist_file):
241*4882a593Smuzhiyun        events = []
242*4882a593Smuzhiyun        with open(toaster_statlist_file, "r") as fin:
243*4882a593Smuzhiyun            for line in fin:
244*4882a593Smuzhiyun                (taskfile, taskname, filename, recipename) = line.strip().split("::")
245*4882a593Smuzhiyun                stats = _read_stats(filename)
246*4882a593Smuzhiyun                events.append((taskfile, taskname, stats, recipename))
247*4882a593Smuzhiyun        bb.event.fire(bb.event.MetadataEvent("BuildStatsList", events), e.data)
248*4882a593Smuzhiyun        os.unlink(toaster_statlist_file)
249*4882a593Smuzhiyun}
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun# dump relevant build history data as an event when the build is completed
252*4882a593Smuzhiyun
253*4882a593Smuzhiyunpython toaster_buildhistory_dump() {
254*4882a593Smuzhiyun    import re
255*4882a593Smuzhiyun    BUILDHISTORY_DIR = e.data.expand("${TOPDIR}/buildhistory")
256*4882a593Smuzhiyun    BUILDHISTORY_DIR_IMAGE_BASE = e.data.expand("%s/images/${MACHINE_ARCH}/${TCLIBC}/"% BUILDHISTORY_DIR)
257*4882a593Smuzhiyun    pkgdata_dir = e.data.getVar("PKGDATA_DIR")
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun    # scan the build targets for this build
261*4882a593Smuzhiyun    images = {}
262*4882a593Smuzhiyun    allpkgs = {}
263*4882a593Smuzhiyun    files = {}
264*4882a593Smuzhiyun    for target in e._pkgs:
265*4882a593Smuzhiyun        target = target.split(':')[0] # strip ':<task>' suffix from the target
266*4882a593Smuzhiyun        installed_img_path = e.data.expand(os.path.join(BUILDHISTORY_DIR_IMAGE_BASE, target))
267*4882a593Smuzhiyun        if os.path.exists(installed_img_path):
268*4882a593Smuzhiyun            images[target] = {}
269*4882a593Smuzhiyun            files[target] = {}
270*4882a593Smuzhiyun            files[target]['dirs'] = []
271*4882a593Smuzhiyun            files[target]['syms'] = []
272*4882a593Smuzhiyun            files[target]['files'] = []
273*4882a593Smuzhiyun            with open("%s/installed-package-sizes.txt" % installed_img_path, "r") as fin:
274*4882a593Smuzhiyun                for line in fin:
275*4882a593Smuzhiyun                    line = line.rstrip(";")
276*4882a593Smuzhiyun                    psize, punit, pname = line.split()
277*4882a593Smuzhiyun                    # this size is "installed-size" as it measures how much space it takes on disk
278*4882a593Smuzhiyun                    images[target][pname.strip()] = {'size':int(psize)*1024, 'depends' : []}
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun            with open("%s/depends.dot" % installed_img_path, "r") as fin:
281*4882a593Smuzhiyun                p = re.compile(r'\s*"(?P<name>[^"]+)"\s*->\s*"(?P<dep>[^"]+)"(?P<rec>.*?\[style=dotted\])?')
282*4882a593Smuzhiyun                for line in fin:
283*4882a593Smuzhiyun                    m = p.match(line)
284*4882a593Smuzhiyun                    if not m:
285*4882a593Smuzhiyun                        continue
286*4882a593Smuzhiyun                    pname = m.group('name')
287*4882a593Smuzhiyun                    dependsname = m.group('dep')
288*4882a593Smuzhiyun                    deptype = 'recommends' if m.group('rec') else 'depends'
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun                    # If RPM is used for packaging, then there may be
291*4882a593Smuzhiyun                    # dependencies such as "/bin/sh", which will confuse
292*4882a593Smuzhiyun                    # _toaster_load_pkgdatafile() later on. While at it, ignore
293*4882a593Smuzhiyun                    # any dependencies that contain parentheses, e.g.,
294*4882a593Smuzhiyun                    # "libc.so.6(GLIBC_2.7)".
295*4882a593Smuzhiyun                    if dependsname.startswith('/') or '(' in dependsname:
296*4882a593Smuzhiyun                        continue
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun                    if not pname in images[target]:
299*4882a593Smuzhiyun                        images[target][pname] = {'size': 0, 'depends' : []}
300*4882a593Smuzhiyun                    if not dependsname in images[target]:
301*4882a593Smuzhiyun                        images[target][dependsname] = {'size': 0, 'depends' : []}
302*4882a593Smuzhiyun                    images[target][pname]['depends'].append((dependsname, deptype))
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun            # files-in-image.txt is only generated if an image file is created,
305*4882a593Smuzhiyun            # so the file entries ('syms', 'dirs', 'files') for a target will be
306*4882a593Smuzhiyun            # empty for rootfs builds and other "image" tasks which don't
307*4882a593Smuzhiyun            # produce image files
308*4882a593Smuzhiyun            # (e.g. "bitbake core-image-minimal -c populate_sdk")
309*4882a593Smuzhiyun            files_in_image_path = "%s/files-in-image.txt" % installed_img_path
310*4882a593Smuzhiyun            if os.path.exists(files_in_image_path):
311*4882a593Smuzhiyun                with open(files_in_image_path, "r") as fin:
312*4882a593Smuzhiyun                    for line in fin:
313*4882a593Smuzhiyun                        lc = [ x for x in line.strip().split(" ") if len(x) > 0 ]
314*4882a593Smuzhiyun                        if lc[0].startswith("l"):
315*4882a593Smuzhiyun                            files[target]['syms'].append(lc)
316*4882a593Smuzhiyun                        elif lc[0].startswith("d"):
317*4882a593Smuzhiyun                            files[target]['dirs'].append(lc)
318*4882a593Smuzhiyun                        else:
319*4882a593Smuzhiyun                            files[target]['files'].append(lc)
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun            for pname in images[target]:
322*4882a593Smuzhiyun                if not pname in allpkgs:
323*4882a593Smuzhiyun                    try:
324*4882a593Smuzhiyun                        pkgdata = _toaster_load_pkgdatafile("%s/runtime-reverse/" % pkgdata_dir, pname)
325*4882a593Smuzhiyun                    except IOError as err:
326*4882a593Smuzhiyun                        if err.errno == 2:
327*4882a593Smuzhiyun                            # We expect this e.g. for RRECOMMENDS that are unsatisfied at runtime
328*4882a593Smuzhiyun                            continue
329*4882a593Smuzhiyun                        else:
330*4882a593Smuzhiyun                            raise
331*4882a593Smuzhiyun                    allpkgs[pname] = pkgdata
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun    data = { 'pkgdata' : allpkgs, 'imgdata' : images, 'filedata' : files }
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun    bb.event.fire(bb.event.MetadataEvent("ImagePkgList", data), e.data)
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun}
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun# get list of artifacts from sstate manifest
341*4882a593Smuzhiyunpython toaster_artifacts() {
342*4882a593Smuzhiyun    if e.taskname in ["do_deploy", "do_image_complete", "do_populate_sdk", "do_populate_sdk_ext"]:
343*4882a593Smuzhiyun        d2 = d.createCopy()
344*4882a593Smuzhiyun        d2.setVar('FILE', e.taskfile)
345*4882a593Smuzhiyun        # Use 'stamp-extra-info' if present, else use workaround
346*4882a593Smuzhiyun        # to determine 'SSTATE_MANMACH'
347*4882a593Smuzhiyun        extrainf = d2.getVarFlag(e.taskname, 'stamp-extra-info')
348*4882a593Smuzhiyun        if extrainf:
349*4882a593Smuzhiyun            d2.setVar('SSTATE_MANMACH', extrainf)
350*4882a593Smuzhiyun        else:
351*4882a593Smuzhiyun            if "do_populate_sdk" == e.taskname:
352*4882a593Smuzhiyun                d2.setVar('SSTATE_MANMACH', d2.expand("${MACHINE}${SDKMACHINE}"))
353*4882a593Smuzhiyun            else:
354*4882a593Smuzhiyun                d2.setVar('SSTATE_MANMACH', d2.expand("${MACHINE}"))
355*4882a593Smuzhiyun        manifest = oe.sstatesig.sstate_get_manifest_filename(e.taskname[3:], d2)[0]
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun        if os.access(manifest, os.R_OK):
358*4882a593Smuzhiyun            with open(manifest) as fmanifest:
359*4882a593Smuzhiyun                artifacts = [fname.strip() for fname in fmanifest]
360*4882a593Smuzhiyun                data = {"task": e.taskid, "artifacts": artifacts}
361*4882a593Smuzhiyun                bb.event.fire(bb.event.MetadataEvent("TaskArtifacts", data), d2)
362*4882a593Smuzhiyun}
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun# set event handlers
365*4882a593Smuzhiyunaddhandler toaster_layerinfo_dumpdata
366*4882a593Smuzhiyuntoaster_layerinfo_dumpdata[eventmask] = "bb.event.TreeDataPreparationCompleted"
367*4882a593Smuzhiyun
368*4882a593Smuzhiyunaddhandler toaster_collect_task_stats
369*4882a593Smuzhiyuntoaster_collect_task_stats[eventmask] = "bb.event.BuildCompleted bb.build.TaskSucceeded bb.build.TaskFailed"
370*4882a593Smuzhiyun
371*4882a593Smuzhiyunaddhandler toaster_buildhistory_dump
372*4882a593Smuzhiyuntoaster_buildhistory_dump[eventmask] = "bb.event.BuildCompleted"
373*4882a593Smuzhiyun
374*4882a593Smuzhiyunaddhandler toaster_artifacts
375*4882a593Smuzhiyuntoaster_artifacts[eventmask] = "bb.runqueue.runQueueTaskSkipped bb.runqueue.runQueueTaskCompleted"
376*4882a593Smuzhiyun
377*4882a593Smuzhiyundo_packagedata_setscene[postfuncs] += "toaster_packagedata_dumpdata "
378*4882a593Smuzhiyundo_packagedata_setscene[vardepsexclude] += "toaster_packagedata_dumpdata "
379*4882a593Smuzhiyun
380*4882a593Smuzhiyundo_package[postfuncs] += "toaster_package_dumpdata "
381*4882a593Smuzhiyundo_package[vardepsexclude] += "toaster_package_dumpdata "
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun#do_populate_sdk[postfuncs] += "toaster_artifact_dumpdata "
384*4882a593Smuzhiyun#do_populate_sdk[vardepsexclude] += "toaster_artifact_dumpdata "
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun#do_populate_sdk_ext[postfuncs] += "toaster_artifact_dumpdata "
387*4882a593Smuzhiyun#do_populate_sdk_ext[vardepsexclude] += "toaster_artifact_dumpdata "
388*4882a593Smuzhiyun
389