From: Zac Medico Date: Wed, 11 Mar 2009 06:31:52 +0000 (-0000) Subject: Thanks to Petteri Räty for this new cache module which X-Git-Tag: v2.1.6.8~91 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=696a6ac860bc1446274aa92e72bfa88778624a50;p=portage.git Thanks to Petteri Räty for this new cache module which uses extended attributes (via pyxattr) to attach metadata cache directly to the ebuild files themselves. (trunk r12737) svn path=/main/branches/2.1.6/; revision=12986 --- diff --git a/pym/portage/cache/ebuild_xattr.py b/pym/portage/cache/ebuild_xattr.py new file mode 100644 index 000000000..83ed8a783 --- /dev/null +++ b/pym/portage/cache/ebuild_xattr.py @@ -0,0 +1,163 @@ +# -*- coding: UTF8 -*- +# Copyright: 2009 Gentoo Foundation +# Author(s): Petteri Räty (betelgeuse@gentoo.org) +# License: GPL2 +# $Id$ + +__all__ = ['database'] + +from portage.cache import fs_template +from portage.versions import catsplit +from portage import cpv_getkey +from portage.util import writemsg +import os +import xattr +from errno import ENODATA,ENOSPC,E2BIG + +class NoValueException(Exception): + pass + +class database(fs_template.FsBased): + + autocommits = True + + def __init__(self, *args, **config): + super(database,self).__init__(*args, **config) + self.portdir = self.label + self.ns = xattr.NS_USER + '.gentoo.cache' + self.keys = set(self._known_keys) + self.keys.add('_mtime_') + self.keys.add('_eclasses_') + # xattrs have an upper length + self.max_len = self.__get_max() + + def __get_max(self): + path = os.path.join(self.portdir,'profiles/repo_name') + try: + return int(self.__get(path,'value_max_len')) + except NoValueException,e: + max = self.__calc_max(path) + self.__set(path,'value_max_len',str(max)) + return max + + def __calc_max(self,path): + """ Find out max attribute length supported by the file system """ + + hundred = '' + for i in range(100): + hundred+='a' + + s=hundred + + # Could use finally but needs python 2.5 then + try: + while True: + self.__set(path,'test_max',s) + s+=hundred + except IOError,e: + # ext based give wrong errno + # http://bugzilla.kernel.org/show_bug.cgi?id=12793 + if e.errno in (E2BIG,ENOSPC): + result = len(s)-100 + else: + raise e + + try: + self.__remove(path,'test_max') + except IOError,e: + if e.errno is not ENODATA: + raise e + + return result + + def __get_path(self,cpv): + cat,pn = catsplit(cpv_getkey(cpv)) + return os.path.join(self.portdir,cat,pn,os.path.basename(cpv) + ".ebuild") + + def __has_cache(self,path): + try: + self.__get(path,'_mtime_') + except NoValueException,e: + return False + + return True + + def __get(self,path,key,default=None): + try: + return xattr.get(path,key,namespace=self.ns) + except IOError,e: + if not default is None and ENODATA == e.errno: + return default + else: + raise NoValueException() + + def __remove(self,path,key): + xattr.remove(path,key,namespace=self.ns) + + def __set(self,path,key,value): + xattr.set(path,key,value,namespace=self.ns) + + def _getitem(self, cpv): + values = {} + path = self.__get_path(cpv) + all = {} + for tuple in xattr.get_all(path,namespace=self.ns): + key,value = tuple + all[key] = value + + if not '_mtime_' in all: + raise KeyError(cpv) + + # We default to '' like other caches + for key in self.keys: + attr_value = all.get(key,'1:') + parts,sep,value = attr_value.partition(':') + parts = int(parts) + if parts > 1: + for i in range(1,parts): + value += all.get(key+str(i)) + values[key] = value + + return values + + def _setitem(self, cpv, values): + path = self.__get_path(cpv) + max = self.max_len + for key,value in values.iteritems(): + # mtime comes in as long so need to convert to strings + s = str(value) + # We need to split long values + value_len = len(s) + parts = 0 + if value_len > max: + # Find out how many parts we need + parts = value_len/max + if value_len % max > 0: + parts += 1 + + # Only the first entry carries the number of parts + self.__set(path,key,'%s:%s'%(parts,s[0:max])) + + # Write out the rest + for i in range(1,parts): + start = i * max + val = s[start:start+max] + self.__set(path,key+str(i),val) + else: + self.__set(path,key,"%s:%s"%(1,s)) + + def _delitem(self, cpv): + pass # Will be gone with the ebuild + + def __contains__(self, cpv): + return os.path.exists(self.__get_path(cpv)) + + def __iter__(self): + for root,dirs,files in os.walk(self.portdir): + for file in files: + if file[-7:] == '.ebuild': + cat = os.path.basename(os.path.dirname(root)) + pn_pv = file[:-7] + path = os.path.join(root,file) + if self.__has_cache(path): + yield "%s/%s/%s" % (cat,os.path.basename(root),file[:-7])