aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGunnar Wrobel <p@rdus.de>2007-09-11 05:53:25 +0000
committerGunnar Wrobel <p@rdus.de>2007-09-11 05:53:25 +0000
commite4293652d5b2a80725e6f83843f91364e19ba199 (patch)
tree0c9aee330c4be51e249f1aa646a2603cd21635b5
downloadlayman-e4293652d5b2a80725e6f83843f91364e19ba199.tar.gz
layman-e4293652d5b2a80725e6f83843f91364e19ba199.tar.bz2
layman-e4293652d5b2a80725e6f83843f91364e19ba199.zip
Import layman.
-rw-r--r--.svn.ignore1
-rw-r--r--__init__.py1
-rw-r--r--action.py420
-rw-r--r--config.py300
-rw-r--r--db.py585
-rw-r--r--debug.py501
-rw-r--r--overlay.py247
-rw-r--r--overlays/.svn.ignore1
-rw-r--r--overlays/__init__.py1
-rw-r--r--overlays/bzr.py66
-rw-r--r--overlays/cvs.py73
-rw-r--r--overlays/darcs.py64
-rw-r--r--overlays/git.py63
-rw-r--r--overlays/mercurial.py64
-rw-r--r--overlays/overlay.py266
-rw-r--r--overlays/rsync.py68
-rw-r--r--overlays/svn.py65
-rw-r--r--overlays/tar.py189
-rw-r--r--tests/dtest.py90
-rw-r--r--tests/testfiles/global-overlays.xml30
-rw-r--r--tests/testfiles/layman-test.tar.bz2bin0 -> 845 bytes
-rw-r--r--tests/testfiles/make.conf345
-rw-r--r--utils.py208
-rw-r--r--version.py24
24 files changed, 3672 insertions, 0 deletions
diff --git a/.svn.ignore b/.svn.ignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/.svn.ignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/action.py b/action.py
new file mode 100644
index 0000000..b2c67ce
--- /dev/null
+++ b/action.py
@@ -0,0 +1,420 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN ACTIONS
+#################################################################################
+# File: action.py
+#
+# Handles layman actions.
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+#
+''' Provides the different actions that can be performed by layman.'''
+
+__version__ = "$Id: action.py 312 2007-04-09 19:45:49Z wrobel $"
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+import sys
+
+from layman.db import DB, RemoteDB
+
+from layman.debug import OUT
+
+#===============================================================================
+#
+# Class Fetch
+#
+#-------------------------------------------------------------------------------
+
+class Fetch:
+ ''' Fetches the overlay listing.
+
+ >>> import os
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> cache = os.tmpnam()
+ >>> config = {'overlays' :
+ ... 'file://' + here + '/tests/testfiles/global-overlays.xml',
+ ... 'cache' : cache,
+ ... 'nocheck' : True,
+ ... 'proxy' : None,
+ ... 'quietness':3}
+ >>> a = Fetch(config)
+ >>> a.run()
+ 0
+ >>> b = open(a.db.path(config['overlays']))
+ >>> b.readlines()[24]
+ ' A collection of ebuilds from Gunnar Wrobel [wrobel@gentoo.org].\\n'
+
+ >>> b.close()
+ >>> os.unlink(a.db.path(config['overlays']))
+
+ >>> a.db.overlays.keys()
+ [u'wrobel', u'wrobel-stable']
+ '''
+
+ def __init__(self, config):
+ self.db = RemoteDB(config)
+
+ def run(self):
+ '''Fetch the overlay listing.'''
+ try:
+ self.db.cache()
+ except Exception, error:
+ OUT.die('Failed to fetch overlay list!\nError was: '
+ + str(error))
+
+ return 0
+
+#===============================================================================
+#
+# Class Sync
+#
+#-------------------------------------------------------------------------------
+
+class Sync:
+ ''' Syncs the selected overlays.'''
+
+ def __init__(self, config):
+
+ self.db = DB(config)
+
+ self.rdb = RemoteDB(config)
+
+ self.selection = config['sync']
+
+ if config['sync_all'] or 'ALL' in self.selection:
+ self.selection = self.db.overlays.keys()
+
+ def run(self):
+ '''Synchronize the overlays.'''
+
+ OUT.debug('Updating selected overlays', 6)
+
+ warnings = []
+ success = []
+ for i in self.selection:
+ ordb = self.rdb.select(i)
+ odb = self.db.select(i)
+ if ordb and odb and ordb.src != odb.src:
+ warnings.append(
+ 'The source of the overlay "' + i + '" seems to have c'
+ 'hanged. You currently sync from "' + odb.src + '" whi'
+ 'le the global layman list reports "' + ordb.src + '" '
+ 'as correct location. Please consider removing and rea'
+ 'dding the overlay!')
+
+ try:
+ self.db.sync(i)
+ success.append('Successfully synchronized overlay "' + i + '".')
+ except Exception, error:
+ warnings.append(
+ 'Failed to sync overlay "' + i + '".\nError was: '
+ + str(error))
+
+ if success:
+ OUT.info('\nSuccess:\n------\n', 3)
+ for i in success:
+ OUT.info(i, 3)
+
+ if warnings:
+ OUT.warn('\nErrors:\n------\n', 2)
+ for i in warnings:
+ OUT.warn(i + '\n', 2)
+ return 1
+
+ return 0
+
+#===============================================================================
+#
+# Class Add
+#
+#-------------------------------------------------------------------------------
+
+class Add:
+ ''' Adds the selected overlays.'''
+
+ def __init__(self, config):
+
+ self.config = config
+
+ self.db = DB(config)
+
+ self.rdb = RemoteDB(config)
+
+ self.selection = config['add']
+
+ if 'ALL' in self.selection:
+ self.selection = self.rdb.overlays.keys()
+
+ def run(self):
+ '''Add the overlay.'''
+
+ OUT.debug('Adding selected overlays', 6)
+
+ result = 0
+
+ for i in self.selection:
+ overlay = self.rdb.select(i)
+
+ OUT.debug('Selected overlay', 7)
+
+ if overlay:
+ try:
+ self.db.add(overlay)
+ OUT.info('Successfully added overlay "' + i + '".', 2)
+ except Exception, error:
+ OUT.warn('Failed to add overlay "' + i + '".\nError was: '
+ + str(error), 2)
+ result = 1
+ else:
+ OUT.warn('Overlay "' + i + '" does not exist!', 2)
+ result = 1
+
+ return result
+
+#===============================================================================
+#
+# Class Delete
+#
+#-------------------------------------------------------------------------------
+
+class Delete:
+ ''' Deletes the selected overlays.'''
+
+ def __init__(self, config):
+
+ self.db = DB(config)
+
+ self.selection = config['delete']
+
+ if 'ALL' in self.selection:
+ self.selection = self.db.overlays.keys()
+
+ def run(self):
+ '''Delete the overlay.'''
+
+ OUT.debug('Deleting selected overlays', 6)
+
+ result = 0
+
+ for i in self.selection:
+ overlay = self.db.select(i)
+
+ OUT.debug('Selected overlay', 7)
+
+ if overlay:
+ try:
+ self.db.delete(overlay)
+ OUT.info('Successfully deleted overlay "' + i + '".', 2)
+ except Exception, error:
+ OUT.warn('Failed to delete overlay "' + i + '".\nError was: '
+ + str(error), 2)
+ result = 1
+ else:
+ OUT.warn('Overlay "' + i + '" does not exist!', 2)
+ result = 1
+
+ return result
+
+#===============================================================================
+#
+# Class List
+#
+#-------------------------------------------------------------------------------
+
+class List:
+ ''' Lists the available overlays.
+
+ >>> import os
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> cache = os.tmpnam()
+ >>> config = {'overlays' :
+ ... 'file://' + here + '/tests/testfiles/global-overlays.xml',
+ ... 'cache' : cache,
+ ... 'proxy' : None,
+ ... 'nocheck' : False,
+ ... 'verbose': False,
+ ... 'quietness':3}
+ >>> a = List(config)
+ >>> a.rdb.cache()
+ >>> OUT.color_off()
+ >>> a.run()
+ * wrobel [Subversion] (source: https://overlays.gentoo.or...)
+ 0
+ >>> a.config['verbose'] = True
+ >>> a.run()
+ * wrobel
+ * ~~~~~~
+ * Source : https://overlays.gentoo.org/svn/dev/wrobel
+ * Contact : nobody@gentoo.org
+ * Type : Subversion; Priority: 10
+ *
+ * Description:
+ * Test
+ *
+ * *** This is no official gentoo overlay ***
+ *
+ * wrobel-stable
+ * ~~~~~~~~~~~~~
+ * Source : rsync://gunnarwrobel.de/wrobel-stable
+ * Contact : nobody@gentoo.org
+ * Type : Rsync; Priority: 50
+ *
+ * Description:
+ * A collection of ebuilds from Gunnar Wrobel [wrobel@gentoo.org].
+ *
+ 0
+ '''
+
+ def __init__(self, config):
+
+ OUT.debug('Creating RemoteDB handler', 6)
+
+ self.rdb = RemoteDB(config)
+ self.config = config
+
+ def run(self):
+ ''' List the available overlays.'''
+
+ for i in self.rdb.list(self.config['verbose']):
+ # Is the overlay supported?
+ if i[1]:
+ # Is this an official overlay?
+ if i[2]:
+ OUT.info(i[0], 1)
+ # Unofficial overlays will only be listed if we are not
+ # checking or listing verbose
+ elif self.config['nocheck'] or self.config['verbose']:
+ # Give a reason why this is marked yellow if it is a verbose
+ # listing
+ if self.config['verbose']:
+ OUT.warn('*** This is no official gentoo overlay ***\n', 1)
+ OUT.warn(i[0], 1)
+ # Unsupported overlays will only be listed if we are not checking
+ # or listing verbose
+ elif self.config['nocheck'] or self.config['verbose']:
+ # Give a reason why this is marked red if it is a verbose
+ # listing
+ if self.config['verbose']:
+ OUT.error('*** You are lacking the necessary tools to insta'
+ 'll this overlay ***\n')
+ OUT.error(i[0])
+
+ return 0
+
+#===============================================================================
+#
+# Class ListLocal
+#
+#-------------------------------------------------------------------------------
+
+class ListLocal:
+ ''' Lists the local overlays.'''
+
+ def __init__(self, config):
+ self.db = DB(config)
+ self.config = config
+
+ def run(self):
+ '''List the overlays.'''
+
+ for i in self.db.list(self.config['verbose']):
+
+ OUT.debug('Printing local overlay.', 8)
+
+ # Is the overlay supported?
+ if i[1]:
+ # Is this an official overlay?
+ if i[2]:
+ OUT.info(i[0], 1)
+ # Unofficial overlays will only be listed if we are not
+ # checking or listing verbose
+ else:
+ # Give a reason why this is marked yellow if it is a verbose
+ # listing
+ if self.config['verbose']:
+ OUT.warn('*** This is no official gentoo overlay ***\n', 1)
+ OUT.warn(i[0], 1)
+ # Unsupported overlays will only be listed if we are not checking
+ # or listing verbose
+ else:
+ # Give a reason why this is marked red if it is a verbose
+ # listing
+ if self.config['verbose']:
+ OUT.error('*** You are lacking the necessary tools to insta'
+ 'll this overlay ***\n')
+ OUT.error(i[0])
+
+ return 0
+
+#===============================================================================
+#
+# Class Actions
+#
+#-------------------------------------------------------------------------------
+
+class Actions:
+ '''Dispatches to the actions the user selected. '''
+
+ # Given in order of precedence
+ actions = [('fetch', Fetch),
+ ('add', Add),
+ ('sync', Sync),
+ ('sync_all', Sync),
+ ('delete', Delete),
+ ('list', List),
+ ('list_local', ListLocal),]
+
+ def __init__(self, config):
+
+ # Make fetching the overlay list a default action
+ if not 'nofetch' in config.keys():
+ # Actions that implicitely call the fetch operation before
+ fetch_actions = ['fetch', 'sync', 'sync_all', 'list']
+ for i in fetch_actions:
+ if i in config.keys():
+ # Implicitely call fetch, break loop
+ Fetch(config).run()
+ break
+
+ result = 0
+
+ for i in self.actions:
+
+ OUT.debug('Checking for action', 7)
+
+ if i[0] in config.keys():
+ result += i[1](config).run()
+
+
+ if not result:
+ sys.exit(0)
+ else:
+ sys.exit(1)
+
+#===============================================================================
+#
+# Testing
+#
+#-------------------------------------------------------------------------------
+
+if __name__ == '__main__':
+ import doctest, sys
+
+ # Ignore warnings here. We are just testing
+ from warnings import filterwarnings, resetwarnings
+ filterwarnings('ignore')
+
+ doctest.testmod(sys.modules[__name__])
+
+ resetwarnings()
diff --git a/config.py b/config.py
new file mode 100644
index 0000000..287f223
--- /dev/null
+++ b/config.py
@@ -0,0 +1,300 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN CONFIGURATION
+#################################################################################
+# File: config.py
+#
+# Handles layman configuration
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+#
+'''Defines the configuration options and provides parsing functionality.'''
+
+__version__ = "$Id: config.py 286 2007-01-09 17:48:23Z wrobel $"
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+import sys, ConfigParser
+
+from optparse import OptionParser, OptionGroup
+from layman.debug import OUT
+from layman.version import VERSION
+
+#===============================================================================
+#
+# Class Config
+#
+#-------------------------------------------------------------------------------
+
+class Config(object):
+ '''Handles the configuration.'''
+
+ def __init__(self):
+ '''
+ Creates and describes all possible polymeraZe options and creates
+ a debugging object.
+
+ >>> import os.path
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> sys.argv.append('--config')
+ >>> sys.argv.append(here + '/../etc/layman.cfg')
+ >>> a = Config()
+ >>> a['overlays']
+ '\\nhttp://www.gentoo.org/proj/en/overlays/layman-global.txt'
+ >>> sorted(a.keys())
+ ['cache', 'config', 'config_dir', 'local_list', 'make_conf', 'nocheck', 'overlays', 'proxy', 'quietness', 'storage']
+ '''
+
+ self.defaults = {'config_dir': '/etc/layman',
+ 'config' : '/etc/layman/layman.cfg',
+ 'storage' : '/usr/portage/local/layman',
+ 'cache' : '%(storage)s/cache',
+ 'local_list': '%(storage)s/overlays.xml',
+ 'make_conf' : '%(storage)s/make.conf',
+ 'nocheck' : 'no',
+ 'proxy' : '',
+ 'overlays' :
+ 'http://www.gentoo.org/proj/en/overlays/layman-global.'
+ 'txt',}
+
+
+ self.parser = OptionParser(
+ usage = '\n\nlayman -a/-d/-S|overlay\nlayman -f [-o url]\nlayman' \
+ ' -l|-L',
+ version = VERSION)
+
+ #-----------------------------------------------------------------
+ # Main Options
+
+ group = OptionGroup(self.parser,
+ '<Actions>')
+
+ group.add_option('-a',
+ '--add',
+ action = 'append',
+ help = 'Add the given overlay from the cached remote li'
+ 'st to your locally installed overlays.. Specify "ALL" '
+ 'to add all overlays from the remote list.')
+
+ group.add_option('-d',
+ '--delete',
+ action = 'append',
+ help = 'Remove the given overlay from your locally inst'
+ 'alled overlays. Specify "ALL" to remove all overlays')
+
+ group.add_option('-s',
+ '--sync',
+ action = 'append',
+ help = 'Update the specified overlay. Use "ALL" as para'
+ 'meter to synchronize all overlays')
+
+ group.add_option('-S',
+ '--sync-all',
+ action = 'store_true',
+ help = 'Update all overlays.')
+
+ group.add_option('-L',
+ '--list',
+ action = 'store_true',
+ help = 'List the contents of the remote list.')
+
+ group.add_option('-l',
+ '--list-local',
+ action = 'store_true',
+ help = 'List the locally installed overlays.')
+
+ group.add_option('-f',
+ '--fetch',
+ action = 'store_true',
+ help = 'Fetch a remote list of overlays. This option is'
+ ' deprecated. The fetch operation will be performed by '
+ 'default when you run sync, sync-all, or list.')
+
+ group.add_option('-n',
+ '--nofetch',
+ action = 'store_true',
+ help = 'Do not fetch a remote list of overlays.')
+
+ group.add_option('-p',
+ '--priority',
+ action = 'store',
+ help = 'Use this with the --add switch to set the prior'
+ 'ity of the added overlay. This will influence the sort'
+ 'ing order of the overlays in the PORTDIR_OVERLAY varia'
+ 'ble.')
+
+ self.parser.add_option_group(group)
+
+ #-----------------------------------------------------------------
+ # Additional Options
+
+ group = OptionGroup(self.parser,
+ '<Path options>')
+
+ group.add_option('-c',
+ '--config',
+ action = 'store',
+ help = 'Path to the config file [default: ' \
+ + self.defaults['config'] + '].')
+
+ group.add_option('-o',
+ '--overlays',
+ action = 'append',
+ help = 'The list of overlays [default: ' \
+ + self.defaults['overlays'] + '].')
+
+ self.parser.add_option_group(group)
+
+ #-----------------------------------------------------------------
+ # Output Options
+
+ group = OptionGroup(self.parser,
+ '<Output options>')
+
+ group.add_option('-v',
+ '--verbose',
+ action = 'store_true',
+ help = 'Increase amount of output.')
+
+ group.add_option('-q',
+ '--quiet',
+ action = 'store_true',
+ help = 'Yield no output. Please be careful with this op'
+ 'tion: If the processes spawned by layman when adding o'
+ 'r synchronizing overlays require any input layman will'
+ ' hang without telling you why. This might happen for e'
+ 'xample if your overlay resides in subversion and the S'
+ 'SL certificate of the server needs acceptance.')
+
+ group.add_option('-Q',
+ '--quietness',
+ action = 'store',
+ type = 'int',
+ default = '4',
+ help = 'Set the level of output (0-4). Default: 4. Once'
+ ' you set this below 2 the same warning as given for --'
+ 'quiet applies! ')
+
+ group.add_option('-k',
+ '--nocheck',
+ action = 'store_true',
+ help = 'Do not check overlay definitions and do not i'
+ 'ssue a warning if description or contact information'
+ ' are missing.')
+
+ self.parser.add_option_group(group)
+
+ #-----------------------------------------------------------------
+ # Debug Options
+
+ OUT.cli_opts(self.parser)
+
+ # Parse the command line first since we need to get the config
+ # file option.
+ (self.options, args) = self.parser.parse_args()
+
+ # handle debugging
+ OUT.cli_handle(self.options)
+
+ # Fetch only an alternate config setting from the options
+ if not self.options.__dict__['config'] is None:
+ self.defaults['config'] = self.options.__dict__['config']
+
+ OUT.debug('Got config file at ' + self.defaults['config'], 8)
+
+ # Now parse the config file
+ self.config = ConfigParser.ConfigParser(self.defaults)
+ self.config.add_section('MAIN')
+
+ # handle quietness
+ if self['quiet']:
+ OUT.set_info_level(1)
+ OUT.set_warn_level(1)
+ self.defaults['quietness'] = 0
+ elif 'quietness' in self.keys():
+ OUT.set_info_level(int(self['quietness']))
+ OUT.set_warn_level(int(self['quietness']))
+
+ OUT.debug('Reading config file at ' + self.defaults['config'], 8)
+
+ self.config.read(self.defaults['config'])
+
+ def __getitem__(self, key):
+
+ if key == 'overlays':
+ overlays = ''
+ if (key in self.options.__dict__.keys()
+ and not self.options.__dict__[key] is None):
+ overlays = '\n'.join(self.options.__dict__[key])
+ if self.config.has_option('MAIN', 'overlays'):
+ overlays += '\n' + self.config.get('MAIN', 'overlays')
+ if overlays:
+ return overlays
+
+ OUT.debug('Retrieving option', 8)
+
+ if (key in self.options.__dict__.keys()
+ and not self.options.__dict__[key] is None):
+ return self.options.__dict__[key]
+
+ OUT.debug('Retrieving option', 8)
+
+ if self.config.has_option('MAIN', key):
+ if key == 'nocheck':
+ if self.config.get('MAIN', key).lower() == 'yes':
+ return True
+ else:
+ return False
+ return self.config.get('MAIN', key)
+
+ OUT.debug('Retrieving option', 8)
+
+ if key in self.defaults.keys():
+ return self.defaults[key]
+
+ OUT.debug('Retrieving option', 8)
+
+ return None
+
+ def keys(self):
+ '''Special handler for the configuration keys.'''
+
+ OUT.debug('Retrieving keys', 8)
+
+ keys = [i for i in self.options.__dict__.keys()
+ if not self.options.__dict__[i] is None]
+
+ OUT.debug('Retrieving keys', 8)
+
+ keys += [name for name, value in self.config.items('MAIN')
+ if not name in keys]
+
+ OUT.debug('Retrieving keys', 8)
+
+ keys += [i for i in self.defaults.keys()
+ if not i in keys]
+
+ OUT.debug('Retrieving keys', 8)
+
+ return keys
+
+
+#===============================================================================
+#
+# Testing
+#
+#-------------------------------------------------------------------------------
+
+if __name__ == '__main__':
+ import doctest
+ doctest.testmod(sys.modules[__name__])
diff --git a/db.py b/db.py
new file mode 100644
index 0000000..ae32114
--- /dev/null
+++ b/db.py
@@ -0,0 +1,585 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN OVERLAY DB
+#################################################################################
+# File: db.py
+#
+# Access to the db of overlays
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+#
+'''Handles different storage files.'''
+
+__version__ = "$Id: db.py 309 2007-04-09 16:23:38Z wrobel $"
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+import os, os.path, urllib2, re, md5
+
+from layman.utils import path
+from layman.overlay import Overlays
+
+from layman.debug import OUT
+
+#===============================================================================
+#
+# Class DB
+#
+#-------------------------------------------------------------------------------
+
+class DB(Overlays):
+ ''' Handle the list of local overlays.'''
+
+ def __init__(self, config):
+
+ self.config = config
+
+ self.path = config['local_list']
+
+ if config['nocheck']:
+ ignore = 2
+ else:
+ ignore = 1
+
+ quiet = int(config['quietness']) < 3
+
+ Overlays.__init__(self,
+ [config['local_list'], ],
+ ignore,
+ quiet)
+
+ OUT.debug('DB handler initiated', 6)
+
+ def add(self, overlay):
+ '''
+ Add an overlay to the local list of overlays.
+
+ >>> write = os.tmpnam()
+ >>> write2 = os.tmpnam()
+ >>> write3 = os.tmpnam()
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> config = {'local_list' :
+ ... here + '/tests/testfiles/global-overlays.xml',
+ ... 'make_conf' : write2,
+ ... 'nocheck' : True,
+ ... 'storage' : write3,
+ ... 'quietness':3}
+
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> a = DB(config)
+ >>> config['local_list'] = write
+ >>> b = DB(config)
+ >>> OUT.color_off()
+
+ >>> m = MakeConf(config, b.overlays)
+ >>> m.path = write2
+ >>> m.write()
+
+ Commented out since it needs network access:
+
+ # >>> b.add(a.select('wrobel-stable')) #doctest: +ELLIPSIS
+ # * Running command "/usr/bin/rsync -rlptDvz --progress --delete --delete-after --timeout=180 --exclude="distfiles/*" --exclude="local/*" --exclude="packages/*" "rsync://gunnarwrobel.de/wrobel-stable/*" "/tmp/file.../wrobel-stable""...
+ # >>> c = Overlays([write, ])
+ # >>> c.overlays.keys()
+ # [u'wrobel-stable']
+
+ # >>> m = MakeConf(config, b.overlays)
+ # >>> [i.name for i in m.overlays] #doctest: +ELLIPSIS
+ # [u'wrobel-stable']
+
+
+ # >>> os.unlink(write)
+ >>> os.unlink(write2)
+ >>> import shutil
+
+ # >>> shutil.rmtree(write3)
+ '''
+
+ if overlay.name not in self.overlays.keys():
+ result = overlay.add(self.config['storage'])
+ if result == 0:
+ if 'priority' in self.config.keys():
+ overlay.priority = int(self.config['priority'])
+ self.overlays[overlay.name] = overlay
+ self.write(self.path)
+ make_conf = MakeConf(self.config, self.overlays)
+ make_conf.add(overlay)
+ else:
+ overlay.delete(self.config['storage'])
+ raise Exception('Adding the overlay failed!')
+ else:
+ raise Exception('Overlay "' + overlay.name + '" already in the loca'
+ 'l list!')
+
+ def delete(self, overlay):
+ '''
+ Add an overlay to the local list of overlays.
+
+ >>> write = os.tmpnam()
+ >>> write2 = os.tmpnam()
+ >>> write3 = os.tmpnam()
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> config = {'local_list' :
+ ... here + '/tests/testfiles/global-overlays.xml',
+ ... 'make_conf' : write2,
+ ... 'nocheck' : True,
+ ... 'storage' : write3,
+ ... 'quietness':3}
+
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> a = DB(config)
+ >>> config['local_list'] = write
+ >>> b = DB(config)
+ >>> OUT.color_off()
+
+ >>> m = MakeConf(config, b.overlays)
+ >>> m.path = here + '/tests/testfiles/make.conf'
+ >>> m.read()
+
+ >>> m.path = write2
+ >>> m.write()
+
+ # >>> b.add(a.select('wrobel-stable')) #doctest: +ELLIPSIS
+ # * Running command "/usr/bin/rsync -rlptDvz --progress --delete --delete-after --timeout=180 --exclude="distfiles/*" --exclude="local/*" --exclude="packages/*" "rsync://gunnarwrobel.de/wrobel-stable/*" "/tmp/file.../wrobel-stable""...
+ # >>> b.add(a.select('wrobel')) #doctest: +ELLIPSIS
+ # * Running command "/usr/bin/svn co "https://overlays.gentoo.org/svn/dev/wrobel/" "/tmp/file.../wrobel""...
+ # >>> c = Overlays([write, ])
+ # >>> c.overlays.keys()
+ # [u'wrobel', u'wrobel-stable']
+
+ # >>> b.delete(b.select('wrobel'))
+ # >>> c = Overlays([write, ])
+ # >>> c.overlays.keys()
+ # [u'wrobel-stable']
+
+ # >>> m = MakeConf(config, b.overlays)
+ # >>> [i.name for i in m.overlays] #doctest: +ELLIPSIS
+ # [u'wrobel-stable']
+
+ # >>> os.unlink(write)
+ >>> os.unlink(write2)
+ >>> import shutil
+
+ # >>> shutil.rmtree(write3)
+ '''
+
+ if overlay.name in self.overlays.keys():
+ make_conf = MakeConf(self.config, self.overlays)
+ overlay.delete(self.config['storage'])
+ del self.overlays[overlay.name]
+ self.write(self.path)
+ make_conf.delete(overlay)
+ else:
+ raise Exception('No local overlay named "' + overlay.name + '"!')
+
+ def sync(self, overlay_name):
+ '''Synchronize the given overlay.'''
+
+ overlay = self.select(overlay_name)
+
+ if overlay:
+ result = overlay.sync(self.config['storage'])
+ if result:
+ raise Exception('Syncing overlay "' + overlay_name +
+ '" returned status ' + str(result) + '!')
+ else:
+ raise Exception('No such overlay ("' + overlay_name + '")!')
+
+#===============================================================================
+#
+# Class RemoteDB
+#
+#-------------------------------------------------------------------------------
+
+class RemoteDB(Overlays):
+ '''Handles fetching the remote overlay list.'''
+
+ def __init__(self, config):
+
+ self.config = config
+
+ self.proxies = {}
+
+ if config['proxy']:
+ self.proxies['http'] = config['proxy']
+ elif os.getenv('http_proxy'):
+ self.proxies['http'] = os.getenv('http_proxy')
+
+ if self.proxies:
+ proxy_handler = urllib2.ProxyHandler(self.proxies)
+ opener = urllib2.build_opener(proxy_handler)
+ urllib2.install_opener(opener)
+
+ self.urls = [i.strip() for i in config['overlays'].split('\n') if i]
+
+ paths = [self.path(i) for i in self.urls]
+
+ if config['nocheck']:
+ ignore = 2
+ else:
+ ignore = 0
+
+ quiet = int(config['quietness']) < 3
+
+ Overlays.__init__(self, paths, ignore, quiet)
+
+ def cache(self):
+ '''
+ Copy the remote overlay list to the local cache.
+
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> cache = os.tmpnam()
+ >>> config = {'overlays' :
+ ... 'file://' + here + '/tests/testfiles/global-overlays.xml',
+ ... 'cache' : cache,
+ ... 'nocheck' : True,
+ ... 'proxy' : None,
+ ... 'quietness':3}
+ >>> a = RemoteDB(config)
+ >>> a.cache()
+ >>> b = open(a.path(config['overlays']))
+ >>> b.readlines()[24]
+ ' A collection of ebuilds from Gunnar Wrobel [wrobel@gentoo.org].\\n'
+
+ >>> b.close()
+ >>> os.unlink(a.path(config['overlays']))
+
+ >>> a.overlays.keys()
+ [u'wrobel', u'wrobel-stable']
+ '''
+ for url in self.urls:
+
+ mpath = self.path(url)
+
+ try:
+
+ # Fetch the remote list
+ olist = urllib2.urlopen(url).read()
+
+ # Create our storage directory if it is missing
+ if not os.path.exists(os.path.dirname(mpath)):
+ try:
+ os.makedirs(os.path.dirname(mpath))
+ except OSError, error:
+ raise OSError('Failed to create layman storage direct'
+ + 'ory ' + os.path.dirname(mpath) + '\n'
+ + 'Error was:' + str(error))
+
+ # Before we overwrite the old cache, check that the downloaded
+ # file is intact and can be parsed
+ try:
+ self.read(olist)
+ except Exception, error:
+ raise IOError('Failed to parse the overlays list fetched fr'
+ 'om ' + url + '\nThis means that the download'
+ 'ed file is somehow corrupt or there was a pr'
+ 'oblem with the webserver. Check the content '
+ 'of the file. Error was:\n' + str(error))
+
+ # Ok, now we can overwrite the old cache
+ try:
+ out_file = open(mpath, 'w')
+ out_file.write(olist)
+ out_file.close()
+
+ except Exception, error:
+ raise IOError('Failed to temporarily cache overlays list in'
+ ' ' + mpath + '\nError was:\n' + str(error))
+
+
+ except IOError, error:
+ OUT.warn('Failed to update the overlay list from: '
+ + url + '\nError was:\n' + str(error))
+
+ try:
+ # Finally parse the contents of the cache
+ self.read_file(mpath)
+ except IOError, error:
+ OUT.warn('Failed to read a cached version of the overlay list f'
+ 'rom ' + url + '. You probably did not download the fi'
+ 'le before. The corresponding entry in your layman.cfg'
+ ' file will be disregarded.\nError was:\n' + str(error)
+ )
+
+ def path(self, url):
+ '''Return a unique file name for the url.'''
+
+ base = self.config['cache']
+
+ OUT.debug('Generating cache path.', 6)
+
+ return base + '_' + md5.md5(url).hexdigest() + '.xml'
+
+#===============================================================================
+#
+# Helper class MakeConf
+#
+#-------------------------------------------------------------------------------
+
+class MakeConf:
+ '''
+ Handles modifications to /etc/make.conf
+
+ Check that an add/remove cycle does not modify the make.conf:
+
+ >>> import md5
+ >>> write = os.tmpnam()
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> config = {'local_list' :
+ ... here + '/tests/testfiles/global-overlays.xml',
+ ... 'make_conf' : here + '/tests/testfiles/make.conf',
+ ... 'nocheck' : True,
+ ... 'storage' : '/usr/portage/local/layman',
+ ... 'quietness':3}
+ >>> b = DB(config)
+ >>> a = MakeConf(config, b.overlays)
+ >>> o_md5 = str(md5.md5(open(here + '/tests/testfiles/make.conf').read()).hexdigest())
+ >>> a.path = write
+ >>> a.add(b.overlays['wrobel-stable'])
+ >>> [i.name for i in a.overlays]
+ [u'wrobel-stable', u'wrobel-stable']
+ >>> a.add(b.overlays['wrobel'])
+ >>> [i.name for i in a.overlays]
+ [u'wrobel', u'wrobel-stable', u'wrobel-stable']
+ >>> a.delete(b.overlays['wrobel-stable'])
+ >>> [i.name for i in a.overlays]
+ [u'wrobel']
+ >>> a.add(b.overlays['wrobel-stable'])
+ >>> [i.name for i in a.overlays]
+ [u'wrobel', u'wrobel-stable']
+ >>> a.delete(b.overlays['wrobel'])
+ >>> n_md5 = str(md5.md5(open(write).read()).hexdigest())
+ >>> o_md5 == n_md5
+ True
+ >>> os.unlink(write)
+ '''
+
+ my_re = re.compile('PORTDIR_OVERLAY\s*=\s*"([^"]*)"')
+
+ def __init__(self, config, overlays):
+
+ self.path = config['make_conf']
+ self.storage = config['storage']
+ self.data = ''
+ self.db = overlays
+ self.overlays = []
+ self.extra = []
+
+ self.read()
+
+ def add(self, overlay):
+ '''
+ Add an overlay to make.conf.
+
+ >>> write = os.tmpnam()
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> config = {'local_list' :
+ ... here + '/tests/testfiles/global-overlays.xml',
+ ... 'make_conf' : here + '/tests/testfiles/make.conf',
+ ... 'nocheck' : True,
+ ... 'storage' : '/usr/portage/local/layman',
+ ... 'quietness':3}
+ >>> c = DB(config)
+ >>> a = MakeConf(config, c.overlays)
+ >>> a.path = write
+ >>> a.add(c.select('wrobel'))
+ >>> config['make_conf'] = write
+ >>> b = MakeConf(config, c.overlays)
+ >>> [i.name for i in b.overlays]
+ [u'wrobel', u'wrobel-stable']
+ >>> b.extra
+ ['/usr/portage/local/ebuilds/testing', '/usr/portage/local/ebuilds/stable', '/usr/portage/local/kolab2', '/usr/portage/local/gentoo-webapps-overlay/experimental', '/usr/portage/local/gentoo-webapps-overlay/production-ready']
+
+ >>> os.unlink(write)
+ '''
+ self.overlays.append(overlay)
+ self.write()
+
+ def delete(self, overlay):
+ '''
+ Delete an overlay from make.conf.
+
+ >>> write = os.tmpnam()
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> config = {'local_list' :
+ ... here + '/tests/testfiles/global-overlays.xml',
+ ... 'make_conf' : here + '/tests/testfiles/make.conf',
+ ... 'nocheck' : True,
+ ... 'storage' : '/usr/portage/local/layman',
+ ... 'quietness':3}
+ >>> c = DB(config)
+ >>> a = MakeConf(config, c.overlays)
+ >>> a.path = write
+ >>> a.delete(c.select('wrobel-stable'))
+ >>> config['make_conf'] = write
+ >>> b = MakeConf(config, c.overlays)
+ >>> [i.name for i in b.overlays]
+ []
+ >>> b.extra
+ ['/usr/portage/local/ebuilds/testing', '/usr/portage/local/ebuilds/stable', '/usr/portage/local/kolab2', '/usr/portage/local/gentoo-webapps-overlay/experimental', '/usr/portage/local/gentoo-webapps-overlay/production-ready']
+
+ >>> os.unlink(write)
+ '''
+ self.overlays = [i
+ for i in self.overlays
+ if i.name != overlay.name]
+ self.write()
+
+ def read(self):
+ '''
+ Read the list of registered overlays from /etc/make.conf.
+
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> config = {'local_list' :
+ ... here + '/tests/testfiles/global-overlays.xml',
+ ... 'make_conf' : here + '/tests/testfiles/make.conf',
+ ... 'nocheck' : True,
+ ... 'storage' : '/usr/portage/local/layman',
+ ... 'quietness':3}
+ >>> c = DB(config)
+ >>> a = MakeConf(config, c.overlays)
+ >>> [i.name for i in a.overlays]
+ [u'wrobel-stable']
+ >>> a.extra
+ ['/usr/portage/local/ebuilds/testing', '/usr/portage/local/ebuilds/stable', '/usr/portage/local/kolab2', '/usr/portage/local/gentoo-webapps-overlay/experimental', '/usr/portage/local/gentoo-webapps-overlay/production-ready']
+ '''
+ if os.path.isfile(self.path):
+ self.content()
+
+ overlays = self.my_re.search(self.data)
+
+ if not overlays:
+ raise Exception('Did not find a PORTDIR_OVERLAY entry in file ' +
+ self.path +'! Did you specify the correct file?')
+
+ overlays = [i.strip()
+ for i in overlays.group(1).split('\n')
+ if i.strip()]
+
+ for i in overlays:
+ if i[:len(self.storage)] == self.storage:
+ oname = os.path.basename(i)
+ if oname in self.db.keys():
+ self.overlays.append(self.db[oname])
+ else:
+ # These are additional overlays that we dont know
+ # anything about. The user probably added them manually
+ self.extra.append(i)
+ else:
+ # These are additional overlays that we dont know anything
+ # about. The user probably added them manually
+ self.extra.append(i)
+
+
+ else:
+ self.overlays = []
+ self.data = 'PORTDIR_OVERLAY="\n"\n'
+
+ self.extra = [i for i in self.extra
+ if (i != '$PORTDIR_OVERLAY'
+ and i != '${PORTDIR_OVERLAY}')]
+
+ def write(self):
+ '''
+ Write the list of registered overlays to /etc/make.conf.
+
+ >>> write = os.tmpnam()
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> config = {'local_list' :
+ ... here + '/tests/testfiles/global-overlays.xml',
+ ... 'make_conf' : here + '/tests/testfiles/make.conf',
+ ... 'nocheck' : True,
+ ... 'storage' : '/usr/portage/local/layman',
+ ... 'quietness':3}
+ >>> c = DB(config)
+ >>> a = MakeConf(config, c.overlays)
+ >>> a.path = write
+ >>> a.write()
+ >>> config['make_conf'] = write
+ >>> b = MakeConf(config, c.overlays)
+ >>> [i.name for i in b.overlays]
+ [u'wrobel-stable']
+ >>> b.extra
+ ['/usr/portage/local/ebuilds/testing', '/usr/portage/local/ebuilds/stable', '/usr/portage/local/kolab2', '/usr/portage/local/gentoo-webapps-overlay/experimental', '/usr/portage/local/gentoo-webapps-overlay/production-ready']
+
+ >>> os.unlink(write)
+ '''
+ def prio_sort(a, b):
+ '''Sort by priority.'''
+ if a.priority < b.priority:
+ return -1
+ elif a.priority > b.priority:
+ return 1
+ return 0
+
+ self.overlays.sort(prio_sort)
+
+ paths = []
+ for i in self.overlays:
+ paths.append(path((self.storage, i.name, )))
+
+ overlays = 'PORTDIR_OVERLAY="\n'
+ overlays += '\n'.join(paths) + '\n'
+ overlays += '$PORTDIR_OVERLAY\n'
+ overlays += '\n'.join(self.extra)
+ overlays += '"'
+
+ content = self.my_re.sub(overlays, self.data)
+
+ if not self.my_re.search(content):
+ raise Exception('Ups, failed to set a proper PORTDIR_OVERLAY entry '
+ 'in file ' + self.path +'! Did not overwrite the fi'
+ 'le.')
+
+ try:
+ make_conf = open(self.path, 'w')
+
+ make_conf.write(content)
+
+ make_conf.close()
+
+ except Exception, error:
+ raise Exception('Failed to read "' + self.path + '".\nError was:\n'
+ + str(error))
+
+ def content(self):
+ '''
+ Returns the content of the /etc/make.conf file.
+ '''
+ try:
+ make_conf = open(self.path)
+
+ self.data = make_conf.read()
+
+ make_conf.close()
+
+ except Exception, error:
+ raise Exception('Failed to read "' + self.path + '".\nError was:\n'
+ + str(error))
+
+#===============================================================================
+#
+# Testing
+#
+#-------------------------------------------------------------------------------
+
+if __name__ == '__main__':
+ import doctest, sys
+
+ # Ignore warnings here. We are just testing
+ from warnings import filterwarnings, resetwarnings
+ filterwarnings('ignore')
+
+ doctest.testmod(sys.modules[__name__])
+
+ resetwarnings()
diff --git a/debug.py b/debug.py
new file mode 100644
index 0000000..925f49c
--- /dev/null
+++ b/debug.py
@@ -0,0 +1,501 @@
+#################################################################################
+# LAYMAN - DEBUGGING FUNCTIONS
+#################################################################################
+# debug.py -- Utility function for debugging
+# Copyright 2005 - 2006 Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+
+__version__ = "$Id: debug.py 153 2006-06-05 06:03:16Z wrobel $"
+
+#################################################################################
+##
+## Dependancies
+##
+#################################################################################
+
+import sys, inspect
+
+from optparse import OptionGroup
+
+#################################################################################
+##
+## Color codes (taken from portage)
+##
+#################################################################################
+
+esc_seq = '\x1b['
+
+codes = {}
+codes['reset'] = esc_seq + '39;49;00m'
+codes['red'] = esc_seq + '31;01m'
+codes['green'] = esc_seq + '32;01m'
+codes['yellow'] = esc_seq + '33;01m'
+codes['turquoise'] = esc_seq + '36;01m'
+
+#################################################################################
+##
+## Message Class
+##
+#################################################################################
+
+class Message:
+ #FIXME: Think about some simple doctests before you modify this class the
+ # next time.
+
+ def __init__(self, module = '',
+ err = sys.stderr,
+ dbg = sys.stderr,
+ debugging_level = 4,
+ debugging_verbosity = 2,
+ info_level = 4,
+ warn_level = 4,
+ col = True,
+ mth = ['*'],
+ obj = ['*'],
+ var = ['*']):
+
+ # A description of the module that is being debugged
+ self.debug_env = module
+
+ # Where should the debugging output go? This can also be a file
+ self.debug_out = dbg
+
+ # Where should the error output go? This can also be a file
+ self.error_out = err
+
+ # The higher the level the more information you will get
+ self.warn_lev = warn_level
+
+ # The higher the level the more information you will get
+ self.info_lev = info_level
+
+ # The highest level of debugging messages acceptable for output
+ # The higher the level the more output you will get
+ self.debug_lev = debugging_level
+
+ # The debugging output can range from very verbose (3) to
+ # very compressed (1)
+ self.debug_vrb = debugging_verbosity
+
+ # Which methods should actually be debugged?
+ # Use '*' to indicate 'All methods'
+ self.debug_mth = mth
+
+ # Which objects should actually be debugged?
+ # Use '*' to indicate 'All objects'
+ self.debug_obj = obj
+
+ # Which variables should actually be debugged?
+ # Use '*' to indicate 'All variables'
+ self.debug_var = var
+
+ # Exclude class variables by default
+ self.show_class_variables = False
+
+ # Should the output be colored?
+ self.use_color = col
+
+ self.has_error = False
+
+
+ ############################################################################
+ # Add command line options
+
+ def cli_opts(self, parser):
+
+ group = OptionGroup(parser,
+ '<Debugging options>',
+ 'Control the debugging features of '
+ + self.debug_env)
+
+ group.add_option('--debug',
+ action = 'store_true',
+ help = 'Activates debugging features.')
+
+ group.add_option('--debug-level',
+ action = 'store',
+ type = 'int',
+ help = 'A value between 0 and 10. 0 means no debugging '
+ 'messages will be selected, 10 selects all debugging me'
+ 'ssages. Default is "4".')
+
+ group.add_option('--debug-verbose',
+ action = 'store',
+ type = 'int',
+ help = 'A value between 1 and 3. Lower values yield les'
+ 's verbose debugging output. Default is "2".')
+
+ group.add_option('--debug-methods',
+ action = 'store',
+ help = 'Limits the methods that will return debugging o'
+ 'utput. The function name is sufficient and there is no'
+ 'difference between class methods or general functions.'
+ ' Several methods can be specified by seperating them w'
+ ' with a comma. Default is "*" which specifies all meth'
+ 'ods.')
+
+ group.add_option('--debug-classes',
+ action = 'store',
+ help = 'Limits the classes that will return debugging o'
+ 'utput. Specify only the class name not including the m'
+ 'odules in which the class is defined (e.g. MyModule.ma'
+ 'in.Main should only be represented by "Main"). Several'
+ 'classes can be specified by seperating them with a com'
+ 'ma. Default is "*" which specifies all classes.')
+
+ group.add_option('--debug-variables',
+ action = 'store',
+ help = 'Limits the variables that will return debugging'
+ ' output. Several variables can be specified by seperat'
+ 'ing them with a comma. Default is "*" which specifies '
+ 'all variables.')
+
+ group.add_option('--debug-class-vars',
+ action = 'store_true',
+ help = 'In default mode the debugging code will only re'
+ 'turn information on the local variable which does not '
+ 'include the class variables. Use this switch to add al'
+ 'l values that are provided by "self".')
+
+ group.add_option('--debug-nocolor',
+ action = 'store_true',
+ help = 'Deactivates colors in the debugging output.')
+
+ parser.add_option_group(group)
+
+
+ #############################################################################
+ # Handle command line options
+
+ def cli_handle(self, options):
+
+ if (options.__dict__.has_key('debug')
+ and options.__dict__['debug']):
+ self.debug_on()
+ else:
+ self.debug_off()
+ return
+
+ if (options.__dict__.has_key('debug_class_vars')
+ and options.__dict__['debug_class_vars']):
+ self.class_variables_on()
+ else:
+ self.class_variables_off()
+
+ if (options.__dict__.has_key('debug_nocolor')
+ and options.__dict__['debug_nocolor']):
+ self.color_off()
+ else:
+ self.color_on()
+
+ if (options.__dict__.has_key('debug_level') and
+ options.__dict__['debug_level']):
+ dbglvl = int(options.__dict__['debug_level'])
+ if dbglvl < 0:
+ dbglvl = 0
+ if dbglvl > 10:
+ dbglvl = 10
+ self.set_debug_level(dbglvl)
+
+ if (options.__dict__.has_key('debug_verbose') and
+ options.__dict__['debug_verbose']):
+ dbgvrb = int(options.__dict__['debug_verbose'])
+ if dbgvrb < 1:
+ dbgvrb = 1
+ if dbgvrb > 3:
+ dbgvrb = 3
+ self.set_debug_verbosity(dbgvrb)
+
+ for i in [('debug_methods', self.set_debug_methods),
+ ('debug_classes', self.set_debug_classes),
+ ('debug_variables', self.set_debug_variables),]:
+
+ if (options.__dict__.has_key(i[0]) and
+ options.__dict__[i[0]]):
+ i[1](options.__dict__[i[0]])
+
+
+ #############################################################################
+ ## Helper Functions
+
+ def set_module(self, module):
+
+ self.debug_env = module
+
+ def set_debug_methods(self, methods):
+
+ methods = methods.split(',')
+
+ if methods:
+ self.debug_mth = methods
+
+ def set_debug_classes(self, classes):
+
+ classes = classes.split(',')
+
+ if classes:
+ self.debug_obj = classes
+
+ def set_debug_variables(self, variables):
+
+ variables = variables.split(',')
+
+ if variables:
+ self.debug_var = variables
+
+ def maybe_color (self, col, text):
+ if self.use_color:
+ return codes[col] + text + codes['reset']
+ return text
+
+ def set_info_level(self, info_level = 4):
+ self.info_lev = info_level
+
+ def info_off(self):
+ self.set_info_level(0)
+
+ def info_on(self, info_level = 4):
+ self.set_info_level(info_level)
+
+ def set_warn_level(self, warn_level = 4):
+ self.warn_lev = warn_level
+
+ def warn_off(self):
+ self.set_warn_level(0)
+
+ def warn_on(self, warn_level = 4):
+ self.set_warn_level(warn_level)
+
+ def set_debug_level(self, debugging_level = 4):
+ self.debug_lev = debugging_level
+
+ def set_debug_verbosity(self, debugging_verbosity = 2):
+ self.debug_vrb = debugging_verbosity
+
+ def debug_off(self):
+ self.set_debug_level(0)
+
+ def debug_on(self):
+ self.set_debug_level()
+
+ def color_off(self):
+ self.use_color = False
+
+ def color_on(self):
+ self.use_color = True
+
+ def class_variables_off(self):
+ self.show_class_variables = False
+
+ def class_variables_on(self):
+ self.show_class_variables = True
+
+ #############################################################################
+ ## Output Functions
+
+ def notice (self, note):
+ print note
+
+ def info (self, info, level = 4):
+
+ info = str(info)
+
+ if level > self.info_lev:
+ return
+
+ for i in info.split('\n'):
+ print self.maybe_color('green', '* ') + i
+
+ def status (self, message, status, info = 'ignored'):
+
+ message = str(message)
+
+ lines = message.split('\n')
+
+ if not lines:
+ return
+
+ for i in lines[0:-1]:
+ print self.maybe_color('green', '* ') + i
+
+ i = lines[-1]
+
+ if len(i) > 58:
+ i = i[0:57]
+
+ if status == 1:
+ result = '[' + self.maybe_color('green', 'ok') + ']'
+ elif status == 0:
+ result = '[' + self.maybe_color('red', 'failed') + ']'
+ else:
+ result = '[' + self.maybe_color('yellow', info) + ']'
+
+ print self.maybe_color('green', '* ') + i + ' ' + '.' * (58 - len(i)) \
+ + ' ' + result
+
+ def warn (self, warn, level = 4):
+
+ warn = str(warn)
+
+ if level > self.warn_lev:
+ return
+
+ for i in warn.split('\n'):
+ print self.maybe_color('yellow', '* ') + i
+
+ def error (self, error):
+
+ error = str(error)
+
+ for i in error.split('\n'):
+ print >> self.error_out, self.maybe_color('red', '* ') + i
+ self.has_error = True
+
+ def die (self, error):
+
+ error = str(error)
+
+ for i in error.split('\n'):
+ self.error(self.maybe_color('red', 'Fatal error: ') + i)
+ self.error(self.maybe_color('red', 'Fatal error(s) - aborting'))
+ sys.exit(1)
+
+ def debug (self, message, level = 4):
+ '''
+ This is a generic debugging method.
+ '''
+ ## Check the debug level first. This is the most inexpensive check.
+ if level > self.debug_lev:
+ return
+
+ ## Maybe this should be debugged. So get the stack first.
+ stack = inspect.stack()
+
+ ## This can probably never happen but does not harm to check
+ ## that there is actually something calling this function
+ if len(stack) < 2:
+ return
+
+ ## Get the stack length to determine indentation of the debugging output
+ stacklength = len(stack)
+ ls = ' ' * stacklength
+
+ ## Get the information about the caller
+ caller = stack[1]
+
+ ## The function name of the calling frame is the fourth item in the list
+ callermethod = caller[3]
+
+ ## Is this actually one of the methods that should be debugged?
+ if not '*' in self.debug_mth and not callermethod in self.debug_mth:
+ return
+
+ ## Still looks like this should be debugged. So retrieve the dictionary
+ ## of local variables from the caller
+ callerlocals = inspect.getargvalues(caller[0])[3]
+
+ ## Is the caller an obejct? If so he provides 'self'
+ if 'self' in callerlocals.keys():
+ callerobject = callerlocals['self']
+ del callerlocals['self']
+ if self.show_class_variables:
+ cv = inspect.getmembers(callerobject,
+ lambda x: not inspect.ismethod(x))
+ callerlocals.sync(cv)
+ else:
+ callerobject = None
+
+ # Remove variables not requested
+ if not '*' in self.debug_var:
+ callerlocals = dict([i for i in callerlocals.items()
+ if i[0] in self.debug_var])
+
+ ## Is the object among the list of objects to debug?
+ if (not '*' in self.debug_obj and
+ not str(callerobject.__class__.__name__) in self.debug_obj):
+ return
+
+ message = str(message)
+
+ def breaklines(x):
+ '''
+ Helper function to keep width of the debugging output.
+
+ This may look ugly for arrays but it is acceptable and not
+ breaking the line would break the output format
+ '''
+ ## Get the number of lines we need (rounded down)
+ lines = len(x) // 60
+ if lines > 0:
+ for j in range(lines):
+ ## Print line with continuation marker
+ print >> self.debug_out, ls + '// ' + x[0:60] + ' \\'
+ ## Remove printed characters from output
+ x = x[60:]
+ ## Print final line
+ print >> self.debug_out, ls + '// ' + x
+
+ if self.debug_vrb == 1:
+ # Top line indicates class and method
+ c = ''
+ if callerobject:
+ c += 'Class: ' + str(callerobject.__class__.__name__) + ' | '
+ if callermethod:
+ c += 'Method: ' + str(callermethod)
+ print >> self.debug_out, '// ' + c
+ # Selected variables follow
+ if callerlocals:
+ for i,j in callerlocals.items():
+ print >> self.debug_out, '// ' \
+ + self.maybe_color('turquoise', str(i)) + ':' + str(j)
+ # Finally the message
+ print >> self.debug_out, self.maybe_color('yellow', message)
+ return
+
+ if self.debug_vrb == 3:
+ print >> self.debug_out, ls + '/////////////////////////////////' + \
+ '////////////////////////////////'
+
+ # General information about what is being debugged
+ #(module name or similar)
+ print >> self.debug_out, ls + '// ' + self.debug_env
+ print >> self.debug_out, ls + '//-----------------------------------' + \
+ '----------------------------'
+
+ ## If the caller is a class print the name here
+ if callerobject:
+ print >> self.debug_out, ls + \
+ '// Object Class: ' + str(callerobject.__class__.__name__)
+
+ ## If the method has been extracted print it here
+ if callermethod:
+ print >> self.debug_out, ls + '// ' \
+ + self.maybe_color('green', 'Method: ') + str(callermethod)
+ if self.debug_vrb == 3:
+ print >> self.debug_out, ls + '//---------------------------' + \
+ '------------------------------------'
+
+ ## Print the information on all available local variables
+ if callerlocals:
+ if self.debug_vrb == 3:
+ print >> self.debug_out, ls + '//'
+ print >> self.debug_out, ls + '// VALUES '
+ for i,j in callerlocals.items():
+ print >> self.debug_out, ls + '// ------------------> ' \
+ + self.maybe_color('turquoise', str(i)) + ':'
+ breaklines(str(j))
+ if self.debug_vrb == 3:
+ print >> self.debug_out, ls + '//------------------------------'\
+ '---------------------------------'
+
+ # Finally print the message
+ breaklines(self.maybe_color('yellow', message))
+
+ if self.debug_vrb == 3:
+ print >> self.debug_out, ls + '//-------------------------------' + \
+ '--------------------------------'
+ print >> self.debug_out, ls + '/////////////////////////////////' + \
+ '////////////////////////////////'
+
+## gloabal message handler
+OUT = Message('layman')
diff --git a/overlay.py b/overlay.py
new file mode 100644
index 0000000..38e9364
--- /dev/null
+++ b/overlay.py
@@ -0,0 +1,247 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN OVERLAY HANDLER
+#################################################################################
+# File: overlay.py
+#
+# Access to an xml list of overlays
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+#
+'''Main handler for overlays.'''
+
+__version__ = "$Id: overlay.py 273 2006-12-30 15:54:50Z wrobel $"
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+import os, os.path, xml.dom.minidom
+
+from layman.overlays.bzr import BzrOverlay
+from layman.overlays.darcs import DarcsOverlay
+from layman.overlays.git import GitOverlay
+from layman.overlays.mercurial import MercurialOverlay
+from layman.overlays.cvs import CvsOverlay
+from layman.overlays.svn import SvnOverlay
+from layman.overlays.rsync import RsyncOverlay
+from layman.overlays.tar import TarOverlay
+
+from layman.debug import OUT
+
+#===============================================================================
+#
+# Constants
+#
+#-------------------------------------------------------------------------------
+
+OVERLAY_TYPES = {'git' : GitOverlay,
+ 'cvs' : CvsOverlay,
+ 'svn' : SvnOverlay,
+ 'rsync' : RsyncOverlay,
+ 'tar' : TarOverlay,
+ 'bzr' : BzrOverlay,
+ 'mercurial' : MercurialOverlay,
+ 'darcs' : DarcsOverlay}
+
+#===============================================================================
+#
+# Class Overlays
+#
+#-------------------------------------------------------------------------------
+
+class Overlays:
+ ''' Handle a list of overlays.'''
+
+ def __init__(self, paths, ignore = 0, quiet = False):
+
+ self.quiet = quiet
+ self.paths = paths
+ self.ignore = ignore
+
+ self.overlays = {}
+
+ OUT.debug('Initializing overlay list handler', 8)
+
+ for path in self.paths:
+ if os.path.exists(path):
+ self.read_file(path)
+
+ def read_file(self, path):
+ '''Read the overlay definition file.'''
+
+ try:
+
+ document = open(path).read()
+
+ except Exception, error:
+ raise IOError('Failed to read the overlay list at ("'
+ + path + '")!\nError was:\n' + str(error))
+
+
+ self.read(document)
+
+ def read(self, document):
+ '''
+ Read an xml list of overlays.
+
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+
+ >>> a = Overlays([here + '/tests/testfiles/global-overlays.xml', ])
+ >>> a.overlays.keys()
+ [u'wrobel', u'wrobel-stable']
+
+ >>> a.overlays['wrobel-stable'].data['&src']
+ u'rsync://gunnarwrobel.de/wrobel-stable'
+ '''
+ try:
+
+ document = xml.dom.minidom.parseString(document)
+
+ except Exception, error:
+ raise Exception('Failed to parse the overlay list!\nError was:\n'
+ + str(error))
+
+ overlays = document.getElementsByTagName('overlay')
+
+ for overlay in overlays:
+
+ OUT.debug('Parsing overlay entry', 8)
+
+ for index in range(0, overlay.attributes.length):
+ attr = overlay.attributes.item(index)
+ if attr.name == 'type':
+ if attr.nodeValue in OVERLAY_TYPES.keys():
+ try:
+ ovl = OVERLAY_TYPES[attr.nodeValue](overlay,
+ self.ignore,
+ self.quiet)
+ self.overlays[ovl.name] = ovl
+ except Exception, error:
+ OUT.warn(str(error), 3)
+ else:
+ raise Exception('Unknown overlay type "' +
+ attr.nodeValue + '"!')
+
+ def write(self, path):
+ '''
+ Write the list of overlays to a file.
+
+ >>> write = os.tmpnam()
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+
+ >>> a = Overlays([here + '/tests/testfiles/global-overlays.xml', ])
+ >>> b = Overlays([write,])
+ >>> b.overlays['wrobel-stable'] = a.overlays['wrobel-stable']
+ >>> b.write(write)
+ >>> c = Overlays([write,])
+ >>> c.overlays.keys()
+ [u'wrobel-stable']
+
+ >>> os.unlink(write)
+ '''
+
+ imp = xml.dom.minidom.getDOMImplementation()
+
+ doc = imp.createDocument('layman', 'overlays', None)
+
+ root = doc.childNodes[0]
+
+ for name, overlay in self.overlays.items():
+
+ root.appendChild(overlay.to_minidom(doc))
+
+ try:
+
+ out_file = open(path, 'w')
+
+ doc.writexml(out_file, '', ' ', '\n')
+
+ except Exception, error:
+ raise Exception('Failed to write to local overlays file: '
+ + path + '\nError was:\n' + str(error))
+
+ def select(self, overlay):
+ '''
+ Select an overlay from the list.
+
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> a = Overlays([here + '/tests/testfiles/global-overlays.xml', ])
+ >>> a.select('wrobel-stable').data['&src']
+ u'rsync://gunnarwrobel.de/wrobel-stable'
+ '''
+
+ if overlay in self.overlays.keys():
+ return self.overlays[overlay]
+
+ def list(self, verbose = False):
+ '''
+ List all overlays.
+
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> a = Overlays([here + '/tests/testfiles/global-overlays.xml', ])
+ >>> for i in a.list(True):
+ ... print i[0]
+ wrobel
+ ~~~~~~
+ Source : https://overlays.gentoo.org/svn/dev/wrobel
+ Contact : nobody@gentoo.org
+ Type : Subversion; Priority: 10
+ <BLANKLINE>
+ Description:
+ Test
+ <BLANKLINE>
+ wrobel-stable
+ ~~~~~~~~~~~~~
+ Source : rsync://gunnarwrobel.de/wrobel-stable
+ Contact : nobody@gentoo.org
+ Type : Rsync; Priority: 50
+ <BLANKLINE>
+ Description:
+ A collection of ebuilds from Gunnar Wrobel [wrobel@gentoo.org].
+ <BLANKLINE>
+
+ >>> for i in a.list(False):
+ ... print i[0]
+ wrobel [Subversion] (source: https://overlays.gentoo.or...)
+ wrobel-stable [Rsync ] (source: rsync://gunnarwrobel.de/wr...)
+ '''
+ result = []
+
+ for name, overlay in self.overlays.items():
+
+ if verbose:
+ result.append((str(overlay), overlay.is_supported(),
+ overlay.is_official()))
+ else:
+ result.append((overlay.short_list(), overlay.is_supported(),
+ overlay.is_official()))
+
+ result = sorted(result)
+
+ return result
+
+#===============================================================================
+#
+# Testing
+#
+#-------------------------------------------------------------------------------
+
+if __name__ == '__main__':
+ import doctest, sys
+
+ # Ignore warnings here. We are just testing
+ from warnings import filterwarnings, resetwarnings
+ filterwarnings('ignore')
+
+ doctest.testmod(sys.modules[__name__])
+
+ resetwarnings()
diff --git a/overlays/.svn.ignore b/overlays/.svn.ignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/overlays/.svn.ignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/overlays/__init__.py b/overlays/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/overlays/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/overlays/bzr.py b/overlays/bzr.py
new file mode 100644
index 0000000..8e8bb47
--- /dev/null
+++ b/overlays/bzr.py
@@ -0,0 +1,66 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN BZR OVERLAY HANDLER
+#################################################################################
+# File: bzr.py
+#
+# Handles bzr overlays
+#
+# Copyright:
+# (c) 2005 - 2006 Adrian Perez, Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Adrian Perez <moebius@connectical.net>
+# Gunnar Wrobel <wrobel@gentoo.org>
+#
+'''Should work with any version of Bzr equal to or better than 0.7 --
+ caution: tested only with 0.8 and 0.8.2...'''
+
+__version__ = "$Id: bzr.py 236 2006-09-05 20:39:37Z wrobel $"
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+from layman.utils import path
+from layman.overlays.overlay import Overlay
+
+#===============================================================================
+#
+# Class BzrOverlay
+#
+#-------------------------------------------------------------------------------
+
+class BzrOverlay(Overlay):
+ ''' Handles bzr overlays.'''
+
+ type = 'Bzr'
+
+ binary_command = '/usr/bin/bzr'
+
+ def add(self, base):
+ '''Add overlay.'''
+
+ self.supported()
+
+ return self.cmd(self.binary_command + ' get "' + self.src + '/" "' +\
+ path([base, self.name]) + '"')
+
+ def sync(self, base):
+ '''Sync overlay.'''
+
+ self.supported()
+
+ return self.cmd('cd "' + path([base, self.name]) + '" && ' + \
+ self.binary_command + ' pull --overwrite "' + self.src \
+ + '"')
+
+ def supported(self):
+ '''Overlay type supported?'''
+
+ return Overlay.supported(self, [(self.binary_command, 'bzr',
+ 'dev-util/bzr'),])
diff --git a/overlays/cvs.py b/overlays/cvs.py
new file mode 100644
index 0000000..95f20ea
--- /dev/null
+++ b/overlays/cvs.py
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN CVS OVERLAY HANDLER
+#################################################################################
+# File: cvs.py
+#
+# Handles cvs overlays
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+#
+''' Cvs overlay support.'''
+
+__version__ = "$Id$"
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+from layman.utils import path
+from layman.overlays.overlay import Overlay
+
+#===============================================================================
+#
+# Class CvsOverlay
+#
+#-------------------------------------------------------------------------------
+
+class CvsOverlay(Overlay):
+ ''' Handles cvs overlays.'''
+
+ type = 'cvs'
+
+ binary = '/usr/bin/cvs'
+
+ def __init__(self, xml, ignore = 0, quiet = False):
+
+ Overlay.__init__(self, xml, ignore, quiet)
+
+ if '&subpath' in self.data.keys():
+ self.subpath = self.data['&subpath']
+ else:
+ self.subpath = ''
+
+ def add(self, base):
+ '''Add overlay.'''
+
+ self.supported()
+
+ return self.cmd('cd "' + base + '" && CVSROOT="' + self.src + '" ' +
+ self.binary + ' co -d "' + self.name
+ + '" "' + self.subpath + '"' )
+
+ def sync(self, base):
+ '''Sync overlay.'''
+
+ self.supported()
+
+ return self.cmd('cd "' + path([base, self.name]) + '" && ' +
+ self.binary + ' update')
+
+ def supported(self):
+ '''Overlay type supported?'''
+
+ return Overlay.supported(self, [(self.binary, 'cvs',
+ 'dev-util/cvs'),])
diff --git a/overlays/darcs.py b/overlays/darcs.py
new file mode 100644
index 0000000..56e6d91
--- /dev/null
+++ b/overlays/darcs.py
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN DARCS OVERLAY HANDLER
+#################################################################################
+# File: darcs.py
+#
+# Handles darcs overlays
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel, Andres Loeh
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+# Andres Loeh <kosmikus@gentoo.org>
+#
+''' Darcs overlay support.'''
+
+__version__ = "$Id: darcs.py 236 2006-09-05 20:39:37Z wrobel $"
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+from layman.utils import path
+from layman.overlays.overlay import Overlay
+
+#===============================================================================
+#
+# Class BzrOverlay
+#
+#-------------------------------------------------------------------------------
+
+class DarcsOverlay(Overlay):
+ ''' Handles darcs overlays.'''
+
+ type = 'Darcs'
+
+ binary_command = '/usr/bin/darcs'
+
+ def add(self, base):
+ '''Add overlay.'''
+
+ self.supported()
+
+ return self.cmd(self.binary_command + ' get --partial "' + self.src +
+ '/" "' + path([base, self.name]) + '"')
+
+ def sync(self, base):
+ '''Sync overlay.'''
+
+ self.supported()
+
+ return self.cmd('cd "' + path([base, self.name]) + '" && ' +
+ self.binary_command + ' pull --all "' + self.src + '"')
+
+ def supported(self):
+ '''Overlay type supported?'''
+
+ return Overlay.supported(self, [(self.binary_command, 'darcs',
+ 'dev-util/darcs'),])
diff --git a/overlays/git.py b/overlays/git.py
new file mode 100644
index 0000000..007e841
--- /dev/null
+++ b/overlays/git.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN GIT OVERLAY HANDLER
+#################################################################################
+# File: git.py
+#
+# Handles git overlays
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel, Stefan Schweizer
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+# Stefan Schweizer <genstef@gentoo.org>
+''' Git overlay support.'''
+
+__version__ = "$Id: git.py 146 2006-05-27 09:52:36Z wrobel $"
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+from layman.utils import path
+from layman.overlays.overlay import Overlay
+
+#===============================================================================
+#
+# Class GitOverlay
+#
+#-------------------------------------------------------------------------------
+
+class GitOverlay(Overlay):
+ ''' Handles git overlays.'''
+
+ type = 'Git'
+
+ binary_command = '/usr/bin/git'
+
+ def add(self, base):
+ '''Add overlay.'''
+
+ self.supported()
+
+ return self.cmd(self.binary_command + ' clone "' + self.src + '/" "'
+ + path([base, self.name]) + '"')
+
+ def sync(self, base):
+ '''Sync overlay.'''
+
+ self.supported()
+
+ return self.cmd('cd "' + path([base, self.name]) + '" && '
+ + self.binary_command + ' pull')
+
+ def supported(self):
+ '''Overlay type supported?'''
+
+ return Overlay.supported(self, [(self.binary_command, 'git',
+ 'dev-util/git'),])
diff --git a/overlays/mercurial.py b/overlays/mercurial.py
new file mode 100644
index 0000000..3def5fc
--- /dev/null
+++ b/overlays/mercurial.py
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN MERCURIAL OVERLAY HANDLER
+#################################################################################
+# File: darcs.py
+#
+# Handles darcs overlays
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel, Andres Loeh
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+# Andres Loeh <kosmikus@gentoo.org>
+#
+''' Mercurial overlay support.'''
+
+__version__ = "$Id: mercurial.py 236 2006-09-05 20:39:37Z wrobel $"
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+from layman.utils import path
+from layman.overlays.overlay import Overlay
+
+#===============================================================================
+#
+# Class MercurialOverlay
+#
+#-------------------------------------------------------------------------------
+
+class MercurialOverlay(Overlay):
+ ''' Handles mercurial overlays.'''
+
+ type = 'Mercurial'
+
+ binary_command = '/usr/bin/hg'
+
+ def add(self, base):
+ '''Add overlay.'''
+
+ self.supported()
+
+ return self.cmd(self.binary_command + ' clone "' + self.src + '/" "' +
+ path([base, self.name]) + '"')
+
+ def sync(self, base):
+ '''Sync overlay.'''
+
+ self.supported()
+
+ return self.cmd('cd "' + path([base, self.name]) + '" && ' +
+ self.binary_command + ' pull -u "' + self.src + '"')
+
+ def supported(self):
+ '''Overlay type supported?'''
+
+ return Overlay.supported(self, [(self.binary_command, 'mercurial',
+ 'dev-util/mercurial'),])
diff --git a/overlays/overlay.py b/overlays/overlay.py
new file mode 100644
index 0000000..b7a006f
--- /dev/null
+++ b/overlays/overlay.py
@@ -0,0 +1,266 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN OVERLAY BASE CLASS
+#################################################################################
+# File: overlay.py
+#
+# Base class for the different overlay types.
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+#
+''' Basic overlay class.'''
+
+__version__ = "$Id: overlay.py 273 2006-12-30 15:54:50Z wrobel $"
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+import re, os, os.path, shutil, popen2
+
+from layman.utils import node_to_dict, dict_to_node, path
+
+from layman.debug import OUT
+
+#===============================================================================
+#
+# Class Overlay
+#
+#-------------------------------------------------------------------------------
+
+class Overlay:
+ ''' Derive the real implementations from this.'''
+
+ type = 'None'
+
+ def __init__(self, xml, ignore = 0, quiet = False):
+ '''
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> document = open(here + '/../tests/testfiles/global-overlays.xml').read()
+ >>> import xml.dom.minidom
+ >>> document = xml.dom.minidom.parseString(document)
+ >>> overlays = document.getElementsByTagName('overlay')
+ >>> a = Overlay(overlays[0])
+ >>> a.name
+ u'wrobel'
+ >>> a.is_official()
+ True
+ >>> a.src
+ u'https://overlays.gentoo.org/svn/dev/wrobel'
+ >>> a.contact
+ u'nobody@gentoo.org'
+ >>> a.description
+ u'Test'
+ >>> a.priority
+ 10
+ >>> b = Overlay(overlays[1])
+ >>> b.is_official()
+ False
+ '''
+ self.quiet = quiet
+
+ self.data = node_to_dict(xml)
+
+ if '&name' in self.data.keys():
+ self.name = self.data['&name']
+ else:
+ raise Exception('Overlay is missing a "name" attribute!')
+
+ if '&src' in self.data.keys():
+ self.src = self.data['&src']
+ else:
+ raise Exception('Overlay "' + self.name + '" is missing a "src" '
+ 'attribute!')
+
+ if '&contact' in self.data.keys():
+ self.contact = self.data['&contact']
+ else:
+ self.contact = ''
+ if not ignore:
+ raise Exception('Overlay "' + self.name + '" is missing a '
+ '"contact" attribute!')
+ elif ignore == 1:
+ OUT.warn('Overlay "' + self.name + '" is missing a '
+ '"contact" attribute!', 4)
+
+ if '<description>1' in self.data.keys():
+ self.description = self.data['<description>1']['@'].strip()
+ else:
+ self.description = ''
+ if not ignore:
+ raise Exception('Overlay "' + self.name + '" is missing a '
+ '"description" entry!')
+ elif ignore == 1:
+ OUT.warn('Overlay "' + self.name + '" is missing a '
+ '"description" entry!', 4)
+
+ if '&status' in self.data.keys():
+ self.status = self.data['&status']
+ else:
+ self.status = ''
+
+ if '&priority' in self.data.keys():
+ self.priority = int(self.data['&priority'])
+ else:
+ self.priority = 50
+
+ def to_minidom(self, document):
+ '''Convert to xml.'''
+
+ return dict_to_node(self.data, document, 'overlay')
+
+ def add(self, base):
+ '''Add the overlay.'''
+
+ mdir = path([base, self.name])
+
+ if os.path.exists(mdir):
+ raise Exception('Directory ' + mdir + ' already exists. Will not ov'
+ 'erwrite its contents!')
+
+ os.makedirs(mdir)
+
+ def sync(self, base):
+ '''Sync the overlay.'''
+ pass
+
+ def delete(self, base):
+ '''Delete the overlay.'''
+ mdir = path([base, self.name])
+
+ if not os.path.exists(mdir):
+ raise Exception('Directory ' + mdir + ' does not exist. Cannot remo'
+ 've the overlay!')
+
+ shutil.rmtree(mdir)
+
+ def cmd(self, command):
+ '''Run a command.'''
+
+ OUT.info('Running command "' + command + '"...', 2)
+
+ if not self.quiet:
+ return os.system(command)
+ else:
+ cmd = popen2.Popen4(command)
+ cmd.fromchild.readlines()
+ result = cmd.wait()
+ cmd.fromchild.readlines()
+ cmd.fromchild.close()
+ cmd.tochild.close()
+ return result
+
+ def __str__(self):
+ '''
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> document = open(here + '/../tests/testfiles/global-overlays.xml').read()
+ >>> import xml.dom.minidom
+ >>> document = xml.dom.minidom.parseString(document)
+ >>> overlays = document.getElementsByTagName('overlay')
+ >>> a = Overlay(overlays[0])
+ >>> print str(a)
+ wrobel
+ ~~~~~~
+ Source : https://overlays.gentoo.org/svn/dev/wrobel
+ Contact : nobody@gentoo.org
+ Type : None; Priority: 10
+ <BLANKLINE>
+ Description:
+ Test
+ <BLANKLINE>
+ '''
+
+ result = ''
+
+ result += self.name + '\n' + (len(self.name) * '~')
+
+ result += '\nSource : ' + self.src
+ result += '\nContact : ' + self.contact
+ result += '\nType : ' + self.type
+ result += '; Priority: ' + str(self.priority) + '\n'
+
+ description = self.description
+ description = re.compile(' +').sub(' ', description)
+ description = re.compile('\n ').sub('\n', description)
+ result += '\nDescription:'
+ result += '\n '.join(('\n' + description).split('\n'))
+ result += '\n'
+
+ if '<link>1' in self.data.keys():
+ link = self.data['<link>1']['@'].strip()
+ link = re.compile(' +').sub(' ', link)
+ link = re.compile('\n ').sub('\n', link)
+ result += '\nLink:\n'
+ result += '\n '.join(('\n' + link).split('\n'))
+ result += '\n'
+
+ return result
+
+ def short_list(self):
+ '''
+ >>> here = os.path.dirname(os.path.realpath(__file__))
+ >>> document = open(here + '/../tests/testfiles/global-overlays.xml').read()
+ >>> import xml.dom.minidom
+ >>> document = xml.dom.minidom.parseString(document)
+ >>> overlays = document.getElementsByTagName('overlay')
+ >>> a = Overlay(overlays[0])
+ >>> print a.short_list()
+ wrobel [None ] (source: https://overlays.gentoo.or...)
+ '''
+
+ def pad(string, length):
+ '''Pad a string with spaces.'''
+ if len(string) <= length:
+ return string + ' ' * (length - len(string))
+ else:
+ return string[:length - 3] + '...'
+
+ name = pad(self.name, 25)
+ mtype = ' [' + pad(self.type, 10) + ']'
+ source = ' (source: ' + pad(self.src, 29) + ')'
+
+ return name + mtype + source
+
+ def supported(self, binaries = []):
+ '''Is the overlay type supported?'''
+
+ if binaries:
+ for mpath, mtype, package in binaries:
+ if not os.path.exists(mpath):
+ raise Exception('Binary ' + mpath + ' seems to be missing!'
+ ' Overlay type "' + mtype + '" not support'
+ 'ed. Did you emerge ' + package + '?')
+
+ return True
+
+ def is_supported(self):
+ '''Is the overlay type supported?'''
+
+ try:
+ self.supported()
+ return True
+ except Exception, error:
+ return False
+
+ def is_official(self):
+ '''Is the overlay official?'''
+
+ return self.status == 'official'
+
+#================================================================================
+#
+# Testing
+#
+#--------------------------------------------------------------------------------
+
+if __name__ == '__main__':
+ import doctest, sys
+ doctest.testmod(sys.modules[__name__])
diff --git a/overlays/rsync.py b/overlays/rsync.py
new file mode 100644
index 0000000..e2483ad
--- /dev/null
+++ b/overlays/rsync.py
@@ -0,0 +1,68 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN RSYNC OVERLAY HANDLER
+#################################################################################
+# File: rsync.py
+#
+# Handles rsync overlays
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+#
+''' Rsync overlay support.'''
+
+__version__ = "$Id: rsync.py 236 2006-09-05 20:39:37Z wrobel $"
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+from layman.utils import path
+from layman.overlays.overlay import Overlay
+
+#===============================================================================
+#
+# Class RsyncOverlay
+#
+#-------------------------------------------------------------------------------
+
+class RsyncOverlay(Overlay):
+ ''' Handles rsync overlays.'''
+
+ type = 'Rsync'
+
+ binary = '/usr/bin/rsync'
+
+ base = binary + ' -rlptDvz --progress --delete --delete-after ' + \
+ '--timeout=180 --exclude="distfiles/*" --exclude="local/*" ' + \
+ '--exclude="packages/*" '
+
+ def add(self, base):
+ '''Add overlay.'''
+
+ self.supported()
+
+ Overlay.add(self, base)
+
+ return self.sync(base)
+
+ def sync(self, base):
+ '''Sync overlay.'''
+
+ self.supported()
+
+ return self.cmd(self.base + '"' + self.src + '/*" "' +
+ path([base, self.name]) + '"')
+
+ def supported(self):
+ '''Overlay type supported?'''
+
+ return Overlay.supported(self, [(self.binary, 'rsync',
+ 'net-misc/rsync'),])
diff --git a/overlays/svn.py b/overlays/svn.py
new file mode 100644
index 0000000..5086448
--- /dev/null
+++ b/overlays/svn.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN SVN OVERLAY HANDLER
+#################################################################################
+# File: svn.py
+#
+# Handles subversion overlays
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+#
+''' Subversion overlay support.'''
+
+__version__ = "$Id: svn.py 236 2006-09-05 20:39:37Z wrobel $"
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+from layman.utils import path
+from layman.overlays.overlay import Overlay
+
+#===============================================================================
+#
+# Class SvnOverlay
+#
+#-------------------------------------------------------------------------------
+
+class SvnOverlay(Overlay):
+ ''' Handles subversion overlays.'''
+
+ type = 'Subversion'
+
+ binary = '/usr/bin/svn'
+
+ def add(self, base):
+ '''Add overlay.'''
+
+ self.supported()
+
+ Overlay.add(self, base)
+
+ return self.cmd(self.binary + ' co "' + self.src + '/" "' +
+ path([base, self.name]) + '"')
+
+ def sync(self, base):
+ '''Sync overlay.'''
+
+ self.supported()
+
+ return self.cmd(self.binary + ' update "' + path([base, self.name]) +
+ '"')
+
+ def supported(self):
+ '''Overlay type supported?'''
+
+ return Overlay.supported(self, [(self.binary, 'svn',
+ 'dev-util/subversion'),])
diff --git a/overlays/tar.py b/overlays/tar.py
new file mode 100644
index 0000000..24a9b91
--- /dev/null
+++ b/overlays/tar.py
@@ -0,0 +1,189 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN TAR OVERLAY HANDLER
+#################################################################################
+# File: tar.py
+#
+# Handles tar overlays
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+#
+''' Tar overlay support.'''
+
+__version__ = "$Id: tar.py 310 2007-04-09 16:30:40Z wrobel $"
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+import os, os.path, sys, urllib2, shutil
+
+from layman.utils import path
+from layman.overlays.overlay import Overlay
+
+#===============================================================================
+#
+# Class TarOverlay
+#
+#-------------------------------------------------------------------------------
+
+class TarOverlay(Overlay):
+ ''' Handles tar overlays.
+
+ A dummy tar handler that overwrites the __init__ method
+ so that we don't need to provide xml input:
+
+ >>> from layman.debug import OUT
+ >>> class DummyTar(TarOverlay):
+ ... def __init__(self):
+ ... self.name = 'dummy'
+ ... here = os.path.dirname(os.path.realpath(__file__))
+ ... self.src = 'file://' + here + '/../tests/testfiles/layman-test.tar.bz2'
+ ... self.subpath = 'layman-test'
+ ... self.format = 'bz2'
+ ... self.quiet = False
+ >>> testdir = os.tmpnam()
+ >>> os.mkdir(testdir)
+ >>> a = DummyTar()
+ >>> OUT.color_off()
+ >>> a.add(testdir) #doctest: +ELLIPSIS
+ * Running command "/bin/tar -v -x -j -f...
+ >>> sorted(os.listdir(testdir + '/dummy'))
+ ['app-admin', 'app-portage']
+ >>> shutil.rmtree(testdir)
+ '''
+
+ type = 'Tar'
+
+ binary = '/bin/tar'
+
+ def __init__(self, xml, ignore = 0, quiet = False):
+
+ Overlay.__init__(self, xml, ignore)
+
+ if '&format' in self.data.keys():
+ self.format = self.data['&format']
+ else:
+ self.format = ''
+
+ if '&subpath' in self.data.keys():
+ self.subpath = self.data['&subpath']
+ else:
+ self.subpath = ''
+
+ if '&category' in self.data.keys():
+ if self.subpath:
+ raise Exception('Cannot use "category" and "subpath" at the same'
+ ' time!')
+
+ self.category = self.data['&category']
+ else:
+ self.category = ''
+
+ def add(self, base):
+ '''Add overlay.'''
+
+ self.supported()
+
+ mdir = path([base, self.name])
+
+ if os.path.exists(mdir):
+ raise Exception('Directory ' + mdir + ' already exists. Will not ov'
+ 'erwrite its contents!')
+
+ if self.format == 'bz2' or (not self.format and self.src[-3:] == 'bz2'):
+ ext = 'bz2'
+ opt = '-j'
+ elif self.format == 'gz' or (not self.format and self.src[-2:] == 'gz'):
+ ext = 'gz'
+ opt = '-z'
+ else:
+ raise Exception('Unsupported file format!')
+
+ try:
+
+ tar = urllib2.urlopen(self.src).read()
+
+ except Exception, error:
+ raise Exception('Failed to fetch the tar package from: '
+ + self.src + '\nError was:' + str(error))
+
+ pkg = path([base, self.name + '.tar.' + ext])
+
+ try:
+
+ out_file = open(pkg, 'w')
+ out_file.write(tar)
+ out_file.close()
+
+ except Exception, error:
+ raise Exception('Failed to store tar package in '
+ + pkg + '\nError was:' + str(error))
+
+ if self.subpath:
+ target = path([base, 'tmp'])
+ else:
+ if self.category:
+ target = mdir + '/' + self.category
+ else:
+ target = mdir
+
+ os.makedirs(target)
+
+ result = self.cmd(self.binary + ' -v -x ' + opt + ' -f "' + pkg
+ + '" -C "' + target + '"')
+
+ if self.subpath:
+ source = target + '/' + self.subpath
+ if os.path.exists(source):
+ try:
+ os.rename(source, mdir)
+ except Exception, error:
+ raise Exception('Failed to rename tar subdirectory ' +
+ source + ' to ' + mdir + '\nError was:'
+ + str(error))
+ else:
+ raise Exception('Given subpath "' + source + '" does not exist '
+ ' in the tar package!')
+ try:
+ shutil.rmtree(target)
+ except Exception, error:
+ raise Exception('Failed to remove unnecessary tar structure "'
+ + target + '"\nError was:' + str(error))
+
+ os.unlink(pkg)
+
+ return result
+
+ def sync(self, base):
+ '''Sync overlay.'''
+
+ self.supported()
+
+ self.delete(base)
+
+ self.add(base)
+
+ def supported(self):
+ '''Overlay type supported?'''
+
+ return Overlay.supported(self, [(self.binary, 'tar', 'app-arch/tar'), ])
+
+if __name__ == '__main__':
+ import doctest
+
+ # Ignore warnings here. We are just testing
+ from warnings import filterwarnings, resetwarnings
+ filterwarnings('ignore')
+
+ doctest.testmod(sys.modules[__name__])
+
+ resetwarnings()
diff --git a/tests/dtest.py b/tests/dtest.py
new file mode 100644
index 0000000..8f65af1
--- /dev/null
+++ b/tests/dtest.py
@@ -0,0 +1,90 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN DOCTEST AGGREGATOR
+#################################################################################
+# File: dtest.py
+#
+# Combines the doctests that are available for the different modules
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+#
+'''Aggregates doctests from all modules that provide such tests.'''
+
+__version__ = '$Id: dtest.py 237 2006-09-05 21:18:54Z wrobel $'
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+import unittest, doctest, sys
+
+# On module creation:
+
+# 1.) Check header section (copyright notice)
+# 2.) Add module doc string
+# 3.) Add version string
+# 4.) Add testing handler at bottom of module
+# 5.) Add module into tests/dtest.py. Check that tests run through
+# 6.) Run pylint over the code. Fix any reasonable complaints.
+# 7.) Whitespace clean the buffer.
+# 8.) Add svn:keywords "Id" to file.
+
+# On module change:
+
+# 1.) Check header section (copyright notice)
+# 5.) Check that tests run through
+# 6.) Run pylint over the code. Fix any reasonable complaints.
+# 7.) Whitespace clean the buffer.
+
+# clean modules : CT
+# not yet clean : UT
+# clean but no testing : CN
+# unclean but no testing: UN
+
+import layman.action #CT
+import layman.config #CT
+import layman.db #CT
+import layman.overlay #CT
+import layman.utils #CT
+import layman.overlays.overlay #CT
+import layman.overlays.tar #CT
+
+#===============================================================================
+#
+# Test Suite
+#
+#-------------------------------------------------------------------------------
+
+def test_suite():
+ return unittest.TestSuite((
+ doctest.DocTestSuite(layman.action),
+ doctest.DocTestSuite(layman.config),
+ doctest.DocTestSuite(layman.db),
+ doctest.DocTestSuite(layman.overlay),
+ doctest.DocTestSuite(layman.utils),
+ doctest.DocTestSuite(layman.overlays.overlay),
+ doctest.DocTestSuite(layman.overlays.tar),
+ ))
+
+#===============================================================================
+#
+# Run Testing
+#
+#-------------------------------------------------------------------------------
+
+if __name__ == '__main__':
+ # Ignore warnings here. We are just testing
+ from warnings import filterwarnings, resetwarnings
+ filterwarnings('ignore')
+
+ unittest.main(defaultTest='test_suite')
+
+ resetwarnings()
diff --git a/tests/testfiles/global-overlays.xml b/tests/testfiles/global-overlays.xml
new file mode 100644
index 0000000..d770692
--- /dev/null
+++ b/tests/testfiles/global-overlays.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" ?>
+<layman>
+
+ <overlay
+ type = "svn"
+ src = "https://overlays.gentoo.org/svn/dev/wrobel"
+ contact = "nobody@gentoo.org"
+ name = "wrobel"
+ status = "official"
+ priority = "10">
+
+ <description>
+ Test
+ </description>
+
+ </overlay>
+
+ <overlay
+ type = "rsync"
+ src = "rsync://gunnarwrobel.de/wrobel-stable"
+ contact = "nobody@gentoo.org"
+ name = "wrobel-stable">
+
+ <description>
+ A collection of ebuilds from Gunnar Wrobel [wrobel@gentoo.org].
+ </description>
+
+ </overlay>
+
+</layman>
diff --git a/tests/testfiles/layman-test.tar.bz2 b/tests/testfiles/layman-test.tar.bz2
new file mode 100644
index 0000000..85ee7fd
--- /dev/null
+++ b/tests/testfiles/layman-test.tar.bz2
Binary files differ
diff --git a/tests/testfiles/make.conf b/tests/testfiles/make.conf
new file mode 100644
index 0000000..d32dd16
--- /dev/null
+++ b/tests/testfiles/make.conf
@@ -0,0 +1,345 @@
+# Copyright 1999-2004 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: /var/cvsroot/gentoo-src/portage/cnf/make.conf.x86,v 1.5.2.5 2005/04/13 15:28:38 jstubbs Exp $
+# Contains local system settings for Portage system
+
+# Please review 'man make.conf' for more information.
+
+# Build-time functionality
+# ========================
+#
+# The USE variable is used to enable optional build-time functionality. For
+# example, quite a few packages have optional X, gtk or GNOME functionality
+# that can only be enabled or disabled at compile-time. Gentoo Linux has a
+# very extensive set of USE variables described in our USE variable HOWTO at
+# http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?part=2&chap=1
+#
+# The available list of use flags with descriptions is in your portage tree.
+# Use 'less' to view them: --> less /usr/portage/profiles/use.desc <--
+#
+# 'ufed' is an ncurses/dialog interface available in portage to make handling
+# useflags for you. 'emerge app-portage/ufed'
+#
+# Example:
+
+# Use flags will be handled by polymeraZe
+USE="-*"
+
+# Host Setting
+# ============
+#
+# DO NOT CHANGE THIS SETTING UNLESS YOU ARE USING STAGE1!
+# Change this line as appropriate (i686, i586, i486 or i386).
+# All modern systems (even Athlons) should use "i686-pc-linux-gnu".
+# All K6's are i586.
+CHOST="i686-pc-linux-gnu"
+
+# Host and optimization settings
+# ==============================
+#
+# For optimal performance, enable a CFLAGS setting appropriate for your CPU.
+#
+# Please note that if you experience strange issues with a package, it may be
+# due to gcc's optimizations interacting in a strange way. Please test the
+# package (and in some cases the libraries it uses) at default optimizations
+# before reporting errors to developers.
+#
+# -mcpu=<cpu-type> means optimize code for the particular type of CPU without
+# breaking compatibility with other CPUs.
+#
+# -march=<cpu-type> means to take full advantage of the ABI and instructions
+# for the particular CPU; this will break compatibility with older CPUs (for
+# example, -march=athlon-xp code will not run on a regular Athlon, and
+# -march=i686 code will not run on a Pentium Classic.
+#
+# CPU types supported in gcc-3.2 and higher: athlon-xp, athlon-mp,
+# athlon-tbird, athlon, k6, k6-2, k6-3, i386, i486, i586 (Pentium), i686
+# (PentiumPro), pentium, pentium-mmx, pentiumpro, pentium2 (Celeron),
+# pentium3, and pentium4.
+#
+# Note that Gentoo Linux 1.4 and higher include at least gcc-3.2.
+#
+# CPU types supported in gcc-2.95*: k6, i386, i486, i586 (Pentium), i686
+# (Pentium Pro), pentium, pentiumpro Gentoo Linux 1.2 and below use gcc-2.95*
+#
+# CRITICAL WARNINGS: ****************************************************** #
+# K6 markings are deceptive. Avoid setting -march for them. See Bug #24379. #
+# Pentium-M CPU's should not enable sse2 until at least gcc-3.4. Bug 50616. #
+# ************************************************************************* #
+#
+# Decent examples:
+#
+#CFLAGS="-mcpu=athlon-xp -O3 -pipe"
+
+CFLAGS="-march=athlon-xp -O3 -pipe"
+
+
+# If you set a CFLAGS above, then this line will set your default C++ flags to
+# the same settings.
+CXXFLAGS="${CFLAGS}"
+
+# Advanced Masking
+# ================
+#
+# Gentoo is using a new masking system to allow for easier stability testing
+# on packages. KEYWORDS are used in ebuilds to mask and unmask packages based
+# on the platform they are set for. A special form has been added that
+# indicates packages and revisions that are expected to work, but have not yet
+# been approved for the stable set. '~arch' is a superset of 'arch' which
+# includes the unstable, in testing, packages. Users of the 'x86' architecture
+# would add '~x86' to ACCEPT_KEYWORDS to enable unstable/testing packages.
+# '~ppc', '~sparc' are the unstable KEYWORDS for their respective platforms.
+#
+# Please note that this is not for development, alpha, beta, nor cvs release
+# packages. "Broken" packages will not be added to testing and should not be
+# requested to be added. Alternative routes are available to developers
+# for experimental packages, and it is at their discretion to use them.
+#
+# DO NOT PUT ANYTHING BUT YOUR SPECIFIC ~ARCHITECTURE IN THE LIST.
+# IF YOU ARE UNSURE OF YOUR ARCH, OR THE IMPLICATIONS, DO NOT MODIFY THIS.
+#
+
+ACCEPT_KEYWORDS="x86"
+
+
+# Portage Directories
+# ===================
+#
+# Each of these settings controls an aspect of portage's storage and file
+# system usage. If you change any of these, be sure it is available when
+# you try to use portage. *** DO NOT INCLUDE A TRAILING "/" ***
+#
+# PORTAGE_TMPDIR is the location portage will use for compilations and
+# temporary storage of data. This can get VERY large depending upon
+# the application being installed.
+PORTAGE_TMPDIR=/var/tmp
+#
+# PORTDIR is the location of the portage tree. This is the repository
+# for all profile information as well as all ebuilds. If you change
+# this, you must update your /etc/make.profile symlink accordingly.
+PORTDIR=/usr/portage
+#
+# DISTDIR is where all of the source code tarballs will be placed for
+# emerges. The source code is maintained here unless you delete
+# it. The entire repository of tarballs for gentoo is 9G. This is
+# considerably more than any user will ever download. 2-3G is
+# a large DISTDIR.
+DISTDIR=/usr/distfiles
+#
+# PKGDIR is the location of binary packages that you can have created
+# with '--buildpkg' or '-b' while emerging a package. This can get
+# upto several hundred megs, or even a few gigs.
+#PKGDIR=${PORTDIR}/packages
+#
+# PORT_LOGDIR is the location where portage will store all the logs it
+# creates from each individual merge. They are stored as NNNN-$PF.log
+# in the directory specified. This is disabled until you enable it by
+# providing a directory. Permissions will be modified as needed IF the
+# directory exists, otherwise logging will be disabled. NNNN is the
+# increment at the time the log is created. Logs are thus sequential.
+PORT_LOGDIR=/var/log/services/portage.d
+#
+# PORTDIR_OVERLAY is a directory where local ebuilds may be stored without
+# concern that they will be deleted by rsync updates. Default is not
+# defined.
+PORTDIR_OVERLAY="
+/usr/portage/local/layman/wrobel-stable
+$PORTDIR_OVERLAY
+/usr/portage/local/ebuilds/testing
+/usr/portage/local/ebuilds/stable
+/usr/portage/local/kolab2
+/usr/portage/local/gentoo-webapps-overlay/experimental
+/usr/portage/local/gentoo-webapps-overlay/production-ready"
+
+# Fetching files
+# ==============
+#
+# If you need to set a proxy for wget or lukemftp, add the appropriate "export
+# ftp_proxy=<proxy>" and "export http_proxy=<proxy>" lines to /etc/profile if
+# all users on your system should use them.
+#
+# Portage uses wget by default. Here are some settings for some alternate
+# downloaders -- note that you need to merge these programs first before they
+# will be available.
+#
+# Default fetch command (5 tries, passive ftp for firewall compatibility)
+#FETCHCOMMAND="/usr/bin/wget -t 5 --passive-ftp \${URI} -P \${DISTDIR}"
+#RESUMECOMMAND="/usr/bin/wget -c -t 5 --passive-ftp \${URI} -P \${DISTDIR}"
+#
+# Using wget, ratelimiting downloads
+#FETCHCOMMAND="/usr/bin/wget -t 5 --passive-ftp --limit-rate=200k \${URI} -P \${DISTDIR}"
+#RESUMECOMMAND="/usr/bin/wget -c -t 5 --passive-ftp --limit-rate=200k \${URI} -P \${DISTDIR}"
+#
+# Lukemftp (BSD ftp):
+#FETCHCOMMAND="/usr/bin/lukemftp -s -a -o \${DISTDIR}/\${FILE} \${URI}"
+#RESUMECOMMAND="/usr/bin/lukemftp -s -a -R -o \${DISTDIR}/\${FILE} \${URI}"
+#
+
+FETCHCOMMAND="/usr/bin/getdelta.sh \${URI}"
+
+
+# Portage uses GENTOO_MIRRORS to specify mirrors to use for source retrieval.
+# The list is a space separated list which is read left to right. If you use
+# another mirror we highly recommend leaving the default mirror at the end of
+# the list so that portage will fall back to it if the files cannot be found
+# on your specified mirror. We _HIGHLY_ recommend that you change this setting
+# to a nearby mirror by merging and using the 'mirrorselect' tool.
+
+GENTOO_MIRRORS="http://pandemonium.tiscali.de/pub/gentoo/ ftp://pandemonium.tiscali.de/pub/gentoo/ ftp://ftp-stud.fht-esslingen.de/pub/Mirrors/gentoo/ http://mir.zyrianes.net/gentoo/ http://ftp.snt.utwente.nl/pub/os/linux/gentoo http://distfiles.gentoo.org http://www.ibiblio.org/pub/Linux/distributions/gentoo"
+
+#
+# Portage uses PORTAGE_BINHOST to specify mirrors for prebuilt-binary packages.
+# The list is a single entry specifying the full address of the directory
+# serving the tbz2's for your system. Running emerge with either '--getbinpkg'
+# or '--getbinpkgonly' will cause portage to retrieve the metadata from all
+# packages in the directory specified, and use that data to determine what will
+# be downloaded and merged. '-g' or '-gK' are the recommend parameters. Please
+# consult the man pages and 'emerge --help' for more information. For FTP, the
+# default connection is passive -- If you require an active connection, affix
+# an asterisk (*) to the end of the host:port string before the path.
+#PORTAGE_BINHOST="http://grp.mirror.site/gentoo/grp/1.4/i686/athlon-xp/"
+# This ftp connection is passive ftp.
+#PORTAGE_BINHOST="ftp://login:pass@grp.mirror.site/pub/grp/i686/athlon-xp/"
+# This ftp connection is active ftp.
+#PORTAGE_BINHOST="ftp://login:pass@grp.mirror.site:21*/pub/grp/i686/athlon-xp/"
+
+# Synchronizing Portage
+# =====================
+#
+# Each of these settings affects how Gentoo synchronizes your Portage tree.
+# Synchronization is handled by rsync and these settings allow some control
+# over how it is done.
+#
+#
+# SYNC is the server used by rsync to retrieve a localized rsync mirror
+# rotation. This allows you to select servers that are geographically
+# close to you, yet still distribute the load over a number of servers.
+# Please do not single out specific rsync mirrors. Doing so places undue
+# stress on particular mirrors. Instead you may use one of the following
+# continent specific rotations:
+#
+# Default: "rsync://rsync.gentoo.org/gentoo-portage"
+# North America: "rsync://rsync.namerica.gentoo.org/gentoo-portage"
+# South America: "rsync://rsync.samerica.gentoo.org/gentoo-portage"
+# Europe: "rsync://rsync.europe.gentoo.org/gentoo-portage"
+# Asia: "rsync://rsync.asia.gentoo.org/gentoo-portage"
+# Australia: "rsync://rsync.au.gentoo.org/gentoo-portage"
+
+SYNC="rsync://rsync.europe.gentoo.org/gentoo-portage"
+
+#
+# RSYNC_RETRIES sets the number of times portage will attempt to retrieve
+# a current portage tree before it exits with an error. This allows
+# for a more successful retrieval without user intervention most times.
+#RSYNC_RETRIES="3"
+#
+# RSYNC_TIMEOUT sets the length of time rsync will wait before it times out
+# on a connection. Most users will benefit from this setting as it will
+# reduce the amount of 'dead air' they experience when they run across
+# the occasional, unreachable mirror. Dialup users might want to set this
+# value up around the 300 second mark.
+#RSYNC_TIMEOUT=180
+
+# Advanced Features
+# =================
+#
+# MAKEOPTS provides extra options that may be passed to 'make' when a
+# program is compiled. Presently the only use is for specifying
+# the number of parallel makes (-j) to perform. The suggested number
+# for parallel makes is CPUs+1.
+MAKEOPTS="-j2"
+#
+# PORTAGE_NICENESS provides a default increment to emerge's niceness level.
+# Note: This is an increment. Running emerge in a niced environment will
+# reduce it further. Default is unset.
+PORTAGE_NICENESS=3
+#
+# AUTOCLEAN enables portage to automatically clean out older or overlapping
+# packages from the system after every successful merge. This is the
+# same as running 'emerge -c' after every merge. Set with: "yes" or "no".
+# This does not affect the unpacked source. See 'noclean' below.
+AUTOCLEAN="yes"
+#
+# PORTAGE_TMPFS is a location where portage may create temporary files.
+# If specified, portage will use this directory whenever possible
+# for all rapid operations such as lockfiles and transient data.
+# It is _highly_ recommended that this be a tmpfs or ramdisk. Do not
+# set this to anything that does not give a significant performance
+# enhancement and proper FS compliance for locks and read/write.
+# /dev/shm is a glibc mandated tmpfs, and should be a reasonable
+# setting for all linux kernel+glibc based systems.
+#PORTAGE_TMPFS="/dev/shm"
+#
+# FEATURES are settings that affect the functionality of portage. Most of
+# these settings are for developer use, but some are available to non-
+# developers as well.
+#
+# 'autoaddcvs' causes portage to automatically try to add files to cvs
+# that will have to be added later. Done at generation times
+# and only has an effect when 'cvs' is also set.
+# 'buildpkg' causes binary packages to be created of all packages that
+# are being merged.
+# 'ccache' enables ccache support via CC.
+# 'collision-protect'
+# prevents packages from overwriting files that are owned by
+# another package or by no package at all.
+# 'cvs' causes portage to enable all cvs features (commits, adds),
+# and to apply all USE flags in SRC_URI for digests -- for
+# developers only.
+# 'digest' causes digests to be generated for all packages being merged.
+# 'distcc' enables distcc support via CC.
+# 'distlocks' enables distfiles locking using fcntl or hardlinks. This
+# is enabled by default. Tools exist to help clean the locks
+# after crashes: /usr/lib/portage/bin/clean_locks.
+# 'fixpackages' allows portage to fix binary packages that are stored in
+# PKGDIR. This can consume a lot of time. 'fixpackages' is
+# also a script that can be run at any given time to force
+# the same actions.
+# 'gpg' enables basic verification of Manifest files using gpg.
+# This features is UNDER DEVELOPMENT and reacts to features
+# of strict and severe. Heavy use of gpg sigs is coming.
+# 'keeptemp' prevents the clean phase from deleting the temp files ($T)
+# from a merge.
+# 'keepwork' prevents the clean phase from deleting the WORKDIR.
+# 'maketest' causes ebuilds to perform testing phases if they are capable
+# of it. Some packages support this automaticaly via makefiles.
+# 'noauto' causes ebuild to perform only the action requested and
+# not any other required actions like clean or unpack -- for
+# debugging purposes only.
+# 'noclean' prevents portage from removing the source and temporary files
+# after a merge -- for debugging purposes only.
+# 'nostrip' prevents the stripping of binaries.
+# 'notitles' disables xterm titlebar updates (which contain status info).
+# 'sandbox' enables sandboxing when running emerge and ebuild.
+# 'strict' causes portage to react strongly to conditions that are
+# potentially dangerous, like missing/incorrect Manifest files.
+# 'userpriv' allows portage to drop root privileges while it is compiling,
+# as a security measure. As a side effect this can remove
+# sandbox access violations for users.
+# 'usersandbox' enables sandboxing while portage is running under userpriv.
+#FEATURES="sandbox buildpkg ccache distcc userpriv usersandbox notitles noclean noauto cvs keeptemp keepwork autoaddcvs"
+FEATURES="sandbox ccache userprivs distlocks cvs"
+#
+# CCACHE_SIZE sets the space use limitations for ccache. The default size is
+# 2G, and will be set if not defined otherwise and ccache is in features.
+# Portage will set the default ccache dir if it is not present in the
+# user's environment, for userpriv it sets: ${PORTAGE_TMPDIR}/ccache
+# (/var/tmp/ccache), and for regular use the default is /root/.ccache.
+# Sizes are specified with 'G' 'M' or 'K'.
+# '2G' for 2 gigabytes, '2048M' for 2048 megabytes (same as 2G).
+CCACHE_SIZE="1G"
+#
+# DISTCC_DIR sets the temporary space used by distcc.
+#DISTCC_DIR="${PORTAGE_TMPDIR}/.distcc"
+#
+# RSYNC_EXCLUDEFROM is a file that portage will pass to rsync when it updates
+# the portage tree. Specific chunks of the tree may be excluded from
+# consideration. This may cause dependency failures if you are not careful.
+# The file format is one pattern per line, blanks and ';' or '#' lines are
+# comments. See 'man rsync' for more details on the exclude-from format.
+#RSYNC_EXCLUDEFROM=/etc/portage/rsync_excludes
+EBEEP_IGNORE=yes
+
+CONFIG_PROTECT_MASK="/usr/X11R6/lib/X11"
+
+
diff --git a/utils.py b/utils.py
new file mode 100644
index 0000000..239f1b3
--- /dev/null
+++ b/utils.py
@@ -0,0 +1,208 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# POLYMERAZE XML UTILITIES
+#################################################################################
+# File: xml.py
+#
+# Utilities to deal with xml nodes.
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+#
+
+'''Utility functions to deal with xml nodes. '''
+
+__version__ = '$Id: utils.py 236 2006-09-05 20:39:37Z wrobel $'
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+import types, re
+
+#===============================================================================
+#
+# Helper functions
+#
+#-------------------------------------------------------------------------------
+
+def node_to_text(node):
+ '''
+ Reduces an xml node to its text elements. The function does not
+ collect the text nodes recursively.
+
+ >>> import xml.dom.minidom
+ >>> imp = xml.dom.minidom.getDOMImplementation()
+ >>> doc = imp.createDocument('test', 'root', None)
+ >>> root = doc.childNodes[0]
+ >>> node = doc.createTextNode('text')
+ >>> a = root.appendChild(node)
+ >>> node = doc.createElement('text')
+ >>> node2 = doc.createTextNode('text')
+ >>> a = node.appendChild(node2)
+ >>> a = root.appendChild(node)
+ >>> node = doc.createTextNode('text')
+ >>> a = root.appendChild(node)
+ >>> doc.toprettyxml('', '') #doctest: +ELLIPSIS
+ '<?xml version="1.0" ?>...<root>text<text>text</text>text</root>'
+
+ >>> node_to_text(root)
+ 'texttext'
+
+ '''
+ text = ''
+
+ for child in node.childNodes:
+ if child.nodeType == child.TEXT_NODE:
+ text = text + child.data
+
+ return text
+
+def node_to_dict(node):
+ ''' Converts a xml node to a dictionary. The function collects the
+ nodes recursively. Attributes will be prepended with '&', child
+ nodes will be surrounded with tags. An index will be appended
+ since several child nodes with the same tag may exist. Text
+ elements will be collapsed and stored in a n entry prepended with
+ '@'. Comments will be ignored.
+
+ >>> import xml.dom.minidom
+ >>> imp = xml.dom.minidom.getDOMImplementation()
+ >>> doc = imp.createDocument('test', 'root', None)
+ >>> root = doc.childNodes[0]
+ >>> node = doc.createTextNode('text')
+ >>> a = root.appendChild(node)
+ >>> node = doc.createElement('text')
+ >>> node2 = doc.createTextNode('text')
+ >>> comm = doc.createComment('comment')
+ >>> attr = doc.createAttribute('&attr')
+ >>> a = node.appendChild(node2)
+ >>> a = root.appendChild(comm)
+ >>> node.setAttributeNode(attr)
+ >>> node.setAttribute('&attr','test')
+ >>> a = root.appendChild(node)
+ >>> node3 = doc.createElement('text')
+ >>> a = root.appendChild(node3)
+ >>> node = doc.createTextNode('text')
+ >>> a = root.appendChild(node)
+ >>> doc.toprettyxml('', '') #doctest: +ELLIPSIS
+ '<?xml version="1.0" ?>...<root>text<!--comment--><text &attr="test">text</text><text/>text</root>'
+
+ >>> node_to_dict(root)
+ {'<text>1': {'@': 'text', '&&attr': 'test'}, '<text>2': {'@': ''}, '@': 'texttext'}
+
+ '''
+ result = {}
+
+ # Map the attributes
+ for index in range(0, node.attributes.length):
+ attr = node.attributes.item(index)
+ result['&' + attr.name] = attr.nodeValue
+
+ text = ''
+
+ # Map the nodes
+ for child in node.childNodes:
+ if child.nodeType == child.TEXT_NODE:
+ text = text + child.data
+ if child.nodeType == child.ELEMENT_NODE:
+ index = 1
+ while ('<' + child.tagName + '>' + str(index)) in result.keys():
+ index += 1
+ result['<' + child.tagName + '>' + str(index)] = node_to_dict(child)
+
+ result['@'] = text
+
+ return result
+
+def dict_to_node(data, document, root_name):
+ ''' Reverts the node_to_dict operation.
+
+ >>> import xml.dom.minidom
+ >>> imp = xml.dom.minidom.getDOMImplementation()
+ >>> doc = imp.createDocument('test', 'root', None)
+ >>> a = {'<text>1': {'@': 'text', '&&attr': 'test'}, '<text>2': {'@': ''}, '@': 'texttext'}
+ >>> doc.childNodes[0] = dict_to_node(a, doc, 'root')
+ >>> doc.toprettyxml('', '') #doctest: +ELLIPSIS
+ '<?xml version="1.0" ?>...<root><text &attr="test">text</text><text></text>texttext</root>'
+
+ '''
+ node = document.createElement(root_name)
+
+ for i, j in data.items():
+
+ if i[0] == '&':
+ attr = document.createAttribute(i[1:])
+ node.setAttributeNode(attr)
+ node.setAttribute(i[1:], j)
+ if i[0] == '<':
+ k = i[1:]
+ while k[-1] in '0123456789':
+ k = k[:-1]
+ child = dict_to_node(data[i],
+ document,
+ k[:-1])
+ node.appendChild(child)
+ if i[0] == '@':
+ child = document.createTextNode(j)
+ node.appendChild(child)
+
+ return node
+
+def path(path_elements):
+ '''
+ Concatenate a path from several elements.
+
+ >>> path([])
+ ''
+ >>> path(['a'])
+ 'a'
+ >>> path(['a','b'])
+ 'a/b'
+ >>> path(['a/','b'])
+ 'a/b'
+ >>> path(['/a/','b'])
+ '/a/b'
+ >>> path(['/a/','b/'])
+ '/a/b'
+ >>> path(['/a/','b/'])
+ '/a/b'
+ >>> path(['/a/','/b/'])
+ '/a/b'
+ >>> path(['/a/','/b','c/'])
+ '/a/b/c'
+ '''
+ pathname = ''
+
+ if type(path_elements) in types.StringTypes:
+ path_elements = [path_elements]
+
+ # Concatenate elements and seperate with /
+ for i in path_elements:
+ pathname += i + '/'
+
+ # Replace multiple consecutive slashes
+ pathname = re.compile('/+').sub('/', pathname)
+
+ # Remove the final / if there is one
+ if pathname and pathname[-1] == '/':
+ pathname = pathname[:-1]
+
+ return pathname
+
+#===============================================================================
+#
+# Testing
+#
+#-------------------------------------------------------------------------------
+
+if __name__ == '__main__':
+ import doctest, sys
+ doctest.testmod(sys.modules[__name__])
diff --git a/version.py b/version.py
new file mode 100644
index 0000000..943522b
--- /dev/null
+++ b/version.py
@@ -0,0 +1,24 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#################################################################################
+# LAYMAN VERSION
+#################################################################################
+# File: version.py
+#
+# Current version number
+#
+# Copyright:
+# (c) 2005 - 2006 Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+# Gunnar Wrobel <wrobel@gentoo.org>
+#
+
+__version__ = "$Id: version.py 309 2007-04-09 16:23:38Z wrobel $"
+
+
+VERSION = '1.0.99'
+
+if __name__ == '__main__':
+ print VERSION