diff options
Diffstat (limited to 'bitbake/lib')
| -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> |
