Commits

Moises Henriquez committed 7d6d10e

Added working vpackager depending on sbbuilder

Comments (0)

Files changed (7)

+syntax: glob
+*.py~
+*.pyc

dialogs.py

-#!/usr/bin/env python
-
-#    This file is part of vpackager.
-#
-#    vpackager is free software: you can redistribute it and/or modify
-#    it under the terms of the GNU General Public License v2 as published by
-#    the Free Software Foundation.
-#
-#    vpackager 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 vpackager.  If not, see <http://www.gnu.org/licenses/>.
-
-import gtk
-
-class question(gtk.MessageDialog):
-    def __init__(self, question, parent):
-        gtk.MessageDialog.__init__(self,
-            type = gtk.MESSAGE_QUESTION,
-            parent = parent)
-        self.set_markup(question)
-        self.add_buttons(gtk.STOCK_YES, gtk.RESPONSE_ACCEPT,
-            gtk.STOCK_NO, gtk.RESPONSE_CANCEL)
-        self.set_title('Question')
-
-class info(gtk.MessageDialog):
-    def __init__(self, message, parent):
-        gtk.MessageDialog.__init__(self,
-            type = gtk.MESSAGE_INFO,
-            parent = parent)
-        self.set_markup(message)
-        self.add_buttons(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)
-        self.set_title('Info')
-        
-class error(gtk.MessageDialog):
-    def __init__(self, message, parent):
-        gtk.MessageDialog.__init__(self,
-            type = gtk.MESSAGE_ERROR,
-            parent = parent)
-        self.set_markup(message)
-        self.add_buttons(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)
-        self.set_position(gtk.WIN_POS_CENTER)
-        self.set_title('Error')
-        

vpackager/bottools.py

+#!/usr/bin/env python
+
+#    This file is part of vpackager.
+#
+#    vpackager is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License v2 as published by
+#    the Free Software Foundation.
+#
+#    vpackager 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 vpackager.  If not, see <http://www.gnu.org/licenses/>.
+
+
+import subprocess as sp
+import os
+import psutil
+import time
+
+__author__ = 'rbistolfi'
+__modified_by__ = 'M0E-lnx'
+__author_email__ = 'moc.liamg@xnl.E0M'[::-1]
+__version__ = '0.1'
+
+
+
+class Execute(object):
+    """ A wrapper for the Popen object that provides a run method for shell
+    processes and whose stdout and stderr can be read"""
+    def __init__(self, command, **kwargs):
+        self.command = command
+        self.stdout = None
+        self.popen = None
+        self.observer = Observer()
+        self._popen_running = False
+
+    def kill_popen(self, parentpid=None):
+        """ Recursively kill the self.popen process and all its children
+        processes."""
+        parent = psutil.Process(self.popen.pid)
+        for child in parent.get_children():
+            if child.cmdline[0] in ('make'):
+                killer = 'killall %s -u %s'% (child.cmdline[0], child.username)
+                proc = sp.Popen(killer.split(), stdout=sp.PIPE, stderr=sp.STDOUT)
+                proc.communicate()
+            else:
+                child.kill()
+
+
+    def run(self):
+        """ Trigger execution of self.command"""
+        self._popen_running = True
+        popen = self.popen = sp.Popen(
+                self.command.split(), stdout=sp.PIPE,
+                stderr=sp.STDOUT)
+        self.stdout = self.observer.set_file_object(
+                popen.stdout, close=True)
+        self.observer.notify()
+        self.popen.communicate()
+        self._popen_running = False
+
+class Emptyproc(Execute):
+    """ Just a dummy empty Execute class that does nothing but 'sleep 1'.
+    This is used to avoid returning a None or 0 value on methods of any source
+    type that do not support a given build method"""
+    def __init__(self):
+        Execute.__init__(self, 'sleep 1')
+
+class Observer(object):
+    """observer pattern for a file-like object"""
+
+    def __init__(self):
+        self.file_object = None
+        self.close_file = False
+        self._callbacks = set()
+
+    def add_callback(self, func):
+        """Add a callable function to the set of callbacks. Callbacks must take
+        a single argument: A line of the file object."""
+        self._callbacks.add(func)
+        return
+
+    def notify(self):
+        """ Return all the callbacks for each line of the file object"""
+        while True:
+            line = self.file_object.readline()
+            if not line:
+                break
+            for cb in self._callbacks:
+                cb(line)
+
+    def set_file_object(self, file_object, close=False):
+        """ Set the file object to observe. If close is True, the file object
+        will be closed with self.file_object.close()"""
+        self.file_object = file_object
+        self.close_file = close
+
+

