mirror of
https://git.yoctoproject.org/poky
synced 2026-07-16 15:57:04 +00:00
08fbdc02e3
Here is the primary driving enhancement:
* Bug 12785 - Support Project Specific configuration for external
tools (e.g. ISS, Eclipse)
- Isolated project-specific configuration page (full Toaster context
hidden)
- Support for new project, reconfigure existing project, and import
existing command line project
- Ability to define variables (e.g. image recipe) and pass them back
to external GUI
- Ability to execute the cloning phase, so that external GUI receive
a buildable project
- Ability to call back to the external GUI when updates are completed
and ready
- Compatibility of above projects with the normal full Toaster interface
- Ability to pass to a 'complete' or 'cancel' web page so that the
external GUI can immediately stop that Toaster instance, and not
leave dangling servers nor edit sessions open
Here are the supporting enhancements, where at least the
back end is implemented:
* Bug 12821 - Make Toaster conf changes compatible with command line usage
* Bug 12822 - Support importing user changes to conf files into Toaster
* Bug 12823 - Support importing user build directories into Toaster
* Bug 12824 - Scan imported layers for content so that they are
immediately available
* Bug 12825 - show layer clone item in progress bar
Here are defects fixed:
* Bug 12817 - builddelete.py requires explicit 'add_arguments'
* Bug 12818 - Remove orphaned imported layers when project is deleted
* Bug 12826 - fix imported layer management
* Bug 12819 - build using selected bitbake env, not Toaster's env
* Bug 12820 - Toaster randomizes the layer order in toaster_bblayers.conf
[YOCTO #12785]
(Bitbake rev: 985d6cec290bdd80998a63483561a73c75d82d65)
Signed-off-by: David Reyna <David.Reyna@windriver.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
193 lines
7.1 KiB
Python
193 lines
7.1 KiB
Python
from django.core.management.base import BaseCommand
|
|
from django.db import transaction
|
|
from django.db.models import Q
|
|
|
|
from bldcontrol.bbcontroller import getBuildEnvironmentController
|
|
from bldcontrol.models import BuildRequest, BuildEnvironment
|
|
from bldcontrol.models import BRError, BRVariable
|
|
|
|
from orm.models import Build, LogMessage, Target
|
|
|
|
import logging
|
|
import traceback
|
|
import signal
|
|
import os
|
|
|
|
logger = logging.getLogger("toaster")
|
|
|
|
|
|
class Command(BaseCommand):
|
|
args = ""
|
|
help = "Schedules and executes build requests as possible. "\
|
|
"Does not return (interrupt with Ctrl-C)"
|
|
|
|
@transaction.atomic
|
|
def _selectBuildEnvironment(self):
|
|
bec = getBuildEnvironmentController(lock=BuildEnvironment.LOCK_FREE)
|
|
bec.be.lock = BuildEnvironment.LOCK_LOCK
|
|
bec.be.save()
|
|
return bec
|
|
|
|
@transaction.atomic
|
|
def _selectBuildRequest(self):
|
|
br = BuildRequest.objects.filter(state=BuildRequest.REQ_QUEUED).first()
|
|
return br
|
|
|
|
def schedule(self):
|
|
try:
|
|
# select the build environment and the request to build
|
|
br = self._selectBuildRequest()
|
|
if br:
|
|
br.state = BuildRequest.REQ_INPROGRESS
|
|
br.save()
|
|
else:
|
|
return
|
|
|
|
try:
|
|
bec = self._selectBuildEnvironment()
|
|
except IndexError as e:
|
|
# we could not find a BEC; postpone the BR
|
|
br.state = BuildRequest.REQ_QUEUED
|
|
br.save()
|
|
logger.debug("runbuilds: No build env (%s)" % e)
|
|
return
|
|
|
|
logger.info("runbuilds: starting build %s, environment %s" %
|
|
(br, bec.be))
|
|
|
|
# let the build request know where it is being executed
|
|
br.environment = bec.be
|
|
br.save()
|
|
|
|
# this triggers an async build
|
|
bec.triggerBuild(br.brbitbake, br.brlayer_set.all(),
|
|
br.brvariable_set.all(), br.brtarget_set.all(),
|
|
"%d:%d" % (br.pk, bec.be.pk))
|
|
|
|
except Exception as e:
|
|
logger.error("runbuilds: Error launching build %s" % e)
|
|
traceback.print_exc()
|
|
if "[Errno 111] Connection refused" in str(e):
|
|
# Connection refused, read toaster_server.out
|
|
errmsg = bec.readServerLogFile()
|
|
else:
|
|
errmsg = str(e)
|
|
|
|
BRError.objects.create(req=br, errtype=str(type(e)), errmsg=errmsg,
|
|
traceback=traceback.format_exc())
|
|
br.state = BuildRequest.REQ_FAILED
|
|
br.save()
|
|
bec.be.lock = BuildEnvironment.LOCK_FREE
|
|
bec.be.save()
|
|
# Cancel the pending build and report the exception to the UI
|
|
log_object = LogMessage.objects.create(
|
|
build = br.build,
|
|
level = LogMessage.EXCEPTION,
|
|
message = errmsg)
|
|
log_object.save()
|
|
br.build.outcome = Build.FAILED
|
|
br.build.save()
|
|
|
|
def archive(self):
|
|
for br in BuildRequest.objects.filter(state=BuildRequest.REQ_ARCHIVE):
|
|
if br.build is None:
|
|
br.state = BuildRequest.REQ_FAILED
|
|
else:
|
|
br.state = BuildRequest.REQ_COMPLETED
|
|
br.save()
|
|
|
|
def cleanup(self):
|
|
from django.utils import timezone
|
|
from datetime import timedelta
|
|
# environments locked for more than 30 seconds
|
|
# they should be unlocked
|
|
BuildEnvironment.objects.filter(
|
|
Q(buildrequest__state__in=[BuildRequest.REQ_FAILED,
|
|
BuildRequest.REQ_COMPLETED,
|
|
BuildRequest.REQ_CANCELLING]) &
|
|
Q(lock=BuildEnvironment.LOCK_LOCK) &
|
|
Q(updated__lt=timezone.now() - timedelta(seconds=30))
|
|
).update(lock=BuildEnvironment.LOCK_FREE)
|
|
|
|
# update all Builds that were in progress and failed to start
|
|
for br in BuildRequest.objects.filter(
|
|
state=BuildRequest.REQ_FAILED,
|
|
build__outcome=Build.IN_PROGRESS):
|
|
# transpose the launch errors in ToasterExceptions
|
|
br.build.outcome = Build.FAILED
|
|
for brerror in br.brerror_set.all():
|
|
logger.debug("Saving error %s" % brerror)
|
|
LogMessage.objects.create(build=br.build,
|
|
level=LogMessage.EXCEPTION,
|
|
message=brerror.errmsg)
|
|
br.build.save()
|
|
|
|
# we don't have a true build object here; hence, toasterui
|
|
# didn't have a change to release the BE lock
|
|
br.environment.lock = BuildEnvironment.LOCK_FREE
|
|
br.environment.save()
|
|
|
|
# update all BuildRequests without a build created
|
|
for br in BuildRequest.objects.filter(build=None):
|
|
br.build = Build.objects.create(project=br.project,
|
|
completed_on=br.updated,
|
|
started_on=br.created)
|
|
br.build.outcome = Build.FAILED
|
|
try:
|
|
br.build.machine = br.brvariable_set.get(name='MACHINE').value
|
|
except BRVariable.DoesNotExist:
|
|
pass
|
|
br.save()
|
|
# transpose target information
|
|
for brtarget in br.brtarget_set.all():
|
|
Target.objects.create(build=br.build,
|
|
target=brtarget.target,
|
|
task=brtarget.task)
|
|
# transpose the launch errors in ToasterExceptions
|
|
for brerror in br.brerror_set.all():
|
|
LogMessage.objects.create(build=br.build,
|
|
level=LogMessage.EXCEPTION,
|
|
message=brerror.errmsg)
|
|
|
|
br.build.save()
|
|
|
|
# Make sure the LOCK is removed for builds which have been fully
|
|
# cancelled
|
|
for br in BuildRequest.objects.filter(
|
|
Q(build__outcome=Build.CANCELLED) &
|
|
Q(state=BuildRequest.REQ_CANCELLING) &
|
|
~Q(environment=None)):
|
|
br.environment.lock = BuildEnvironment.LOCK_FREE
|
|
br.environment.save()
|
|
|
|
def runbuild(self):
|
|
try:
|
|
self.cleanup()
|
|
except Exception as e:
|
|
logger.warn("runbuilds: cleanup exception %s" % str(e))
|
|
|
|
try:
|
|
self.archive()
|
|
except Exception as e:
|
|
logger.warn("runbuilds: archive exception %s" % str(e))
|
|
|
|
try:
|
|
self.schedule()
|
|
except Exception as e:
|
|
logger.warn("runbuilds: schedule exception %s" % str(e))
|
|
|
|
def handle(self, **options):
|
|
pidfile_path = os.path.join(os.environ.get("BUILDDIR", "."),
|
|
".runbuilds.pid")
|
|
|
|
with open(pidfile_path, 'w') as pidfile:
|
|
pidfile.write("%s" % os.getpid())
|
|
|
|
self.runbuild()
|
|
|
|
signal.signal(signal.SIGUSR1, lambda sig, frame: None)
|
|
|
|
while True:
|
|
signal.pause()
|
|
self.runbuild()
|