Skip to contentSkip to conversation

Hob - BitBake GUI (v2)

PATCH Applied Joshua Lock 2 participants 3 messages Jul 1, 2011 – Jul 5, 2011 Arm STM32

lore
Share & tools
lore
#0 lore raw

cover Hob - BitBake GUI (v2)

Since the first revision the groundwork patches have been merged and I fixed a couple of issues noticed today. Namely:
1) The wording in the "Build Complete" dialog no longer matched the buttons
2) The recipe writing code now handles images with a base image outside of the oe-core metadata

To see just these two changes look to the top two patches on:
https://github.com/incandescant/bitbake/commits/hob-work

As noted in the v1 pull request 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.

Richard has suggested I modify the GUI to insert a path into BBPATH and then I can rely on buildTasks. I'm working on this now but didn't want to send a half baked patch.

Cheers,
Joshua

The following changes since commit a8246ae5400c23df0d3ee29c36f4d9f257d1e6d1:

bitbake: add -R option for loading configuration files after bitbake.conf (2011-07-01 17:03:03 +0100)

are available in the git repository at:
git://github.com/incandescant/bitbake hob
https://github.com/incandescant/bitbake/tree/hob

Joshua Lock (1):
hob: re-designed interaction and implementation

 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   |  326 ++++++++++---
 lib/bb/ui/hob.py                    |  924 +++++++++++++++++++++++-----------
 8 files changed, 1824 insertions(+), 424 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
#1 lore raw

PATCH 1/1 hob: re-designed interaction and implementation

Highlights 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   |  326 ++++++++++---
 lib/bb/ui/hob.py                    |  924 +++++++++++++++++++++++-----------
 8 files changed, 1824 insertions(+), 424 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
new file mode 100644
index 0000000..b694143
@@ -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
index 70fd57e..bdab340 100644
@@ -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 +248 -78

458 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
#2 lore raw
In reply to Joshua Lock
Quoted 34 lines · Joshua Lock

Since the first revision the groundwork patches have been merged and I fixed a couple of issues noticed today. Namely:
1) The wording in the "Build Complete" dialog no longer matched the buttons
2) The recipe writing code now handles images with a base image outside of the oe-core metadata

To see just these two changes look to the top two patches on:
https://github.com/incandescant/bitbake/commits/hob-work

As noted in the v1 pull request 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.

Richard has suggested I modify the GUI to insert a path into BBPATH and then I can rely on buildTasks. I'm working on this now but didn't want to send a half baked patch.

Cheers,
Joshua

The following changes since commit a8246ae5400c23df0d3ee29c36f4d9f257d1e6d1:

bitbake: add -R option for loading configuration files after bitbake.conf (2011-07-01 17:03:03 +0100)

are available in the git repository at:
git://github.com/incandescant/bitbake hob
https://github.com/incandescant/bitbake/tree/hob

Joshua Lock (1):
hob: re-designed interaction and implementation

Quoted 13 lines
 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   |  326 ++++++++++---
 lib/bb/ui/hob.py                    |  924 +++++++++++++++++++++++-----------
 8 files changed, 1824 insertions(+), 424 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

Merged to master, thanks!

Richard