12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003 |
- #
- # BitBake ToasterUI Implementation
- #
- # Copyright (C) 2013 Intel Corporation
- #
- # SPDX-License-Identifier: GPL-2.0-only
- #
- import sys
- import bb
- import re
- import os
- import django
- from django.utils import timezone
- import toaster
- # Add toaster module to the search path to help django.setup() find the right
- # modules
- sys.path.insert(0, os.path.dirname(toaster.__file__))
- #Set the DJANGO_SETTINGS_MODULE if it's not already set
- os.environ["DJANGO_SETTINGS_MODULE"] =\
- os.environ.get("DJANGO_SETTINGS_MODULE",
- "toaster.toastermain.settings")
- # Setup django framework (needs to be done before importing modules)
- django.setup()
- from orm.models import Build, Task, Recipe, Layer_Version, Layer, Target, LogMessage, HelpText
- from orm.models import Target_Image_File, TargetKernelFile, TargetSDKFile
- from orm.models import Variable, VariableHistory
- from orm.models import Package, Package_File, Target_Installed_Package, Target_File
- from orm.models import Task_Dependency, Package_Dependency
- from orm.models import Recipe_Dependency, Provides
- from orm.models import Project, CustomImagePackage
- from orm.models import signal_runbuilds
- from bldcontrol.models import BuildEnvironment, BuildRequest
- from bldcontrol.models import BRLayer
- from bldcontrol import bbcontroller
- from bb.msg import BBLogFormatter as formatter
- from django.db import models
- from pprint import pformat
- import logging
- from datetime import datetime, timedelta
- from django.db import transaction, connection
- # pylint: disable=invalid-name
- # the logger name is standard throughout BitBake
- logger = logging.getLogger("ToasterLogger")
- class NotExisting(Exception):
- pass
- class ORMWrapper(object):
- """ This class creates the dictionaries needed to store information in the database
- following the format defined by the Django models. It is also used to save this
- information in the database.
- """
- def __init__(self):
- self.layer_version_objects = []
- self.layer_version_built = []
- self.task_objects = {}
- self.recipe_objects = {}
- @staticmethod
- def _build_key(**kwargs):
- key = "0"
- for k in sorted(kwargs.keys()):
- if isinstance(kwargs[k], models.Model):
- key += "-%d" % kwargs[k].id
- else:
- key += "-%s" % str(kwargs[k])
- return key
- def _cached_get_or_create(self, clazz, **kwargs):
- """ This is a memory-cached get_or_create. We assume that the objects will not be created in the
- database through any other means.
- """
- assert issubclass(clazz, models.Model), "_cached_get_or_create needs to get the class as first argument"
- key = ORMWrapper._build_key(**kwargs)
- dictname = "objects_%s" % clazz.__name__
- if not dictname in vars(self).keys():
- vars(self)[dictname] = {}
- created = False
- if not key in vars(self)[dictname].keys():
- vars(self)[dictname][key], created = \
- clazz.objects.get_or_create(**kwargs)
- return (vars(self)[dictname][key], created)
- def _cached_get(self, clazz, **kwargs):
- """ This is a memory-cached get. We assume that the objects will not change in the database between gets.
- """
- assert issubclass(clazz, models.Model), "_cached_get needs to get the class as first argument"
- key = ORMWrapper._build_key(**kwargs)
- dictname = "objects_%s" % clazz.__name__
- if not dictname in vars(self).keys():
- vars(self)[dictname] = {}
- if not key in vars(self)[dictname].keys():
- vars(self)[dictname][key] = clazz.objects.get(**kwargs)
- return vars(self)[dictname][key]
- def get_similar_target_with_image_files(self, target):
- """
- Get a Target object "similar" to target; i.e. with the same target
- name ('core-image-minimal' etc.) and machine.
- """
- return target.get_similar_target_with_image_files()
- def get_similar_target_with_sdk_files(self, target):
- return target.get_similar_target_with_sdk_files()
- def clone_image_artifacts(self, target_from, target_to):
- target_to.clone_image_artifacts_from(target_from)
- def clone_sdk_artifacts(self, target_from, target_to):
- target_to.clone_sdk_artifacts_from(target_from)
- def _timestamp_to_datetime(self, secs):
- """
- Convert timestamp in seconds to Python datetime
- """
- return timezone.make_aware(datetime(1970, 1, 1) + timedelta(seconds=secs))
- # pylint: disable=no-self-use
- # we disable detection of no self use in functions because the methods actually work on the object
- # even if they don't touch self anywhere
- # pylint: disable=bad-continuation
- # we do not follow the python conventions for continuation indentation due to long lines here
- def get_or_create_build_object(self, brbe):
- prj = None
- buildrequest = None
- if brbe is not None:
- # Toaster-triggered build
- logger.debug(1, "buildinfohelper: brbe is %s" % brbe)
- br, _ = brbe.split(":")
- buildrequest = BuildRequest.objects.get(pk=br)
- prj = buildrequest.project
- else:
- # CLI build
- prj = Project.objects.get_or_create_default_project()
- logger.debug(1, "buildinfohelper: project is not specified, defaulting to %s" % prj)
- if buildrequest is not None:
- # reuse existing Build object
- build = buildrequest.build
- build.project = prj
- build.save()
- else:
- # create new Build object
- now = timezone.now()
- build = Build.objects.create(
- project=prj,
- started_on=now,
- completed_on=now,
- build_name='')
- logger.debug(1, "buildinfohelper: build is created %s" % build)
- if buildrequest is not None:
- buildrequest.build = build
- buildrequest.save()
- return build
- def update_build(self, build, data_dict):
- for key in data_dict:
- setattr(build, key, data_dict[key])
- build.save()
- @staticmethod
- def get_or_create_targets(target_info):
- """
- NB get_or_create() is used here because for Toaster-triggered builds,
- we already created the targets when the build was triggered.
- """
- result = []
- for target in target_info['targets']:
- task = ''
- if ':' in target:
- target, task = target.split(':', 1)
- if task.startswith('do_'):
- task = task[3:]
- if task == 'build':
- task = ''
- obj, _ = Target.objects.get_or_create(build=target_info['build'],
- target=target,
- task=task)
- result.append(obj)
- return result
- def update_build_stats_and_outcome(self, build, errors, warnings, taskfailures):
- assert isinstance(build,Build)
- assert isinstance(errors, int)
- assert isinstance(warnings, int)
- if build.outcome == Build.CANCELLED:
- return
- try:
- if build.buildrequest.state == BuildRequest.REQ_CANCELLING:
- return
- except AttributeError:
- # We may not have a buildrequest if this is a command line build
- pass
- outcome = Build.SUCCEEDED
- if errors or taskfailures:
- outcome = Build.FAILED
- build.completed_on = timezone.now()
- build.outcome = outcome
- build.save()
- signal_runbuilds()
- def update_target_set_license_manifest(self, target, license_manifest_path):
- target.license_manifest_path = license_manifest_path
- target.save()
- def update_target_set_package_manifest(self, target, package_manifest_path):
- target.package_manifest_path = package_manifest_path
- target.save()
- def update_task_object(self, build, task_name, recipe_name, task_stats):
- """
- Find the task for build which matches the recipe and task name
- to be stored
- """
- task_to_update = Task.objects.get(
- build = build,
- task_name = task_name,
- recipe__name = recipe_name
- )
- if 'started' in task_stats and 'ended' in task_stats:
- task_to_update.started = self._timestamp_to_datetime(task_stats['started'])
- task_to_update.ended = self._timestamp_to_datetime(task_stats['ended'])
- task_to_update.elapsed_time = (task_stats['ended'] - task_stats['started'])
- task_to_update.cpu_time_user = task_stats.get('cpu_time_user')
- task_to_update.cpu_time_system = task_stats.get('cpu_time_system')
- if 'disk_io_read' in task_stats and 'disk_io_write' in task_stats:
- task_to_update.disk_io_read = task_stats['disk_io_read']
- task_to_update.disk_io_write = task_stats['disk_io_write']
- task_to_update.disk_io = task_stats['disk_io_read'] + task_stats['disk_io_write']
- task_to_update.save()
- def get_update_task_object(self, task_information, must_exist = False):
- assert 'build' in task_information
- assert 'recipe' in task_information
- assert 'task_name' in task_information
- # we use must_exist info for database look-up optimization
- task_object, created = self._cached_get_or_create(Task,
- build=task_information['build'],
- recipe=task_information['recipe'],
- task_name=task_information['task_name']
- )
- if created and must_exist:
- task_information['debug'] = "build id %d, recipe id %d" % (task_information['build'].pk, task_information['recipe'].pk)
- raise NotExisting("Task object created when expected to exist", task_information)
- object_changed = False
- for v in vars(task_object):
- if v in task_information.keys():
- if vars(task_object)[v] != task_information[v]:
- vars(task_object)[v] = task_information[v]
- object_changed = True
- # update setscene-related information if the task has a setscene
- if task_object.outcome == Task.OUTCOME_COVERED and 1 == task_object.get_related_setscene().count():
- task_object.outcome = Task.OUTCOME_CACHED
- object_changed = True
- outcome_task_setscene = Task.objects.get(task_executed=True, build = task_object.build,
- recipe = task_object.recipe, task_name=task_object.task_name+"_setscene").outcome
- if outcome_task_setscene == Task.OUTCOME_SUCCESS:
- task_object.sstate_result = Task.SSTATE_RESTORED
- object_changed = True
- elif outcome_task_setscene == Task.OUTCOME_FAILED:
- task_object.sstate_result = Task.SSTATE_FAILED
- object_changed = True
- if object_changed:
- task_object.save()
- return task_object
- def get_update_recipe_object(self, recipe_information, must_exist = False):
- assert 'layer_version' in recipe_information
- assert 'file_path' in recipe_information
- assert 'pathflags' in recipe_information
- assert not recipe_information['file_path'].startswith("/") # we should have layer-relative paths at all times
- def update_recipe_obj(recipe_object):
- object_changed = False
- for v in vars(recipe_object):
- if v in recipe_information.keys():
- object_changed = True
- vars(recipe_object)[v] = recipe_information[v]
- if object_changed:
- recipe_object.save()
- recipe, created = self._cached_get_or_create(Recipe, layer_version=recipe_information['layer_version'],
- file_path=recipe_information['file_path'], pathflags = recipe_information['pathflags'])
- update_recipe_obj(recipe)
- built_recipe = None
- # Create a copy of the recipe for historical puposes and update it
- for built_layer in self.layer_version_built:
- if built_layer.layer == recipe_information['layer_version'].layer:
- built_recipe, c = self._cached_get_or_create(Recipe,
- layer_version=built_layer,
- file_path=recipe_information['file_path'],
- pathflags = recipe_information['pathflags'])
- update_recipe_obj(built_recipe)
- break
- # If we're in analysis mode or if this is a custom recipe
- # then we are wholly responsible for the data
- # and therefore we return the 'real' recipe rather than the build
- # history copy of the recipe.
- if recipe_information['layer_version'].build is not None and \
- recipe_information['layer_version'].build.project == \
- Project.objects.get_or_create_default_project():
- return recipe
- if built_recipe is None:
- return recipe
- return built_recipe
- def get_update_layer_version_object(self, build_obj, layer_obj, layer_version_information):
- if isinstance(layer_obj, Layer_Version):
- # We already found our layer version for this build so just
- # update it with the new build information
- logger.debug("We found our layer from toaster")
- layer_obj.local_path = layer_version_information['local_path']
- layer_obj.save()
- self.layer_version_objects.append(layer_obj)
- # create a new copy of this layer version as a snapshot for
- # historical purposes
- layer_copy, c = Layer_Version.objects.get_or_create(
- build=build_obj,
- layer=layer_obj.layer,
- release=layer_obj.release,
- branch=layer_version_information['branch'],
- commit=layer_version_information['commit'],
- local_path=layer_version_information['local_path'],
- )
- logger.debug("Created new layer version %s for build history",
- layer_copy.layer.name)
- self.layer_version_built.append(layer_copy)
- return layer_obj
- assert isinstance(build_obj, Build)
- assert isinstance(layer_obj, Layer)
- assert 'branch' in layer_version_information
- assert 'commit' in layer_version_information
- assert 'priority' in layer_version_information
- assert 'local_path' in layer_version_information
- # If we're doing a command line build then associate this new layer with the
- # project to avoid it 'contaminating' toaster data
- project = None
- if build_obj.project == Project.objects.get_or_create_default_project():
- project = build_obj.project
- layer_version_object, _ = Layer_Version.objects.get_or_create(
- build = build_obj,
- layer = layer_obj,
- branch = layer_version_information['branch'],
- commit = layer_version_information['commit'],
- priority = layer_version_information['priority'],
- local_path = layer_version_information['local_path'],
- project=project)
- self.layer_version_objects.append(layer_version_object)
- return layer_version_object
- def get_update_layer_object(self, layer_information, brbe):
- assert 'name' in layer_information
- assert 'layer_index_url' in layer_information
- # From command line builds we have no brbe as the request is directly
- # from bitbake
- if brbe is None:
- # If we don't have git commit sha then we're using a non-git
- # layer so set the layer_source_dir to identify it as such
- if not layer_information['version']['commit']:
- local_source_dir = layer_information["local_path"]
- else:
- local_source_dir = None
- layer_object, _ = \
- Layer.objects.get_or_create(
- name=layer_information['name'],
- local_source_dir=local_source_dir,
- layer_index_url=layer_information['layer_index_url'])
- return layer_object
- else:
- br_id, be_id = brbe.split(":")
- # Find the layer version by matching the layer event information
- # against the metadata we have in Toaster
- try:
- br_layer = BRLayer.objects.get(req=br_id,
- name=layer_information['name'])
- return br_layer.layer_version
- except (BRLayer.MultipleObjectsReturned, BRLayer.DoesNotExist):
- # There are multiple of the same layer name or the name
- # hasn't been determined by the toaster.bbclass layer
- # so let's filter by the local_path
- bc = bbcontroller.getBuildEnvironmentController(pk=be_id)
- for br_layer in BRLayer.objects.filter(req=br_id):
- if br_layer.giturl and \
- layer_information['local_path'].endswith(
- bc.getGitCloneDirectory(br_layer.giturl,
- br_layer.commit)):
- return br_layer.layer_version
- if br_layer.local_source_dir == \
- layer_information['local_path']:
- return br_layer.layer_version
- # We've reached the end of our search and couldn't find the layer
- # we can continue but some data may be missing
- raise NotExisting("Unidentified layer %s" %
- pformat(layer_information))
- def save_target_file_information(self, build_obj, target_obj, filedata):
- assert isinstance(build_obj, Build)
- assert isinstance(target_obj, Target)
- dirs = filedata['dirs']
- files = filedata['files']
- syms = filedata['syms']
- # always create the root directory as a special case;
- # note that this is never displayed, so the owner, group,
- # size, permission are irrelevant
- tf_obj = Target_File.objects.create(target = target_obj,
- path = '/',
- size = 0,
- owner = '',
- group = '',
- permission = '',
- inodetype = Target_File.ITYPE_DIRECTORY)
- tf_obj.save()
- # insert directories, ordered by name depth
- for d in sorted(dirs, key=lambda x:len(x[-1].split("/"))):
- (user, group, size) = d[1:4]
- permission = d[0][1:]
- path = d[4].lstrip(".")
- # we already created the root directory, so ignore any
- # entry for it
- if len(path) == 0:
- continue
- parent_path = "/".join(path.split("/")[:len(path.split("/")) - 1])
- if len(parent_path) == 0:
- parent_path = "/"
- parent_obj = self._cached_get(Target_File, target = target_obj, path = parent_path, inodetype = Target_File.ITYPE_DIRECTORY)
- tf_obj = Target_File.objects.create(
- target = target_obj,
- path = path,
- size = size,
- inodetype = Target_File.ITYPE_DIRECTORY,
- permission = permission,
- owner = user,
- group = group,
- directory = parent_obj)
- # we insert files
- for d in files:
- (user, group, size) = d[1:4]
- permission = d[0][1:]
- path = d[4].lstrip(".")
- parent_path = "/".join(path.split("/")[:len(path.split("/")) - 1])
- inodetype = Target_File.ITYPE_REGULAR
- if d[0].startswith('b'):
- inodetype = Target_File.ITYPE_BLOCK
- if d[0].startswith('c'):
- inodetype = Target_File.ITYPE_CHARACTER
- if d[0].startswith('p'):
- inodetype = Target_File.ITYPE_FIFO
- tf_obj = Target_File.objects.create(
- target = target_obj,
- path = path,
- size = size,
- inodetype = inodetype,
- permission = permission,
- owner = user,
- group = group)
- parent_obj = self._cached_get(Target_File, target = target_obj, path = parent_path, inodetype = Target_File.ITYPE_DIRECTORY)
- tf_obj.directory = parent_obj
- tf_obj.save()
- # we insert symlinks
- for d in syms:
- (user, group, size) = d[1:4]
- permission = d[0][1:]
- path = d[4].lstrip(".")
- filetarget_path = d[6]
- parent_path = "/".join(path.split("/")[:len(path.split("/")) - 1])
- if not filetarget_path.startswith("/"):
- # we have a relative path, get a normalized absolute one
- filetarget_path = parent_path + "/" + filetarget_path
- fcp = filetarget_path.split("/")
- fcpl = []
- for i in fcp:
- if i == "..":
- fcpl.pop()
- else:
- fcpl.append(i)
- filetarget_path = "/".join(fcpl)
- try:
- filetarget_obj = Target_File.objects.get(target = target_obj, path = filetarget_path)
- except Target_File.DoesNotExist:
- # we might have an invalid link; no way to detect this. just set it to None
- filetarget_obj = None
- parent_obj = Target_File.objects.get(target = target_obj, path = parent_path, inodetype = Target_File.ITYPE_DIRECTORY)
- tf_obj = Target_File.objects.create(
- target = target_obj,
- path = path,
- size = size,
- inodetype = Target_File.ITYPE_SYMLINK,
- permission = permission,
- owner = user,
- group = group,
- directory = parent_obj,
- sym_target = filetarget_obj)
- def save_target_package_information(self, build_obj, target_obj, packagedict, pkgpnmap, recipes, built_package=False):
- assert isinstance(build_obj, Build)
- assert isinstance(target_obj, Target)
- errormsg = ""
- for p in packagedict:
- # Search name swtiches round the installed name vs package name
- # by default installed name == package name
- searchname = p
- if p not in pkgpnmap:
- logger.warning("Image packages list contains %p, but is"
- " missing from all packages list where the"
- " metadata comes from. Skipping...", p)
- continue
- if 'OPKGN' in pkgpnmap[p].keys():
- searchname = pkgpnmap[p]['OPKGN']
- built_recipe = recipes[pkgpnmap[p]['PN']]
- if built_package:
- packagedict[p]['object'], created = Package.objects.get_or_create( build = build_obj, name = searchname )
- recipe = built_recipe
- else:
- packagedict[p]['object'], created = \
- CustomImagePackage.objects.get_or_create(name=searchname)
- # Clear the Package_Dependency objects as we're going to update
- # the CustomImagePackage with the latest dependency information
- packagedict[p]['object'].package_dependencies_target.all().delete()
- packagedict[p]['object'].package_dependencies_source.all().delete()
- try:
- recipe = self._cached_get(
- Recipe,
- name=built_recipe.name,
- layer_version__build=None,
- layer_version__release=
- built_recipe.layer_version.release,
- file_path=built_recipe.file_path,
- version=built_recipe.version
- )
- except (Recipe.DoesNotExist,
- Recipe.MultipleObjectsReturned) as e:
- logger.info("We did not find one recipe for the"
- "configuration data package %s %s" % (p, e))
- continue
- 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]
- # fill in everything we can from the runtime-reverse package data
- try:
- packagedict[p]['object'].recipe = recipe
- packagedict[p]['object'].version = pkgpnmap[p]['PV']
- packagedict[p]['object'].installed_name = p
- packagedict[p]['object'].revision = pkgpnmap[p]['PR']
- packagedict[p]['object'].license = pkgpnmap[p]['LICENSE']
- packagedict[p]['object'].section = pkgpnmap[p]['SECTION']
- packagedict[p]['object'].summary = pkgpnmap[p]['SUMMARY']
- packagedict[p]['object'].description = pkgpnmap[p]['DESCRIPTION']
- packagedict[p]['object'].size = int(pkgpnmap[p]['PKGSIZE'])
- # no files recorded for this package, so save files info
- packagefile_objects = []
- for targetpath in pkgpnmap[p]['FILES_INFO']:
- targetfilesize = pkgpnmap[p]['FILES_INFO'][targetpath]
- packagefile_objects.append(Package_File( package = packagedict[p]['object'],
- path = targetpath,
- size = targetfilesize))
- if len(packagefile_objects):
- Package_File.objects.bulk_create(packagefile_objects)
- except KeyError as e:
- errormsg += " stpi: Key error, package %s key %s \n" % ( p, e )
- # save disk installed size
- packagedict[p]['object'].installed_size = packagedict[p]['size']
- packagedict[p]['object'].save()
- if built_package:
- Target_Installed_Package.objects.create(target = target_obj, package = packagedict[p]['object'])
- packagedeps_objs = []
- pattern_so = re.compile(r'.*\.so(\.\d*)?$')
- pattern_lib = re.compile(r'.*\-suffix(\d*)?$')
- pattern_ko = re.compile(r'^kernel-module-.*')
- for p in packagedict:
- for (px,deptype) in packagedict[p]['depends']:
- if deptype == 'depends':
- tdeptype = Package_Dependency.TYPE_TRDEPENDS
- elif deptype == 'recommends':
- tdeptype = Package_Dependency.TYPE_TRECOMMENDS
- try:
- # Skip known non-package objects like libraries and kernel modules
- if pattern_so.match(px) or pattern_lib.match(px):
- logger.info("Toaster does not add library file dependencies to packages (%s,%s)", p, px)
- continue
- if pattern_ko.match(px):
- logger.info("Toaster does not add kernel module dependencies to packages (%s,%s)", p, px)
- continue
- packagedeps_objs.append(Package_Dependency(
- package = packagedict[p]['object'],
- depends_on = packagedict[px]['object'],
- dep_type = tdeptype,
- target = target_obj))
- except KeyError as e:
- logger.warning("Could not add dependency to the package %s "
- "because %s is an unknown package", p, px)
- if len(packagedeps_objs) > 0:
- Package_Dependency.objects.bulk_create(packagedeps_objs)
- else:
- logger.info("No package dependencies created")
- if len(errormsg) > 0:
- logger.warning("buildinfohelper: target_package_info could not identify recipes: \n%s", errormsg)
- def save_target_image_file_information(self, target_obj, file_name, file_size):
- Target_Image_File.objects.create(target=target_obj,
- file_name=file_name, file_size=file_size)
- def save_target_kernel_file(self, target_obj, file_name, file_size):
- """
- Save kernel file (bzImage, modules*) information for a Target target_obj.
- """
- TargetKernelFile.objects.create(target=target_obj,
- file_name=file_name, file_size=file_size)
- def save_target_sdk_file(self, target_obj, file_name, file_size):
- """
- Save SDK artifacts to the database, associating them with a
- Target object.
- """
- TargetSDKFile.objects.create(target=target_obj, file_name=file_name,
- file_size=file_size)
- def create_logmessage(self, log_information):
- assert 'build' in log_information
- assert 'level' in log_information
- assert 'message' in log_information
- log_object = LogMessage.objects.create(
- build = log_information['build'],
- level = log_information['level'],
- message = log_information['message'])
- for v in vars(log_object):
- if v in log_information.keys():
- vars(log_object)[v] = log_information[v]
- return log_object.save()
- def save_build_package_information(self, build_obj, package_info, recipes,
- built_package):
- # assert isinstance(build_obj, Build)
- if not 'PN' in package_info.keys():
- # no package data to save (e.g. 'OPKGN'="lib64-*"|"lib32-*")
- return None
- # create and save the object
- pname = package_info['PKG']
- built_recipe = recipes[package_info['PN']]
- if 'OPKGN' in package_info.keys():
- pname = package_info['OPKGN']
- if built_package:
- bp_object, _ = Package.objects.get_or_create( build = build_obj,
- name = pname )
- recipe = built_recipe
- else:
- bp_object, created = \
- CustomImagePackage.objects.get_or_create(name=pname)
- try:
- recipe = self._cached_get(Recipe,
- name=built_recipe.name,
- layer_version__build=None,
- file_path=built_recipe.file_path,
- version=built_recipe.version)
- except (Recipe.DoesNotExist, Recipe.MultipleObjectsReturned):
- logger.debug("We did not find one recipe for the configuration"
- "data package %s" % pname)
- return
- bp_object.installed_name = package_info['PKG']
- bp_object.recipe = recipe
- bp_object.version = package_info['PKGV']
- bp_object.revision = package_info['PKGR']
- bp_object.summary = package_info['SUMMARY']
- bp_object.description = package_info['DESCRIPTION']
- bp_object.size = int(package_info['PKGSIZE'])
- bp_object.section = package_info['SECTION']
- bp_object.license = package_info['LICENSE']
- bp_object.save()
- # save any attached file information
- packagefile_objects = []
- for path in package_info['FILES_INFO']:
- packagefile_objects.append(Package_File( package = bp_object,
- path = path,
- size = package_info['FILES_INFO'][path] ))
- if len(packagefile_objects):
- Package_File.objects.bulk_create(packagefile_objects)
- def _po_byname(p):
- if built_package:
- pkg, created = Package.objects.get_or_create(build=build_obj,
- name=p)
- else:
- pkg, created = CustomImagePackage.objects.get_or_create(name=p)
- if created:
- pkg.size = -1
- pkg.save()
- return pkg
- packagedeps_objs = []
- # save soft dependency information
- if 'RDEPENDS' in package_info and package_info['RDEPENDS']:
- for p in bb.utils.explode_deps(package_info['RDEPENDS']):
- packagedeps_objs.append(Package_Dependency( package = bp_object,
- depends_on = _po_byname(p), dep_type = Package_Dependency.TYPE_RDEPENDS))
- if 'RPROVIDES' in package_info and package_info['RPROVIDES']:
- for p in bb.utils.explode_deps(package_info['RPROVIDES']):
- packagedeps_objs.append(Package_Dependency( package = bp_object,
- depends_on = _po_byname(p), dep_type = Package_Dependency.TYPE_RPROVIDES))
- if 'RRECOMMENDS' in package_info and package_info['RRECOMMENDS']:
- for p in bb.utils.explode_deps(package_info['RRECOMMENDS']):
- packagedeps_objs.append(Package_Dependency( package = bp_object,
- depends_on = _po_byname(p), dep_type = Package_Dependency.TYPE_RRECOMMENDS))
- if 'RSUGGESTS' in package_info and package_info['RSUGGESTS']:
- for p in bb.utils.explode_deps(package_info['RSUGGESTS']):
- packagedeps_objs.append(Package_Dependency( package = bp_object,
- depends_on = _po_byname(p), dep_type = Package_Dependency.TYPE_RSUGGESTS))
- if 'RREPLACES' in package_info and package_info['RREPLACES']:
- for p in bb.utils.explode_deps(package_info['RREPLACES']):
- packagedeps_objs.append(Package_Dependency( package = bp_object,
- depends_on = _po_byname(p), dep_type = Package_Dependency.TYPE_RREPLACES))
- if 'RCONFLICTS' in package_info and package_info['RCONFLICTS']:
- for p in bb.utils.explode_deps(package_info['RCONFLICTS']):
- packagedeps_objs.append(Package_Dependency( package = bp_object,
- depends_on = _po_byname(p), dep_type = Package_Dependency.TYPE_RCONFLICTS))
- if len(packagedeps_objs) > 0:
- Package_Dependency.objects.bulk_create(packagedeps_objs)
- return bp_object
- def save_build_variables(self, build_obj, vardump):
- assert isinstance(build_obj, Build)
- for k in vardump:
- desc = vardump[k]['doc']
- if desc is None:
- var_words = [word for word in k.split('_')]
- root_var = "_".join([word for word in var_words if word.isupper()])
- if root_var and root_var != k and root_var in vardump:
- desc = vardump[root_var]['doc']
- if desc is None:
- desc = ''
- if len(desc):
- HelpText.objects.get_or_create(build=build_obj,
- area=HelpText.VARIABLE,
- key=k, text=desc)
- if not bool(vardump[k]['func']):
- value = vardump[k]['v']
- if value is None:
- value = ''
- variable_obj = Variable.objects.create( build = build_obj,
- variable_name = k,
- variable_value = value,
- description = desc)
- varhist_objects = []
- for vh in vardump[k]['history']:
- if not 'documentation.conf' in vh['file']:
- varhist_objects.append(VariableHistory( variable = variable_obj,
- file_name = vh['file'],
- line_number = vh['line'],
- operation = vh['op']))
- if len(varhist_objects):
- VariableHistory.objects.bulk_create(varhist_objects)
- class MockEvent(object):
- """ This object is used to create event, for which normal event-processing methods can
- be used, out of data that is not coming via an actual event
- """
- def __init__(self):
- self.msg = None
- self.levelno = None
- self.taskname = None
- self.taskhash = None
- self.pathname = None
- self.lineno = None
- def getMessage(self):
- """
- Simulate LogRecord message return
- """
- return self.msg
- class BuildInfoHelper(object):
- """ This class gathers the build information from the server and sends it
- towards the ORM wrapper for storing in the database
- It is instantiated once per build
- Keeps in memory all data that needs matching before writing it to the database
- """
- # tasks which produce image files; note we include '', as we set
- # the task for a target to '' (i.e. 'build') if no target is
- # explicitly defined
- IMAGE_GENERATING_TASKS = ['', 'build', 'image', 'populate_sdk_ext']
- # pylint: disable=protected-access
- # the code will look into the protected variables of the event; no easy way around this
- # pylint: disable=bad-continuation
- # we do not follow the python conventions for continuation indentation due to long lines here
- def __init__(self, server, has_build_history = False, brbe = None):
- self.internal_state = {}
- self.internal_state['taskdata'] = {}
- self.internal_state['targets'] = []
- self.task_order = 0
- self.autocommit_step = 1
- self.server = server
- # we use manual transactions if the database doesn't autocommit on us
- if not connection.features.autocommits_when_autocommit_is_off:
- transaction.set_autocommit(False)
- self.orm_wrapper = ORMWrapper()
- self.has_build_history = has_build_history
- self.tmp_dir = self.server.runCommand(["getVariable", "TMPDIR"])[0]
- # this is set for Toaster-triggered builds by localhostbecontroller
- # via toasterui
- self.brbe = brbe
- self.project = None
- logger.debug(1, "buildinfohelper: Build info helper inited %s" % vars(self))
- ###################
- ## methods to convert event/external info into objects that the ORM layer uses
- def _ensure_build(self):
- """
- Ensure the current build object exists and is up to date with
- data on the bitbake server
- """
- if not 'build' in self.internal_state or not self.internal_state['build']:
- # create the Build object
- self.internal_state['build'] = \
- self.orm_wrapper.get_or_create_build_object(self.brbe)
- build = self.internal_state['build']
- # update missing fields on the Build object with found data
- build_info = {}
- # set to True if at least one field is going to be set
- changed = False
- if not build.build_name:
- build_name = self.server.runCommand(["getVariable", "BUILDNAME"])[0]
- # only reset the build name if the one on the server is actually
- # a valid value for the build_name field
- if build_name is not None:
- build_info['build_name'] = build_name
- changed = True
- if not build.machine:
- build_info['machine'] = self.server.runCommand(["getVariable", "MACHINE"])[0]
- changed = True
- if not build.distro:
- build_info['distro'] = self.server.runCommand(["getVariable", "DISTRO"])[0]
- changed = True
- if not build.distro_version:
- build_info['distro_version'] = self.server.runCommand(["getVariable", "DISTRO_VERSION"])[0]
- changed = True
- if not build.bitbake_version:
- build_info['bitbake_version'] = self.server.runCommand(["getVariable", "BB_VERSION"])[0]
- changed = True
- if changed:
- self.orm_wrapper.update_build(self.internal_state['build'], build_info)
- def _get_task_information(self, event, recipe):
- assert 'taskname' in vars(event)
- self._ensure_build()
- task_information = {}
- task_information['build'] = self.internal_state['build']
- task_information['outcome'] = Task.OUTCOME_NA
- task_information['recipe'] = recipe
- task_information['task_name'] = event.taskname
- try:
- # some tasks don't come with a hash. and that's ok
- task_information['sstate_checksum'] = event.taskhash
- except AttributeError:
- pass
- return task_information
- def _get_layer_version_for_dependency(self, pathRE):
- """ Returns the layer in the toaster db that has a full regex
- match to the pathRE. pathRE - the layer path passed as a regex in the
- event. It is created in cooker.py as a collection for the layer
- priorities.
- """
- self._ensure_build()
- def _sort_longest_path(layer_version):
- assert isinstance(layer_version, Layer_Version)
- return len(layer_version.local_path)
- # Our paths don't append a trailing slash
- if pathRE.endswith("/"):
- pathRE = pathRE[:-1]
- p = re.compile(pathRE)
- path=re.sub(r'[$^]',r'',pathRE)
- # Heuristics: we always match recipe to the deepest layer path in
- # the discovered layers
- for lvo in sorted(self.orm_wrapper.layer_version_objects,
- reverse=True, key=_sort_longest_path):
- if p.fullmatch(os.path.abspath(lvo.local_path)):
- return lvo
- if lvo.layer.local_source_dir:
- if p.fullmatch(os.path.abspath(lvo.layer.local_source_dir)):
- return lvo
- if 0 == path.find(lvo.local_path):
- # sub-layer path inside existing layer
- return lvo
- # if we get here, we didn't read layers correctly;
- # dump whatever information we have on the error log
- logger.warning("Could not match layer dependency for path %s : %s",
- pathRE,
- self.orm_wrapper.layer_version_objects)
- return None
- def _get_layer_version_for_path(self, path):
- self._ensure_build()
- def _slkey_interactive(layer_version):
- assert isinstance(layer_version, Layer_Version)
- return len(layer_version.local_path)
- # Heuristics: we always match recipe to the deepest layer path in the discovered layers
- for lvo in sorted(self.orm_wrapper.layer_version_objects, reverse=True, key=_slkey_interactive):
- # we can match to the recipe file path
- if path.startswith(lvo.local_path):
- return lvo
- if lvo.layer.local_source_dir and \
- path.startswith(lvo.layer.local_source_dir):
- return lvo
- #if we get here, we didn't read layers correctly; dump whatever information we have on the error log
- logger.warning("Could not match layer version for recipe path %s : %s", path, self.orm_wrapper.layer_version_objects)
- #mockup the new layer
- unknown_layer, _ = Layer.objects.get_or_create(name="Unidentified layer", layer_index_url="")
- unknown_layer_version_obj, _ = Layer_Version.objects.get_or_create(layer = unknown_layer, build = self.internal_state['build'])
- # append it so we don't run into this error again and again
- self.orm_wrapper.layer_version_objects.append(unknown_layer_version_obj)
- return unknown_layer_version_obj
- def _get_recipe_information_from_taskfile(self, taskfile):
- localfilepath = taskfile.split(":")[-1]
- filepath_flags = ":".join(sorted(taskfile.split(":")[:-1]))
- layer_version_obj = self._get_layer_version_for_path(localfilepath)
- recipe_info = {}
- recipe_info['layer_version'] = layer_version_obj
- recipe_info['file_path'] = localfilepath
- recipe_info['pathflags'] = filepath_flags
- if recipe_info['file_path'].startswith(recipe_info['layer_version'].local_path):
- recipe_info['file_path'] = recipe_info['file_path'][len(recipe_info['layer_version'].local_path):].lstrip("/")
- else:
- raise RuntimeError("Recipe file path %s is not under layer version at %s" % (recipe_info['file_path'], recipe_info['layer_version'].local_path))
- return recipe_info
- def _get_path_information(self, task_object):
- self._ensure_build()
- assert isinstance(task_object, Task)
- build_stats_format = "{tmpdir}/buildstats/{buildname}/{package}/"
- build_stats_path = []
- for t in self.internal_state['targets']:
- buildname = self.internal_state['build'].build_name
- pe, pv = task_object.recipe.version.split(":",1)
- if len(pe) > 0:
- package = task_object.recipe.name + "-" + pe + "_" + pv
- else:
- package = task_object.recipe.name + "-" + pv
- build_stats_path.append(build_stats_format.format(tmpdir=self.tmp_dir,
- buildname=buildname,
- package=package))
- return build_stats_path
- ################################
- ## external available methods to store information
- @staticmethod
- def _get_data_from_event(event):
- evdata = None
- if '_localdata' in vars(event):
- evdata = event._localdata
- elif 'data' in vars(event):
- evdata = event.data
- else:
- raise Exception("Event with neither _localdata or data properties")
- return evdata
- def store_layer_info(self, event):
- layerinfos = BuildInfoHelper._get_data_from_event(event)
- self.internal_state['lvs'] = {}
- for layer in layerinfos:
- try:
- self.internal_state['lvs'][self.orm_wrapper.get_update_layer_object(layerinfos[layer], self.brbe)] = layerinfos[layer]['version']
- self.internal_state['lvs'][self.orm_wrapper.get_update_layer_object(layerinfos[layer], self.brbe)]['local_path'] = layerinfos[layer]['local_path']
- except NotExisting as nee:
- logger.warning("buildinfohelper: cannot identify layer exception:%s ", nee)
- def store_started_build(self):
- self._ensure_build()
- def save_build_log_file_path(self, build_log_path):
- self._ensure_build()
- if not self.internal_state['build'].cooker_log_path:
- data_dict = {'cooker_log_path': build_log_path}
- self.orm_wrapper.update_build(self.internal_state['build'], data_dict)
- def save_build_targets(self, event):
- self._ensure_build()
- # create target information
- assert '_pkgs' in vars(event)
- target_information = {}
- target_information['targets'] = event._pkgs
- target_information['build'] = self.internal_state['build']
- self.internal_state['targets'] = self.orm_wrapper.get_or_create_targets(target_information)
- def save_build_layers_and_variables(self):
- self._ensure_build()
- build_obj = self.internal_state['build']
- # save layer version information for this build
- if not 'lvs' in self.internal_state:
- logger.error("Layer version information not found; Check if the bitbake server was configured to inherit toaster.bbclass.")
- else:
- for layer_obj in self.internal_state['lvs']:
- self.orm_wrapper.get_update_layer_version_object(build_obj, layer_obj, self.internal_state['lvs'][layer_obj])
- del self.internal_state['lvs']
- # Save build configuration
- data = self.server.runCommand(["getAllKeysWithFlags", ["doc", "func"]])[0]
- # convert the paths from absolute to relative to either the build directory or layer checkouts
- path_prefixes = []
- if self.brbe is not None:
- _, be_id = self.brbe.split(":")
- be = BuildEnvironment.objects.get(pk = be_id)
- path_prefixes.append(be.builddir)
- for layer in sorted(self.orm_wrapper.layer_version_objects, key = lambda x:len(x.local_path), reverse=True):
- path_prefixes.append(layer.local_path)
- # we strip the prefixes
- for k in data:
- if not bool(data[k]['func']):
- for vh in data[k]['history']:
- if not 'documentation.conf' in vh['file']:
- abs_file_name = vh['file']
- for pp in path_prefixes:
- if abs_file_name.startswith(pp + "/"):
- # preserve layer name in relative path
- vh['file']=abs_file_name[pp.rfind("/")+1:]
- break
- # save the variables
- self.orm_wrapper.save_build_variables(build_obj, data)
- return self.brbe
- def set_recipes_to_parse(self, num_recipes):
- """
- Set the number of recipes which need to be parsed for this build.
- This is set the first time ParseStarted is received by toasterui.
- """
- self._ensure_build()
- self.internal_state['build'].recipes_to_parse = num_recipes
- self.internal_state['build'].save()
- def set_recipes_parsed(self, num_recipes):
- """
- Set the number of recipes parsed so far for this build; this is updated
- each time a ParseProgress or ParseCompleted event is received by
- toasterui.
- """
- self._ensure_build()
- if num_recipes <= self.internal_state['build'].recipes_to_parse:
- self.internal_state['build'].recipes_parsed = num_recipes
- self.internal_state['build'].save()
- def update_target_image_file(self, event):
- evdata = BuildInfoHelper._get_data_from_event(event)
- for t in self.internal_state['targets']:
- if t.is_image:
- output_files = list(evdata.keys())
- for output in output_files:
- if t.target in output and 'rootfs' in output and not output.endswith(".manifest"):
- self.orm_wrapper.save_target_image_file_information(t, output, evdata[output])
- def update_artifact_image_file(self, event):
- self._ensure_build()
- evdata = BuildInfoHelper._get_data_from_event(event)
- for artifact_path in evdata.keys():
- self.orm_wrapper.save_artifact_information(
- self.internal_state['build'], artifact_path,
- evdata[artifact_path])
- def update_build_information(self, event, errors, warnings, taskfailures):
- self._ensure_build()
- self.orm_wrapper.update_build_stats_and_outcome(
- self.internal_state['build'], errors, warnings, taskfailures)
- def store_started_task(self, event):
- assert isinstance(event, (bb.runqueue.sceneQueueTaskStarted, bb.runqueue.runQueueTaskStarted, bb.runqueue.runQueueTaskSkipped))
- assert 'taskfile' in vars(event)
- localfilepath = event.taskfile.split(":")[-1]
- assert localfilepath.startswith("/")
- identifier = event.taskfile + ":" + event.taskname
- recipe_information = self._get_recipe_information_from_taskfile(event.taskfile)
- recipe = self.orm_wrapper.get_update_recipe_object(recipe_information, True)
- task_information = self._get_task_information(event, recipe)
- task_information['outcome'] = Task.OUTCOME_NA
- if isinstance(event, bb.runqueue.runQueueTaskSkipped):
- assert 'reason' in vars(event)
- task_information['task_executed'] = False
- if event.reason == "covered":
- task_information['outcome'] = Task.OUTCOME_COVERED
- if event.reason == "existing":
- task_information['outcome'] = Task.OUTCOME_PREBUILT
- else:
- task_information['task_executed'] = True
- if 'noexec' in vars(event) and event.noexec:
- task_information['task_executed'] = False
- task_information['outcome'] = Task.OUTCOME_EMPTY
- task_information['script_type'] = Task.CODING_NA
- # do not assign order numbers to scene tasks
- if not isinstance(event, bb.runqueue.sceneQueueTaskStarted):
- self.task_order += 1
- task_information['order'] = self.task_order
- self.orm_wrapper.get_update_task_object(task_information)
- self.internal_state['taskdata'][identifier] = {
- 'outcome': task_information['outcome'],
- }
- def store_tasks_stats(self, event):
- self._ensure_build()
- task_data = BuildInfoHelper._get_data_from_event(event)
- for (task_file, task_name, task_stats, recipe_name) in task_data:
- build = self.internal_state['build']
- self.orm_wrapper.update_task_object(build, task_name, recipe_name, task_stats)
- def update_and_store_task(self, event):
- assert 'taskfile' in vars(event)
- localfilepath = event.taskfile.split(":")[-1]
- assert localfilepath.startswith("/")
- identifier = event.taskfile + ":" + event.taskname
- if not identifier in self.internal_state['taskdata']:
- if isinstance(event, bb.build.TaskBase):
- # we do a bit of guessing
- candidates = [x for x in self.internal_state['taskdata'].keys() if x.endswith(identifier)]
- if len(candidates) == 1:
- identifier = candidates[0]
- elif len(candidates) > 1 and hasattr(event,'_package'):
- if 'native-' in event._package:
- identifier = 'native:' + identifier
- if 'nativesdk-' in event._package:
- identifier = 'nativesdk:' + identifier
- candidates = [x for x in self.internal_state['taskdata'].keys() if x.endswith(identifier)]
- if len(candidates) == 1:
- identifier = candidates[0]
- assert identifier in self.internal_state['taskdata']
- identifierlist = identifier.split(":")
- realtaskfile = ":".join(identifierlist[0:len(identifierlist)-1])
- recipe_information = self._get_recipe_information_from_taskfile(realtaskfile)
- recipe = self.orm_wrapper.get_update_recipe_object(recipe_information, True)
- task_information = self._get_task_information(event,recipe)
- task_information['outcome'] = self.internal_state['taskdata'][identifier]['outcome']
- if 'logfile' in vars(event):
- task_information['logfile'] = event.logfile
- if '_message' in vars(event):
- task_information['message'] = event._message
- if 'taskflags' in vars(event):
- # with TaskStarted, we get even more information
- if 'python' in event.taskflags.keys() and event.taskflags['python'] == '1':
- task_information['script_type'] = Task.CODING_PYTHON
- else:
- task_information['script_type'] = Task.CODING_SHELL
- if task_information['outcome'] == Task.OUTCOME_NA:
- if isinstance(event, (bb.runqueue.runQueueTaskCompleted, bb.runqueue.sceneQueueTaskCompleted)):
- task_information['outcome'] = Task.OUTCOME_SUCCESS
- del self.internal_state['taskdata'][identifier]
- if isinstance(event, (bb.runqueue.runQueueTaskFailed, bb.runqueue.sceneQueueTaskFailed)):
- task_information['outcome'] = Task.OUTCOME_FAILED
- del self.internal_state['taskdata'][identifier]
- if not connection.features.autocommits_when_autocommit_is_off:
- # we force a sync point here, to get the progress bar to show
- if self.autocommit_step % 3 == 0:
- transaction.set_autocommit(True)
- transaction.set_autocommit(False)
- self.autocommit_step += 1
- self.orm_wrapper.get_update_task_object(task_information, True) # must exist
- def store_missed_state_tasks(self, event):
- for (fn, taskname, taskhash, sstatefile) in BuildInfoHelper._get_data_from_event(event)['missed']:
- # identifier = fn + taskname + "_setscene"
- recipe_information = self._get_recipe_information_from_taskfile(fn)
- recipe = self.orm_wrapper.get_update_recipe_object(recipe_information)
- mevent = MockEvent()
- mevent.taskname = taskname
- mevent.taskhash = taskhash
- task_information = self._get_task_information(mevent,recipe)
- task_information['start_time'] = timezone.now()
- task_information['outcome'] = Task.OUTCOME_NA
- task_information['sstate_checksum'] = taskhash
- task_information['sstate_result'] = Task.SSTATE_MISS
- task_information['path_to_sstate_obj'] = sstatefile
- self.orm_wrapper.get_update_task_object(task_information)
- for (fn, taskname, taskhash, sstatefile) in BuildInfoHelper._get_data_from_event(event)['found']:
- # identifier = fn + taskname + "_setscene"
- recipe_information = self._get_recipe_information_from_taskfile(fn)
- recipe = self.orm_wrapper.get_update_recipe_object(recipe_information)
- mevent = MockEvent()
- mevent.taskname = taskname
- mevent.taskhash = taskhash
- task_information = self._get_task_information(mevent,recipe)
- task_information['path_to_sstate_obj'] = sstatefile
- self.orm_wrapper.get_update_task_object(task_information)
- def store_target_package_data(self, event):
- self._ensure_build()
- # for all image targets
- for target in self.internal_state['targets']:
- if target.is_image:
- pkgdata = BuildInfoHelper._get_data_from_event(event)['pkgdata']
- imgdata = BuildInfoHelper._get_data_from_event(event)['imgdata'].get(target.target, {})
- filedata = BuildInfoHelper._get_data_from_event(event)['filedata'].get(target.target, {})
- try:
- self.orm_wrapper.save_target_package_information(self.internal_state['build'], target, imgdata, pkgdata, self.internal_state['recipes'], built_package=True)
- self.orm_wrapper.save_target_package_information(self.internal_state['build'], target, imgdata.copy(), pkgdata, self.internal_state['recipes'], built_package=False)
- except KeyError as e:
- logger.warning("KeyError in save_target_package_information"
- "%s ", e)
- # only try to find files in the image if the task for this
- # target is one which produces image files; otherwise, the old
- # list of files in the files-in-image.txt file will be
- # appended to the target even if it didn't produce any images
- if target.task in BuildInfoHelper.IMAGE_GENERATING_TASKS:
- try:
- self.orm_wrapper.save_target_file_information(self.internal_state['build'], target, filedata)
- except KeyError as e:
- logger.warning("KeyError in save_target_file_information"
- "%s ", e)
- def cancel_cli_build(self):
- """
- If a build is currently underway, set its state to CANCELLED;
- note that this only gets called for command line builds which are
- interrupted, so it doesn't touch any BuildRequest objects
- """
- self._ensure_build()
- self.internal_state['build'].outcome = Build.CANCELLED
- self.internal_state['build'].save()
- signal_runbuilds()
- def store_dependency_information(self, event):
- assert '_depgraph' in vars(event)
- assert 'layer-priorities' in event._depgraph
- assert 'pn' in event._depgraph
- assert 'tdepends' in event._depgraph
- errormsg = ""
- # save layer version priorities
- if 'layer-priorities' in event._depgraph.keys():
- for lv in event._depgraph['layer-priorities']:
- (_, path, _, priority) = lv
- layer_version_obj = self._get_layer_version_for_dependency(path)
- if layer_version_obj:
- layer_version_obj.priority = priority
- layer_version_obj.save()
- # save recipe information
- self.internal_state['recipes'] = {}
- for pn in event._depgraph['pn']:
- file_name = event._depgraph['pn'][pn]['filename'].split(":")[-1]
- pathflags = ":".join(sorted(event._depgraph['pn'][pn]['filename'].split(":")[:-1]))
- layer_version_obj = self._get_layer_version_for_path(file_name)
- assert layer_version_obj is not None
- recipe_info = {}
- recipe_info['name'] = pn
- recipe_info['layer_version'] = layer_version_obj
- if 'version' in event._depgraph['pn'][pn]:
- recipe_info['version'] = event._depgraph['pn'][pn]['version'].lstrip(":")
- if 'summary' in event._depgraph['pn'][pn]:
- recipe_info['summary'] = event._depgraph['pn'][pn]['summary']
- if 'license' in event._depgraph['pn'][pn]:
- recipe_info['license'] = event._depgraph['pn'][pn]['license']
- if 'description' in event._depgraph['pn'][pn]:
- recipe_info['description'] = event._depgraph['pn'][pn]['description']
- if 'section' in event._depgraph['pn'][pn]:
- recipe_info['section'] = event._depgraph['pn'][pn]['section']
- if 'homepage' in event._depgraph['pn'][pn]:
- recipe_info['homepage'] = event._depgraph['pn'][pn]['homepage']
- if 'bugtracker' in event._depgraph['pn'][pn]:
- recipe_info['bugtracker'] = event._depgraph['pn'][pn]['bugtracker']
- recipe_info['file_path'] = file_name
- recipe_info['pathflags'] = pathflags
- if recipe_info['file_path'].startswith(recipe_info['layer_version'].local_path):
- recipe_info['file_path'] = recipe_info['file_path'][len(recipe_info['layer_version'].local_path):].lstrip("/")
- else:
- raise RuntimeError("Recipe file path %s is not under layer version at %s" % (recipe_info['file_path'], recipe_info['layer_version'].local_path))
- recipe = self.orm_wrapper.get_update_recipe_object(recipe_info)
- recipe.is_image = False
- if 'inherits' in event._depgraph['pn'][pn].keys():
- for cls in event._depgraph['pn'][pn]['inherits']:
- if cls.endswith('/image.bbclass'):
- recipe.is_image = True
- recipe_info['is_image'] = True
- # Save the is_image state to the relevant recipe objects
- self.orm_wrapper.get_update_recipe_object(recipe_info)
- break
- if recipe.is_image:
- for t in self.internal_state['targets']:
- if pn == t.target:
- t.is_image = True
- t.save()
- self.internal_state['recipes'][pn] = recipe
- # we'll not get recipes for key w/ values listed in ASSUME_PROVIDED
- assume_provided = self.server.runCommand(["getVariable", "ASSUME_PROVIDED"])[0].split()
- # save recipe dependency
- # buildtime
- recipedeps_objects = []
- for recipe in event._depgraph['depends']:
- target = self.internal_state['recipes'][recipe]
- for dep in event._depgraph['depends'][recipe]:
- if dep in assume_provided:
- continue
- via = None
- if 'providermap' in event._depgraph and dep in event._depgraph['providermap']:
- deprecipe = event._depgraph['providermap'][dep][0]
- dependency = self.internal_state['recipes'][deprecipe]
- via = Provides.objects.get_or_create(name=dep,
- recipe=dependency)[0]
- elif dep in self.internal_state['recipes']:
- dependency = self.internal_state['recipes'][dep]
- else:
- errormsg += " stpd: KeyError saving recipe dependency for %s, %s \n" % (recipe, dep)
- continue
- recipe_dep = Recipe_Dependency(recipe=target,
- depends_on=dependency,
- via=via,
- dep_type=Recipe_Dependency.TYPE_DEPENDS)
- recipedeps_objects.append(recipe_dep)
- Recipe_Dependency.objects.bulk_create(recipedeps_objects)
- # save all task information
- def _save_a_task(taskdesc):
- spec = re.split(r'\.', taskdesc)
- pn = ".".join(spec[0:-1])
- taskname = spec[-1]
- e = event
- e.taskname = pn
- recipe = self.internal_state['recipes'][pn]
- task_info = self._get_task_information(e, recipe)
- task_info['task_name'] = taskname
- task_obj = self.orm_wrapper.get_update_task_object(task_info)
- return task_obj
- # create tasks
- tasks = {}
- for taskdesc in event._depgraph['tdepends']:
- tasks[taskdesc] = _save_a_task(taskdesc)
- # create dependencies between tasks
- taskdeps_objects = []
- for taskdesc in event._depgraph['tdepends']:
- target = tasks[taskdesc]
- for taskdep in event._depgraph['tdepends'][taskdesc]:
- if taskdep not in tasks:
- # Fetch tasks info is not collected previously
- dep = _save_a_task(taskdep)
- else:
- dep = tasks[taskdep]
- taskdeps_objects.append(Task_Dependency( task = target, depends_on = dep ))
- Task_Dependency.objects.bulk_create(taskdeps_objects)
- if len(errormsg) > 0:
- logger.warning("buildinfohelper: dependency info not identify recipes: \n%s", errormsg)
- def store_build_package_information(self, event):
- self._ensure_build()
- package_info = BuildInfoHelper._get_data_from_event(event)
- self.orm_wrapper.save_build_package_information(
- self.internal_state['build'],
- package_info,
- self.internal_state['recipes'],
- built_package=True)
- self.orm_wrapper.save_build_package_information(
- self.internal_state['build'],
- package_info,
- self.internal_state['recipes'],
- built_package=False)
- def _store_build_done(self, errorcode):
- logger.info("Build exited with errorcode %d", errorcode)
- if not self.brbe:
- return
- br_id, be_id = self.brbe.split(":")
- br = BuildRequest.objects.get(pk = br_id)
- # if we're 'done' because we got cancelled update the build outcome
- if br.state == BuildRequest.REQ_CANCELLING:
- logger.info("Build cancelled")
- br.build.outcome = Build.CANCELLED
- br.build.save()
- self.internal_state['build'] = br.build
- errorcode = 0
- if errorcode == 0:
- # request archival of the project artifacts
- br.state = BuildRequest.REQ_COMPLETED
- else:
- br.state = BuildRequest.REQ_FAILED
- br.save()
- be = BuildEnvironment.objects.get(pk = be_id)
- be.lock = BuildEnvironment.LOCK_FREE
- be.save()
- signal_runbuilds()
- def store_log_error(self, text):
- mockevent = MockEvent()
- mockevent.levelno = formatter.ERROR
- mockevent.msg = text
- mockevent.pathname = '-- None'
- mockevent.lineno = LogMessage.ERROR
- self.store_log_event(mockevent)
- def store_log_exception(self, text, backtrace = ""):
- mockevent = MockEvent()
- mockevent.levelno = -1
- mockevent.msg = text
- mockevent.pathname = backtrace
- mockevent.lineno = -1
- self.store_log_event(mockevent)
- def store_log_event(self, event,cli_backlog=True):
- self._ensure_build()
- if event.levelno < formatter.WARNING:
- return
- # early return for CLI builds
- if cli_backlog and self.brbe is None:
- if not 'backlog' in self.internal_state:
- self.internal_state['backlog'] = []
- self.internal_state['backlog'].append(event)
- return
- if 'backlog' in self.internal_state:
- # if we have a backlog of events, do our best to save them here
- if len(self.internal_state['backlog']):
- tempevent = self.internal_state['backlog'].pop()
- logger.debug(1, "buildinfohelper: Saving stored event %s "
- % tempevent)
- self.store_log_event(tempevent,cli_backlog)
- else:
- logger.info("buildinfohelper: All events saved")
- del self.internal_state['backlog']
- log_information = {}
- log_information['build'] = self.internal_state['build']
- if event.levelno == formatter.CRITICAL:
- log_information['level'] = LogMessage.CRITICAL
- elif event.levelno == formatter.ERROR:
- log_information['level'] = LogMessage.ERROR
- elif event.levelno == formatter.WARNING:
- log_information['level'] = LogMessage.WARNING
- elif event.levelno == -2: # toaster self-logging
- log_information['level'] = -2
- else:
- log_information['level'] = LogMessage.INFO
- log_information['message'] = event.getMessage()
- log_information['pathname'] = event.pathname
- log_information['lineno'] = event.lineno
- logger.info("Logging error 2: %s", log_information)
- self.orm_wrapper.create_logmessage(log_information)
- def _get_filenames_from_image_license(self, image_license_manifest_path):
- """
- Find the FILES line in the image_license.manifest file,
- which has the basenames of the bzImage and modules files
- in this format:
- FILES: bzImage--4.4.11+git0+3a5f494784_53e84104c5-r0-qemux86-20160603165040.bin modules--4.4.11+git0+3a5f494784_53e84104c5-r0-qemux86-20160603165040.tgz
- """
- files = []
- with open(image_license_manifest_path) as image_license:
- for line in image_license:
- if line.startswith('FILES'):
- files_str = line.split(':')[1].strip()
- files_str = re.sub(r' {2,}', ' ', files_str)
- # ignore lines like "FILES:" with no filenames
- if files_str:
- files += files_str.split(' ')
- return files
- def _endswith(self, str_to_test, endings):
- """
- Returns True if str ends with one of the strings in the list
- endings, False otherwise
- """
- endswith = False
- for ending in endings:
- if str_to_test.endswith(ending):
- endswith = True
- break
- return endswith
- def scan_task_artifacts(self, event):
- """
- The 'TaskArtifacts' event passes the manifest file content for the
- tasks 'do_deploy', 'do_image_complete', 'do_populate_sdk', and
- 'do_populate_sdk_ext'. The first two will be implemented later.
- """
- task_vars = BuildInfoHelper._get_data_from_event(event)
- task_name = task_vars['task'][task_vars['task'].find(':')+1:]
- task_artifacts = task_vars['artifacts']
- if task_name in ['do_populate_sdk', 'do_populate_sdk_ext']:
- targets = [target for target in self.internal_state['targets'] \
- if target.task == task_name[3:]]
- if not targets:
- logger.warning("scan_task_artifacts: SDK targets not found: %s\n", task_name)
- return
- for artifact_path in task_artifacts:
- if not os.path.isfile(artifact_path):
- logger.warning("scan_task_artifacts: artifact file not found: %s\n", artifact_path)
- continue
- for target in targets:
- # don't record the file if it's already been added
- # to this target
- matching_files = TargetSDKFile.objects.filter(
- target=target, file_name=artifact_path)
- if matching_files.count() == 0:
- artifact_size = os.stat(artifact_path).st_size
- self.orm_wrapper.save_target_sdk_file(
- target, artifact_path, artifact_size)
- def _get_image_files(self, deploy_dir_image, image_name, image_file_extensions):
- """
- Find files in deploy_dir_image whose basename starts with the
- string image_name and ends with one of the strings in
- image_file_extensions.
- Returns a list of file dictionaries like
- [
- {
- 'path': '/path/to/image/file',
- 'size': <file size in bytes>
- }
- ]
- """
- image_files = []
- for dirpath, _, filenames in os.walk(deploy_dir_image):
- for filename in filenames:
- if filename.startswith(image_name) and \
- self._endswith(filename, image_file_extensions):
- image_file_path = os.path.join(dirpath, filename)
- image_file_size = os.stat(image_file_path).st_size
- image_files.append({
- 'path': image_file_path,
- 'size': image_file_size
- })
- return image_files
- def scan_image_artifacts(self):
- """
- Scan for built image artifacts in DEPLOY_DIR_IMAGE and associate them
- with a Target object in self.internal_state['targets'].
- We have two situations to handle:
- 1. This is the first time a target + machine has been built, so
- add files from the DEPLOY_DIR_IMAGE to the target.
- OR
- 2. There are no new files for the target (they were already produced by
- a previous build), so copy them from the most recent previous build with
- the same target, task and machine.
- """
- deploy_dir_image = \
- self.server.runCommand(['getVariable', 'DEPLOY_DIR_IMAGE'])[0]
- # if there's no DEPLOY_DIR_IMAGE, there aren't going to be
- # any image artifacts, so we can return immediately
- if not deploy_dir_image:
- return
- buildname = self.server.runCommand(['getVariable', 'BUILDNAME'])[0]
- machine = self.server.runCommand(['getVariable', 'MACHINE'])[0]
- image_name = self.server.runCommand(['getVariable', 'IMAGE_NAME'])[0]
- # location of the manifest files for this build;
- # note that this file is only produced if an image is produced
- license_directory = \
- self.server.runCommand(['getVariable', 'LICENSE_DIRECTORY'])[0]
- # file name extensions for image files
- image_file_extensions_unique = {}
- image_fstypes = self.server.runCommand(
- ['getVariable', 'IMAGE_FSTYPES'])[0]
- if image_fstypes is not None:
- image_types_str = image_fstypes.strip()
- image_file_extensions = re.sub(r' {2,}', ' ', image_types_str)
- image_file_extensions_unique = set(image_file_extensions.split(' '))
- targets = self.internal_state['targets']
- # filter out anything which isn't an image target
- image_targets = [target for target in targets if target.is_image]
- for image_target in image_targets:
- # this is set to True if we find at least one file relating to
- # this target; if this remains False after the scan, we copy the
- # files from the most-recent Target with the same target + machine
- # onto this Target instead
- has_files = False
- # we construct this because by the time we reach
- # BuildCompleted, this has reset to
- # 'defaultpkgname-<MACHINE>-<BUILDNAME>';
- # we need to change it to
- # <TARGET>-<MACHINE>-<BUILDNAME>
- real_image_name = re.sub(r'^defaultpkgname', image_target.target,
- image_name)
- image_license_manifest_path = os.path.join(
- license_directory,
- real_image_name,
- 'image_license.manifest')
- image_package_manifest_path = os.path.join(
- license_directory,
- real_image_name,
- 'image_license.manifest')
- # if image_license.manifest exists, we can read the names of
- # bzImage, modules etc. files for this build from it, then look for
- # them in the DEPLOY_DIR_IMAGE; note that this file is only produced
- # if an image file was produced
- if os.path.isfile(image_license_manifest_path):
- has_files = True
- basenames = self._get_filenames_from_image_license(
- image_license_manifest_path)
- for basename in basenames:
- artifact_path = os.path.join(deploy_dir_image, basename)
- if not os.path.exists(artifact_path):
- logger.warning("artifact %s doesn't exist, skipping" % artifact_path)
- continue
- artifact_size = os.stat(artifact_path).st_size
- # note that the artifact will only be saved against this
- # build if it hasn't been already
- self.orm_wrapper.save_target_kernel_file(image_target,
- artifact_path, artifact_size)
- # store the license manifest path on the target
- # (this file is also created any time an image file is created)
- license_manifest_path = os.path.join(license_directory,
- real_image_name, 'license.manifest')
- self.orm_wrapper.update_target_set_license_manifest(
- image_target, license_manifest_path)
- # store the package manifest path on the target (this file
- # is created any time an image file is created)
- package_manifest_path = os.path.join(deploy_dir_image,
- real_image_name + '.rootfs.manifest')
- if os.path.exists(package_manifest_path):
- self.orm_wrapper.update_target_set_package_manifest(
- image_target, package_manifest_path)
- # scan the directory for image files relating to this build
- # (via real_image_name); note that we don't have to set
- # has_files = True, as searching for the license manifest file
- # will already have set it to true if at least one image file was
- # produced; note that the real_image_name includes BUILDNAME, which
- # in turn includes a timestamp; so if no files were produced for
- # this timestamp (i.e. the build reused existing image files already
- # in the directory), no files will be recorded against this target
- image_files = self._get_image_files(deploy_dir_image,
- real_image_name, image_file_extensions_unique)
- for image_file in image_files:
- self.orm_wrapper.save_target_image_file_information(
- image_target, image_file['path'], image_file['size'])
- if not has_files:
- # copy image files and build artifacts from the
- # most-recently-built Target with the
- # same target + machine as this Target; also copy the license
- # manifest path, as that is not treated as an artifact and needs
- # to be set separately
- similar_target = \
- self.orm_wrapper.get_similar_target_with_image_files(
- image_target)
- if similar_target:
- logger.info('image artifacts for target %s cloned from ' \
- 'target %s' % (image_target.pk, similar_target.pk))
- self.orm_wrapper.clone_image_artifacts(similar_target,
- image_target)
- def _get_sdk_targets(self):
- """
- Return targets which could generate SDK artifacts, i.e.
- "do_populate_sdk" and "do_populate_sdk_ext".
- """
- return [target for target in self.internal_state['targets'] \
- if target.task in ['populate_sdk', 'populate_sdk_ext']]
- def scan_sdk_artifacts(self, event):
- """
- Note that we have to intercept an SDKArtifactInfo event from
- toaster.bbclass (via toasterui) to get hold of the SDK variables we
- need to be able to scan for files accurately: this is because
- variables like TOOLCHAIN_OUTPUTNAME have reset to None by the time
- BuildCompleted is fired by bitbake, so we have to get those values
- while the build is still in progress.
- For populate_sdk_ext, this runs twice, with two different
- TOOLCHAIN_OUTPUTNAME settings, each of which will capture some of the
- files in the SDK output directory.
- """
- sdk_vars = BuildInfoHelper._get_data_from_event(event)
- toolchain_outputname = sdk_vars['TOOLCHAIN_OUTPUTNAME']
- # targets which might have created SDK artifacts
- sdk_targets = self._get_sdk_targets()
- # location of SDK artifacts
- tmpdir = self.server.runCommand(['getVariable', 'TMPDIR'])[0]
- sdk_dir = os.path.join(tmpdir, 'deploy', 'sdk')
- # all files in the SDK directory
- artifacts = []
- for dir_path, _, filenames in os.walk(sdk_dir):
- for filename in filenames:
- full_path = os.path.join(dir_path, filename)
- if not os.path.islink(full_path):
- artifacts.append(full_path)
- for sdk_target in sdk_targets:
- # find files in the SDK directory which haven't already been
- # recorded against a Target and whose basename matches
- # TOOLCHAIN_OUTPUTNAME
- for artifact_path in artifacts:
- basename = os.path.basename(artifact_path)
- toolchain_match = basename.startswith(toolchain_outputname)
- # files which match the name of the target which produced them;
- # for example,
- # poky-glibc-x86_64-core-image-sato-i586-toolchain-ext-2.1+snapshot.sh
- target_match = re.search(sdk_target.target, basename)
- # targets which produce "*-nativesdk-*" files
- is_ext_sdk_target = sdk_target.task in \
- ['do_populate_sdk_ext', 'populate_sdk_ext']
- # SDK files which don't match the target name, i.e.
- # x86_64-nativesdk-libc.*
- # poky-glibc-x86_64-buildtools-tarball-i586-buildtools-nativesdk-standalone-2.1+snapshot*
- is_ext_sdk_file = re.search('-nativesdk-', basename)
- file_from_target = (toolchain_match and target_match) or \
- (is_ext_sdk_target and is_ext_sdk_file)
- if file_from_target:
- # don't record the file if it's already been added to this
- # target
- matching_files = TargetSDKFile.objects.filter(
- target=sdk_target, file_name=artifact_path)
- if matching_files.count() == 0:
- artifact_size = os.stat(artifact_path).st_size
- self.orm_wrapper.save_target_sdk_file(
- sdk_target, artifact_path, artifact_size)
- def clone_required_sdk_artifacts(self):
- """
- If an SDK target doesn't have any SDK artifacts, this means that
- the postfuncs of populate_sdk or populate_sdk_ext didn't fire, which
- in turn means that the targets of this build didn't generate any new
- artifacts.
- In this case, clone SDK artifacts for targets in the current build
- from existing targets for this build.
- """
- sdk_targets = self._get_sdk_targets()
- for sdk_target in sdk_targets:
- # only clone for SDK targets which have no TargetSDKFiles yet
- if sdk_target.targetsdkfile_set.all().count() == 0:
- similar_target = \
- self.orm_wrapper.get_similar_target_with_sdk_files(
- sdk_target)
- if similar_target:
- logger.info('SDK artifacts for target %s cloned from ' \
- 'target %s' % (sdk_target.pk, similar_target.pk))
- self.orm_wrapper.clone_sdk_artifacts(similar_target,
- sdk_target)
- def close(self, errorcode):
- self._store_build_done(errorcode)
- if 'backlog' in self.internal_state:
- # we save missed events in the database for the current build
- tempevent = self.internal_state['backlog'].pop()
- # Do not skip command line build events
- self.store_log_event(tempevent,False)
- if not connection.features.autocommits_when_autocommit_is_off:
- transaction.set_autocommit(True)
- # unset the brbe; this is to prevent subsequent command-line builds
- # being incorrectly attached to the previous Toaster-triggered build;
- # see https://bugzilla.yoctoproject.org/show_bug.cgi?id=9021
- self.brbe = None
- # unset the internal Build object to prevent it being reused for the
- # next build
- self.internal_state['build'] = None
|