Hob - BitBake GUI
cover Hob - BitBake GUI
All,
This series are the current state of the image creator project I have been working on for the past few months.
It's by no means perfect but I'd love to get some review and preferably move development into master, rather than a less public branch.
The first 7 patches are changes to BitBake to be used by the GUI whilst the final patch is a squashed patch of my image creator development tree[1].
Of course, before sending this pull request I ran some final tests and encountered a significant problem with the implementation approach. Images are built by first using the buildTargets command to build all of the target dependencies then creating a recipe file and using the buildFile command to handle the build.
The problem is that if you have a clean build directory, perfectly reasonable with such a tool, the buildFile command does not handle the native dependencies added by the image class.
The alternative I have is to save the image files somewhere within BBPATH, and perhaps suggest the user create a "workspace" layer, and build them with buildTargets. I didn't do this because I'd rather not pollute the users file system without good cause. It may be the only tenable approach though.
Any suggestions here?
Regards,
Joshua
1. https://github.com/incandescant/bitbake/commits/hob-full
The following changes since commit 5d41720d1550c04aac76275614ca15110c1c7f52:
Update version to 1.13.2 (2011-06-28 15:27:14 +0100)
are available in the git repository at:
git://github.com/incandescant/bitbake hob
https://github.com/incandescant/bitbake/tree/hob
Joshua Lock (8):
command|cooker: allow generating targets tree for specified pkgs
cooker: add generic method to locate configuration files command|cooker|event: add findConfigFilePath command cooker|command|event: add new command findFilesMatchingInDir
bitbake: add -R option for loading configuration files after
bitbake.conf
cooker: switch to new universe target rather than world
cooker: remove code duplication in non trivial functions
hob: re-designed interaction and implementation
Lianhao Lu (1):
ui/hob: Fixed the "build again" hang.
Liping Ke (1):
bitbake: Make bitbake server type configurable.
bin/bitbake | 30 +- lib/bb/command.py | 34 ++- lib/bb/cooker.py | 113 +++-- lib/bb/event.py | 18 + lib/bb/ui/crumbs/configurator.py | 278 +++++++++++ lib/bb/ui/crumbs/hig.py | 61 +++ lib/bb/ui/crumbs/hobeventhandler.py | 218 +++++++-- lib/bb/ui/crumbs/hobprefs.py | 293 +++++++++++ lib/bb/ui/crumbs/layereditor.py | 136 +++++ lib/bb/ui/crumbs/runningbuild.py | 12 +- lib/bb/ui/crumbs/tasklistmodel.py | 306 +++++++++--- lib/bb/ui/hob.py | 925 +++++++++++++++++++++++------------ 12 files changed, 1939 insertions(+), 485 deletions(-) create mode 100644 lib/bb/ui/crumbs/configurator.py create mode 100644 lib/bb/ui/crumbs/hig.py create mode 100644 lib/bb/ui/crumbs/hobprefs.py create mode 100644 lib/bb/ui/crumbs/layereditor.py
Signature · 1 line
1.7.5.4
PATCH 1/10 ui/hob: Fixed the "build again" hang.
From: Lianhao Lu <[email protected]>
Using gobject.threads_init() instead of gtk.gdk.threads_init(). These two modes are conflict to each other. Using gobject.threads_init() allows only the main thread to touch GUI(gtk) part.
--- lib/bb/ui/hob.py | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-)
lib/bb/ui/hob.py +1 -2
@@ -76,7 +76,7 @@ class MainWindow (gtk.Window): label.show() response = dialog.run() dialog.destroy() - if not response == gtk.RESPONSE_YES: + if response == gtk.RESPONSE_YES: self.model.reset() # NOTE: really? self.nb.set_current_page(0) return
@@ -555,7 +555,6 @@ class MainWindow (gtk.Window): def main (server, eventHandler): gobject.threads_init() - gtk.gdk.threads_init() taskmodel = TaskListModel() handler = HobHandler(taskmodel, server)
Signature · 1 line
1.7.5.4
PATCH 2/10 bitbake: Make bitbake server type configurable.
In reply to Joshua LockFrom: Liping Ke <[email protected]>
Add -t options in bitbake for configuring server type.
--- bin/bitbake | 25 ++++++++++++++++++------- 1 files changed, 18 insertions(+), 7 deletions(-)
bin/bitbake +18 -7
@@ -39,15 +39,11 @@ import bb.msg from bb import cooker from bb import ui from bb import server -#from bb.server import none -from bb.server import process -#from bb.server import xmlrpc __version__ = "1.13.2" logger = logging.getLogger("BitBake") - class BBConfiguration(object): """ Manages build options and configurations for one run
@@ -160,6 +156,9 @@ Default BBFILES are the .bb files in the current directory.""") parser.add_option("-u", "--ui", help = "userinterface to use", action = "store", dest = "ui") + parser.add_option("-t", "--servertype", help = "Choose which server to use, none, process or xmlrpc", + action = "store", dest = "servertype") + parser.add_option("", "--revisions-changed", help = "Set the exit code depending on whether upstream floating revisions have changed or not", action = "store_true", dest = "revisions_changed", default = False)
@@ -170,6 +169,20 @@ Default BBFILES are the .bb files in the current directory.""") ui_main = get_ui(configuration) + # Server type could be xmlrpc or none currently, if nothing is specified, + # default server would be none + if configuration.servertype: + server_type = configuration.servertype + else: + server_type = 'process' + + try: + module = __import__("bb.server", fromlist = [server_type]) + server = getattr(module, server_type) + except AttributeError: + sys.exit("FATAL: Invalid server type '%s' specified.\n" + "Valid interfaces: xmlrpc, process, none [default]." % servertype) + # Save a logfile for cooker into the current working directory. When the # server is daemonized this logfile will be truncated. cooker_logfile = os.path.join(os.getcwd(), "cooker.log")
@@ -186,9 +199,7 @@ Default BBFILES are the .bb files in the current directory.""") # of the UIs (e.g. for DISPLAY, etc.) bb.utils.clean_environment() - #server = bb.server.none.BitBakeServer() - server = bb.server.process.BitBakeServer() - #server = bb.server.xmlrpc.BitBakeServer() + server = server.BitBakeServer() server.initServer() idle = server.getServerIdleCB()
Signature · 1 line
1.7.5.4
PATCH 3/10 command|cooker: allow generating targets tree for specified pkgs
In reply to Joshua LockModify the generateTargetsTree command to allow a list of packages to be supplied by the caller, in this case we will only generate a target tree for user requested targets rather than building a tree for the world list.
--- lib/bb/command.py | 12 ++++++++++-- lib/bb/cooker.py | 6 ++++-- 2 files changed, 14 insertions(+), 4 deletions(-)
lib/bb/command.py +10 -2
@@ -224,11 +224,19 @@ class CommandsAsync: def generateTargetsTree(self, command, params): """ - Generate a tree of all buildable targets. + Generate a tree of buildable targets. + If klass is provided ensure all recipes that inherit the class are + included in the package list. + If pkg_list provided use that list (plus any extras brought in by + klass) rather than generating a tree for all packages. """ klass = params[0] + if len(params) > 1: + pkg_list = params[1] + else: + pkg_list = [] - command.cooker.generateTargetsTree(klass) + command.cooker.generateTargetsTree(klass, pkg_list) command.finishAsyncCommand() generateTargetsTree.needcache = True
lib/bb/cooker.py +4 -2
@@ -591,12 +591,14 @@ class BBCooker: return target_tree - def generateTargetsTree(self, klass): + def generateTargetsTree(self, klass=None, pkgs=[]): """ Generate a dependency tree of buildable targets Generate an event with the result """ - pkgs = ['world'] + # if the caller hasn't specified a pkgs list default to world + if not len(pkgs): + pkgs = ['world'] # if inherited_class passed ensure all recipes which inherit the # specified class are included in pkgs if klass:
Signature · 1 line
1.7.5.4
PATCH 4/10 cooker: add generic method to locate configuration files
In reply to Joshua LockConvert _findLayerConf(self) to _findConfigFile(self, configfile) so that the core functionality of the method can be used elsewhere.
--- lib/bb/cooker.py | 11 +++++++---- 1 files changed, 7 insertions(+), 4 deletions(-)
lib/bb/cooker.py +7 -4
@@ -643,15 +643,18 @@ class BBCooker: else: shell.start( self ) - def _findLayerConf(self): + def _findConfigFile(self, configfile): path = os.getcwd() while path != "/": - bblayers = os.path.join(path, "conf", "bblayers.conf") - if os.path.exists(bblayers): - return bblayers + confpath = os.path.join(path, "conf", configfile) + if os.path.exists(confpath): + return confpath path, _ = os.path.split(path) + def _findLayerConf(self): + return self._findConfigFile("bblayers.conf") + def parseConfigurationFiles(self, files): data = self.configuration.data bb.parse.init_parser(data)
Signature · 1 line
1.7.5.4
PATCH 5/10 command|cooker|event: add findConfigFilePath command
In reply to Joshua LockThis takes the name of a .conf file and returns the full path to it
--- lib/bb/command.py | 10 ++++++++++ lib/bb/cooker.py | 4 ++++ lib/bb/event.py | 8 ++++++++ 3 files changed, 22 insertions(+), 0 deletions(-)
lib/bb/command.py +10 -0
@@ -251,6 +251,16 @@ class CommandsAsync: command.finishAsyncCommand() findConfigFiles.needcache = True + def findConfigFilePath(self, command, params): + """ + Find the path of the requested configuration file + """ + configfile = params[0] + + command.cooker.findConfigFilePath(configfile) + command.finishAsyncCommand() + findConfigFilePath.needcache = False + def showVersions(self, command, params): """ Show the currently selected versions
lib/bb/cooker.py +4 -0
@@ -513,6 +513,10 @@ class BBCooker: if regex in unmatched: collectlog.warn("No bb files matched BBFILE_PATTERN_%s '%s'" % (collection, pattern)) + def findConfigFilePath(self, configfile): + path = self._findConfigFile(configfile) + bb.event.fire(bb.event.ConfigFilePathFound(path), self.configuration.data) + def findConfigFiles(self, varname): """ Find config files which are appropriate values for varname.
lib/bb/event.py +8 -0
@@ -399,6 +399,14 @@ class ConfigFilesFound(Event): self._variable = variable self._values = values +class ConfigFilePathFound(Event): + """ + Event when a path for a config file has been found + """ + def __init__(self, path): + Event.__init__(self) + self._path = path + class MsgBase(Event): """Base class for messages"""
Signature · 1 line
1.7.5.4
PATCH 6/10 cooker|command|event: add new command findFilesMatchingInDir
In reply to Joshua LockThis command can be used to search each BBPATH for files in the passed directory which have a filename matching the supplied pattern.
This is implemented for use from the GUI (to determine the available PACKAGE_CLASSES) but has been written so as to be generically useful and reusable.
--- lib/bb/command.py | 12 ++++++++++++ lib/bb/cooker.py | 23 +++++++++++++++++++++++ lib/bb/event.py | 10 ++++++++++ 3 files changed, 45 insertions(+), 0 deletions(-)
lib/bb/command.py +12 -0
@@ -251,6 +251,18 @@ class CommandsAsync: command.finishAsyncCommand() findConfigFiles.needcache = True + def findFilesMatchingInDir(self, command, params): + """ + Find implementation files matching the specified pattern + in the requested subdirectory of a BBPATH + """ + pattern = params[0] + directory = params[1] + + command.cooker.findFilesMatchingInDir(pattern, directory) + command.finishAsyncCommand() + findFilesMatchingInDir.needcache = True + def findConfigFilePath(self, command, params): """ Find the path of the requested configuration file
lib/bb/cooker.py +23 -0
@@ -517,6 +517,29 @@ class BBCooker: path = self._findConfigFile(configfile) bb.event.fire(bb.event.ConfigFilePathFound(path), self.configuration.data) + def findFilesMatchingInDir(self, filepattern, directory): + """ + Searches for files matching the regex 'pattern' which are children of + 'directory' in each BBPATH. i.e. to find all rootfs package classes available + to BitBake one could call findFilesMatchingInDir(self, 'rootfs_', 'classes') + or to find all machine configuration files on could call + findFilesMatchingInDir(self, 'conf/machines', 'conf') + """ + import re + + matches = [] + p = re.compile(re.escape(filepattern)) + bbpaths = bb.data.getVar('BBPATH', self.configuration.data, True).split(':') + for path in bbpaths: + dirpath = os.path.join(path, directory) + if os.path.exists(dirpath): + for root, dirs, files in os.walk(dirpath): + for f in files: + if p.search(f): + matches.append(f) + + bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.configuration.data) + def findConfigFiles(self, varname): """ Find config files which are appropriate values for varname.
lib/bb/event.py +10 -0
@@ -390,6 +390,16 @@ class TargetsTreeGenerated(Event): Event.__init__(self) self._model = model +class FilesMatchingFound(Event): + """ + Event when a list of files matching the supplied pattern has + been generated + """ + def __init__(self, pattern, matches): + Event.__init__(self) + self._pattern = pattern + self._matches = matches + class ConfigFilesFound(Event): """ Event when a list of appropriate config files has been generated
Signature · 1 line
1.7.5.4
PATCH 7/10 bitbake: add -R option for loading configuration files after bitbake.conf
In reply to Joshua LockUseful if you want to load a configuration file that sets values which may also be set in bitbake.conf or one of the files it includes.
--- bin/bitbake | 5 ++++- lib/bb/cooker.py | 13 ++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-)
bin/bitbake +4 -1
@@ -118,7 +118,10 @@ Default BBFILES are the .bb files in the current directory.""") action = "store", dest = "cmd") parser.add_option("-r", "--read", help = "read the specified file before bitbake.conf", - action = "append", dest = "file", default = []) + action = "append", dest = "prefile", default = []) + + parser.add_option("-R", "--postread", help = "read the specified file after bitbake.conf", + action = "append", dest = "postfile", default = []) parser.add_option("-v", "--verbose", help = "output more chit-chat to the terminal", action = "store_true", dest = "verbose", default = False)
lib/bb/cooker.py +10 -3
@@ -126,7 +126,8 @@ class BBCooker: bb.data.inheritFromOS(self.configuration.data) try: - self.parseConfigurationFiles(self.configuration.file) + self.parseConfigurationFiles(self.configuration.prefile, + self.configuration.postfile) except SyntaxError: sys.exit(1) except Exception:
@@ -682,10 +683,12 @@ class BBCooker: def _findLayerConf(self): return self._findConfigFile("bblayers.conf") - def parseConfigurationFiles(self, files): + def parseConfigurationFiles(self, prefiles, postfiles): data = self.configuration.data bb.parse.init_parser(data) - for f in files: + + # Parse files for loading *before* bitbake.conf and any includes + for f in prefiles: data = _parse(f, data) layerconf = self._findLayerConf()
@@ -709,6 +712,10 @@ class BBCooker: data = _parse(os.path.join("conf", "bitbake.conf"), data) + # Parse files for loading *after* bitbake.conf and any includes + for p in postfiles: + data = _parse(p, data) + # Handle any INHERITs and inherit the base class bbclasses = ["base"] + (data.getVar('INHERIT', True) or "").split() for bbclass in bbclasses:
Signature · 1 line
1.7.5.4
PATCH 8/10 cooker: switch to new universe target rather than world
In reply to Joshua LockWhen the caller doesn't specify a pkgs list we want to generate the tree of all available packages. To do so use the new universe target list.
--- lib/bb/cooker.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
lib/bb/cooker.py +2 -2
@@ -624,9 +624,9 @@ class BBCooker: Generate a dependency tree of buildable targets Generate an event with the result """ - # if the caller hasn't specified a pkgs list default to world + # if the caller hasn't specified a pkgs list default to universe if not len(pkgs): - pkgs = ['world'] + pkgs = ['universe'] # if inherited_class passed ensure all recipes which inherit the # specified class are included in pkgs if klass:
Signature · 1 line
1.7.5.4
PATCH 9/10 cooker: remove code duplication in non trivial functions
In reply to Joshua LockThe generateTargetsTreeData() and generateDepTreeData() functions perform are essentially the same function only creating slightly different data structures. Instead of duplicating non-trivial code drop generateTargetsTreeData and instead have an optional boolean argument which defaults to False for generateDepTreeData() which has it include the extra fields required for the targets tree.
--- lib/bb/cooker.py | 56 +++++++++++------------------------------------------ 1 files changed, 12 insertions(+), 44 deletions(-)
lib/bb/cooker.py +12 -44
@@ -325,9 +325,11 @@ class BBCooker: return taskdata, rq - def generateDepTreeData(self, pkgs_to_build, task): + def generateDepTreeData(self, pkgs_to_build, task, more_meta=False): """ Create a dependency tree of pkgs_to_build, returning the data. + When more_meta is set to True include summary, license and group + information in the returned tree. """ taskdata, rq = self.prepareTreeData(pkgs_to_build, task)
@@ -347,10 +349,18 @@ class BBCooker: fn = taskdata.fn_index[fnid] pn = self.status.pkg_fn[fn] version = "%s:%s-%s" % self.status.pkg_pepvpr[fn] + if more_meta: + summary = self.status.summary[fn] + lic = self.status.license[fn] + section = self.status.section[fn] if pn not in depend_tree["pn"]: depend_tree["pn"][pn] = {} depend_tree["pn"][pn]["filename"] = fn depend_tree["pn"][pn]["version"] = version + if more_meta: + depend_tree["pn"][pn]["summary"] = summary + depend_tree["pn"][pn]["license"] = lic + depend_tree["pn"][pn]["section"] = section for dep in rq.rqdata.runq_depends[task]: depfn = taskdata.fn_index[rq.rqdata.runq_fnid[dep]] deppn = self.status.pkg_fn[depfn]
@@ -577,48 +587,6 @@ class BBCooker: return pkg_list - def generateTargetsTreeData(self, pkgs_to_build, task): - """ - Create a tree of pkgs_to_build metadata, returning the data. - """ - taskdata, rq = self.prepareTreeData(pkgs_to_build, task) - - seen_fnids = [] - target_tree = {} - target_tree["depends"] = {} - target_tree["pn"] = {} - target_tree["rdepends-pn"] = {} - - for task in xrange(len(rq.rqdata.runq_fnid)): - taskname = rq.rqdata.runq_task[task] - fnid = rq.rqdata.runq_fnid[task] - fn = taskdata.fn_index[fnid] - pn = self.status.pkg_fn[fn] - version = "%s:%s-%s" % self.status.pkg_pepvpr[fn] - summary = self.status.summary[fn] - license = self.status.license[fn] - section = self.status.section[fn] - if pn not in target_tree["pn"]: - target_tree["pn"][pn] = {} - target_tree["pn"][pn]["filename"] = fn - target_tree["pn"][pn]["version"] = version - target_tree["pn"][pn]["summary"] = summary - target_tree["pn"][pn]["license"] = license - target_tree["pn"][pn]["section"] = section - if fnid not in seen_fnids: - seen_fnids.append(fnid) - packages = [] - - target_tree["depends"][pn] = [] - for dep in taskdata.depids[fnid]: - target_tree["depends"][pn].append(taskdata.build_names_index[dep]) - - target_tree["rdepends-pn"][pn] = [] - for rdep in taskdata.rdepids[fnid]: - target_tree["rdepends-pn"][pn].append(taskdata.run_names_index[rdep]) - - return target_tree - def generateTargetsTree(self, klass=None, pkgs=[]): """ Generate a dependency tree of buildable targets
@@ -634,7 +602,7 @@ class BBCooker: pkgs = pkgs + extra_pkgs # generate a dependency tree for all our packages - tree = self.generateTargetsTreeData(pkgs, 'build') + tree = self.generateDepTreeData(pkgs, 'build', more_meta=True) bb.event.fire(bb.event.TargetsTreeGenerated(tree), self.configuration.data) def buildWorldTargetList(self):
Signature · 1 line
1.7.5.4
PATCH 10/10 hob: re-designed interaction and implementation
In reply to Joshua LockHighlights include:
* Atempted GNOME HIG compliance
* Simplified UI and interaction model
* Sorting and type to find in tree views
* Preferences dialog to modify local settings
* Dialog to add and remove layers
* Search in packages list
* Save/Load image recipes
The build model has been changed, hob will attempt to build all dependent packages of an image and then use the buildFile server method to build the created image.
--- lib/bb/ui/crumbs/configurator.py | 278 +++++++++++ lib/bb/ui/crumbs/hig.py | 61 +++ lib/bb/ui/crumbs/hobeventhandler.py | 218 +++++++-- lib/bb/ui/crumbs/hobprefs.py | 293 +++++++++++ lib/bb/ui/crumbs/layereditor.py | 136 +++++ lib/bb/ui/crumbs/runningbuild.py | 12 +- lib/bb/ui/crumbs/tasklistmodel.py | 306 +++++++++--- lib/bb/ui/hob.py | 924 +++++++++++++++++++++++----------- 8 files changed, 1807 insertions(+), 421 deletions(-) create mode 100644 lib/bb/ui/crumbs/configurator.py create mode 100644 lib/bb/ui/crumbs/hig.py create mode 100644 lib/bb/ui/crumbs/hobprefs.py create mode 100644 lib/bb/ui/crumbs/layereditor.py
lib/bb/ui/crumbs/configurator.py new file +278 -0
@@ -0,0 +1,278 @@ +# +# BitBake Graphical GTK User Interface +# +# Copyright (C) 2011 Intel Corporation +# +# Authored by Joshua Lock <[email protected]> +# +# 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 copy +import re, os +from bb import data + +class Configurator(gobject.GObject): + + """ + A GObject to handle writing modified configuration values back + to conf files. + """ + __gsignals__ = { + "layers-loaded" : (gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + ()), + "layers-changed" : (gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + ()) + } + + def __init__(self): + gobject.GObject.__init__(self) + self.local = None + self.bblayers = None + self.enabled_layers = {} + self.loaded_layers = {} + self.config = {} + self.orig_config = {} + + # NOTE: cribbed from the cooker... + def _parse(self, f, data, include=False): + try: + return bb.parse.handle(f, data, include) + except (IOError, bb.parse.ParseError) as exc: + parselog.critical("Unable to parse %s: %s" % (f, exc)) + sys.exit(1) + + def _loadLocalConf(self, path): + def getString(var): + return bb.data.getVar(var, data, True) or "" + + self.local = path + + if self.orig_config: + del self.orig_config + self.orig_config = {} + + data = bb.data.init() + data = self._parse(self.local, data) + + # We only need to care about certain variables + mach = getString('MACHINE') + if mach and mach != self.config.get('MACHINE', ''): + self.config['MACHINE'] = mach + sdkmach = getString('SDKMACHINE') + if sdkmach and sdkmach != self.config.get('SDKMACHINE', ''): + self.config['SDKMACHINE'] = sdkmach + distro = getString('DISTRO') + if distro and distro != self.config.get('DISTRO', ''): + self.config['DISTRO'] = distro + bbnum = getString('BB_NUMBER_THREADS') + if bbnum and bbnum != self.config.get('BB_NUMBER_THREADS', ''): + self.config['BB_NUMBER_THREADS'] = bbnum + pmake = getString('PARALLEL_MAKE') + if pmake and pmake != self.config.get('PARALLEL_MAKE', ''): + self.config['PARALLEL_MAKE'] = pmake + incompat = getString('INCOMPATIBLE_LICENSE') + if incompat and incompat != self.config.get('INCOMPATIBLE_LICENSE', ''): + self.config['INCOMPATIBLE_LICENSE'] = incompat + pclass = getString('PACKAGE_CLASSES') + if pclass and pclass != self.config.get('PACKAGE_CLASSES', ''): + self.config['PACKAGE_CLASSES'] = pclass + + self.orig_config = copy.deepcopy(self.config) + + def setLocalConfVar(self, var, val): + if var in self.config: + self.config[var] = val + + def _loadLayerConf(self, path): + self.bblayers = path + self.enabled_layers = {} + self.loaded_layers = {} + data = bb.data.init() + data = self._parse(self.bblayers, data) + layers = (bb.data.getVar('BBLAYERS', data, True) or "").split() + for layer in layers: + # TODO: we may be better off calling the layer by its + # BBFILE_COLLECTIONS value? + name = self._getLayerName(layer) + self.loaded_layers[name] = layer + + self.enabled_layers = copy.deepcopy(self.loaded_layers) + self.emit("layers-loaded") + + def _addConfigFile(self, path): + pref, sep, filename = path.rpartition("/") + if filename == "local.conf" or filename == "hob.local.conf": + self._loadLocalConf(path) + elif filename == "bblayers.conf": + self._loadLayerConf(path) + + def _splitLayer(self, path): + # we only care about the path up to /conf/layer.conf + layerpath, conf, end = path.rpartition("/conf/") + return layerpath + + def _getLayerName(self, path): + # Should this be the collection name? + layerpath, sep, name = path.rpartition("/") + return name + + def disableLayer(self, layer): + if layer in self.enabled_layers: + del self.enabled_layers[layer] + + def addLayerConf(self, confpath): + layerpath = self._splitLayer(confpath) + name = self._getLayerName(layerpath) + if name not in self.enabled_layers: + self.addLayer(name, layerpath) + return name, layerpath + + def addLayer(self, name, path): + self.enabled_layers[name] = path + + def _isLayerConfDirty(self): + # if a different number of layers enabled to what was + # loaded, definitely different + if len(self.enabled_layers) != len(self.loaded_layers): + return True + + for layer in self.loaded_layers: + # if layer loaded but no longer present, definitely dirty + if layer not in self.enabled_layers: + return True + + for layer in self.enabled_layers: + # if this layer wasn't present at load, definitely dirty + if layer not in self.loaded_layers: + return True + # if this layers path has changed, definitely dirty + if self.enabled_layers[layer] != self.loaded_layers[layer]: + return True + + return False + + def _constructLayerEntry(self): + """ + Returns a string representing the new layer selection + """ + layers = self.enabled_layers.copy() + # Construct BBLAYERS entry + layer_entry = "BBLAYERS = \" \\\n" + if 'meta' in layers: + layer_entry = layer_entry + " %s \\\n" % layers['meta'] + del layers['meta'] + for layer in layers: + layer_entry = layer_entry + " %s \\\n" % layers[layer] + layer_entry = layer_entry + " \"" + + return "".join(layer_entry) + + def writeLocalConf(self): + # Dictionary containing only new or modified variables + changed_values = {} + for var in self.config: + val = self.config[var] + if self.orig_config.get(var, None) != val: + changed_values[var] = val + + if not len(changed_values): + return + + # Create a backup of the local.conf + bkup = "%s~" % self.local + os.rename(self.local, bkup) + + # read the original conf into a list + with open(bkup, 'r') as config: + config_lines = config.readlines() + + new_config_lines = ["\n"] + for var in changed_values: + # Convenience function for re.subn(). If the pattern matches + # return a string which contains an assignment using the same + # assignment operator as the old assignment. + def replace_val(matchobj): + var = matchobj.group(1) # config variable + op = matchobj.group(2) # assignment operator + val = changed_values[var] # new config value + return "%s %s \"%s\"" % (var, op, val) + + pattern = '^\s*(%s)\s*([+=?.]+)(.*)' % re.escape(var) + p = re.compile(pattern) + cnt = 0 + replaced = False + + # Iterate over the local.conf lines and if they are a match + # for the pattern comment out the line and append a new line + # with the new VAR op "value" entry + for line in config_lines: + new_line, replacements = p.subn(replace_val, line) + if replacements: + config_lines[cnt] = "#%s" % line + new_config_lines.append(new_line) + replaced = True + cnt = cnt + 1 + + if not replaced: + new_config_lines.append("%s = \"%s\"" % (var, changed_values[var])) + + # Add the modified variables + config_lines.extend(new_config_lines) + + # Write the updated lines list object to the local.conf + with open(self.local, "w") as n: + n.write("".join(config_lines)) + + del self.orig_config + self.orig_config = copy.deepcopy(self.config) + + def writeLayerConf(self): + # If we've not added/removed new layers don't write + if not self._isLayerConfDirty(): + return + + # This pattern should find the existing BBLAYERS + pattern = 'BBLAYERS\s=\s\".*\"' + + # Backup the users bblayers.conf + bkup = "%s~" % self.bblayers + os.rename(self.bblayers, bkup) + + replacement = self._constructLayerEntry() + + with open(bkup, "r") as f: + contents = f.read() + p = re.compile(pattern, re.DOTALL) + new = p.sub(replacement, contents) + + with open(self.bblayers, "w") as n: + n.write(new) + + # At some stage we should remove the backup we've created + # though we should probably verify it first + #os.remove(bkup) + + # set loaded_layers for dirtiness tracking + self.loaded_layers = copy.deepcopy(self.enabled_layers) + + self.emit("layers-changed") + + def configFound(self, handler, path): + self._addConfigFile(path) + + def loadConfig(self, path): + self._addConfigFile(path)
lib/bb/ui/crumbs/hig.py new file +61 -0
61 lines not on this page · raw message
lib/bb/ui/crumbs/hobeventhandler.py +172 -46
278 lines not on this page · raw message
lib/bb/ui/crumbs/hobprefs.py new file +293 -0
293 lines not on this page · raw message
lib/bb/ui/crumbs/layereditor.py new file +136 -0
136 lines not on this page · raw message
lib/bb/ui/crumbs/runningbuild.py +11 -1
@@ -47,12 +47,18 @@ class RunningBuildModel (gtk.TreeStore): class RunningBuild (gobject.GObject): __gsignals__ = { + 'build-started' : (gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + ()), 'build-succeeded' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), 'build-failed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - ()) + ()), + 'build-complete' : (gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + ()) } pids_to_task = {} tasks_to_iter = {}
@@ -201,6 +207,7 @@ class RunningBuild (gobject.GObject): elif isinstance(event, bb.event.BuildStarted): + self.emit("build-started") self.model.prepend(None, (None, None, None,
@@ -218,6 +225,9 @@ class RunningBuild (gobject.GObject): Colors.OK, 0)) + # Emit a generic "build-complete" signal for things wishing to + # handle when the build is finished + self.emit("build-complete") # Emit the appropriate signal depending on the number of failures if (failures >= 1): self.emit ("build-failed")
lib/bb/ui/crumbs/tasklistmodel.py +231 -75
442 lines not on this page · raw message
lib/bb/ui/hob.py +625 -299
1,153 lines not on this page · raw message
Signature · 1 line
1.7.5.4
Quoted 15 lines · Richard Purdie
Quoted 14 lines · Joshua Lock
Of course, before sending this pull request I ran some final tests and encountered a significant problem with the implementation approach. Images are built by first using the buildTargets command to build all of the target dependencies then creating a recipe file and using the buildFile command to handle the build.
The problem is that if you have a clean build directory, perfectly reasonable with such a tool, the buildFile command does not handle the native dependencies added by the image class.The alternative I have is to save the image files somewhere within BBPATH, and perhaps suggest the user create a "workspace" layer, and build them with buildTargets. I didn't do this because I'd rather not pollute the users file system without good cause. It may be the only tenable approach though.
Quoted 7 lines
Quoted 1 line
Any suggestions here?
I think you should be able to modify the base configuration data and just add a local directory that the image creator uses to BBPATH yourself?
That way you should always be able to use buildTargets...
Right. I had some code like this but I hadn't achieved it in a way which was both a) non-invasive and b) didn't result in parsing a bunch of stuff then throwing that away and parsing again. I'll look into this approach some more.
Thanks for the feedback,
Joshua
Signature · 3 lines
Joshua Lock
Yocto Project "Johannes factotum"
Intel Open Source Technology CentreRichard Purdie
AppliedRe: [PATCH 01/10] ui/hob: Fixed the "build again" hang.
In reply to Joshua LockQuoted 11 lines · Joshua Lock
From: Lianhao Lu <[email protected]>
Using
gobject.threads_init()instead ofgtk.gdk.threads_init(). These two modes are conflict to each other. Usinggobject.threads_init()allows only the main thread to touch GUI(gtk) part.Signed-off-by Lianhao Lu <[email protected]>Signed-off-by Joshua Lock <[email protected]>--- lib/bb/ui/hob.py | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-)
Merged to master, thanks.
Richard
Quoted 50 lines · Joshua Lock
All,
This series are the current state of the image creator project I have been working on for the past few months.
It's by no means perfect but I'd love to get some review and preferably move development into master, rather than a less public branch.The first 7 patches are changes to BitBake to be used by the GUI whilst the final patch is a squashed patch of my image creator development tree[1].
Of course, before sending this pull request I ran some final tests and encountered a significant problem with the implementation approach. Images are built by first using the buildTargets command to build all of the target dependencies then creating a recipe file and using the buildFile command to handle the build.
The problem is that if you have a clean build directory, perfectly reasonable with such a tool, the buildFile command does not handle the native dependencies added by the image class.The alternative I have is to save the image files somewhere within BBPATH, and perhaps suggest the user create a "workspace" layer, and build them with buildTargets. I didn't do this because I'd rather not pollute the users file system without good cause. It may be the only tenable approach though.
Any suggestions here?
Regards,
Joshua1. https://github.com/incandescant/bitbake/commits/hob-full
The following changes since commit
5d41720d1550c04aac76275614ca15110c1c7f52:Update version to 1.13.2 (2011-06-28 15:27:14 +0100)
are available in the git repository at:
git://github.com/incandescant/bitbakehob
https://github.com/incandescant/bitbake/tree/hobJoshua Lock (8):
command|cooker: allow generating targets tree for specified pkgs
cooker: add generic method to locate configuration files command|cooker|event: add findConfigFilePath command cooker|command|event: add new command findFilesMatchingInDir
bitbake: add -R option for loading configuration files after
bitbake.conf
cooker: switch to new universe target rather than world
cooker: remove code duplication in non trivial functions
hob: re-designed interaction and implementation
Quoted 5 lines
Lianhao Lu (1):
ui/hob: Fixed the "build again" hang.Liping Ke (1):
bitbake: Make bitbake server type configurable.
I merged in the first 9 of these as they looked ok. I've not had a chance to look through the hob patch yet though.
Cheers,
Richard
Quoted 9 lines · Joshua Lock
Of course, before sending this pull request I ran some final tests and encountered a significant problem with the implementation approach. Images are built by first using the buildTargets command to build all of the target dependencies then creating a recipe file and using the buildFile command to handle the build.
The problem is that if you have a clean build directory, perfectly reasonable with such a tool, the buildFile command does not handle the native dependencies added by the image class.
Quoted 6 lines
The alternative I have is to save the image files somewhere within BBPATH, and perhaps suggest the user create a "workspace" layer, and build them with buildTargets. I didn't do this because I'd rather not pollute the users file system without good cause. It may be the only tenable approach though.
Any suggestions here?
I think you should be able to modify the base configuration data and just add a local directory that the image creator uses to BBPATH yourself?
That way you should always be able to use buildTargets...
Cheers,
Richard