diff options
Diffstat (limited to 'bitbake')
-rw-r--r-- | bitbake/lib/bb/ui/buildinfohelper.py | 118 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/toasterui.py | 31 | ||||
-rw-r--r-- | bitbake/lib/toaster/orm/migrations/0013_recipe_parse_progress_fields.py | 24 | ||||
-rw-r--r-- | bitbake/lib/toaster/orm/migrations/0014_allow_empty_buildname.py | 19 | ||||
-rw-r--r-- | bitbake/lib/toaster/orm/models.py | 17 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/api.py | 10 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/static/js/mrbsection.js | 50 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/templates/mrb_section.html | 112 |
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 -*- | ||
2 | from __future__ import unicode_literals | ||
3 | |||
4 | from django.db import migrations, models | ||
5 | |||
6 | |||
7 | class 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 -*- | ||
2 | from __future__ import unicode_literals | ||
3 | |||
4 | from django.db import migrations, models | ||
5 | |||
6 | |||
7 | class 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 | ||
85 | the 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> |