blob: 0761f73b3ba99a295126a0a7164defdda9cda509 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#
2# BitBake ToasterUI Implementation
3#
4# Copyright (C) 2013 Intel Corporation
5#
Brad Bishopc342db32019-05-15 21:57:59 -04006# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -05007#
Patrick Williamsc124f4f2015-09-15 14:41:29 -05008
9import sys
10import bb
11import re
12import os
13
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050014import django
Patrick Williamsc124f4f2015-09-15 14:41:29 -050015from django.utils import timezone
16
Patrick Williamsc0f7c042017-02-23 20:41:17 -060017import toaster
18# Add toaster module to the search path to help django.setup() find the right
19# modules
20sys.path.insert(0, os.path.dirname(toaster.__file__))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021
Patrick Williamsc0f7c042017-02-23 20:41:17 -060022#Set the DJANGO_SETTINGS_MODULE if it's not already set
23os.environ["DJANGO_SETTINGS_MODULE"] =\
24 os.environ.get("DJANGO_SETTINGS_MODULE",
25 "toaster.toastermain.settings")
26# Setup django framework (needs to be done before importing modules)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050027django.setup()
Patrick Williamsc124f4f2015-09-15 14:41:29 -050028
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050029from orm.models import Build, Task, Recipe, Layer_Version, Layer, Target, LogMessage, HelpText
Patrick Williamsc0f7c042017-02-23 20:41:17 -060030from orm.models import Target_Image_File, TargetKernelFile, TargetSDKFile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050031from orm.models import Variable, VariableHistory
32from orm.models import Package, Package_File, Target_Installed_Package, Target_File
33from orm.models import Task_Dependency, Package_Dependency
34from orm.models import Recipe_Dependency, Provides
Brad Bishop6e60e8b2018-02-01 10:27:11 -050035from orm.models import Project, CustomImagePackage
Patrick Williamsc0f7c042017-02-23 20:41:17 -060036from orm.models import signal_runbuilds
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050037
Patrick Williamsc124f4f2015-09-15 14:41:29 -050038from bldcontrol.models import BuildEnvironment, BuildRequest
Brad Bishop6e60e8b2018-02-01 10:27:11 -050039from bldcontrol.models import BRLayer
40from bldcontrol import bbcontroller
Patrick Williamsc124f4f2015-09-15 14:41:29 -050041
42from bb.msg import BBLogFormatter as formatter
43from django.db import models
44from pprint import pformat
45import logging
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050046from datetime import datetime, timedelta
Patrick Williamsc124f4f2015-09-15 14:41:29 -050047
48from django.db import transaction, connection
49
Patrick Williamsc0f7c042017-02-23 20:41:17 -060050
Patrick Williamsc124f4f2015-09-15 14:41:29 -050051# pylint: disable=invalid-name
52# the logger name is standard throughout BitBake
53logger = logging.getLogger("ToasterLogger")
54
Patrick Williamsc124f4f2015-09-15 14:41:29 -050055class NotExisting(Exception):
56 pass
57
58class ORMWrapper(object):
59 """ This class creates the dictionaries needed to store information in the database
60 following the format defined by the Django models. It is also used to save this
61 information in the database.
62 """
63
64 def __init__(self):
65 self.layer_version_objects = []
Patrick Williamsf1e5d692016-03-30 15:21:19 -050066 self.layer_version_built = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -050067 self.task_objects = {}
68 self.recipe_objects = {}
69
70 @staticmethod
71 def _build_key(**kwargs):
72 key = "0"
73 for k in sorted(kwargs.keys()):
74 if isinstance(kwargs[k], models.Model):
75 key += "-%d" % kwargs[k].id
76 else:
77 key += "-%s" % str(kwargs[k])
78 return key
79
80
81 def _cached_get_or_create(self, clazz, **kwargs):
82 """ This is a memory-cached get_or_create. We assume that the objects will not be created in the
83 database through any other means.
84 """
85
86 assert issubclass(clazz, models.Model), "_cached_get_or_create needs to get the class as first argument"
87
88 key = ORMWrapper._build_key(**kwargs)
89 dictname = "objects_%s" % clazz.__name__
90 if not dictname in vars(self).keys():
91 vars(self)[dictname] = {}
92
93 created = False
94 if not key in vars(self)[dictname].keys():
Patrick Williamsf1e5d692016-03-30 15:21:19 -050095 vars(self)[dictname][key], created = \
96 clazz.objects.get_or_create(**kwargs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050097
98 return (vars(self)[dictname][key], created)
99
100
101 def _cached_get(self, clazz, **kwargs):
102 """ This is a memory-cached get. We assume that the objects will not change in the database between gets.
103 """
104 assert issubclass(clazz, models.Model), "_cached_get needs to get the class as first argument"
105
106 key = ORMWrapper._build_key(**kwargs)
107 dictname = "objects_%s" % clazz.__name__
108
109 if not dictname in vars(self).keys():
110 vars(self)[dictname] = {}
111
112 if not key in vars(self)[dictname].keys():
113 vars(self)[dictname][key] = clazz.objects.get(**kwargs)
114
115 return vars(self)[dictname][key]
116
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600117 def get_similar_target_with_image_files(self, target):
118 """
119 Get a Target object "similar" to target; i.e. with the same target
120 name ('core-image-minimal' etc.) and machine.
121 """
122 return target.get_similar_target_with_image_files()
123
124 def get_similar_target_with_sdk_files(self, target):
125 return target.get_similar_target_with_sdk_files()
126
127 def clone_image_artifacts(self, target_from, target_to):
128 target_to.clone_image_artifacts_from(target_from)
129
130 def clone_sdk_artifacts(self, target_from, target_to):
131 target_to.clone_sdk_artifacts_from(target_from)
132
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500133 def _timestamp_to_datetime(self, secs):
134 """
135 Convert timestamp in seconds to Python datetime
136 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600137 return timezone.make_aware(datetime(1970, 1, 1) + timedelta(seconds=secs))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500138
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500139 # pylint: disable=no-self-use
140 # we disable detection of no self use in functions because the methods actually work on the object
141 # even if they don't touch self anywhere
142
143 # pylint: disable=bad-continuation
144 # we do not follow the python conventions for continuation indentation due to long lines here
145
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600146 def get_or_create_build_object(self, brbe):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500147 prj = None
148 buildrequest = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600149 if brbe is not None:
150 # Toaster-triggered build
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600151 logger.debug("buildinfohelper: brbe is %s" % brbe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500152 br, _ = brbe.split(":")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600153 buildrequest = BuildRequest.objects.get(pk=br)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500154 prj = buildrequest.project
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600155 else:
156 # CLI build
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500157 prj = Project.objects.get_or_create_default_project()
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600158 logger.debug("buildinfohelper: project is not specified, defaulting to %s" % prj)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500159
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500160 if buildrequest is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600161 # reuse existing Build object
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500162 build = buildrequest.build
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500163 build.project = prj
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500164 build.save()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500165 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600166 # create new Build object
167 now = timezone.now()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500168 build = Build.objects.create(
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600169 project=prj,
170 started_on=now,
171 completed_on=now,
172 build_name='')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500173
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600174 logger.debug("buildinfohelper: build is created %s" % build)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500175
176 if buildrequest is not None:
177 buildrequest.build = build
178 buildrequest.save()
179
180 return build
181
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600182 def update_build(self, build, data_dict):
183 for key in data_dict:
184 setattr(build, key, data_dict[key])
185 build.save()
186
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500187 @staticmethod
188 def get_or_create_targets(target_info):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600189 """
190 NB get_or_create() is used here because for Toaster-triggered builds,
191 we already created the targets when the build was triggered.
192 """
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500193 result = []
194 for target in target_info['targets']:
195 task = ''
196 if ':' in target:
197 target, task = target.split(':', 1)
198 if task.startswith('do_'):
199 task = task[3:]
200 if task == 'build':
201 task = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600202
203 obj, _ = Target.objects.get_or_create(build=target_info['build'],
204 target=target,
205 task=task)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500206 result.append(obj)
207 return result
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500208
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600209 def update_build_stats_and_outcome(self, build, errors, warnings, taskfailures):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500210 assert isinstance(build,Build)
211 assert isinstance(errors, int)
212 assert isinstance(warnings, int)
213
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500214 if build.outcome == Build.CANCELLED:
215 return
216 try:
217 if build.buildrequest.state == BuildRequest.REQ_CANCELLING:
218 return
219 except AttributeError:
220 # We may not have a buildrequest if this is a command line build
221 pass
222
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500223 outcome = Build.SUCCEEDED
224 if errors or taskfailures:
225 outcome = Build.FAILED
226
227 build.completed_on = timezone.now()
228 build.outcome = outcome
229 build.save()
Andrew Geissler9aee5002022-03-30 16:27:02 +0000230
231 # We force a sync point here to force the outcome status commit,
232 # which resolves a race condition with the build completion takedown
233 transaction.set_autocommit(True)
234 transaction.set_autocommit(False)
235
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600236 signal_runbuilds()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500237
238 def update_target_set_license_manifest(self, target, license_manifest_path):
239 target.license_manifest_path = license_manifest_path
240 target.save()
241
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600242 def update_target_set_package_manifest(self, target, package_manifest_path):
243 target.package_manifest_path = package_manifest_path
244 target.save()
245
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500246 def update_task_object(self, build, task_name, recipe_name, task_stats):
247 """
248 Find the task for build which matches the recipe and task name
249 to be stored
250 """
251 task_to_update = Task.objects.get(
252 build = build,
253 task_name = task_name,
254 recipe__name = recipe_name
255 )
256
257 if 'started' in task_stats and 'ended' in task_stats:
258 task_to_update.started = self._timestamp_to_datetime(task_stats['started'])
259 task_to_update.ended = self._timestamp_to_datetime(task_stats['ended'])
260 task_to_update.elapsed_time = (task_stats['ended'] - task_stats['started'])
261 task_to_update.cpu_time_user = task_stats.get('cpu_time_user')
262 task_to_update.cpu_time_system = task_stats.get('cpu_time_system')
263 if 'disk_io_read' in task_stats and 'disk_io_write' in task_stats:
264 task_to_update.disk_io_read = task_stats['disk_io_read']
265 task_to_update.disk_io_write = task_stats['disk_io_write']
266 task_to_update.disk_io = task_stats['disk_io_read'] + task_stats['disk_io_write']
267
268 task_to_update.save()
269
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500270 def get_update_task_object(self, task_information, must_exist = False):
271 assert 'build' in task_information
272 assert 'recipe' in task_information
273 assert 'task_name' in task_information
274
275 # we use must_exist info for database look-up optimization
276 task_object, created = self._cached_get_or_create(Task,
277 build=task_information['build'],
278 recipe=task_information['recipe'],
279 task_name=task_information['task_name']
280 )
281 if created and must_exist:
282 task_information['debug'] = "build id %d, recipe id %d" % (task_information['build'].pk, task_information['recipe'].pk)
283 raise NotExisting("Task object created when expected to exist", task_information)
284
285 object_changed = False
286 for v in vars(task_object):
287 if v in task_information.keys():
288 if vars(task_object)[v] != task_information[v]:
289 vars(task_object)[v] = task_information[v]
290 object_changed = True
291
292 # update setscene-related information if the task has a setscene
293 if task_object.outcome == Task.OUTCOME_COVERED and 1 == task_object.get_related_setscene().count():
294 task_object.outcome = Task.OUTCOME_CACHED
295 object_changed = True
296
297 outcome_task_setscene = Task.objects.get(task_executed=True, build = task_object.build,
298 recipe = task_object.recipe, task_name=task_object.task_name+"_setscene").outcome
299 if outcome_task_setscene == Task.OUTCOME_SUCCESS:
300 task_object.sstate_result = Task.SSTATE_RESTORED
301 object_changed = True
302 elif outcome_task_setscene == Task.OUTCOME_FAILED:
303 task_object.sstate_result = Task.SSTATE_FAILED
304 object_changed = True
305
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500306 if object_changed:
307 task_object.save()
308 return task_object
309
310
311 def get_update_recipe_object(self, recipe_information, must_exist = False):
312 assert 'layer_version' in recipe_information
313 assert 'file_path' in recipe_information
314 assert 'pathflags' in recipe_information
315
316 assert not recipe_information['file_path'].startswith("/") # we should have layer-relative paths at all times
317
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500318
319 def update_recipe_obj(recipe_object):
320 object_changed = False
321 for v in vars(recipe_object):
322 if v in recipe_information.keys():
323 object_changed = True
324 vars(recipe_object)[v] = recipe_information[v]
325
326 if object_changed:
327 recipe_object.save()
328
329 recipe, created = self._cached_get_or_create(Recipe, layer_version=recipe_information['layer_version'],
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500330 file_path=recipe_information['file_path'], pathflags = recipe_information['pathflags'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500331
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500332 update_recipe_obj(recipe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500333
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500334 built_recipe = None
335 # Create a copy of the recipe for historical puposes and update it
336 for built_layer in self.layer_version_built:
337 if built_layer.layer == recipe_information['layer_version'].layer:
338 built_recipe, c = self._cached_get_or_create(Recipe,
339 layer_version=built_layer,
340 file_path=recipe_information['file_path'],
341 pathflags = recipe_information['pathflags'])
342 update_recipe_obj(built_recipe)
343 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500344
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500345
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500346 # If we're in analysis mode or if this is a custom recipe
347 # then we are wholly responsible for the data
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500348 # and therefore we return the 'real' recipe rather than the build
349 # history copy of the recipe.
350 if recipe_information['layer_version'].build is not None and \
351 recipe_information['layer_version'].build.project == \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500352 Project.objects.get_or_create_default_project():
353 return recipe
354
355 if built_recipe is None:
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500356 return recipe
357
358 return built_recipe
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500359
360 def get_update_layer_version_object(self, build_obj, layer_obj, layer_version_information):
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500361 if isinstance(layer_obj, Layer_Version):
362 # We already found our layer version for this build so just
363 # update it with the new build information
364 logger.debug("We found our layer from toaster")
365 layer_obj.local_path = layer_version_information['local_path']
366 layer_obj.save()
367 self.layer_version_objects.append(layer_obj)
368
369 # create a new copy of this layer version as a snapshot for
370 # historical purposes
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500371 layer_copy, c = Layer_Version.objects.get_or_create(
372 build=build_obj,
373 layer=layer_obj.layer,
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600374 release=layer_obj.release,
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500375 branch=layer_version_information['branch'],
376 commit=layer_version_information['commit'],
377 local_path=layer_version_information['local_path'],
378 )
379
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500380 logger.debug("Created new layer version %s for build history",
381 layer_copy.layer.name)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500382
383 self.layer_version_built.append(layer_copy)
384
385 return layer_obj
386
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500387 assert isinstance(build_obj, Build)
388 assert isinstance(layer_obj, Layer)
389 assert 'branch' in layer_version_information
390 assert 'commit' in layer_version_information
391 assert 'priority' in layer_version_information
392 assert 'local_path' in layer_version_information
393
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500394 # If we're doing a command line build then associate this new layer with the
395 # project to avoid it 'contaminating' toaster data
396 project = None
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500397 if build_obj.project == Project.objects.get_or_create_default_project():
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500398 project = build_obj.project
399
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500400 layer_version_object, _ = Layer_Version.objects.get_or_create(
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500401 build = build_obj,
402 layer = layer_obj,
403 branch = layer_version_information['branch'],
404 commit = layer_version_information['commit'],
405 priority = layer_version_information['priority'],
406 local_path = layer_version_information['local_path'],
407 project=project)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500408
409 self.layer_version_objects.append(layer_version_object)
410
411 return layer_version_object
412
413 def get_update_layer_object(self, layer_information, brbe):
414 assert 'name' in layer_information
415 assert 'layer_index_url' in layer_information
416
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600417 # From command line builds we have no brbe as the request is directly
418 # from bitbake
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500419 if brbe is None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600420 # If we don't have git commit sha then we're using a non-git
421 # layer so set the layer_source_dir to identify it as such
422 if not layer_information['version']['commit']:
423 local_source_dir = layer_information["local_path"]
424 else:
425 local_source_dir = None
426
427 layer_object, _ = \
428 Layer.objects.get_or_create(
429 name=layer_information['name'],
430 local_source_dir=local_source_dir,
431 layer_index_url=layer_information['layer_index_url'])
432
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500433 return layer_object
434 else:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500435 br_id, be_id = brbe.split(":")
436
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500437 # Find the layer version by matching the layer event information
438 # against the metadata we have in Toaster
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500439
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500440 try:
441 br_layer = BRLayer.objects.get(req=br_id,
442 name=layer_information['name'])
443 return br_layer.layer_version
444 except (BRLayer.MultipleObjectsReturned, BRLayer.DoesNotExist):
445 # There are multiple of the same layer name or the name
446 # hasn't been determined by the toaster.bbclass layer
447 # so let's filter by the local_path
448 bc = bbcontroller.getBuildEnvironmentController(pk=be_id)
449 for br_layer in BRLayer.objects.filter(req=br_id):
450 if br_layer.giturl and \
451 layer_information['local_path'].endswith(
452 bc.getGitCloneDirectory(br_layer.giturl,
453 br_layer.commit)):
454 return br_layer.layer_version
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500455
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500456 if br_layer.local_source_dir == \
457 layer_information['local_path']:
458 return br_layer.layer_version
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500459
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500460 # We've reached the end of our search and couldn't find the layer
461 # we can continue but some data may be missing
462 raise NotExisting("Unidentified layer %s" %
463 pformat(layer_information))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500464
465 def save_target_file_information(self, build_obj, target_obj, filedata):
466 assert isinstance(build_obj, Build)
467 assert isinstance(target_obj, Target)
468 dirs = filedata['dirs']
469 files = filedata['files']
470 syms = filedata['syms']
471
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500472 # always create the root directory as a special case;
473 # note that this is never displayed, so the owner, group,
474 # size, permission are irrelevant
475 tf_obj = Target_File.objects.create(target = target_obj,
476 path = '/',
477 size = 0,
478 owner = '',
479 group = '',
480 permission = '',
481 inodetype = Target_File.ITYPE_DIRECTORY)
482 tf_obj.save()
483
484 # insert directories, ordered by name depth
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500485 for d in sorted(dirs, key=lambda x:len(x[-1].split("/"))):
486 (user, group, size) = d[1:4]
487 permission = d[0][1:]
488 path = d[4].lstrip(".")
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500489
490 # we already created the root directory, so ignore any
491 # entry for it
Andrew Geissler595f6302022-01-24 19:11:47 +0000492 if not path:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500493 continue
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500494
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500495 parent_path = "/".join(path.split("/")[:len(path.split("/")) - 1])
Andrew Geissler595f6302022-01-24 19:11:47 +0000496 if not parent_path:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500497 parent_path = "/"
498 parent_obj = self._cached_get(Target_File, target = target_obj, path = parent_path, inodetype = Target_File.ITYPE_DIRECTORY)
499 tf_obj = Target_File.objects.create(
500 target = target_obj,
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600501 path = path,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500502 size = size,
503 inodetype = Target_File.ITYPE_DIRECTORY,
504 permission = permission,
505 owner = user,
506 group = group,
507 directory = parent_obj)
508
509
510 # we insert files
511 for d in files:
512 (user, group, size) = d[1:4]
513 permission = d[0][1:]
514 path = d[4].lstrip(".")
515 parent_path = "/".join(path.split("/")[:len(path.split("/")) - 1])
516 inodetype = Target_File.ITYPE_REGULAR
517 if d[0].startswith('b'):
518 inodetype = Target_File.ITYPE_BLOCK
519 if d[0].startswith('c'):
520 inodetype = Target_File.ITYPE_CHARACTER
521 if d[0].startswith('p'):
522 inodetype = Target_File.ITYPE_FIFO
523
524 tf_obj = Target_File.objects.create(
525 target = target_obj,
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600526 path = path,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500527 size = size,
528 inodetype = inodetype,
529 permission = permission,
530 owner = user,
531 group = group)
532 parent_obj = self._cached_get(Target_File, target = target_obj, path = parent_path, inodetype = Target_File.ITYPE_DIRECTORY)
533 tf_obj.directory = parent_obj
534 tf_obj.save()
535
536 # we insert symlinks
537 for d in syms:
538 (user, group, size) = d[1:4]
539 permission = d[0][1:]
540 path = d[4].lstrip(".")
541 filetarget_path = d[6]
542
543 parent_path = "/".join(path.split("/")[:len(path.split("/")) - 1])
544 if not filetarget_path.startswith("/"):
545 # we have a relative path, get a normalized absolute one
546 filetarget_path = parent_path + "/" + filetarget_path
547 fcp = filetarget_path.split("/")
548 fcpl = []
549 for i in fcp:
550 if i == "..":
551 fcpl.pop()
552 else:
553 fcpl.append(i)
554 filetarget_path = "/".join(fcpl)
555
556 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600557 filetarget_obj = Target_File.objects.get(target = target_obj, path = filetarget_path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500558 except Target_File.DoesNotExist:
559 # we might have an invalid link; no way to detect this. just set it to None
560 filetarget_obj = None
561
562 parent_obj = Target_File.objects.get(target = target_obj, path = parent_path, inodetype = Target_File.ITYPE_DIRECTORY)
563
564 tf_obj = Target_File.objects.create(
565 target = target_obj,
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600566 path = path,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500567 size = size,
568 inodetype = Target_File.ITYPE_SYMLINK,
569 permission = permission,
570 owner = user,
571 group = group,
572 directory = parent_obj,
573 sym_target = filetarget_obj)
574
575
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500576 def save_target_package_information(self, build_obj, target_obj, packagedict, pkgpnmap, recipes, built_package=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500577 assert isinstance(build_obj, Build)
578 assert isinstance(target_obj, Target)
579
Andrew Geissler595f6302022-01-24 19:11:47 +0000580 errormsg = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500581 for p in packagedict:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500582 # Search name swtiches round the installed name vs package name
583 # by default installed name == package name
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500584 searchname = p
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500585 if p not in pkgpnmap:
586 logger.warning("Image packages list contains %p, but is"
587 " missing from all packages list where the"
588 " metadata comes from. Skipping...", p)
589 continue
590
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500591 if 'OPKGN' in pkgpnmap[p].keys():
592 searchname = pkgpnmap[p]['OPKGN']
593
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500594 built_recipe = recipes[pkgpnmap[p]['PN']]
595
596 if built_package:
597 packagedict[p]['object'], created = Package.objects.get_or_create( build = build_obj, name = searchname )
598 recipe = built_recipe
599 else:
600 packagedict[p]['object'], created = \
601 CustomImagePackage.objects.get_or_create(name=searchname)
602 # Clear the Package_Dependency objects as we're going to update
603 # the CustomImagePackage with the latest dependency information
604 packagedict[p]['object'].package_dependencies_target.all().delete()
605 packagedict[p]['object'].package_dependencies_source.all().delete()
606 try:
607 recipe = self._cached_get(
608 Recipe,
609 name=built_recipe.name,
610 layer_version__build=None,
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600611 layer_version__release=
612 built_recipe.layer_version.release,
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500613 file_path=built_recipe.file_path,
614 version=built_recipe.version
615 )
616 except (Recipe.DoesNotExist,
617 Recipe.MultipleObjectsReturned) as e:
618 logger.info("We did not find one recipe for the"
619 "configuration data package %s %s" % (p, e))
620 continue
621
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500622 if created or packagedict[p]['object'].size == -1: # save the data anyway we can, not just if it was not created here; bug [YOCTO #6887]
623 # fill in everything we can from the runtime-reverse package data
624 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500625 packagedict[p]['object'].recipe = recipe
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500626 packagedict[p]['object'].version = pkgpnmap[p]['PV']
627 packagedict[p]['object'].installed_name = p
628 packagedict[p]['object'].revision = pkgpnmap[p]['PR']
629 packagedict[p]['object'].license = pkgpnmap[p]['LICENSE']
630 packagedict[p]['object'].section = pkgpnmap[p]['SECTION']
631 packagedict[p]['object'].summary = pkgpnmap[p]['SUMMARY']
632 packagedict[p]['object'].description = pkgpnmap[p]['DESCRIPTION']
633 packagedict[p]['object'].size = int(pkgpnmap[p]['PKGSIZE'])
634
635 # no files recorded for this package, so save files info
636 packagefile_objects = []
637 for targetpath in pkgpnmap[p]['FILES_INFO']:
638 targetfilesize = pkgpnmap[p]['FILES_INFO'][targetpath]
639 packagefile_objects.append(Package_File( package = packagedict[p]['object'],
640 path = targetpath,
641 size = targetfilesize))
Andrew Geissler595f6302022-01-24 19:11:47 +0000642 if packagefile_objects:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500643 Package_File.objects.bulk_create(packagefile_objects)
644 except KeyError as e:
Andrew Geissler595f6302022-01-24 19:11:47 +0000645 errormsg.append(" stpi: Key error, package %s key %s \n" % (p, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500646
647 # save disk installed size
648 packagedict[p]['object'].installed_size = packagedict[p]['size']
649 packagedict[p]['object'].save()
650
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500651 if built_package:
652 Target_Installed_Package.objects.create(target = target_obj, package = packagedict[p]['object'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500653
654 packagedeps_objs = []
Brad Bishop00e122a2019-10-05 11:10:57 -0400655 pattern_so = re.compile(r'.*\.so(\.\d*)?$')
656 pattern_lib = re.compile(r'.*\-suffix(\d*)?$')
657 pattern_ko = re.compile(r'^kernel-module-.*')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500658 for p in packagedict:
659 for (px,deptype) in packagedict[p]['depends']:
660 if deptype == 'depends':
661 tdeptype = Package_Dependency.TYPE_TRDEPENDS
662 elif deptype == 'recommends':
663 tdeptype = Package_Dependency.TYPE_TRECOMMENDS
664
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500665 try:
Brad Bishop00e122a2019-10-05 11:10:57 -0400666 # Skip known non-package objects like libraries and kernel modules
667 if pattern_so.match(px) or pattern_lib.match(px):
668 logger.info("Toaster does not add library file dependencies to packages (%s,%s)", p, px)
669 continue
670 if pattern_ko.match(px):
671 logger.info("Toaster does not add kernel module dependencies to packages (%s,%s)", p, px)
672 continue
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500673 packagedeps_objs.append(Package_Dependency(
674 package = packagedict[p]['object'],
675 depends_on = packagedict[px]['object'],
676 dep_type = tdeptype,
677 target = target_obj))
678 except KeyError as e:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600679 logger.warning("Could not add dependency to the package %s "
680 "because %s is an unknown package", p, px)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500681
Andrew Geissler595f6302022-01-24 19:11:47 +0000682 if packagedeps_objs:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500683 Package_Dependency.objects.bulk_create(packagedeps_objs)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500684 else:
685 logger.info("No package dependencies created")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500686
Andrew Geissler595f6302022-01-24 19:11:47 +0000687 if errormsg:
688 logger.warning("buildinfohelper: target_package_info could not identify recipes: \n%s", "".join(errormsg))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500689
690 def save_target_image_file_information(self, target_obj, file_name, file_size):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600691 Target_Image_File.objects.create(target=target_obj,
692 file_name=file_name, file_size=file_size)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500693
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600694 def save_target_kernel_file(self, target_obj, file_name, file_size):
695 """
696 Save kernel file (bzImage, modules*) information for a Target target_obj.
697 """
698 TargetKernelFile.objects.create(target=target_obj,
699 file_name=file_name, file_size=file_size)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500700
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600701 def save_target_sdk_file(self, target_obj, file_name, file_size):
702 """
703 Save SDK artifacts to the database, associating them with a
704 Target object.
705 """
706 TargetSDKFile.objects.create(target=target_obj, file_name=file_name,
707 file_size=file_size)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500708
709 def create_logmessage(self, log_information):
710 assert 'build' in log_information
711 assert 'level' in log_information
712 assert 'message' in log_information
713
714 log_object = LogMessage.objects.create(
715 build = log_information['build'],
716 level = log_information['level'],
717 message = log_information['message'])
718
719 for v in vars(log_object):
720 if v in log_information.keys():
721 vars(log_object)[v] = log_information[v]
722
723 return log_object.save()
724
725
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500726 def save_build_package_information(self, build_obj, package_info, recipes,
727 built_package):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500728 # assert isinstance(build_obj, Build)
729
730 if not 'PN' in package_info.keys():
731 # no package data to save (e.g. 'OPKGN'="lib64-*"|"lib32-*")
732 return None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500733
734 # create and save the object
735 pname = package_info['PKG']
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500736 built_recipe = recipes[package_info['PN']]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500737 if 'OPKGN' in package_info.keys():
738 pname = package_info['OPKGN']
739
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500740 if built_package:
741 bp_object, _ = Package.objects.get_or_create( build = build_obj,
742 name = pname )
743 recipe = built_recipe
744 else:
745 bp_object, created = \
746 CustomImagePackage.objects.get_or_create(name=pname)
747 try:
748 recipe = self._cached_get(Recipe,
749 name=built_recipe.name,
750 layer_version__build=None,
751 file_path=built_recipe.file_path,
752 version=built_recipe.version)
753
754 except (Recipe.DoesNotExist, Recipe.MultipleObjectsReturned):
755 logger.debug("We did not find one recipe for the configuration"
756 "data package %s" % pname)
757 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500758
759 bp_object.installed_name = package_info['PKG']
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500760 bp_object.recipe = recipe
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500761 bp_object.version = package_info['PKGV']
762 bp_object.revision = package_info['PKGR']
763 bp_object.summary = package_info['SUMMARY']
764 bp_object.description = package_info['DESCRIPTION']
765 bp_object.size = int(package_info['PKGSIZE'])
766 bp_object.section = package_info['SECTION']
767 bp_object.license = package_info['LICENSE']
768 bp_object.save()
769
770 # save any attached file information
771 packagefile_objects = []
772 for path in package_info['FILES_INFO']:
773 packagefile_objects.append(Package_File( package = bp_object,
774 path = path,
775 size = package_info['FILES_INFO'][path] ))
Andrew Geissler595f6302022-01-24 19:11:47 +0000776 if packagefile_objects:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500777 Package_File.objects.bulk_create(packagefile_objects)
778
779 def _po_byname(p):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500780 if built_package:
781 pkg, created = Package.objects.get_or_create(build=build_obj,
782 name=p)
783 else:
784 pkg, created = CustomImagePackage.objects.get_or_create(name=p)
785
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500786 if created:
787 pkg.size = -1
788 pkg.save()
789 return pkg
790
791 packagedeps_objs = []
792 # save soft dependency information
793 if 'RDEPENDS' in package_info and package_info['RDEPENDS']:
794 for p in bb.utils.explode_deps(package_info['RDEPENDS']):
795 packagedeps_objs.append(Package_Dependency( package = bp_object,
796 depends_on = _po_byname(p), dep_type = Package_Dependency.TYPE_RDEPENDS))
797 if 'RPROVIDES' in package_info and package_info['RPROVIDES']:
798 for p in bb.utils.explode_deps(package_info['RPROVIDES']):
799 packagedeps_objs.append(Package_Dependency( package = bp_object,
800 depends_on = _po_byname(p), dep_type = Package_Dependency.TYPE_RPROVIDES))
801 if 'RRECOMMENDS' in package_info and package_info['RRECOMMENDS']:
802 for p in bb.utils.explode_deps(package_info['RRECOMMENDS']):
803 packagedeps_objs.append(Package_Dependency( package = bp_object,
804 depends_on = _po_byname(p), dep_type = Package_Dependency.TYPE_RRECOMMENDS))
805 if 'RSUGGESTS' in package_info and package_info['RSUGGESTS']:
806 for p in bb.utils.explode_deps(package_info['RSUGGESTS']):
807 packagedeps_objs.append(Package_Dependency( package = bp_object,
808 depends_on = _po_byname(p), dep_type = Package_Dependency.TYPE_RSUGGESTS))
809 if 'RREPLACES' in package_info and package_info['RREPLACES']:
810 for p in bb.utils.explode_deps(package_info['RREPLACES']):
811 packagedeps_objs.append(Package_Dependency( package = bp_object,
812 depends_on = _po_byname(p), dep_type = Package_Dependency.TYPE_RREPLACES))
813 if 'RCONFLICTS' in package_info and package_info['RCONFLICTS']:
814 for p in bb.utils.explode_deps(package_info['RCONFLICTS']):
815 packagedeps_objs.append(Package_Dependency( package = bp_object,
816 depends_on = _po_byname(p), dep_type = Package_Dependency.TYPE_RCONFLICTS))
817
Andrew Geissler595f6302022-01-24 19:11:47 +0000818 if packagedeps_objs:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500819 Package_Dependency.objects.bulk_create(packagedeps_objs)
820
821 return bp_object
822
823 def save_build_variables(self, build_obj, vardump):
824 assert isinstance(build_obj, Build)
825
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500826 for k in vardump:
827 desc = vardump[k]['doc']
828 if desc is None:
829 var_words = [word for word in k.split('_')]
830 root_var = "_".join([word for word in var_words if word.isupper()])
831 if root_var and root_var != k and root_var in vardump:
832 desc = vardump[root_var]['doc']
833 if desc is None:
834 desc = ''
Andrew Geissler595f6302022-01-24 19:11:47 +0000835 if desc:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500836 HelpText.objects.get_or_create(build=build_obj,
837 area=HelpText.VARIABLE,
838 key=k, text=desc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500839 if not bool(vardump[k]['func']):
840 value = vardump[k]['v']
841 if value is None:
842 value = ''
843 variable_obj = Variable.objects.create( build = build_obj,
844 variable_name = k,
845 variable_value = value,
846 description = desc)
847
848 varhist_objects = []
849 for vh in vardump[k]['history']:
850 if not 'documentation.conf' in vh['file']:
851 varhist_objects.append(VariableHistory( variable = variable_obj,
852 file_name = vh['file'],
853 line_number = vh['line'],
854 operation = vh['op']))
Andrew Geissler595f6302022-01-24 19:11:47 +0000855 if varhist_objects:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500856 VariableHistory.objects.bulk_create(varhist_objects)
857
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500858
859class MockEvent(object):
860 """ This object is used to create event, for which normal event-processing methods can
861 be used, out of data that is not coming via an actual event
862 """
863 def __init__(self):
864 self.msg = None
865 self.levelno = None
866 self.taskname = None
867 self.taskhash = None
868 self.pathname = None
869 self.lineno = None
870
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500871 def getMessage(self):
872 """
873 Simulate LogRecord message return
874 """
875 return self.msg
876
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500877
878class BuildInfoHelper(object):
879 """ This class gathers the build information from the server and sends it
880 towards the ORM wrapper for storing in the database
881 It is instantiated once per build
882 Keeps in memory all data that needs matching before writing it to the database
883 """
884
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600885 # tasks which produce image files; note we include '', as we set
886 # the task for a target to '' (i.e. 'build') if no target is
887 # explicitly defined
888 IMAGE_GENERATING_TASKS = ['', 'build', 'image', 'populate_sdk_ext']
889
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500890 # pylint: disable=protected-access
891 # the code will look into the protected variables of the event; no easy way around this
892 # pylint: disable=bad-continuation
893 # we do not follow the python conventions for continuation indentation due to long lines here
894
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500895 def __init__(self, server, has_build_history = False, brbe = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500896 self.internal_state = {}
897 self.internal_state['taskdata'] = {}
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500898 self.internal_state['targets'] = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500899 self.task_order = 0
900 self.autocommit_step = 1
901 self.server = server
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500902 self.orm_wrapper = ORMWrapper()
903 self.has_build_history = has_build_history
904 self.tmp_dir = self.server.runCommand(["getVariable", "TMPDIR"])[0]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500905
906 # this is set for Toaster-triggered builds by localhostbecontroller
907 # via toasterui
908 self.brbe = brbe
909
910 self.project = None
911
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600912 logger.debug("buildinfohelper: Build info helper inited %s" % vars(self))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500913
914
915 ###################
916 ## methods to convert event/external info into objects that the ORM layer uses
917
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600918 def _ensure_build(self):
919 """
920 Ensure the current build object exists and is up to date with
921 data on the bitbake server
922 """
923 if not 'build' in self.internal_state or not self.internal_state['build']:
924 # create the Build object
925 self.internal_state['build'] = \
926 self.orm_wrapper.get_or_create_build_object(self.brbe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500927
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600928 build = self.internal_state['build']
929
930 # update missing fields on the Build object with found data
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500931 build_info = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600932
933 # set to True if at least one field is going to be set
934 changed = False
935
936 if not build.build_name:
937 build_name = self.server.runCommand(["getVariable", "BUILDNAME"])[0]
938
939 # only reset the build name if the one on the server is actually
940 # a valid value for the build_name field
Andrew Geissler82c905d2020-04-13 13:39:40 -0500941 if build_name is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600942 build_info['build_name'] = build_name
943 changed = True
944
945 if not build.machine:
946 build_info['machine'] = self.server.runCommand(["getVariable", "MACHINE"])[0]
947 changed = True
948
949 if not build.distro:
950 build_info['distro'] = self.server.runCommand(["getVariable", "DISTRO"])[0]
951 changed = True
952
953 if not build.distro_version:
954 build_info['distro_version'] = self.server.runCommand(["getVariable", "DISTRO_VERSION"])[0]
955 changed = True
956
957 if not build.bitbake_version:
958 build_info['bitbake_version'] = self.server.runCommand(["getVariable", "BB_VERSION"])[0]
959 changed = True
960
961 if changed:
962 self.orm_wrapper.update_build(self.internal_state['build'], build_info)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500963
964 def _get_task_information(self, event, recipe):
965 assert 'taskname' in vars(event)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600966 self._ensure_build()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500967
968 task_information = {}
969 task_information['build'] = self.internal_state['build']
970 task_information['outcome'] = Task.OUTCOME_NA
971 task_information['recipe'] = recipe
972 task_information['task_name'] = event.taskname
973 try:
974 # some tasks don't come with a hash. and that's ok
975 task_information['sstate_checksum'] = event.taskhash
976 except AttributeError:
977 pass
978 return task_information
979
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500980 def _get_layer_version_for_dependency(self, pathRE):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500981 """ Returns the layer in the toaster db that has a full regex
982 match to the pathRE. pathRE - the layer path passed as a regex in the
983 event. It is created in cooker.py as a collection for the layer
984 priorities.
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500985 """
986 self._ensure_build()
987
988 def _sort_longest_path(layer_version):
989 assert isinstance(layer_version, Layer_Version)
990 return len(layer_version.local_path)
991
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500992 # Our paths don't append a trailing slash
993 if pathRE.endswith("/"):
994 pathRE = pathRE[:-1]
995
996 p = re.compile(pathRE)
997 path=re.sub(r'[$^]',r'',pathRE)
998 # Heuristics: we always match recipe to the deepest layer path in
999 # the discovered layers
1000 for lvo in sorted(self.orm_wrapper.layer_version_objects,
1001 reverse=True, key=_sort_longest_path):
1002 if p.fullmatch(os.path.abspath(lvo.local_path)):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001003 return lvo
1004 if lvo.layer.local_source_dir:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001005 if p.fullmatch(os.path.abspath(lvo.layer.local_source_dir)):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001006 return lvo
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001007 if 0 == path.find(lvo.local_path):
1008 # sub-layer path inside existing layer
1009 return lvo
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001010
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001011 # if we get here, we didn't read layers correctly;
1012 # dump whatever information we have on the error log
1013 logger.warning("Could not match layer dependency for path %s : %s",
1014 pathRE,
1015 self.orm_wrapper.layer_version_objects)
1016 return None
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001017
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001018 def _get_layer_version_for_path(self, path):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001019 self._ensure_build()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001020
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001021 def _slkey_interactive(layer_version):
1022 assert isinstance(layer_version, Layer_Version)
1023 return len(layer_version.local_path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001024
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001025 # Heuristics: we always match recipe to the deepest layer path in the discovered layers
1026 for lvo in sorted(self.orm_wrapper.layer_version_objects, reverse=True, key=_slkey_interactive):
1027 # we can match to the recipe file path
1028 if path.startswith(lvo.local_path):
1029 return lvo
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001030 if lvo.layer.local_source_dir and \
1031 path.startswith(lvo.layer.local_source_dir):
1032 return lvo
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001033
1034 #if we get here, we didn't read layers correctly; dump whatever information we have on the error log
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001035 logger.warning("Could not match layer version for recipe path %s : %s", path, self.orm_wrapper.layer_version_objects)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001036
1037 #mockup the new layer
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001038 unknown_layer, _ = Layer.objects.get_or_create(name="Unidentified layer", layer_index_url="")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001039 unknown_layer_version_obj, _ = Layer_Version.objects.get_or_create(layer = unknown_layer, build = self.internal_state['build'])
1040
1041 # append it so we don't run into this error again and again
1042 self.orm_wrapper.layer_version_objects.append(unknown_layer_version_obj)
1043
1044 return unknown_layer_version_obj
1045
1046 def _get_recipe_information_from_taskfile(self, taskfile):
1047 localfilepath = taskfile.split(":")[-1]
1048 filepath_flags = ":".join(sorted(taskfile.split(":")[:-1]))
1049 layer_version_obj = self._get_layer_version_for_path(localfilepath)
1050
1051
1052
1053 recipe_info = {}
1054 recipe_info['layer_version'] = layer_version_obj
1055 recipe_info['file_path'] = localfilepath
1056 recipe_info['pathflags'] = filepath_flags
1057
1058 if recipe_info['file_path'].startswith(recipe_info['layer_version'].local_path):
1059 recipe_info['file_path'] = recipe_info['file_path'][len(recipe_info['layer_version'].local_path):].lstrip("/")
1060 else:
1061 raise RuntimeError("Recipe file path %s is not under layer version at %s" % (recipe_info['file_path'], recipe_info['layer_version'].local_path))
1062
1063 return recipe_info
1064
1065 def _get_path_information(self, task_object):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001066 self._ensure_build()
1067
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001068 assert isinstance(task_object, Task)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001069 build_stats_format = "{tmpdir}/buildstats/{buildname}/{package}/"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001070 build_stats_path = []
1071
1072 for t in self.internal_state['targets']:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001073 buildname = self.internal_state['build'].build_name
1074 pe, pv = task_object.recipe.version.split(":",1)
Andrew Geissler595f6302022-01-24 19:11:47 +00001075 if pe:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001076 package = task_object.recipe.name + "-" + pe + "_" + pv
1077 else:
1078 package = task_object.recipe.name + "-" + pv
1079
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001080 build_stats_path.append(build_stats_format.format(tmpdir=self.tmp_dir,
1081 buildname=buildname,
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001082 package=package))
1083
1084 return build_stats_path
1085
1086
1087 ################################
1088 ## external available methods to store information
1089 @staticmethod
1090 def _get_data_from_event(event):
1091 evdata = None
1092 if '_localdata' in vars(event):
1093 evdata = event._localdata
1094 elif 'data' in vars(event):
1095 evdata = event.data
1096 else:
1097 raise Exception("Event with neither _localdata or data properties")
1098 return evdata
1099
1100 def store_layer_info(self, event):
1101 layerinfos = BuildInfoHelper._get_data_from_event(event)
1102 self.internal_state['lvs'] = {}
1103 for layer in layerinfos:
1104 try:
1105 self.internal_state['lvs'][self.orm_wrapper.get_update_layer_object(layerinfos[layer], self.brbe)] = layerinfos[layer]['version']
1106 self.internal_state['lvs'][self.orm_wrapper.get_update_layer_object(layerinfos[layer], self.brbe)]['local_path'] = layerinfos[layer]['local_path']
1107 except NotExisting as nee:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001108 logger.warning("buildinfohelper: cannot identify layer exception:%s ", nee)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001109
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001110 def store_started_build(self):
1111 self._ensure_build()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001112
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001113 def save_build_log_file_path(self, build_log_path):
1114 self._ensure_build()
1115
1116 if not self.internal_state['build'].cooker_log_path:
1117 data_dict = {'cooker_log_path': build_log_path}
1118 self.orm_wrapper.update_build(self.internal_state['build'], data_dict)
1119
1120 def save_build_targets(self, event):
1121 self._ensure_build()
1122
1123 # create target information
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001124 assert '_pkgs' in vars(event)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001125 target_information = {}
1126 target_information['targets'] = event._pkgs
1127 target_information['build'] = self.internal_state['build']
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001128
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001129 self.internal_state['targets'] = self.orm_wrapper.get_or_create_targets(target_information)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001130
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001131 def save_build_layers_and_variables(self):
1132 self._ensure_build()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001133
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001134 build_obj = self.internal_state['build']
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001135
1136 # save layer version information for this build
1137 if not 'lvs' in self.internal_state:
1138 logger.error("Layer version information not found; Check if the bitbake server was configured to inherit toaster.bbclass.")
1139 else:
1140 for layer_obj in self.internal_state['lvs']:
1141 self.orm_wrapper.get_update_layer_version_object(build_obj, layer_obj, self.internal_state['lvs'][layer_obj])
1142
1143 del self.internal_state['lvs']
1144
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001145 # Save build configuration
1146 data = self.server.runCommand(["getAllKeysWithFlags", ["doc", "func"]])[0]
1147
1148 # convert the paths from absolute to relative to either the build directory or layer checkouts
1149 path_prefixes = []
1150
1151 if self.brbe is not None:
1152 _, be_id = self.brbe.split(":")
1153 be = BuildEnvironment.objects.get(pk = be_id)
1154 path_prefixes.append(be.builddir)
1155
1156 for layer in sorted(self.orm_wrapper.layer_version_objects, key = lambda x:len(x.local_path), reverse=True):
1157 path_prefixes.append(layer.local_path)
1158
1159 # we strip the prefixes
1160 for k in data:
1161 if not bool(data[k]['func']):
1162 for vh in data[k]['history']:
1163 if not 'documentation.conf' in vh['file']:
1164 abs_file_name = vh['file']
1165 for pp in path_prefixes:
1166 if abs_file_name.startswith(pp + "/"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001167 # preserve layer name in relative path
1168 vh['file']=abs_file_name[pp.rfind("/")+1:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001169 break
1170
1171 # save the variables
1172 self.orm_wrapper.save_build_variables(build_obj, data)
1173
1174 return self.brbe
1175
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001176 def set_recipes_to_parse(self, num_recipes):
1177 """
1178 Set the number of recipes which need to be parsed for this build.
1179 This is set the first time ParseStarted is received by toasterui.
1180 """
1181 self._ensure_build()
1182 self.internal_state['build'].recipes_to_parse = num_recipes
1183 self.internal_state['build'].save()
1184
1185 def set_recipes_parsed(self, num_recipes):
1186 """
1187 Set the number of recipes parsed so far for this build; this is updated
1188 each time a ParseProgress or ParseCompleted event is received by
1189 toasterui.
1190 """
1191 self._ensure_build()
1192 if num_recipes <= self.internal_state['build'].recipes_to_parse:
1193 self.internal_state['build'].recipes_parsed = num_recipes
1194 self.internal_state['build'].save()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001195
1196 def update_target_image_file(self, event):
1197 evdata = BuildInfoHelper._get_data_from_event(event)
1198
1199 for t in self.internal_state['targets']:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001200 if t.is_image:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001201 output_files = list(evdata.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001202 for output in output_files:
1203 if t.target in output and 'rootfs' in output and not output.endswith(".manifest"):
1204 self.orm_wrapper.save_target_image_file_information(t, output, evdata[output])
1205
1206 def update_artifact_image_file(self, event):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001207 self._ensure_build()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001208 evdata = BuildInfoHelper._get_data_from_event(event)
1209 for artifact_path in evdata.keys():
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001210 self.orm_wrapper.save_artifact_information(
1211 self.internal_state['build'], artifact_path,
1212 evdata[artifact_path])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001213
1214 def update_build_information(self, event, errors, warnings, taskfailures):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001215 self._ensure_build()
1216 self.orm_wrapper.update_build_stats_and_outcome(
1217 self.internal_state['build'], errors, warnings, taskfailures)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001218
1219 def store_started_task(self, event):
1220 assert isinstance(event, (bb.runqueue.sceneQueueTaskStarted, bb.runqueue.runQueueTaskStarted, bb.runqueue.runQueueTaskSkipped))
1221 assert 'taskfile' in vars(event)
1222 localfilepath = event.taskfile.split(":")[-1]
1223 assert localfilepath.startswith("/")
1224
1225 identifier = event.taskfile + ":" + event.taskname
1226
1227 recipe_information = self._get_recipe_information_from_taskfile(event.taskfile)
1228 recipe = self.orm_wrapper.get_update_recipe_object(recipe_information, True)
1229
1230 task_information = self._get_task_information(event, recipe)
1231 task_information['outcome'] = Task.OUTCOME_NA
1232
1233 if isinstance(event, bb.runqueue.runQueueTaskSkipped):
1234 assert 'reason' in vars(event)
1235 task_information['task_executed'] = False
1236 if event.reason == "covered":
1237 task_information['outcome'] = Task.OUTCOME_COVERED
1238 if event.reason == "existing":
1239 task_information['outcome'] = Task.OUTCOME_PREBUILT
1240 else:
1241 task_information['task_executed'] = True
Andrew Geissler82c905d2020-04-13 13:39:40 -05001242 if 'noexec' in vars(event) and event.noexec:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001243 task_information['task_executed'] = False
1244 task_information['outcome'] = Task.OUTCOME_EMPTY
1245 task_information['script_type'] = Task.CODING_NA
1246
1247 # do not assign order numbers to scene tasks
1248 if not isinstance(event, bb.runqueue.sceneQueueTaskStarted):
1249 self.task_order += 1
1250 task_information['order'] = self.task_order
1251
1252 self.orm_wrapper.get_update_task_object(task_information)
1253
1254 self.internal_state['taskdata'][identifier] = {
1255 'outcome': task_information['outcome'],
1256 }
1257
1258
1259 def store_tasks_stats(self, event):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001260 self._ensure_build()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001261 task_data = BuildInfoHelper._get_data_from_event(event)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001262
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001263 for (task_file, task_name, task_stats, recipe_name) in task_data:
1264 build = self.internal_state['build']
1265 self.orm_wrapper.update_task_object(build, task_name, recipe_name, task_stats)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001266
1267 def update_and_store_task(self, event):
1268 assert 'taskfile' in vars(event)
1269 localfilepath = event.taskfile.split(":")[-1]
1270 assert localfilepath.startswith("/")
1271
1272 identifier = event.taskfile + ":" + event.taskname
1273 if not identifier in self.internal_state['taskdata']:
1274 if isinstance(event, bb.build.TaskBase):
1275 # we do a bit of guessing
1276 candidates = [x for x in self.internal_state['taskdata'].keys() if x.endswith(identifier)]
1277 if len(candidates) == 1:
1278 identifier = candidates[0]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001279 elif len(candidates) > 1 and hasattr(event,'_package'):
1280 if 'native-' in event._package:
1281 identifier = 'native:' + identifier
1282 if 'nativesdk-' in event._package:
1283 identifier = 'nativesdk:' + identifier
1284 candidates = [x for x in self.internal_state['taskdata'].keys() if x.endswith(identifier)]
1285 if len(candidates) == 1:
1286 identifier = candidates[0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001287
1288 assert identifier in self.internal_state['taskdata']
1289 identifierlist = identifier.split(":")
1290 realtaskfile = ":".join(identifierlist[0:len(identifierlist)-1])
1291 recipe_information = self._get_recipe_information_from_taskfile(realtaskfile)
1292 recipe = self.orm_wrapper.get_update_recipe_object(recipe_information, True)
1293 task_information = self._get_task_information(event,recipe)
1294
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001295 task_information['outcome'] = self.internal_state['taskdata'][identifier]['outcome']
1296
1297 if 'logfile' in vars(event):
1298 task_information['logfile'] = event.logfile
1299
1300 if '_message' in vars(event):
1301 task_information['message'] = event._message
1302
1303 if 'taskflags' in vars(event):
1304 # with TaskStarted, we get even more information
1305 if 'python' in event.taskflags.keys() and event.taskflags['python'] == '1':
1306 task_information['script_type'] = Task.CODING_PYTHON
1307 else:
1308 task_information['script_type'] = Task.CODING_SHELL
1309
1310 if task_information['outcome'] == Task.OUTCOME_NA:
1311 if isinstance(event, (bb.runqueue.runQueueTaskCompleted, bb.runqueue.sceneQueueTaskCompleted)):
1312 task_information['outcome'] = Task.OUTCOME_SUCCESS
1313 del self.internal_state['taskdata'][identifier]
1314
1315 if isinstance(event, (bb.runqueue.runQueueTaskFailed, bb.runqueue.sceneQueueTaskFailed)):
1316 task_information['outcome'] = Task.OUTCOME_FAILED
1317 del self.internal_state['taskdata'][identifier]
1318
Andrew Geissler9aee5002022-03-30 16:27:02 +00001319 # we force a sync point here, to get the progress bar to show
1320 if self.autocommit_step % 3 == 0:
1321 transaction.set_autocommit(True)
1322 transaction.set_autocommit(False)
1323 self.autocommit_step += 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001324
1325 self.orm_wrapper.get_update_task_object(task_information, True) # must exist
1326
1327
1328 def store_missed_state_tasks(self, event):
1329 for (fn, taskname, taskhash, sstatefile) in BuildInfoHelper._get_data_from_event(event)['missed']:
1330
1331 # identifier = fn + taskname + "_setscene"
1332 recipe_information = self._get_recipe_information_from_taskfile(fn)
1333 recipe = self.orm_wrapper.get_update_recipe_object(recipe_information)
1334 mevent = MockEvent()
1335 mevent.taskname = taskname
1336 mevent.taskhash = taskhash
1337 task_information = self._get_task_information(mevent,recipe)
1338
1339 task_information['start_time'] = timezone.now()
1340 task_information['outcome'] = Task.OUTCOME_NA
1341 task_information['sstate_checksum'] = taskhash
1342 task_information['sstate_result'] = Task.SSTATE_MISS
1343 task_information['path_to_sstate_obj'] = sstatefile
1344
1345 self.orm_wrapper.get_update_task_object(task_information)
1346
1347 for (fn, taskname, taskhash, sstatefile) in BuildInfoHelper._get_data_from_event(event)['found']:
1348
1349 # identifier = fn + taskname + "_setscene"
1350 recipe_information = self._get_recipe_information_from_taskfile(fn)
1351 recipe = self.orm_wrapper.get_update_recipe_object(recipe_information)
1352 mevent = MockEvent()
1353 mevent.taskname = taskname
1354 mevent.taskhash = taskhash
1355 task_information = self._get_task_information(mevent,recipe)
1356
1357 task_information['path_to_sstate_obj'] = sstatefile
1358
1359 self.orm_wrapper.get_update_task_object(task_information)
1360
1361
1362 def store_target_package_data(self, event):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001363 self._ensure_build()
1364
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001365 # for all image targets
1366 for target in self.internal_state['targets']:
1367 if target.is_image:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001368 pkgdata = BuildInfoHelper._get_data_from_event(event)['pkgdata']
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001369 imgdata = BuildInfoHelper._get_data_from_event(event)['imgdata'].get(target.target, {})
1370 filedata = BuildInfoHelper._get_data_from_event(event)['filedata'].get(target.target, {})
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001371
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001372 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001373 self.orm_wrapper.save_target_package_information(self.internal_state['build'], target, imgdata, pkgdata, self.internal_state['recipes'], built_package=True)
1374 self.orm_wrapper.save_target_package_information(self.internal_state['build'], target, imgdata.copy(), pkgdata, self.internal_state['recipes'], built_package=False)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001375 except KeyError as e:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001376 logger.warning("KeyError in save_target_package_information"
1377 "%s ", e)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001378
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001379 # only try to find files in the image if the task for this
1380 # target is one which produces image files; otherwise, the old
1381 # list of files in the files-in-image.txt file will be
1382 # appended to the target even if it didn't produce any images
1383 if target.task in BuildInfoHelper.IMAGE_GENERATING_TASKS:
1384 try:
1385 self.orm_wrapper.save_target_file_information(self.internal_state['build'], target, filedata)
1386 except KeyError as e:
1387 logger.warning("KeyError in save_target_file_information"
1388 "%s ", e)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001389
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001390
1391
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001392 def cancel_cli_build(self):
1393 """
1394 If a build is currently underway, set its state to CANCELLED;
1395 note that this only gets called for command line builds which are
1396 interrupted, so it doesn't touch any BuildRequest objects
1397 """
1398 self._ensure_build()
1399 self.internal_state['build'].outcome = Build.CANCELLED
1400 self.internal_state['build'].save()
1401 signal_runbuilds()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001402
1403 def store_dependency_information(self, event):
1404 assert '_depgraph' in vars(event)
1405 assert 'layer-priorities' in event._depgraph
1406 assert 'pn' in event._depgraph
1407 assert 'tdepends' in event._depgraph
1408
Andrew Geissler595f6302022-01-24 19:11:47 +00001409 errormsg = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001410
1411 # save layer version priorities
1412 if 'layer-priorities' in event._depgraph.keys():
1413 for lv in event._depgraph['layer-priorities']:
1414 (_, path, _, priority) = lv
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001415 layer_version_obj = self._get_layer_version_for_dependency(path)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001416 if layer_version_obj:
1417 layer_version_obj.priority = priority
1418 layer_version_obj.save()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001419
1420 # save recipe information
1421 self.internal_state['recipes'] = {}
1422 for pn in event._depgraph['pn']:
1423
1424 file_name = event._depgraph['pn'][pn]['filename'].split(":")[-1]
1425 pathflags = ":".join(sorted(event._depgraph['pn'][pn]['filename'].split(":")[:-1]))
1426 layer_version_obj = self._get_layer_version_for_path(file_name)
1427
1428 assert layer_version_obj is not None
1429
1430 recipe_info = {}
1431 recipe_info['name'] = pn
1432 recipe_info['layer_version'] = layer_version_obj
1433
1434 if 'version' in event._depgraph['pn'][pn]:
1435 recipe_info['version'] = event._depgraph['pn'][pn]['version'].lstrip(":")
1436
1437 if 'summary' in event._depgraph['pn'][pn]:
1438 recipe_info['summary'] = event._depgraph['pn'][pn]['summary']
1439
1440 if 'license' in event._depgraph['pn'][pn]:
1441 recipe_info['license'] = event._depgraph['pn'][pn]['license']
1442
1443 if 'description' in event._depgraph['pn'][pn]:
1444 recipe_info['description'] = event._depgraph['pn'][pn]['description']
1445
1446 if 'section' in event._depgraph['pn'][pn]:
1447 recipe_info['section'] = event._depgraph['pn'][pn]['section']
1448
1449 if 'homepage' in event._depgraph['pn'][pn]:
1450 recipe_info['homepage'] = event._depgraph['pn'][pn]['homepage']
1451
1452 if 'bugtracker' in event._depgraph['pn'][pn]:
1453 recipe_info['bugtracker'] = event._depgraph['pn'][pn]['bugtracker']
1454
1455 recipe_info['file_path'] = file_name
1456 recipe_info['pathflags'] = pathflags
1457
1458 if recipe_info['file_path'].startswith(recipe_info['layer_version'].local_path):
1459 recipe_info['file_path'] = recipe_info['file_path'][len(recipe_info['layer_version'].local_path):].lstrip("/")
1460 else:
1461 raise RuntimeError("Recipe file path %s is not under layer version at %s" % (recipe_info['file_path'], recipe_info['layer_version'].local_path))
1462
1463 recipe = self.orm_wrapper.get_update_recipe_object(recipe_info)
1464 recipe.is_image = False
1465 if 'inherits' in event._depgraph['pn'][pn].keys():
1466 for cls in event._depgraph['pn'][pn]['inherits']:
1467 if cls.endswith('/image.bbclass'):
1468 recipe.is_image = True
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001469 recipe_info['is_image'] = True
1470 # Save the is_image state to the relevant recipe objects
1471 self.orm_wrapper.get_update_recipe_object(recipe_info)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001472 break
1473 if recipe.is_image:
1474 for t in self.internal_state['targets']:
1475 if pn == t.target:
1476 t.is_image = True
1477 t.save()
1478 self.internal_state['recipes'][pn] = recipe
1479
1480 # we'll not get recipes for key w/ values listed in ASSUME_PROVIDED
1481
1482 assume_provided = self.server.runCommand(["getVariable", "ASSUME_PROVIDED"])[0].split()
1483
1484 # save recipe dependency
1485 # buildtime
1486 recipedeps_objects = []
1487 for recipe in event._depgraph['depends']:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001488 target = self.internal_state['recipes'][recipe]
1489 for dep in event._depgraph['depends'][recipe]:
1490 if dep in assume_provided:
1491 continue
1492 via = None
1493 if 'providermap' in event._depgraph and dep in event._depgraph['providermap']:
1494 deprecipe = event._depgraph['providermap'][dep][0]
1495 dependency = self.internal_state['recipes'][deprecipe]
1496 via = Provides.objects.get_or_create(name=dep,
1497 recipe=dependency)[0]
1498 elif dep in self.internal_state['recipes']:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001499 dependency = self.internal_state['recipes'][dep]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001500 else:
Andrew Geissler595f6302022-01-24 19:11:47 +00001501 errormsg.append(" stpd: KeyError saving recipe dependency for %s, %s \n" % (recipe, dep))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001502 continue
1503 recipe_dep = Recipe_Dependency(recipe=target,
1504 depends_on=dependency,
1505 via=via,
1506 dep_type=Recipe_Dependency.TYPE_DEPENDS)
1507 recipedeps_objects.append(recipe_dep)
1508
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001509 Recipe_Dependency.objects.bulk_create(recipedeps_objects)
1510
1511 # save all task information
1512 def _save_a_task(taskdesc):
1513 spec = re.split(r'\.', taskdesc)
1514 pn = ".".join(spec[0:-1])
1515 taskname = spec[-1]
1516 e = event
1517 e.taskname = pn
1518 recipe = self.internal_state['recipes'][pn]
1519 task_info = self._get_task_information(e, recipe)
1520 task_info['task_name'] = taskname
1521 task_obj = self.orm_wrapper.get_update_task_object(task_info)
1522 return task_obj
1523
1524 # create tasks
1525 tasks = {}
1526 for taskdesc in event._depgraph['tdepends']:
1527 tasks[taskdesc] = _save_a_task(taskdesc)
1528
1529 # create dependencies between tasks
1530 taskdeps_objects = []
1531 for taskdesc in event._depgraph['tdepends']:
1532 target = tasks[taskdesc]
1533 for taskdep in event._depgraph['tdepends'][taskdesc]:
1534 if taskdep not in tasks:
1535 # Fetch tasks info is not collected previously
1536 dep = _save_a_task(taskdep)
1537 else:
1538 dep = tasks[taskdep]
1539 taskdeps_objects.append(Task_Dependency( task = target, depends_on = dep ))
1540 Task_Dependency.objects.bulk_create(taskdeps_objects)
1541
Andrew Geissler595f6302022-01-24 19:11:47 +00001542 if errormsg:
1543 logger.warning("buildinfohelper: dependency info not identify recipes: \n%s", "".join(errormsg))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001544
1545
1546 def store_build_package_information(self, event):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001547 self._ensure_build()
1548
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001549 package_info = BuildInfoHelper._get_data_from_event(event)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001550 self.orm_wrapper.save_build_package_information(
1551 self.internal_state['build'],
1552 package_info,
1553 self.internal_state['recipes'],
1554 built_package=True)
1555
1556 self.orm_wrapper.save_build_package_information(
1557 self.internal_state['build'],
1558 package_info,
1559 self.internal_state['recipes'],
1560 built_package=False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001561
1562 def _store_build_done(self, errorcode):
1563 logger.info("Build exited with errorcode %d", errorcode)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001564
1565 if not self.brbe:
1566 return
1567
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001568 br_id, be_id = self.brbe.split(":")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001569
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001570 br = BuildRequest.objects.get(pk = br_id)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001571
1572 # if we're 'done' because we got cancelled update the build outcome
1573 if br.state == BuildRequest.REQ_CANCELLING:
1574 logger.info("Build cancelled")
1575 br.build.outcome = Build.CANCELLED
1576 br.build.save()
1577 self.internal_state['build'] = br.build
1578 errorcode = 0
1579
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001580 if errorcode == 0:
1581 # request archival of the project artifacts
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001582 br.state = BuildRequest.REQ_COMPLETED
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001583 else:
1584 br.state = BuildRequest.REQ_FAILED
1585 br.save()
1586
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001587 be = BuildEnvironment.objects.get(pk = be_id)
1588 be.lock = BuildEnvironment.LOCK_FREE
1589 be.save()
1590 signal_runbuilds()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001591
1592 def store_log_error(self, text):
1593 mockevent = MockEvent()
1594 mockevent.levelno = formatter.ERROR
1595 mockevent.msg = text
1596 mockevent.pathname = '-- None'
1597 mockevent.lineno = LogMessage.ERROR
1598 self.store_log_event(mockevent)
1599
1600 def store_log_exception(self, text, backtrace = ""):
1601 mockevent = MockEvent()
1602 mockevent.levelno = -1
1603 mockevent.msg = text
1604 mockevent.pathname = backtrace
1605 mockevent.lineno = -1
1606 self.store_log_event(mockevent)
1607
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001608 def store_log_event(self, event,cli_backlog=True):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001609 self._ensure_build()
1610
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001611 if event.levelno < formatter.WARNING:
1612 return
1613
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001614 # early return for CLI builds
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001615 if cli_backlog and self.brbe is None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001616 if not 'backlog' in self.internal_state:
1617 self.internal_state['backlog'] = []
1618 self.internal_state['backlog'].append(event)
1619 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001620
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001621 if 'backlog' in self.internal_state:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001622 # if we have a backlog of events, do our best to save them here
Andrew Geissler595f6302022-01-24 19:11:47 +00001623 if self.internal_state['backlog']:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001624 tempevent = self.internal_state['backlog'].pop()
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001625 logger.debug("buildinfohelper: Saving stored event %s "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001626 % tempevent)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001627 self.store_log_event(tempevent,cli_backlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001628 else:
1629 logger.info("buildinfohelper: All events saved")
1630 del self.internal_state['backlog']
1631
1632 log_information = {}
1633 log_information['build'] = self.internal_state['build']
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001634 if event.levelno == formatter.CRITICAL:
1635 log_information['level'] = LogMessage.CRITICAL
1636 elif event.levelno == formatter.ERROR:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001637 log_information['level'] = LogMessage.ERROR
1638 elif event.levelno == formatter.WARNING:
1639 log_information['level'] = LogMessage.WARNING
1640 elif event.levelno == -2: # toaster self-logging
1641 log_information['level'] = -2
1642 else:
1643 log_information['level'] = LogMessage.INFO
1644
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001645 log_information['message'] = event.getMessage()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001646 log_information['pathname'] = event.pathname
1647 log_information['lineno'] = event.lineno
1648 logger.info("Logging error 2: %s", log_information)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001649
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001650 self.orm_wrapper.create_logmessage(log_information)
1651
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001652 def _get_filenames_from_image_license(self, image_license_manifest_path):
1653 """
1654 Find the FILES line in the image_license.manifest file,
1655 which has the basenames of the bzImage and modules files
1656 in this format:
1657 FILES: bzImage--4.4.11+git0+3a5f494784_53e84104c5-r0-qemux86-20160603165040.bin modules--4.4.11+git0+3a5f494784_53e84104c5-r0-qemux86-20160603165040.tgz
1658 """
1659 files = []
1660 with open(image_license_manifest_path) as image_license:
1661 for line in image_license:
1662 if line.startswith('FILES'):
1663 files_str = line.split(':')[1].strip()
1664 files_str = re.sub(r' {2,}', ' ', files_str)
1665
1666 # ignore lines like "FILES:" with no filenames
1667 if files_str:
1668 files += files_str.split(' ')
1669 return files
1670
1671 def _endswith(self, str_to_test, endings):
1672 """
1673 Returns True if str ends with one of the strings in the list
1674 endings, False otherwise
1675 """
1676 endswith = False
1677 for ending in endings:
1678 if str_to_test.endswith(ending):
1679 endswith = True
1680 break
1681 return endswith
1682
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001683 def scan_task_artifacts(self, event):
1684 """
1685 The 'TaskArtifacts' event passes the manifest file content for the
1686 tasks 'do_deploy', 'do_image_complete', 'do_populate_sdk', and
1687 'do_populate_sdk_ext'. The first two will be implemented later.
1688 """
1689 task_vars = BuildInfoHelper._get_data_from_event(event)
1690 task_name = task_vars['task'][task_vars['task'].find(':')+1:]
1691 task_artifacts = task_vars['artifacts']
1692
1693 if task_name in ['do_populate_sdk', 'do_populate_sdk_ext']:
1694 targets = [target for target in self.internal_state['targets'] \
1695 if target.task == task_name[3:]]
1696 if not targets:
1697 logger.warning("scan_task_artifacts: SDK targets not found: %s\n", task_name)
1698 return
1699 for artifact_path in task_artifacts:
1700 if not os.path.isfile(artifact_path):
1701 logger.warning("scan_task_artifacts: artifact file not found: %s\n", artifact_path)
1702 continue
1703 for target in targets:
1704 # don't record the file if it's already been added
1705 # to this target
1706 matching_files = TargetSDKFile.objects.filter(
1707 target=target, file_name=artifact_path)
1708 if matching_files.count() == 0:
1709 artifact_size = os.stat(artifact_path).st_size
1710 self.orm_wrapper.save_target_sdk_file(
1711 target, artifact_path, artifact_size)
1712
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001713 def _get_image_files(self, deploy_dir_image, image_name, image_file_extensions):
1714 """
1715 Find files in deploy_dir_image whose basename starts with the
1716 string image_name and ends with one of the strings in
1717 image_file_extensions.
1718
1719 Returns a list of file dictionaries like
1720
1721 [
1722 {
1723 'path': '/path/to/image/file',
1724 'size': <file size in bytes>
1725 }
1726 ]
1727 """
1728 image_files = []
1729
1730 for dirpath, _, filenames in os.walk(deploy_dir_image):
1731 for filename in filenames:
1732 if filename.startswith(image_name) and \
1733 self._endswith(filename, image_file_extensions):
1734 image_file_path = os.path.join(dirpath, filename)
1735 image_file_size = os.stat(image_file_path).st_size
1736
1737 image_files.append({
1738 'path': image_file_path,
1739 'size': image_file_size
1740 })
1741
1742 return image_files
1743
1744 def scan_image_artifacts(self):
1745 """
1746 Scan for built image artifacts in DEPLOY_DIR_IMAGE and associate them
1747 with a Target object in self.internal_state['targets'].
1748
1749 We have two situations to handle:
1750
1751 1. This is the first time a target + machine has been built, so
1752 add files from the DEPLOY_DIR_IMAGE to the target.
1753
1754 OR
1755
1756 2. There are no new files for the target (they were already produced by
1757 a previous build), so copy them from the most recent previous build with
1758 the same target, task and machine.
1759 """
1760 deploy_dir_image = \
1761 self.server.runCommand(['getVariable', 'DEPLOY_DIR_IMAGE'])[0]
1762
1763 # if there's no DEPLOY_DIR_IMAGE, there aren't going to be
1764 # any image artifacts, so we can return immediately
1765 if not deploy_dir_image:
1766 return
1767
1768 buildname = self.server.runCommand(['getVariable', 'BUILDNAME'])[0]
1769 machine = self.server.runCommand(['getVariable', 'MACHINE'])[0]
1770 image_name = self.server.runCommand(['getVariable', 'IMAGE_NAME'])[0]
1771
1772 # location of the manifest files for this build;
1773 # note that this file is only produced if an image is produced
1774 license_directory = \
1775 self.server.runCommand(['getVariable', 'LICENSE_DIRECTORY'])[0]
1776
1777 # file name extensions for image files
1778 image_file_extensions_unique = {}
1779 image_fstypes = self.server.runCommand(
1780 ['getVariable', 'IMAGE_FSTYPES'])[0]
Andrew Geissler82c905d2020-04-13 13:39:40 -05001781 if image_fstypes is not None:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001782 image_types_str = image_fstypes.strip()
1783 image_file_extensions = re.sub(r' {2,}', ' ', image_types_str)
1784 image_file_extensions_unique = set(image_file_extensions.split(' '))
1785
1786 targets = self.internal_state['targets']
1787
1788 # filter out anything which isn't an image target
1789 image_targets = [target for target in targets if target.is_image]
1790
1791 for image_target in image_targets:
1792 # this is set to True if we find at least one file relating to
1793 # this target; if this remains False after the scan, we copy the
1794 # files from the most-recent Target with the same target + machine
1795 # onto this Target instead
1796 has_files = False
1797
1798 # we construct this because by the time we reach
1799 # BuildCompleted, this has reset to
1800 # 'defaultpkgname-<MACHINE>-<BUILDNAME>';
1801 # we need to change it to
1802 # <TARGET>-<MACHINE>-<BUILDNAME>
1803 real_image_name = re.sub(r'^defaultpkgname', image_target.target,
1804 image_name)
1805
1806 image_license_manifest_path = os.path.join(
1807 license_directory,
1808 real_image_name,
1809 'image_license.manifest')
1810
1811 image_package_manifest_path = os.path.join(
1812 license_directory,
1813 real_image_name,
1814 'image_license.manifest')
1815
1816 # if image_license.manifest exists, we can read the names of
1817 # bzImage, modules etc. files for this build from it, then look for
1818 # them in the DEPLOY_DIR_IMAGE; note that this file is only produced
1819 # if an image file was produced
1820 if os.path.isfile(image_license_manifest_path):
1821 has_files = True
1822
1823 basenames = self._get_filenames_from_image_license(
1824 image_license_manifest_path)
1825
1826 for basename in basenames:
1827 artifact_path = os.path.join(deploy_dir_image, basename)
1828 if not os.path.exists(artifact_path):
1829 logger.warning("artifact %s doesn't exist, skipping" % artifact_path)
1830 continue
1831 artifact_size = os.stat(artifact_path).st_size
1832
1833 # note that the artifact will only be saved against this
1834 # build if it hasn't been already
1835 self.orm_wrapper.save_target_kernel_file(image_target,
1836 artifact_path, artifact_size)
1837
1838 # store the license manifest path on the target
1839 # (this file is also created any time an image file is created)
1840 license_manifest_path = os.path.join(license_directory,
1841 real_image_name, 'license.manifest')
1842
1843 self.orm_wrapper.update_target_set_license_manifest(
1844 image_target, license_manifest_path)
1845
1846 # store the package manifest path on the target (this file
1847 # is created any time an image file is created)
1848 package_manifest_path = os.path.join(deploy_dir_image,
1849 real_image_name + '.rootfs.manifest')
1850
1851 if os.path.exists(package_manifest_path):
1852 self.orm_wrapper.update_target_set_package_manifest(
1853 image_target, package_manifest_path)
1854
1855 # scan the directory for image files relating to this build
1856 # (via real_image_name); note that we don't have to set
1857 # has_files = True, as searching for the license manifest file
1858 # will already have set it to true if at least one image file was
1859 # produced; note that the real_image_name includes BUILDNAME, which
1860 # in turn includes a timestamp; so if no files were produced for
1861 # this timestamp (i.e. the build reused existing image files already
1862 # in the directory), no files will be recorded against this target
1863 image_files = self._get_image_files(deploy_dir_image,
1864 real_image_name, image_file_extensions_unique)
1865
1866 for image_file in image_files:
1867 self.orm_wrapper.save_target_image_file_information(
1868 image_target, image_file['path'], image_file['size'])
1869
1870 if not has_files:
1871 # copy image files and build artifacts from the
1872 # most-recently-built Target with the
1873 # same target + machine as this Target; also copy the license
1874 # manifest path, as that is not treated as an artifact and needs
1875 # to be set separately
1876 similar_target = \
1877 self.orm_wrapper.get_similar_target_with_image_files(
1878 image_target)
1879
1880 if similar_target:
1881 logger.info('image artifacts for target %s cloned from ' \
1882 'target %s' % (image_target.pk, similar_target.pk))
1883 self.orm_wrapper.clone_image_artifacts(similar_target,
1884 image_target)
1885
1886 def _get_sdk_targets(self):
1887 """
1888 Return targets which could generate SDK artifacts, i.e.
1889 "do_populate_sdk" and "do_populate_sdk_ext".
1890 """
1891 return [target for target in self.internal_state['targets'] \
1892 if target.task in ['populate_sdk', 'populate_sdk_ext']]
1893
1894 def scan_sdk_artifacts(self, event):
1895 """
1896 Note that we have to intercept an SDKArtifactInfo event from
1897 toaster.bbclass (via toasterui) to get hold of the SDK variables we
1898 need to be able to scan for files accurately: this is because
1899 variables like TOOLCHAIN_OUTPUTNAME have reset to None by the time
1900 BuildCompleted is fired by bitbake, so we have to get those values
1901 while the build is still in progress.
1902
1903 For populate_sdk_ext, this runs twice, with two different
1904 TOOLCHAIN_OUTPUTNAME settings, each of which will capture some of the
1905 files in the SDK output directory.
1906 """
1907 sdk_vars = BuildInfoHelper._get_data_from_event(event)
1908 toolchain_outputname = sdk_vars['TOOLCHAIN_OUTPUTNAME']
1909
1910 # targets which might have created SDK artifacts
1911 sdk_targets = self._get_sdk_targets()
1912
1913 # location of SDK artifacts
1914 tmpdir = self.server.runCommand(['getVariable', 'TMPDIR'])[0]
1915 sdk_dir = os.path.join(tmpdir, 'deploy', 'sdk')
1916
1917 # all files in the SDK directory
1918 artifacts = []
1919 for dir_path, _, filenames in os.walk(sdk_dir):
1920 for filename in filenames:
1921 full_path = os.path.join(dir_path, filename)
1922 if not os.path.islink(full_path):
1923 artifacts.append(full_path)
1924
1925 for sdk_target in sdk_targets:
1926 # find files in the SDK directory which haven't already been
1927 # recorded against a Target and whose basename matches
1928 # TOOLCHAIN_OUTPUTNAME
1929 for artifact_path in artifacts:
1930 basename = os.path.basename(artifact_path)
1931
1932 toolchain_match = basename.startswith(toolchain_outputname)
1933
1934 # files which match the name of the target which produced them;
1935 # for example,
1936 # poky-glibc-x86_64-core-image-sato-i586-toolchain-ext-2.1+snapshot.sh
1937 target_match = re.search(sdk_target.target, basename)
1938
1939 # targets which produce "*-nativesdk-*" files
1940 is_ext_sdk_target = sdk_target.task in \
1941 ['do_populate_sdk_ext', 'populate_sdk_ext']
1942
1943 # SDK files which don't match the target name, i.e.
1944 # x86_64-nativesdk-libc.*
1945 # poky-glibc-x86_64-buildtools-tarball-i586-buildtools-nativesdk-standalone-2.1+snapshot*
1946 is_ext_sdk_file = re.search('-nativesdk-', basename)
1947
1948 file_from_target = (toolchain_match and target_match) or \
1949 (is_ext_sdk_target and is_ext_sdk_file)
1950
1951 if file_from_target:
1952 # don't record the file if it's already been added to this
1953 # target
1954 matching_files = TargetSDKFile.objects.filter(
1955 target=sdk_target, file_name=artifact_path)
1956
1957 if matching_files.count() == 0:
1958 artifact_size = os.stat(artifact_path).st_size
1959
1960 self.orm_wrapper.save_target_sdk_file(
1961 sdk_target, artifact_path, artifact_size)
1962
1963 def clone_required_sdk_artifacts(self):
1964 """
1965 If an SDK target doesn't have any SDK artifacts, this means that
1966 the postfuncs of populate_sdk or populate_sdk_ext didn't fire, which
1967 in turn means that the targets of this build didn't generate any new
1968 artifacts.
1969
1970 In this case, clone SDK artifacts for targets in the current build
1971 from existing targets for this build.
1972 """
1973 sdk_targets = self._get_sdk_targets()
1974 for sdk_target in sdk_targets:
1975 # only clone for SDK targets which have no TargetSDKFiles yet
1976 if sdk_target.targetsdkfile_set.all().count() == 0:
1977 similar_target = \
1978 self.orm_wrapper.get_similar_target_with_sdk_files(
1979 sdk_target)
1980 if similar_target:
1981 logger.info('SDK artifacts for target %s cloned from ' \
1982 'target %s' % (sdk_target.pk, similar_target.pk))
1983 self.orm_wrapper.clone_sdk_artifacts(similar_target,
1984 sdk_target)
1985
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001986 def close(self, errorcode):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001987 self._store_build_done(errorcode)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001988
1989 if 'backlog' in self.internal_state:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001990 # we save missed events in the database for the current build
1991 tempevent = self.internal_state['backlog'].pop()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001992 # Do not skip command line build events
1993 self.store_log_event(tempevent,False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001994
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001995
1996 # unset the brbe; this is to prevent subsequent command-line builds
1997 # being incorrectly attached to the previous Toaster-triggered build;
1998 # see https://bugzilla.yoctoproject.org/show_bug.cgi?id=9021
1999 self.brbe = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002000
2001 # unset the internal Build object to prevent it being reused for the
2002 # next build
2003 self.internal_state['build'] = None