summaryrefslogtreecommitdiffstats
path: root/bitbake
diff options
context:
space:
mode:
authorElliot Smith <elliot.smith@intel.com>2016-07-11 14:47:06 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2016-08-11 00:09:26 +0100
commitdd99cf957da5836dc9b48d200f15a66f0bbce245 (patch)
treee7b8b8fa6b88520aabd6f17bc41cc8410af66761 /bitbake
parent952ffb3e1f4a00793e0c9c49bc0c8fb8729424c4 (diff)
downloadpoky-dd99cf957da5836dc9b48d200f15a66f0bbce245.tar.gz
bitbake: toaster: show progress of recipe parsing in recent builds area
Modify buildinfohelper and toasterui so that they record the recipe parse progress (from ParseProgress events in bitbake) on the Build object. Note that because the Build object is now created at the point when ParseStarted occurs, it is necessary to set the build name to the empty string initially (hence the migration). The build name can be set when the build properly starts, i.e. at the BuildStarted event. Then use this additional data to determine whether a Build is in a "Parsing" state, and report this in the JSON API. This enables the most recent builds area to show the recipe parse progress. Add additional logic to update the progress bar if the progress for a build object changes. [YOCTO #9631] (Bitbake rev: f33d51d46d70e73e04e325807c1bc4eb68462f7b) Signed-off-by: Elliot Smith <elliot.smith@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake')
-rw-r--r--bitbake/lib/bb/ui/buildinfohelper.py118
-rw-r--r--bitbake/lib/bb/ui/toasterui.py31
-rw-r--r--bitbake/lib/toaster/orm/migrations/0013_recipe_parse_progress_fields.py24
-rw-r--r--bitbake/lib/toaster/orm/migrations/0014_allow_empty_buildname.py19
-rw-r--r--bitbake/lib/toaster/orm/models.py17
-rw-r--r--bitbake/lib/toaster/toastergui/api.py10
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/mrbsection.js50
-rw-r--r--bitbake/lib/toaster/toastergui/templates/mrb_section.html112
8 files changed, 259 insertions, 122 deletions
diff --git a/bitbake/lib/bb/ui/buildinfohelper.py b/bitbake/lib/bb/ui/buildinfohelper.py
index 8cd9371b06..b093b37a5d 100644
--- a/bitbake/lib/bb/ui/buildinfohelper.py
+++ b/bitbake/lib/bb/ui/buildinfohelper.py
@@ -150,55 +150,48 @@ class ORMWrapper(object):
150 # pylint: disable=bad-continuation 150 # pylint: disable=bad-continuation
151 # we do not follow the python conventions for continuation indentation due to long lines here 151 # we do not follow the python conventions for continuation indentation due to long lines here
152 152
153 def create_build_object(self, build_info, brbe, project_id): 153 def create_build_object(self, build_info, brbe):
154 assert 'machine' in build_info 154 assert 'machine' in build_info
155 assert 'distro' in build_info 155 assert 'distro' in build_info
156 assert 'distro_version' in build_info 156 assert 'distro_version' in build_info
157 assert 'started_on' in build_info 157 assert 'started_on' in build_info
158 assert 'cooker_log_path' in build_info 158 assert 'cooker_log_path' in build_info
159 assert 'build_name' in build_info
160 assert 'bitbake_version' in build_info 159 assert 'bitbake_version' in build_info
161 160
162 prj = None 161 prj = None
163 buildrequest = None 162 buildrequest = None
164 if brbe is not None: # this build was triggered by a request from a user 163 if brbe is not None:
164 # Toaster-triggered build
165 logger.debug(1, "buildinfohelper: brbe is %s" % brbe) 165 logger.debug(1, "buildinfohelper: brbe is %s" % brbe)
166 br, _ = brbe.split(":") 166 br, _ = brbe.split(":")
167 buildrequest = BuildRequest.objects.get(pk = br) 167 buildrequest = BuildRequest.objects.get(pk=br)
168 prj = buildrequest.project 168 prj = buildrequest.project
169 169 else:
170 elif project_id is not None: # this build was triggered by an external system for a specific project 170 # CLI build
171 logger.debug(1, "buildinfohelper: project is %s" % prj)
172 prj = Project.objects.get(pk = project_id)
173
174 else: # this build was triggered by a legacy system, or command line interactive mode
175 prj = Project.objects.get_or_create_default_project() 171 prj = Project.objects.get_or_create_default_project()
176 logger.debug(1, "buildinfohelper: project is not specified, defaulting to %s" % prj) 172 logger.debug(1, "buildinfohelper: project is not specified, defaulting to %s" % prj)
177 173
178
179 if buildrequest is not None: 174 if buildrequest is not None:
180 build = buildrequest.build 175 build = buildrequest.build
181 logger.info("Updating existing build, with %s", build_info) 176 logger.info("Updating existing build, with %s", build_info)
182 build.project = prj 177 build.project = prj
183 build.machine=build_info['machine'] 178 build.machine = build_info['machine']
184 build.distro=build_info['distro'] 179 build.distro = build_info['distro']
185 build.distro_version=build_info['distro_version'] 180 build.distro_version = build_info['distro_version']
186 build.cooker_log_path=build_info['cooker_log_path'] 181 build.cooker_log_path = build_info['cooker_log_path']
187 build.build_name=build_info['build_name'] 182 build.bitbake_version = build_info['bitbake_version']
188 build.bitbake_version=build_info['bitbake_version']
189 build.save() 183 build.save()
190 184
191 else: 185 else:
192 build = Build.objects.create( 186 build = Build.objects.create(
193 project = prj, 187 project=prj,
194 machine=build_info['machine'], 188 machine=build_info['machine'],
195 distro=build_info['distro'], 189 distro=build_info['distro'],
196 distro_version=build_info['distro_version'], 190 distro_version=build_info['distro_version'],
197 started_on=build_info['started_on'], 191 started_on=build_info['started_on'],
198 completed_on=build_info['started_on'], 192 completed_on=build_info['started_on'],
199 cooker_log_path=build_info['cooker_log_path'], 193 cooker_log_path=build_info['cooker_log_path'],
200 build_name=build_info['build_name'], 194 bitbake_version=build_info['bitbake_version'])
201 bitbake_version=build_info['bitbake_version'])
202 195
203 logger.debug(1, "buildinfohelper: build is created %s" % build) 196 logger.debug(1, "buildinfohelper: build is created %s" % build)
204 197
@@ -208,6 +201,10 @@ class ORMWrapper(object):
208 201
209 return build 202 return build
210 203
204 def update_build_name(self, build, build_name):
205 build.build_name = build_name
206 build.save()
207
211 @staticmethod 208 @staticmethod
212 def get_or_create_targets(target_info): 209 def get_or_create_targets(target_info):
213 """ 210 """
@@ -924,9 +921,7 @@ class BuildInfoHelper(object):
924 build_info['started_on'] = timezone.now() 921 build_info['started_on'] = timezone.now()
925 build_info['completed_on'] = timezone.now() 922 build_info['completed_on'] = timezone.now()
926 build_info['cooker_log_path'] = build_log_path 923 build_info['cooker_log_path'] = build_log_path
927 build_info['build_name'] = self.server.runCommand(["getVariable", "BUILDNAME"])[0]
928 build_info['bitbake_version'] = self.server.runCommand(["getVariable", "BB_VERSION"])[0] 924 build_info['bitbake_version'] = self.server.runCommand(["getVariable", "BB_VERSION"])[0]
929 build_info['project'] = self.project = self.server.runCommand(["getVariable", "TOASTER_PROJECT"])[0]
930 return build_info 925 return build_info
931 926
932 def _get_task_information(self, event, recipe): 927 def _get_task_information(self, event, recipe):
@@ -1032,17 +1027,28 @@ class BuildInfoHelper(object):
1032 except NotExisting as nee: 1027 except NotExisting as nee:
1033 logger.warning("buildinfohelper: cannot identify layer exception:%s ", nee) 1028 logger.warning("buildinfohelper: cannot identify layer exception:%s ", nee)
1034 1029
1035 1030 def store_started_build(self, build_log_path):
1036 def store_started_build(self, event, build_log_path):
1037 assert '_pkgs' in vars(event)
1038 build_information = self._get_build_information(build_log_path) 1031 build_information = self._get_build_information(build_log_path)
1032 self.internal_state['build'] = \
1033 self.orm_wrapper.create_build_object(build_information, self.brbe)
1039 1034
1040 # Update brbe and project as they can be changed for every build 1035 def save_build_name_and_targets(self, event):
1041 self.project = build_information['project'] 1036 # NB the BUILDNAME variable isn't set until BuildInit (or
1037 # BuildStarted for older bitbakes)
1038 build_name = self.server.runCommand(["getVariable", "BUILDNAME"])[0]
1039 self.orm_wrapper.update_build_name(self.internal_state['build'],
1040 build_name)
1042 1041
1043 build_obj = self.orm_wrapper.create_build_object(build_information, self.brbe, self.project) 1042 # create target information
1043 assert '_pkgs' in vars(event)
1044 target_information = {}
1045 target_information['targets'] = event._pkgs
1046 target_information['build'] = self.internal_state['build']
1044 1047
1045 self.internal_state['build'] = build_obj 1048 self.internal_state['targets'] = self.orm_wrapper.get_or_create_targets(target_information)
1049
1050 def save_build_layers_and_variables(self):
1051 build_obj = self.internal_state['build']
1046 1052
1047 # save layer version information for this build 1053 # save layer version information for this build
1048 if not 'lvs' in self.internal_state: 1054 if not 'lvs' in self.internal_state:
@@ -1053,13 +1059,6 @@ class BuildInfoHelper(object):
1053 1059
1054 del self.internal_state['lvs'] 1060 del self.internal_state['lvs']
1055 1061
1056 # create target information
1057 target_information = {}
1058 target_information['targets'] = event._pkgs
1059 target_information['build'] = build_obj
1060
1061 self.internal_state['targets'] = self.orm_wrapper.get_or_create_targets(target_information)
1062
1063 # Save build configuration 1062 # Save build configuration
1064 data = self.server.runCommand(["getAllKeysWithFlags", ["doc", "func"]])[0] 1063 data = self.server.runCommand(["getAllKeysWithFlags", ["doc", "func"]])[0]
1065 1064
@@ -1090,6 +1089,41 @@ class BuildInfoHelper(object):
1090 1089
1091 return self.brbe 1090 return self.brbe
1092 1091
1092 def set_recipes_to_parse(self, num_recipes):
1093 """
1094 Set the number of recipes which need to be parsed for this build.
1095 This is set the first time ParseStarted is received by toasterui.
1096 """
1097 if self.internal_state['build']:
1098 self.internal_state['build'].recipes_to_parse = num_recipes
1099 self.internal_state['build'].save()
1100
1101 def set_recipes_parsed(self, num_recipes):
1102 """
1103 Set the number of recipes parsed so far for this build; this is updated
1104 each time a ParseProgress or ParseCompleted event is received by
1105 toasterui.
1106 """
1107 if self.internal_state['build']:
1108 if num_recipes <= self.internal_state['build'].recipes_to_parse:
1109 self.internal_state['build'].recipes_parsed = num_recipes
1110 self.internal_state['build'].save()
1111
1112 def update_target_image_file(self, event):
1113 evdata = BuildInfoHelper._get_data_from_event(event)
1114
1115 for t in self.internal_state['targets']:
1116 if t.is_image == True:
1117 output_files = list(evdata.keys())
1118 for output in output_files:
1119 if t.target in output and 'rootfs' in output and not output.endswith(".manifest"):
1120 self.orm_wrapper.save_target_image_file_information(t, output, evdata[output])
1121
1122 def update_artifact_image_file(self, event):
1123 evdata = BuildInfoHelper._get_data_from_event(event)
1124 for artifact_path in evdata.keys():
1125 self.orm_wrapper.save_artifact_information(self.internal_state['build'], artifact_path, evdata[artifact_path])
1126
1093 def update_build_information(self, event, errors, warnings, taskfailures): 1127 def update_build_information(self, event, errors, warnings, taskfailures):
1094 if 'build' in self.internal_state: 1128 if 'build' in self.internal_state:
1095 self.orm_wrapper.update_build_object(self.internal_state['build'], errors, warnings, taskfailures) 1129 self.orm_wrapper.update_build_object(self.internal_state['build'], errors, warnings, taskfailures)
diff --git a/bitbake/lib/bb/ui/toasterui.py b/bitbake/lib/bb/ui/toasterui.py
index f399a7d316..7d670bef6f 100644
--- a/bitbake/lib/bb/ui/toasterui.py
+++ b/bitbake/lib/bb/ui/toasterui.py
@@ -102,6 +102,7 @@ _evt_list = [
102 "bb.command.CommandExit", 102 "bb.command.CommandExit",
103 "bb.command.CommandFailed", 103 "bb.command.CommandFailed",
104 "bb.cooker.CookerExit", 104 "bb.cooker.CookerExit",
105 "bb.event.BuildInit",
105 "bb.event.BuildCompleted", 106 "bb.event.BuildCompleted",
106 "bb.event.BuildStarted", 107 "bb.event.BuildStarted",
107 "bb.event.CacheLoadCompleted", 108 "bb.event.CacheLoadCompleted",
@@ -115,6 +116,7 @@ _evt_list = [
115 "bb.event.NoProvider", 116 "bb.event.NoProvider",
116 "bb.event.ParseCompleted", 117 "bb.event.ParseCompleted",
117 "bb.event.ParseProgress", 118 "bb.event.ParseProgress",
119 "bb.event.ParseStarted",
118 "bb.event.RecipeParsed", 120 "bb.event.RecipeParsed",
119 "bb.event.SanityCheck", 121 "bb.event.SanityCheck",
120 "bb.event.SanityCheckPassed", 122 "bb.event.SanityCheckPassed",
@@ -231,19 +233,30 @@ def main(server, eventHandler, params):
231 # pylint: disable=protected-access 233 # pylint: disable=protected-access
232 # the code will look into the protected variables of the event; no easy way around this 234 # the code will look into the protected variables of the event; no easy way around this
233 235
234 # we treat ParseStarted as the first event of toaster-triggered
235 # builds; that way we get the Build Configuration included in the log
236 # and any errors that occur before BuildStarted is fired
237 if isinstance(event, bb.event.ParseStarted): 236 if isinstance(event, bb.event.ParseStarted):
238 if not (build_log and build_log_file_path): 237 if not (build_log and build_log_file_path):
239 build_log, build_log_file_path = _open_build_log(log_dir) 238 build_log, build_log_file_path = _open_build_log(log_dir)
239
240 buildinfohelper.store_started_build(build_log_file_path)
241 buildinfohelper.set_recipes_to_parse(event.total)
240 continue 242 continue
241 243
242 if isinstance(event, bb.event.BuildStarted): 244 # create a build object in buildinfohelper from either BuildInit
243 if not (build_log and build_log_file_path): 245 # (if available) or BuildStarted (for jethro and previous versions)
244 build_log, build_log_file_path = _open_build_log(log_dir) 246 if isinstance(event, (bb.event.BuildStarted, bb.event.BuildInit)):
247 buildinfohelper.save_build_name_and_targets(event)
245 248
246 buildinfohelper.store_started_build(event, build_log_file_path) 249 # get additional data from BuildStarted
250 if isinstance(event, bb.event.BuildStarted):
251 buildinfohelper.save_build_layers_and_variables()
252 continue
253
254 if isinstance(event, bb.event.ParseProgress):
255 buildinfohelper.set_recipes_parsed(event.current)
256 continue
257
258 if isinstance(event, bb.event.ParseCompleted):
259 buildinfohelper.set_recipes_parsed(event.total)
247 continue 260 continue
248 261
249 if isinstance(event, (bb.build.TaskStarted, bb.build.TaskSucceeded, bb.build.TaskFailedSilent)): 262 if isinstance(event, (bb.build.TaskStarted, bb.build.TaskSucceeded, bb.build.TaskFailedSilent)):
@@ -289,10 +302,6 @@ def main(server, eventHandler, params):
289 # timing and error informations from the parsing phase in Toaster 302 # timing and error informations from the parsing phase in Toaster
290 if isinstance(event, (bb.event.SanityCheckPassed, bb.event.SanityCheck)): 303 if isinstance(event, (bb.event.SanityCheckPassed, bb.event.SanityCheck)):
291 continue 304 continue
292 if isinstance(event, bb.event.ParseProgress):
293 continue
294 if isinstance(event, bb.event.ParseCompleted):
295 continue
296 if isinstance(event, bb.event.CacheLoadStarted): 305 if isinstance(event, bb.event.CacheLoadStarted):
297 continue 306 continue
298 if isinstance(event, bb.event.CacheLoadProgress): 307 if isinstance(event, bb.event.CacheLoadProgress):
diff --git a/bitbake/lib/toaster/orm/migrations/0013_recipe_parse_progress_fields.py b/bitbake/lib/toaster/orm/migrations/0013_recipe_parse_progress_fields.py
new file mode 100644
index 0000000000..cc5c96d2dd
--- /dev/null
+++ b/bitbake/lib/toaster/orm/migrations/0013_recipe_parse_progress_fields.py
@@ -0,0 +1,24 @@
1# -*- coding: utf-8 -*-
2from __future__ import unicode_literals
3
4from django.db import migrations, models
5
6
7class Migration(migrations.Migration):
8
9 dependencies = [
10 ('orm', '0012_use_release_instead_of_up_branch'),
11 ]
12
13 operations = [
14 migrations.AddField(
15 model_name='build',
16 name='recipes_parsed',
17 field=models.IntegerField(default=0),
18 ),
19 migrations.AddField(
20 model_name='build',
21 name='recipes_to_parse',
22 field=models.IntegerField(default=1),
23 ),
24 ]
diff --git a/bitbake/lib/toaster/orm/migrations/0014_allow_empty_buildname.py b/bitbake/lib/toaster/orm/migrations/0014_allow_empty_buildname.py
new file mode 100644
index 0000000000..4749a14b26
--- /dev/null
+++ b/bitbake/lib/toaster/orm/migrations/0014_allow_empty_buildname.py
@@ -0,0 +1,19 @@
1# -*- coding: utf-8 -*-
2from __future__ import unicode_literals
3
4from django.db import migrations, models
5
6
7class Migration(migrations.Migration):
8
9 dependencies = [
10 ('orm', '0013_recipe_parse_progress_fields'),
11 ]
12
13 operations = [
14 migrations.AlterField(
15 model_name='build',
16 name='build_name',
17 field=models.CharField(default='', max_length=100),
18 ),
19 ]
diff --git a/bitbake/lib/toaster/orm/models.py b/bitbake/lib/toaster/orm/models.py
index 2df6d4910a..4641736add 100644
--- a/bitbake/lib/toaster/orm/models.py
+++ b/bitbake/lib/toaster/orm/models.py
@@ -397,9 +397,15 @@ class Build(models.Model):
397 completed_on = models.DateTimeField() 397 completed_on = models.DateTimeField()
398 outcome = models.IntegerField(choices=BUILD_OUTCOME, default=IN_PROGRESS) 398 outcome = models.IntegerField(choices=BUILD_OUTCOME, default=IN_PROGRESS)
399 cooker_log_path = models.CharField(max_length=500) 399 cooker_log_path = models.CharField(max_length=500)
400 build_name = models.CharField(max_length=100) 400 build_name = models.CharField(max_length=100, default='')
401 bitbake_version = models.CharField(max_length=50) 401 bitbake_version = models.CharField(max_length=50)
402 402
403 # number of recipes to parse for this build
404 recipes_to_parse = models.IntegerField(default=1)
405
406 # number of recipes parsed so far for this build
407 recipes_parsed = models.IntegerField(default=0)
408
403 @staticmethod 409 @staticmethod
404 def get_recent(project=None): 410 def get_recent(project=None):
405 """ 411 """
@@ -615,6 +621,13 @@ class Build(models.Model):
615 else: 621 else:
616 return False 622 return False
617 623
624 def is_parsing(self):
625 """
626 True if the build is still parsing recipes
627 """
628 return self.outcome == Build.IN_PROGRESS and \
629 self.recipes_parsed < self.recipes_to_parse
630
618 def get_state(self): 631 def get_state(self):
619 """ 632 """
620 Get the state of the build; one of 'Succeeded', 'Failed', 'In Progress', 633 Get the state of the build; one of 'Succeeded', 'Failed', 'In Progress',
@@ -628,6 +641,8 @@ class Build(models.Model):
628 return 'Cancelling'; 641 return 'Cancelling';
629 elif self.is_queued(): 642 elif self.is_queued():
630 return 'Queued' 643 return 'Queued'
644 elif self.is_parsing():
645 return 'Parsing'
631 else: 646 else:
632 return self.get_outcome_text() 647 return self.get_outcome_text()
633 648
diff --git a/bitbake/lib/toaster/toastergui/api.py b/bitbake/lib/toaster/toastergui/api.py
index aa3cbd83b2..b57f670ba3 100644
--- a/bitbake/lib/toaster/toastergui/api.py
+++ b/bitbake/lib/toaster/toastergui/api.py
@@ -87,7 +87,7 @@ class XhrBuildRequest(View):
87 br.save() 87 br.save()
88 88
89 except BuildRequest.DoesNotExist: 89 except BuildRequest.DoesNotExist:
90 return error_response('No such build id %s' % i) 90 return error_response('No such build request id %s' % i)
91 91
92 return error_response('ok') 92 return error_response('ok')
93 93
@@ -256,6 +256,14 @@ class MostRecentBuildsView(View):
256 build['id'] = build_obj.pk 256 build['id'] = build_obj.pk
257 build['dashboard_url'] = dashboard_url 257 build['dashboard_url'] = dashboard_url
258 258
259 buildrequest_id = None
260 if hasattr(build_obj, 'buildrequest'):
261 buildrequest_id = build_obj.buildrequest.pk
262 build['buildrequest_id'] = buildrequest_id
263
264 build['recipes_parsed_percentage'] = \
265 int((build_obj.recipes_parsed / build_obj.recipes_to_parse) * 100)
266
259 tasks_complete_percentage = 0 267 tasks_complete_percentage = 0
260 if build_obj.outcome in (Build.SUCCEEDED, Build.FAILED): 268 if build_obj.outcome in (Build.SUCCEEDED, Build.FAILED):
261 tasks_complete_percentage = 100 269 tasks_complete_percentage = 100
diff --git a/bitbake/lib/toaster/toastergui/static/js/mrbsection.js b/bitbake/lib/toaster/toastergui/static/js/mrbsection.js
index d8c3bf7750..e7fbf01731 100644
--- a/bitbake/lib/toaster/toastergui/static/js/mrbsection.js
+++ b/bitbake/lib/toaster/toastergui/static/js/mrbsection.js
@@ -33,8 +33,8 @@ function mrbSectionInit(ctx){
33 return buildData[build.id] || {}; 33 return buildData[build.id] || {};
34 } 34 }
35 35
36 // returns true if a build's state changed to "Succeeded" or "Failed" 36 // returns true if a build's state changed to "Succeeded", "Failed"
37 // from some other value 37 // or "Cancelled" from some other value
38 function buildFinished(build) { 38 function buildFinished(build) {
39 var cached = getCached(build); 39 var cached = getCached(build);
40 return cached.state && 40 return cached.state &&
@@ -49,12 +49,18 @@ function mrbSectionInit(ctx){
49 return (cached.state !== build.state); 49 return (cached.state !== build.state);
50 } 50 }
51 51
52 // returns true if the complete_percentage changed 52 // returns true if the tasks_complete_percentage changed
53 function progressChanged(build) { 53 function tasksProgressChanged(build) {
54 var cached = getCached(build); 54 var cached = getCached(build);
55 return (cached.tasks_complete_percentage !== build.tasks_complete_percentage); 55 return (cached.tasks_complete_percentage !== build.tasks_complete_percentage);
56 } 56 }
57 57
58 // returns true if the number of recipes parsed/to parse changed
59 function recipeProgressChanged(build) {
60 var cached = getCached(build);
61 return (cached.recipes_parsed_percentage !== build.recipes_parsed_percentage);
62 }
63
58 function refreshMostRecentBuilds(){ 64 function refreshMostRecentBuilds(){
59 libtoaster.getMostRecentBuilds( 65 libtoaster.getMostRecentBuilds(
60 libtoaster.ctx.mostRecentBuildsUrl, 66 libtoaster.ctx.mostRecentBuildsUrl,
@@ -68,10 +74,6 @@ function mrbSectionInit(ctx){
68 var colourClass; 74 var colourClass;
69 var elements; 75 var elements;
70 76
71 // classes on the parent which signify the build state and affect
72 // the colour of the container for the build
73 var buildStateClasses = 'alert-info alert-success alert-danger';
74
75 for (var i = 0; i < data.length; i++) { 77 for (var i = 0; i < data.length; i++) {
76 build = data[i]; 78 build = data[i];
77 79
@@ -91,31 +93,25 @@ function mrbSectionInit(ctx){
91 container = $(selector); 93 container = $(selector);
92 94
93 container.html(html); 95 container.html(html);
94
95 // style the outermost container for this build to reflect
96 // the new build state (red, green, blue);
97 // NB class set here should be in buildStateClasses
98 colourClass = 'alert-info';
99 if (build.state == 'Succeeded') {
100 colourClass = 'alert-success';
101 }
102 else if (build.state == 'Failed') {
103 colourClass = 'alert-danger';
104 }
105
106 elements = $('[data-latest-build-result="' + build.id + '"]');
107 elements.removeClass(buildStateClasses);
108 elements.addClass(colourClass);
109 } 96 }
110 else if (progressChanged(build)) { 97 else if (tasksProgressChanged(build)) {
111 // update the progress text 98 // update the task progress text
112 selector = '#build-pc-done-' + build.id; 99 selector = '#build-pc-done-' + build.id;
113 $(selector).html(build.tasks_complete_percentage); 100 $(selector).html(build.tasks_complete_percentage);
114 101
115 // update the progress bar 102 // update the task progress bar
116 selector = '#build-pc-done-bar-' + build.id; 103 selector = '#build-pc-done-bar-' + build.id;
117 $(selector).width(build.tasks_complete_percentage + '%'); 104 $(selector).width(build.tasks_complete_percentage + '%');
118 } 105 }
106 else if (recipeProgressChanged(build)) {
107 // update the recipe progress text
108 selector = '#recipes-parsed-percentage-' + build.id;
109 $(selector).html(build.recipes_parsed_percentage);
110
111 // update the recipe progress bar
112 selector = '#recipes-parsed-percentage-bar-' + build.id;
113 $(selector).width(build.recipes_parsed_percentage + '%');
114 }
119 115
120 buildData[build.id] = build; 116 buildData[build.id] = build;
121 } 117 }
@@ -128,6 +124,6 @@ function mrbSectionInit(ctx){
128 ); 124 );
129 } 125 }
130 126
131 window.setInterval(refreshMostRecentBuilds, 1000); 127 window.setInterval(refreshMostRecentBuilds, 1500);
132 refreshMostRecentBuilds(); 128 refreshMostRecentBuilds();
133} 129}
diff --git a/bitbake/lib/toaster/toastergui/templates/mrb_section.html b/bitbake/lib/toaster/toastergui/templates/mrb_section.html
index 302b4b0da4..880485d45f 100644
--- a/bitbake/lib/toaster/toastergui/templates/mrb_section.html
+++ b/bitbake/lib/toaster/toastergui/templates/mrb_section.html
@@ -26,7 +26,9 @@
26 <div class="row project-name"> 26 <div class="row project-name">
27 <div class="col-md-12"> 27 <div class="col-md-12">
28 <small> 28 <small>
29 <a class="alert-link text-uppercase" href={% project_url build.project %}>{{build.project.name}}</a> 29 <a class="alert-link text-uppercase" href="{% project_url build.project %}">
30 {{build.project.name}}
31 </a>
30 </small> 32 </small>
31 </div> 33 </div>
32 </div> 34 </div>
@@ -52,14 +54,18 @@
52 <%:targets_abbreviated%> 54 <%:targets_abbreviated%>
53 </span> 55 </span>
54 </a> 56 </a>
55 <%else%> 57 <%else targets_abbreviated !== ''%>
56 <span data-toggle="tooltip" data-role="targets-text" title="Recipes: <%:targets%>"> 58 <span data-toggle="tooltip" data-role="targets-text" title="Recipes: <%:targets%>">
57 <%:targets_abbreviated%> 59 <%:targets_abbreviated%>
58 </span> 60 </span>
61 <%else%>
62 ...targets not yet available...
59 <%/if%> 63 <%/if%>
60 </div> 64 </div>
61 65
62 <%if state == 'Queued'%> 66 <%if state == 'Parsing'%>
67 <%include tmpl='#parsing-recipes-build-template'/%>
68 <%else state == 'Queued'%>
63 <%include tmpl='#queued-build-template'/%> 69 <%include tmpl='#queued-build-template'/%>
64 <%else state == 'Succeeded' || state == 'Failed'%> 70 <%else state == 'Succeeded' || state == 'Failed'%>
65 <%include tmpl='#succeeded-or-failed-build-template'/%> 71 <%include tmpl='#succeeded-or-failed-build-template'/%>
@@ -75,21 +81,38 @@
75<!-- queued build --> 81<!-- queued build -->
76<script id="queued-build-template" type="text/x-jsrender"> 82<script id="queued-build-template" type="text/x-jsrender">
77 <div class="col-md-5"> 83 <div class="col-md-5">
84 <span class="glyphicon glyphicon-question-sign get-help get-help-blue" title="This build is waiting for
85the build directory to become available"></span>
86
78 Build queued 87 Build queued
79 </div> 88 </div>
80 89
81 <div class="col-md-4"> 90 <div class="col-md-4">
82 <%if is_default_project_build%> 91 <!-- cancel button -->
83 <!-- no cancel icon --> 92 <%include tmpl='#cancel-template'/%>
84 <span class="glyphicon glyphicon-question-sign get-help get-help-blue pull-right" title="Builds in this project cannot be cancelled from Toaster: they can only be cancelled from the command line"></span> 93 </div>
85 <%else%> 94</script>
86 <!-- cancel button --> 95
87 <span class="cancel-build-btn pull-right alert-link" 96<!-- parsing recipes build -->
88 data-buildrequest-id="<%:id%>" data-request-url="<%:cancel_url%>"> 97<script id="parsing-recipes-build-template" type="text/x-jsrender">
89 <span class="glyphicon glyphicon-remove-circle"></span> 98 <!-- progress bar and parse completion percentage -->
90 Cancel 99 <div data-role="build-status" class="col-md-4 col-md-offset-1 progress-info">
91 </span> 100 <!-- progress bar -->
92 <%/if%> 101 <div class="progress">
102 <div id="recipes-parsed-percentage-bar-<%:id%>"
103 style="width: <%:recipes_parsed_percentage%>%;"
104 class="progress-bar">
105 </div>
106 </div>
107 </div>
108
109 <div class="col-md-4 progress-info">
110 <!-- parse completion percentage -->
111 <span class="glyphicon glyphicon-question-sign get-help get-help-blue" title="BitBake is parsing the layers required for your build"></span>
112
113 Parsing <span id="recipes-parsed-percentage-<%:id%>"><%:recipes_parsed_percentage%></span>% complete
114
115 <%include tmpl='#cancel-template'/%>
93 </div> 116 </div>
94</script> 117</script>
95 118
@@ -110,17 +133,9 @@
110 <!-- task completion percentage --> 133 <!-- task completion percentage -->
111 <span id="build-pc-done-<%:id%>"><%:tasks_complete_percentage%></span>% of 134 <span id="build-pc-done-<%:id%>"><%:tasks_complete_percentage%></span>% of
112 tasks complete 135 tasks complete
113 <%if is_default_project_build%> 136
114 <!-- no cancel icon --> 137 <!-- cancel button -->
115 <span class="glyphicon glyphicon-question-sign get-help get-help-blue pull-right" title="Builds in this project cannot be cancelled from Toaster: they can only be cancelled from the command line"></span> 138 <%include tmpl='#cancel-template'/%>
116 <%else%>
117 <!-- cancel button -->
118 <span class="cancel-build-btn pull-right alert-link"
119 data-buildrequest-id="<%:id%>" data-request-url="<%:cancel_url%>">
120 <span class="glyphicon glyphicon-remove-circle"></span>
121 Cancel
122 </span>
123 <%/if%>
124 </div> 139 </div>
125</script> 140</script>
126 141
@@ -162,19 +177,8 @@
162 <div class="col-md-3"> 177 <div class="col-md-3">
163 Build time: <a class="alert-link" href="<%:buildtime_url%>"><%:buildtime%></a> 178 Build time: <a class="alert-link" href="<%:buildtime_url%>"><%:buildtime%></a>
164 179
165 <%if is_default_project_build%> 180 <!-- rebuild button -->
166 <!-- info icon --> 181 <%include tmpl='#rebuild-template'/%>
167 <span class="pull-right glyphicon glyphicon-question-sign get-help <%if state == 'Success'%>get-help-green<%else state == 'Failed'%>get-help-red<%else%>get-help-blue<%/if%>"
168 title="Builds in this project cannot be started from Toaster: they are started from the command line">
169 </span>
170 <%else%>
171 <!-- rebuild button -->
172 <span class="rebuild-btn alert-link <%if state == 'Success'%>success<%else state == 'Failed'%>danger<%else%>info<%/if%> pull-right"
173 data-request-url="<%:rebuild_url%>" data-target='<%:build_targets_json%>'>
174 <span class="glyphicon glyphicon-repeat"></span>
175 Rebuild
176 </span>
177 <%/if%>
178 </div> 182 </div>
179</script> 183</script>
180 184
@@ -187,12 +191,40 @@
187 191
188 <!-- rebuild button --> 192 <!-- rebuild button -->
189 <div class="col-md-3"> 193 <div class="col-md-3">
190 <span class="info pull-right rebuild-btn alert-link" 194 <%include tmpl='#rebuild-template'/%>
195 </div>
196</script>
197
198<!-- rebuild button or no rebuild icon -->
199<script id="rebuild-template" type="text/x-jsrender">
200 <%if is_default_project_build%>
201 <!-- no rebuild info icon -->
202 <span class="pull-right glyphicon glyphicon-question-sign get-help <%if state == 'Success'%>get-help-green<%else state == 'Failed'%>get-help-red<%else%>get-help-blue<%/if%>"
203 title="Builds in this project cannot be started from Toaster: they are started from the command line">
204 </span>
205 <%else%>
206 <!-- rebuild button -->
207 <span class="rebuild-btn alert-link <%if state == 'Success'%>success<%else state == 'Failed'%>danger<%else%>info<%/if%> pull-right"
191 data-request-url="<%:rebuild_url%>" data-target='<%:build_targets_json%>'> 208 data-request-url="<%:rebuild_url%>" data-target='<%:build_targets_json%>'>
192 <span class="glyphicon glyphicon-repeat"></span> 209 <span class="glyphicon glyphicon-repeat"></span>
193 Rebuild 210 Rebuild
194 </span> 211 </span>
195 </div> 212 <%/if%>
213</script>
214
215<!-- cancel button or no cancel icon -->
216<script id="cancel-template" type="text/x-jsrender">
217 <%if is_default_project_build%>
218 <!-- no cancel icon -->
219 <span class="glyphicon glyphicon-question-sign get-help get-help-blue pull-right" title="Builds in this project cannot be cancelled from Toaster: they can only be cancelled from the command line"></span>
220 <%else%>
221 <!-- cancel button -->
222 <span class="cancel-build-btn pull-right alert-link"
223 data-buildrequest-id="<%:buildrequest_id%>" data-request-url="<%:cancel_url%>">
224 <span class="glyphicon glyphicon-remove-circle"></span>
225 Cancel
226 </span>
227 <%/if%>
196</script> 228</script>
197 229
198<script> 230<script>