mirror of
https://git.yoctoproject.org/poky
synced 2026-05-09 17:39:31 +00:00
bitbake-dev: Add basics of "puccho" image builder UI
This commit is contained in:
committed by
Richard Purdie
parent
340b2b5612
commit
199828c20e
@@ -0,0 +1,459 @@
|
|||||||
|
#
|
||||||
|
# BitBake Graphical GTK User Interface
|
||||||
|
#
|
||||||
|
# Copyright (C) 2008 Intel Corporation
|
||||||
|
#
|
||||||
|
# Authored by Rob Bradford <rob@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 gtk
|
||||||
|
import gobject
|
||||||
|
import gtk.glade
|
||||||
|
import threading
|
||||||
|
import urllib2
|
||||||
|
import os
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
class BuildConfiguration:
|
||||||
|
""" Represents a potential *or* historic *or* concrete build. It
|
||||||
|
encompasses all the things that we need to tell bitbake to do to make it
|
||||||
|
build what we want it to build.
|
||||||
|
|
||||||
|
It also stored the metadata URL and the set of possible machines (and the
|
||||||
|
distros / images / uris for these. Apart from the metdata URL these are
|
||||||
|
not serialised to file (since they may be transient). In some ways this
|
||||||
|
functionality might be shifted to the loader class."""
|
||||||
|
|
||||||
|
def __init__ (self):
|
||||||
|
self.metadata_url = None
|
||||||
|
|
||||||
|
# Tuple of (distros, image, urls)
|
||||||
|
self.machine_options = {}
|
||||||
|
|
||||||
|
self.machine = None
|
||||||
|
self.distro = None
|
||||||
|
self.image = None
|
||||||
|
self.urls = []
|
||||||
|
self.extra_urls = []
|
||||||
|
self.extra_pkgs = []
|
||||||
|
|
||||||
|
def get_machines_model (self):
|
||||||
|
model = gtk.ListStore (gobject.TYPE_STRING)
|
||||||
|
for machine in self.machine_options.keys():
|
||||||
|
model.append ([machine])
|
||||||
|
|
||||||
|
return model
|
||||||
|
|
||||||
|
def get_distro_and_images_models (self, machine):
|
||||||
|
distro_model = gtk.ListStore (gobject.TYPE_STRING)
|
||||||
|
|
||||||
|
for distro in self.machine_options[machine][0]:
|
||||||
|
distro_model.append ([distro])
|
||||||
|
|
||||||
|
image_model = gtk.ListStore (gobject.TYPE_STRING)
|
||||||
|
|
||||||
|
for image in self.machine_options[machine][1]:
|
||||||
|
image_model.append ([image])
|
||||||
|
|
||||||
|
return (distro_model, image_model)
|
||||||
|
|
||||||
|
def get_repos (self):
|
||||||
|
self.urls = self.machine_options[self.machine][2]
|
||||||
|
return self.urls
|
||||||
|
|
||||||
|
# It might be a lot lot better if we stored these in like, bitbake conf
|
||||||
|
# file format.
|
||||||
|
@staticmethod
|
||||||
|
def load_from_file (filename):
|
||||||
|
f = open (filename, "r")
|
||||||
|
|
||||||
|
conf = BuildConfiguration()
|
||||||
|
for line in f.readlines():
|
||||||
|
data = line.split (";")[1]
|
||||||
|
if (line.startswith ("metadata-url;")):
|
||||||
|
conf.metadata_url = data.strip()
|
||||||
|
continue
|
||||||
|
if (line.startswith ("url;")):
|
||||||
|
conf.urls += [data.strip()]
|
||||||
|
continue
|
||||||
|
if (line.startswith ("extra-url;")):
|
||||||
|
conf.extra_urls += [data.strip()]
|
||||||
|
continue
|
||||||
|
if (line.startswith ("machine;")):
|
||||||
|
conf.machine = data.strip()
|
||||||
|
continue
|
||||||
|
if (line.startswith ("distribution;")):
|
||||||
|
conf.distro = data.strip()
|
||||||
|
continue
|
||||||
|
if (line.startswith ("image;")):
|
||||||
|
conf.image = data.strip()
|
||||||
|
continue
|
||||||
|
|
||||||
|
f.close ()
|
||||||
|
return conf
|
||||||
|
|
||||||
|
# Serialise to a file. This is part of the build process and we use this
|
||||||
|
# to be able to repeat a given build (using the same set of parameters)
|
||||||
|
# but also so that we can include the details of the image / machine /
|
||||||
|
# distro in the build manager tree view.
|
||||||
|
def write_to_file (self, filename):
|
||||||
|
f = open (filename, "w")
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
if (self.metadata_url):
|
||||||
|
lines += ["metadata-url;%s\n" % (self.metadata_url)]
|
||||||
|
|
||||||
|
for url in self.urls:
|
||||||
|
lines += ["url;%s\n" % (url)]
|
||||||
|
|
||||||
|
for url in self.extra_urls:
|
||||||
|
lines += ["extra-url;%s\n" % (url)]
|
||||||
|
|
||||||
|
if (self.machine):
|
||||||
|
lines += ["machine;%s\n" % (self.machine)]
|
||||||
|
|
||||||
|
if (self.distro):
|
||||||
|
lines += ["distribution;%s\n" % (self.distro)]
|
||||||
|
|
||||||
|
if (self.image):
|
||||||
|
lines += ["image;%s\n" % (self.image)]
|
||||||
|
|
||||||
|
f.writelines (lines)
|
||||||
|
f.close ()
|
||||||
|
|
||||||
|
class BuildResult(gobject.GObject):
|
||||||
|
""" Represents an historic build. Perhaps not successful. But it includes
|
||||||
|
things such as the files that are in the directory (the output from the
|
||||||
|
build) as well as a deserialised BuildConfiguration file that is stored in
|
||||||
|
".conf" in the directory for the build.
|
||||||
|
|
||||||
|
This is GObject so that it can be included in the TreeStore."""
|
||||||
|
|
||||||
|
(STATE_COMPLETE, STATE_FAILED, STATE_ONGOING) = \
|
||||||
|
(0, 1, 2)
|
||||||
|
|
||||||
|
def __init__ (self, parent, identifier):
|
||||||
|
gobject.GObject.__init__ (self)
|
||||||
|
self.date = None
|
||||||
|
|
||||||
|
self.files = []
|
||||||
|
self.status = None
|
||||||
|
self.identifier = identifier
|
||||||
|
self.path = os.path.join (parent, identifier)
|
||||||
|
|
||||||
|
# Extract the date, since the directory name is of the
|
||||||
|
# format build-<year><month><day>-<ordinal> we can easily
|
||||||
|
# pull it out.
|
||||||
|
# TODO: Better to stat a file?
|
||||||
|
(_ , date, revision) = identifier.split ("-")
|
||||||
|
print date
|
||||||
|
|
||||||
|
year = int (date[0:4])
|
||||||
|
month = int (date[4:6])
|
||||||
|
day = int (date[6:8])
|
||||||
|
|
||||||
|
self.date = datetime.date (year, month, day)
|
||||||
|
|
||||||
|
self.conf = None
|
||||||
|
|
||||||
|
# By default builds are STATE_FAILED unless we find a "complete" file
|
||||||
|
# in which case they are STATE_COMPLETE
|
||||||
|
self.state = BuildResult.STATE_FAILED
|
||||||
|
for file in os.listdir (self.path):
|
||||||
|
if (file.startswith (".conf")):
|
||||||
|
conffile = os.path.join (self.path, file)
|
||||||
|
self.conf = BuildConfiguration.load_from_file (conffile)
|
||||||
|
elif (file.startswith ("complete")):
|
||||||
|
self.state = BuildResult.STATE_COMPLETE
|
||||||
|
else:
|
||||||
|
self.add_file (file)
|
||||||
|
|
||||||
|
def add_file (self, file):
|
||||||
|
# Just add the file for now. Don't care about the type.
|
||||||
|
self.files += [(file, None)]
|
||||||
|
|
||||||
|
class BuildManagerModel (gtk.TreeStore):
|
||||||
|
""" Model for the BuildManagerTreeView. This derives from gtk.TreeStore
|
||||||
|
but it abstracts nicely what the columns mean and the setup of the columns
|
||||||
|
in the model. """
|
||||||
|
|
||||||
|
(COL_IDENT, COL_DESC, COL_MACHINE, COL_DISTRO, COL_BUILD_RESULT, COL_DATE, COL_STATE) = \
|
||||||
|
(0, 1, 2, 3, 4, 5, 6)
|
||||||
|
|
||||||
|
def __init__ (self):
|
||||||
|
gtk.TreeStore.__init__ (self,
|
||||||
|
gobject.TYPE_STRING,
|
||||||
|
gobject.TYPE_STRING,
|
||||||
|
gobject.TYPE_STRING,
|
||||||
|
gobject.TYPE_STRING,
|
||||||
|
gobject.TYPE_OBJECT,
|
||||||
|
gobject.TYPE_INT64,
|
||||||
|
gobject.TYPE_INT)
|
||||||
|
|
||||||
|
class BuildManager (gobject.GObject):
|
||||||
|
""" This class manages the historic builds that have been found in the
|
||||||
|
"results" directory but is also used for starting a new build."""
|
||||||
|
|
||||||
|
__gsignals__ = {
|
||||||
|
'population-finished' : (gobject.SIGNAL_RUN_LAST,
|
||||||
|
gobject.TYPE_NONE,
|
||||||
|
()),
|
||||||
|
'populate-error' : (gobject.SIGNAL_RUN_LAST,
|
||||||
|
gobject.TYPE_NONE,
|
||||||
|
())
|
||||||
|
}
|
||||||
|
|
||||||
|
def update_build_result (self, result, iter):
|
||||||
|
# Convert the date into something we can sort by.
|
||||||
|
date = long (time.mktime (result.date.timetuple()))
|
||||||
|
|
||||||
|
# Add a top level entry for the build
|
||||||
|
|
||||||
|
self.model.set (iter,
|
||||||
|
BuildManagerModel.COL_IDENT, result.identifier,
|
||||||
|
BuildManagerModel.COL_DESC, result.conf.image,
|
||||||
|
BuildManagerModel.COL_MACHINE, result.conf.machine,
|
||||||
|
BuildManagerModel.COL_DISTRO, result.conf.distro,
|
||||||
|
BuildManagerModel.COL_BUILD_RESULT, result,
|
||||||
|
BuildManagerModel.COL_DATE, date,
|
||||||
|
BuildManagerModel.COL_STATE, result.state)
|
||||||
|
|
||||||
|
# And then we use the files in the directory as the children for the
|
||||||
|
# top level iter.
|
||||||
|
for file in result.files:
|
||||||
|
self.model.append (iter, (None, file[0], None, None, None, date, -1))
|
||||||
|
|
||||||
|
# This function is called as an idle by the BuildManagerPopulaterThread
|
||||||
|
def add_build_result (self, result):
|
||||||
|
gtk.gdk.threads_enter()
|
||||||
|
self.known_builds += [result]
|
||||||
|
|
||||||
|
self.update_build_result (result, self.model.append (None))
|
||||||
|
|
||||||
|
gtk.gdk.threads_leave()
|
||||||
|
|
||||||
|
def notify_build_finished (self):
|
||||||
|
# This is a bit of a hack. If we have a running build running then we
|
||||||
|
# will have a row in the model in STATE_ONGOING. Find it and make it
|
||||||
|
# as if it was a proper historic build (well, it is completed now....)
|
||||||
|
|
||||||
|
# We need to use the iters here rather than the Python iterator
|
||||||
|
# interface to the model since we need to pass it into
|
||||||
|
# update_build_result
|
||||||
|
|
||||||
|
iter = self.model.get_iter_first()
|
||||||
|
|
||||||
|
while (iter):
|
||||||
|
(ident, state) = self.model.get(iter,
|
||||||
|
BuildManagerModel.COL_IDENT,
|
||||||
|
BuildManagerModel.COL_STATE)
|
||||||
|
|
||||||
|
if state == BuildResult.STATE_ONGOING:
|
||||||
|
result = BuildResult (self.results_directory, ident)
|
||||||
|
self.update_build_result (result, iter)
|
||||||
|
iter = self.model.iter_next(iter)
|
||||||
|
|
||||||
|
def notify_build_succeeded (self):
|
||||||
|
# Write the "complete" file so that when we create the BuildResult
|
||||||
|
# object we put into the model
|
||||||
|
|
||||||
|
complete_file_path = os.path.join (self.cur_build_directory, "complete")
|
||||||
|
f = file (complete_file_path, "w")
|
||||||
|
f.close()
|
||||||
|
self.notify_build_finished()
|
||||||
|
|
||||||
|
def notify_build_failed (self):
|
||||||
|
# Without a "complete" file then this will mark the build as failed:
|
||||||
|
self.notify_build_finished()
|
||||||
|
|
||||||
|
# This function is called as an idle
|
||||||
|
def emit_population_finished_signal (self):
|
||||||
|
gtk.gdk.threads_enter()
|
||||||
|
self.emit ("population-finished")
|
||||||
|
gtk.gdk.threads_leave()
|
||||||
|
|
||||||
|
class BuildManagerPopulaterThread (threading.Thread):
|
||||||
|
def __init__ (self, manager, directory):
|
||||||
|
threading.Thread.__init__ (self)
|
||||||
|
self.manager = manager
|
||||||
|
self.directory = directory
|
||||||
|
|
||||||
|
def run (self):
|
||||||
|
# For each of the "build-<...>" directories ..
|
||||||
|
|
||||||
|
if os.path.exists (self.directory):
|
||||||
|
for directory in os.listdir (self.directory):
|
||||||
|
|
||||||
|
if not directory.startswith ("build-"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
build_result = BuildResult (self.directory, directory)
|
||||||
|
self.manager.add_build_result (build_result)
|
||||||
|
|
||||||
|
gobject.idle_add (BuildManager.emit_population_finished_signal,
|
||||||
|
self.manager)
|
||||||
|
|
||||||
|
def __init__ (self, server, results_directory):
|
||||||
|
gobject.GObject.__init__ (self)
|
||||||
|
|
||||||
|
# The builds that we've found from walking the result directory
|
||||||
|
self.known_builds = []
|
||||||
|
|
||||||
|
# Save out the bitbake server, we need this for issuing commands to
|
||||||
|
# the cooker:
|
||||||
|
self.server = server
|
||||||
|
|
||||||
|
# The TreeStore that we use
|
||||||
|
self.model = BuildManagerModel ()
|
||||||
|
|
||||||
|
# The results directory is where we create (and look for) the
|
||||||
|
# build-<xyz>-<n> directories. We need to populate ourselves from
|
||||||
|
# directory
|
||||||
|
self.results_directory = results_directory
|
||||||
|
self.populate_from_directory (self.results_directory)
|
||||||
|
|
||||||
|
def populate_from_directory (self, directory):
|
||||||
|
thread = BuildManager.BuildManagerPopulaterThread (self, directory)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
# Come up with the name for the next build ident by combining "build-"
|
||||||
|
# with the date formatted as yyyymmdd and then an ordinal. We do this by
|
||||||
|
# an optimistic algorithm incrementing the ordinal if we find that it
|
||||||
|
# already exists.
|
||||||
|
def get_next_build_ident (self):
|
||||||
|
today = datetime.date.today ()
|
||||||
|
datestr = str (today.year) + str (today.month) + str (today.day)
|
||||||
|
|
||||||
|
revision = 0
|
||||||
|
test_name = "build-%s-%d" % (datestr, revision)
|
||||||
|
test_path = os.path.join (self.results_directory, test_name)
|
||||||
|
|
||||||
|
while (os.path.exists (test_path)):
|
||||||
|
revision += 1
|
||||||
|
test_name = "build-%s-%d" % (datestr, revision)
|
||||||
|
test_path = os.path.join (self.results_directory, test_name)
|
||||||
|
|
||||||
|
return test_name
|
||||||
|
|
||||||
|
# Take a BuildConfiguration and then try and build it based on the
|
||||||
|
# parameters of that configuration. S
|
||||||
|
def do_build (self, conf):
|
||||||
|
server = self.server
|
||||||
|
|
||||||
|
# Work out the build directory. Note we actually create the
|
||||||
|
# directories here since we need to write the ".conf" file. Otherwise
|
||||||
|
# we could have relied on bitbake's builder thread to actually make
|
||||||
|
# the directories as it proceeds with the build.
|
||||||
|
ident = self.get_next_build_ident ()
|
||||||
|
build_directory = os.path.join (self.results_directory,
|
||||||
|
ident)
|
||||||
|
self.cur_build_directory = build_directory
|
||||||
|
os.makedirs (build_directory)
|
||||||
|
|
||||||
|
conffile = os.path.join (build_directory, ".conf")
|
||||||
|
conf.write_to_file (conffile)
|
||||||
|
|
||||||
|
# Add a row to the model representing this ongoing build. It's kinda a
|
||||||
|
# fake entry. If this build completes or fails then this gets updated
|
||||||
|
# with the real stuff like the historic builds
|
||||||
|
date = long (time.time())
|
||||||
|
self.model.append (None, (ident, conf.image, conf.machine, conf.distro,
|
||||||
|
None, date, BuildResult.STATE_ONGOING))
|
||||||
|
try:
|
||||||
|
server.runCommand(["setVariable", "BUILD_IMAGES_FROM_FEEDS", 1])
|
||||||
|
server.runCommand(["setVariable", "MACHINE", conf.machine])
|
||||||
|
server.runCommand(["setVariable", "DISTRO", conf.distro])
|
||||||
|
server.runCommand(["setVariable", "PACKAGE_CLASSES", "package_ipk"])
|
||||||
|
server.runCommand(["setVariable", "BBFILES", \
|
||||||
|
"""${OEROOT}/meta/packages/*/*.bb ${OEROOT}/meta-moblin/packages/*/*.bb"""])
|
||||||
|
server.runCommand(["setVariable", "TMPDIR", "${OEROOT}/build/tmp"])
|
||||||
|
server.runCommand(["setVariable", "IPK_FEED_URIS", \
|
||||||
|
" ".join(conf.get_repos())])
|
||||||
|
server.runCommand(["setVariable", "DEPLOY_DIR_IMAGE",
|
||||||
|
build_directory])
|
||||||
|
server.runCommand(["buildTargets", [conf.image], "rootfs"])
|
||||||
|
|
||||||
|
except Exception, e:
|
||||||
|
print e
|
||||||
|
|
||||||
|
class BuildManagerTreeView (gtk.TreeView):
|
||||||
|
""" The tree view for the build manager. This shows the historic builds
|
||||||
|
and so forth. """
|
||||||
|
|
||||||
|
# We use this function to control what goes in the cell since we store
|
||||||
|
# the date in the model as seconds since the epoch (for sorting) and so we
|
||||||
|
# need to make it human readable.
|
||||||
|
def date_format_custom_cell_data_func (self, col, cell, model, iter):
|
||||||
|
date = model.get (iter, BuildManagerModel.COL_DATE)[0]
|
||||||
|
datestr = time.strftime("%A %d %B %Y", time.localtime(date))
|
||||||
|
cell.set_property ("text", datestr)
|
||||||
|
|
||||||
|
# This format function controls what goes in the cell. We use this to map
|
||||||
|
# the integer state to a string and also to colourise the text
|
||||||
|
def state_format_custom_cell_data_fun (self, col, cell, model, iter):
|
||||||
|
state = model.get (iter, BuildManagerModel.COL_STATE)[0]
|
||||||
|
|
||||||
|
if (state == BuildResult.STATE_ONGOING):
|
||||||
|
cell.set_property ("text", "Active")
|
||||||
|
cell.set_property ("foreground", "#000000")
|
||||||
|
elif (state == BuildResult.STATE_FAILED):
|
||||||
|
cell.set_property ("text", "Failed")
|
||||||
|
cell.set_property ("foreground", "#ff0000")
|
||||||
|
elif (state == BuildResult.STATE_COMPLETE):
|
||||||
|
cell.set_property ("text", "Complete")
|
||||||
|
cell.set_property ("foreground", "#00ff00")
|
||||||
|
else:
|
||||||
|
cell.set_property ("text", "")
|
||||||
|
|
||||||
|
def __init__ (self):
|
||||||
|
gtk.TreeView.__init__(self)
|
||||||
|
|
||||||
|
# Misc descriptiony thing
|
||||||
|
renderer = gtk.CellRendererText ()
|
||||||
|
col = gtk.TreeViewColumn (None, renderer,
|
||||||
|
text=BuildManagerModel.COL_DESC)
|
||||||
|
self.append_column (col)
|
||||||
|
|
||||||
|
# Machine
|
||||||
|
renderer = gtk.CellRendererText ()
|
||||||
|
col = gtk.TreeViewColumn ("Machine", renderer,
|
||||||
|
text=BuildManagerModel.COL_MACHINE)
|
||||||
|
self.append_column (col)
|
||||||
|
|
||||||
|
# distro
|
||||||
|
renderer = gtk.CellRendererText ()
|
||||||
|
col = gtk.TreeViewColumn ("Distribution", renderer,
|
||||||
|
text=BuildManagerModel.COL_DISTRO)
|
||||||
|
self.append_column (col)
|
||||||
|
|
||||||
|
# date (using a custom function for formatting the cell contents it
|
||||||
|
# takes epoch -> human readable string)
|
||||||
|
renderer = gtk.CellRendererText ()
|
||||||
|
col = gtk.TreeViewColumn ("Date", renderer,
|
||||||
|
text=BuildManagerModel.COL_DATE)
|
||||||
|
self.append_column (col)
|
||||||
|
col.set_cell_data_func (renderer,
|
||||||
|
self.date_format_custom_cell_data_func)
|
||||||
|
|
||||||
|
# For status.
|
||||||
|
renderer = gtk.CellRendererText ()
|
||||||
|
col = gtk.TreeViewColumn ("Status", renderer,
|
||||||
|
text = BuildManagerModel.COL_STATE)
|
||||||
|
self.append_column (col)
|
||||||
|
col.set_cell_data_func (renderer,
|
||||||
|
self.state_format_custom_cell_data_fun)
|
||||||
|
|
||||||
@@ -0,0 +1,606 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
|
||||||
|
<!--Generated with glade3 3.4.5 on Mon Nov 10 12:24:12 2008 -->
|
||||||
|
<glade-interface>
|
||||||
|
<widget class="GtkDialog" id="build_dialog">
|
||||||
|
<property name="title" translatable="yes">Start a build</property>
|
||||||
|
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
|
||||||
|
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
|
||||||
|
<property name="has_separator">False</property>
|
||||||
|
<child internal-child="vbox">
|
||||||
|
<widget class="GtkVBox" id="dialog-vbox1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="spacing">2</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkTable" id="build_table">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="border_width">6</property>
|
||||||
|
<property name="n_rows">7</property>
|
||||||
|
<property name="n_columns">3</property>
|
||||||
|
<property name="column_spacing">5</property>
|
||||||
|
<property name="row_spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkAlignment" id="status_alignment">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="left_padding">12</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHBox" id="status_hbox">
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkImage" id="status_image">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="no_show_all">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="stock">gtk-dialog-error</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="status_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="label" translatable="yes">If you see this text something is wrong...</property>
|
||||||
|
<property name="use_markup">True</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
<property name="bottom_attach">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="label" translatable="yes"><b>Build configuration</b></property>
|
||||||
|
<property name="use_markup">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
<property name="bottom_attach">4</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkComboBox" id="image_combo">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">6</property>
|
||||||
|
<property name="bottom_attach">7</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="image_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="xpad">12</property>
|
||||||
|
<property name="label" translatable="yes">Image:</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="top_attach">6</property>
|
||||||
|
<property name="bottom_attach">7</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkComboBox" id="distribution_combo">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">5</property>
|
||||||
|
<property name="bottom_attach">6</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="distribution_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="xpad">12</property>
|
||||||
|
<property name="label" translatable="yes">Distribution:</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="top_attach">5</property>
|
||||||
|
<property name="bottom_attach">6</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkComboBox" id="machine_combo">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">4</property>
|
||||||
|
<property name="bottom_attach">5</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="machine_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="xpad">12</property>
|
||||||
|
<property name="label" translatable="yes">Machine:</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="top_attach">4</property>
|
||||||
|
<property name="bottom_attach">5</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="refresh_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="label" translatable="yes">gtk-refresh</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<property name="response_id">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">2</property>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="bottom_attach">2</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkEntry" id="location_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="width_chars">32</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="bottom_attach">2</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label3">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="xpad">12</property>
|
||||||
|
<property name="label" translatable="yes">Location:</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="bottom_attach">2</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="label" translatable="yes"><b>Repository</b></property>
|
||||||
|
<property name="use_markup">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkAlignment" id="alignment1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">2</property>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="top_attach">4</property>
|
||||||
|
<property name="bottom_attach">5</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkAlignment" id="alignment2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">2</property>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="top_attach">5</property>
|
||||||
|
<property name="bottom_attach">6</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkAlignment" id="alignment3">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">2</property>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="top_attach">6</property>
|
||||||
|
<property name="bottom_attach">7</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child internal-child="action_area">
|
||||||
|
<widget class="GtkHButtonBox" id="dialog-action_area1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="layout_style">GTK_BUTTONBOX_END</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="pack_type">GTK_PACK_END</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<widget class="GtkDialog" id="dialog2">
|
||||||
|
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
|
||||||
|
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
|
||||||
|
<property name="has_separator">False</property>
|
||||||
|
<child internal-child="vbox">
|
||||||
|
<widget class="GtkVBox" id="dialog-vbox2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="spacing">2</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkTable" id="table2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="border_width">6</property>
|
||||||
|
<property name="n_rows">7</property>
|
||||||
|
<property name="n_columns">3</property>
|
||||||
|
<property name="column_spacing">6</property>
|
||||||
|
<property name="row_spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label7">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="label" translatable="yes"><b>Repositories</b></property>
|
||||||
|
<property name="use_markup">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkAlignment" id="alignment4">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="left_padding">12</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkScrolledWindow" id="scrolledwindow1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||||
|
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkTreeView" id="treeview1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="headers_clickable">True</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
<property name="bottom_attach">3</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkEntry" id="entry1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="bottom_attach">2</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label9">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="label" translatable="yes"><b>Additional packages</b></property>
|
||||||
|
<property name="use_markup">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="top_attach">4</property>
|
||||||
|
<property name="bottom_attach">5</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkAlignment" id="alignment6">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="xscale">0</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label8">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">12</property>
|
||||||
|
<property name="label" translatable="yes">Location: </property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="bottom_attach">2</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkAlignment" id="alignment7">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="xscale">0</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHButtonBox" id="hbuttonbox1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="spacing">5</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="button7">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="label" translatable="yes">gtk-remove</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<property name="response_id">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="button6">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="label" translatable="yes">gtk-edit</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<property name="response_id">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="button5">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="label" translatable="yes">gtk-add</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<property name="response_id">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
<property name="bottom_attach">4</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkAlignment" id="alignment5">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
<property name="bottom_attach">4</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label10">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">12</property>
|
||||||
|
<property name="label" translatable="yes">Search:</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="top_attach">5</property>
|
||||||
|
<property name="bottom_attach">6</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkEntry" id="entry2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="top_attach">5</property>
|
||||||
|
<property name="bottom_attach">6</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkAlignment" id="alignment8">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="left_padding">12</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkScrolledWindow" id="scrolledwindow2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||||
|
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkTreeView" id="treeview2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="headers_clickable">True</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="top_attach">6</property>
|
||||||
|
<property name="bottom_attach">7</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child internal-child="action_area">
|
||||||
|
<widget class="GtkHButtonBox" id="dialog-action_area2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="layout_style">GTK_BUTTONBOX_END</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="button4">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="label" translatable="yes">gtk-close</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<property name="response_id">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="pack_type">GTK_PACK_END</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<widget class="GtkWindow" id="main_window">
|
||||||
|
<child>
|
||||||
|
<widget class="GtkVBox" id="main_window_vbox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkToolbar" id="main_toolbar">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkToolButton" id="main_toolbutton_build">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Build</property>
|
||||||
|
<property name="stock_id">gtk-execute</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkVPaned" id="vpaned1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkScrolledWindow" id="results_scrolledwindow">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||||
|
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="resize">False</property>
|
||||||
|
<property name="shrink">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<widget class="GtkScrolledWindow" id="progress_scrolledwindow">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||||
|
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="resize">True</property>
|
||||||
|
<property name="shrink">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</glade-interface>
|
||||||
@@ -18,8 +18,9 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
import gobject
|
|
||||||
import gtk
|
import gtk
|
||||||
|
import gobject
|
||||||
|
import gtk.glade
|
||||||
|
|
||||||
class RunningBuildModel (gtk.TreeStore):
|
class RunningBuildModel (gtk.TreeStore):
|
||||||
(COL_TYPE, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_ACTIVE) = (0, 1, 2, 3, 4, 5)
|
(COL_TYPE, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_ACTIVE) = (0, 1, 2, 3, 4, 5)
|
||||||
@@ -34,9 +35,12 @@ class RunningBuildModel (gtk.TreeStore):
|
|||||||
|
|
||||||
class RunningBuild (gobject.GObject):
|
class RunningBuild (gobject.GObject):
|
||||||
__gsignals__ = {
|
__gsignals__ = {
|
||||||
'build-finished' : (gobject.SIGNAL_RUN_LAST,
|
'build-succeeded' : (gobject.SIGNAL_RUN_LAST,
|
||||||
gobject.TYPE_NONE,
|
gobject.TYPE_NONE,
|
||||||
())
|
()),
|
||||||
|
'build-failed' : (gobject.SIGNAL_RUN_LAST,
|
||||||
|
gobject.TYPE_NONE,
|
||||||
|
())
|
||||||
}
|
}
|
||||||
pids_to_task = {}
|
pids_to_task = {}
|
||||||
tasks_to_iter = {}
|
tasks_to_iter = {}
|
||||||
@@ -150,6 +154,15 @@ class RunningBuild (gobject.GObject):
|
|||||||
del self.tasks_to_iter[(package, task)]
|
del self.tasks_to_iter[(package, task)]
|
||||||
del self.pids_to_task[pid]
|
del self.pids_to_task[pid]
|
||||||
|
|
||||||
|
elif event[0].startswith('bb.event.BuildCompleted'):
|
||||||
|
failures = int (event[1]['_failures'])
|
||||||
|
|
||||||
|
# Emit the appropriate signal depending on the number of failures
|
||||||
|
if (failures > 1):
|
||||||
|
self.emit ("build-failed")
|
||||||
|
else:
|
||||||
|
self.emit ("build-succeeded")
|
||||||
|
|
||||||
class RunningBuildTreeView (gtk.TreeView):
|
class RunningBuildTreeView (gtk.TreeView):
|
||||||
def __init__ (self):
|
def __init__ (self):
|
||||||
gtk.TreeView.__init__ (self)
|
gtk.TreeView.__init__ (self)
|
||||||
@@ -166,4 +179,3 @@ class RunningBuildTreeView (gtk.TreeView):
|
|||||||
self.append_column (col)
|
self.append_column (col)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,426 @@
|
|||||||
|
#
|
||||||
|
# BitBake Graphical GTK User Interface
|
||||||
|
#
|
||||||
|
# Copyright (C) 2008 Intel Corporation
|
||||||
|
#
|
||||||
|
# Authored by Rob Bradford <rob@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 gtk
|
||||||
|
import gobject
|
||||||
|
import gtk.glade
|
||||||
|
import threading
|
||||||
|
import urllib2
|
||||||
|
import os
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from bb.ui.crumbs.buildmanager import BuildManager, BuildConfiguration
|
||||||
|
from bb.ui.crumbs.buildmanager import BuildManagerTreeView
|
||||||
|
|
||||||
|
from bb.ui.crumbs.runningbuild import RunningBuild, RunningBuildTreeView
|
||||||
|
|
||||||
|
# The metadata loader is used by the BuildSetupDialog to download the
|
||||||
|
# available options to populate the dialog
|
||||||
|
class MetaDataLoader(gobject.GObject):
|
||||||
|
""" This class provides the mechanism for loading the metadata (the
|
||||||
|
fetching and parsing) from a given URL. The metadata encompasses details
|
||||||
|
on what machines are available. The distribution and images available for
|
||||||
|
the machine and the the uris to use for building the given machine."""
|
||||||
|
__gsignals__ = {
|
||||||
|
'success' : (gobject.SIGNAL_RUN_LAST,
|
||||||
|
gobject.TYPE_NONE,
|
||||||
|
()),
|
||||||
|
'error' : (gobject.SIGNAL_RUN_LAST,
|
||||||
|
gobject.TYPE_NONE,
|
||||||
|
(gobject.TYPE_STRING,))
|
||||||
|
}
|
||||||
|
|
||||||
|
# We use these little helper functions to ensure that we take the gdk lock
|
||||||
|
# when emitting the signal. These functions are called as idles (so that
|
||||||
|
# they happen in the gtk / main thread's main loop.
|
||||||
|
def emit_error_signal (self, remark):
|
||||||
|
gtk.gdk.threads_enter()
|
||||||
|
self.emit ("error", remark)
|
||||||
|
gtk.gdk.threads_leave()
|
||||||
|
|
||||||
|
def emit_success_signal (self):
|
||||||
|
gtk.gdk.threads_enter()
|
||||||
|
self.emit ("success")
|
||||||
|
gtk.gdk.threads_leave()
|
||||||
|
|
||||||
|
def __init__ (self):
|
||||||
|
gobject.GObject.__init__ (self)
|
||||||
|
|
||||||
|
class LoaderThread(threading.Thread):
|
||||||
|
""" This class provides an asynchronous loader for the metadata (by
|
||||||
|
using threads and signals). This is useful since the metadata may be
|
||||||
|
at a remote URL."""
|
||||||
|
class LoaderImportException (Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __init__(self, loader, url):
|
||||||
|
threading.Thread.__init__ (self)
|
||||||
|
self.url = url
|
||||||
|
self.loader = loader
|
||||||
|
|
||||||
|
def run (self):
|
||||||
|
result = {}
|
||||||
|
try:
|
||||||
|
f = urllib2.urlopen (self.url)
|
||||||
|
|
||||||
|
# Parse the metadata format. The format is....
|
||||||
|
# <machine>;<default distro>|<distro>...;<default image>|<image>...;<type##url>|...
|
||||||
|
for line in f.readlines():
|
||||||
|
components = line.split(";")
|
||||||
|
if (len (components) < 4):
|
||||||
|
raise MetaDataLoader.LoaderThread.LoaderImportException
|
||||||
|
machine = components[0]
|
||||||
|
distros = components[1].split("|")
|
||||||
|
images = components[2].split("|")
|
||||||
|
urls = components[3].split("|")
|
||||||
|
|
||||||
|
result[machine] = (distros, images, urls)
|
||||||
|
|
||||||
|
# Create an object representing this *potential*
|
||||||
|
# configuration. It can become concrete if the machine, distro
|
||||||
|
# and image are all chosen in the UI
|
||||||
|
configuration = BuildConfiguration()
|
||||||
|
configuration.metadata_url = self.url
|
||||||
|
configuration.machine_options = result
|
||||||
|
self.loader.configuration = configuration
|
||||||
|
|
||||||
|
# Emit that we've actually got a configuration
|
||||||
|
gobject.idle_add (MetaDataLoader.emit_success_signal,
|
||||||
|
self.loader)
|
||||||
|
|
||||||
|
except MetaDataLoader.LoaderThread.LoaderImportException, e:
|
||||||
|
gobject.idle_add (MetaDataLoader.emit_error_signal, self.loader,
|
||||||
|
"Repository metadata corrupt")
|
||||||
|
except Exception, e:
|
||||||
|
gobject.idle_add (MetaDataLoader.emit_error_signal, self.loader,
|
||||||
|
"Unable to download repository metadata")
|
||||||
|
print e
|
||||||
|
|
||||||
|
def try_fetch_from_url (self, url):
|
||||||
|
# Try and download the metadata. Firing a signal if successful
|
||||||
|
thread = MetaDataLoader.LoaderThread(self, url)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
class BuildSetupDialog (gtk.Dialog):
|
||||||
|
RESPONSE_BUILD = 1
|
||||||
|
|
||||||
|
# A little helper method that just sets the states on the widgets based on
|
||||||
|
# whether we've got good metadata or not.
|
||||||
|
def set_configurable (self, configurable):
|
||||||
|
if (self.configurable == configurable):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.configurable = configurable
|
||||||
|
for widget in self.conf_widgets:
|
||||||
|
widget.set_sensitive (configurable)
|
||||||
|
|
||||||
|
if not configurable:
|
||||||
|
self.machine_combo.set_active (-1)
|
||||||
|
self.distribution_combo.set_active (-1)
|
||||||
|
self.image_combo.set_active (-1)
|
||||||
|
|
||||||
|
# GTK widget callbacks
|
||||||
|
def refresh_button_clicked (self, button):
|
||||||
|
# Refresh button clicked.
|
||||||
|
|
||||||
|
url = self.location_entry.get_chars (0, -1)
|
||||||
|
self.loader.try_fetch_from_url(url)
|
||||||
|
|
||||||
|
def repository_entry_editable_changed (self, entry):
|
||||||
|
if (len (entry.get_chars (0, -1)) > 0):
|
||||||
|
self.refresh_button.set_sensitive (True)
|
||||||
|
else:
|
||||||
|
self.refresh_button.set_sensitive (False)
|
||||||
|
self.clear_status_message()
|
||||||
|
|
||||||
|
# If we were previously configurable we are no longer since the
|
||||||
|
# location entry has been changed
|
||||||
|
self.set_configurable (False)
|
||||||
|
|
||||||
|
def machine_combo_changed (self, combobox):
|
||||||
|
active_iter = combobox.get_active_iter()
|
||||||
|
|
||||||
|
if not active_iter:
|
||||||
|
return
|
||||||
|
|
||||||
|
model = combobox.get_model()
|
||||||
|
|
||||||
|
if model:
|
||||||
|
chosen_machine = model.get (active_iter, 0)[0]
|
||||||
|
|
||||||
|
(distros_model, images_model) = \
|
||||||
|
self.loader.configuration.get_distro_and_images_models (chosen_machine)
|
||||||
|
|
||||||
|
self.distribution_combo.set_model (distros_model)
|
||||||
|
self.image_combo.set_model (images_model)
|
||||||
|
|
||||||
|
# Callbacks from the loader
|
||||||
|
def loader_success_cb (self, loader):
|
||||||
|
self.status_image.set_from_icon_name ("info",
|
||||||
|
gtk.ICON_SIZE_BUTTON)
|
||||||
|
self.status_image.show()
|
||||||
|
self.status_label.set_label ("Repository metadata successfully downloaded")
|
||||||
|
|
||||||
|
# Set the models on the combo boxes based on the models generated from
|
||||||
|
# the configuration that the loader has created
|
||||||
|
|
||||||
|
# We just need to set the machine here, that then determines the
|
||||||
|
# distro and image options. Cunning huh? :-)
|
||||||
|
|
||||||
|
self.configuration = self.loader.configuration
|
||||||
|
model = self.configuration.get_machines_model ()
|
||||||
|
self.machine_combo.set_model (model)
|
||||||
|
|
||||||
|
self.set_configurable (True)
|
||||||
|
|
||||||
|
def loader_error_cb (self, loader, message):
|
||||||
|
self.status_image.set_from_icon_name ("error",
|
||||||
|
gtk.ICON_SIZE_BUTTON)
|
||||||
|
self.status_image.show()
|
||||||
|
self.status_label.set_text ("Error downloading repository metadata")
|
||||||
|
for widget in self.conf_widgets:
|
||||||
|
widget.set_sensitive (False)
|
||||||
|
|
||||||
|
def clear_status_message (self):
|
||||||
|
self.status_image.hide()
|
||||||
|
self.status_label.set_label (
|
||||||
|
"""<i>Enter the repository location and press _Refresh</i>""")
|
||||||
|
|
||||||
|
def __init__ (self):
|
||||||
|
gtk.Dialog.__init__ (self)
|
||||||
|
|
||||||
|
# Cancel
|
||||||
|
self.add_button (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
||||||
|
|
||||||
|
# Build
|
||||||
|
button = gtk.Button ("_Build", None, True)
|
||||||
|
image = gtk.Image ()
|
||||||
|
image.set_from_stock (gtk.STOCK_EXECUTE,gtk.ICON_SIZE_BUTTON)
|
||||||
|
button.set_image (image)
|
||||||
|
self.add_action_widget (button, BuildSetupDialog.RESPONSE_BUILD)
|
||||||
|
button.show_all ()
|
||||||
|
|
||||||
|
# Pull in *just* the table from the Glade XML data.
|
||||||
|
gxml = gtk.glade.XML ("bitbake-dev/lib/bb/ui/crumbs/puccho.glade",
|
||||||
|
root = "build_table")
|
||||||
|
table = gxml.get_widget ("build_table")
|
||||||
|
self.vbox.pack_start (table, True, False, 0)
|
||||||
|
|
||||||
|
# Grab all the widgets that we need to turn on/off when we refresh...
|
||||||
|
self.conf_widgets = []
|
||||||
|
self.conf_widgets += [gxml.get_widget ("machine_label")]
|
||||||
|
self.conf_widgets += [gxml.get_widget ("distribution_label")]
|
||||||
|
self.conf_widgets += [gxml.get_widget ("image_label")]
|
||||||
|
self.conf_widgets += [gxml.get_widget ("machine_combo")]
|
||||||
|
self.conf_widgets += [gxml.get_widget ("distribution_combo")]
|
||||||
|
self.conf_widgets += [gxml.get_widget ("image_combo")]
|
||||||
|
|
||||||
|
# Grab the status widgets
|
||||||
|
self.status_image = gxml.get_widget ("status_image")
|
||||||
|
self.status_label = gxml.get_widget ("status_label")
|
||||||
|
|
||||||
|
# Grab the refresh button and connect to the clicked signal
|
||||||
|
self.refresh_button = gxml.get_widget ("refresh_button")
|
||||||
|
self.refresh_button.connect ("clicked", self.refresh_button_clicked)
|
||||||
|
|
||||||
|
# Grab the location entry and connect to editable::changed
|
||||||
|
self.location_entry = gxml.get_widget ("location_entry")
|
||||||
|
self.location_entry.connect ("changed",
|
||||||
|
self.repository_entry_editable_changed)
|
||||||
|
|
||||||
|
# Grab the machine combo and hook onto the changed signal. This then
|
||||||
|
# allows us to populate the distro and image combos
|
||||||
|
self.machine_combo = gxml.get_widget ("machine_combo")
|
||||||
|
self.machine_combo.connect ("changed", self.machine_combo_changed)
|
||||||
|
|
||||||
|
# Setup the combo
|
||||||
|
cell = gtk.CellRendererText()
|
||||||
|
self.machine_combo.pack_start(cell, True)
|
||||||
|
self.machine_combo.add_attribute(cell, 'text', 0)
|
||||||
|
|
||||||
|
# Grab the distro and image combos. We need these to populate with
|
||||||
|
# models once the machine is chosen
|
||||||
|
self.distribution_combo = gxml.get_widget ("distribution_combo")
|
||||||
|
cell = gtk.CellRendererText()
|
||||||
|
self.distribution_combo.pack_start(cell, True)
|
||||||
|
self.distribution_combo.add_attribute(cell, 'text', 0)
|
||||||
|
|
||||||
|
self.image_combo = gxml.get_widget ("image_combo")
|
||||||
|
cell = gtk.CellRendererText()
|
||||||
|
self.image_combo.pack_start(cell, True)
|
||||||
|
self.image_combo.add_attribute(cell, 'text', 0)
|
||||||
|
|
||||||
|
# Put the default descriptive text in the status box
|
||||||
|
self.clear_status_message()
|
||||||
|
|
||||||
|
# Mark as non-configurable, this is just greys out the widgets the
|
||||||
|
# user can't yet use
|
||||||
|
self.configurable = False
|
||||||
|
self.set_configurable(False)
|
||||||
|
|
||||||
|
# Show the table
|
||||||
|
table.show_all ()
|
||||||
|
|
||||||
|
# The loader and some signals connected to it to update the status
|
||||||
|
# area
|
||||||
|
self.loader = MetaDataLoader()
|
||||||
|
self.loader.connect ("success", self.loader_success_cb)
|
||||||
|
self.loader.connect ("error", self.loader_error_cb)
|
||||||
|
|
||||||
|
def update_configuration (self):
|
||||||
|
""" A poorly named function but it updates the internal configuration
|
||||||
|
from the widgets. This can make that configuration concrete and can
|
||||||
|
thus be used for building """
|
||||||
|
# Extract the chosen machine from the combo
|
||||||
|
model = self.machine_combo.get_model()
|
||||||
|
active_iter = self.machine_combo.get_active_iter()
|
||||||
|
if (active_iter):
|
||||||
|
self.configuration.machine = model.get(active_iter, 0)[0]
|
||||||
|
|
||||||
|
# Extract the chosen distro from the combo
|
||||||
|
model = self.distribution_combo.get_model()
|
||||||
|
active_iter = self.distribution_combo.get_active_iter()
|
||||||
|
if (active_iter):
|
||||||
|
self.configuration.distro = model.get(active_iter, 0)[0]
|
||||||
|
|
||||||
|
# Extract the chosen image from the combo
|
||||||
|
model = self.image_combo.get_model()
|
||||||
|
active_iter = self.image_combo.get_active_iter()
|
||||||
|
if (active_iter):
|
||||||
|
self.configuration.image = model.get(active_iter, 0)[0]
|
||||||
|
|
||||||
|
# This function operates to pull events out from the event queue and then push
|
||||||
|
# them into the RunningBuild (which then drives the RunningBuild which then
|
||||||
|
# pushes through and updates the progress tree view.)
|
||||||
|
#
|
||||||
|
# TODO: Should be a method on the RunningBuild class
|
||||||
|
def event_handle_timeout (eventHandler, build):
|
||||||
|
# Consume as many messages as we can ...
|
||||||
|
event = eventHandler.getEvent()
|
||||||
|
while event:
|
||||||
|
build.handle_event (event)
|
||||||
|
event = eventHandler.getEvent()
|
||||||
|
return True
|
||||||
|
|
||||||
|
class MainWindow (gtk.Window):
|
||||||
|
|
||||||
|
# Callback that gets fired when the user hits a button in the
|
||||||
|
# BuildSetupDialog.
|
||||||
|
def build_dialog_box_response_cb (self, dialog, response_id):
|
||||||
|
conf = None
|
||||||
|
if (response_id == BuildSetupDialog.RESPONSE_BUILD):
|
||||||
|
dialog.update_configuration()
|
||||||
|
print dialog.configuration.machine, dialog.configuration.distro, \
|
||||||
|
dialog.configuration.image
|
||||||
|
conf = dialog.configuration
|
||||||
|
|
||||||
|
dialog.destroy()
|
||||||
|
|
||||||
|
if conf:
|
||||||
|
self.manager.do_build (conf)
|
||||||
|
|
||||||
|
def build_button_clicked_cb (self, button):
|
||||||
|
dialog = BuildSetupDialog ()
|
||||||
|
|
||||||
|
# For some unknown reason Dialog.run causes nice little deadlocks ... :-(
|
||||||
|
dialog.connect ("response", self.build_dialog_box_response_cb)
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
def __init__ (self):
|
||||||
|
gtk.Window.__init__ (self)
|
||||||
|
|
||||||
|
# Pull in *just* the main vbox from the Glade XML data and then pack
|
||||||
|
# that inside the window
|
||||||
|
gxml = gtk.glade.XML ("bitbake-dev/lib/bb/ui/crumbs/puccho.glade",
|
||||||
|
root = "main_window_vbox")
|
||||||
|
vbox = gxml.get_widget ("main_window_vbox")
|
||||||
|
self.add (vbox)
|
||||||
|
|
||||||
|
# Create the tree views for the build manager view and the progress view
|
||||||
|
self.build_manager_view = BuildManagerTreeView()
|
||||||
|
self.running_build_view = RunningBuildTreeView()
|
||||||
|
|
||||||
|
# Grab the scrolled windows that we put the tree views into
|
||||||
|
self.results_scrolledwindow = gxml.get_widget ("results_scrolledwindow")
|
||||||
|
self.progress_scrolledwindow = gxml.get_widget ("progress_scrolledwindow")
|
||||||
|
|
||||||
|
# Put the tree views inside ...
|
||||||
|
self.results_scrolledwindow.add (self.build_manager_view)
|
||||||
|
self.progress_scrolledwindow.add (self.running_build_view)
|
||||||
|
|
||||||
|
# Hook up the build button...
|
||||||
|
self.build_button = gxml.get_widget ("main_toolbutton_build")
|
||||||
|
self.build_button.connect ("clicked", self.build_button_clicked_cb)
|
||||||
|
|
||||||
|
# I'm not very happy about the current ownership of the RunningBuild. I have
|
||||||
|
# my suspicions that this object should be held by the BuildManager since we
|
||||||
|
# care about the signals in the manager
|
||||||
|
|
||||||
|
def running_build_succeeded_cb (running_build, manager):
|
||||||
|
# Notify the manager that a build has succeeded. This is necessary as part
|
||||||
|
# of the 'hack' that we use for making the row in the model / view
|
||||||
|
# representing the ongoing build change into a row representing the
|
||||||
|
# completed build. Since we know only one build can be running a time then
|
||||||
|
# we can handle this.
|
||||||
|
|
||||||
|
# FIXME: Refactor all this so that the RunningBuild is owned by the
|
||||||
|
# BuildManager. It can then hook onto the signals directly and drive
|
||||||
|
# interesting things it cares about.
|
||||||
|
manager.notify_build_succeeded ()
|
||||||
|
print "build succeeded"
|
||||||
|
|
||||||
|
def running_build_failed_cb (running_build, manager):
|
||||||
|
# As above
|
||||||
|
print "build failed"
|
||||||
|
manager.notify_build_failed ()
|
||||||
|
|
||||||
|
def init (server, eventHandler):
|
||||||
|
# Initialise threading...
|
||||||
|
gobject.threads_init()
|
||||||
|
gtk.gdk.threads_init()
|
||||||
|
|
||||||
|
main_window = MainWindow ()
|
||||||
|
main_window.show_all ()
|
||||||
|
|
||||||
|
# Set up the build manager stuff in general
|
||||||
|
builds_dir = os.path.join (os.getcwd(), "results")
|
||||||
|
manager = BuildManager (server, builds_dir)
|
||||||
|
main_window.build_manager_view.set_model (manager.model)
|
||||||
|
|
||||||
|
# Do the running build setup
|
||||||
|
running_build = RunningBuild ()
|
||||||
|
main_window.running_build_view.set_model (running_build.model)
|
||||||
|
running_build.connect ("build-succeeded", running_build_succeeded_cb,
|
||||||
|
manager)
|
||||||
|
running_build.connect ("build-failed", running_build_failed_cb, manager)
|
||||||
|
|
||||||
|
# We need to save the manager into the MainWindow so that the toolbar
|
||||||
|
# button can use it.
|
||||||
|
# FIXME: Refactor ?
|
||||||
|
main_window.manager = manager
|
||||||
|
|
||||||
|
# Use a timeout function for probing the event queue to find out if we
|
||||||
|
# have a message waiting for us.
|
||||||
|
gobject.timeout_add (200,
|
||||||
|
event_handle_timeout,
|
||||||
|
eventHandler,
|
||||||
|
running_build)
|
||||||
|
|
||||||
|
gtk.main()
|
||||||
Reference in New Issue
Block a user