[jhbuild] Add systemmodule tag & check deps on build (GNOME bug 669554)



commit 2bed7087604b89dfbf1b30ffce10ec54d05d1428
Author: Craig Keogh <cskeogh adam com au>
Date:   Wed Jan 25 17:15:49 2012 +1030

    Add systemmodule tag & check deps on build (GNOME bug 669554)
    
    Add a systemmodule modtype. systemmodules are modules that must be
    provided by your system. 'jhbuild build' will raise an error if the
    systemmodule is not provided. systemmodules aim to remove the need for
    'sudo yum install libcurl-devel perl-XML-Simple ...' pre-setup.
    
    Use the 'jhbuild sysdeps' command to learn how your system meets the
    required dependencies.
    
    To turn off systemmodules, use --nodeps or set in ~/.jhbuildrc:
    check_sysdeps = False

 jhbuild/commands/__init__.py     |   35 +++++++++++++++
 jhbuild/commands/base.py         |   31 ++++++++++---
 jhbuild/commands/sysdeps.py      |   90 ++++++++++++++++++++++----------------
 jhbuild/commands/tinderbox.py    |   25 ++++++++--
 jhbuild/config.py                |    4 +-
 jhbuild/defaults.jhbuildrc       |    3 +
 jhbuild/modtypes/systemmodule.py |   36 +++++++++++++++
 jhbuild/moduleset.py             |   26 ++++++-----
 jhbuild/versioncontrol/system.py |   47 ++++++++++++++++++++
 9 files changed, 234 insertions(+), 63 deletions(-)
---
diff --git a/jhbuild/commands/__init__.py b/jhbuild/commands/__init__.py
index 210580d..4e34f8d 100644
--- a/jhbuild/commands/__init__.py
+++ b/jhbuild/commands/__init__.py
@@ -62,6 +62,41 @@ class Command:
         """The body of the command"""
         raise NotImplementedError
 
+class BuildCommand(Command):
+    """Abstract class for commands that build modules"""
+
+    def required_system_dependencies_installed(self, module_state):
+        '''Returns true if all required system dependencies are installed for
+        modules in module_state.'''
+        for pkg_config,(module, req_version, installed_version, new_enough, required_sysdep) in module_state.iteritems():
+            if required_sysdep:
+                if installed_version is None or not new_enough:
+                    return False
+        return True
+
+    def print_system_dependencies(self, module_state):
+        print _('Required packages:')
+        print _('  System installed packages which are too old:')
+        have_too_old = False
+        for pkg_config,(module, req_version, installed_version, new_enough, required_sysdep) in module_state.iteritems():
+            if (installed_version is not None) and (not new_enough) and required_sysdep:
+                have_too_old = True
+                print (_("    %(pkg)s (required=%(req)s, installed=%(installed)s)" % {'pkg': pkg_config,
+                                                                                      'req': req_version,
+                                                                                      'installed': installed_version}))
+        if not have_too_old:
+            print _('    (none)')
+
+        print _('  No matching system package installed:')
+        uninstalled = []
+        for pkg_config,(module, req_version, installed_version, new_enough, required_sysdep) in module_state.iteritems():
+            if installed_version is None and required_sysdep:
+                print (_("    %(pkg)s (required=%(req)s)") % {'pkg': pkg_config,
+                                                              'req': req_version})
+                uninstalled.append(pkg_config)
+        if len(uninstalled) == 0:
+            print _('    (none)')
+
 
 def print_help():
     import os
diff --git a/jhbuild/commands/base.py b/jhbuild/commands/base.py
index dd24e51..daeab73 100644
--- a/jhbuild/commands/base.py
+++ b/jhbuild/commands/base.py
@@ -28,7 +28,7 @@ import logging
 import jhbuild.moduleset
 import jhbuild.frontends
 from jhbuild.errors import UsageError, FatalError, CommandError
-from jhbuild.commands import Command, register_command
+from jhbuild.commands import Command, BuildCommand, register_command
 
 from jhbuild.config import parse_relative_time
 
@@ -151,7 +151,7 @@ class cmd_cleanone(Command):
 register_command(cmd_cleanone)
 
 
-class cmd_build(Command):
+class cmd_build(BuildCommand):
     doc = N_('Update and compile all modules (the default)')
 
     name = 'build'
