343 lines
14 KiB
Python
343 lines
14 KiB
Python
#
|
|
# BitBake Graphical GTK User Interface
|
|
#
|
|
# Copyright (C) 2011 Intel Corporation
|
|
#
|
|
# Authored by Joshua Lock <josh@linux.intel.com>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License version 2 as
|
|
# published by the Free Software Foundation.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
import gobject
|
|
import logging
|
|
import tempfile
|
|
import datetime
|
|
|
|
progress_total = 0
|
|
|
|
class HobHandler(gobject.GObject):
|
|
|
|
"""
|
|
This object does BitBake event handling for the hob gui.
|
|
"""
|
|
__gsignals__ = {
|
|
"machines-updated" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
(gobject.TYPE_PYOBJECT,)),
|
|
"sdk-machines-updated": (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
(gobject.TYPE_PYOBJECT,)),
|
|
"distros-updated" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
(gobject.TYPE_PYOBJECT,)),
|
|
"package-formats-found" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
(gobject.TYPE_PYOBJECT,)),
|
|
"config-found" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
(gobject.TYPE_STRING,)),
|
|
"generating-data" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
()),
|
|
"data-generated" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
()),
|
|
"fatal-error" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
(gobject.TYPE_STRING,
|
|
gobject.TYPE_STRING,)),
|
|
"command-failed" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
(gobject.TYPE_STRING,)),
|
|
"reload-triggered" : (gobject.SIGNAL_RUN_LAST,
|
|
gobject.TYPE_NONE,
|
|
(gobject.TYPE_STRING,
|
|
gobject.TYPE_STRING,)),
|
|
}
|
|
|
|
(CFG_PATH_LOCAL, CFG_PATH_PRE, CFG_PATH_POST, CFG_PATH_LAYERS, CFG_FILES_DISTRO, CFG_FILES_MACH, CFG_FILES_SDK, FILES_MATCH_CLASS, GENERATE_TGTS, REPARSE_FILES, BUILD_IMAGE) = range(11)
|
|
|
|
def __init__(self, taskmodel, server):
|
|
gobject.GObject.__init__(self)
|
|
|
|
self.current_command = None
|
|
self.building = None
|
|
self.build_toolchain = False
|
|
self.build_toolchain_headers = False
|
|
self.generating = False
|
|
self.build_queue = []
|
|
self.current_phase = None
|
|
self.bbpath_ok = False
|
|
self.bbfiles_ok = False
|
|
self.image_dir = os.path.join(tempfile.gettempdir(), 'hob-images')
|
|
|
|
self.model = taskmodel
|
|
self.server = server
|
|
|
|
self.image_output_types = self.server.runCommand(["getVariable", "IMAGE_FSTYPES"]).split(" ")
|
|
|
|
def run_next_command(self):
|
|
if self.current_command and not self.generating:
|
|
self.emit("generating-data")
|
|
self.generating = True
|
|
|
|
if self.current_command == self.CFG_PATH_LOCAL:
|
|
self.current_command = self.CFG_PATH_PRE
|
|
self.server.runCommand(["findConfigFilePath", "hob-pre.conf"])
|
|
elif self.current_command == self.CFG_PATH_PRE:
|
|
self.current_command = self.CFG_PATH_POST
|
|
self.server.runCommand(["findConfigFilePath", "hob-post.conf"])
|
|
elif self.current_command == self.CFG_PATH_POST:
|
|
self.current_command = self.CFG_PATH_LAYERS
|
|
self.server.runCommand(["findConfigFilePath", "bblayers.conf"])
|
|
elif self.current_command == self.CFG_PATH_LAYERS:
|
|
self.current_command = self.CFG_FILES_DISTRO
|
|
self.server.runCommand(["findConfigFiles", "DISTRO"])
|
|
elif self.current_command == self.CFG_FILES_DISTRO:
|
|
self.current_command = self.CFG_FILES_MACH
|
|
self.server.runCommand(["findConfigFiles", "MACHINE"])
|
|
elif self.current_command == self.CFG_FILES_MACH:
|
|
self.current_command = self.CFG_FILES_SDK
|
|
self.server.runCommand(["findConfigFiles", "MACHINE-SDK"])
|
|
elif self.current_command == self.CFG_FILES_SDK:
|
|
self.current_command = self.FILES_MATCH_CLASS
|
|
self.server.runCommand(["findFilesMatchingInDir", "rootfs_", "classes"])
|
|
elif self.current_command == self.FILES_MATCH_CLASS:
|
|
self.current_command = self.GENERATE_TGTS
|
|
self.server.runCommand(["generateTargetsTree", "classes/image.bbclass"])
|
|
elif self.current_command == self.GENERATE_TGTS:
|
|
if self.generating:
|
|
self.emit("data-generated")
|
|
self.generating = False
|
|
self.current_command = None
|
|
elif self.current_command == self.REPARSE_FILES:
|
|
if self.build_queue:
|
|
self.current_command = self.BUILD_IMAGE
|
|
else:
|
|
self.current_command = self.CFG_PATH_LAYERS
|
|
self.server.runCommand(["resetCooker"])
|
|
self.server.runCommand(["reparseFiles"])
|
|
elif self.current_command == self.BUILD_IMAGE:
|
|
self.building = "image"
|
|
if self.generating:
|
|
self.emit("data-generated")
|
|
self.generating = False
|
|
self.server.runCommand(["buildTargets", self.build_queue, "build"])
|
|
self.build_queue = []
|
|
self.current_command = None
|
|
|
|
def handle_event(self, event, running_build, pbar):
|
|
if not event:
|
|
return
|
|
|
|
# If we're running a build, use the RunningBuild event handler
|
|
if self.building:
|
|
self.current_phase = "building"
|
|
running_build.handle_event(event)
|
|
elif isinstance(event, bb.event.TargetsTreeGenerated):
|
|
self.current_phase = "data generation"
|
|
if event._model:
|
|
self.model.populate(event._model)
|
|
elif isinstance(event, bb.event.ConfigFilesFound):
|
|
self.current_phase = "configuration lookup"
|
|
var = event._variable
|
|
if var == "distro":
|
|
distros = event._values
|
|
distros.sort()
|
|
self.emit("distros-updated", distros)
|
|
elif var == "machine":
|
|
machines = event._values
|
|
machines.sort()
|
|
self.emit("machines-updated", machines)
|
|
elif var == "machine-sdk":
|
|
sdk_machines = event._values
|
|
sdk_machines.sort()
|
|
self.emit("sdk-machines-updated", sdk_machines)
|
|
elif isinstance(event, bb.event.ConfigFilePathFound):
|
|
self.current_phase = "configuration lookup"
|
|
path = event._path
|
|
self.emit("config-found", path)
|
|
elif isinstance(event, bb.event.FilesMatchingFound):
|
|
self.current_phase = "configuration lookup"
|
|
# FIXME: hard coding, should at least be a variable shared between
|
|
# here and the caller
|
|
if event._pattern == "rootfs_":
|
|
formats = []
|
|
for match in event._matches:
|
|
classname, sep, cls = match.rpartition(".")
|
|
fs, sep, format = classname.rpartition("_")
|
|
formats.append(format)
|
|
formats.sort()
|
|
self.emit("package-formats-found", formats)
|
|
elif isinstance(event, bb.command.CommandCompleted):
|
|
self.current_phase = None
|
|
self.run_next_command()
|
|
elif isinstance(event, bb.command.CommandFailed):
|
|
self.emit("command-failed", event.error)
|
|
elif isinstance(event, bb.event.CacheLoadStarted):
|
|
self.current_phase = "cache loading"
|
|
bb.ui.crumbs.hobeventhandler.progress_total = event.total
|
|
pbar.set_text("Loading cache: %s/%s" % (0, bb.ui.crumbs.hobeventhandler.progress_total))
|
|
elif isinstance(event, bb.event.CacheLoadProgress):
|
|
self.current_phase = "cache loading"
|
|
pbar.set_text("Loading cache: %s/%s" % (event.current, bb.ui.crumbs.hobeventhandler.progress_total))
|
|
elif isinstance(event, bb.event.CacheLoadCompleted):
|
|
self.current_phase = None
|
|
pbar.set_text("Loading...")
|
|
elif isinstance(event, bb.event.ParseStarted):
|
|
self.current_phase = "recipe parsing"
|
|
if event.total == 0:
|
|
return
|
|
bb.ui.crumbs.hobeventhandler.progress_total = event.total
|
|
pbar.set_text("Processing recipes: %s/%s" % (0, bb.ui.crumbs.hobeventhandler.progress_total))
|
|
elif isinstance(event, bb.event.ParseProgress):
|
|
self.current_phase = "recipe parsing"
|
|
pbar.set_text("Processing recipes: %s/%s" % (event.current, bb.ui.crumbs.hobeventhandler.progress_total))
|
|
elif isinstance(event, bb.event.ParseCompleted):
|
|
self.current_phase = None
|
|
pbar.set_fraction(1.0)
|
|
pbar.set_text("Loading...")
|
|
elif isinstance(event, logging.LogRecord):
|
|
format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
|
|
if event.levelno >= format.CRITICAL:
|
|
self.emit("fatal-error", event.getMessage(), self.current_phase)
|
|
return
|
|
|
|
def event_handle_idle_func (self, eventHandler, running_build, pbar):
|
|
# Consume as many messages as we can in the time available to us
|
|
event = eventHandler.getEvent()
|
|
while event:
|
|
self.handle_event(event, running_build, pbar)
|
|
event = eventHandler.getEvent()
|
|
return True
|
|
|
|
def set_machine(self, machine):
|
|
self.server.runCommand(["setVariable", "MACHINE", machine])
|
|
|
|
def set_sdk_machine(self, sdk_machine):
|
|
self.server.runCommand(["setVariable", "SDKMACHINE", sdk_machine])
|
|
|
|
def set_distro(self, distro):
|
|
self.server.runCommand(["setVariable", "DISTRO", distro])
|
|
|
|
def set_package_format(self, format):
|
|
self.server.runCommand(["setVariable", "PACKAGE_CLASSES", "package_%s" % format])
|
|
|
|
def reload_data(self, config=None):
|
|
img = self.model.selected_image
|
|
selected_packages, _ = self.model.get_selected_packages()
|
|
self.emit("reload-triggered", img, " ".join(selected_packages))
|
|
self.current_command = self.REPARSE_FILES
|
|
self.run_next_command()
|
|
|
|
def set_bbthreads(self, threads):
|
|
self.server.runCommand(["setVariable", "BB_NUMBER_THREADS", threads])
|
|
|
|
def set_pmake(self, threads):
|
|
pmake = "-j %s" % threads
|
|
self.server.runCommand(["setVariable", "BB_NUMBER_THREADS", pmake])
|
|
|
|
def build_image(self, image, configurator):
|
|
targets = []
|
|
nbbp = None
|
|
nbbf = None
|
|
targets.append(image)
|
|
if self.build_toolchain and self.build_toolchain_headers:
|
|
targets.append("meta-toolchain-sdk")
|
|
elif self.build_toolchain:
|
|
targets.append("meta-toolchain")
|
|
self.build_queue = targets
|
|
|
|
if not self.bbpath_ok:
|
|
bbpath = self.server.runCommand(["getVariable", "BBPATH"])
|
|
if self.image_dir in bbpath.split(":"):
|
|
self.bbpath_ok = True
|
|
else:
|
|
nbbp = self.image_dir
|
|
|
|
if not self.bbfiles_ok:
|
|
import re
|
|
pattern = "%s/\*.bb" % self.image_dir
|
|
bbfiles = self.server.runCommand(["getVariable", "BBFILES"]).split(" ")
|
|
for files in bbfiles:
|
|
if re.match(pattern, files):
|
|
self.bbfiles_ok = True
|
|
|
|
if not self.bbfiles_ok:
|
|
nbbf = "%s/*.bb" % self.image_dir
|
|
|
|
if nbbp or nbbf:
|
|
configurator.insertTempBBPath(nbbp, nbbf)
|
|
self.bbpath_ok = True
|
|
self.bbfiles_ok = True
|
|
|
|
self.current_command = self.REPARSE_FILES
|
|
self.run_next_command()
|
|
|
|
def build_packages(self, pkgs):
|
|
self.building = "packages"
|
|
self.server.runCommand(["buildTargets", pkgs, "build"])
|
|
|
|
def cancel_build(self, force=False):
|
|
if force:
|
|
# Force the cooker to stop as quickly as possible
|
|
self.server.runCommand(["stateStop"])
|
|
else:
|
|
# Wait for tasks to complete before shutting down, this helps
|
|
# leave the workdir in a usable state
|
|
self.server.runCommand(["stateShutdown"])
|
|
|
|
def set_incompatible_license(self, incompatible):
|
|
self.server.runCommand(["setVariable", "INCOMPATIBLE_LICENSE", incompatible])
|
|
|
|
def toggle_toolchain(self, enabled):
|
|
if self.build_toolchain != enabled:
|
|
self.build_toolchain = enabled
|
|
|
|
def toggle_toolchain_headers(self, enabled):
|
|
if self.build_toolchain_headers != enabled:
|
|
self.build_toolchain_headers = enabled
|
|
|
|
def set_fstypes(self, fstypes):
|
|
self.server.runCommand(["setVariable", "IMAGE_FSTYPES", fstypes])
|
|
|
|
def add_image_output_type(self, output_type):
|
|
if output_type not in self.image_output_types:
|
|
self.image_output_types.append(output_type)
|
|
fstypes = " ".join(self.image_output_types).lstrip(" ")
|
|
self.set_fstypes(fstypes)
|
|
return self.image_output_types
|
|
|
|
def remove_image_output_type(self, output_type):
|
|
if output_type in self.image_output_types:
|
|
ind = self.image_output_types.index(output_type)
|
|
self.image_output_types.pop(ind)
|
|
fstypes = " ".join(self.image_output_types).lstrip(" ")
|
|
self.set_fstypes(fstypes)
|
|
return self.image_output_types
|
|
|
|
def get_image_deploy_dir(self):
|
|
return self.server.runCommand(["getVariable", "DEPLOY_DIR_IMAGE"])
|
|
|
|
def make_temp_dir(self):
|
|
bb.utils.mkdirhier(self.image_dir)
|
|
|
|
def remove_temp_dir(self):
|
|
bb.utils.remove(self.image_dir, True)
|
|
|
|
def get_temp_recipe_path(self, name):
|
|
timestamp = datetime.date.today().isoformat()
|
|
image_file = "hob-%s-variant-%s.bb" % (name, timestamp)
|
|
recipepath = os.path.join(self.image_dir, image_file)
|
|
return recipepath
|