blob: 59b25b60601fbca3366056e4de4a03d186557108 [file] [log] [blame]
# Copyright 2005-2018 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:
# pylint: disable=W0622
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', 'BDEPEND', '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)