@@ -213,6 +213,9 @@ class cmd_build(Command):
             make_option('--min-age', metavar='TIME-SPEC',
                         action='store', dest='min_age', default=None,
                         help=_('skip modules installed less than the given time ago')),
+            make_option('--nodeps',
+                        action='store_false', dest='check_sysdeps', default=True,
+                        help=_('ignore missing system dependencies')),
             ])
 
     def run(self, config, options, args, help=None):
@@ -220,10 +223,13 @@ class cmd_build(Command):
 
         module_set = jhbuild.moduleset.load(config)
         modules = args or config.modules
-        module_list = module_set.get_module_list(modules,
-                config.skip, tags=config.tags,
-                include_suggests=not config.ignore_suggests,
-                include_afters=options.build_optional_modules)
+        full_module_list = module_set.get_full_module_list \
+                               (modules, config.skip,
+                                include_suggests=not config.ignore_suggests,
+                                include_afters=options.build_optional_modules)
+        full_module_list = module_set.remove_tag_modules(full_module_list,
+                                                         config.tags)
+        module_list = module_set.remove_system_modules(full_module_list)
         # remove modules up to startat
         if options.startat:
             while module_list and module_list[0].name != options.startat:
@@ -236,13 +242,24 @@ class cmd_build(Command):
                     _('requested module is in the ignore list, nothing to do.'))
             return 0
 
+        module_state = module_set.get_system_modules(full_module_list)
+        if (config.check_sysdeps
+            and not self.required_system_dependencies_installed(module_state)):
+            self.print_system_dependencies(module_state)
+            raise FatalError(_('Required system dependencies not installed. '
+                               'Install using the command %(cmd)s or to '
+                               'ignore system dependencies use command-line '
+                               'option %(opt)s' \
+                               % {'cmd' : "'jhbuild sysdeps --install'",
+                                  'opt' : '--nodeps'}))
+
         build = jhbuild.frontends.get_buildscript(config, module_list, module_set=module_set)
         return build.build()
 
 register_command(cmd_build)
 
 
-class cmd_buildone(Command):
+class cmd_buildone(BuildCommand):
     doc = N_('Update and compile one or more modules')
 
     name = 'buildone'
diff --git a/jhbuild/commands/sysdeps.py b/jhbuild/commands/sysdeps.py
index 1ac0b38..76ab2a1 100644
--- a/jhbuild/commands/sysdeps.py
+++ b/jhbuild/commands/sysdeps.py
@@ -17,21 +17,14 @@
 # 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 sys
-import urllib
 from optparse import make_option
 import logging
 
 import jhbuild.moduleset
-import jhbuild.frontends
-from jhbuild.errors import UsageError, FatalError
+from jhbuild.errors import FatalError
 from jhbuild.commands import Command, register_command
-import jhbuild.commands.base
 from jhbuild.commands.base import cmd_build
-from jhbuild.utils.cmds import check_version
 from jhbuild.utils.systeminstall import SystemInstall
-from jhbuild.versioncontrol.tarball import TarballBranch
 
 class cmd_sysdeps(cmd_build):
     doc = N_('Check and install tarball dependencies using system packages')
@@ -47,9 +40,6 @@ class cmd_sysdeps(cmd_build):
     def run(self, config, options, args, help=None):
         config.set_from_cmdline_options(options)
 
-        if not config.partial_build:
-            raise FatalError(_("Partial build is not enabled; add partial_build = True to ~/.jhbuildrc"))
-
         module_set = jhbuild.moduleset.load(config)
         modules = args or config.modules
         module_list = module_set.get_full_module_list(modules)
@@ -59,44 +49,68 @@ class cmd_sysdeps(cmd_build):
         have_too_old = False
 
         print _('System installed packages which are new enough:')
-        for pkg_config,(module, req_version, installed_version, new_enough) in module_state.iteritems():
-            if (installed_version is not None) and new_enough:
+        for pkg_config,(module, req_version, installed_version, new_enough, required_sysdep) in module_state.iteritems():
+            if (installed_version is not None) and new_enough and (config.partial_build or required_sysdep):
                 have_new_enough = True