vpackager/buildutils.py

+#!/usr/bin/env python
+
+#    This file is part of vpackager.
+#
+#    vpackager is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License v2 as published by
+#    the Free Software Foundation.
+#
+#    vpackager 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 vpackager.  If not, see <http://www.gnu.org/licenses/>.
+
+
+import os
+import bottools
+import glob
+import shutil
+import exceptions
+
+
+__author__ = 'M0E-lnx'
+__author_email__ = 'moc.liamg@xnl.E0M'[::-1]
+__version__ = '0.1'
+
+_sources_home = '/tmp/vpackager/'
+
+class SourceCopyError(Exception):
+    pass
+
+class SlackBuildCreationError(Exception):
+    pass
+
+class MissingSourcesHomeError(Exception):
+    pass
+
+
+def get_vltag():
+    ''' Return a string representing the distro tag for the installed system'''
+    vlconfig = glob.glob('/var/log/packages/vlconfig2*')[0]
+    if not vlconfig: return None
+    tag = vlconfig[-4:]
+    return tag
+
+class Source(object):
+    """ Main construct for all compressed source archives. Provides convinient
+    methods for extraction, compilation, and packaging different source
+    types"""
+
+    def __init__(self, path):
+        self.path = path
+        filename = os.path.split(path)[1]
+        if '-' in filename:
+            fnsplit = filename.rsplit('-', 2)
+        elif '_' in filename:
+            fnsplit = filename.rsplit('_', 2)
+        self.app = fnsplit[0]
+
+        if '.tar.' in fnsplit[1]:
+            self.ver = fnsplit[1].rsplit('.tar.', 2)[0]
+        else:
+            self.ver = fnsplit[1].rsplit('.', 2)[0]
+
+        self.srclocation = os.path.join(
+                _sources_home, self.app,
+                '%s-%s'% (self.app, self.ver))
+        self.__myhome = os.path.join(_sources_home, self.app)
+        self.pkg = os.path.join(
+                _sources_home, self.app,
+                'package_%s-%s'% (self.app, self.ver))
+        self.__dump = []
+        self.builder = self.FindSourceType()
+
+    def makeSlackBuild(self, buildno='1', packager='vpackager'):
+        """ Generate a slackbuild using sbbuilder to build this source"""
+        ### Use sbbuilder to create a slackbuild for his package.
+        ### The only diff from using this vs sbbuilder itself is that
+        ### This app can guess what type of slackbuild needs to be
+        ### created for the app to build.
+        _currdir = os.getcwd()
+        try:
+            os.makedirs(_sources_home)
+        except:
+            pass
+        try:
+            os.chdir(_sources_home)
+        except:
+            raise MissingSourcesHomeError
+
+        # call the sbbuilder binary
+        proc = bottools.Execute(
+            'sbbuilder --package=%s --version=%s --type=%s --build=%s --user=%s' %(
+                self.app, self.ver, self.builder.type,
+                buildno, packager))
+        proc.run()
+        if proc.popen.returncode > 0:
+            # the process failed
+            raise SlackBuildCreationError
+
+        self.__myhome = os.path.join(_sources_home, self.app)
+        self.srclocation = os.path.join(self.__myhome, self.ver, 'src')
+
+        # copy the source archive to the build dir
+        try:
+            shutil.copy2(self.path, self.srclocation)
+        except:
+            raise SourceCopyError
+        # Return to the original path
+        os.chdir(_currdir)
+
+        sbpath = os.path.join(
+            self.__myhome, self.ver, 'src', '%s.SlackBuild'% self.app)
+        if os.path.exists(sbpath):
+            return sbpath
+
+        return None
+
+    def buildSlackBuild(self):
+        """ Execute the build script generated by sbbuilder"""
+        return bottools.Execute('sh %s.SlackBuild'% self.app)
+
+    def Extract(self, srcdir=None):
+        """ Extract the contents of the source code. If srcdir is parsed, it
+        will be extracted there, otherwise, it defaults to /tmp/vpackager"""
+        if not srcdir:
+            srcdir = self.__myhome
+        cmd = 'tar xfv %s -C %s'% (self.path, srcdir)
+        return bottools.Execute(cmd)
+
+    def __get_dump(self, line):
+        """ Internal method to collect a list of the contents of the tarball"""
+        self.__dump.append('/'.join(line.strip().split('/')[1::]))
+
+    def FindSourceType(self):
+        """ Determine what type of slackbuild we need to generate for this
+        application to build."""
+        cmd = 'tar -tf %s'% self.path
+        proc = bottools.Execute(cmd)
+        proc.observer.add_callback(self.__get_dump)
+        proc.run()
+
+        if 'configure' in self.__dump:
+            self.builder = AutoTools(self)
+        elif 'autogen.sh' in self.__dump:
+            self.builder = AutoConf(self)
+        elif 'Makefile' in self.__dump:
+            self.builder = AutoMake(self)
+        elif 'CMakeLists.txt' in self.__dump:
+            self.builder = CMake(self)
+        elif 'setup.py' in self.__dump:
+            self.builder = Python()
+        self.__dump = None
+        return self.builder
+
+

