diff options
Diffstat (limited to 'yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py')
-rw-r--r-- | yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py | 367 |
1 files changed, 226 insertions, 141 deletions
diff --git a/yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py b/yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py index 78f1e9274..93979054d 100644 --- a/yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py +++ b/yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py @@ -24,6 +24,7 @@ import os os.environ["DJANGO_SETTINGS_MODULE"] = "toaster.toastermain.settings" +import django from django.utils import timezone @@ -33,20 +34,23 @@ def _configure_toaster(): sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'toaster')) _configure_toaster() -from toaster.orm.models import Build, Task, Recipe, Layer_Version, Layer, Target, LogMessage, HelpText -from toaster.orm.models import Target_Image_File, BuildArtifact -from toaster.orm.models import Variable, VariableHistory -from toaster.orm.models import Package, Package_File, Target_Installed_Package, Target_File -from toaster.orm.models import Task_Dependency, Package_Dependency -from toaster.orm.models import Recipe_Dependency +django.setup() + +from orm.models import Build, Task, Recipe, Layer_Version, Layer, Target, LogMessage, HelpText +from orm.models import Target_Image_File, BuildArtifact +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, CustomImageRecipe -from toaster.orm.models import Project from bldcontrol.models import BuildEnvironment, BuildRequest 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 @@ -117,6 +121,12 @@ class ORMWrapper(object): return vars(self)[dictname][key] + def _timestamp_to_datetime(self, secs): + """ + Convert timestamp in seconds to Python datetime + """ + return 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 @@ -146,7 +156,7 @@ class ORMWrapper(object): prj = Project.objects.get(pk = project_id) else: # this build was triggered by a legacy system, or command line interactive mode - prj = Project.objects.get_default_project() + prj = Project.objects.get_or_create_default_project() logger.debug(1, "buildinfohelper: project is not specified, defaulting to %s" % prj) @@ -208,6 +218,15 @@ class ORMWrapper(object): 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 @@ -220,6 +239,30 @@ class ORMWrapper(object): target.license_manifest_path = license_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 @@ -256,14 +299,6 @@ class ORMWrapper(object): task_object.sstate_result = Task.SSTATE_FAILED object_changed = True - # mark down duration if we have a start time and a current time - if 'start_time' in task_information.keys() and 'end_time' in task_information.keys(): - duration = task_information['end_time'] - task_information['start_time'] - task_object.elapsed_time = duration - object_changed = True - del task_information['start_time'] - del task_information['end_time'] - if object_changed: task_object.save() return task_object @@ -304,18 +339,27 @@ class ORMWrapper(object): break - # If we're in analysis mode then we are wholly responsible for the data + # 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_default_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): + # Special case the toaster-custom-images layer which is created + # on the fly so don't update the values which may cause the layer + # to be duplicated on a future get_or_create + if layer_obj.layer.name == CustomImageRecipe.LAYER_NAME: + return layer_obj # 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") @@ -325,12 +369,17 @@ class ORMWrapper(object): # 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, - commit=layer_version_information['commit'], - local_path = layer_version_information['local_path'], - ) - logger.info("created new historical layer version %d", layer_copy.pk) + layer_copy, c = Layer_Version.objects.get_or_create( + build=build_obj, + layer=layer_obj.layer, + up_branch=layer_obj.up_branch, + branch=layer_version_information['branch'], + commit=layer_version_information['commit'], + local_path=layer_version_information['local_path'], + ) + + logger.info("created new historical layer version %d", + layer_copy.pk) self.layer_version_built.append(layer_copy) @@ -346,7 +395,7 @@ class ORMWrapper(object): # 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_default_project(): + if build_obj.project == Project.objects.get_or_create_default_project(): project = build_obj.project layer_version_object, _ = Layer_Version.objects.get_or_create( @@ -445,7 +494,7 @@ class ORMWrapper(object): 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, + path = unicode(path, 'utf-8'), size = size, inodetype = Target_File.ITYPE_DIRECTORY, permission = permission, @@ -470,7 +519,7 @@ class ORMWrapper(object): tf_obj = Target_File.objects.create( target = target_obj, - path = path, + path = unicode(path, 'utf-8'), size = size, inodetype = inodetype, permission = permission, @@ -501,7 +550,9 @@ class ORMWrapper(object): filetarget_path = "/".join(fcpl) try: - filetarget_obj = Target_File.objects.get(target = target_obj, path = filetarget_path) + filetarget_obj = Target_File.objects.get( + target = target_obj, + path = unicode(filetarget_path, 'utf-8')) except Target_File.DoesNotExist: # we might have an invalid link; no way to detect this. just set it to None filetarget_obj = None @@ -510,7 +561,7 @@ class ORMWrapper(object): tf_obj = Target_File.objects.create( target = target_obj, - path = path, + path = unicode(path, 'utf-8'), size = size, inodetype = Target_File.ITYPE_SYMLINK, permission = permission, @@ -520,12 +571,14 @@ class ORMWrapper(object): sym_target = filetarget_obj) - def save_target_package_information(self, build_obj, target_obj, packagedict, pkgpnmap, recipes): + 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" @@ -536,11 +589,38 @@ class ORMWrapper(object): if 'OPKGN' in pkgpnmap[p].keys(): searchname = pkgpnmap[p]['OPKGN'] - packagedict[p]['object'], created = Package.objects.get_or_create( build = build_obj, name = searchname ) + 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__up_branch= + built_recipe.layer_version.up_branch, + 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 = recipes[pkgpnmap[p]['PN']] + 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'] @@ -566,7 +646,8 @@ class ORMWrapper(object): packagedict[p]['object'].installed_size = packagedict[p]['size'] packagedict[p]['object'].save() - Target_Installed_Package.objects.create(target = target_obj, package = packagedict[p]['object']) + if built_package: + Target_Installed_Package.objects.create(target = target_obj, package = packagedict[p]['object']) packagedeps_objs = [] for p in packagedict: @@ -627,19 +708,37 @@ class ORMWrapper(object): return log_object.save() - def save_build_package_information(self, build_obj, package_info, recipes): - assert isinstance(build_obj, Build) + def save_build_package_information(self, build_obj, package_info, recipes, + built_package): + # assert isinstance(build_obj, Build) # 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'] - bp_object, _ = Package.objects.get_or_create( build = build_obj, - name = pname ) + 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 = recipes[package_info['PN']] + bp_object.recipe = recipe bp_object.version = package_info['PKGV'] bp_object.revision = package_info['PKGR'] bp_object.summary = package_info['SUMMARY'] @@ -659,7 +758,12 @@ class ORMWrapper(object): Package_File.objects.bulk_create(packagefile_objects) def _po_byname(p): - pkg, created = Package.objects.get_or_create(build = build_obj, name = 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() @@ -700,7 +804,6 @@ class ORMWrapper(object): def save_build_variables(self, build_obj, vardump): assert isinstance(build_obj, Build) - helptext_objects = [] for k in vardump: desc = vardump[k]['doc'] if desc is None: @@ -711,10 +814,9 @@ class ORMWrapper(object): if desc is None: desc = '' if len(desc): - helptext_objects.append(HelpText(build=build_obj, - area=HelpText.VARIABLE, - key=k, - text=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: @@ -734,8 +836,6 @@ class ORMWrapper(object): if len(varhist_objects): VariableHistory.objects.bulk_create(varhist_objects) - HelpText.objects.bulk_create(helptext_objects) - class MockEvent(object): """ This object is used to create event, for which normal event-processing methods can @@ -762,7 +862,7 @@ class BuildInfoHelper(object): # 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): + def __init__(self, server, has_build_history = False, brbe = None): self.internal_state = {} self.internal_state['taskdata'] = {} self.internal_state['targets'] = [] @@ -775,8 +875,13 @@ class BuildInfoHelper(object): self.orm_wrapper = ORMWrapper() self.has_build_history = has_build_history self.tmp_dir = self.server.runCommand(["getVariable", "TMPDIR"])[0] - self.brbe = self.server.runCommand(["getVariable", "TOASTER_BRBE"])[0] - self.project = self.server.runCommand(["getVariable", "TOASTER_PROJECT"])[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)) @@ -786,8 +891,6 @@ class BuildInfoHelper(object): def _get_build_information(self, build_log_path): build_info = {} - # Generate an identifier for each new build - build_info['machine'] = self.server.runCommand(["getVariable", "MACHINE"])[0] build_info['distro'] = self.server.runCommand(["getVariable", "DISTRO"])[0] build_info['distro_version'] = self.server.runCommand(["getVariable", "DISTRO_VERSION"])[0] @@ -796,7 +899,7 @@ class BuildInfoHelper(object): build_info['cooker_log_path'] = build_log_path build_info['build_name'] = self.server.runCommand(["getVariable", "BUILDNAME"])[0] build_info['bitbake_version'] = self.server.runCommand(["getVariable", "BB_VERSION"])[0] - + build_info['project'] = self.project = self.server.runCommand(["getVariable", "TOASTER_PROJECT"])[0] return build_info def _get_task_information(self, event, recipe): @@ -818,44 +921,15 @@ class BuildInfoHelper(object): assert path.startswith("/") assert 'build' in self.internal_state - if self.brbe is None: - 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 - - else: - br_id, be_id = self.brbe.split(":") - from bldcontrol.bbcontroller import getBuildEnvironmentController - bc = getBuildEnvironmentController(pk = be_id) - - def _slkey_managed(layer_version): - return len(bc.getGitCloneDirectory(layer_version.giturl, layer_version.commit) + layer_version.dirpath) - - # Heuristics: we match the path to where the layers have been checked out - for brl in sorted(BuildRequest.objects.get(pk = br_id).brlayer_set.all(), reverse = True, key = _slkey_managed): - localdirname = os.path.join(bc.getGitCloneDirectory(brl.giturl, brl.commit), brl.dirpath) - # we get a relative path, unless running in HEAD mode where the path is absolute - if not localdirname.startswith("/"): - localdirname = os.path.join(bc.be.sourcedir, localdirname) - if path.startswith(localdirname): - # If the build request came from toaster this field - # should contain the information from the layer_version - # That created this build request. - if brl.layer_version: - return brl.layer_version - - #logger.warn("-- managed: matched path %s with layer %s " % (path, localdirname)) - # we matched the BRLayer, but we need the layer_version that generated this br + def _slkey_interactive(layer_version): + assert isinstance(layer_version, Layer_Version) + return len(layer_version.local_path) - for lvo in self.orm_wrapper.layer_version_objects: - if brl.name == lvo.layer.name: - return lvo + # 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 we get here, we didn't read layers correctly; dump whatever information we have on the error log logger.warn("Could not match layer version for recipe path %s : %s", path, self.orm_wrapper.layer_version_objects) @@ -890,12 +964,10 @@ class BuildInfoHelper(object): def _get_path_information(self, task_object): assert isinstance(task_object, Task) - build_stats_format = "{tmpdir}/buildstats/{target}-{machine}/{buildname}/{package}/" + build_stats_format = "{tmpdir}/buildstats/{buildname}/{package}/" build_stats_path = [] for t in self.internal_state['targets']: - target = t.target - machine = self.internal_state['build'].machine buildname = self.internal_state['build'].build_name pe, pv = task_object.recipe.version.split(":",1) if len(pe) > 0: @@ -903,8 +975,8 @@ class BuildInfoHelper(object): else: package = task_object.recipe.name + "-" + pv - build_stats_path.append(build_stats_format.format(tmpdir=self.tmp_dir, target=target, - machine=machine, buildname=buildname, + build_stats_path.append(build_stats_format.format(tmpdir=self.tmp_dir, + buildname=buildname, package=package)) return build_stats_path @@ -938,6 +1010,9 @@ class BuildInfoHelper(object): assert '_pkgs' in vars(event) build_information = self._get_build_information(build_log_path) + # Update brbe and project as they can be changed for every build + self.project = build_information['project'] + build_obj = self.orm_wrapper.create_build_object(build_information, self.brbe, self.project) self.internal_state['build'] = build_obj @@ -1059,31 +1134,11 @@ class BuildInfoHelper(object): def store_tasks_stats(self, event): - for (taskfile, taskname, taskstats, recipename) in BuildInfoHelper._get_data_from_event(event): - localfilepath = taskfile.split(":")[-1] - assert localfilepath.startswith("/") + task_data = BuildInfoHelper._get_data_from_event(event) - recipe_information = self._get_recipe_information_from_taskfile(taskfile) - try: - if recipe_information['file_path'].startswith(recipe_information['layer_version'].local_path): - recipe_information['file_path'] = recipe_information['file_path'][len(recipe_information['layer_version'].local_path):].lstrip("/") - - recipe_object = Recipe.objects.get(layer_version = recipe_information['layer_version'], - file_path__endswith = recipe_information['file_path'], - name = recipename) - except Recipe.DoesNotExist: - logger.error("Could not find recipe for recipe_information %s name %s" , pformat(recipe_information), recipename) - raise - - task_information = {} - task_information['build'] = self.internal_state['build'] - task_information['recipe'] = recipe_object - task_information['task_name'] = taskname - task_information['cpu_usage'] = taskstats['cpu_usage'] - task_information['disk_io'] = taskstats['disk_io'] - if 'elapsed_time' in taskstats: - task_information['elapsed_time'] = taskstats['elapsed_time'] - self.orm_wrapper.get_update_task_object(task_information) + 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) @@ -1105,13 +1160,6 @@ class BuildInfoHelper(object): recipe = self.orm_wrapper.get_update_recipe_object(recipe_information, True) task_information = self._get_task_information(event,recipe) - if 'time' in vars(event): - if not 'start_time' in self.internal_state['taskdata'][identifier]: - self.internal_state['taskdata'][identifier]['start_time'] = event.time - else: - task_information['end_time'] = event.time - task_information['start_time'] = self.internal_state['taskdata'][identifier]['start_time'] - task_information['outcome'] = self.internal_state['taskdata'][identifier]['outcome'] if 'logfile' in vars(event): @@ -1185,11 +1233,12 @@ class BuildInfoHelper(object): 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'][target.target] - filedata = BuildInfoHelper._get_data_from_event(event)['filedata'][target.target] + 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']) + 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.warn("KeyError in save_target_package_information" "%s ", e) @@ -1269,6 +1318,9 @@ class BuildInfoHelper(object): 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']: @@ -1285,15 +1337,27 @@ class BuildInfoHelper(object): # buildtime recipedeps_objects = [] for recipe in event._depgraph['depends']: - try: - target = self.internal_state['recipes'][recipe] - for dep in event._depgraph['depends'][recipe]: + 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] - recipedeps_objects.append(Recipe_Dependency( recipe = target, - depends_on = dependency, dep_type = Recipe_Dependency.TYPE_DEPENDS)) - except KeyError as e: - if e not in assume_provided and not str(e).startswith("virtual/"): - errormsg += " stpd: KeyError saving recipe dependency for %s, %s \n" % (recipe, e) + 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 @@ -1333,10 +1397,17 @@ class BuildInfoHelper(object): def store_build_package_information(self, event): 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'], - ) + 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) @@ -1345,9 +1416,18 @@ class BuildInfoHelper(object): be.lock = BuildEnvironment.LOCK_LOCK be.save() 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_ARCHIVE + br.state = BuildRequest.REQ_COMPLETED else: br.state = BuildRequest.REQ_FAILED br.save() @@ -1434,3 +1514,8 @@ class BuildInfoHelper(object): 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 |