-                print (_("  %(pkg)s (required=%(req)s, installed=%(installed)s)" % {'pkg': pkg_config,
-                                                                                   'req': req_version,
-                                                                                   'installed': installed_version}))
+                print (_("    %(pkg)s (required=%(req)s, installed=%(installed)s)" % {'pkg': pkg_config,
+                                                                                      'req': req_version,
+                                                                                      'installed': installed_version}))
         if not have_new_enough:
             print _('  (none)')
 
-        print _('System installed packages which are too old:') 
-        for pkg_config,(module, req_version, installed_version, new_enough) in module_state.iteritems():
-            if (installed_version is not None) and (not new_enough):
+        print _('Required packages:')
+        print _('  System installed packages which are too old:')
+        for pkg_config,(module, req_version, installed_version, new_enough, required_sysdep) in module_state.iteritems():
+            if (installed_version is not None) and (not new_enough) and required_sysdep:
                 have_too_old = True
-                print (_("  %(pkg)s (required=%(req)s, installed=%(installed)s)" % {'pkg': pkg_config,
-                                                                                    'req': req_version,
-                                                                                    'installed': installed_version}))
+                print (_("    %(pkg)s (required=%(req)s, installed=%(installed)s)" % {'pkg': pkg_config,
+                                                                                      'req': req_version,
+                                                                                      'installed': installed_version}))
         if not have_too_old:
-            print _('  (none)')
-                
-        print _('No matching system package installed:')
+            print _('    (none)')
+
+        print _('  No matching system package installed:')
         uninstalled = []
-        for pkg_config,(module, req_version, installed_version, new_enough) in module_state.iteritems():
-            if installed_version is None:
-                print (_("  %(pkg)s (required=%(req)s)") % {'pkg': pkg_config,
-                                                            'req': req_version})
+        for pkg_config,(module, req_version, installed_version, new_enough, required_sysdep) in module_state.iteritems():
+            if installed_version is None and required_sysdep:
+                print (_("    %(pkg)s (required=%(req)s)") % {'pkg': pkg_config,
+                                                              'req': req_version})
                 uninstalled.append(pkg_config)
         if len(uninstalled) == 0:
-            print _('  (none)')
+            print _('    (none)')
 
-        if options.install:
-            installer = SystemInstall.find_best()
-            if installer is None:
-                raise FatalError(_("Don't know how to install packages on this system"))
+        have_too_old = False
+
+        if config.partial_build:
+            print _('Optional packages: (JHBuild will build the missing packages)')
+            print _('  System installed packages which are too old:')
+            for pkg_config,(module, req_version, installed_version, new_enough, required_sysdep) in module_state.iteritems():
+                if (installed_version is not None) and (not new_enough) and (not required_sysdep):
+                    have_too_old = True
+                    print (_("    %(pkg)s (required=%(req)s, installed=%(installed)s)" % {'pkg': pkg_config,
+                                                                                          'req': req_version,
+                                                                                          'installed': installed_version}))
+            if not have_too_old:
+                print _('    (none)')
 
+            print _('  No matching system package installed:')
+            for pkg_config,(module, req_version, installed_version, new_enough, required_sysdep) in module_state.iteritems():
+                if installed_version is None and (not required_sysdep):
+                    print (_("    %(pkg)s (required=%(req)s)") % {'pkg': pkg_config,
+                                                                  'req': req_version})
+                    uninstalled.append(pkg_config)
             if len(uninstalled) == 0:
-                logging.info(_("No uninstalled system dependencies to install for modules: %r" % (modules, )))
-            else:
-                logging.info(_("Installing dependencies on system: %s" % (' '.join(uninstalled), )))
-                installer.install(uninstalled)
+                print _('  (none)')
+
+            if options.install:
+                installer = SystemInstall.find_best()
+                if installer is None:
+                    raise FatalError(_("Don't know how to install packages on this system"))
+
+                if len(uninstalled) == 0:
+                    logging.info(_("No uninstalled system dependencies to install for modules: %r" % (modules, )))
+                else:
+                    logging.info(_("Installing dependencies on system: %s" % (' '.join(uninstalled), )))
+                    installer.install(uninstalled)
 
 register_command(cmd_sysdeps)
diff --git a/jhbuild/commands/tinderbox.py b/jhbuild/commands/tinderbox.py
index d102e3f..0080bd3 100644
--- a/jhbuild/commands/tinderbox.py
+++ b/jhbuild/commands/tinderbox.py
@@ -20,11 +20,11 @@
 from optparse import make_option
 
 from jhbuild.errors import UsageError, FatalError
