blob: d2d9e2710f75f02b2ee1bd755bcd8b2c510f6f8a [file] [log] [blame]
# Copyright 2005-2014 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# Author(s): Nicholas Carpaski (, Brian Harring (
from __future__ import unicode_literals
__all__ = ["cache"]
import stat
import sys
import operator
import warnings
from portage.util import normalize_path
import errno
from portage.exception import FileNotFound, PermissionDenied
from portage import os
from portage import checksum
from portage import _shell_quote
if sys.hexversion >= 0x3000000:
# pylint: disable=W0622
long = int
class hashed_path(object):
def __init__(self, location):
self.location = location
def __getattr__(self, attr):
if attr == 'mtime':
# use stat.ST_MTIME; accessing .st_mtime gets you a float
# depending on the python version, and long(float) introduces
# some rounding issues that aren't present for people using
# the straight c api.
# thus use the defacto python compatibility work around;
# access via index, which guarantees you get the raw long.
self.mtime = obj = os.stat(self.location)[stat.ST_MTIME]
except OSError as e:
if e.errno in (errno.ENOENT, errno.ESTALE):
raise FileNotFound(self.location)
elif e.errno == PermissionDenied.errno:
raise PermissionDenied(self.location)
return obj
if not attr.islower():
# we don't care to allow .mD5 as an alias for .md5
raise AttributeError(attr)
hashname = attr.upper()
if hashname not in checksum.get_valid_checksum_keys():
raise AttributeError(attr)
val = checksum.perform_checksum(self.location, hashname)[0]
setattr(self, attr, val)
return val
def __repr__(self):
return "<portage.eclass_cache.hashed_path('%s')>" % (self.location,)
class cache(object):
Maintains the cache information about eclasses used in ebuild.
def __init__(self, porttree_root, overlays=None):
if overlays is not None:
warnings.warn("overlays parameter of portage.eclass_cache.cache constructor is deprecated and no longer used",
DeprecationWarning, stacklevel=2)
self.eclasses = {} # {"Name": hashed_path}
self._eclass_locations = {}
self._eclass_locations_str = None
# screw with the porttree ordering, w/out having bash inherit match it, and I'll hurt you.
# ~harring
if porttree_root:
self.porttree_root = porttree_root
self.porttrees = (normalize_path(self.porttree_root),)
self._master_eclass_root = os.path.join(self.porttrees[0], "eclass")
self.porttree_root = None
self.porttrees = ()
self._master_eclass_root = None
def copy(self):
return self.__copy__()
def __copy__(self):
result = self.__class__(None)
result.eclasses = self.eclasses.copy()
result._eclass_locations = self._eclass_locations.copy()
result.porttree_root = self.porttree_root
result.porttrees = self.porttrees
result._master_eclass_root = self._master_eclass_root
return result
def append(self, other):
Append another instance to this instance. This will cause eclasses
from the other instance to override any eclasses from this instance
that have the same name.
if not isinstance(other, self.__class__):
raise TypeError(
"expected type %s, got %s" % (self.__class__, type(other)))
self.porttrees = self.porttrees + other.porttrees
self._eclass_locations_str = None
def update_eclasses(self):
self.eclasses = {}
self._eclass_locations = {}
master_eclasses = {}
eclass_len = len(".eclass")
ignored_listdir_errnos = (errno.ENOENT, errno.ENOTDIR)
for x in [normalize_path(os.path.join(y,"eclass")) for y in self.porttrees]:
eclass_filenames = os.listdir(x)
except OSError as e:
if e.errno in ignored_listdir_errnos:
del e
elif e.errno == PermissionDenied.errno:
raise PermissionDenied(x)
for y in eclass_filenames:
if not y.endswith(".eclass"):
obj = hashed_path(os.path.join(x, y))
obj.eclass_dir = x
mtime = obj.mtime
except FileNotFound:
ys = y[:-eclass_len]
if x == self._master_eclass_root:
master_eclasses[ys] = mtime
self.eclasses[ys] = obj
self._eclass_locations[ys] = x
master_mtime = master_eclasses.get(ys)
if master_mtime is not None:
if master_mtime == mtime:
# It appears to be identical to the master,
# so prefer the master entry.
self.eclasses[ys] = obj
self._eclass_locations[ys] = x
def validate_and_rewrite_cache(self, ec_dict, chf_type, stores_paths):
This will return an empty dict if the ec_dict parameter happens
to be empty, therefore callers must take care to distinguish
between empty dict and None return values.
if not isinstance(ec_dict, dict):
return None
our_getter = operator.attrgetter(chf_type)
cache_getter = lambda x:x
if stores_paths:
cache_getter = operator.itemgetter(1)
d = {}
for eclass, ec_data in ec_dict.items():
cached_data = self.eclasses.get(eclass)
if cached_data is None:
return None
if cache_getter(ec_data) != our_getter(cached_data):
return None
d[eclass] = cached_data
return d
def get_eclass_data(self, inherits):
ec_dict = {}
for x in inherits:
ec_dict[x] = self.eclasses[x]
return ec_dict
def eclass_locations_string(self):
if self._eclass_locations_str is None:
self._eclass_locations_str = " ".join(_shell_quote(x)
for x in reversed(self.porttrees))
return self._eclass_locations_str