| # Copyright: 2005 Gentoo Foundation |
| # Author(s): Brian Harring (ferringb@gentoo.org) |
| # License: GPL2 |
| |
| import errno |
| import re |
| import stat |
| import sys |
| from operator import attrgetter |
| from portage import os |
| from portage import _encodings |
| from portage import _unicode_encode |
| from portage.cache import cache_errors, flat_hash |
| import portage.eclass_cache |
| from portage.cache.template import reconstruct_eclasses |
| from portage.cache.mappings import ProtectedDict |
| |
| if sys.hexversion >= 0x3000000: |
| basestring = str |
| long = int |
| |
| # this is the old cache format, flat_list. count maintained here. |
| magic_line_count = 22 |
| |
| # store the current key order *here*. |
| class database(flat_hash.database): |
| complete_eclass_entries = False |
| auxdbkey_order=('DEPEND', 'RDEPEND', 'SLOT', 'SRC_URI', |
| 'RESTRICT', 'HOMEPAGE', 'LICENSE', 'DESCRIPTION', |
| 'KEYWORDS', 'INHERITED', 'IUSE', 'REQUIRED_USE', |
| 'PDEPEND', 'PROVIDE', 'EAPI', 'PROPERTIES', |
| 'DEFINED_PHASES', 'HDEPEND') |
| |
| autocommits = True |
| serialize_eclasses = False |
| |
| _hashed_re = re.compile('^(\\w+)=([^\n]*)') |
| |
| def __init__(self, location, *args, **config): |
| loc = location |
| super(database, self).__init__(location, *args, **config) |
| self.location = os.path.join(loc, "metadata","cache") |
| self.ec = None |
| self.raise_stat_collision = False |
| |
| def _parse_data(self, data, cpv): |
| _hashed_re_match = self._hashed_re.match |
| d = {} |
| |
| for line in data: |
| hashed = False |
| hashed_match = _hashed_re_match(line) |
| if hashed_match is None: |
| d.clear() |
| try: |
| for i, key in enumerate(self.auxdbkey_order): |
| d[key] = data[i] |
| except IndexError: |
| pass |
| break |
| else: |
| d[hashed_match.group(1)] = hashed_match.group(2) |
| |
| if "_eclasses_" not in d: |
| if "INHERITED" in d: |
| if self.ec is None: |
| self.ec = portage.eclass_cache.cache(self.location[:-15]) |
| getter = attrgetter(self.validation_chf) |
| try: |
| ec_data = self.ec.get_eclass_data(d["INHERITED"].split()) |
| d["_eclasses_"] = dict((k, (v.eclass_dir, getter(v))) |
| for k,v in ec_data.items()) |
| except KeyError as e: |
| # INHERITED contains a non-existent eclass. |
| raise cache_errors.CacheCorruption(cpv, e) |
| else: |
| d["_eclasses_"] = {} |
| elif isinstance(d["_eclasses_"], basestring): |
| # We skip this if flat_hash.database._parse_data() was called above |
| # because it calls reconstruct_eclasses() internally. |
| d["_eclasses_"] = reconstruct_eclasses(None, d["_eclasses_"]) |
| |
| return d |
| |
| def _setitem(self, cpv, values): |
| if "_eclasses_" in values: |
| values = ProtectedDict(values) |
| values["INHERITED"] = ' '.join(sorted(values["_eclasses_"])) |
| |
| new_content = [] |
| for k in self.auxdbkey_order: |
| new_content.append(values.get(k, '')) |
| new_content.append('\n') |
| for i in range(magic_line_count - len(self.auxdbkey_order)): |
| new_content.append('\n') |
| new_content = ''.join(new_content) |
| new_content = _unicode_encode(new_content, |
| _encodings['repo.content'], errors='backslashreplace') |
| |
| new_fp = os.path.join(self.location, cpv) |
| try: |
| f = open(_unicode_encode(new_fp, |
| encoding=_encodings['fs'], errors='strict'), 'rb') |
| except EnvironmentError: |
| pass |
| else: |
| try: |
| try: |
| existing_st = os.fstat(f.fileno()) |
| existing_content = f.read() |
| finally: |
| f.close() |
| except EnvironmentError: |
| pass |
| else: |
| existing_mtime = existing_st[stat.ST_MTIME] |
| if values['_mtime_'] == existing_mtime and \ |
| existing_content == new_content: |
| return |
| |
| if self.raise_stat_collision and \ |
| values['_mtime_'] == existing_mtime and \ |
| len(new_content) == existing_st.st_size: |
| raise cache_errors.StatCollision(cpv, new_fp, |
| existing_mtime, existing_st.st_size) |
| |
| s = cpv.rfind("/") |
| fp = os.path.join(self.location,cpv[:s], |
| ".update.%i.%s" % (os.getpid(), cpv[s+1:])) |
| try: |
| myf = open(_unicode_encode(fp, |
| encoding=_encodings['fs'], errors='strict'), 'wb') |
| except EnvironmentError as e: |
| if errno.ENOENT == e.errno: |
| try: |
| self._ensure_dirs(cpv) |
| myf = open(_unicode_encode(fp, |
| encoding=_encodings['fs'], errors='strict'), 'wb') |
| except EnvironmentError as e: |
| raise cache_errors.CacheCorruption(cpv, e) |
| else: |
| raise cache_errors.CacheCorruption(cpv, e) |
| |
| try: |
| myf.write(new_content) |
| finally: |
| myf.close() |
| self._ensure_access(fp, mtime=values["_mtime_"]) |
| |
| try: |
| os.rename(fp, new_fp) |
| except EnvironmentError as e: |
| try: |
| os.unlink(fp) |
| except EnvironmentError: |
| pass |
| raise cache_errors.CacheCorruption(cpv, e) |