Toaster project main page and other bugfixes and enhancements
cover Toaster project main page and other bugfixes and enhancements
From: Alexandru DAMIAN <[email protected]>
Hello,
This patchset adds the main project edit page, where you can edit project settings, and trigger builds. Currently, only the target add/delete setting is working.
There are two patches that add provisions for running Toaster inside a virtualenv environment, and Django 1.6 compatibility support.
A fix for 6332 requested by WR is included.
This patchset HAS NOT BEEN REVIEWED independently on toaster mailing list, so this submission is also CCed there for review.
Can you please pull on your convenience ?
Thank you,
Alex
The following changes since commit af1d3373706d365f9138caec110fcb20a5966b7b:
toasterui: fix build - project identification (2014-07-09 17:33:35 +0100)
are available in the git repository at:
git://git.yoctoproject.org/poky-contrib adamian/bitbake-fixes-build-enhancements
http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=adamian/bitbake-fixes-build-enhancements
Alexandru DAMIAN (8):
toaster: add fields for sourcedir and builddir paths
toaster: added subdirectory entry for build layers
toaster: move bldcontrol initial_data fixture to code
toaster: add project main edit page
toaster: properly set layers when running a build document requirements for the python environment toaster start script allows for Django 1.6
toasterui: fixing event.data clash
.gitignore | 2 + bin/toaster | 18 +- lib/bb/event.py | 2 +- lib/bb/ui/buildinfohelper.py | 30 +- lib/toaster/bldcontrol/bbcontroller.py | 137 ++++++-- lib/toaster/bldcontrol/fixtures/initial_data.json | 1 - .../management/commands/checksettings.py | 38 +++ .../bldcontrol/management/commands/runbuilds.py | 23 +- ...onment_sourcedir__add_field_buildenvironment.py | 106 ++++++ .../0003_auto__add_field_brlayer_dirpath.py | 99 ++++++ .../bldcontrol/migrations/0004_loadinitialdata.py | 104 ++++++ .../migrations/0005_auto__add_brerror.py | 112 +++++++ lib/toaster/bldcontrol/models.py | 12 +- .../0011_auto__add_field_projectlayer_dirpath.py | 242 ++++++++++++++ ...layer_optional__add_field_projecttarget_task.py | 252 +++++++++++++++ lib/toaster/orm/models.py | 25 +- lib/toaster/toastergui/static/css/default.css | 42 ++- lib/toaster/toastergui/templates/base.html | 4 + lib/toaster/toastergui/templates/project.html | 356 +++++++++++++++++++++ lib/toaster/toastergui/urls.py | 3 + lib/toaster/toastergui/views.py | 105 +++++- requirements.txt | 4 + 22 files changed, 1632 insertions(+), 85 deletions(-) delete mode 100644 lib/toaster/bldcontrol/fixtures/initial_data.json create mode 100644 lib/toaster/bldcontrol/management/commands/checksettings.py create mode 100644 lib/toaster/bldcontrol/migrations/0002_auto__add_field_buildenvironment_sourcedir__add_field_buildenvironment.py create mode 100644 lib/toaster/bldcontrol/migrations/0003_auto__add_field_brlayer_dirpath.py create mode 100644 lib/toaster/bldcontrol/migrations/0004_loadinitialdata.py create mode 100644 lib/toaster/bldcontrol/migrations/0005_auto__add_brerror.py create mode 100644 lib/toaster/orm/migrations/0011_auto__add_field_projectlayer_dirpath.py create mode 100644 lib/toaster/orm/migrations/0012_auto__add_field_projectlayer_optional__add_field_projecttarget_task.py create mode 100644 requirements.txt
Signature · 1 line
1.9.1
PATCH 1/8 toaster: add fields for sourcedir and builddir paths
From: Alexandru DAMIAN <[email protected]>
We add explicit absolute paths for a directory where the layer sources will be checked out (sourcedir) and where the build activities will take place.
Adding minimal checking when starting the application in order to make sure that BuildEnvironment (BE) settings are usable.
Modify the localhost bbcontroller to use the BE settings instead of trying to self-configure on checked out sources.
--- bin/toaster | 1 + lib/toaster/bldcontrol/bbcontroller.py | 51 +++++----- .../management/commands/checksettings.py | 38 ++++++++ ...onment_sourcedir__add_field_buildenvironment.py | 106 +++++++++++++++++++++ lib/toaster/bldcontrol/models.py | 2 + 5 files changed, 172 insertions(+), 26 deletions(-) create mode 100644 lib/toaster/bldcontrol/management/commands/checksettings.py create mode 100644 lib/toaster/bldcontrol/migrations/0002_auto__add_field_buildenvironment_sourcedir__add_field_buildenvironment.py
bin/toaster +1 -0
@@ -64,6 +64,7 @@ function webserverStartAll() python $BBBASEDIR/lib/toaster/manage.py migrate orm || retval=1 fi python $BBBASEDIR/lib/toaster/manage.py migrate bldcontrol || retval=1 + python $BBBASEDIR/lib/toaster/manage.py checksettings || retval=1 if [ $retval -eq 0 ]; then python $BBBASEDIR/lib/toaster/manage.py runserver 0.0.0.0:8000 </dev/null >${BUILDDIR}/toaster_web.log 2>&1 & echo $! >${BUILDDIR}/.toastermain.pid
lib/toaster/bldcontrol/bbcontroller.py +25 -26
91 lines not on this page · raw message
lib/toaster/bldcontrol/management/commands/checksettings.py new file +38 -0
@@ -0,0 +1,38 @@ +from django.core.management.base import NoArgsCommand, CommandError +from django.db import transaction +from orm.models import Build +from bldcontrol.bbcontroller import getBuildEnvironmentController, ShellCmdException +from bldcontrol.models import BuildRequest, BuildEnvironment +import os + +class Command(NoArgsCommand): + args = "" + help = "Verifies thid %dthe configured settings are valid and usable, or prompts the user to fix the settings." + + def handle(self, **options): + # we make sure we have builddir and sourcedir for all defined build envionments + for be in BuildEnvironment.objects.all(): + def _verify_be(): + is_changed = False + print("Verifying the Build Environment type %s id %d." % (be.get_betype_display(), be.pk)) + if len(be.sourcedir) == 0: + be.sourcedir = raw_input(" -- sourcedir may not be empty:") + is_changed = True + if not be.sourcedir.startswith("/"): + be.sourcedir = raw_input(" -- sourcedir must be an absolute path:") + is_changed = True + if len(be.builddir) == 0: + be.builddir = raw_input(" -- builddir may not be empty:") + is_changed = True + if not be.builddir.startswith("/"): + be.builddir = raw_input(" -- builddir must be an absolute path:") + is_changed = True + return is_changed + + while (_verify_be()): + pass + + + + +
lib/toaster/bldcontrol/migrations/0002_auto__add_field_buildenvironment_sourcedir__add_field_buildenvironment.py new file +106 -0
107 lines not on this page · raw message
lib/toaster/bldcontrol/models.py +2 -0
@@ -33,6 +33,8 @@ class BuildEnvironment(models.Model): bbport = models.IntegerField(default = -1) bbtoken = models.CharField(max_length = 126, blank = True) bbstate = models.IntegerField(choices = SERVER_STATE, default = SERVER_STOPPED) + sourcedir = models.CharField(max_length = 512, blank = True) + builddir = models.CharField(max_length = 512, blank = True) lock = models.IntegerField(choices = LOCK_STATE, default = LOCK_FREE) created = models.DateTimeField(auto_now_add = True) updated = models.DateTimeField(auto_now = True)
Signature · 1 line
1.9.1
PATCH 2/8 toaster: added subdirectory entry for build layers
From: Alexandru DAMIAN <[email protected]>
A layer may live in a subdirectory of a git repository, so we add a field to track this setting.
We add the Project schedule_build function, which creates a build request from the current project configuration.
We also fix an import problem with Projects in views.
--- .../0003_auto__add_field_brlayer_dirpath.py | 99 +++++++++ lib/toaster/bldcontrol/models.py | 2 + .../0011_auto__add_field_projectlayer_dirpath.py | 242 +++++++++++++++++++++ lib/toaster/orm/models.py | 21 +- lib/toaster/toastergui/views.py | 2 + 5 files changed, 364 insertions(+), 2 deletions(-) create mode 100644 lib/toaster/bldcontrol/migrations/0003_auto__add_field_brlayer_dirpath.py create mode 100644 lib/toaster/orm/migrations/0011_auto__add_field_projectlayer_dirpath.py
lib/toaster/bldcontrol/migrations/0003_auto__add_field_brlayer_dirpath.py new file +99 -0
@@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'BRLayer.dirpath' + db.add_column(u'bldcontrol_brlayer', 'dirpath', + self.gf('django.db.models.fields.CharField')(default='', max_length=254), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'BRLayer.dirpath' + db.delete_column(u'bldcontrol_brlayer', 'dirpath') + + + models = { + u'bldcontrol.brlayer': { + 'Meta': {'object_name': 'BRLayer'}, + 'commit': ('django.db.models.fields.CharField', [], {'max_length': '254'}), + 'dirpath': ('django.db.models.fields.CharField', [], {'max_length': '254'}), + 'giturl': ('django.db.models.fields.CharField', [], {'max_length': '254'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'req': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['bldcontrol.BuildRequest']"}) + }, + u'bldcontrol.brtarget': { + 'Meta': {'object_name': 'BRTarget'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'req': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['bldcontrol.BuildRequest']"}), + 'target': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'task': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}) + }, + u'bldcontrol.brvariable': { + 'Meta': {'object_name': 'BRVariable'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'req': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['bldcontrol.BuildRequest']"}), + 'value': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + u'bldcontrol.buildenvironment': { + 'Meta': {'object_name': 'BuildEnvironment'}, + 'address': ('django.db.models.fields.CharField', [], {'max_length': '254'}), + 'bbaddress': ('django.db.models.fields.CharField', [], {'max_length': '254', 'blank': 'True'}), + 'bbport': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), + 'bbstate': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'bbtoken': ('django.db.models.fields.CharField', [], {'max_length': '126', 'blank': 'True'}), + 'betype': ('django.db.models.fields.IntegerField', [], {}), + 'builddir': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lock': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'sourcedir': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) + }, + u'bldcontrol.buildrequest': { + 'Meta': {'object_name': 'BuildRequest'}, + 'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']", 'null': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}), + 'state': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) + }, + u'orm.build': { + 'Meta': {'object_name': 'Build'}, + 'bitbake_version': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'build_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'completed_on': ('django.db.models.fields.DateTimeField', [], {}), + 'cooker_log_path': ('django.db.models.fields.CharField', [], {'max_length': '500'}), + 'distro': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'distro_version': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'errors_no': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'machine': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'outcome': ('django.db.models.fields.IntegerField', [], {'default': '2'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']", 'null': 'True'}), + 'started_on': ('django.db.models.fields.DateTimeField', [], {}), + 'timespent': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'warnings_no': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + u'orm.project': { + 'Meta': {'object_name': 'Project'}, + 'branch': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'short_description': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'user_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'}) + } + } + + complete_apps = ['bldcontrol']
lib/toaster/bldcontrol/models.py +2 -0
@@ -71,6 +71,8 @@ class BRLayer(models.Model): name = models.CharField(max_length = 100) giturl = models.CharField(max_length = 254) commit = models.CharField(max_length = 254) + dirpath = models.CharField(max_length = 254) + class BRVariable(models.Model): req = models.ForeignKey(BuildRequest)
lib/toaster/orm/migrations/0011_auto__add_field_projectlayer_dirpath.py new file +242 -0
243 lines not on this page · raw message
lib/toaster/orm/models.py +19 -2
@@ -37,13 +37,13 @@ class ProjectManager(models.Manager): name = "meta", giturl = "git://git.yoctoproject.org/poky", commit = branch, - treepath = "meta") + dirpath = "meta") ProjectLayer.objects.create(project = prj, name = "meta-yocto", giturl = "git://git.yoctoproject.org/poky", commit = branch, - treepath = "meta-yocto") + dirpath = "meta-yocto") return prj
@@ -66,6 +66,22 @@ class Project(models.Model): user_id = models.IntegerField(null = True) objects = ProjectManager() + + def schedule_build(self): + from bldcontrol.models import BuildRequest, BRTarget, BRLayer, BRVariable + br = BuildRequest.objects.create(project = self) + for l in self.projectlayer_set.all(): + BRLayer.objects.create(req = br, name = l.name, giturl = l.giturl, commit = l.commit, dirpath = l.dirpath) + for t in self.projecttarget_set.all(): + BRTarget.objects.create(req = br, target = t.target, task = t.task) + for v in self.projectvariable_set.all(): + BRVariable.objects.create(req = br, name = v.name, value = v.value) + + br.state = BuildRequest.REQ_QUEUED + br.save() + return br + + class Build(models.Model): SUCCEEDED = 0 FAILED = 1
@@ -375,6 +391,7 @@ class ProjectLayer(models.Model): name = models.CharField(max_length = 100) giturl = models.CharField(max_length = 254) commit = models.CharField(max_length = 254) + dirpath = models.CharField(max_length = 254) class Layer(models.Model): name = models.CharField(max_length=100)
lib/toaster/toastergui/views.py +2 -0
@@ -1773,6 +1773,8 @@ if toastermain.settings.MANAGED: from django.contrib.auth import authenticate, login from django.contrib.auth.decorators import login_required + from orm.models import Project + import traceback class BadParameterException(Exception): pass # error thrown on invalid POST requests
Signature · 1 line
1.9.1
PATCH 3/8 toaster: move bldcontrol initial_data fixture to code
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
We move the initial_data fixture to a code migration, in order to make sure that we don't overwrite modified data.
--- bin/toaster | 2 +- lib/toaster/bldcontrol/fixtures/initial_data.json | 1 - .../management/commands/checksettings.py | 8 +- .../bldcontrol/migrations/0004_loadinitialdata.py | 104 +++++++++++++++++++++ 4 files changed, 109 insertions(+), 6 deletions(-) delete mode 100644 lib/toaster/bldcontrol/fixtures/initial_data.json create mode 100644 lib/toaster/bldcontrol/migrations/0004_loadinitialdata.py
bin/toaster +1 -1
@@ -65,7 +65,7 @@ function webserverStartAll() fi python $BBBASEDIR/lib/toaster/manage.py migrate bldcontrol || retval=1 python $BBBASEDIR/lib/toaster/manage.py checksettings || retval=1 - + echo "Starting webserver" if [ $retval -eq 0 ]; then python $BBBASEDIR/lib/toaster/manage.py runserver 0.0.0.0:8000 </dev/null >${BUILDDIR}/toaster_web.log 2>&1 & echo $! >${BUILDDIR}/.toastermain.pid sleep 1
lib/toaster/bldcontrol/fixtures/initial_data.json deleted +0 -1
@@ -1 +0,0 @@ -[{"pk": 1, "model": "bldcontrol.buildenvironment", "fields": {"updated": "2014-05-20T12:17:30Z", "created": "2014-05-20T12:17:30Z", "lock": 0, "bbstate": 0, "bbaddress": "", "betype": 0, "bbtoken": "", "address": "", "bbport": -1}}]
lib/toaster/bldcontrol/management/commands/checksettings.py +4 -4
@@ -27,12 +27,12 @@ class Command(NoArgsCommand): if not be.builddir.startswith("/"): be.builddir = raw_input(" -- builddir must be an absolute path:") is_changed = True + if is_changed: + print "saved" + be.save() return is_changed while (_verify_be()): pass - - - - + return 0
lib/toaster/bldcontrol/migrations/0004_loadinitialdata.py new file +104 -0
104 lines not on this page · raw message
Signature · 1 line
1.9.1
PATCH 4/8 toaster: add project main edit page
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
This is the first commit on the project main edit page.
At this point we have:
* the default settings for a newly created project
* the ability to add targets
* the ability to trigger a build command, and have the build executed
--- bin/toaster | 4 +- ...layer_optional__add_field_projecttarget_task.py | 252 +++++++++++++++ lib/toaster/orm/models.py | 8 +- lib/toaster/toastergui/static/css/default.css | 42 ++- lib/toaster/toastergui/templates/base.html | 4 + lib/toaster/toastergui/templates/project.html | 356 +++++++++++++++++++++ lib/toaster/toastergui/urls.py | 3 + lib/toaster/toastergui/views.py | 105 +++++- 8 files changed, 750 insertions(+), 24 deletions(-) create mode 100644 lib/toaster/orm/migrations/0012_auto__add_field_projectlayer_optional__add_field_projecttarget_task.py
bin/toaster +2 -2
@@ -128,8 +128,8 @@ if [ -z "$ZSH_NAME" ] && [ `basename \"$0\"` = `basename \"$BASH_SOURCE\"` ]; th # Start just the web server, point the web browser to the interface, and start any Django services. if [ -n "$BUILDDIR" ]; then - echo "Error: build/ directory detected. Standalone Toaster will not start in a build environment." 1>&2; - return 1; + echo -e "Error: build/ directory detected. Toaster will not start in managed mode if a build environment is detected.\nUse a clean terminal to start Toaster." 1>&2; + exit 1; fi # Define a fake builddir where only the pid files are actually created. No real builds will take place here.
lib/toaster/orm/migrations/0012_auto__add_field_projectlayer_optional__add_field_projecttarget_task.py new file +252 -0
253 lines not on this page · raw message
lib/toaster/orm/models.py +6 -2
@@ -37,13 +37,15 @@ class ProjectManager(models.Manager): name = "meta", giturl = "git://git.yoctoproject.org/poky", commit = branch, - dirpath = "meta") + dirpath = "meta", + optional = False) ProjectLayer.objects.create(project = prj, name = "meta-yocto", giturl = "git://git.yoctoproject.org/poky", commit = branch, - dirpath = "meta-yocto") + dirpath = "meta-yocto", + optional = False) return prj
@@ -116,6 +118,7 @@ class Build(models.Model): class ProjectTarget(models.Model): project = models.ForeignKey(Project) target = models.CharField(max_length=100) + task = models.CharField(max_length=100, null=True) @python_2_unicode_compatible class Target(models.Model):
@@ -392,6 +395,7 @@ class ProjectLayer(models.Model): giturl = models.CharField(max_length = 254) commit = models.CharField(max_length = 254) dirpath = models.CharField(max_length = 254) + optional = models.BooleanField(default = True) class Layer(models.Model): name = models.CharField(max_length=100)
lib/toaster/toastergui/static/css/default.css +28 -14
45 lines not on this page · raw message
lib/toaster/toastergui/templates/base.html +4 -0
@@ -65,6 +65,10 @@ function reload_params(params) { <i class="icon-caret-down"></i> </button> <ul class="dropdown-menu"> +{% for prj in projects %} + <li><a href="{% url 'project' prj.id %}">{{prj.name}}</a></li> +{% endfor %} + <li><hr/></li> <li><a href="#">Clone project</a></li> <li><a href="#">Export project</a></li> <li><a href="#">Import project</a></li>
lib/toaster/toastergui/templates/project.html +356 -0
360 lines not on this page · raw message
lib/toaster/toastergui/urls.py +3 -0
@@ -69,6 +69,9 @@ urlpatterns = patterns('toastergui.views', # project URLs url(r'^newproject/$', 'newproject', name='newproject'), url(r'^project/(?P<pid>\d+)/$', 'project', name='project'), + url(r'^xhr_projectbuild/(?P<pid>\d+)/$', 'xhr_projectbuild', name='xhr_projectbuild'), + url(r'^xhr_projectedit/(?P<pid>\d+)/$', 'xhr_projectedit', name='xhr_projectedit'), + # default redirection url(r'^$', RedirectView.as_view( url= 'builds/')),
lib/toaster/toastergui/views.py +99 -6
140 lines not on this page · raw message
Signature · 1 line
1.9.1
PATCH 5/8 toaster: properly set layers when running a build
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
This patch enables the localhost build controller to properly set the layers before the build runs.
It creates the checkout directories under BuildEnvironment sourcedir directory, and runs the build in the buildir directory.
Build launch errors are tracked in the newly added BRError table. These are different from build errors, in the sense that the build can't start due to these errors.
--- lib/toaster/bldcontrol/bbcontroller.py | 108 +++++++++++++++++--- .../bldcontrol/management/commands/runbuilds.py | 23 +++-- .../migrations/0005_auto__add_brerror.py | 112 +++++++++++++++++++++ lib/toaster/bldcontrol/models.py | 8 +- 4 files changed, 226 insertions(+), 25 deletions(-) create mode 100644 lib/toaster/bldcontrol/migrations/0005_auto__add_brerror.py
lib/toaster/bldcontrol/bbcontroller.py +93 -15
@@ -126,6 +126,8 @@ class BuildEnvironmentController(object): def setLayers(self,ls): """ Sets the layer variables in the config file, after validating local layer paths. The layer paths must be in a list of BRLayer object + + a word of attention: by convention, the first layer for any build will be poky! """ raise Exception("Must override setLayers")
@@ -165,25 +167,31 @@ class BuildEnvironmentController(object): class ShellCmdException(Exception): pass + +class BuildSetupException(Exception): + pass + class LocalhostBEController(BuildEnvironmentController): """ Implementation of the BuildEnvironmentController for the localhost; this controller manages the default build directory, the server setup and system start and stop for the localhost-type build environment """ - from os.path import dirname as DN def __init__(self, be): super(LocalhostBEController, self).__init__(be) - from os.path import dirname as DN self.dburl = settings.getDATABASE_URL() + self.pokydirname = None + + def _shellcmd(self, command, cwd = None): + if cwd is None: + cwd = self.be.sourcedir - def _shellcmd(self, command): - p = subprocess.Popen(command, cwd=self.be.sourcedir, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen(command, cwd = cwd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (out,err) = p.communicate() if p.returncode: if len(err) == 0: - err = "command: %s" % command + err = "command: %s \n%s" % (command, out) else: err = "command: %s \n%s" % (command, err) raise ShellCmdException(err)
@@ -191,22 +199,20 @@ class LocalhostBEController(BuildEnvironmentController): return out def _createdirpath(self, path): + from os.path import dirname as DN if not os.path.exists(DN(path)): self._createdirpath(DN(path)) if not os.path.exists(path): os.mkdir(path, 0755) def _startBE(self): - assert self.be.sourcedir and os.path.exists(self.be.sourcedir) + assert self.pokydirname and os.path.exists(self.pokydirname) self._createdirpath(self.be.builddir) - self._shellcmd("bash -c \"source %s/oe-init-build-env %s\"" % (self.be.sourcedir, self.be.builddir)) + self._shellcmd("bash -c \"source %s/oe-init-build-env %s\"" % (self.pokydirname, self.be.builddir)) def startBBServer(self): - assert self.be.sourcedir and os.path.exists(self.be.sourcedir) - - self._startBE() - - print self._shellcmd("bash -c \"source %s/oe-init-build-env %s && DATABASE_URL=%s source toaster start noweb && sleep 1\"" % (self.be.sourcedir, self.be.builddir, self.dburl)) + assert self.pokydirname and os.path.exists(self.pokydirname) + print self._shellcmd("bash -c \"source %s/oe-init-build-env %s && DATABASE_URL=%s source toaster start noweb && sleep 1\"" % (self.pokydirname, self.be.builddir, self.dburl)) # FIXME unfortunate sleep 1 - we need to make sure that bbserver is started and the toaster ui is connected # but since they start async without any return, we just wait a bit print "Started server"
@@ -225,10 +231,82 @@ class LocalhostBEController(BuildEnvironmentController): print "Stopped server" def setLayers(self, layers): + """ a word of attention: by convention, the first layer for any build will be poky! """ + assert self.be.sourcedir is not None - layerconf = os.path.join(self.be.builddir, "conf/bblayers.conf") - if not os.path.exists(layerconf): - raise Exception("BE is not consistent: bblayers.conf file missing at ", layerconf) + # set layers in the layersource + + # 1. get a list of repos, and map dirpaths for each layer + gitrepos = {} + for layer in layers: + if not layer.giturl in gitrepos: + gitrepos[layer.giturl] = [] + gitrepos[layer.giturl].append( (layer.name, layer.dirpath, layer.commit)) + for giturl in gitrepos.keys(): + commitid = gitrepos[giturl][0][2] + for e in gitrepos[giturl]: + if commitid != e[2]: + raise BuildSetupException("More than one commit per git url, unsupported configuration") + + def _getgitdirectoryname(url): + import re + components = re.split(r'[\.\/]', url) + return components[-2] if components[-1] == "git" else components[-1] + + layerlist = [] + + # 2. checkout the repositories + for giturl in gitrepos.keys(): + localdirname = os.path.join(self.be.sourcedir, _getgitdirectoryname(giturl)) + print "DEBUG: giturl checking out in current directory", localdirname + + # make sure our directory is a git repository + if os.path.exists(localdirname): + if not giturl in self._shellcmd("git remote -v", localdirname): + raise BuildSetupException("Existing git repository at %s, but with different remotes (not '%s'). Aborting." % (localdirname, giturl)) + else: + self._shellcmd("git clone \"%s\" \"%s\"" % (giturl, localdirname)) + # checkout the needed commit + commit = gitrepos[giturl][0][2] + self._shellcmd("git fetch --all && git checkout \"%s\"" % commit , localdirname) + print "DEBUG: checked out commit ", commit, "to", localdirname + + # if this is the first checkout, take the localdirname as poky dir + if self.pokydirname is None: + print "DEBUG: selected poky dir name", localdirname + self.pokydirname = localdirname + + # verify our repositories + for name, dirpath, commit in gitrepos[giturl]: + localdirpath = os.path.join(localdirname, dirpath) + if not os.path.exists(localdirpath): + raise BuildSetupException("Cannot find layer git path '%s' in checked out repository '%s:%s'. Aborting." % (localdirpath, giturl, commit)) + + layerlist.append(localdirpath) + + print "DEBUG: current layer list ", layerlist + + # 3. configure the build environment, so we have a conf/bblayers.conf + assert self.pokydirname is not None + self._startBE() + + # 4. update the bblayers.conf + bblayerconf = os.path.join(self.be.builddir, "conf/bblayers.conf") + if not os.path.exists(bblayerconf): + raise BuildSetupException("BE is not consistent: bblayers.conf file missing at %s" % bblayerconf) + + conflines = open(bblayerconf, "r").readlines() + + bblayerconffile = open(bblayerconf, "w") + for i in xrange(len(conflines)): + if conflines[i].startswith("# line added by toaster"): + i += 2 + else: + bblayerconffile.write(conflines[i]) + + bblayerconffile.write("\n# line added by toaster build control\nBBLAYERS = \"" + " ".join(layerlist) + "\"") + bblayerconffile.close() + return True def release(self):
lib/toaster/bldcontrol/management/commands/runbuilds.py +14 -9
@@ -1,8 +1,8 @@ from django.core.management.base import NoArgsCommand, CommandError from django.db import transaction from orm.models import Build -from bldcontrol.bbcontroller import getBuildEnvironmentController, ShellCmdException -from bldcontrol.models import BuildRequest, BuildEnvironment +from bldcontrol.bbcontroller import getBuildEnvironmentController, ShellCmdException, BuildSetupException +from bldcontrol.models import BuildRequest, BuildEnvironment, BRError import os class Command(NoArgsCommand):
@@ -25,6 +25,7 @@ class Command(NoArgsCommand): return br def schedule(self): + import traceback try: br = None try:
@@ -63,15 +64,19 @@ class Command(NoArgsCommand): # cleanup to be performed by toaster when the deed is done - except ShellCmdException as e: - import traceback - print " EE Error executing shell command\n", e - traceback.format_exc(e) except Exception as e: - import traceback - traceback.print_exc() - raise e + print " EE Error executing shell command\n", e + traceback.print_exc(e) + BRError.objects.create(req = br, + errtype = str(type(e)), + errmsg = str(e), + traceback = traceback.format_exc(e)) + br.state = BuildRequest.REQ_FAILED + br.save() + bec.be.lock = BuildEnvironment.LOCK_FREE + bec.be.save() + def cleanup(self): from django.utils import timezone
lib/toaster/bldcontrol/migrations/0005_auto__add_brerror.py new file +112 -0
113 lines not on this page · raw message
lib/toaster/bldcontrol/models.py +7 -1
@@ -48,12 +48,14 @@ class BuildRequest(models.Model): REQ_QUEUED = 1 REQ_INPROGRESS = 2 REQ_COMPLETED = 3 + REQ_FAILED = 4 REQUEST_STATE = ( (REQ_CREATED, "created"), (REQ_QUEUED, "queued"), (REQ_INPROGRESS, "in progress"), (REQ_COMPLETED, "completed"), + (REQ_FAILED, "failed"), ) project = models.ForeignKey(Project)
@@ -84,4 +86,8 @@ class BRTarget(models.Model): target = models.CharField(max_length=100) task = models.CharField(max_length=100, null=True) - +class BRError(models.Model): + req = models.ForeignKey(BuildRequest) + errtype = models.CharField(max_length=100) + errmsg = models.TextField() + traceback = models.TextField()
Signature · 1 line
1.9.1
PATCH 6/8 document requirements for the python environment
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
Since Toaster adds a number of specific requirements managed by pip, we document these requirements in order to be able to run bitbake and toaster under virtualenv. The target here is to break the dependency on distro packages for specific python libraries.
In order to start bitbake/Toaster in a distro-independent manner, we use virtualenv and pip. We add venv to the .gitignore list to make room for the virtualenv deployment in this directory.
Use this command sequence to setup the virtualenv:
$ virtualenv venv $ . venv/bin/activate $ cat requirements.txt | xargs pip install
bitbake and Toaster will then run normally under virtualenv.
--- .gitignore | 2 ++ requirements.txt | 4 ++++ 2 files changed, 6 insertions(+) create mode 100644 requirements.txt
.gitignore +2 -0
@@ -7,6 +7,8 @@ pyshtables.py /bin/bitbakec *.swp tags +*.sqlite +venv/ doc/bitbake-user-manual/bitbake-user-manual.html doc/bitbake-user-manual/bitbake-user-manual.pdf doc/bitbake-user-manual/bitbake-user-manual.tgz
requirements.txt new file +4 -0
@@ -0,0 +1,4 @@ +Django==1.5 +South==0.8.4 +argparse==1.2.1 +wsgiref==0.1.2
Signature · 1 line
1.9.1
PATCH 7/8 toaster start script allows for Django 1.6
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
We perform upgrade to Django 1.6 in order to keep using a current Django version. In practice, this means allowing for both 1.5 and 1.6 version in the toaster start script. We also modify the suggestion installation commands to remove "sudo" since now pip can be used in a virtualenv.
Update requirements.txt to install the 1.6 Django release.
--- bin/toaster | 13 ++++++++----- requirements.txt | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-)
bin/toaster +8 -5
@@ -63,8 +63,10 @@ function webserverStartAll() retval=0 python $BBBASEDIR/lib/toaster/manage.py migrate orm || retval=1 fi - python $BBBASEDIR/lib/toaster/manage.py migrate bldcontrol || retval=1 - python $BBBASEDIR/lib/toaster/manage.py checksettings || retval=1 + if [ "x$TOASTER_MANAGED" == "x1" ]; then + python $BBBASEDIR/lib/toaster/manage.py migrate bldcontrol || retval=1 + python $BBBASEDIR/lib/toaster/manage.py checksettings || retval=1 + fi echo "Starting webserver" if [ $retval -eq 0 ]; then python $BBBASEDIR/lib/toaster/manage.py runserver 0.0.0.0:8000 </dev/null >${BUILDDIR}/toaster_web.log 2>&1 & echo $! >${BUILDDIR}/.toastermain.pid
@@ -140,6 +142,7 @@ if [ -z "$ZSH_NAME" ] && [ `basename \"$0\"` = `basename \"$BASH_SOURCE\"` ]; th webserverKillAll RUNNING=0 } + TOASTER_MANAGED=1 export TOASTER_MANAGED=1 webserverStartAll || (echo "Fail to start the web server, stopping" 1>&2 && exit 1) xdg-open http://0.0.0.0:8000/ >/dev/null 2>&1 &
@@ -163,13 +166,13 @@ fi # Verify prerequisites -if ! echo "import django; print (1,5) == django.VERSION[0:2]" | python 2>/dev/null | grep True >/dev/null; then - echo -e "This program needs Django 1.5. Please install with\n\nsudo pip install django==1.5" +if ! echo "import django; print (1,) == django.VERSION[0:1] and django.VERSION[1:2][0] in (5,6)" | python 2>/dev/null | grep True >/dev/null; then + echo -e "This program needs Django 1.5 or 1.6. Please install with\n\npip install django==1.6" return 2 fi if ! echo "import south; print [0,8,4] == map(int,south.__version__.split(\".\"))" | python 2>/dev/null | grep True >/dev/null; then - echo -e "This program needs South 0.8.4. Please install with\n\nsudo pip install south==0.8.4" + echo -e "This program needs South 0.8.4. Please install with\n\npip install south==0.8.4" return 2 fi
requirements.txt +1 -1
@@ -1,4 +1,4 @@ -Django==1.5 +Django==1.6 South==0.8.4 argparse==1.2.1 wsgiref==0.1.2
Signature · 1 line
1.9.1
PATCH 8/8 toasterui: fixing event.data clash
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
This patch fixes a name collision on the event.data in the MetadataEvent class. event.data is used in the event system as a copy of "d" in certain situations, and this collision triggered a bug leading to data loss.
[YOCTO #6332]
--- lib/bb/event.py | 2 +- lib/bb/ui/buildinfohelper.py | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-)
lib/bb/event.py +1 -1
@@ -594,7 +594,7 @@ class MetadataEvent(Event): def __init__(self, eventtype, eventdata): Event.__init__(self) self.type = eventtype - self.data = eventdata + self._localdata = eventdata class SanityCheck(Event): """
lib/bb/ui/buildinfohelper.py +15 -15
@@ -596,8 +596,8 @@ class BuildInfoHelper(object): ## external available methods to store information def store_layer_info(self, event): - assert 'data' in vars(event) - layerinfos = event.data + assert '_localdata' in vars(event) + layerinfos = event._localdata self.internal_state['lvs'] = {} for layer in layerinfos: self.internal_state['lvs'][self.orm_wrapper.get_update_layer_object(layerinfos[layer])] = layerinfos[layer]['version']
@@ -636,10 +636,10 @@ class BuildInfoHelper(object): image_fstypes = self.server.runCommand(["getVariable", "IMAGE_FSTYPES"])[0] for t in self.internal_state['targets']: if t.is_image == True: - output_files = list(event.data.viewkeys()) + output_files = list(event._localdata.viewkeys()) for output in output_files: if t.target in output and output.split('.rootfs.')[1] in image_fstypes: - self.orm_wrapper.save_target_image_file_information(t, output, event.data[output]) + self.orm_wrapper.save_target_image_file_information(t, output, event._localdata[output]) def update_build_information(self, event, errors, warnings, taskfailures): if 'build' in self.internal_state:
@@ -647,8 +647,8 @@ class BuildInfoHelper(object): def store_license_manifest_path(self, event): - deploy_dir = event.data['deploy_dir'] - image_name = event.data['image_name'] + deploy_dir = event._localdata['deploy_dir'] + image_name = event._localdata['image_name'] path = deploy_dir + "/licenses/" + image_name + "/" for target in self.internal_state['targets']: if target.target in image_name:
@@ -696,7 +696,7 @@ class BuildInfoHelper(object): def store_tasks_stats(self, event): - for (taskfile, taskname, taskstats, recipename) in event.data: + for (taskfile, taskname, taskstats, recipename) in event._localdata: localfilepath = taskfile.split(":")[-1] assert localfilepath.startswith("/")
@@ -768,7 +768,7 @@ class BuildInfoHelper(object): def store_missed_state_tasks(self, event): - for (fn, taskname, taskhash, sstatefile) in event.data['missed']: + for (fn, taskname, taskhash, sstatefile) in event._localdata['missed']: identifier = fn + taskname + "_setscene" recipe_information = self._get_recipe_information_from_taskfile(fn)
@@ -787,7 +787,7 @@ class BuildInfoHelper(object): self.orm_wrapper.get_update_task_object(task_information) - for (fn, taskname, taskhash, sstatefile) in event.data['found']: + for (fn, taskname, taskhash, sstatefile) in event._localdata['found']: identifier = fn + taskname + "_setscene" recipe_information = self._get_recipe_information_from_taskfile(fn)
@@ -804,15 +804,15 @@ class BuildInfoHelper(object): def store_target_package_data(self, event): - assert 'data' in vars(event) + assert '_localdata' in vars(event) # for all image targets for target in self.internal_state['targets']: if target.is_image: try: - pkgdata = event.data['pkgdata'] - imgdata = event.data['imgdata'][target.target] + pkgdata = event._localdata['pkgdata'] + imgdata = event._localdata['imgdata'][target.target] self.orm_wrapper.save_target_package_information(self.internal_state['build'], target, imgdata, pkgdata, self.internal_state['recipes']) - filedata = event.data['filedata'][target.target] + filedata = event._localdata['filedata'][target.target] self.orm_wrapper.save_target_file_information(self.internal_state['build'], target, filedata) except KeyError: # we must have not got the data for this image, nothing to save
@@ -922,8 +922,8 @@ class BuildInfoHelper(object): def store_build_package_information(self, event): - assert 'data' in vars(event) - package_info = event.data + assert '_localdata' in vars(event) + package_info = event._localdata self.orm_wrapper.save_build_package_information(self.internal_state['build'], package_info, self.internal_state['recipes'],
Signature · 1 line
1.9.1