vpackager/message.py

+#!/usr/bin/env python
+
+#    This file is part of vpackager.
+#
+#    vpackager is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License v2 as published by
+#    the Free Software Foundation.
+#
+#    vpackager 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 vpackager.  If not, see <http://www.gnu.org/licenses/>.
+
+
+import gtk
+
+__author__ = 'M0E-lnx'
+__author_email__ = 'moc.liamg@xnl.E0M'[::-1]
+__version__ = '0.1'
+
+
+def _(str):
+    return str
+
+class Error(gtk.MessageDialog):
+    ''' Generic gtk error message '''
+    def __init__(self, text, parent=None):
+        gtk.MessageDialog.__init__(self, parent = parent,
+            type = gtk.MESSAGE_ERROR,
+            buttons = gtk.BUTTONS_OK)
+        self.set_markup(text)
+        self.set_title(_('Error'))
+
+class Question(gtk.MessageDialog):
+    """ Question dialog that can return a gtk.RESPONSE_OK value"""
+    def __init__(self, text, parent=None):
+        gtk.MessageDialog.__init__(self, parent = parent,
+                type = gtk.MESSAGE_QUESTION,
+                buttons = gtk.BUTTONS_YES_NO)
+        self.set_markup(text)
+        self.set_title(_('Question'))
+
+class Info(gtk.MessageDialog):
+    """ Informational dialog message."""
+    def __init__(self, text, parent=None):
+        gtk.MessageDialog.__init__(self, parent=parent,
+                type = gtk.MESSAGE_INFO,
+                buttons = gtk.BUTTONS_OK)
+        self.set_markup(text)
+        self.set_title(_('For Your Information'))
+
+
+class Warning(gtk.MessageDialog):
+    """ Warning dialog box."""
+    def __init__(self, text, parent = None):
+        gtk.MessageDialog.__init__(self, parent = parent,
+                type = gtk.MESSAGE_WARNING,
+                buttons = gtk.BUTTONS_OK)
+        self.set_markup(text)
+        self.set_title(_('Warning'))

vpackager/outsample.py

+#!/usr/bin/env python
+
+import subprocess as sp
+import gtk
+import glib
+
+class CommandTextView(gtk.TextView):
+    """ Nice textview to display the output of shell processes"""
+    def __init__(self, command):
+        gtk.TextView.__init__(self)
+        self.command = command
+
+    def run(self):
+        proc = sp.Popen(self.command.split(), 
+                stdout=sp.PIPE, stderr=sp.STDOUT)
+        glib.io_add_watch(proc.stdout,
+                glib.IO_IN,
+                self.write_to_buffer)
+
+    def write_to_buffer(self, fd, condition):
+        if condition is glib.IO_IN:
+            char = fd.read(1)
+            gtk.gdk.threads_enter()
+            buf = self.get_buffer()
+            buf.insert_at_cursor(char)
+            gtk.gdk.threads_leave()
+            return True
+        else:
+            return False
+
+def main():
+    ctv = CommandTextView('find /home/vluser/')
+    win = gtk.Window(gtk.WINDOW_TOPLEVEL)
+    win.connect('delete-event', gtk.main_quit)
+    win.connect('destroy', gtk.main_quit)
+    win.set_size_request(200,300)
+    win.add(ctv)
+    win.show_all()
+    ctv.run()
+    gtk.main()
+
+if __name__ == '__main__':
+    gtk.gdk.threads_init()
+#    gtk.gdk.threads_enter()
+    main()

