Index: jhbuild/dbusservice.py
===================================================================
RCS file: jhbuild/dbusservice.py
diff -N jhbuild/dbusservice.py
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ jhbuild/dbusservice.py	24 Jul 2006 11:07:39 -0000
@@ -0,0 +1,361 @@
+# jhbuild - a build script for GNOME 1.x and 2.x
+# Copyright (C) 2001-2006  James Henstridge
+#
+#   base.py: the most common jhbuild commands
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import gobject
+import os
+import dbus
+import dbus.service
+import jhbuild.config
+if getattr(dbus, 'version', (0,0,0)) >= (0,41,0):
+    import dbus.glib
+import copy
+import time
+import threading
+import sys
+
+import jhbuild.moduleset
+import jhbuild.frontends
+from jhbuild.modtypes import MetaModule
+from jhbuild.modtypes.autotools import AutogenModule
+from jhbuild.modtypes.mozillamodule import MozillaModule
+from jhbuild.modtypes.tarball import Tarball
+from jhbuild.versioncontrol.cvs import CVSBranch
+from jhbuild.versioncontrol.svn import SubversionBranch
+from jhbuild.versioncontrol.arch import ArchBranch
+from jhbuild.versioncontrol.darcs import DarcsBranch
+from jhbuild.versioncontrol.git import GitBranch
+from jhbuild.versioncontrol.tarball import TarballBranch
+
+
+def get_info(module, packagedb, module_set):
+    result = {}
+    if isinstance(module, MozillaModule):
+        installdate = packagedb.installdate(module.name,
+                                            module.get_revision() or '')
+    elif isinstance(module, AutogenModule):
+        installdate = packagedb.installdate(module.name,
+                                            module.branch.branchname or '')
+    elif isinstance(module, Tarball):
+        installdate = packagedb.installdate(module.name,
+                                            module.version or '')
+    else:
+        installdate = packagedb.installdate(module.name)
+
+    result['Name'] = module.name
+    result['Type'] = module.type
+
+    if installdate is not None:
+        result['Install-date'] = time.strftime('%Y-%m-%d %H:%M:%S',
+                                               time.localtime(installdate))
+    else:
+        result['Install-date:'] = 'not installed'
+
+    if isinstance(module, MozillaModule):
+        result['CVS-Root:'] = module.repository.cvsroot
+        if module.revision is not None:
+            result['CVS-Revision'] = module.revision
+        if module.projects:
+            result['Moz-Projects'] = ', '.join(module.projects)
+    elif isinstance(module, AutogenModule):
+        if isinstance(module.branch, CVSBranch):
+            result['CVS-Root'] = module.branch.repository.cvsroot
+            result['CVS-Module'] = module.branch.module
+            if module.branch.revision:
+                result['CVS-Revision'] = module.branch.revision
+        elif isinstance(module.branch, SubversionBranch):
+            result['Subversion-Module'] = module.branch.module
+        elif isinstance(module.branch, ArchBranch):
+            result['Arch-Version'] = module.branch.module
+        elif isinstance(module.branch, DarcsBranch):
+            result['Darcs-Archive'] = module.branch.module
+        elif isinstance(module.branch, GitBranch):
+            result['Git-Module'] = module.branch.module
+        elif isinstance(module.branch, TarballBranch):
+            result['URL'] = module.branch.module
+            result['Version'] = module.branch.version
+        try:
+            tree_id = module.branch.tree_id()
+            result['Tree-ID'] = tree_id
+        except NotImplementedError:
+            pass
+    elif isinstance(module, Tarball):
+        result['URL'] = module.source_url
+        result['Version'] = module.version
+
+    # dependencies
+    if module.dependencies:
+        result['Requires'] = ', '.join(module.dependencies)
+    requiredby = [ mod.name for mod in module_set.modules.values()
+                   if module.name in mod.dependencies ]
+    if requiredby:
+        result['Required-by'] = ', '.join(requiredby)
+    if module.after:
+        result['After'] = ', '.join(module.after)
+    before = [ mod.name for mod in module_set.modules.values()
+               if module.name in mod.after ]
+    if before:
+        result['Before'] = ', '.join(before)
+
+    return result
+
+class JHBuildBuilderIdle(gobject.Idle):
+    def __init__(self, builder):
+        gobject.Idle.__init__(self)
+        self.set_callback(self.callback, builder)
+
+    def callback(self, builder):
+        builder.build()
+        return False
+
+class JHBuildObject(dbus.service.Object):
+    config = None
+    module = None
+    phase = None
+    command = None
+    def __init__(self, bus_name, object_path='/org/gnome/JHBuildObject', config=None):
+        self.config = config
+        self.config.buildscript='dbusfrontend'
+        self.config.server = self
+        self.set_idle()
+        dbus.service.Object.__init__(self, bus_name, object_path)
+
+    def set_idle(self):
+        self.module = None
+        self.phase = None
+        self.command = 'idle'
+
+    @dbus.service.method('org.gnome.JHBuildIFace')
+    def hello(self):
+        return 'Hello from JHBuild'
+
+    @dbus.service.method('org.gnome.JHBuildIFace')
+    def info(self, modulelist = []):
+        self.command='info'
+        result = {}
+        packagedb = jhbuild.frontends.get_buildscript(self.config, []).packagedb
+        module_set = jhbuild.moduleset.load(self.config)
+
+        for modname in modulelist:
+            try:
+                module = module_set.modules[modname]
+            except KeyError:
+                raise FatalError('unknown module %s' % modname)
+            result[modname] = get_info(module, packagedb, module_set)
+
+        self.set_idle()
+        return result
+        
+
+    @dbus.service.method('org.gnome.JHBuildIFace')
+    def list(self, modulelist = [], skiplist=[]):
+        self.command='list'
+        skip_list = []
+        skip_list += self.config.skip
+        skip_list += skiplist
+        module_set = jhbuild.moduleset.load(self.config)
+        module_list = module_set.get_module_list(modulelist or self.config.modules,
+                                                 skip_list)
+
+        result = []
+        for mod in module_list:
+            rev = mod.get_revision()
+            if rev:
+                tuple = (mod.name, rev)
+            else:
+                tuple = (mod.name, '')
+            result.append(tuple)
+        self.set_idle()
+        return result
+
+    @dbus.service.method('org.gnome.JHBuildIFace')
+    def update(self, modulelist = [], skiplist = [], start = None, datespec = None):
+        self.command='update'
+        ownconfig = copy.copy(self.config)
+
+        skip_list = []
+        skip_list += ownconfig.skip
+        skip_list += skiplist
+
+        module_set = jhbuild.moduleset.load(ownconfig)
+        module_list = module_set.get_module_list(modulelist or ownconfig.modules,
+                                                 skip_list)
+
+        if start:
+            while module_list and module_list[0].name != start:
+                del module_list[0]
+            if not module_list:
+                raise FatalError('%s not in module list' % start)
+
+        ownconfig.nobuild = True
+        ownconfig.nonetwork = False
+        
+        build = jhbuild.frontends.get_buildscript(ownconfig, module_list)
+        build.build()
+        idle = JHBuildBuilderIdle(build)
+        idle.attach()
+        return
+
+    @dbus.service.method('org.gnome.JHBuildIFace')
+    def updateone(self, modulelist = [], datespec = None):
+        self.command='updateone'
+        ownconfig = copy.copy(self.config)
+
+        module_set = jhbuild.moduleset.load(ownconfig)
+        try:
+            module_list = [module_set.modules[modname] for modname in modulelist]
+        except KeyError, e:
+            raise FatalError("A module called '%s' could not be found."
+                             % str(e))
+        
+        ownconfig.nobuild = True
+        ownconfig.nonetwork = False
+
+        build = jhbuild.frontends.get_buildscript(ownconfig, module_list)
+        build.build()
+        idle = JHBuildBuilderIdle(build)
+        idle.attach()
+        return
+
+    @dbus.service.method('org.gnome.JHBuildIFace')
+    def build(self, modulelist = [], skiplist = [], start = None, datespec = None, options = {}):
+        self.command='build'
+        ownconfig = copy.copy(self.config)
+        if options.get('autogen'):
+            ownconfig.alwaysautogen = True
+        if options.get('clean'):
+            ownconfig.makeclean = True
+        if options.get('nonetwork'):
+            ownconfig.nonetwork = True
+
+        skip_list = []
+        skip_list += ownconfig.skip
+        skip_list += skiplist
+
+        module_set = jhbuild.moduleset.load(ownconfig)
+        module_list = module_set.get_module_list(modulelist or ownconfig.modules,
+                                                 skip_list)
+
+        if start:
+            while module_list and module_list[0].name != start:
+                del module_list[0]
+            if not module_list:
+                raise FatalError('%s not in module list' % start)
+        
+        build = jhbuild.frontends.get_buildscript(ownconfig, module_list)
+
+        idle = JHBuildBuilderIdle(build)
+        idle.attach()
+        return
+
+    @dbus.service.method('org.gnome.JHBuildIFace')
+    def buildone(self, modulelist = [], datespec = None, options = {}):
+        self.command='buildone'
+        ownconfig = copy.copy(self.config)
+        if options.get('autogen'):
+            ownconfig.alwaysautogen = True
+        if options.get('clean'):
+            ownconfig.makeclean = True
+        if options.get('nonetwork'):
+            ownconfig.nonetwork = True
+
+        module_set = jhbuild.moduleset.load(ownconfig)
+        try:
+            module_list = [module_set.modules[modname] for modname in modulelist]
+        except KeyError, e:
+            raise FatalError("A module called '%s' could not be found."
+                             % str(e))
+        
+        build = jhbuild.frontends.get_buildscript(ownconfig, module_list)
+        idle = JHBuildBuilderIdle(build)
+        idle.attach()
+        return
+
+    @dbus.service.method('org.gnome.JHBuildIFace')
+    def set_autogen(self, enabled):
+        self.config.alwaysautogen = enabled
+
+    @dbus.service.method('org.gnome.JHBuildIFace')
+    def get_clean(self):
+        return dbus.Boolean(self.config.makeclean)
+
+    @dbus.service.method('org.gnome.JHBuildIFace')
+    def set_clean(self, enabled):
+        self.config.makeclean = enabled
+
+    @dbus.service.method('org.gnome.JHBuildIFace')
+    def get_no_network(self):
+        return dbus.Boolean(self.config.nonetwork)
+
+    @dbus.service.method('org.gnome.JHBuildIFace')
+    def set_no_network(self, enabled):
+        self.config.nonetwork = enabled
+
+    @dbus.service.method('org.gnome.JHBuildIFace')
+    def get_autogen(self):
+        return dbus.Boolean(self.config.alwaysautogen)
+
+    @dbus.service.method('org.gnome.JHBuildIFace')
+    def get_status(self):
+        return (dbus.String(self.command), dbus.String(self.module), dbus.String(self.phase))
+
+    @dbus.service.signal('org.gnome.JHBuildIFace')
+    def message_signal(self, message):
+        pass
+
+    @dbus.service.signal('org.gnome.JHBuildIFace')
+    def start_build_signal(self):
+        pass
+
+    @dbus.service.signal('org.gnome.JHBuildIFace')
+    def end_build_signal(self, failures):
+        pass
+
+    @dbus.service.signal('org.gnome.JHBuildIFace')
+    def start_module_signal(self, module):
+        pass
+
+    @dbus.service.signal('org.gnome.JHBuildIFace')
+    def end_module_signal(self, module, failed):
+        pass
+
+    @dbus.service.signal('org.gnome.JHBuildIFace')
+    def start_phase_signal(self, module, state):
+        pass
+
+    @dbus.service.signal('org.gnome.JHBuildIFace')
+    def end_phase_signal(self, module, state, error):
+        pass
+
+def run (config):
+    session_bus = dbus.SessionBus()
+    bus_name = dbus.service.BusName('org.gnome.JHBuild', bus=session_bus)
+    obj = JHBuildObject(bus_name, config=config)
+
+    dbus_id = bus_name.get_bus().get_connection().get_unique_name()
+
+    mainloop = gobject.MainLoop()
+    obj.config.mainloopcontext = mainloop.get_context()
+    
+    mainloop.run()
+#     pid = os.fork()
+#     if not pid:
+#         mainloop = gobject.MainLoop()
+#         mainloop.run()
+#     else:
+#         print dbus_id
Index: jhbuild/commands/base.py
===================================================================
RCS file: /cvs/gnome/jhbuild/jhbuild/commands/base.py,v
retrieving revision 1.12
diff -U5 -r1.12 base.py
--- jhbuild/commands/base.py	24 May 2006 05:37:27 -0000	1.12
+++ jhbuild/commands/base.py	24 Jul 2006 11:07:39 -0000
@@ -284,5 +284,16 @@
         else:
             modules = config.modules
         module_set.write_dot(modules)
 
 register_command(cmd_dot)