-from jhbuild.commands import Command, register_command
+from jhbuild.commands import Command, BuildCommand, register_command
 import jhbuild.frontends
 
 
-class cmd_tinderbox(Command):
+class cmd_tinderbox(BuildCommand):
     doc = N_('Build modules non-interactively and store build logs')
 
     name = 'tinderbox'
@@ -61,7 +61,10 @@ class cmd_tinderbox(Command):
                         help=_("don't poison modules on failure")),
             make_option('-f', '--force',
                         action='store_true', dest='force_policy', default=False,
-                        help=_('build even if policy says not to'))
+                        help=_('build even if policy says not to')),
+            make_option('--nodeps',
+                        action='store_false', dest='check_sysdeps', default=True,
+                        help=_('ignore missing system dependencies'))
             ])
 
     def run(self, config, options, args, help=None):
@@ -75,8 +78,9 @@ class cmd_tinderbox(Command):
             raise UsageError(_('output directory for tinderbox build not specified'))
 
         module_set = jhbuild.moduleset.load(config)
-        module_list = module_set.get_module_list(args or config.modules,
-                                                 config.skip)
+        full_module_list = module_set.get_full_module_list \
+                               (args or config.modules, config.skip)
+        module_list = module_set.remove_system_modules(full_module_list)
 
         # remove modules up to startat
         if options.startat:
@@ -85,6 +89,17 @@ class cmd_tinderbox(Command):
             if not module_list:
                 raise FatalError(_('%s not in module list') % options.startat)
 
+        module_state = module_set.get_system_modules(full_module_list)
+        if (config.check_sysdeps
+            and not self.required_system_dependencies_installed(module_state)):
+            self.print_system_dependencies(module_state)
+            raise FatalError(_('Required system dependencies not installed. '
+                               'Install using the command %(cmd)s or to '
+                               'ignore system dependencies use command-line '
+                               'option %(opt)s' \
+                               % {'cmd' : "'jhbuild sysdeps --install'",
+                                  'opt' : '--nodeps'}))
+
         build = jhbuild.frontends.get_buildscript(config, module_list, module_set=module_set)
         return build.build()
 
diff --git a/jhbuild/config.py b/jhbuild/config.py
index 65eee5d..9dbcc62 100644
--- a/jhbuild/config.py
+++ b/jhbuild/config.py
@@ -62,7 +62,7 @@ _known_keys = [ 'moduleset', 'modules', 'skip', 'tags', 'prefix',
                 'module_mirror_policy', 'dvcs_mirror_dir', 'build_targets',
                 'cmakeargs', 'module_cmakeargs', 'print_command_pattern',
                 'static_analyzer', 'module_static_analyzer', 'static_analyzer_template', 'static_analyzer_outputdir',
-
+                'check_sysdeps',
                 # Internal only keys (propagated from command line options)
                 '_internal_noautogen',
                 ]
@@ -618,6 +618,8 @@ class Config:
             except ValueError:
                 raise FatalError(_('Failed to parse \'min_age\' relative '
                                    'time'))
+        if hasattr(options, 'check_sysdeps'):
+            self.check_sysdeps = options.check_sysdeps
 
     def __setattr__(self, k, v):
         '''Override __setattr__ for additional checks on some options.'''
diff --git a/jhbuild/defaults.jhbuildrc b/jhbuild/defaults.jhbuildrc
index 6cc0c32..f96a8c1 100644
--- a/jhbuild/defaults.jhbuildrc
+++ b/jhbuild/defaults.jhbuildrc
@@ -199,3 +199,6 @@ dvcs_mirror_dir = None
 # A string displayed before JHBuild executes a command. String may contain the
 # variables %(command)s, %(cwd)s
 print_command_pattern = '%(command)s'