vpackager/vpackager.py

+#!/usr/bin/env python
+
+#    This file is part of vpackager.
+#
+#    vpackager is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License v2 as published by
+#    the Free Software Foundation.
+#
+#    vpackager 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 vpackager.  If not, see <http://www.gnu.org/licenses/>.
+
+
+
+import gtk
+import os
+import buildutils
+import bottools
+import user
+import message
+import time
+import threading
+
+__author__ = 'M0E-lnx'
+__author_email__ = 'moc.liamg@xnl.E0M'[::-1]
+__version__ = '0.1'
+
+""" Pygtk interface for vpackager. Consists of a wizard guiding the user
+though the stems necessary to perform a single package build."""
+
+def _(str):
+    return str
+
+class MissingSrcLocationDir(Exception):
+    pass
+
+class Build(object):
+    """ Generic Build project object representing a build session. This object
+    does nothing other than store some global variables and attributes for
+    the final steps to read"""
+    def __init__(self):
+        self.source = None
+        self.arch = None
+        self.release = '1'
+        self.tag = None
+        self.packager = 'packagebot'
+        self.buildoptions = []
+        self.cflags = ''
+        self.summary = ''
+        self.description = ''
+        self.url = ''
+        self.licence = ''
+
+class Page(gtk.VBox):
+    ''' Dedicated class based on gtk.VBox to be used as a wizard page
+    ARGUMENTS:
+        parent = parent wizard. Available via the ._parent attribute'''
+    def __init__(self, parent=None):
+        gtk.VBox.__init__(self)
+        self._parent = parent
+        self.set_border_width(4)
+        self.complete = False
+        self.type = gtk.ASSISTANT_PAGE_CONTENT
+        self.title = ''
+
+class PageVerify(Page):
+    """ Verify the build details. (Wizard page)"""
+    def __init__(self, parent=None):
+        Page.__init__(self, parent)
+        self.title = _('Verify Build Details')
+        self.type = gtk.ASSISTANT_PAGE_CONTENT
+        self.complete = True
+
+        self.pack_start(self.get_content(), True, True, 8)
+
+    def get_content(self):
+        '''Returns the content of this page'''
+        l = gtk.VBox()
+        txt = _('Ready to build')
+        lb = gtk.Label()
+        lb.set_use_markup(True)
+        lb.set_markup('<b> %s </b>'%txt)
+
+        txt = [
+            _('Please verify that all the information about the package is correct.'),
+            _('You may still go back and make any adjustments as necessary.'),
+            _('This is just dump. I dont know why this does not fill up the'),
+            _('entire label')
+        ]
+        lbl = gtk.Label()
+        lbl.set_property('justify', gtk.JUSTIFY_FILL)
+        lbl.set_property('wrap', True)
+        lbl.set_text(' '.join(txt).strip())
+
+        l.pack_start(lb, False, False, 4)
+        l.pack_start(lbl, False, False, 4)
+
+        lbr = gtk.Label()
+        lbr.set_use_markup(True)
+        lbr.set_markup(
+            '<b>%s</b>'% _('Click forward to continue')  )
+
+        l.pack_start(lbr, True, True, 12)
+        return l
+
+    def process_data(self):
+        #pass
+        return self._do_create_build()
+
+    def _do_create_build(self):
+        if self._parent.project.source.makeSlackBuild(
+            buildno = self._parent.project.release,
+            packager = self._parent.project.packager):
+            pass
+        else:
+            dia = message.Error(text = _('Error creating slackbuild'),
+                                         parent = self.parent)
+            if dia.run():
+                dia.destroy()
+
+class PageDoBuild(Page):
+    """ Build process. Wizard page displaying the build output"""
+    def __init__(self, parent=None):
+        Page.__init__(self, parent)
+        self.title = _('Compile Source')
+        self.type = gtk.ASSISTANT_PAGE_PROGRESS
+        self.pack_start(self.get_content(), True, True, 8)
+
+    def get_content(self):
+        ''' Return the content of this page'''
+        box = gtk.VBox()
+
+        self.pbar = gtk.ProgressBar()
+        box.pack_start(self.pbar, False, True, 8)
+
+
+        frmout = gtk.Frame(_('Output'))
+        sw = gtk.ScrolledWindow()
+        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        tview = gtk.TextView()
+        for edge in (gtk.TEXT_WINDOW_LEFT, gtk.TEXT_WINDOW_RIGHT,
+                     gtk.TEXT_WINDOW_TOP, gtk.TEXT_WINDOW_BOTTOM):
+            tview.set_border_window_size(edge, 4)
+
+        sw.add(tview)
+        frmout.add(sw)
+        self.stdoutview = tview
+        self.scrollw = sw
+        tview.set_property('editable', False)
+
+        box.pack_start(frmout, True, True, 8)
+        return box
+
+
+    def process_data(self):
+        #pass
+        if os.getuid() > 0:
+            dia = message.Error(
+                text = _('You must be root to use this application'),
+                parent = self._parent)
+            if dia.run():
+                dia.destroy()
+            return
+        return self._act()
+
+    def action(self):
+        _prevdir = os.getcwd()
+        try:
+            os.chdir(self._parent.project.source.srclocation)
+        except:
+            raise MissingSrcLocationDir
+        self._parent.task = self._parent.project.source.buildSlackBuild()
+        self._parent.task.observer.add_callback(self.update_textview)
+        # launch the task
+        self._parent.task.run()
+        self._parent.set_page_complete(self, True)
+        self.pbar.set_fraction(1.0)
+        self._parent.set_page_type(self, gtk.ASSISTANT_PAGE_SUMMARY)
+
+        try:
+            os.chdir(_prevdir)
+        except:
+            pass
+
+    def update_textview(self, line):
+        gtk.gdk.threads_enter()
+        buf = self.stdoutview.get_buffer()
+        buf.insert(buf.get_end_iter(), line)
+        vad = self.scrollw.get_vadjustment()
+
+        # Scroll the view
+        newpos = vad.get_upper() - vad.get_page_size()
+        vad.set_value(newpos)
+        self.scrollw.set_vadjustment(vad)
+
+        gtk.gdk.threads_leave()
+
+    def _pulse_bar(self):
+        self.pbar.set_text(
+            _('Compiling') + ' %s-%s'% (self._parent.project.source.app,
+                                        self._parent.project.source.ver)
+        )
+        while self._parent.task._popen_running:
+            gtk.gdk.threads_enter()
+            self.pbar.pulse()
+            gtk.gdk.threads_leave()
+            time.sleep(0.1)
+
+    def _act(self, widget=None):
+        tpopen = threading.Thread(target = self.action, args=())
+        tpulse = threading.Thread(target = self._pulse_bar, args=())
+
+        for t in (tpopen, tpulse):
+            t.start()
+
+class PageIntro(Page):
+    ''' Introduction page. Collect information about the build process.'''
+    def __init__(self, parent=None):
+        Page.__init__(self, parent)
+        self.title = _('Load source package')
+        self.type = gtk.ASSISTANT_PAGE_INTRO
+
+        self.pack_start(self.get_content(), False, True, 8)
+        self.pack_start(self.__pkgline1(), False, True, 4)
+        self.pack_start(self.__pkgline2(), False, True, 4)
+
+        self.pack_start(self.__pkgline4(), True, True, 4)
+        self.vboxDesc.set_property('sensitive', False)
+
+    def get_content(self):
+        ''' Return a container filled with stuff '''
+        frm = gtk.Frame(_('Source Archive'))
+        box = gtk.HBox()
+        self.srcentry = gtk.Entry()
+        box.pack_start(self.srcentry, True, True, 2)
+        btbrowse = gtk.Button(stock=gtk.STOCK_OPEN)
+        btbrowse.connect('clicked', self._browse)
+        box.pack_start(btbrowse, False, False, 2)
+        frm.add(box)
+        return frm
+
+    def _browse(self, widget=None):
+
+        dia = gtk.FileChooserDialog(
+            parent = self._parent,
+            buttons = (gtk.STOCK_CANCEL,
+                       gtk.RESPONSE_CANCEL,
+                       gtk.STOCK_OK,
+                       gtk.RESPONSE_OK),
+            title = _('Select source to build'))
+        filter = gtk.FileFilter()
+        filter.add_pattern('*.[bg]z*')
+        filter.set_name(_('Compressed Files'))
+        try:
+            dia.set_current_folder(os.path.join(user.home, 'Downloads'))
+        except:
+            pass
+        dia.add_filter(filter)
+        res = dia.run()
+        dia.hide()
+        if res == gtk.RESPONSE_OK:
+            self.srcentry.set_text(dia.get_filename())
+            self._src_change(dia.get_filename())
+        else:
+            self.srcentry.set_text('')
+
+        self._parent.set_page_complete(self, res == gtk.RESPONSE_OK)
+
+    def _src_change(self, newpath):
+        srcob = buildutils.Source(newpath)
+        if srcob.builder is not None:
+            self._parent.project.source = srcob
+            self.eName.set_text(srcob.app)
+            self.eVer.set_text(srcob.ver)
+        return
+
+    def _license_change(self, widget):
+
+        newl = widget.get_model()[widget.get_active()][0]
+        self._parent.project.license = newl
+
+    def _tag_change(self, widget):
+        newtag = widget.get_model()[widget.get_active()][0]
+        if newtag is 'None':
+            self._parent.project.tag = ''
+        else:
+            self._parent.project.tag = newtag
+
+    def _packager_change(self, widget):
+        self._parent.project.packager = widget.get_text()
+
+    def _release_change(self, widget):
+        self._parent.project.release = widget.get_text()
+
+    def __pkgline1(self):
+        l = gtk.HBox()
+        lName = gtk.Label(_("Application"))
+        self.eName = gtk.Entry()
+        l.pack_start(lName, False, False, 2)
+        l.pack_start(self.eName, True ,True, 2)
+
+        lVer = gtk.Label(_('Version'))
+        self.eVer = gtk.Entry()
+        l.pack_start(lVer, False, False, 2)
+        l.pack_start(self.eVer, True, True, 2)
+
+        return l
+
+    def __pkgline2(self):
+        l = gtk.HBox()
+
+        lRel = gtk.Label(_('Release'))
+        self.eRel = gtk.Entry(1)
+        self.eRel.connect('changed', self._release_change)
+        self.eRel.set_text('1')
+        self.eRel.set_property('width-chars', 4)
+
+        lPackager = gtk.Label(_('Packager'))
+        self.ePackager = gtk.Entry()
+        self.ePackager.connect('changed', self._packager_change)
+
+        l.pack_start(lRel, False, False, 2)
+        l.pack_start(self.eRel, True, True, 2)
+        l.pack_start(lPackager, False, False, 2)
+        l.pack_start(self.ePackager, True, True, 2)
+
+        return l
+
+    def __pkgline3(self):
+        l = gtk.HBox()
+
+        lSummary = gtk.Label(_('Summary'))
+        self.eSummary = gtk.Entry(70)
+
+        l.pack_start(lSummary, False, False, 2)
+        l.pack_start(self.eSummary, True, True, 2)
+
+        return l
+
+    def __pkgline4(self):
+        l = gtk.Frame(_('Package Description'))
+        h = gtk.HBox()
+        v = gtk.VBox()
+        l.add(v)
+        desc_choice = []
+        self.radLoadDesc = gtk.RadioButton(group=None,
+                                           label=_('Existing slack-desc'))
+        self.eDescPath = gtk.Entry()
+        self.btDescBrowse = gtk.Button(stock=gtk.STOCK_OPEN)
+        h.pack_start(self.radLoadDesc, False, False, 2)
+        h.pack_start(self.eDescPath, True, True, 2)
+        h.pack_start(self.btDescBrowse, False, False, 2)
+        v.pack_start(h, False, True, 4)
+
+        self.radTypeInDesc = gtk.RadioButton(group=self.radLoadDesc,
+                                             label=_('Write New'))
+        v.pack_start(self.radTypeInDesc, False, True, 2)
+        self.radLoadDesc.connect('toggled', self._toggle_desc_entry_mode)
+        self.vboxDesc = gtk.VBox()
+
+        self.vboxDesc.pack_start(self.__pkgline3(), False, True, 2)
+
+        self.descriptionArea = self.__pkgline5()
+        self.vboxDesc.pack_start(self.descriptionArea, True, True, 2)
+
+        v.pack_start(self.vboxDesc, True, True, 2)
+
+        return l
+
+    def _toggle_desc_entry_mode(self, widget):
+
+        for wid in (self.eDescPath,
+                    self.btDescBrowse):
+            wid.set_property('sensitive', widget.get_active())
+
+        self.vboxDesc.set_property('sensitive', not widget.get_active())
+
+    def __pkgline5(self):
+        f = gtk.Frame(_('Description (7 lines max.)'))
+        sw = gtk.ScrolledWindow()
+        f.add(sw)
+        self.descTextView = gtk.TextView()
+        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        sw.add(self.descTextView)
+        self.descTextView.get_buffer().connect('changed', self._desc_changed)
+        for edge in (gtk.TEXT_WINDOW_LEFT, gtk.TEXT_WINDOW_RIGHT,
+                     gtk.TEXT_WINDOW_TOP, gtk.TEXT_WINDOW_BOTTOM):
+            self.descTextView.set_border_window_size(edge, 4)
+
+        return f
+
+    def _desc_changed(self, widget=None):
+        #print widget.get_line_count()
+        if widget.get_line_count() > 7:
+            dia = message.Error(
+                text='7 Lines max please',
+                parent = self._parent)
+            if dia.run():
+                dia.destroy()
+                return
+        #print widget.get_insert().get_name()
+        iter = widget.get_end_iter()
+        if iter.get_chars_in_line() > 70:
+            print "WARNING: Line is too long"
+            if iter.backward_word_start():
+                widget.insert_at_cursor('\n')
+
+    def process_data(self):
+        pass
+        #print 'Processing data for intro page'
+
+class Wizard(gtk.Assistant):
+    """ Main application class"""
+    def __init__(self):
+        gtk.Assistant.__init__(self)
+        self.connect('destroy', self._exit)
+        self.connect('delete-event', self._exit)
+        self.connect('cancel', self._exit)
+        self.connect('close', self._exit)
+        self.connect('prepare', self._prepare)
+        self.set_property('title', 'vpackager')
+        self.set_size_request(600, 550)
+        self.set_position(gtk.WIN_POS_CENTER)
+        self.project = Build()
+
+        self.task = None
+
+
+        for page in (PageIntro(self),
+                     PageVerify(self),
+                     PageDoBuild(self)):
+            self.append_page(page)
+            self.set_page_title(page, page.title)
+            self.set_page_complete(page, page.complete)
+            self.set_page_type(page, page.type)
+
+        self.show_all()
+
+    def _force_stop(self):
+        dia = message.Question(
+                parent = self,
+                text = _('The build process is currently running.') \
+                        + " " + _('Are you sure you want to force it to stop?') \
+                        +'\n <b>' + _('WARNING:') + ' </b>'\
+                        +_('Quitting now will stop ALL build processes now.'))
+        res = dia.run()
+        dia.destroy()
+        return res ==  gtk.RESPONSE_YES
+
+    def _terminate(self):
+        if self.task:
+            if self.task._popen_running:
+                if not self._force_stop():
+                    return True
+        try:
+            self.task.kill_popen()
+        except:
+            pass
+        return False
+    def _exit(self, Widget = None, ud=None):
+        if self._terminate() is True:
+            return
+        self.set_page_complete(self.get_nth_page(
+            self.get_current_page()), True)
+        return gtk.main_quit()
+
+
+    def _prepare(self, assistant=None, page=None):
+        return page.process_data()
+
+
+
+if __name__ == '__main__':
+    w = Wizard()
+    gtk.gdk.threads_init()
+    gtk.main()
+