+
+class cmd_dbus(Command):
+    """Run JHBuild as a DBUS service"""
+
+    
+    name = 'dbus'
+    def run(self, config, options, args):
+        import jhbuild.dbusservice
+        jhbuild.dbusservice.run(config)
+
+register_command(cmd_dbus)
Index: jhbuild/frontends/dbusfrontend.py
===================================================================
RCS file: jhbuild/frontends/dbusfrontend.py
diff -N jhbuild/frontends/dbusfrontend.py
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ jhbuild/frontends/dbusfrontend.py	24 Jul 2006 11:07:39 -0000
@@ -0,0 +1,161 @@
+# jhbuild - a build script for GNOME 1.x and 2.x
+# Copyright (C) 2001-2006  James Henstridge
+#
+#   tinderbox.py: build logic for a non-interactive reporting build
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import os
+import time
+import subprocess
+import locale
+import dbus
+import fcntl
+import select
+import gobject
+import sys
+
+from jhbuild.utils import cmds
+from jhbuild.errors import CommandError
+import buildscript
+
+class DBusBuildScript(buildscript.BuildScript):
+    server = None
+    def __init__(self, config, module_list):
+        buildscript.BuildScript.__init__(self, config, module_list)
+        self.server = self.config.server
+
+    def _makeNonBlocking(self, fd):
+        fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NDELAY)
+
+    def _runEventLoop(self):
+        while self.config.mainloopcontext.pending():
+            self.config.mainloopcontext.iteration(0)
+        
+        
+    def timestamp(self):
+        tm = time.time()
+        s = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(tm))
+        msecs = max(int((tm - int(tm)) * 1000), 0)
+        return '%s.%03d' % (s, msecs)
+
+    def message(self, msg, module_num=-1):
+        '''Display a message to the user'''
+        self.server.message_signal(dbus.String(msg))
+
+    def set_action(self, action, module, module_num=-1, action_target=None):
+        if module_num == -1:
+            module_num = self.module_num
+        if not action_target:
+            action_target = module.name
+        self.message('%s %s' % (action, action_target), module_num)
+
+    def execute(self, command, hint=None, cwd=None, extra_env=None):
+        '''executes a command, and returns the error code'''
+
+        kws = {
+            'close_fds': True
+            }
+        if isinstance(command, (str, unicode)):
+            kws['shell'] = True
+        kws['stdin'] = subprocess.PIPE
+        kws['stdout'] = subprocess.PIPE
+        kws['stderr'] = subprocess.PIPE
+
+        if cwd is not None:
+            kws['cwd'] = cwd
+
+        if extra_env is not None:
+            kws['env'] = os.environ.copy()
+            kws['env'].update(extra_env)
+
+        try:
+            p = subprocess.Popen(command, **kws)
+        except OSError, e:
+            raise CommandError(str(e))
+
+        p.stdin.close()
+        self._makeNonBlocking(p.stdout)
+        self._makeNonBlocking(p.stderr)
+
+        read_set = [p.stdout, p.stderr]
+
+        quantum = 0
+        output = ''
+
+        while read_set:
+            rlist, wlist, xlist = select.select(read_set, [], [], 0)
+
+            if p.stdout in rlist:
+                chunk = p.stdout.read()
+                if chunk == '':
+                    p.stdout.close()
+                    read_set.remove(p.stdout)
+                output += chunk
+
+            if p.stderr in rlist:
+                chunk = p.stderr.read()
+                if chunk == '':
+                    p.stderr.close()
+                    read_set.remove(p.stderr)
+                output += chunk
+
+            time.sleep(0.05)
+            self._runEventLoop()
+            quantum += 1
+            if quantum >= 100:
+                self.message(output)
+                output = ''
+                quantum = 0
+        self.message(output)
+        return p.wait()
+
+    def start_build(self):
+        self.server.start_build_signal()
+
+    def end_build(self, failures):
+        self.server.end_build_signal(dbus.Array(failures or [], type=dbus.String))
+
+    def start_module(self, module):
+        self.server.module = module
+        self.server.start_module_signal(dbus.String(module))
+
+    def end_module(self, module, failed):
+        self.server.module = None
+        if failed:
+            self.message('Failed')
+        else:
+            self.message('Succeeded')
+        self.server.end_module_signal(dbus.String(module), failed)
+
+    def start_phase(self, module, state):
+        self.server.phase = state
+        self.server.start_phase_signal(dbus.String(module), dbus.String(state))
+    def end_phase(self, module, state, error):
+        self.server.phase = None
+        if error:
+            dbus_error = True
+        else:
+            dbus_error = False
+        self.server.end_phase_signal(dbus.String(module), dbus.String(state), dbus.Boolean(dbus_error))
+
+    def handle_error(self, module, state, nextstate, error, altstates):
+        '''handle error during build'''
+        self.message('error during stage %s of %s: %s' % (state, module.name,
+                                                          error))
+        return 'fail'
+
+BUILD_SCRIPT = DBusBuildScript