+
+# Check system dependencies. If not dependencies not met, raise error
+check_sysdeps = True
diff --git a/jhbuild/modtypes/systemmodule.py b/jhbuild/modtypes/systemmodule.py
new file mode 100644
index 0000000..6a1ab27
--- /dev/null
+++ b/jhbuild/modtypes/systemmodule.py
@@ -0,0 +1,36 @@
+# jhbuild - a tool to ease building collections of source packages
+# Copyright (C) 2011-2012  Craig Keogh
+#
+#   systemmodule.py: systemmodule type definitions.
+#
+# 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
+
+__metaclass__ = type
+
+from jhbuild.modtypes import Package, register_module_type
+
+__all__ = [ 'SystemModule' ]
+
+
+class SystemModule(Package):
+    pass
+
+
+def parse_systemmodule(node, config, uri, repositories, default_repo):
+    instance = SystemModule.parse_from_xml(node, config, uri, repositories,
+                                           default_repo)
+    return instance
+
+register_module_type('systemmodule', parse_systemmodule)
diff --git a/jhbuild/moduleset.py b/jhbuild/moduleset.py
index e63fb14..7e952e6 100644
--- a/jhbuild/moduleset.py
+++ b/jhbuild/moduleset.py
@@ -39,6 +39,7 @@ from jhbuild.utils import httpcache
 from jhbuild.utils import packagedb
 from jhbuild.utils.cmds import compare_version, get_output
 from jhbuild.modtypes.testmodule import TestModule
+from jhbuild.modtypes.systemmodule import SystemModule
 from jhbuild.versioncontrol.tarball import TarballBranch
 from jhbuild.utils import systeminstall
 from jhbuild.utils import fileutils
@@ -177,7 +178,6 @@ class ModuleSet:
         return test_modules
 
     def get_system_modules(self, modules):
-        assert self.config.partial_build
 
         installed_pkgconfig = systeminstall.get_installed_pkgconfigs(self.config)
         
@@ -186,17 +186,17 @@ class ModuleSet:
         for module in modules:
             if module.pkg_config is None:
                 continue
-            if not isinstance(module.branch, TarballBranch):
-                continue
-            # Strip off the .pc
-            module_pkg = module.pkg_config[:-3]
-            required_version = module.branch.version
-            if not module_pkg in installed_pkgconfig:
-                module_state[module_pkg] = (module, required_version, None, False)
-            else:
-                installed_version = installed_pkgconfig[module_pkg]
-                new_enough = compare_version(installed_version, required_version)
-                module_state[module_pkg] = (module, required_version, installed_version, new_enough)
+            if (isinstance(module.branch, TarballBranch)
+                or isinstance(module, SystemModule)):
+                # Strip off the .pc
+                module_pkg = module.pkg_config[:-3]
+                required_version = module.branch.version
+                if not module_pkg in installed_pkgconfig:
+                    module_state[module_pkg] = (module, required_version, None, False, isinstance(module, SystemModule))
+                else:
+                    installed_version = installed_pkgconfig[module_pkg]
+                    new_enough = compare_version(installed_version, required_version)
+                    module_state[module_pkg] = (module, required_version, installed_version, new_enough, isinstance(module, SystemModule))
         return module_state
 
     def remove_system_modules(self, modules):
@@ -208,6 +208,8 @@ class ModuleSet:
         return_list = []
 
         for module in modules:
+            if isinstance(module, SystemModule):
+                continue
             skip = False
             if module.pkg_config is not None and \
             isinstance(module.branch, TarballBranch):
diff --git a/jhbuild/versioncontrol/system.py b/jhbuild/versioncontrol/system.py
new file mode 100644
index 0000000..3454299
--- /dev/null
+++ b/jhbuild/versioncontrol/system.py
@@ -0,0 +1,47 @@
+# jhbuild - a tool to ease building collections of source packages
+# Copyright (C) 2012  Craig Keogh
+#
+#   systemm.py: system module support code.
+#
+# 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
+
+from jhbuild.versioncontrol import Branch, Repository, register_repo_type
+from jhbuild.utils.sxml import sxml
+
+class SystemRepository(Repository):
+
+    branch_xml_attrs = ['version']
+
+    def branch(self, name, version, module = None,
+               checkoutdir = None):
+        instance = SystemBranch(self, module, version, checkoutdir)
+        return instance
+
+    def to_sxml(self):
+        return [sxml.repository(type='system', name=self.name)]
+
+class SystemBranch(Branch):
+
+    def __init__(self, repository, module, version, checkoutdir):
+        Branch.__init__(self, repository, module, checkoutdir)
+        self.version = version
+
+    def to_sxml(self):
+        return ([sxml.branch(module=self.module,
+                             repo=self.repository,
+                             version=self.version)])
+
+
+register_repo_type('system', SystemRepository)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]