3 # BitBake Graphical GTK User Interface
5 # Copyright (C) 2011-2012 Intel Corporation
7 # Authored by Joshua Lock <josh@linux.intel.com>
8 # Authored by Dongxiao Xu <dongxiao.xu@intel.com>
9 # Authored by Shane Wang <shane.wang@intel.com>
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License version 2 as
13 # published by the Free Software Foundation.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License along
21 # with this program; if not, write to the Free Software Foundation, Inc.,
22 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 from bb.ui.crumbs.template import TemplateMgr
31 from bb.ui.crumbs.imageconfigurationpage import ImageConfigurationPage
32 from bb.ui.crumbs.recipeselectionpage import RecipeSelectionPage
33 from bb.ui.crumbs.packageselectionpage import PackageSelectionPage
34 from bb.ui.crumbs.builddetailspage import BuildDetailsPage
35 from bb.ui.crumbs.imagedetailspage import ImageDetailsPage
36 from bb.ui.crumbs.hobwidget import hwc, HobButton, HobAltButton, hcc
37 from bb.ui.crumbs.hig import CrumbsMessageDialog, ImageSelectionDialog, \
38 AdvancedSettingDialog, LayerSelectionDialog, \
40 from bb.ui.crumbs.persistenttooltip import PersistentTooltip
41 import bb.ui.crumbs.utils
46 '''Represents the data structure of configuration.'''
49 def parse_proxy_string(cls, proxy):
50 pattern = "^\s*((http|https|ftp|git|cvs)://)?((\S+):(\S+)@)?(\S+):(\d+)/?"
51 match = re.search(pattern, proxy)
53 return match.group(2), match.group(4), match.group(5), match.group(6), match.group(7)
55 return None, None, None, "", ""
58 def make_host_string(cls, prot, user, passwd, host, default_prot=""):
59 if host == None or host == "":
64 if user != None and user != "":
65 if prot == None or prot == "":
67 return prot + "://" + user + ":" + passwd + "@" + host
69 if prot == None or prot == "":
72 return prot + "://" + host
75 def make_port_string(cls, port):
80 def make_proxy_string(cls, prot, user, passwd, host, port, default_prot=""):
81 if host == None or host == "" or port == None or port == "":
84 return Configuration.make_host_string(prot, user, passwd, host, default_prot) + ":" + Configuration.make_port_string(port)
90 self.dldir = self.sstatedir = self.sstatemirror = ""
91 self.pmake = self.bbthread = 0
92 self.curr_package_format = ""
93 self.image_rootfs_size = self.image_extra_size = 0
94 self.image_overhead_factor = 1
95 self.incompat_license = ""
96 self.curr_sdk_machine = ""
97 self.conf_version = self.lconf_version = ""
98 self.extra_setting = {}
99 self.toolchain_build = False
100 self.image_fstypes = ""
103 # image/recipes/packages
104 self.clear_selection()
106 self.user_selected_packages = []
108 self.default_task = "build"
111 self.enable_proxy = None
112 self.same_proxy = False
114 "http" : [None, None, None, "", ""], # protocol : [prot, user, passwd, host, port]
115 "https" : [None, None, None, "", ""],
116 "ftp" : [None, None, None, "", ""],
117 "git" : [None, None, None, "", ""],
118 "cvs" : [None, None, None, "", ""],
121 def clear_selection(self):
122 self.selected_image = None
123 self.selected_recipes = []
124 self.selected_packages = []
126 def split_proxy(self, protocol, proxy):
128 prot, user, passwd, host, port = Configuration.parse_proxy_string(proxy)
134 self.proxies[protocol] = entry
136 def combine_proxy(self, protocol):
137 entry = self.proxies[protocol]
138 return Configuration.make_proxy_string(entry[0], entry[1], entry[2], entry[3], entry[4], protocol)
140 def combine_host_only(self, protocol):
141 entry = self.proxies[protocol]
142 return Configuration.make_host_string(entry[0], entry[1], entry[2], entry[3], protocol)
144 def combine_port_only(self, protocol):
145 entry = self.proxies[protocol]
146 return Configuration.make_port_string(entry[4])
148 def update(self, params):
150 self.curr_distro = params["distro"]
151 self.dldir = params["dldir"]
152 self.sstatedir = params["sstatedir"]
153 self.sstatemirror = params["sstatemirror"]
154 self.pmake = int(params["pmake"].split()[1])
155 self.bbthread = params["bbthread"]
156 self.curr_package_format = " ".join(params["pclass"].split("package_")).strip()
157 self.image_rootfs_size = params["image_rootfs_size"]
158 self.image_extra_size = params["image_extra_size"]
159 self.image_overhead_factor = params['image_overhead_factor']
160 self.incompat_license = params["incompat_license"]
161 self.curr_sdk_machine = params["sdk_machine"]
162 self.conf_version = params["conf_version"]
163 self.lconf_version = params["lconf_version"]
164 self.image_fstypes = params["image_fstypes"]
165 # self.extra_setting/self.toolchain_build
167 self.layers = params["layer"].split()
168 self.default_task = params["default_task"]
171 self.enable_proxy = params["http_proxy"] != "" or params["https_proxy"] != "" or params["ftp_proxy"] != "" \
172 or params["git_proxy_host"] != "" or params["git_proxy_port"] != "" \
173 or params["cvs_proxy_host"] != "" or params["cvs_proxy_port"] != ""
174 self.split_proxy("http", params["http_proxy"])
175 self.split_proxy("https", params["https_proxy"])
176 self.split_proxy("ftp", params["ftp_proxy"])
177 self.split_proxy("git", params["git_proxy_host"] + ":" + params["git_proxy_port"])
178 self.split_proxy("cvs", params["cvs_proxy_host"] + ":" + params["cvs_proxy_port"])
180 def load(self, template):
181 self.curr_mach = template.getVar("MACHINE")
182 self.curr_package_format = " ".join(template.getVar("PACKAGE_CLASSES").split("package_")).strip()
183 self.curr_distro = template.getVar("DISTRO")
184 self.dldir = template.getVar("DL_DIR")
185 self.sstatedir = template.getVar("SSTATE_DIR")
186 self.sstatemirror = template.getVar("SSTATE_MIRROR")
188 self.pmake = int(template.getVar("PARALLEL_MAKE").split()[1])
192 self.bbthread = int(template.getVar("BB_NUMBER_THREADS"))
196 self.image_rootfs_size = int(template.getVar("IMAGE_ROOTFS_SIZE"))
200 self.image_extra_size = int(template.getVar("IMAGE_EXTRA_SPACE"))
203 # image_overhead_factor is read-only.
204 self.incompat_license = template.getVar("INCOMPATIBLE_LICENSE")
205 self.curr_sdk_machine = template.getVar("SDKMACHINE")
206 self.conf_version = template.getVar("CONF_VERSION")
207 self.lconf_version = template.getVar("LCONF_VERSION")
208 self.extra_setting = eval(template.getVar("EXTRA_SETTING"))
209 self.toolchain_build = eval(template.getVar("TOOLCHAIN_BUILD"))
210 self.image_fstypes = template.getVar("IMAGE_FSTYPES")
212 self.layers = template.getVar("BBLAYERS").split()
213 # image/recipes/packages
214 self.selected_image = template.getVar("__SELECTED_IMAGE__")
215 self.selected_recipes = template.getVar("DEPENDS").split()
216 self.selected_packages = template.getVar("IMAGE_INSTALL").split()
218 self.enable_proxy = eval(template.getVar("enable_proxy"))
219 self.same_proxy = eval(template.getVar("use_same_proxy"))
220 self.split_proxy("http", template.getVar("http_proxy"))
221 self.split_proxy("https", template.getVar("https_proxy"))
222 self.split_proxy("ftp", template.getVar("ftp_proxy"))
223 self.split_proxy("git", template.getVar("GIT_PROXY_HOST") + ":" + template.getVar("GIT_PROXY_PORT"))
224 self.split_proxy("cvs", template.getVar("CVS_PROXY_HOST") + ":" + template.getVar("CVS_PROXY_PORT"))
226 def save(self, template, defaults=False):
227 template.setVar("VERSION", "%s" % hobVer)
229 template.setVar("BBLAYERS", " ".join(self.layers))
232 template.setVar("MACHINE", self.curr_mach)
233 template.setVar("DISTRO", self.curr_distro)
234 template.setVar("DL_DIR", self.dldir)
235 template.setVar("SSTATE_DIR", self.sstatedir)
236 template.setVar("SSTATE_MIRROR", self.sstatemirror)
237 template.setVar("PARALLEL_MAKE", "-j %s" % self.pmake)
238 template.setVar("BB_NUMBER_THREADS", self.bbthread)
239 template.setVar("PACKAGE_CLASSES", " ".join(["package_" + i for i in self.curr_package_format.split()]))
240 template.setVar("IMAGE_ROOTFS_SIZE", self.image_rootfs_size)
241 template.setVar("IMAGE_EXTRA_SPACE", self.image_extra_size)
242 template.setVar("INCOMPATIBLE_LICENSE", self.incompat_license)
243 template.setVar("SDKMACHINE", self.curr_sdk_machine)
244 template.setVar("CONF_VERSION", self.conf_version)
245 template.setVar("LCONF_VERSION", self.lconf_version)
246 template.setVar("EXTRA_SETTING", self.extra_setting)
247 template.setVar("TOOLCHAIN_BUILD", self.toolchain_build)
248 template.setVar("IMAGE_FSTYPES", self.image_fstypes)
250 # image/recipes/packages
251 template.setVar("__SELECTED_IMAGE__", self.selected_image)
252 template.setVar("DEPENDS", self.selected_recipes)
253 template.setVar("IMAGE_INSTALL", self.user_selected_packages)
255 template.setVar("enable_proxy", self.enable_proxy)
256 template.setVar("use_same_proxy", self.same_proxy)
257 template.setVar("http_proxy", self.combine_proxy("http"))
258 template.setVar("https_proxy", self.combine_proxy("https"))
259 template.setVar("ftp_proxy", self.combine_proxy("ftp"))
260 template.setVar("GIT_PROXY_HOST", self.combine_host_only("git"))
261 template.setVar("GIT_PROXY_PORT", self.combine_port_only("git"))
262 template.setVar("CVS_PROXY_HOST", self.combine_host_only("cvs"))
263 template.setVar("CVS_PROXY_PORT", self.combine_port_only("cvs"))
266 '''Represents other variables like available machines, etc.'''
270 self.max_threads = 65535
273 self.image_types = []
274 self.runnable_image_types = []
275 self.runnable_machine_patterns = []
276 self.deployable_image_types = []
279 self.all_machines = []
280 self.all_package_formats = []
281 self.all_distros = []
282 self.all_sdk_machines = []
284 self.image_names = []
285 self.image_white_pattern = ""
286 self.image_black_pattern = ""
288 # for build log to show
290 self.target_arch = ""
292 self.distro_version = ""
293 self.tune_pkgarch = ""
295 def update(self, params):
296 self.max_threads = params["max_threads"]
297 self.core_base = params["core_base"]
298 self.image_addr = params["image_addr"]
299 self.image_types = params["image_types"].split()
300 self.runnable_image_types = params["runnable_image_types"].split()
301 self.runnable_machine_patterns = params["runnable_machine_patterns"].split()
302 self.deployable_image_types = params["deployable_image_types"].split()
303 self.tmpdir = params["tmpdir"]
304 self.image_white_pattern = params["image_white_pattern"]
305 self.image_black_pattern = params["image_black_pattern"]
306 self.kernel_image_type = params["kernel_image_type"]
307 # for build log to show
308 self.bb_version = params["bb_version"]
309 self.target_arch = params["target_arch"]
310 self.target_os = params["target_os"]
311 self.distro_version = params["distro_version"]
312 self.tune_pkgarch = params["tune_pkgarch"]
314 def hob_conf_filter(fn, data):
315 if fn.endswith("/local.conf"):
316 distro = data.getVar("DISTRO_HOB")
318 if distro != "defaultsetup":
319 data.setVar("DISTRO", distro)
321 data.delVar("DISTRO")
323 keys = ["MACHINE_HOB", "SDKMACHINE_HOB", "PACKAGE_CLASSES_HOB", \
324 "BB_NUMBER_THREADS_HOB", "PARALLEL_MAKE_HOB", "DL_DIR_HOB", \
325 "SSTATE_DIR_HOB", "SSTATE_MIRROR_HOB", "INCOMPATIBLE_LICENSE_HOB"]
327 var_hob = data.getVar(key)
329 data.setVar(key.split("_HOB")[0], var_hob)
332 if fn.endswith("/bblayers.conf"):
333 layers = data.getVar("BBLAYERS_HOB")
335 data.setVar("BBLAYERS", layers)
338 class Builder(gtk.Window):
341 RCPPKGINFO_POPULATING,
342 RCPPKGINFO_POPULATED,
348 FAST_IMAGE_GENERATING,
353 END_NOOP) = range(14)
355 (IMAGE_CONFIGURATION,
363 MACHINE_SELECTION : IMAGE_CONFIGURATION,
364 RCPPKGINFO_POPULATING : IMAGE_CONFIGURATION,
365 RCPPKGINFO_POPULATED : IMAGE_CONFIGURATION,
366 BASEIMG_SELECTED : IMAGE_CONFIGURATION,
367 RECIPE_SELECTION : RECIPE_DETAILS,
368 PACKAGE_GENERATING : BUILD_DETAILS,
369 PACKAGE_GENERATED : PACKAGE_DETAILS,
370 PACKAGE_SELECTION : PACKAGE_DETAILS,
371 FAST_IMAGE_GENERATING : BUILD_DETAILS,
372 IMAGE_GENERATING : BUILD_DETAILS,
373 IMAGE_GENERATED : IMAGE_DETAILS,
374 MY_IMAGE_OPENED : IMAGE_DETAILS,
379 def interpret_markup(cls, msg):
380 msg = msg.replace('&', '&')
381 msg = msg.replace('<', '<')
382 msg = msg.replace('>', '>')
383 msg = msg.replace('"', '"')
384 msg = msg.replace("'", "´")
387 def __init__(self, hobHandler, recipe_model, package_model):
388 super(Builder, self).__init__()
390 self.hob_image = "hob-image"
391 self.hob_toolchain = "hob-toolchain"
394 self.handler = hobHandler
398 # configuration and parameters
399 self.configuration = Configuration()
400 self.parameters = Parameters()
403 self.current_step = None
404 self.previous_step = None
406 self.stopping = False
408 # recipe model and package model
409 self.recipe_model = recipe_model
410 self.package_model = package_model
412 # Indicate whether user has customized the image
413 self.customized = False
415 # Indicate whether the UI is working
416 self.sensitive = True
418 # create visual elements
419 self.create_visual_elements()
421 # connect the signals to functions
422 self.connect("delete-event", self.destroy_window_cb)
423 self.recipe_model.connect ("recipe-selection-changed", self.recipelist_changed_cb)
424 self.package_model.connect("package-selection-changed", self.packagelist_changed_cb)
425 self.handler.connect("config-updated", self.handler_config_updated_cb)
426 self.handler.connect("package-formats-updated", self.handler_package_formats_updated_cb)
427 self.handler.connect("parsing-started", self.handler_parsing_started_cb)
428 self.handler.connect("parsing", self.handler_parsing_cb)
429 self.handler.connect("parsing-completed", self.handler_parsing_completed_cb)
430 self.handler.build.connect("build-started", self.handler_build_started_cb)
431 self.handler.build.connect("build-succeeded", self.handler_build_succeeded_cb)
432 self.handler.build.connect("build-failed", self.handler_build_failed_cb)
433 self.handler.build.connect("task-started", self.handler_task_started_cb)
434 self.handler.build.connect("log-error", self.handler_build_failure_cb)
435 self.handler.build.connect("no-provider", self.handler_no_provider_cb)
436 self.handler.connect("generating-data", self.handler_generating_data_cb)
437 self.handler.connect("data-generated", self.handler_data_generated_cb)
438 self.handler.connect("command-succeeded", self.handler_command_succeeded_cb)
439 self.handler.connect("command-failed", self.handler_command_failed_cb)
440 self.handler.connect("sanity-failed", self.handler_sanity_failed_cb)
441 self.handler.connect("recipe-populated", self.handler_recipe_populated_cb)
442 self.handler.connect("package-populated", self.handler_package_populated_cb)
444 self.handler.set_config_filter(hob_conf_filter)
446 self.initiate_new_build_async()
448 def create_visual_elements(self):
449 self.set_title("Hob")
450 self.set_icon_name("applications-development")
451 self.set_resizable(True)
452 window_width = self.get_screen().get_width()
453 window_height = self.get_screen().get_height()
454 if window_width >= hwc.MAIN_WIN_WIDTH:
455 window_width = hwc.MAIN_WIN_WIDTH
456 window_height = hwc.MAIN_WIN_HEIGHT
457 self.set_size_request(window_width, window_height)
459 self.vbox = gtk.VBox(False, 0)
460 self.vbox.set_border_width(0)
464 self.image_configuration_page = ImageConfigurationPage(self)
465 self.recipe_details_page = RecipeSelectionPage(self)
466 self.build_details_page = BuildDetailsPage(self)
467 self.package_details_page = PackageSelectionPage(self)
468 self.image_details_page = ImageDetailsPage(self)
470 self.nb = gtk.Notebook()
471 self.nb.set_show_tabs(False)
472 self.nb.insert_page(self.image_configuration_page, None, self.IMAGE_CONFIGURATION)
473 self.nb.insert_page(self.recipe_details_page, None, self.RECIPE_DETAILS)
474 self.nb.insert_page(self.build_details_page, None, self.BUILD_DETAILS)
475 self.nb.insert_page(self.package_details_page, None, self.PACKAGE_DETAILS)
476 self.nb.insert_page(self.image_details_page, None, self.IMAGE_DETAILS)
477 self.vbox.pack_start(self.nb, expand=True, fill=True)
480 self.nb.set_current_page(0)
482 def initiate_new_build_async(self):
483 self.switch_page(self.MACHINE_SELECTION)
484 if self.load_template(TemplateMgr.convert_to_template_pathfilename("default", ".hob/")) == False:
485 self.handler.init_cooker()
486 self.handler.set_extra_inherit("image_types")
487 self.handler.generate_configuration()
489 def update_config_async(self):
490 self.switch_page(self.MACHINE_SELECTION)
491 self.set_user_config()
492 self.handler.generate_configuration()
494 def sanity_check(self):
495 self.handler.trigger_sanity_check()
497 def populate_recipe_package_info_async(self):
498 self.switch_page(self.RCPPKGINFO_POPULATING)
500 self.set_user_config()
501 self.handler.generate_recipes()
503 def generate_packages_async(self):
504 self.switch_page(self.PACKAGE_GENERATING)
506 _, all_recipes = self.recipe_model.get_selected_recipes()
507 self.set_user_config()
508 self.handler.reset_build()
509 self.handler.generate_packages(all_recipes, self.configuration.default_task)
511 def fast_generate_image_async(self):
512 self.switch_page(self.FAST_IMAGE_GENERATING)
514 _, all_recipes = self.recipe_model.get_selected_recipes()
515 self.set_user_config()
516 self.handler.reset_build()
517 self.handler.generate_packages(all_recipes, self.configuration.default_task)
519 def generate_image_async(self):
520 self.switch_page(self.IMAGE_GENERATING)
521 self.handler.reset_build()
523 self.set_user_config()
524 toolchain_packages = []
525 if self.configuration.toolchain_build:
526 toolchain_packages = self.package_model.get_selected_packages_toolchain()
527 if self.configuration.selected_image == self.recipe_model.__custom_image__:
528 packages = self.package_model.get_selected_packages()
529 image = self.hob_image
532 image = self.configuration.selected_image
533 self.handler.generate_image(image,
537 self.configuration.default_task)
539 def get_parameters_sync(self):
540 return self.handler.get_parameters()
542 def request_package_info_async(self):
543 self.handler.request_package_info()
545 def cancel_build_sync(self, force=False):
546 self.handler.cancel_build(force)
548 def cancel_parse_sync(self):
549 self.handler.cancel_parse()
551 def load_template(self, path):
552 if not os.path.isfile(path):
555 self.template = TemplateMgr()
556 # check compatibility
557 tempVer = self.template.getVersion(path)
558 if not tempVer or int(tempVer) < hobVer:
559 self.template.destroy()
564 self.template.load(path)
565 self.configuration.load(self.template)
566 except Exception as e:
567 self.show_error_dialog("Hob Exception - %s" % (str(e)))
570 self.template.destroy()
573 for layer in self.configuration.layers:
574 if not os.path.exists(layer+'/conf/layer.conf'):
577 self.save_defaults() # remember layers and settings
578 self.update_config_async()
581 def save_template(self, path, defaults=False):
582 if path.rfind("/") == -1:
586 filename = path[path.rfind("/") + 1:len(path)]
587 path = path[0:path.rfind("/")]
589 self.template = TemplateMgr()
591 self.template.open(filename, path)
592 self.configuration.save(self.template, defaults)
595 except Exception as e:
596 self.show_error_dialog("Hob Exception - %s" % (str(e)))
599 self.template.destroy()
602 def save_defaults(self):
603 if not os.path.exists(".hob/"):
605 self.save_template(".hob/default", True)
607 def switch_page(self, next_step):
608 # Main Workflow (Business Logic)
609 self.nb.set_current_page(self.__step2page__[next_step])
611 if next_step == self.MACHINE_SELECTION: # init step
612 self.image_configuration_page.show_machine()
614 elif next_step == self.RCPPKGINFO_POPULATING:
615 # MACHINE CHANGED action or SETTINGS CHANGED
616 # show the progress bar
617 self.image_configuration_page.show_info_populating()
619 elif next_step == self.RCPPKGINFO_POPULATED:
620 self.image_configuration_page.show_info_populated()
622 elif next_step == self.BASEIMG_SELECTED:
623 self.image_configuration_page.show_baseimg_selected()
625 elif next_step == self.RECIPE_SELECTION:
628 elif next_step == self.PACKAGE_SELECTION:
631 elif next_step == self.PACKAGE_GENERATING or next_step == self.FAST_IMAGE_GENERATING:
632 # both PACKAGE_GENEATING and FAST_IMAGE_GENERATING share the same page
633 self.build_details_page.show_page(next_step)
635 elif next_step == self.PACKAGE_GENERATED:
638 elif next_step == self.IMAGE_GENERATING:
639 # after packages are generated, selected_packages need to
640 # be updated in package_model per selected_image in recipe_model
641 self.build_details_page.show_page(next_step)
643 elif next_step == self.IMAGE_GENERATED:
644 self.image_details_page.show_page(next_step)
646 elif next_step == self.MY_IMAGE_OPENED:
647 self.image_details_page.show_page(next_step)
649 self.previous_step = self.current_step
650 self.current_step = next_step
652 def set_user_config(self):
653 self.handler.init_cooker()
655 self.handler.set_bblayers(self.configuration.layers)
656 # set local configuration
657 self.handler.set_machine(self.configuration.curr_mach)
658 self.handler.set_package_format(self.configuration.curr_package_format)
659 self.handler.set_distro(self.configuration.curr_distro)
660 self.handler.set_dl_dir(self.configuration.dldir)
661 self.handler.set_sstate_dir(self.configuration.sstatedir)
662 self.handler.set_sstate_mirror(self.configuration.sstatemirror)
663 self.handler.set_pmake(self.configuration.pmake)
664 self.handler.set_bbthreads(self.configuration.bbthread)
665 self.handler.set_rootfs_size(self.configuration.image_rootfs_size)
666 self.handler.set_extra_size(self.configuration.image_extra_size)
667 self.handler.set_incompatible_license(self.configuration.incompat_license)
668 self.handler.set_sdk_machine(self.configuration.curr_sdk_machine)
669 self.handler.set_image_fstypes(self.configuration.image_fstypes)
670 self.handler.set_extra_config(self.configuration.extra_setting)
671 self.handler.set_extra_inherit("packageinfo")
672 self.handler.set_extra_inherit("image_types")
674 if self.configuration.enable_proxy == True:
675 self.handler.set_http_proxy(self.configuration.combine_proxy("http"))
676 self.handler.set_https_proxy(self.configuration.combine_proxy("https"))
677 self.handler.set_ftp_proxy(self.configuration.combine_proxy("ftp"))
678 self.handler.set_git_proxy(self.configuration.combine_host_only("git"), self.configuration.combine_port_only("git"))
679 self.handler.set_cvs_proxy(self.configuration.combine_host_only("cvs"), self.configuration.combine_port_only("cvs"))
680 elif self.configuration.enable_proxy == False:
681 self.handler.set_http_proxy("")
682 self.handler.set_https_proxy("")
683 self.handler.set_ftp_proxy("")
684 self.handler.set_git_proxy("", "")
685 self.handler.set_cvs_proxy("", "")
687 def update_recipe_model(self, selected_image, selected_recipes):
688 self.recipe_model.set_selected_image(selected_image)
689 self.recipe_model.set_selected_recipes(selected_recipes)
691 def update_package_model(self, selected_packages):
692 left = self.package_model.set_selected_packages(selected_packages)
693 self.configuration.selected_packages += left
695 def update_configuration_parameters(self, params):
697 self.configuration.update(params)
698 self.parameters.update(params)
701 self.configuration.curr_mach = ""
702 self.configuration.clear_selection()
703 self.image_configuration_page.switch_machine_combo()
704 self.switch_page(self.MACHINE_SELECTION)
707 def handler_config_updated_cb(self, handler, which, values):
708 if which == "distro":
709 self.parameters.all_distros = values
710 elif which == "machine":
711 self.parameters.all_machines = values
712 self.image_configuration_page.update_machine_combo()
713 elif which == "machine-sdk":
714 self.parameters.all_sdk_machines = values
716 def handler_package_formats_updated_cb(self, handler, formats):
717 self.parameters.all_package_formats = formats
719 def handler_command_succeeded_cb(self, handler, initcmd):
720 if initcmd == self.handler.GENERATE_CONFIGURATION:
721 self.update_configuration_parameters(self.get_parameters_sync())
723 elif initcmd == self.handler.SANITY_CHECK:
724 self.image_configuration_page.switch_machine_combo()
725 elif initcmd in [self.handler.GENERATE_RECIPES,
726 self.handler.GENERATE_PACKAGES,
727 self.handler.GENERATE_IMAGE]:
728 self.update_configuration_parameters(self.get_parameters_sync())
729 self.request_package_info_async()
730 elif initcmd == self.handler.POPULATE_PACKAGEINFO:
731 if self.current_step == self.RCPPKGINFO_POPULATING:
732 self.switch_page(self.RCPPKGINFO_POPULATED)
733 self.rcppkglist_populated()
736 self.rcppkglist_populated()
737 if self.current_step == self.FAST_IMAGE_GENERATING:
738 self.generate_image_async()
740 def show_error_dialog(self, msg):
741 lbl = "<b>Error</b>\n"
742 lbl = lbl + "%s\n\n" % Builder.interpret_markup(msg)
743 dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR)
744 button = dialog.add_button("Close", gtk.RESPONSE_OK)
745 HobButton.style_button(button)
746 response = dialog.run()
749 def handler_command_failed_cb(self, handler, msg):
751 self.show_error_dialog(msg)
754 def handler_sanity_failed_cb(self, handler, msg):
755 msg = msg.replace("your local.conf", "Settings")
756 self.show_error_dialog(msg)
759 def window_sensitive(self, sensitive):
760 self.image_configuration_page.machine_combo.set_sensitive(sensitive)
761 self.image_configuration_page.machine_combo.child.set_sensitive(sensitive)
762 self.image_configuration_page.image_combo.set_sensitive(sensitive)
763 self.image_configuration_page.image_combo.child.set_sensitive(sensitive)
764 self.image_configuration_page.layer_button.set_sensitive(sensitive)
765 self.image_configuration_page.layer_info_icon.set_sensitive(sensitive)
766 self.image_configuration_page.toolbar.set_sensitive(sensitive)
767 self.image_configuration_page.view_recipes_button.set_sensitive(sensitive)
768 self.image_configuration_page.view_packages_button.set_sensitive(sensitive)
769 self.image_configuration_page.config_build_button.set_sensitive(sensitive)
771 self.recipe_details_page.set_sensitive(sensitive)
772 self.package_details_page.set_sensitive(sensitive)
773 self.build_details_page.set_sensitive(sensitive)
774 self.image_details_page.set_sensitive(sensitive)
777 self.get_root_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR))
779 self.get_root_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
780 self.sensitive = sensitive
783 def handler_generating_data_cb(self, handler):
784 self.window_sensitive(False)
786 def handler_data_generated_cb(self, handler):
787 self.window_sensitive(True)
789 def rcppkglist_populated(self):
790 selected_image = self.configuration.selected_image
791 selected_recipes = self.configuration.selected_recipes[:]
792 selected_packages = self.configuration.selected_packages[:]
794 self.image_configuration_page.update_image_combo(self.recipe_model, selected_image)
795 self.image_configuration_page.update_image_desc()
796 self.update_recipe_model(selected_image, selected_recipes)
797 self.update_package_model(selected_packages)
799 def recipelist_changed_cb(self, recipe_model):
800 self.recipe_details_page.refresh_selection()
802 def packagelist_changed_cb(self, package_model):
803 self.package_details_page.refresh_selection()
805 def handler_recipe_populated_cb(self, handler):
806 self.image_configuration_page.update_progress_bar("Populated recipes", 0.99)
808 def handler_package_populated_cb(self, handler):
809 self.image_configuration_page.update_progress_bar("Populated packages", 1.0)
811 def handler_parsing_started_cb(self, handler, message):
812 if self.current_step != self.RCPPKGINFO_POPULATING:
816 if message["eventname"] == "TreeDataPreparationStarted":
817 fraction = 0.6 + fraction
818 self.image_configuration_page.stop_button.set_sensitive(False)
820 self.image_configuration_page.stop_button.set_sensitive(True)
822 self.image_configuration_page.update_progress_bar(message["title"], fraction)
824 def handler_parsing_cb(self, handler, message):
825 if self.current_step != self.RCPPKGINFO_POPULATING:
828 fraction = message["current"] * 1.0/message["total"]
829 if message["eventname"] == "TreeDataPreparationProgress":
830 fraction = 0.6 + 0.38 * fraction
832 fraction = 0.6 * fraction
833 self.image_configuration_page.update_progress_bar(message["title"], fraction)
835 def handler_parsing_completed_cb(self, handler, message):
836 if self.current_step != self.RCPPKGINFO_POPULATING:
839 if message["eventname"] == "TreeDataPreparationCompleted":
843 self.image_configuration_page.update_progress_bar(message["title"], fraction)
845 def handler_build_started_cb(self, running_build):
846 if self.current_step == self.FAST_IMAGE_GENERATING:
848 elif self.current_step == self.IMAGE_GENERATING:
849 if self.previous_step == self.FAST_IMAGE_GENERATING:
853 elif self.current_step == self.PACKAGE_GENERATING:
855 self.build_details_page.update_progress_bar("Build Started: ", fraction)
856 self.build_details_page.show_configurations(self.configuration, self.parameters)
858 def build_succeeded(self):
859 if self.current_step == self.FAST_IMAGE_GENERATING:
861 elif self.current_step == self.IMAGE_GENERATING:
863 self.parameters.image_names = []
864 selected_image = self.recipe_model.get_selected_image()
865 if selected_image == self.recipe_model.__custom_image__:
866 linkname = 'hob-image-' + self.configuration.curr_mach
868 linkname = selected_image + '-' + self.configuration.curr_mach
869 for image_type in self.parameters.image_types:
870 for real_image_type in hcc.SUPPORTED_IMAGE_TYPES[image_type]:
871 linkpath = self.parameters.image_addr + '/' + linkname + '.' + real_image_type
872 if os.path.exists(linkpath):
873 self.parameters.image_names.append(os.readlink(linkpath))
874 elif self.current_step == self.PACKAGE_GENERATING:
876 self.build_details_page.update_progress_bar("Build Completed: ", fraction)
877 self.handler.build_succeeded_async()
878 self.stopping = False
880 if self.current_step == self.PACKAGE_GENERATING:
881 self.switch_page(self.PACKAGE_GENERATED)
882 elif self.current_step == self.IMAGE_GENERATING:
883 self.switch_page(self.IMAGE_GENERATED)
885 def build_failed(self):
888 message = "Build stopped: "
889 fraction = self.build_details_page.progress_bar.get_fraction()
891 fail_to_next_edit = ""
892 if self.current_step == self.FAST_IMAGE_GENERATING:
893 fail_to_next_edit = "image configuration"
895 elif self.current_step == self.IMAGE_GENERATING:
896 if self.previous_step == self.FAST_IMAGE_GENERATING:
897 fail_to_next_edit = "image configuration"
899 fail_to_next_edit = "packages"
901 elif self.current_step == self.PACKAGE_GENERATING:
902 fail_to_next_edit = "recipes"
904 self.build_details_page.show_fail_page(fail_to_next_edit.split(' ')[0], fail_to_next_edit)
906 message = "Build failed: "
907 self.build_details_page.update_progress_bar(message, fraction, status)
908 self.build_details_page.show_back_button()
909 self.build_details_page.hide_stop_button()
910 self.handler.build_failed_async()
911 self.stopping = False
913 def handler_build_succeeded_cb(self, running_build):
914 if not self.stopping:
915 self.build_succeeded()
920 def handler_build_failed_cb(self, running_build):
923 def handler_no_provider_cb(self, running_build, msg):
924 dialog = CrumbsMessageDialog(self, Builder.interpret_markup(msg), gtk.STOCK_DIALOG_INFO)
925 button = dialog.add_button("Close", gtk.RESPONSE_OK)
926 HobButton.style_button(button)
931 def handler_task_started_cb(self, running_build, message):
932 fraction = message["current"] * 1.0/message["total"]
933 title = "Build packages"
934 if self.current_step == self.FAST_IMAGE_GENERATING:
935 if message["eventname"] == "sceneQueueTaskStarted":
936 fraction = 0.27 * fraction
937 elif message["eventname"] == "runQueueTaskStarted":
938 fraction = 0.27 + 0.63 * fraction
939 elif self.current_step == self.IMAGE_GENERATING:
940 title = "Build image"
941 if self.previous_step == self.FAST_IMAGE_GENERATING:
942 if message["eventname"] == "sceneQueueTaskStarted":
943 fraction = 0.27 + 0.63 + 0.03 * fraction
944 elif message["eventname"] == "runQueueTaskStarted":
945 fraction = 0.27 + 0.63 + 0.03 + 0.07 * fraction
947 if message["eventname"] == "sceneQueueTaskStarted":
948 fraction = 0.2 * fraction
949 elif message["eventname"] == "runQueueTaskStarted":
950 fraction = 0.2 + 0.8 * fraction
951 elif self.current_step == self.PACKAGE_GENERATING:
952 if message["eventname"] == "sceneQueueTaskStarted":
953 fraction = 0.2 * fraction
954 elif message["eventname"] == "runQueueTaskStarted":
955 fraction = 0.2 + 0.8 * fraction
956 self.build_details_page.update_progress_bar(title + ": ", fraction)
957 self.build_details_page.update_build_status(message["current"], message["total"], message["task"])
959 def handler_build_failure_cb(self, running_build):
960 self.build_details_page.show_issues()
962 def destroy_window_cb(self, widget, event):
963 if not self.sensitive:
965 lbl = "<b>Do you really want to exit the Hob image creator?</b>"
966 dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
967 button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
968 HobAltButton.style_button(button)
969 button = dialog.add_button("Exit Hob", gtk.RESPONSE_YES)
970 HobButton.style_button(button)
971 dialog.set_default_response(gtk.RESPONSE_YES)
972 response = dialog.run()
974 if response == gtk.RESPONSE_YES:
980 def build_packages(self):
981 _, all_recipes = self.recipe_model.get_selected_recipes()
983 lbl = "<b>No selections made</b>\nYou have not made any selections"
984 lbl = lbl + " so there isn't anything to bake at this time."
985 dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
986 button = dialog.add_button("Close", gtk.RESPONSE_OK)
987 HobButton.style_button(button)
991 self.generate_packages_async()
993 def build_image(self):
994 selected_packages = self.package_model.get_selected_packages()
995 if not selected_packages:
996 lbl = "<b>No selections made</b>\nYou have not made any selections"
997 lbl = lbl + " so there isn't anything to bake at this time."
998 dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
999 button = dialog.add_button("Close", gtk.RESPONSE_OK)
1000 HobButton.style_button(button)
1004 self.generate_image_async()
1006 def just_bake(self):
1007 selected_image = self.recipe_model.get_selected_image()
1008 selected_packages = self.package_model.get_selected_packages() or []
1010 # If no base image and no selected packages don't build anything
1011 if not (selected_packages or selected_image != self.recipe_model.__custom_image__):
1012 lbl = "<b>No selections made</b>\nYou have not made any selections"
1013 lbl = lbl + " so there isn't anything to bake at this time."
1014 dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1015 button = dialog.add_button("Close", gtk.RESPONSE_OK)
1016 HobButton.style_button(button)
1021 self.fast_generate_image_async()
1023 def show_binb_dialog(self, binb):
1024 markup = "<b>Brought in by:</b>\n%s" % binb
1025 ptip = PersistentTooltip(markup, self)
1029 def show_layer_selection_dialog(self):
1030 dialog = LayerSelectionDialog(title = "Layers",
1031 layers = copy.deepcopy(self.configuration.layers),
1032 all_layers = self.parameters.all_layers,
1034 flags = gtk.DIALOG_MODAL
1035 | gtk.DIALOG_DESTROY_WITH_PARENT
1036 | gtk.DIALOG_NO_SEPARATOR)
1037 button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1038 HobAltButton.style_button(button)
1039 button = dialog.add_button("OK", gtk.RESPONSE_YES)
1040 HobButton.style_button(button)
1041 response = dialog.run()
1042 if response == gtk.RESPONSE_YES:
1043 self.configuration.layers = dialog.layers
1044 self.save_defaults() # remember layers
1046 if dialog.layers_changed:
1047 self.update_config_async()
1050 def show_load_template_dialog(self):
1051 dialog = gtk.FileChooserDialog("Load Template Files", self,
1052 gtk.FILE_CHOOSER_ACTION_OPEN)
1053 button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1054 HobAltButton.style_button(button)
1055 button = dialog.add_button("Open", gtk.RESPONSE_YES)
1056 HobButton.style_button(button)
1057 filter = gtk.FileFilter()
1058 filter.set_name("Hob Files")
1059 filter.add_pattern("*.hob")
1060 dialog.add_filter(filter)
1062 response = dialog.run()
1064 if response == gtk.RESPONSE_YES:
1065 path = dialog.get_filename()
1067 return response == gtk.RESPONSE_YES, path
1069 def show_save_template_dialog(self):
1070 dialog = gtk.FileChooserDialog("Save Template Files", self,
1071 gtk.FILE_CHOOSER_ACTION_SAVE)
1072 button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1073 HobAltButton.style_button(button)
1074 button = dialog.add_button("Save", gtk.RESPONSE_YES)
1075 HobButton.style_button(button)
1076 dialog.set_current_name("hob")
1077 response = dialog.run()
1078 if response == gtk.RESPONSE_YES:
1079 path = dialog.get_filename()
1080 self.save_template(path)
1083 def show_load_my_images_dialog(self):
1084 dialog = ImageSelectionDialog(self.parameters.image_addr, self.parameters.image_types,
1085 "Open My Images", self,
1086 gtk.FILE_CHOOSER_ACTION_SAVE)
1087 button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1088 HobAltButton.style_button(button)
1089 button = dialog.add_button("Open", gtk.RESPONSE_YES)
1090 HobButton.style_button(button)
1091 response = dialog.run()
1092 if response == gtk.RESPONSE_YES:
1093 if not dialog.image_names:
1094 lbl = "<b>No selections made</b>\nYou have not made any selections"
1095 crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1096 button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK)
1097 HobButton.style_button(button)
1099 crumbs_dialog.destroy()
1103 self.parameters.image_addr = dialog.image_folder
1104 self.parameters.image_names = dialog.image_names[:]
1105 self.switch_page(self.MY_IMAGE_OPENED)
1109 def show_adv_settings_dialog(self):
1110 dialog = AdvancedSettingDialog(title = "Settings",
1111 configuration = copy.deepcopy(self.configuration),
1112 all_image_types = self.parameters.image_types,
1113 all_package_formats = self.parameters.all_package_formats,
1114 all_distros = self.parameters.all_distros,
1115 all_sdk_machines = self.parameters.all_sdk_machines,
1116 max_threads = self.parameters.max_threads,
1118 flags = gtk.DIALOG_MODAL
1119 | gtk.DIALOG_DESTROY_WITH_PARENT
1120 | gtk.DIALOG_NO_SEPARATOR)
1121 button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1122 HobAltButton.style_button(button)
1123 button = dialog.add_button("Save", gtk.RESPONSE_YES)
1124 HobButton.style_button(button)
1125 response = dialog.run()
1126 settings_changed = False
1127 if response == gtk.RESPONSE_YES:
1128 self.configuration = dialog.configuration
1129 self.save_defaults() # remember settings
1130 settings_changed = dialog.settings_changed
1132 return response == gtk.RESPONSE_YES, settings_changed
1134 def reparse_post_adv_settings(self):
1135 if not self.configuration.curr_mach:
1136 self.update_config_async()
1138 self.configuration.clear_selection()
1139 # DO reparse recipes
1140 self.populate_recipe_package_info_async()
1142 def deploy_image(self, image_name):
1144 lbl = "<b>Please select an image to deploy.</b>"
1145 dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1146 button = dialog.add_button("Close", gtk.RESPONSE_OK)
1147 HobButton.style_button(button)
1152 image_path = os.path.join(self.parameters.image_addr, image_name)
1153 dialog = DeployImageDialog(title = "Usb Image Maker",
1154 image_path = image_path,
1156 flags = gtk.DIALOG_MODAL
1157 | gtk.DIALOG_DESTROY_WITH_PARENT
1158 | gtk.DIALOG_NO_SEPARATOR)
1159 button = dialog.add_button("Close", gtk.RESPONSE_NO)
1160 HobAltButton.style_button(button)
1161 button = dialog.add_button("Make usb image", gtk.RESPONSE_YES)
1162 HobButton.style_button(button)
1163 response = dialog.run()
1166 def show_load_kernel_dialog(self):
1167 dialog = gtk.FileChooserDialog("Load Kernel Files", self,
1168 gtk.FILE_CHOOSER_ACTION_SAVE)
1169 button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1170 HobAltButton.style_button(button)
1171 button = dialog.add_button("Open", gtk.RESPONSE_YES)
1172 HobButton.style_button(button)
1173 filter = gtk.FileFilter()
1174 filter.set_name("Kernel Files")
1175 filter.add_pattern("*.bin")
1176 dialog.add_filter(filter)
1178 dialog.set_current_folder(self.parameters.image_addr)
1180 response = dialog.run()
1182 if response == gtk.RESPONSE_YES:
1183 kernel_path = dialog.get_filename()
1189 def runqemu_image(self, image_name, kernel_name):
1190 if not image_name or not kernel_name:
1191 lbl = "<b>Please select an %s to launch in QEMU.</b>" % ("kernel" if image_name else "image")
1192 dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1193 button = dialog.add_button("Close", gtk.RESPONSE_OK)
1194 HobButton.style_button(button)
1199 kernel_path = os.path.join(self.parameters.image_addr, kernel_name)
1200 image_path = os.path.join(self.parameters.image_addr, image_name)
1202 source_env_path = os.path.join(self.parameters.core_base, "oe-init-build-env")
1203 tmp_path = self.parameters.tmpdir
1204 cmdline = bb.ui.crumbs.utils.which_terminal()
1205 if os.path.exists(image_path) and os.path.exists(kernel_path) \
1206 and os.path.exists(source_env_path) and os.path.exists(tmp_path) \
1208 cmdline += "\' bash -c \"export OE_TMPDIR=" + tmp_path + "; "
1209 cmdline += "source " + source_env_path + " " + os.getcwd() + "; "
1210 cmdline += "runqemu " + kernel_path + " " + image_path + "\"\'"
1211 subprocess.Popen(shlex.split(cmdline))
1213 lbl = "<b>Path error</b>\nOne of your paths is wrong,"
1214 lbl = lbl + " please make sure the following paths exist:\n"
1215 lbl = lbl + "image path:" + image_path + "\n"
1216 lbl = lbl + "kernel path:" + kernel_path + "\n"
1217 lbl = lbl + "source environment path:" + source_env_path + "\n"
1218 lbl = lbl + "tmp path: " + tmp_path + "."
1219 lbl = lbl + "You may be missing either xterm or vte for terminal services."
1220 dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR)
1221 button = dialog.add_button("Close", gtk.RESPONSE_OK)
1222 HobButton.style_button(button)
1226 def show_packages(self, ask=True):
1227 _, selected_recipes = self.recipe_model.get_selected_recipes()
1228 if selected_recipes and ask:
1229 lbl = "<b>Package list may be incomplete!</b>\nDo you want to build selected recipes"
1230 lbl = lbl + " to get a full list or just view the existing packages?"
1231 dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1232 button = dialog.add_button("View packages", gtk.RESPONSE_NO)
1233 HobAltButton.style_button(button)
1234 button = dialog.add_button("Build packages", gtk.RESPONSE_YES)
1235 HobButton.style_button(button)
1236 dialog.set_default_response(gtk.RESPONSE_YES)
1237 response = dialog.run()
1239 if response == gtk.RESPONSE_YES:
1240 self.generate_packages_async()
1242 self.switch_page(self.PACKAGE_SELECTION)
1244 self.switch_page(self.PACKAGE_SELECTION)
1246 def show_recipes(self):
1247 self.switch_page(self.RECIPE_SELECTION)
1249 def show_configuration(self):
1250 self.switch_page(self.BASEIMG_SELECTED)
1252 def stop_build(self):
1254 lbl = "<b>Force Stop build?</b>\nYou've already selected Stop once,"
1255 lbl = lbl + " would you like to 'Force Stop' the build?\n\n"
1256 lbl = lbl + "This will stop the build as quickly as possible but may"
1257 lbl = lbl + " well leave your build directory in an unusable state"
1258 lbl = lbl + " that requires manual steps to fix.\n"
1259 dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
1260 button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
1261 HobAltButton.style_button(button)
1262 button = dialog.add_button("Force Stop", gtk.RESPONSE_YES)
1263 HobButton.style_button(button)
1265 lbl = "<b>Stop build?</b>\n\nAre you sure you want to stop this"
1266 lbl = lbl + " build?\n\n'Force Stop' will stop the build as quickly as"
1267 lbl = lbl + " possible but may well leave your build directory in an"
1268 lbl = lbl + " unusable state that requires manual steps to fix.\n\n"
1269 lbl = lbl + "'Stop' will stop the build as soon as all in"
1270 lbl = lbl + " progress build tasks are finished. However if a"
1271 lbl = lbl + " lengthy compilation phase is in progress this may take"
1272 lbl = lbl + " some time."
1273 dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
1274 button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
1275 HobAltButton.style_button(button)
1276 button = dialog.add_button("Stop", gtk.RESPONSE_OK)
1277 HobAltButton.style_button(button)
1278 button = dialog.add_button("Force Stop", gtk.RESPONSE_YES)
1279 HobButton.style_button(button)
1280 response = dialog.run()
1282 if response != gtk.RESPONSE_CANCEL:
1283 self.stopping = True
1284 if response == gtk.RESPONSE_OK:
1285 self.cancel_build_sync()
1286 elif response == gtk.RESPONSE_YES:
1287 self.cancel_build_sync(True)