mirror of
https://git.yoctoproject.org/poky
synced 2026-01-30 05:18:43 +01:00
Compare commits
21 Commits
fido
...
toaster-do
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1182665de0 | ||
|
|
6302d1baf5 | ||
|
|
3390674247 | ||
|
|
0e6bbf8181 | ||
|
|
3b113312fd | ||
|
|
6f9c4d9778 | ||
|
|
f20de8ac90 | ||
|
|
e5cf3e598e | ||
|
|
46814c99ee | ||
|
|
b0c24033bb | ||
|
|
25f50e24c7 | ||
|
|
a6b357a9af | ||
|
|
57beaf994f | ||
|
|
e1aebfe018 | ||
|
|
f6847b0cd2 | ||
|
|
1e6e27d98d | ||
|
|
73293c6481 | ||
|
|
d7b8f82a64 | ||
|
|
4b64eb444a | ||
|
|
db2a7845a9 | ||
|
|
cb1338dedc |
10
bitbake/LICENSE
Normal file
10
bitbake/LICENSE
Normal file
@@ -0,0 +1,10 @@
|
||||
BitBake is licensed under the GNU General Public License version 2.0. See COPYING for further details.
|
||||
|
||||
The following external components are distributed with this software:
|
||||
|
||||
* The Toaster Simple UI application is based upon the Django project template, the files of which are covered by the BSD license and are copyright (c) Django Software
|
||||
Foundation and individual contributors.
|
||||
|
||||
* Twitter Bootstrap (including Glyphicons), redistributed under the Apache License 2.0.
|
||||
|
||||
* jQuery is redistributed under the MIT license.
|
||||
164
bitbake/bin/toaster
Executable file
164
bitbake/bin/toaster
Executable file
@@ -0,0 +1,164 @@
|
||||
#!/bin/bash
|
||||
# (c) 2013 Intel Corp.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
# This script enables toaster event logging and
|
||||
# starts bitbake resident server
|
||||
# use as: source toaster [start|stop]
|
||||
|
||||
# Helper function to kill a background toaster development server
|
||||
|
||||
function webserverKillAll()
|
||||
{
|
||||
local pidfile
|
||||
for pidfile in ${BUILDDIR}/.toastermain.pid; do
|
||||
if [ -f ${pidfile} ]; then
|
||||
while kill -0 $(< ${pidfile}) 2>/dev/null; do
|
||||
kill -SIGTERM -$(< ${pidfile}) 2>/dev/null
|
||||
sleep 1;
|
||||
done;
|
||||
rm ${pidfile}
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
function webserverStartAll()
|
||||
{
|
||||
retval=0
|
||||
python $BBBASEDIR/lib/toaster/manage.py syncdb || retval=1
|
||||
if [ $retval -eq 1 ]; then
|
||||
echo "Failed db sync, stopping system start" 1>&2
|
||||
else
|
||||
python $BBBASEDIR/lib/toaster/manage.py runserver 0.0.0.0:8000 </dev/null >${BUILDDIR}/toaster_web.log 2>&1 & echo $! >${BUILDDIR}/.toastermain.pid
|
||||
fi
|
||||
return $retval
|
||||
}
|
||||
|
||||
# Helper functions to add a special configuration file
|
||||
|
||||
function addtoConfiguration()
|
||||
{
|
||||
echo "#Created by toaster start script" > ${BUILDDIR}/conf/$2
|
||||
echo $1 >> ${BUILDDIR}/conf/$2
|
||||
}
|
||||
|
||||
# define the stop command
|
||||
function stop_system()
|
||||
{
|
||||
if [ -f ${BUILDDIR}/.toasterui.pid ]; then
|
||||
kill $(< ${BUILDDIR}/.toasterui.pid )
|
||||
rm ${BUILDDIR}/.toasterui.pid
|
||||
fi
|
||||
BBSERVER=localhost:8200 bitbake -m
|
||||
unset BBSERVER
|
||||
webserverKillAll
|
||||
# force stop any misbehaving bitbake server
|
||||
lsof bitbake.lock | awk '{print $2}' | grep "[0-9]\+" | xargs -n1 -r kill
|
||||
}
|
||||
|
||||
# We make sure we're running in the current shell and in a good environment
|
||||
|
||||
if [ -z "$ZSH_NAME" ] && [ `basename \"$0\"` = `basename \"$BASH_SOURCE\"` ]; then
|
||||
echo "Error: This script needs to be sourced. Please run as 'source toaster [start|stop]'" 1>&2;
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$BUILDDIR" ] || [ -z `which bitbake` ]; then
|
||||
echo "Error: Build environment is not setup or bitbake is not in path." 1>&2;
|
||||
return 2
|
||||
fi
|
||||
|
||||
BBBASEDIR=`dirname ${BASH_SOURCE}`/..
|
||||
|
||||
|
||||
# Verify prerequisites
|
||||
|
||||
if ! echo "import django; print (1,4,5) == django.VERSION[0:3]" | python 2>/dev/null | grep True >/dev/null; then
|
||||
echo -e "This program needs Django 1.4.5. Please install with\n\nsudo pip install django==1.4.5"
|
||||
return 2
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Determine the action. If specified by arguments, fine, if not, toggle it
|
||||
if [ "x$1" == "xstart" ] || [ "x$1" == "xstop" ]; then
|
||||
CMD="$1"
|
||||
else
|
||||
if [ -z "$BBSERVER" ]; then
|
||||
CMD="start"
|
||||
else
|
||||
CMD="stop"
|
||||
fi;
|
||||
fi
|
||||
|
||||
NOTOASTERUI=0
|
||||
if [ "x$2" == "xnoui" ]; then
|
||||
NOTOASTERUI=1
|
||||
fi
|
||||
|
||||
echo "The system will $CMD."
|
||||
|
||||
# Make sure it's safe to run by checking bitbake lock
|
||||
|
||||
lock=1
|
||||
if [ -e $BUILDDIR/bitbake.lock ]; then
|
||||
(flock -n 200 ) 200<$BUILDDIR/bitbake.lock || lock=0
|
||||
fi
|
||||
|
||||
if [ ${CMD} == "start" ] && ( [ $lock -eq 0 ] || [ -e $BUILDDIR/.toastermain.pid ] ); then
|
||||
echo "Error: bitbake lock state error. System is already on." 2>&1
|
||||
return 3
|
||||
elif [ ${CMD} == "stop" ] && ( [ $lock -eq 1 ] || ! [ -e $BUILDDIR/.toastermain.pid ] ) ; then
|
||||
echo "Error: bitbake lock state error. Trying to stop a stopped system ?
|
||||
If you think the system is hanged up, you can try to manually stop system with the commands
|
||||
|
||||
# BBSERVER=localhost:8200 bitbake -m
|
||||
|
||||
and
|
||||
|
||||
# webserverKillAll
|
||||
" 2>&1
|
||||
return 3
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Execute the commands
|
||||
|
||||
case $CMD in
|
||||
start )
|
||||
addtoConfiguration "INHERIT+=\"toaster buildhistory\"" toaster.conf
|
||||
webserverStartAll || return 4
|
||||
unset BBSERVER
|
||||
bitbake --postread conf/toaster.conf --server-only -t xmlrpc -B localhost:8200
|
||||
export BBSERVER=localhost:8200
|
||||
if [ $NOTOASTERUI == 0 ]; then # we start the TOASTERUI only if not inhibited
|
||||
bitbake --observe-only -u toasterui >${BUILDDIR}/toaster_ui.log 2>&1 & echo $! >${BUILDDIR}/.toasterui.pid
|
||||
fi
|
||||
# stop system on terminal exit
|
||||
trap stop_system SIGHUP
|
||||
;;
|
||||
stop )
|
||||
stop_system
|
||||
trap '' SIGHUP
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "Successful ${CMD}."
|
||||
|
||||
@@ -91,6 +91,9 @@ class TaskBase(event.Event):
|
||||
|
||||
class TaskStarted(TaskBase):
|
||||
"""Task execution started"""
|
||||
def __init__(self, t, logfile, taskflags, d):
|
||||
super(TaskStarted, self).__init__(t, logfile, d)
|
||||
self.taskflags = taskflags
|
||||
|
||||
class TaskSucceeded(TaskBase):
|
||||
"""Task execution completed"""
|
||||
@@ -422,7 +425,9 @@ def _exec_task(fn, task, d, quieterr):
|
||||
localdata.setVar('BB_LOGFILE', logfn)
|
||||
localdata.setVar('BB_RUNTASK', task)
|
||||
|
||||
event.fire(TaskStarted(task, logfn, localdata), localdata)
|
||||
flags = localdata.getVarFlags(task)
|
||||
|
||||
event.fire(TaskStarted(task, logfn, flags, localdata), localdata)
|
||||
try:
|
||||
for func in (prefuncs or '').split():
|
||||
exec_func(func, localdata)
|
||||
|
||||
@@ -81,7 +81,7 @@ class SkippedPackage:
|
||||
|
||||
|
||||
class CookerFeatures(object):
|
||||
_feature_list = [HOB_EXTRA_CACHES, SEND_DEPENDS_TREE] = range(2)
|
||||
_feature_list = [HOB_EXTRA_CACHES, SEND_DEPENDS_TREE, BASEDATASTORE_TRACKING] = range(3)
|
||||
|
||||
def __init__(self):
|
||||
self._features=set()
|
||||
@@ -177,6 +177,9 @@ class BBCooker:
|
||||
self.data.disableTracking()
|
||||
|
||||
def loadConfigurationData(self):
|
||||
if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
|
||||
self.enableDataTracking()
|
||||
|
||||
self.initConfigurationData()
|
||||
self.databuilder.parseBaseConfiguration()
|
||||
self.data = self.databuilder.data
|
||||
@@ -189,6 +192,10 @@ class BBCooker:
|
||||
bb.data.update_data(self.event_data)
|
||||
bb.parse.init_parser(self.event_data)
|
||||
|
||||
if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
|
||||
self.disableDataTracking()
|
||||
|
||||
|
||||
def modifyConfigurationVar(self, var, val, default_file, op):
|
||||
if op == "append":
|
||||
self.appendConfigurationVar(var, val, default_file)
|
||||
@@ -348,7 +355,6 @@ class BBCooker:
|
||||
open(confpath, 'w').close()
|
||||
|
||||
def parseConfiguration(self):
|
||||
|
||||
# Set log file verbosity
|
||||
verboselogs = bb.utils.to_boolean(self.data.getVar("BB_VERBOSE_LOGS", "0"))
|
||||
if verboselogs:
|
||||
@@ -511,6 +517,7 @@ class BBCooker:
|
||||
depend_tree["packages"] = {}
|
||||
depend_tree["rdepends-pkg"] = {}
|
||||
depend_tree["rrecs-pkg"] = {}
|
||||
depend_tree["layer-priorities"] = self.recipecache.bbfile_config_priorities
|
||||
|
||||
for task in xrange(len(rq.rqdata.runq_fnid)):
|
||||
taskname = rq.rqdata.runq_task[task]
|
||||
@@ -522,6 +529,7 @@ class BBCooker:
|
||||
depend_tree["pn"][pn] = {}
|
||||
depend_tree["pn"][pn]["filename"] = fn
|
||||
depend_tree["pn"][pn]["version"] = version
|
||||
depend_tree["pn"][pn]["inherits"] = self.recipecache.inherits.get(fn, None)
|
||||
|
||||
# if we have extra caches, list all attributes they bring in
|
||||
extra_info = []
|
||||
@@ -1083,7 +1091,6 @@ class BBCooker:
|
||||
|
||||
self.buildSetVars()
|
||||
|
||||
self.recipecache = bb.cache.CacheData(self.caches_array)
|
||||
infos = bb.cache.Cache.parse(fn, self.collection.get_file_appends(fn), \
|
||||
self.data,
|
||||
self.caches_array)
|
||||
@@ -1202,7 +1209,10 @@ class BBCooker:
|
||||
try:
|
||||
v = self.data.getVar(k, True)
|
||||
if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
|
||||
dump[k] = { 'v' : v }
|
||||
dump[k] = {
|
||||
'v' : v ,
|
||||
'history' : self.data.varhistory.variable(k),
|
||||
}
|
||||
for d in flaglist:
|
||||
dump[k][d] = self.data.getVarFlag(k, d)
|
||||
except Exception as e:
|
||||
|
||||
738
bitbake/lib/bb/ui/buildinfohelper.py
Normal file
738
bitbake/lib/bb/ui/buildinfohelper.py
Normal file
@@ -0,0 +1,738 @@
|
||||
#
|
||||
# BitBake ToasterUI Implementation
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import datetime
|
||||
import sys
|
||||
import bb
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "toaster.toastermain.settings")
|
||||
|
||||
import toaster.toastermain.settings as toaster_django_settings
|
||||
from toaster.orm.models import Build, Task, Recipe, Layer_Version, Layer, Target, LogMessage
|
||||
from toaster.orm.models import Variable, VariableHistory
|
||||
from toaster.orm.models import Target_Package, Build_Package, Build_File
|
||||
from toaster.orm.models import Task_Dependency, Build_Package_Dependency
|
||||
from toaster.orm.models import Target_Package_Dependency, Recipe_Dependency
|
||||
from bb.msg import BBLogFormatter as format
|
||||
|
||||
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):
|
||||
pass
|
||||
|
||||
|
||||
def create_build_object(self, build_info):
|
||||
|
||||
build = Build.objects.create(
|
||||
machine=build_info['machine'],
|
||||
image_fstypes=build_info['image_fstypes'],
|
||||
distro=build_info['distro'],
|
||||
distro_version=build_info['distro_version'],
|
||||
started_on=build_info['started_on'],
|
||||
completed_on=build_info['completed_on'],
|
||||
cooker_log_path=build_info['cooker_log_path'],
|
||||
build_name=build_info['build_name'],
|
||||
bitbake_version=build_info['bitbake_version'])
|
||||
|
||||
return build
|
||||
|
||||
def create_target_objects(self, target_info):
|
||||
targets = []
|
||||
for tgt_name in target_info['targets']:
|
||||
tgt_object = Target.objects.create( build = target_info['build'],
|
||||
target = tgt_name,
|
||||
is_image = False,
|
||||
file_name = "",
|
||||
file_size = 0);
|
||||
targets.append(tgt_object)
|
||||
return targets
|
||||
|
||||
def update_build_object(self, build, errors, warnings, taskfailures):
|
||||
|
||||
outcome = Build.SUCCEEDED
|
||||
if errors or taskfailures:
|
||||
outcome = Build.FAILED
|
||||
|
||||
build.completed_on = datetime.datetime.now()
|
||||
build.errors_no = errors
|
||||
build.warnings_no = warnings
|
||||
build.outcome = outcome
|
||||
build.save()
|
||||
|
||||
|
||||
def get_update_task_object(self, task_information):
|
||||
task_object, created = Task.objects.get_or_create(
|
||||
build=task_information['build'],
|
||||
recipe=task_information['recipe'],
|
||||
task_name=task_information['task_name'],
|
||||
)
|
||||
|
||||
for v in vars(task_object):
|
||||
if v in task_information.keys():
|
||||
vars(task_object)[v] = task_information[v]
|
||||
# if we got covered by a setscene task, we're SSTATE
|
||||
if task_object.outcome == Task.OUTCOME_COVERED and 1 == Task.objects.filter(task_executed=True, build = task_object.build, recipe = task_object.recipe, task_name=task_object.task_name+"_setscene").count():
|
||||
task_object.outcome = Task.OUTCOME_SSTATE
|
||||
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
|
||||
elif outcome_task_setscene == Task.OUTCOME_FAILED:
|
||||
task_object.sstate_result = Task.SSTATE_FAILED
|
||||
|
||||
# mark down duration if we have a start time
|
||||
if 'start_time' in task_information.keys():
|
||||
duration = datetime.datetime.now() - task_information['start_time']
|
||||
task_object.elapsed_time = duration.total_seconds()
|
||||
|
||||
task_object.save()
|
||||
return task_object
|
||||
|
||||
|
||||
def get_update_recipe_object(self, recipe_information):
|
||||
|
||||
recipe_object, created = Recipe.objects.get_or_create(
|
||||
layer_version=recipe_information['layer_version'],
|
||||
file_path=recipe_information['file_path'])
|
||||
|
||||
for v in vars(recipe_object):
|
||||
if v in recipe_information.keys():
|
||||
vars(recipe_object)[v] = recipe_information[v]
|
||||
|
||||
recipe_object.save()
|
||||
|
||||
return recipe_object
|
||||
|
||||
def get_layer_version_object(self, layer_version_information):
|
||||
|
||||
layer_version_object = Layer_Version.objects.get_or_create(
|
||||
layer = layer_version_information['layer'],
|
||||
branch = layer_version_information['branch'],
|
||||
commit = layer_version_information['commit'],
|
||||
priority = layer_version_information['priority']
|
||||
)
|
||||
|
||||
layer_version_object[0].save()
|
||||
|
||||
return layer_version_object[0]
|
||||
|
||||
def get_update_layer_object(self, layer_information):
|
||||
|
||||
layer_object = Layer.objects.get_or_create(
|
||||
name=layer_information['name'],
|
||||
local_path=layer_information['local_path'],
|
||||
layer_index_url=layer_information['layer_index_url'])
|
||||
layer_object[0].save()
|
||||
|
||||
return layer_object[0]
|
||||
|
||||
|
||||
def save_target_package_information(self, target_obj, packagedict, bldpkgs, recipes):
|
||||
for p in packagedict:
|
||||
packagedict[p]['object'] = Target_Package.objects.create( target = target_obj,
|
||||
name = p,
|
||||
size = packagedict[p]['size'])
|
||||
if p in bldpkgs:
|
||||
packagedict[p]['object'].version = bldpkgs[p]['version']
|
||||
packagedict[p]['object'].recipe = recipes[bldpkgs[p]['pn']]
|
||||
packagedict[p]['object'].save()
|
||||
|
||||
for p in packagedict:
|
||||
for (px,deptype) in packagedict[p]['depends']:
|
||||
Target_Package_Dependency.objects.create( package = packagedict[p]['object'],
|
||||
depends_on = packagedict[px]['object'],
|
||||
dep_type = deptype);
|
||||
|
||||
|
||||
def create_logmessage(self, 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):
|
||||
# create and save the object
|
||||
bp_object = Build_Package.objects.create( build = build_obj,
|
||||
recipe = recipes[package_info['PN']],
|
||||
name = package_info['PKG'],
|
||||
version = package_info['PKGV'],
|
||||
revision = package_info['PKGR'],
|
||||
summary = package_info['SUMMARY'],
|
||||
description = package_info['DESCRIPTION'],
|
||||
size = int(package_info['PKGSIZE']) * 1024,
|
||||
section = package_info['SECTION'],
|
||||
license = package_info['LICENSE'],
|
||||
)
|
||||
# save any attached file information
|
||||
for path in package_info['FILES_INFO']:
|
||||
fo = Build_File.objects.create( bpackage = bp_object,
|
||||
path = path,
|
||||
size = package_info['FILES_INFO'][path] )
|
||||
|
||||
# save soft dependency information
|
||||
if 'RDEPENDS' in package_info and package_info['RDEPENDS']:
|
||||
for p in bb.utils.explode_deps(package_info['RDEPENDS']):
|
||||
Build_Package_Dependency.objects.get_or_create( package = bp_object,
|
||||
depends_on = p, dep_type = Build_Package_Dependency.TYPE_RDEPENDS)
|
||||
if 'RPROVIDES' in package_info and package_info['RPROVIDES']:
|
||||
for p in bb.utils.explode_deps(package_info['RPROVIDES']):
|
||||
Build_Package_Dependency.objects.get_or_create( package = bp_object,
|
||||
depends_on = p, dep_type = Build_Package_Dependency.TYPE_RPROVIDES)
|
||||
if 'RRECOMMENDS' in package_info and package_info['RRECOMMENDS']:
|
||||
for p in bb.utils.explode_deps(package_info['RRECOMMENDS']):
|
||||
Build_Package_Dependency.objects.get_or_create( package = bp_object,
|
||||
depends_on = p, dep_type = Build_Package_Dependency.TYPE_RRECOMMENDS)
|
||||
if 'RSUGGESTS' in package_info and package_info['RSUGGESTS']:
|
||||
for p in bb.utils.explode_deps(package_info['RSUGGESTS']):
|
||||
Build_Package_Dependency.objects.get_or_create( package = bp_object,
|
||||
depends_on = p, dep_type = Build_Package_Dependency.TYPE_RSUGGESTS)
|
||||
if 'RREPLACES' in package_info and package_info['RREPLACES']:
|
||||
for p in bb.utils.explode_deps(package_info['RREPLACES']):
|
||||
Build_Package_Dependency.objects.get_or_create( package = bp_object,
|
||||
depends_on = p, dep_type = Build_Package_Dependency.TYPE_RREPLACES)
|
||||
if 'RCONFLICTS' in package_info and package_info['RCONFLICTS']:
|
||||
for p in bb.utils.explode_deps(package_info['RCONFLICTS']):
|
||||
Build_Package_Dependency.objects.get_or_create( package = bp_object,
|
||||
depends_on = p, dep_type = Build_Package_Dependency.TYPE_RCONFLICTS)
|
||||
|
||||
return bp_object
|
||||
|
||||
def save_build_variables(self, build_obj, vardump):
|
||||
for k in vardump:
|
||||
if not bool(vardump[k]['func']):
|
||||
value = vardump[k]['v'];
|
||||
if value is None:
|
||||
value = ''
|
||||
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 = ''
|
||||
variable_obj = Variable.objects.create( build = build_obj,
|
||||
variable_name = k,
|
||||
variable_value = value,
|
||||
description = desc)
|
||||
for vh in vardump[k]['history']:
|
||||
VariableHistory.objects.create( variable = variable_obj,
|
||||
file_name = vh['file'],
|
||||
line_number = vh['line'],
|
||||
operation = vh['op'])
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
def __init__(self, server, has_build_history = False):
|
||||
self._configure_django()
|
||||
self.internal_state = {}
|
||||
self.task_order = 0
|
||||
self.server = server
|
||||
self.orm_wrapper = ORMWrapper()
|
||||
self.has_build_history = has_build_history
|
||||
self.tmp_dir = self.server.runCommand(["getVariable", "TMPDIR"])[0]
|
||||
|
||||
def _configure_django(self):
|
||||
# Add toaster to sys path for importing modules
|
||||
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'toaster'))
|
||||
|
||||
###################
|
||||
## methods to convert event/external info into objects that the ORM layer uses
|
||||
|
||||
def _get_layer_dict(self, layer_path):
|
||||
|
||||
layer_info = {}
|
||||
layer_name = layer_path.split('/')[-1]
|
||||
layer_url = 'http://layers.openembedded.org/layerindex/layer/{layer}/'
|
||||
layer_url_name = self._get_url_map_name(layer_name)
|
||||
|
||||
layer_info['name'] = layer_name
|
||||
layer_info['local_path'] = layer_path
|
||||
layer_info['layer_index_url'] = layer_url.format(layer=layer_url_name)
|
||||
|
||||
return layer_info
|
||||
|
||||
def _get_url_map_name(self, layer_name):
|
||||
""" Some layers have a different name on openembedded.org site,
|
||||
this method returns the correct name to use in the URL
|
||||
"""
|
||||
|
||||
url_name = layer_name
|
||||
url_mapping = {'meta': 'openembedded-core'}
|
||||
|
||||
for key in url_mapping.keys():
|
||||
if key == layer_name:
|
||||
url_name = url_mapping[key]
|
||||
|
||||
return url_name
|
||||
|
||||
def _get_layer_information(self):
|
||||
|
||||
layer_info = {}
|
||||
|
||||
return layer_info
|
||||
|
||||
def _get_layer_version_information(self, layer_object):
|
||||
|
||||
layer_version_info = {}
|
||||
layer_version_info['build'] = self.internal_state['build']
|
||||
layer_version_info['layer'] = layer_object
|
||||
layer_version_info['branch'] = self._get_git_branch(layer_object.local_path)
|
||||
layer_version_info['commit'] = self._get_git_revision(layer_object.local_path)
|
||||
layer_version_info['priority'] = 0
|
||||
|
||||
return layer_version_info
|
||||
|
||||
|
||||
def _get_git_branch(self, layer_path):
|
||||
branch = subprocess.Popen("git symbolic-ref HEAD 2>/dev/null ", cwd=layer_path, shell=True, stdout=subprocess.PIPE).communicate()[0]
|
||||
branch = branch.replace('refs/heads/', '').rstrip()
|
||||
return branch
|
||||
|
||||
def _get_git_revision(self, layer_path):
|
||||
revision = subprocess.Popen("git rev-parse HEAD 2>/dev/null ", cwd=layer_path, shell=True, stdout=subprocess.PIPE).communicate()[0].rstrip()
|
||||
return revision
|
||||
|
||||
|
||||
def _get_build_information(self):
|
||||
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]
|
||||
build_info['started_on'] = datetime.datetime.now()
|
||||
build_info['completed_on'] = datetime.datetime.now()
|
||||
build_info['image_fstypes'] = self._remove_redundant(self.server.runCommand(["getVariable", "IMAGE_FSTYPES"])[0] or "")
|
||||
build_info['cooker_log_path'] = self.server.runCommand(["getVariable", "BB_CONSOLELOG"])[0]
|
||||
build_info['build_name'] = self.server.runCommand(["getVariable", "BUILDNAME"])[0]
|
||||
build_info['bitbake_version'] = self.server.runCommand(["getVariable", "BB_VERSION"])[0]
|
||||
|
||||
return build_info
|
||||
|
||||
def _get_task_information(self, event, recipe):
|
||||
|
||||
|
||||
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_path(self, path):
|
||||
def _slkey(layer_version):
|
||||
return len(layer_version.layer.local_path)
|
||||
|
||||
# Heuristics: we always match recipe to the deepest layer path that
|
||||
# we can match to the recipe file path
|
||||
for bl in sorted(self.internal_state['layer_versions'], reverse=True, key=_slkey):
|
||||
if (path.startswith(bl.layer.local_path)):
|
||||
return bl
|
||||
|
||||
#TODO: if we get here, we didn't read layers correctly
|
||||
assert False
|
||||
return None
|
||||
|
||||
def _get_recipe_information_from_build_event(self, event):
|
||||
|
||||
layer_version_obj = self._get_layer_version_for_path(re.split(':', event.taskfile)[-1])
|
||||
|
||||
recipe_info = {}
|
||||
recipe_info['layer_version'] = layer_version_obj
|
||||
recipe_info['file_path'] = re.split(':', event.taskfile)[-1]
|
||||
|
||||
return recipe_info
|
||||
|
||||
def _get_task_build_stats(self, task_object):
|
||||
bs_path = self._get_path_information(task_object)
|
||||
for bp in bs_path: # TODO: split for each target
|
||||
task_build_stats = self._get_build_stats_from_file(bp, task_object.task_name)
|
||||
|
||||
return task_build_stats
|
||||
|
||||
def _get_path_information(self, task_object):
|
||||
build_stats_format = "{tmpdir}/buildstats/{target}-{machine}/{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:
|
||||
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, target=target,
|
||||
machine=machine, buildname=buildname,
|
||||
package=package))
|
||||
|
||||
return build_stats_path
|
||||
|
||||
def _get_build_stats_from_file(self, bs_path, task_name):
|
||||
|
||||
task_bs_filename = str(bs_path) + str(task_name)
|
||||
task_bs = open(task_bs_filename, 'r')
|
||||
|
||||
cpu_usage = 0
|
||||
disk_io = 0
|
||||
startio = ''
|
||||
endio = ''
|
||||
|
||||
for line in task_bs.readlines():
|
||||
if line.startswith('CPU usage: '):
|
||||
cpu_usage = line[11:]
|
||||
elif line.startswith('EndTimeIO: '):
|
||||
endio = line[11:]
|
||||
elif line.startswith('StartTimeIO: '):
|
||||
startio = line[13:]
|
||||
|
||||
task_bs.close()
|
||||
|
||||
if startio and endio:
|
||||
disk_io = int(endio.strip('\n ')) - int(startio.strip('\n '))
|
||||
|
||||
if cpu_usage:
|
||||
cpu_usage = float(cpu_usage.strip('% \n'))
|
||||
|
||||
task_build_stats = {'cpu_usage': cpu_usage, 'disk_io': disk_io}
|
||||
|
||||
return task_build_stats
|
||||
|
||||
def _remove_redundant(self, string):
|
||||
ret = []
|
||||
for i in string.split():
|
||||
if i not in ret:
|
||||
ret.append(i)
|
||||
return " ".join(ret)
|
||||
|
||||
|
||||
################################
|
||||
## external available methods to store information
|
||||
|
||||
def store_layer_info(self):
|
||||
layers = self.server.runCommand(["getVariable", "BBLAYERS"])[0].strip().split(" ")
|
||||
self.internal_state['layers'] = []
|
||||
for layer_path in { l for l in layers if len(l) }:
|
||||
layer_information = self._get_layer_dict(layer_path)
|
||||
self.internal_state['layers'].append(self.orm_wrapper.get_update_layer_object(layer_information))
|
||||
|
||||
def store_started_build(self, event):
|
||||
|
||||
build_information = self._get_build_information()
|
||||
|
||||
build_obj = self.orm_wrapper.create_build_object(build_information)
|
||||
self.internal_state['build'] = build_obj
|
||||
|
||||
# create target information
|
||||
target_information = {}
|
||||
target_information['targets'] = event.getPkgs()
|
||||
target_information['build'] = build_obj
|
||||
|
||||
self.internal_state['targets'] = self.orm_wrapper.create_target_objects(target_information)
|
||||
|
||||
# Load layer information for the build
|
||||
self.internal_state['layer_versions'] = []
|
||||
for layer_object in self.internal_state['layers']:
|
||||
layer_version_information = self._get_layer_version_information(layer_object)
|
||||
self.internal_state['layer_versions'].append(self.orm_wrapper.get_layer_version_object(layer_version_information))
|
||||
|
||||
del self.internal_state['layers']
|
||||
# Save build configuration
|
||||
self.orm_wrapper.save_build_variables(build_obj, self.server.runCommand(["getAllKeysWithFlags", ["doc", "func"]])[0])
|
||||
|
||||
|
||||
def update_build_information(self, event, errors, warnings, taskfailures):
|
||||
if 'build' in self.internal_state:
|
||||
self.orm_wrapper.update_build_object(self.internal_state['build'], errors, warnings, taskfailures)
|
||||
|
||||
def store_started_task(self, event):
|
||||
identifier = event.taskfile + event.taskname
|
||||
|
||||
recipe_information = self._get_recipe_information_from_build_event(event)
|
||||
recipe = self.orm_wrapper.get_update_recipe_object(recipe_information)
|
||||
|
||||
task_information = self._get_task_information(event, recipe)
|
||||
task_information['outcome'] = Task.OUTCOME_NA
|
||||
|
||||
if isinstance(event, bb.runqueue.runQueueTaskSkipped):
|
||||
task_information['task_executed'] = False
|
||||
if event.reason == "covered":
|
||||
task_information['outcome'] = Task.OUTCOME_COVERED
|
||||
if event.reason == "existing":
|
||||
task_information['outcome'] = Task.OUTCOME_EXISTING
|
||||
else:
|
||||
task_information['task_executed'] = True
|
||||
if 'noexec' in vars(event) and event.noexec == True:
|
||||
task_information['script_type'] = Task.CODING_NOEXEC
|
||||
|
||||
self.task_order += 1
|
||||
task_information['order'] = self.task_order
|
||||
task_obj = self.orm_wrapper.get_update_task_object(task_information)
|
||||
|
||||
self.internal_state[identifier] = {'start_time': datetime.datetime.now()}
|
||||
|
||||
def update_and_store_task(self, event):
|
||||
identifier = event.taskfile + event.taskname
|
||||
recipe_information = self._get_recipe_information_from_build_event(event)
|
||||
recipe = self.orm_wrapper.get_update_recipe_object(recipe_information)
|
||||
task_information = self._get_task_information(event,recipe)
|
||||
try:
|
||||
task_information['start_time'] = self.internal_state[identifier]['start_time']
|
||||
except:
|
||||
pass
|
||||
|
||||
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 isinstance(event, (bb.runqueue.runQueueTaskCompleted, bb.runqueue.sceneQueueTaskCompleted)):
|
||||
task_information['outcome'] = Task.OUTCOME_SUCCESS
|
||||
task_build_stats = self._get_task_build_stats(self.orm_wrapper.get_update_task_object(task_information))
|
||||
task_information['cpu_usage'] = task_build_stats['cpu_usage']
|
||||
task_information['disk_io'] = task_build_stats['disk_io']
|
||||
del self.internal_state[identifier]
|
||||
|
||||
if isinstance(event, (bb.runqueue.runQueueTaskFailed, bb.runCommand.sceneQueueTaskFailed)):
|
||||
task_information['outcome'] = Task.OUTCOME_FAILED
|
||||
del self.internal_state[identifier]
|
||||
|
||||
self.orm_wrapper.get_update_task_object(task_information)
|
||||
|
||||
|
||||
def read_target_package_dep_data(self, event):
|
||||
# for all targets
|
||||
for target in self.internal_state['targets']:
|
||||
# verify that we have something to read
|
||||
if not target.is_image or not self.has_build_history:
|
||||
print "not collecting package info ", target.is_image, self.has_build_history
|
||||
break
|
||||
|
||||
# TODO this is a temporary replication of the code in buildhistory.bbclass
|
||||
# This MUST be changed to query the actual BUILD_DIR_IMAGE in the target context when
|
||||
# the capability will be implemented in Bitbake
|
||||
|
||||
MACHINE_ARCH, error = self.server.runCommand(['getVariable', 'MACHINE_ARCH'])
|
||||
TCLIBC, error = self.server.runCommand(['getVariable', 'TCLIBC'])
|
||||
BUILDHISTORY_DIR, error = self.server.runCommand(['getVariable', 'BUILDHISTORY_DIR'])
|
||||
BUILDHISTORY_DIR_IMAGE = "%s/images/%s/%s/%s" % (BUILDHISTORY_DIR, MACHINE_ARCH, TCLIBC, target.target)
|
||||
|
||||
self.internal_state['packages'] = {}
|
||||
|
||||
with open("%s/installed-package-sizes.txt" % BUILDHISTORY_DIR_IMAGE, "r") as fin:
|
||||
for line in fin:
|
||||
line = line.rstrip(";")
|
||||
psize, px = line.split("\t")
|
||||
punit, pname = px.split(" ")
|
||||
self.internal_state['packages'][pname.strip()] = {'size':int(psize)*1024, 'depends' : []}
|
||||
|
||||
with open("%s/depends.dot" % BUILDHISTORY_DIR_IMAGE, "r") as fin:
|
||||
p = re.compile(r' -> ')
|
||||
dot = re.compile(r'.*style=dotted')
|
||||
for line in fin:
|
||||
line = line.rstrip(';')
|
||||
linesplit = p.split(line)
|
||||
if len(linesplit) == 2:
|
||||
pname = linesplit[0].rstrip('"').strip('"')
|
||||
dependsname = linesplit[1].split(" ")[0].strip().strip(";").strip('"').rstrip('"')
|
||||
deptype = Target_Package_Dependency.TYPE_DEPENDS
|
||||
if dot.match(line):
|
||||
deptype = Target_Package_Dependency.TYPE_RECOMMENDS
|
||||
if not pname in self.internal_state['packages']:
|
||||
self.internal_state['packages'][pname] = {'size': 0, 'depends' : []}
|
||||
if not dependsname in self.internal_state['packages']:
|
||||
self.internal_state['packages'][dependsname] = {'size': 0, 'depends' : []}
|
||||
self.internal_state['packages'][pname]['depends'].append((dependsname, deptype))
|
||||
|
||||
self.orm_wrapper.save_target_package_information(target,
|
||||
self.internal_state['packages'],
|
||||
self.internal_state['bldpkgs'], self.internal_state['recipes'])
|
||||
|
||||
|
||||
def store_dependency_information(self, event):
|
||||
# save layer version priorities
|
||||
if 'layer-priorities' in event._depgraph.keys():
|
||||
for lv in event._depgraph['layer-priorities']:
|
||||
(name, path, regexp, priority) = lv
|
||||
layer_version_obj = self._get_layer_version_for_path(path[1:]) # paths start with a ^
|
||||
assert layer_version_obj is not None
|
||||
layer_version_obj.priority = priority
|
||||
layer_version_obj.save()
|
||||
|
||||
# save build time package information
|
||||
self.internal_state['bldpkgs'] = {}
|
||||
for pkg in event._depgraph['packages']:
|
||||
self.internal_state['bldpkgs'][pkg] = event._depgraph['packages'][pkg]
|
||||
|
||||
# save recipe information
|
||||
self.internal_state['recipes'] = {}
|
||||
for pn in event._depgraph['pn']:
|
||||
|
||||
file_name = re.split(':', event._depgraph['pn'][pn]['filename'])[-1]
|
||||
layer_version_obj = self._get_layer_version_for_path(re.split(':', file_name)[-1])
|
||||
|
||||
assert layer_version_obj is not None
|
||||
|
||||
recipe_info = {}
|
||||
recipe_info['name'] = pn
|
||||
recipe_info['version'] = event._depgraph['pn'][pn]['version']
|
||||
recipe_info['layer_version'] = layer_version_obj
|
||||
recipe_info['summary'] = event._depgraph['pn'][pn]['summary']
|
||||
recipe_info['license'] = event._depgraph['pn'][pn]['license']
|
||||
recipe_info['description'] = event._depgraph['pn'][pn]['description']
|
||||
recipe_info['section'] = event._depgraph['pn'][pn]['section']
|
||||
recipe_info['licensing_info'] = 'Not Available'
|
||||
recipe_info['homepage'] = event._depgraph['pn'][pn]['homepage']
|
||||
recipe_info['bugtracker'] = event._depgraph['pn'][pn]['bugtracker']
|
||||
recipe_info['file_path'] = file_name
|
||||
recipe = self.orm_wrapper.get_update_recipe_object(recipe_info)
|
||||
if 'inherits' in event._depgraph['pn'][pn].keys():
|
||||
recipe.is_image = True in map(lambda x: x.endswith('image.bbclass'), event._depgraph['pn'][pn]['inherits'])
|
||||
else:
|
||||
recipe.is_image = False
|
||||
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
|
||||
|
||||
# save recipe dependency
|
||||
# buildtime
|
||||
for recipe in event._depgraph['depends']:
|
||||
try:
|
||||
target = self.internal_state['recipes'][recipe]
|
||||
for dep in event._depgraph['depends'][recipe]:
|
||||
dependency = self.internal_state['recipes'][dep]
|
||||
Recipe_Dependency.objects.get_or_create( recipe = target,
|
||||
depends_on = dependency, dep_type = Recipe_Dependency.TYPE_DEPENDS)
|
||||
except KeyError: # we'll not get recipes for key w/ values listed in ASSUME_PROVIDED
|
||||
pass
|
||||
|
||||
# runtime
|
||||
for recipe in event._depgraph['rdepends-pn']:
|
||||
try:
|
||||
target = self.internal_state['recipes'][recipe]
|
||||
for dep in event._depgraph['rdepends-pn'][recipe]:
|
||||
dependency = self.internal_state['recipes'][dep]
|
||||
Recipe_Dependency.objects.get_or_create( recipe = target,
|
||||
depends_on = dependency, dep_type = Recipe_Dependency.TYPE_RDEPENDS)
|
||||
|
||||
except KeyError: # we'll not get recipes for key w/ values listed in ASSUME_PROVIDED
|
||||
pass
|
||||
|
||||
# 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
|
||||
|
||||
for taskdesc in event._depgraph['tdepends']:
|
||||
target = _save_a_task(taskdesc)
|
||||
for taskdesc1 in event._depgraph['tdepends'][taskdesc]:
|
||||
dep = _save_a_task(taskdesc1)
|
||||
Task_Dependency.objects.get_or_create( task = target, depends_on = dep )
|
||||
|
||||
def store_build_package_information(self, event):
|
||||
package_info = event.data
|
||||
self.orm_wrapper.save_build_package_information(self.internal_state['build'],
|
||||
package_info,
|
||||
self.internal_state['recipes'],
|
||||
)
|
||||
|
||||
def _store_log_information(self, level, text):
|
||||
log_information = {}
|
||||
log_information['build'] = self.internal_state['build']
|
||||
log_information['level'] = level
|
||||
log_information['message'] = text
|
||||
self.orm_wrapper.create_logmessage(log_information)
|
||||
|
||||
def store_log_info(self, text):
|
||||
self._store_log_information(LogMessage.INFO, text)
|
||||
|
||||
def store_log_warn(self, text):
|
||||
self._store_log_information(LogMessage.WARNING, text)
|
||||
|
||||
def store_log_error(self, text):
|
||||
self._store_log_information(LogMessage.ERROR, text)
|
||||
|
||||
def store_log_event(self, event):
|
||||
# look up license files info from insane.bbclass
|
||||
m = re.match("([^:]*): md5 checksum matched for ([^;]*)", event.msg)
|
||||
if m:
|
||||
(pn, fn) = m.groups()
|
||||
self.internal_state['recipes'][pn].licensing_info = fn
|
||||
self.internal_state['recipes'][pn].save()
|
||||
|
||||
if event.levelno < format.WARNING:
|
||||
return
|
||||
if not 'build' in self.internal_state:
|
||||
return
|
||||
log_information = {}
|
||||
log_information['build'] = self.internal_state['build']
|
||||
if event.levelno >= format.ERROR:
|
||||
log_information['level'] = LogMessage.ERROR
|
||||
elif event.levelno == format.WARNING:
|
||||
log_information['level'] = LogMessage.WARNING
|
||||
log_information['message'] = event.msg
|
||||
log_information['pathname'] = event.pathname
|
||||
log_information['lineno'] = event.lineno
|
||||
self.orm_wrapper.create_logmessage(log_information)
|
||||
|
||||
272
bitbake/lib/bb/ui/toasterui.py
Normal file
272
bitbake/lib/bb/ui/toasterui.py
Normal file
@@ -0,0 +1,272 @@
|
||||
#
|
||||
# BitBake ToasterUI Implementation
|
||||
# based on (No)TTY UI Implementation by Richard Purdie
|
||||
#
|
||||
# Handling output to TTYs or files (no TTY)
|
||||
#
|
||||
# Copyright (C) 2006-2012 Richard Purdie
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from __future__ import division
|
||||
try:
|
||||
import bb
|
||||
except RuntimeError as exc:
|
||||
sys.exit(str(exc))
|
||||
|
||||
from bb.ui import uihelper
|
||||
from bb.ui.buildinfohelper import BuildInfoHelper
|
||||
|
||||
import bb.msg
|
||||
import copy
|
||||
import fcntl
|
||||
import logging
|
||||
import os
|
||||
import progressbar
|
||||
import signal
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
import xmlrpclib
|
||||
|
||||
featureSet = [bb.cooker.CookerFeatures.HOB_EXTRA_CACHES, bb.cooker.CookerFeatures.SEND_DEPENDS_TREE, bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING]
|
||||
|
||||
logger = logging.getLogger("BitBake")
|
||||
interactive = sys.stdout.isatty()
|
||||
|
||||
|
||||
|
||||
def _log_settings_from_server(server):
|
||||
# Get values of variables which control our output
|
||||
includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"])
|
||||
if error:
|
||||
logger.error("Unable to get the value of BBINCLUDELOGS variable: %s" % error)
|
||||
raise BaseException(error)
|
||||
loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"])
|
||||
if error:
|
||||
logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s" % error)
|
||||
raise BaseException(error)
|
||||
return includelogs, loglines
|
||||
|
||||
def main(server, eventHandler, params ):
|
||||
|
||||
includelogs, loglines = _log_settings_from_server(server)
|
||||
|
||||
# verify and warn
|
||||
build_history_enabled = True
|
||||
inheritlist, error = server.runCommand(["getVariable", "INHERIT"])
|
||||
if not "buildhistory" in inheritlist.split(" "):
|
||||
logger.warn("buildhistory is not enabled. Please enable INHERIT += \"buildhistory\" to see image details.")
|
||||
build_history_enabled = False
|
||||
|
||||
helper = uihelper.BBUIHelper()
|
||||
|
||||
console = logging.StreamHandler(sys.stdout)
|
||||
format_str = "%(levelname)s: %(message)s"
|
||||
format = bb.msg.BBLogFormatter(format_str)
|
||||
bb.msg.addDefaultlogFilter(console)
|
||||
console.setFormatter(format)
|
||||
logger.addHandler(console)
|
||||
|
||||
if not params.observe_only:
|
||||
logger.error("ToasterUI can only work in observer mode")
|
||||
return
|
||||
|
||||
|
||||
main.shutdown = 0
|
||||
interrupted = False
|
||||
return_value = 0
|
||||
errors = 0
|
||||
warnings = 0
|
||||
taskfailures = []
|
||||
|
||||
buildinfohelper = BuildInfoHelper(server, build_history_enabled)
|
||||
buildinfohelper.store_layer_info()
|
||||
|
||||
|
||||
while True:
|
||||
try:
|
||||
event = eventHandler.waitEvent(0.25)
|
||||
|
||||
if event is None:
|
||||
if main.shutdown > 0:
|
||||
break
|
||||
continue
|
||||
|
||||
helper.eventHandler(event)
|
||||
|
||||
if isinstance(event, bb.event.BuildStarted):
|
||||
buildinfohelper.store_started_build(event)
|
||||
|
||||
if isinstance(event, (bb.build.TaskStarted, bb.build.TaskSucceeded, bb.build.TaskFailedSilent)):
|
||||
buildinfohelper.update_and_store_task(event)
|
||||
continue
|
||||
|
||||
if isinstance(event, bb.event.LogExecTTY):
|
||||
logger.warn(event.msg)
|
||||
continue
|
||||
|
||||
if isinstance(event, logging.LogRecord):
|
||||
buildinfohelper.store_log_event(event)
|
||||
if event.levelno >= format.ERROR:
|
||||
errors = errors + 1
|
||||
return_value = 1
|
||||
elif event.levelno == format.WARNING:
|
||||
warnings = warnings + 1
|
||||
# For "normal" logging conditions, don't show note logs from tasks
|
||||
# but do show them if the user has changed the default log level to
|
||||
# include verbose/debug messages
|
||||
if event.taskpid != 0 and event.levelno <= format.NOTE:
|
||||
continue
|
||||
|
||||
logger.handle(event)
|
||||
continue
|
||||
|
||||
if isinstance(event, bb.build.TaskFailed):
|
||||
buildinfohelper.update_and_store_task(event)
|
||||
return_value = 1
|
||||
logfile = event.logfile
|
||||
if logfile and os.path.exists(logfile):
|
||||
bb.error("Logfile of failure stored in: %s" % logfile)
|
||||
continue
|
||||
|
||||
# these events are unprocessed now, but may be used in the future to log
|
||||
# timing and error informations from the parsing phase in Toaster
|
||||
if isinstance(event, bb.event.ParseStarted):
|
||||
continue
|
||||
if isinstance(event, bb.event.ParseProgress):
|
||||
continue
|
||||
if isinstance(event, bb.event.ParseCompleted):
|
||||
continue
|
||||
if isinstance(event, bb.event.CacheLoadStarted):
|
||||
continue
|
||||
if isinstance(event, bb.event.CacheLoadProgress):
|
||||
continue
|
||||
if isinstance(event, bb.event.CacheLoadCompleted):
|
||||
continue
|
||||
if isinstance(event, bb.event.MultipleProviders):
|
||||
continue
|
||||
if isinstance(event, bb.event.NoProvider):
|
||||
return_value = 1
|
||||
errors = errors + 1
|
||||
if event._runtime:
|
||||
r = "R"
|
||||
else:
|
||||
r = ""
|
||||
|
||||
if event._dependees:
|
||||
text = "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)" % (r, event._item, ", ".join(event._dependees), r)
|
||||
else:
|
||||
text = "Nothing %sPROVIDES '%s'" % (r, event._item)
|
||||
|
||||
logger.error(text)
|
||||
if event._reasons:
|
||||
for reason in event._reasons:
|
||||
logger.error("%s", reason)
|
||||
text += reason
|
||||
buildinfohelper.store_log_error(text)
|
||||
continue
|
||||
|
||||
if isinstance(event, bb.event.ConfigParsed):
|
||||
continue
|
||||
if isinstance(event, bb.event.RecipeParsed):
|
||||
continue
|
||||
|
||||
# end of saved events
|
||||
|
||||
if isinstance(event, (bb.runqueue.sceneQueueTaskStarted, bb.runqueue.runQueueTaskStarted, bb.runqueue.runQueueTaskSkipped)):
|
||||
buildinfohelper.store_started_task(event)
|
||||
continue
|
||||
|
||||
if isinstance(event, bb.runqueue.runQueueTaskCompleted):
|
||||
buildinfohelper.update_and_store_task(event)
|
||||
continue
|
||||
|
||||
if isinstance(event, bb.runqueue.runQueueTaskFailed):
|
||||
buildinfohelper.update_and_store_task(event)
|
||||
taskfailures.append(event.taskstring)
|
||||
logger.error("Task %s (%s) failed with exit code '%s'",
|
||||
event.taskid, event.taskstring, event.exitcode)
|
||||
continue
|
||||
|
||||
if isinstance(event, (bb.runqueue.sceneQueueTaskCompleted, bb.runqueue.sceneQueueTaskFailed)):
|
||||
buildinfohelper.update_and_store_task(event)
|
||||
continue
|
||||
|
||||
|
||||
if isinstance(event, (bb.event.TreeDataPreparationStarted, bb.event.TreeDataPreparationCompleted)):
|
||||
continue
|
||||
|
||||
if isinstance(event, (bb.event.BuildCompleted)):
|
||||
buildinfohelper.read_target_package_dep_data(event)
|
||||
buildinfohelper.update_build_information(event, errors, warnings, taskfailures)
|
||||
continue
|
||||
|
||||
if isinstance(event, (bb.command.CommandCompleted,
|
||||
bb.command.CommandFailed,
|
||||
bb.command.CommandExit)):
|
||||
|
||||
buildinfohelper.update_build_information(event, errors, warnings, taskfailures)
|
||||
|
||||
# we start a new build info
|
||||
errors = 0
|
||||
warnings = 0
|
||||
taskfailures = []
|
||||
buildinfohelper = BuildInfoHelper(server, build_history_enabled)
|
||||
buildinfohelper.store_layer_info()
|
||||
continue
|
||||
|
||||
if isinstance(event, bb.event.MetadataEvent):
|
||||
if event.type == "SinglePackageInfo":
|
||||
buildinfohelper.store_build_package_information(event)
|
||||
continue
|
||||
|
||||
# ignore
|
||||
if isinstance(event, (bb.event.BuildBase,
|
||||
bb.event.StampUpdate,
|
||||
bb.event.RecipePreFinalise,
|
||||
bb.runqueue.runQueueEvent,
|
||||
bb.runqueue.runQueueExitWait,
|
||||
bb.event.OperationProgress,
|
||||
bb.command.CommandFailed,
|
||||
bb.command.CommandExit,
|
||||
bb.command.CommandCompleted,
|
||||
bb.cooker.CookerExit)):
|
||||
continue
|
||||
|
||||
if isinstance(event, bb.event.DepTreeGenerated):
|
||||
buildinfohelper.store_dependency_information(event)
|
||||
continue
|
||||
|
||||
logger.error("Unknown event: %s", event)
|
||||
|
||||
except EnvironmentError as ioerror:
|
||||
# ignore interrupted io
|
||||
if ioerror.args[0] == 4:
|
||||
pass
|
||||
except KeyboardInterrupt:
|
||||
main.shutdown = 1
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
pass
|
||||
|
||||
if interrupted:
|
||||
if return_value == 0:
|
||||
return_value = 1
|
||||
|
||||
return return_value
|
||||
0
bitbake/lib/toaster/__init__.py
Normal file
0
bitbake/lib/toaster/__init__.py
Normal file
0
bitbake/lib/toaster/bldviewer/__init__.py
Normal file
0
bitbake/lib/toaster/bldviewer/__init__.py
Normal file
37
bitbake/lib/toaster/bldviewer/api.py
Normal file
37
bitbake/lib/toaster/bldviewer/api.py
Normal file
@@ -0,0 +1,37 @@
|
||||
#
|
||||
# BitBake Toaster Implementation
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from django.conf.urls import patterns, include, url
|
||||
|
||||
|
||||
urlpatterns = patterns('bldviewer.views',
|
||||
url(r'^builds$', 'model_explorer', {'model_name':'build'}, name='builds'),
|
||||
url(r'^targets$', 'model_explorer', {'model_name':'target'}, name='targets'),
|
||||
url(r'^tasks$', 'model_explorer', {'model_name':'task'}, name='task'),
|
||||
url(r'^task_dependencies$', 'model_explorer', {'model_name':'task_dependency'}, name='task_dependencies'),
|
||||
url(r'^packages$', 'model_explorer', {'model_name':'build_package'}, name='build_packages'),
|
||||
url(r'^package_dependencies$', 'model_explorer', {'model_name':'build_package_dependency'}, name='build_package_dependencies'),
|
||||
url(r'^target_packages$', 'model_explorer', {'model_name':'target_package'}, name='target_packages'),
|
||||
url(r'^package_files$', 'model_explorer', {'model_name':'build_file'}, name='build_files'),
|
||||
url(r'^layers$', 'model_explorer', {'model_name':'layer'}, name='layer'),
|
||||
url(r'^layerversions$', 'model_explorer', {'model_name':'layerversion'}, name='layerversion'),
|
||||
url(r'^recipes$', 'model_explorer', {'model_name':'recipe'}, name='recipe'),
|
||||
url(r'^recipe_dependencies$', 'model_explorer', {'model_name':'recipe_dependency'}, name='recipe_dependencies'),
|
||||
url(r'^variables$', 'model_explorer', {'model_name':'variable'}, name='variables'),
|
||||
url(r'^logmessages$', 'model_explorer', {'model_name':'logmessage'}, name='logmessages'),
|
||||
)
|
||||
4797
bitbake/lib/toaster/bldviewer/static/css/bootstrap.css
vendored
Normal file
4797
bitbake/lib/toaster/bldviewer/static/css/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1982
bitbake/lib/toaster/bldviewer/static/js/bootstrap.js
vendored
Normal file
1982
bitbake/lib/toaster/bldviewer/static/js/bootstrap.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
bitbake/lib/toaster/bldviewer/static/js/jquery-2.0.3.js
vendored
Normal file
6
bitbake/lib/toaster/bldviewer/static/js/jquery-2.0.3.js
vendored
Normal file
File diff suppressed because one or more lines are too long
30
bitbake/lib/toaster/bldviewer/templates/base.html
Normal file
30
bitbake/lib/toaster/bldviewer/templates/base.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
{% load static %}
|
||||
<html>
|
||||
<head>
|
||||
<title>Toaster Simple Explorer</title>
|
||||
<script src="{% static 'js/jquery-2.0.3.js' %}">
|
||||
</script>
|
||||
<script src="{% static 'js/bootstrap.js' %}">
|
||||
</script>
|
||||
<link href="{% static 'css/bootstrap.css' %}" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
|
||||
<body style="height: 100%">
|
||||
<div style="width:100%; height: 100%; position:absolute">
|
||||
<div style="width: 100%; height: 3em" class="nav">
|
||||
<ul class="nav nav-tabs">
|
||||
<li><a href="{% url all-builds %}">All Builds</a></li>
|
||||
<li><a href="{% url all-layers %}">All Layers</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div style="overflow-y:scroll; width: 100%; position: absolute; top: 3em; bottom:70px ">
|
||||
{% block pagecontent %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class="navbar" style="position: absolute; bottom: 0; width:100%"><br/>About Toaster | Yocto Project </div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
17
bitbake/lib/toaster/bldviewer/templates/basebuildpage.html
Normal file
17
bitbake/lib/toaster/bldviewer/templates/basebuildpage.html
Normal file
@@ -0,0 +1,17 @@
|
||||
{% extends "basetable.html" %}
|
||||
|
||||
{% block pagename %}
|
||||
<ul class="nav nav-tabs" style="display: inline-block">
|
||||
<li><a>Build {{build.target_set.all|join:" "}} at {{build.started_on}} : </a></li>
|
||||
<li><a href="{% url task build.id %}"> Tasks </a></li>
|
||||
<li><a href="{% url bpackage build.id %}"> Build Packages </a></li>
|
||||
{% for t in build.target_set.all %}
|
||||
{% if t.is_image %}
|
||||
<li><a href="{% url tpackage build.id t.pk %}"> Packages for {{t.target}} </a> </li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<li><a href="{% url configuration build.id %}"> Configuration </a> </li>
|
||||
</ul>
|
||||
<h1>Toaster - Build {% block pagetitle %} {% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
46
bitbake/lib/toaster/bldviewer/templates/basetable.html
Normal file
46
bitbake/lib/toaster/bldviewer/templates/basetable.html
Normal file
@@ -0,0 +1,46 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block pagecontent %}
|
||||
<script>
|
||||
function showhideTableColumn(i, sh) {
|
||||
if (sh)
|
||||
$('td:nth-child('+i+'),th:nth-child('+i+')').show();
|
||||
else
|
||||
$('td:nth-child('+i+'),th:nth-child('+i+')').hide();
|
||||
}
|
||||
|
||||
|
||||
function filterTableRows(test) {
|
||||
if (test.length > 0) {
|
||||
var r = test.split(/[ ,]+/).map(function (e) { return new RegExp(e, 'i') });
|
||||
$('tr.data').map( function (i, el) {
|
||||
(! r.map(function (j) { return j.test($(el).html())}).reduce(function (c, p) { return c && p;} )) ? $(el).hide() : $(el).show();
|
||||
});
|
||||
} else
|
||||
{
|
||||
$('tr.data').show();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div style="margin-bottom: 0.5em">
|
||||
|
||||
{% block pagename %}
|
||||
{% endblock %}
|
||||
<div align="left" style="display:inline-block; width: 40%; margin-left: 2em"> Search: <input type="search" id="filterstring" style="width: 80%" onkeyup="filterTableRows($('#filterstring').val())" autocomplete="off">
|
||||
</div>
|
||||
{% if hideshowcols %}
|
||||
<div align="right" style="display: inline-block; width: 40%">Show/Hide columns:
|
||||
{% for i in hideshowcols %}
|
||||
<span>{{i.name}} <input type="checkbox" id="ct{{i.name}}" onchange="showhideTableColumn({{i.order}}, $('#ct{{i.name}}').is(':checked'))" checked autocomplete="off"></span> |
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<table class="table table-striped table-condensed" style="width:95%">
|
||||
{% block pagetable %}
|
||||
{% endblock %}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
24
bitbake/lib/toaster/bldviewer/templates/bfile.html
Normal file
24
bitbake/lib/toaster/bldviewer/templates/bfile.html
Normal file
@@ -0,0 +1,24 @@
|
||||
{% extends "basebuildpage.html" %}
|
||||
|
||||
{% block pagetitle %}Files for package {{files.0.bpackage.name}} {% endblock %}
|
||||
{% block pagetable %}
|
||||
{% if not files %}
|
||||
<p>No files were recorded for this package!</p>
|
||||
{% else %}
|
||||
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Size (Bytes)</th>
|
||||
</tr>
|
||||
|
||||
{% for file in files %}
|
||||
|
||||
<tr class="data">
|
||||
<td>{{file.path}}</td>
|
||||
<td>{{file.size}}</td>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
44
bitbake/lib/toaster/bldviewer/templates/bpackage.html
Normal file
44
bitbake/lib/toaster/bldviewer/templates/bpackage.html
Normal file
@@ -0,0 +1,44 @@
|
||||
{% extends "basebuildpage.html" %}
|
||||
|
||||
{% block pagetitle %}Packages{% endblock %}
|
||||
{% block pagetable %}
|
||||
{% if not packages %}
|
||||
<p>No packages were recorded for this target!</p>
|
||||
{% else %}
|
||||
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Version</th>
|
||||
<th>Recipe</th>
|
||||
<th>Summary</th>
|
||||
<th>Section</th>
|
||||
<th>Description</th>
|
||||
<th>Size on host disk (Bytes)</th>
|
||||
<th>License</th>
|
||||
<th>Dependencies List (all)</th>
|
||||
</tr>
|
||||
|
||||
{% for package in packages %}
|
||||
|
||||
<tr class="data">
|
||||
<td><a name="#{{package.name}}" href="{% url bfile build.pk package.pk %}">{{package.name}} ({{package.filelist_bpackage.count}} files)</a></td>
|
||||
<td>{{package.version}}-{{package.revision}}</td>
|
||||
<td><a href="{% url layer_versions_recipes package.recipe.layer_version_id %}#{{package.recipe.name}}">{{package.recipe.name}}</a>{{package.package_name}}</a></td>
|
||||
|
||||
<td>{{package.summary}}</td>
|
||||
<td>{{package.section}}</td>
|
||||
<td>{{package.description}}</td>
|
||||
<td>{{package.size}}</td>
|
||||
<td>{{package.license}}</td>
|
||||
<td>
|
||||
<div style="height: 3em; overflow:auto">
|
||||
{% for bpd in package.bpackage_dependencies_package.all %}
|
||||
{{bpd.dep_type}}: {{bpd.depends_on}} <br/>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
43
bitbake/lib/toaster/bldviewer/templates/build.html
Normal file
43
bitbake/lib/toaster/bldviewer/templates/build.html
Normal file
@@ -0,0 +1,43 @@
|
||||
{% extends "basetable.html" %}
|
||||
|
||||
{% block pagename %}
|
||||
<h1>Toaster - Builds</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block pagetable %}
|
||||
|
||||
{% load projecttags %}
|
||||
<tr>
|
||||
<th>Outcome</th>
|
||||
<th>Started On</th>
|
||||
<th>Completed On</th>
|
||||
<th>Target</th>
|
||||
<th>Machine</th>
|
||||
<th>Time</th>
|
||||
<th>Errors</th>
|
||||
<th>Warnings</th>
|
||||
<th>Output</th>
|
||||
<th>Log</th>
|
||||
<th>Bitbake Version</th>
|
||||
<th>Build Name</th>
|
||||
</tr>
|
||||
{% for build in builds %}
|
||||
<tr class="data">
|
||||
<td><a href="{% url configuration build.id %}">{{build.get_outcome_display}}</a></td>
|
||||
<td>{{build.started_on}}</td>
|
||||
<td>{{build.completed_on}}</td>
|
||||
<td>{% for t in build.target_set.all %}<a href="{% url tpackage build.id t.id %}">{{t.target}}</a>{% if t.is_image %} (Img){% endif %}<br/>{% endfor %}</td>
|
||||
<td>{{build.machine}}</td>
|
||||
<td>{% time_difference build.started_on build.completed_on %}</td>
|
||||
<td>{{build.errors_no}}:{% if build.errors_no %}{% for error in logs %}{% if error.build == build %}{% if error.level == 2 %}<p>{{error.message}}</p>{% endif %}{% endif %}{% endfor %}{% else %}None{% endif %}</td>
|
||||
<td>{{build.warnings_no}}:{% if build.warnings_no %}{% for warning in logs %}{% if warning.build == build %}{% if warning.level == 1 %}<p>{{warning.message}}</p>{% endif %}{% endif %}{% endfor %}{% else %}None{% endif %}</td>
|
||||
<td>{% if build.outcome == 0 %}{% for t in build.target_set.all %}{% if t.is_image %}{{build.image_fstypes}}{% endif %}{% endfor %}{% endif %}</td>
|
||||
<td>{{build.cooker_log_path}}</td>
|
||||
<td>{{build.bitbake_version}}</td>
|
||||
<td>{{build.build_name}}</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
22
bitbake/lib/toaster/bldviewer/templates/configuration.html
Normal file
22
bitbake/lib/toaster/bldviewer/templates/configuration.html
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends "basebuildpage.html" %}
|
||||
|
||||
{% block pagetitle %}Configuration{% endblock %}
|
||||
{% block pagetable %}
|
||||
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Definition history</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
|
||||
{% for variable in configuration %}
|
||||
|
||||
<tr class="data">
|
||||
<td>{{variable.variable_name}}</td>
|
||||
<td>{% if variable.description %}{{variable.description}}{% endif %}</td>
|
||||
<td>{% for vh in variable.variablehistory_set.all %}{{vh.operation}} in {{vh.file_name}}:{{vh.line_number}}<br/>{%endfor%}</td>
|
||||
<td>{{variable.variable_value}}</td>
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
||||
34
bitbake/lib/toaster/bldviewer/templates/layer.html
Normal file
34
bitbake/lib/toaster/bldviewer/templates/layer.html
Normal file
@@ -0,0 +1,34 @@
|
||||
{% extends "basetable.html" %}
|
||||
|
||||
{% block pagename %}
|
||||
<h1>Toaster - Layers</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block pagetable %}
|
||||
{% load projecttags %}
|
||||
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Local Path</th>
|
||||
<th>Layer Index URL</th>
|
||||
<th>Known Versions</th>
|
||||
</tr>
|
||||
|
||||
{% for layer in layers %}
|
||||
|
||||
<tr class="data">
|
||||
<td>{{layer.name}}</td>
|
||||
<td>{{layer.local_path}}</td>
|
||||
<td><a href='{{layer.layer_index_url}}'>{{layer.layer_index_url}}</a></td>
|
||||
<td><table>
|
||||
{% for lv in layer.versions %}
|
||||
<tr><td>
|
||||
<a href="{% url layer_versions_recipes lv.id %}">({{lv.priority}}){{lv.branch}}:{{lv.commit}} ({{lv.count}} recipes)</a>
|
||||
</td></tr>
|
||||
{% endfor %}
|
||||
</table></td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
||||
36
bitbake/lib/toaster/bldviewer/templates/package.html
Normal file
36
bitbake/lib/toaster/bldviewer/templates/package.html
Normal file
@@ -0,0 +1,36 @@
|
||||
{% extends "basebuildpage.html" %}
|
||||
|
||||
{% block pagetable %}
|
||||
{% if not packages %}
|
||||
<p>No packages were recorded for this target!</p>
|
||||
{% else %}
|
||||
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Version</th>
|
||||
<th>Size (Bytes)</th>
|
||||
<th>Recipe</th>
|
||||
<th>Depends on</th>
|
||||
</tr>
|
||||
|
||||
{% for package in packages %}
|
||||
|
||||
<tr class="data">
|
||||
<td><a name="#{{package.name}}">{{package.name}}</a></td>
|
||||
<td>{{package.version}}</td>
|
||||
<td>{{package.size}}</td>
|
||||
<td>{%if package.recipe %}<a name="{{package.recipe.name}}.{{package.package_name}}">
|
||||
<a href="{% url layer_versions_recipes package.recipe.layer_version_id %}#{{package.recipe.name}}">{{package.recipe.name}}</a>{{package.package_name}}</a>{%endif%}</td>
|
||||
<td>
|
||||
<div style="height: 4em; overflow:auto">
|
||||
{% for d in package.tpackage_dependencies_package.all %}
|
||||
<a href="#{{d.name}}">{{d.depends_on.name}}</a><br/>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
53
bitbake/lib/toaster/bldviewer/templates/recipe.html
Normal file
53
bitbake/lib/toaster/bldviewer/templates/recipe.html
Normal file
@@ -0,0 +1,53 @@
|
||||
{% extends "basetable.html" %}
|
||||
|
||||
{% block pagename %}
|
||||
<ul class="nav nav-tabs" style="display: inline-block">
|
||||
<li><a>Layer {{layer_version.layer.name}} : {{layer_version.branch}} : {{layer_version.commit}} : {{layer_version.priority}}</a></li>
|
||||
</ul>
|
||||
<h1>Toaster - Recipes for a Layer</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block pagetable %}
|
||||
{% load projecttags %}
|
||||
|
||||
<tr>
|
||||
</tr>
|
||||
<th>Name</th>
|
||||
<th>Version</th>
|
||||
<th>Summary</th>
|
||||
<th>Description</th>
|
||||
<th>Section</th>
|
||||
<th>License</th>
|
||||
<th>License file</th>
|
||||
<th>Homepage</th>
|
||||
<th>Bugtracker</th>
|
||||
<th>Author</th>
|
||||
<th>File_path</th>
|
||||
<th style="width: 30em">Recipe Dependency</th>
|
||||
|
||||
|
||||
{% for recipe in recipes %}
|
||||
|
||||
<tr class="data">
|
||||
<td><a name="{{recipe.name}}">{{recipe.name}}</a></td>
|
||||
<td>{{recipe.version}}</td>
|
||||
<td>{{recipe.summary}}</td>
|
||||
<td>{{recipe.description}}</td>
|
||||
<td>{{recipe.section}}</td>
|
||||
<td>{{recipe.license}}</td>
|
||||
<td>{{recipe.licensing_info}}</td>
|
||||
<td>{{recipe.homepage}}</td>
|
||||
<td>{{recipe.bugtracker}}</td>
|
||||
<td>{{recipe.file_path}}</td>
|
||||
<td>
|
||||
<div style="height: 5em; overflow:auto">
|
||||
{% for rr in recipe.r_dependencies_recipe.all %}
|
||||
<a href="#{{rr.depends_on.name}}">{{rr.depends_on.name}}</a><br/>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
||||
63
bitbake/lib/toaster/bldviewer/templates/task.html
Normal file
63
bitbake/lib/toaster/bldviewer/templates/task.html
Normal file
@@ -0,0 +1,63 @@
|
||||
{% extends "basebuildpage.html" %}
|
||||
|
||||
{% block pagetitle %}Tasks{% endblock %}
|
||||
{% block pagetable %}
|
||||
{% if not tasks %}
|
||||
<p>No tasks were executed in this build!</p>
|
||||
{% else %}
|
||||
|
||||
<tr>
|
||||
<th>Order</th>
|
||||
<th>Task</th>
|
||||
<th>Recipe Version</th>
|
||||
<th>Task Type</th>
|
||||
<th>Checksum</th>
|
||||
<th>Outcome</th>
|
||||
<th>Message</th>
|
||||
<th>Logfile</th>
|
||||
<th>Time</th>
|
||||
<th>CPU usage</th>
|
||||
<th>Disk I/O</th>
|
||||
<th>Script type</th>
|
||||
<th>File path</th>
|
||||
<th>Depends</th>
|
||||
</tr>
|
||||
|
||||
{% for task in tasks %}
|
||||
|
||||
<tr class="data">
|
||||
<td>{{task.order}}</td>
|
||||
<td><a name="{{task.recipe.name}}.{{task.task_name}}">
|
||||
<a href="{% url layer_versions_recipes task.recipe.layer_version_id %}#{{task.recipe.name}}">{{task.recipe.name}}</a>.{{task.task_name}}</a></td>
|
||||
<td>{{task.recipe.version}}</td>
|
||||
|
||||
{% if task.task_executed %}
|
||||
<td>Executed</td>
|
||||
{% else %}
|
||||
<td>Prebuilt</td>
|
||||
{% endif %}
|
||||
|
||||
<td>{{task.sstate_checksum}}</td>
|
||||
<td>{{task.get_outcome_display}}{% if task.provider %}</br>(by <a href="#{{task.provider.recipe.name}}.{{task.provider.task_name}}">{{task.provider.recipe.name}}.{{task.provider.task_name}}</a>){% endif %}</td>
|
||||
<td><p>{{task.message}}</td>
|
||||
<td><a target="_fileview" href="file:///{{task.logfile}}">{{task.logfile}}</a></td>
|
||||
<td>{{task.elapsed_time}}</td>
|
||||
<td>{{task.cpu_usage}}</td>
|
||||
<td>{{task.disk_io}}</td>
|
||||
<td>{{task.get_script_type_display}}</td>
|
||||
<td><a target="_fileview" href="file:///{{task.recipe.file_path}}">{{task.recipe.file_path}}</a></td>
|
||||
<td>
|
||||
<div style="height: 3em; overflow:auto">
|
||||
{% for tt in task.task_dependencies_task.all %}
|
||||
<a href="#{{tt.depends_on.recipe.name}}.{{tt.depends_on.task_name}}">
|
||||
{{tt.depends_on.recipe.name}}.{{tt.depends_on.task_name}}</a><br/>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
26
bitbake/lib/toaster/bldviewer/templatetags/projecttags.py
Normal file
26
bitbake/lib/toaster/bldviewer/templatetags/projecttags.py
Normal file
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# BitBake Toaster Implementation
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from datetime import datetime
|
||||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@register.simple_tag
|
||||
def time_difference(start_time, end_time):
|
||||
return end_time - start_time
|
||||
32
bitbake/lib/toaster/bldviewer/urls.py
Normal file
32
bitbake/lib/toaster/bldviewer/urls.py
Normal file
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# BitBake Toaster Implementation
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from django.conf.urls import patterns, include, url
|
||||
from django.views.generic.simple import redirect_to
|
||||
|
||||
urlpatterns = patterns('bldviewer.views',
|
||||
url(r'^builds/$', 'build', name='all-builds'),
|
||||
url(r'^build/(?P<build_id>\d+)/task/$', 'task', name='task'),
|
||||
url(r'^build/(?P<build_id>\d+)/packages/$', 'bpackage', name='bpackage'),
|
||||
url(r'^build/(?P<build_id>\d+)/package/(?P<package_id>\d+)/files/$', 'bfile', name='bfile'),
|
||||
url(r'^build/(?P<build_id>\d+)/target/(?P<target_id>\d+)/packages/$', 'tpackage', name='tpackage'),
|
||||
url(r'^build/(?P<build_id>\d+)/configuration/$', 'configuration', name='configuration'),
|
||||
url(r'^layers/$', 'layer', name='all-layers'),
|
||||
url(r'^layerversions/(?P<layerversion_id>\d+)/recipes/.*$', 'layer_versions_recipes', name='layer_versions_recipes'),
|
||||
url(r'^$', redirect_to, {'url': 'builds/'}),
|
||||
)
|
||||
260
bitbake/lib/toaster/bldviewer/views.py
Normal file
260
bitbake/lib/toaster/bldviewer/views.py
Normal file
@@ -0,0 +1,260 @@
|
||||
#
|
||||
# BitBake Toaster Implementation
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import operator
|
||||
|
||||
from django.db.models import Q
|
||||
from django.shortcuts import render
|
||||
from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, Target_Package, LogMessage, Variable
|
||||
from orm.models import Task_Dependency, Recipe_Dependency, Build_Package, Build_File, Build_Package_Dependency
|
||||
from django.views.decorators.cache import cache_control
|
||||
|
||||
@cache_control(no_store=True)
|
||||
def build(request):
|
||||
template = 'build.html'
|
||||
build_info = Build.objects.all()
|
||||
|
||||
logs = LogMessage.objects.all()
|
||||
|
||||
context = {'builds': build_info, 'logs': logs ,
|
||||
'hideshowcols' : [
|
||||
{'name': 'Output', 'order':10},
|
||||
{'name': 'Log', 'order':11},
|
||||
]}
|
||||
|
||||
return render(request, template, context)
|
||||
|
||||
|
||||
def _find_task_revdep(task):
|
||||
tp = []
|
||||
for p in Task_Dependency.objects.filter(depends_on=task):
|
||||
tp.append(p.task);
|
||||
return tp
|
||||
|
||||
def _find_task_provider(task):
|
||||
task_revdeps = _find_task_revdep(task)
|
||||
for tr in task_revdeps:
|
||||
if tr.outcome != Task.OUTCOME_COVERED:
|
||||
return tr
|
||||
for tr in task_revdeps:
|
||||
trc = _find_task_provider(tr)
|
||||
if trc is not None:
|
||||
return trc
|
||||
return None
|
||||
|
||||
def task(request, build_id):
|
||||
template = 'task.html'
|
||||
|
||||
tasks = Task.objects.filter(build=build_id)
|
||||
|
||||
for t in tasks:
|
||||
if t.outcome == Task.OUTCOME_COVERED:
|
||||
t.provider = _find_task_provider(t)
|
||||
|
||||
context = {'build': Build.objects.filter(pk=build_id)[0], 'tasks': tasks}
|
||||
|
||||
return render(request, template, context)
|
||||
|
||||
def configuration(request, build_id):
|
||||
template = 'configuration.html'
|
||||
variables = Variable.objects.filter(build=build_id)
|
||||
context = {'build': Build.objects.filter(pk=build_id)[0], 'configuration' : variables}
|
||||
return render(request, template, context)
|
||||
|
||||
def bpackage(request, build_id):
|
||||
template = 'bpackage.html'
|
||||
packages = Build_Package.objects.filter(build = build_id)
|
||||
context = {'build': Build.objects.filter(pk=build_id)[0], 'packages' : packages}
|
||||
return render(request, template, context)
|
||||
|
||||
def bfile(request, build_id, package_id):
|
||||
template = 'bfile.html'
|
||||
files = Build_File.objects.filter(bpackage = package_id)
|
||||
context = {'build': Build.objects.filter(pk=build_id)[0], 'files' : files}
|
||||
return render(request, template, context)
|
||||
|
||||
def tpackage(request, build_id, target_id):
|
||||
template = 'package.html'
|
||||
|
||||
packages = Target_Package.objects.filter(target=target_id)
|
||||
|
||||
context = {'build' : Build.objects.filter(pk=build_id)[0],'packages': packages}
|
||||
|
||||
return render(request, template, context)
|
||||
|
||||
def layer(request):
|
||||
template = 'layer.html'
|
||||
layer_info = Layer.objects.all()
|
||||
|
||||
for li in layer_info:
|
||||
li.versions = Layer_Version.objects.filter(layer = li)
|
||||
for liv in li.versions:
|
||||
liv.count = Recipe.objects.filter(layer_version__id = liv.id).count()
|
||||
|
||||
context = {'layers': layer_info}
|
||||
|
||||
return render(request, template, context)
|
||||
|
||||
|
||||
def layer_versions_recipes(request, layerversion_id):
|
||||
template = 'recipe.html'
|
||||
recipes = Recipe.objects.filter(layer_version__id = layerversion_id)
|
||||
|
||||
context = {'recipes': recipes,
|
||||
'layer_version' : Layer_Version.objects.filter( id = layerversion_id )[0]
|
||||
}
|
||||
|
||||
return render(request, template, context)
|
||||
|
||||
#### API
|
||||
|
||||
import json
|
||||
from django.core import serializers
|
||||
from django.http import HttpResponse, HttpResponseBadRequest
|
||||
|
||||
|
||||
def model_explorer(request, model_name):
|
||||
|
||||
DESCENDING = 'desc'
|
||||
response_data = {}
|
||||
model_mapping = {
|
||||
'build': Build,
|
||||
'target': Target,
|
||||
'target_package': Target_Package,
|
||||
'task': Task,
|
||||
'task_dependency': Task_Dependency,
|
||||
'package': Build_Package,
|
||||
'layer': Layer,
|
||||
'layerversion': Layer_Version,
|
||||
'recipe': Recipe,
|
||||
'recipe_dependency': Recipe_Dependency,
|
||||
'build_package': Build_Package,
|
||||
'build_package_dependency': Build_Package_Dependency,
|
||||
'build_file': Build_File,
|
||||
'variable': Variable,
|
||||
'logmessage': LogMessage,
|
||||
}
|
||||
|
||||
if model_name not in model_mapping.keys():
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
model = model_mapping[model_name]
|
||||
|
||||
try:
|
||||
limit = int(request.GET.get('limit', 0))
|
||||
except ValueError:
|
||||
limit = 0
|
||||
|
||||
try:
|
||||
offset = int(request.GET.get('offset', 0))
|
||||
except ValueError:
|
||||
offset = 0
|
||||
|
||||
ordering_string, invalid = _validate_input(request.GET.get('orderby', ''),
|
||||
model)
|
||||
if invalid:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
filter_string, invalid = _validate_input(request.GET.get('filter', ''),
|
||||
model)
|
||||
if invalid:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
search_term = request.GET.get('search', '')
|
||||
|
||||
if filter_string:
|
||||
filter_terms = _get_filtering_terms(filter_string)
|
||||
try:
|
||||
queryset = model.objects.filter(**filter_terms)
|
||||
except ValueError:
|
||||
queryset = []
|
||||
else:
|
||||
queryset = model.objects.all()
|
||||
|
||||
if search_term:
|
||||
queryset = _get_search_results(search_term, queryset, model)
|
||||
|
||||
if ordering_string and queryset:
|
||||
column, order = ordering_string.split(':')
|
||||
if order.lower() == DESCENDING:
|
||||
queryset = queryset.order_by('-' + column)
|
||||
else:
|
||||
queryset = queryset.order_by(column)
|
||||
|
||||
if offset and limit:
|
||||
queryset = queryset[offset:(offset+limit)]
|
||||
elif offset:
|
||||
queryset = queryset[offset:]
|
||||
elif limit:
|
||||
queryset = queryset[:limit]
|
||||
|
||||
if queryset:
|
||||
response_data['count'] = queryset.count()
|
||||
else:
|
||||
response_data['count'] = 0
|
||||
|
||||
response_data['list'] = serializers.serialize('json', queryset)
|
||||
|
||||
return HttpResponse(json.dumps(response_data),
|
||||
content_type='application/json')
|
||||
|
||||
def _get_filtering_terms(filter_string):
|
||||
|
||||
search_terms = filter_string.split(":")
|
||||
keys = search_terms[0].split(',')
|
||||
values = search_terms[1].split(',')
|
||||
|
||||
return dict(zip(keys, values))
|
||||
|
||||
def _validate_input(input, model):
|
||||
|
||||
invalid = 0
|
||||
|
||||
if input:
|
||||
input_list = input.split(":")
|
||||
|
||||
# Check we have only one colon
|
||||
if len(input_list) != 2:
|
||||
invalid = 1
|
||||
return None, invalid
|
||||
|
||||
# Check we have an equal number of terms both sides of the colon
|
||||
if len(input_list[0].split(',')) != len(input_list[1].split(',')):
|
||||
invalid = 1
|
||||
return None, invalid
|
||||
|
||||
# Check we are looking for a valid field
|
||||
valid_fields = model._meta.get_all_field_names()
|
||||
for field in input_list[0].split(','):
|
||||
if field not in valid_fields:
|
||||
invalid = 1
|
||||
return None, invalid
|
||||
|
||||
return input, invalid
|
||||
|
||||
def _get_search_results(search_term, queryset, model):
|
||||
search_objects = []
|
||||
for st in search_term.split(" "):
|
||||
q_map = map(lambda x: Q(**{x+'__icontains': st}),
|
||||
model.search_allowed_fields)
|
||||
|
||||
search_objects.append(reduce(operator.or_, q_map))
|
||||
search_object = reduce(operator.and_, search_objects)
|
||||
queryset = queryset.filter(search_object)
|
||||
|
||||
return queryset
|
||||
10
bitbake/lib/toaster/manage.py
Executable file
10
bitbake/lib/toaster/manage.py
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "toastermain.settings")
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
execute_from_command_line(sys.argv)
|
||||
0
bitbake/lib/toaster/orm/__init__.py
Normal file
0
bitbake/lib/toaster/orm/__init__.py
Normal file
265
bitbake/lib/toaster/orm/models.py
Normal file
265
bitbake/lib/toaster/orm/models.py
Normal file
@@ -0,0 +1,265 @@
|
||||
#
|
||||
# BitBake Toaster Implementation
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
|
||||
class Build(models.Model):
|
||||
SUCCEEDED = 0
|
||||
FAILED = 1
|
||||
IN_PROGRESS = 2
|
||||
|
||||
BUILD_OUTCOME = (
|
||||
(SUCCEEDED, 'Succeeded'),
|
||||
(FAILED, 'Failed'),
|
||||
(IN_PROGRESS, 'In Progress'),
|
||||
)
|
||||
|
||||
search_allowed_fields = ['machine',
|
||||
'cooker_log_path']
|
||||
|
||||
machine = models.CharField(max_length=100)
|
||||
image_fstypes = models.CharField(max_length=100)
|
||||
distro = models.CharField(max_length=100)
|
||||
distro_version = models.CharField(max_length=100)
|
||||
started_on = models.DateTimeField()
|
||||
completed_on = models.DateTimeField()
|
||||
outcome = models.IntegerField(choices=BUILD_OUTCOME, default=IN_PROGRESS)
|
||||
errors_no = models.IntegerField(default=0)
|
||||
warnings_no = models.IntegerField(default=0)
|
||||
cooker_log_path = models.CharField(max_length=500)
|
||||
build_name = models.CharField(max_length=100)
|
||||
bitbake_version = models.CharField(max_length=50)
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Target(models.Model):
|
||||
search_allowed_fields = ['target', 'image_fstypes', 'file_name']
|
||||
build = models.ForeignKey(Build)
|
||||
target = models.CharField(max_length=100)
|
||||
is_image = models.BooleanField(default = False)
|
||||
file_name = models.CharField(max_length=100)
|
||||
file_size = models.IntegerField()
|
||||
|
||||
def __str__(self):
|
||||
return self.target
|
||||
|
||||
|
||||
class Task(models.Model):
|
||||
|
||||
SSTATE_NA = 0
|
||||
SSTATE_MISS = 1
|
||||
SSTATE_FAILED = 2
|
||||
SSTATE_RESTORED = 3
|
||||
|
||||
SSTATE_RESULT = (
|
||||
(SSTATE_NA, 'Not Applicable'), # For rest of tasks, but they still need checking.
|
||||
(SSTATE_MISS, 'Missing'), # it is a miss
|
||||
(SSTATE_FAILED, 'Failed'), # there was a pkg, but the script failed
|
||||
(SSTATE_RESTORED, 'Restored'), # succesfully restored
|
||||
)
|
||||
|
||||
CODING_NA = 0
|
||||
CODING_NOEXEC = 1
|
||||
CODING_PYTHON = 2
|
||||
CODING_SHELL = 3
|
||||
|
||||
TASK_CODING = (
|
||||
(CODING_NA, 'N/A'),
|
||||
(CODING_NOEXEC, 'NoExec'),
|
||||
(CODING_PYTHON, 'Python'),
|
||||
(CODING_SHELL, 'Shell'),
|
||||
)
|
||||
|
||||
OUTCOME_SUCCESS = 0
|
||||
OUTCOME_COVERED = 1
|
||||
OUTCOME_SSTATE = 2
|
||||
OUTCOME_EXISTING = 3
|
||||
OUTCOME_FAILED = 4
|
||||
OUTCOME_NA = 5
|
||||
|
||||
TASK_OUTCOME = (
|
||||
(OUTCOME_SUCCESS, 'Succeeded'),
|
||||
(OUTCOME_COVERED, 'Covered'),
|
||||
(OUTCOME_SSTATE, 'Sstate'),
|
||||
(OUTCOME_EXISTING, 'Existing'),
|
||||
(OUTCOME_FAILED, 'Failed'),
|
||||
(OUTCOME_NA, 'Not Available'),
|
||||
)
|
||||
|
||||
build = models.ForeignKey(Build, related_name='task_build')
|
||||
order = models.IntegerField(null=True)
|
||||
task_executed = models.BooleanField(default=False) # True means Executed, False means Prebuilt
|
||||
outcome = models.IntegerField(choices=TASK_OUTCOME, default=OUTCOME_NA)
|
||||
sstate_checksum = models.CharField(max_length=100, blank=True)
|
||||
path_to_sstate_obj = models.FilePathField(max_length=500, blank=True)
|
||||
recipe = models.ForeignKey('Recipe', related_name='build_recipe')
|
||||
task_name = models.CharField(max_length=100)
|
||||
source_url = models.FilePathField(max_length=255, blank=True)
|
||||
work_directory = models.FilePathField(max_length=255, blank=True)
|
||||
script_type = models.IntegerField(choices=TASK_CODING, default=CODING_NA)
|
||||
line_number = models.IntegerField(default=0)
|
||||
disk_io = models.IntegerField(null=True)
|
||||
cpu_usage = models.DecimalField(max_digits=6, decimal_places=2, null=True)
|
||||
elapsed_time = models.CharField(max_length=50, default=0)
|
||||
sstate_result = models.IntegerField(choices=SSTATE_RESULT, default=SSTATE_NA)
|
||||
message = models.CharField(max_length=240)
|
||||
logfile = models.FilePathField(max_length=255, blank=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ('order', 'recipe' ,)
|
||||
|
||||
|
||||
class Task_Dependency(models.Model):
|
||||
task = models.ForeignKey(Task, related_name='task_dependencies_task')
|
||||
depends_on = models.ForeignKey(Task, related_name='task_dependencies_depends')
|
||||
|
||||
|
||||
class Build_Package(models.Model):
|
||||
build = models.ForeignKey('Build')
|
||||
recipe = models.ForeignKey('Recipe', null=True)
|
||||
name = models.CharField(max_length=100)
|
||||
version = models.CharField(max_length=100, blank=True)
|
||||
revision = models.CharField(max_length=32, blank=True)
|
||||
summary = models.CharField(max_length=200, blank=True)
|
||||
description = models.CharField(max_length=200, blank=True)
|
||||
size = models.IntegerField(default=0)
|
||||
section = models.CharField(max_length=80, blank=True)
|
||||
license = models.CharField(max_length=80, blank=True)
|
||||
|
||||
class Build_Package_Dependency(models.Model):
|
||||
TYPE_RDEPENDS = 0
|
||||
TYPE_RPROVIDES = 1
|
||||
TYPE_RRECOMMENDS = 2
|
||||
TYPE_RSUGGESTS = 3
|
||||
TYPE_RREPLACES = 4
|
||||
TYPE_RCONFLICTS = 5
|
||||
DEPENDS_TYPE = (
|
||||
(TYPE_RDEPENDS, "rdepends"),
|
||||
(TYPE_RPROVIDES, "rprovides"),
|
||||
(TYPE_RRECOMMENDS, "rrecommends"),
|
||||
(TYPE_RSUGGESTS, "rsuggests"),
|
||||
(TYPE_RREPLACES, "rreplaces"),
|
||||
(TYPE_RCONFLICTS, "rconflicts"),
|
||||
)
|
||||
package = models.ForeignKey(Build_Package, related_name='bpackage_dependencies_package')
|
||||
depends_on = models.CharField(max_length=100) # soft dependency
|
||||
dep_type = models.IntegerField(choices=DEPENDS_TYPE)
|
||||
|
||||
|
||||
class Target_Package(models.Model):
|
||||
target = models.ForeignKey('Target')
|
||||
recipe = models.ForeignKey('Recipe', null=True)
|
||||
name = models.CharField(max_length=100)
|
||||
version = models.CharField(max_length=100, blank=True)
|
||||
size = models.IntegerField()
|
||||
|
||||
|
||||
class Target_Package_Dependency(models.Model):
|
||||
TYPE_DEPENDS = 0
|
||||
TYPE_RDEPENDS = 1
|
||||
TYPE_RECOMMENDS = 2
|
||||
|
||||
DEPENDS_TYPE = (
|
||||
(TYPE_DEPENDS, "depends"),
|
||||
(TYPE_RDEPENDS, "rdepends"),
|
||||
(TYPE_RECOMMENDS, "recommends"),
|
||||
)
|
||||
package = models.ForeignKey(Target_Package, related_name='tpackage_dependencies_package')
|
||||
depends_on = models.ForeignKey(Target_Package, related_name='tpackage_dependencies_depends')
|
||||
dep_type = models.IntegerField(choices=DEPENDS_TYPE)
|
||||
|
||||
|
||||
class Build_File(models.Model):
|
||||
bpackage = models.ForeignKey(Build_Package, related_name='filelist_bpackage')
|
||||
path = models.FilePathField(max_length=255, blank=True)
|
||||
size = models.IntegerField()
|
||||
|
||||
class Target_File(models.Model):
|
||||
tpackage = models.ForeignKey(Target_Package, related_name='filelist_tpackage')
|
||||
path = models.FilePathField(max_length=255, blank=True)
|
||||
size = models.IntegerField()
|
||||
|
||||
|
||||
class Recipe(models.Model):
|
||||
name = models.CharField(max_length=100, blank=True)
|
||||
version = models.CharField(max_length=100, blank=True)
|
||||
layer_version = models.ForeignKey('Layer_Version', related_name='recipe_layer_version')
|
||||
summary = models.CharField(max_length=100, blank=True)
|
||||
description = models.CharField(max_length=100, blank=True)
|
||||
section = models.CharField(max_length=100, blank=True)
|
||||
license = models.CharField(max_length=200, blank=True)
|
||||
licensing_info = models.TextField(blank=True)
|
||||
homepage = models.URLField(blank=True)
|
||||
bugtracker = models.URLField(blank=True)
|
||||
file_path = models.FilePathField(max_length=255)
|
||||
|
||||
|
||||
class Recipe_Dependency(models.Model):
|
||||
TYPE_DEPENDS = 0
|
||||
TYPE_RDEPENDS = 1
|
||||
|
||||
DEPENDS_TYPE = (
|
||||
(TYPE_DEPENDS, "depends"),
|
||||
(TYPE_RDEPENDS, "rdepends"),
|
||||
)
|
||||
recipe = models.ForeignKey(Recipe, related_name='r_dependencies_recipe')
|
||||
depends_on = models.ForeignKey(Recipe, related_name='r_dependencies_depends')
|
||||
dep_type = models.IntegerField(choices=DEPENDS_TYPE)
|
||||
|
||||
class Layer(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
local_path = models.FilePathField(max_length=255)
|
||||
layer_index_url = models.URLField()
|
||||
|
||||
|
||||
class Layer_Version(models.Model):
|
||||
layer = models.ForeignKey(Layer, related_name='layer_version_layer')
|
||||
branch = models.CharField(max_length=50)
|
||||
commit = models.CharField(max_length=100)
|
||||
priority = models.IntegerField()
|
||||
|
||||
|
||||
class Variable(models.Model):
|
||||
build = models.ForeignKey(Build, related_name='variable_build')
|
||||
variable_name = models.CharField(max_length=100)
|
||||
variable_value = models.TextField(blank=True)
|
||||
changed = models.BooleanField(default=False)
|
||||
human_readable_name = models.CharField(max_length=200)
|
||||
description = models.TextField(blank=True)
|
||||
|
||||
class VariableHistory(models.Model):
|
||||
variable = models.ForeignKey(Variable)
|
||||
file_name = models.FilePathField(max_length=255)
|
||||
line_number = models.IntegerField(null=True)
|
||||
operation = models.CharField(max_length=16)
|
||||
|
||||
class LogMessage(models.Model):
|
||||
INFO = 0
|
||||
WARNING = 1
|
||||
ERROR = 2
|
||||
|
||||
LOG_LEVEL = ( (INFO, "info"),
|
||||
(WARNING, "warn"),
|
||||
(ERROR, "error") )
|
||||
|
||||
build = models.ForeignKey(Build)
|
||||
level = models.IntegerField(choices=LOG_LEVEL, default=INFO)
|
||||
message=models.CharField(max_length=240)
|
||||
pathname = models.FilePathField(max_length=255, blank=True)
|
||||
lineno = models.IntegerField(null=True)
|
||||
0
bitbake/lib/toaster/toastergui/__init__.py
Normal file
0
bitbake/lib/toaster/toastergui/__init__.py
Normal file
BIN
bitbake/lib/toaster/toastergui/static/images/yocto.jpg
Normal file
BIN
bitbake/lib/toaster/toastergui/static/images/yocto.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
13
bitbake/lib/toaster/toastergui/templates/index.html
Normal file
13
bitbake/lib/toaster/toastergui/templates/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>GUI Page</title>
|
||||
</head>
|
||||
<body>
|
||||
{% load staticfiles %}
|
||||
|
||||
<img src="{% static "/static/images/yocto.jpg" %}" alt="Yocto"/>
|
||||
|
||||
This is your basic index page!
|
||||
</body>
|
||||
|
||||
</html>
|
||||
27
bitbake/lib/toaster/toastergui/urls.py
Normal file
27
bitbake/lib/toaster/toastergui/urls.py
Normal file
@@ -0,0 +1,27 @@
|
||||
#
|
||||
# BitBake Toaster Implementation
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import patterns, include, url
|
||||
|
||||
|
||||
urlpatterns = patterns('toastergui.views',
|
||||
url(r'^$', 'guihome', name='guihome'),
|
||||
)
|
||||
26
bitbake/lib/toaster/toastergui/views.py
Normal file
26
bitbake/lib/toaster/toastergui/views.py
Normal file
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# BitBake Toaster Implementation
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from django.shortcuts import render
|
||||
from orm.models import Build, Task
|
||||
|
||||
|
||||
def guihome(request):
|
||||
template = 'index.html'
|
||||
|
||||
return render(request, template)
|
||||
0
bitbake/lib/toaster/toastermain/__init__.py
Normal file
0
bitbake/lib/toaster/toastermain/__init__.py
Normal file
191
bitbake/lib/toaster/toastermain/settings.py
Normal file
191
bitbake/lib/toaster/toastermain/settings.py
Normal file
@@ -0,0 +1,191 @@
|
||||
#
|
||||
# BitBake Toaster Implementation
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
# Django settings for Toaster project.
|
||||
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
ADMINS = (
|
||||
# ('Your Name', 'your_email@example.com'),
|
||||
)
|
||||
|
||||
MANAGERS = ADMINS
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
|
||||
'NAME': 'toaster.sqlite', # Or path to database file if using sqlite3.
|
||||
'USER': '',
|
||||
'PASSWORD': '',
|
||||
'HOST': '127.0.0.1', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP.
|
||||
'PORT': '3306', # Set to empty string for default.
|
||||
}
|
||||
}
|
||||
|
||||
# Hosts/domain names that are valid for this site; required if DEBUG is False
|
||||
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
# Local time zone for this installation. Choices can be found here:
|
||||
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||
# although not all choices may be available on all operating systems.
|
||||
# In a Windows environment this must be set to your system time zone.
|
||||
|
||||
# Always use local computer's time zone
|
||||
import time
|
||||
TIME_ZONE = time.tzname[0]
|
||||
|
||||
# Language code for this installation. All choices can be found here:
|
||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
# If you set this to False, Django will make some optimizations so as not
|
||||
# to load the internationalization machinery.
|
||||
USE_I18N = True
|
||||
|
||||
# If you set this to False, Django will not format dates, numbers and
|
||||
# calendars according to the current locale.
|
||||
USE_L10N = True
|
||||
|
||||
# If you set this to False, Django will not use timezone-aware datetimes.
|
||||
USE_TZ = True
|
||||
|
||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
||||
# Example: "/var/www/example.com/media/"
|
||||
MEDIA_ROOT = ''
|
||||
|
||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||
# trailing slash.
|
||||
# Examples: "http://example.com/media/", "http://media.example.com/"
|
||||
MEDIA_URL = ''
|
||||
|
||||
# Absolute path to the directory static files should be collected to.
|
||||
# Don't put anything in this directory yourself; store your static files
|
||||
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
|
||||
# Example: "/var/www/example.com/static/"
|
||||
STATIC_ROOT = ''
|
||||
|
||||
# URL prefix for static files.
|
||||
# Example: "http://example.com/static/", "http://static.example.com/"
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# Additional locations of static files
|
||||
STATICFILES_DIRS = (
|
||||
# Put strings here, like "/home/html/static" or "C:/www/django/static".
|
||||
# Always use forward slashes, even on Windows.
|
||||
# Don't forget to use absolute paths, not relative paths.
|
||||
)
|
||||
|
||||
# List of finder classes that know how to find static files in
|
||||
# various locations.
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
|
||||
)
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
SECRET_KEY = 'NOT_SUITABLE_FOR_HOSTED_DEPLOYMENT'
|
||||
|
||||
# List of callables that know how to import templates from various sources.
|
||||
TEMPLATE_LOADERS = (
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
# 'django.template.loaders.eggs.Loader',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
# Uncomment the next line for simple clickjacking protection:
|
||||
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'toastermain.urls'
|
||||
|
||||
# Python dotted path to the WSGI application used by Django's runserver.
|
||||
WSGI_APPLICATION = 'toastermain.wsgi.application'
|
||||
|
||||
TEMPLATE_DIRS = (
|
||||
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
||||
# Always use forward slashes, even on Windows.
|
||||
# Don't forget to use absolute paths, not relative paths.
|
||||
)
|
||||
|
||||
INSTALLED_APPS = (
|
||||
#'django.contrib.auth',
|
||||
#'django.contrib.contenttypes',
|
||||
#'django.contrib.sessions',
|
||||
#'django.contrib.sites',
|
||||
#'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
# Uncomment the next line to enable the admin:
|
||||
# 'django.contrib.admin',
|
||||
# Uncomment the next line to enable admin documentation:
|
||||
# 'django.contrib.admindocs',
|
||||
'orm',
|
||||
'toastermain',
|
||||
'bldviewer',
|
||||
'toastergui',
|
||||
)
|
||||
|
||||
# A sample logging configuration. The only tangible logging
|
||||
# performed by this configuration is to send an email to
|
||||
# the site admins on every HTTP 500 error when DEBUG=False.
|
||||
# See http://docs.djangoproject.com/en/dev/topics/logging for
|
||||
# more details on how to customize your logging configuration.
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'filters': {
|
||||
'require_debug_false': {
|
||||
'()': 'django.utils.log.RequireDebugFalse'
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'mail_admins': {
|
||||
'level': 'ERROR',
|
||||
'filters': ['require_debug_false'],
|
||||
'class': 'django.utils.log.AdminEmailHandler'
|
||||
}
|
||||
},
|
||||
'loggers': {
|
||||
'django.request': {
|
||||
'handlers': ['mail_admins'],
|
||||
'level': 'ERROR',
|
||||
'propagate': True,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
# If we're using sqlite, we need to tweak the performance a bit
|
||||
from django.db.backends.signals import connection_created
|
||||
def activate_synchronous_off(sender, connection, **kwargs):
|
||||
if connection.vendor == 'sqlite':
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('PRAGMA synchronous = 0;')
|
||||
connection_created.connect(activate_synchronous_off)
|
||||
#
|
||||
|
||||
|
||||
41
bitbake/lib/toaster/toastermain/urls.py
Normal file
41
bitbake/lib/toaster/toastermain/urls.py
Normal file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# BitBake Toaster Implementation
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from django.conf.urls import patterns, include, url
|
||||
from django.views.generic.simple import redirect_to
|
||||
from django.views.decorators.cache import never_cache
|
||||
|
||||
|
||||
# Uncomment the next two lines to enable the admin:
|
||||
# from django.contrib import admin
|
||||
# admin.autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^simple/', include('bldviewer.urls')),
|
||||
url(r'^api/1.0/', include('bldviewer.api')),
|
||||
url(r'^gui/', include('toastergui.urls')),
|
||||
url(r'^$', never_cache(redirect_to), {'url': '/simple/'}),
|
||||
# Examples:
|
||||
# url(r'^toaster/', include('toaster.foo.urls')),
|
||||
|
||||
# Uncomment the admin/doc line below to enable admin documentation:
|
||||
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
|
||||
# Uncomment the next line to enable the admin:
|
||||
# url(r'^admin/', include(admin.site.urls)),
|
||||
)
|
||||
32
bitbake/lib/toaster/toastermain/wsgi.py
Normal file
32
bitbake/lib/toaster/toastermain/wsgi.py
Normal file
@@ -0,0 +1,32 @@
|
||||
"""
|
||||
WSGI config for Toaster project.
|
||||
|
||||
This module contains the WSGI application used by Django's development server
|
||||
and any production WSGI deployments. It should expose a module-level variable
|
||||
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
|
||||
this application via the ``WSGI_APPLICATION`` setting.
|
||||
|
||||
Usually you will have the standard Django WSGI application here, but it also
|
||||
might make sense to replace the whole Django WSGI application with a custom one
|
||||
that later delegates to the Django one. For example, you could introduce WSGI
|
||||
middleware here, or combine a Django application with an application of another
|
||||
framework.
|
||||
|
||||
"""
|
||||
import os
|
||||
|
||||
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
|
||||
# if running multiple sites in the same mod_wsgi process. To fix this, use
|
||||
# mod_wsgi daemon mode with each site in its own daemon process, or use
|
||||
# os.environ["DJANGO_SETTINGS_MODULE"] = "Toaster.settings"
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "toastermain.settings")
|
||||
|
||||
# This application object is used by any WSGI server configured to use this
|
||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||
# setting points here.
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
application = get_wsgi_application()
|
||||
|
||||
# Apply WSGI middleware here.
|
||||
# from helloworld.wsgi import HelloWorldApplication
|
||||
# application = HelloWorldApplication(application)
|
||||
110
meta/classes/toaster.bbclass
Normal file
110
meta/classes/toaster.bbclass
Normal file
@@ -0,0 +1,110 @@
|
||||
#
|
||||
# Toaster helper class
|
||||
#
|
||||
# Copyright (C) 2013 Intel Corporation
|
||||
#
|
||||
# Released under the MIT license (see COPYING.MIT)
|
||||
#
|
||||
# This bbclass is designed to extract data used by OE-Core during the build process,
|
||||
# for recording in the Toaster system.
|
||||
# The data access is synchronous, preserving the build data integrity across
|
||||
# different builds.
|
||||
#
|
||||
# The data is transferred through the event system, using the MetadataEvent objects.
|
||||
#
|
||||
# The model is to enable the datadump functions as postfuncs, and have the dump
|
||||
# executed after the real taskfunc has been executed. This prevents task signature changing
|
||||
# is toaster is enabled or not. Build performance is not affected if Toaster is not enabled.
|
||||
#
|
||||
# To enable, use INHERIT in local.conf:
|
||||
#
|
||||
# INHERIT += "toaster"
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
# 1. Dump package file info data
|
||||
|
||||
python toaster_package_dumpdata() {
|
||||
"""
|
||||
Dumps the data created by emit_pkgdata
|
||||
"""
|
||||
# replicate variables from the package.bbclass
|
||||
|
||||
packages = d.getVar('PACKAGES', True)
|
||||
pkgdest = d.getVar('PKGDEST', True)
|
||||
|
||||
pkgdatadir = d.getVar('PKGDESTWORK', True)
|
||||
|
||||
|
||||
# scan and send data for each package
|
||||
import ast
|
||||
import fnmatch
|
||||
|
||||
lpkgdata = {}
|
||||
for pkg in packages.split():
|
||||
|
||||
subdata_file = pkgdatadir + "/runtime/%s" % pkg
|
||||
lpkgdata = {}
|
||||
|
||||
sf = open(subdata_file, "r")
|
||||
line = sf.readline()
|
||||
while line:
|
||||
(n, v) = line.rstrip().split(":", 1)
|
||||
if pkg in n:
|
||||
n = n.replace("_" + pkg, "")
|
||||
lpkgdata[n] = v.strip()
|
||||
line = sf.readline()
|
||||
pkgsplitname = os.path.join(pkgdest, pkg)
|
||||
# replace FILES_INFO data with a dictionary of file name - file size
|
||||
if n == 'FILES_INFO':
|
||||
filesizedata = {}
|
||||
val = v.strip().replace('\\\'', '\'')
|
||||
dictval = ast.literal_eval(val)
|
||||
for parent, dirlist in dictval.items():
|
||||
idx = parent.find(pkgsplitname)
|
||||
if idx > -1:
|
||||
parent = parent[idx+len(pkgsplitname):]
|
||||
else:
|
||||
bb.error("Invalid path while looking for file ", parent)
|
||||
for basename in dirlist:
|
||||
fullpath = os.path.join(parent, basename)
|
||||
try:
|
||||
filesizedata[fullpath] = os.stat(pkgsplitname + fullpath).st_size
|
||||
except OSError:
|
||||
# we may hit a symlink that is not pointing correctly over package-split
|
||||
filesizedata[fullpath] = 0
|
||||
lpkgdata[n] = filesizedata
|
||||
|
||||
# Fire an event containing the pkg data
|
||||
bb.event.fire(bb.event.MetadataEvent("SinglePackageInfo", lpkgdata), d)
|
||||
}
|
||||
|
||||
# 2. Dump output image files information
|
||||
|
||||
python toaster_image_dumpdata() {
|
||||
"""
|
||||
Image filename for output images is not standardized.
|
||||
image_types.bbclass will spell out IMAGE_CMD_xxx variables that actually
|
||||
have hardcoded ways to create image file names in them.
|
||||
So we look for files starting with the set name.
|
||||
"""
|
||||
|
||||
deploy_dir_image = d.getVar('DEPLOY_DIR_IMAGE', True);
|
||||
image_name = d.getVar('IMAGE_NAME', True);
|
||||
|
||||
image_info_data = {}
|
||||
|
||||
for dirpath, dirnames, filenames in os.walk(deploy_dir_image):
|
||||
for fn in filenames:
|
||||
if fn.startswith(image_name):
|
||||
image_info_data[dirpath + fn] = os.stat(os.path.join(dirpath, fn)).st_size
|
||||
|
||||
bb.event.fire(bb.event.MetadataEvent("ImageFileSize",image_info_data), d)
|
||||
}
|
||||
|
||||
|
||||
do_package[postfuncs] += "toaster_package_dumpdata "
|
||||
|
||||
do_rootfs[postfuncs] += "toaster_image_dumpdata "
|
||||
@@ -42,6 +42,7 @@ CFLAGS[doc] = "Flags passed to the C compiler for the target system. This variab
|
||||
COMBINED_FEATURES[doc] = "A set of features common between MACHINE_FEATURES and DISTRO_FEATURES."
|
||||
COMPATIBLE_HOST[doc] = "A regular expression that resolves to one or more hosts (when the recipe is native) or one or more targets (when the recipe is non-native) with which a recipe is compatible."
|
||||
COMPATIBLE_MACHINE[doc] = "A regular expression that resolves to one or more target machines with which a recipe is compatible."
|
||||
CONF_VERSION[doc] = "Increased each time build/conf/ changes incompatibly and used to track the version of local.conf"
|
||||
CONFFILES[doc] = "Identifies editable or configurable files that are part of a package."
|
||||
CONFIG_SITE[doc] = "A list of files that contains autoconf test results relevant to the current build. This variable is used by the Autotools utilities when running configure."
|
||||
CORE_IMAGE_EXTRA_INSTALL[doc] = "Specifies the list of packages to be added to the image. You should only set this variable in the conf/local.conf file in the Build Directory."
|
||||
@@ -117,6 +118,7 @@ IMAGE_LINGUAS[doc] = "Specifies the list of locales to install into the image du
|
||||
IMAGE_OVERHEAD_FACTOR[doc] = "Defines a multiplier that the build system applies to the initial image size for cases when the multiplier times the returned disk usage value for the image is greater than the sum of IMAGE_ROOTFS_SIZE and IMAGE_ROOTFS_EXTRA_SPACE."
|
||||
IMAGE_ROOTFS_EXTRA_SPACE[doc] = "Defines additional free disk space created in the image in Kbytes. By default, this variable is set to '0'."
|
||||
IMAGE_ROOTFS_SIZE[doc] = "Defines the size in Kbytes for the generated image."
|
||||
IMAGETEST[doc] = "Enable test booting of virtual machine images under the qemu emulator after any root filesystems are created and run tests against those images."
|
||||
INC_PR[doc] = "Helps define the recipe revision for recipes that share a common include file."
|
||||
INHIBIT_PACKAGE_STRIP[doc] = "If set to "1", causes the build to not strip binaries in resulting packages."
|
||||
INHERIT[doc] = "Causes the named class to be inherited at this point during parsing. The variable is only valid in configuration files."
|
||||
@@ -170,18 +172,10 @@ MLPREFIX[doc] = "Specifies a prefix has been added to PN to create a special ver
|
||||
MODULE_TARBALL_DEPLOY[doc] = "Controls creation of the modules-*.tgz file. Set this variable to "0" to disable creation of this file, which contains all of the kernel modules resulting from a kernel build."
|
||||
MULTIMACH_TARGET_SYS[doc] = "Separates files for different machines such that you can build for multiple target machines using the same output directories."
|
||||
|
||||
BAD_RECOMMENDATIONS[doc] = "List of packages to not install if recommended. These will still be installed if required."
|
||||
NO_RECOMMENDATIONS[doc] = "When set to 1, no recommended packages will be installed. Note: some recommended packages may be required for certain system functionality, such as kernel-modules. It is up to the user to add packages to IMAGE_INSTALL as needed."
|
||||
|
||||
IMAGE_INSTALL[doc] = "Used by an image recipe to list the packages to be installed. See PACKAGE_INSTALL."
|
||||
|
||||
PACKAGE_EXCLUDE[doc] = "Packages to exclude from the installation, if required an error will be generated."
|
||||
PACKAGE_INSTALL[doc] = "Generally not user defined. List of the packages to be installed into the image, uses IMAGE_INSTALL as part of the list."
|
||||
PACKAGE_INSTALL_ATTEMPTONLY[doc] = "Generally not user defined. List of packages that will be attempted to be installed, but no error will generate if any of them fail to install."
|
||||
|
||||
#N
|
||||
|
||||
NATIVELSBSTRING[doc] = "A string identifying the host distribution."
|
||||
NO_RECOMMENDATIONS[doc] = "When set to 1, no recommended packages will be installed. Note: some recommended packages may be required for certain system functionality, such as kernel-modules. It is up to the user to add packages to IMAGE_INSTALL as needed."
|
||||
|
||||
#O
|
||||
|
||||
@@ -194,12 +188,16 @@ PACKAGE_ARCH[doc] = "The architecture of the resulting package or packages."
|
||||
PACKAGE_ARCHS[doc] = "A list of architectures compatible with the given target in order of priority."
|
||||
PACKAGE_BEFORE_PN[doc] = "Enables easily adding packages to PACKAGES before ${PN} so that the packages can pick up files that would normally be included in the default package."
|
||||
PACKAGE_CLASSES[doc] = "This variable specifies the package manager to use when packaging data. It is set in the conf/local.conf file in the Build Directory."
|
||||
PACKAGE_EXCLUDE[doc] = "Packages to exclude from the installation, if required an error will be generated."
|
||||
PACKAGE_EXTRA_ARCHS[doc] = "Specifies the list of architectures compatible with the device CPU. This variable is useful when you build for several different devices that use miscellaneous processors."
|
||||
PACKAGE_INSTALL[doc] = "Generally not user defined. List of the packages to be installed into the image, uses IMAGE_INSTALL as part of the list."
|
||||
PACKAGE_INSTALL_ATTEMPTONLY[doc] = "Generally not user defined. List of packages that will be attempted to be installed, but no error will generate if any of them fail to install."
|
||||
PACKAGECONFIG[doc] = "This variable provides a means of enabling or disabling features of a recipe on a per-recipe basis."
|
||||
PACKAGES[doc] = "The list of packages to be created from the recipe."
|
||||
PACKAGES_DYNAMIC[doc] = "A promise that your recipe satisfies runtime dependencies for optional modules that are found in other recipes."
|
||||
PALMTOP_USE_MULTITHREADED_QT[doc] = "Set to yes, if you want to build qt apps with CONFIG+=thread"
|
||||
PARALLEL_MAKE[doc] = "Specifies extra options that are passed to the make command during the compile tasks. This variable is usually in the form -j 4, where the number represents the maximum number of parallel threads make can run."
|
||||
PATCHRESOLVE[doc] = "Enable / disable interactive patch resolution."
|
||||
PE[doc] = "The epoch of the recipe. The default value is '0'. The field is used to make upgrades possible when the versioning scheme changes in some backwards incompatible way."
|
||||
PF[doc] = "Specifies the recipe or package name and includes all version and revision numbers. This variable is comprised of ${PN}-${EXTENDPE}${PV}-${PR}"
|
||||
PN[doc] = "PN refers to a recipe name in the context of a file used by the OpenEmbedded build system as input to create a package. It refers to a package name in the context of a file created or produced by the OpenEmbedded build system."
|
||||
@@ -227,6 +225,7 @@ RSUGGESTS[doc] = "A list of additional packages that you can suggest for install
|
||||
S[doc] = "The location in the Build Directory where unpacked package source code resides."
|
||||
SANITY_TESTED_DISTROS[doc] = "A list of the host distribution identifiers that the build system has been tested against."
|
||||
SDKIMAGE_FEATURES[doc] = "Equivalent to IMAGE_FEATURES. However, this variable applies to the SDK generated from an image using the command '$ bitbake -c populate_sdk imagename'."
|
||||
SDKMACHINE[doc] = "Specifies the architecture (i686 or x86_64) to build SDK/ADT tiems for."
|
||||
SECTION[doc] = "The section in which packages should be categorized. Package management utilities can make use of this variable."
|
||||
SELECTED_OPTIMIZATION[doc] = "The variable takes the value of FULL_OPTIMIZATION unless DEBUG_BUILD = "1". In this case the value of DEBUG_OPTIMIZATION is used."
|
||||
SERIAL_CONSOLE[doc] = "The speed and device for the serial port used to attach the serial console. This variable is given to the kernel as the 'console' parameter and after booting occurs getty is started on that port so remote login is possible."
|
||||
@@ -263,6 +262,8 @@ TARGET_PREFIX[doc] = "The prefix for the cross compile toolchain. E.g arm-linux-
|
||||
TARGET_SYS[doc] = "The target system is composed out of TARGET_ARCH,TARGET_VENDOR and TARGET_OS."
|
||||
TCLIBC[doc] = "Specifies which variant of the GNU standard C library (libc) to use during the build process. You can select eglibc or uclibc."
|
||||
TCMODE[doc] = "The toolchain selector. It selects the external toolchain built using the OpenEmbedded build system or a few supported combinations of the upstream GCC or CodeSourcery Labs toolchain."
|
||||
TEST_SCEN[doc] = "Controls which tests are run against virtual images if testing is enabled with IMAGETEST."
|
||||
TEST_SERIALIZE[doc] = "Controls the time taken by the sanity tests."
|
||||
TIME[doc] = "The time the build was started HMS"
|
||||
TMPDIR[doc] = "This variable is the temporary directory the OpenEmbedded build system uses when it does its work building images. By default, the TMPDIR variable is named tmp within the Build Directory."
|
||||
TOPDIR[doc] = "This variable is the Build Directory. BitBake automatically sets this variable. The OpenEmbedded build system uses the Build Directory when building images."
|
||||
@@ -272,6 +273,10 @@ TUNEABI_WHITELIST[doc] = "A whitelist of permissible TUNEABI values; if unset, a
|
||||
TUNECONFLICTS[doc] = "List of conflicting features for a given feature."
|
||||
TUNEVALID[doc] = "Descriptions of valid tuning features, stored as flags."
|
||||
|
||||
#T
|
||||
|
||||
USER_CLASSES[doc] = "List of additional classes to use when building images which enable extra features."
|
||||
|
||||
#W
|
||||
|
||||
WORKDIR[doc] = "The pathname of the working directory in which the OpenEmbedded build system builds a recipe. This directory is located within the TMPDIR directory structure and changes as different packages are built."
|
||||
|
||||
Reference in New Issue
Block a user