| # Copyright 2010 Gentoo Foundation |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| __all__ = ( |
| 'VirtualsManager', |
| ) |
| |
| from copy import deepcopy |
| |
| from portage import os |
| from portage.dep import Atom |
| from portage.exception import InvalidAtom |
| from portage.localization import _ |
| from portage.util import grabdict, stack_dictlist, writemsg |
| from portage.versions import cpv_getkey |
| |
| class VirtualsManager(object): |
| |
| def __init__(self, *args, **kwargs): |
| if kwargs.get("_copy"): |
| return |
| |
| assert len(args) == 1, "VirtualsManager.__init__ takes one positional argument" |
| assert not kwargs, "unknown keyword argument(s) '%s' passed to VirtualsManager.__init__" % \ |
| ", ".join(kwargs) |
| |
| profiles = args[0] |
| self._virtuals = None |
| self._dirVirtuals = None |
| self._virts_p = None |
| |
| # Virtuals obtained from the vartree |
| self._treeVirtuals = None |
| # Virtuals added by the depgraph via self.add_depgraph_virtuals(). |
| self._depgraphVirtuals = {} |
| |
| #Initialise _dirVirtuals. |
| self._read_dirVirtuals(profiles) |
| |
| #We could initialise _treeVirtuals here, but some consumers want to |
| #pass their own vartree. |
| |
| def _read_dirVirtuals(self, profiles): |
| """ |
| Read the 'virtuals' file in all profiles. |
| """ |
| virtuals_list = [] |
| for x in profiles: |
| virtuals_file = os.path.join(x, "virtuals") |
| virtuals_dict = grabdict(virtuals_file) |
| atoms_dict = {} |
| for k, v in virtuals_dict.items(): |
| try: |
| virt_atom = Atom(k) |
| except InvalidAtom: |
| virt_atom = None |
| else: |
| if virt_atom.blocker or \ |
| str(virt_atom) != str(virt_atom.cp): |
| virt_atom = None |
| if virt_atom is None: |
| writemsg(_("--- Invalid virtuals atom in %s: %s\n") % \ |
| (virtuals_file, k), noiselevel=-1) |
| continue |
| providers = [] |
| for atom in v: |
| atom_orig = atom |
| if atom[:1] == '-': |
| # allow incrementals |
| atom = atom[1:] |
| try: |
| atom = Atom(atom) |
| except InvalidAtom: |
| atom = None |
| else: |
| if atom.blocker: |
| atom = None |
| if atom is None: |
| writemsg(_("--- Invalid atom in %s: %s\n") % \ |
| (virtuals_file, atom_orig), noiselevel=-1) |
| else: |
| if atom_orig == str(atom): |
| # normal atom, so return as Atom instance |
| providers.append(atom) |
| else: |
| # atom has special prefix, so return as string |
| providers.append(atom_orig) |
| if providers: |
| atoms_dict[virt_atom] = providers |
| if atoms_dict: |
| virtuals_list.append(atoms_dict) |
| |
| self._dirVirtuals = stack_dictlist(virtuals_list, incremental=True) |
| |
| for virt in self._dirVirtuals: |
| # Preference for virtuals decreases from left to right. |
| self._dirVirtuals[virt].reverse() |
| |
| def __deepcopy__(self, memo=None): |
| if memo is None: |
| memo = {} |
| result = VirtualsManager(_copy=True) |
| memo[id(self)] = result |
| |
| # immutable attributes (internal policy ensures lack of mutation) |
| # _treeVirtuals is initilised by _populate_treeVirtuals(). |
| # Before that it's 'None'. |
| result._treeVirtuals = self._treeVirtuals |
| memo[id(self._treeVirtuals)] = self._treeVirtuals |
| # _dirVirtuals is initilised by __init__. |
| result._dirVirtuals = self._dirVirtuals |
| memo[id(self._dirVirtuals)] = self._dirVirtuals |
| |
| # mutable attributes (change when add_depgraph_virtuals() is called) |
| result._virtuals = deepcopy(self._virtuals, memo) |
| result._depgraphVirtuals = deepcopy(self._depgraphVirtuals, memo) |
| result._virts_p = deepcopy(self._virts_p, memo) |
| |
| return result |
| |
| def _compile_virtuals(self): |
| """Stack installed and profile virtuals. Preference for virtuals |
| decreases from left to right. |
| Order of preference: |
| 1. installed and in profile |
| 2. installed only |
| 3. profile only |
| """ |
| |
| assert self._treeVirtuals is not None, "_populate_treeVirtuals() must be called before " + \ |
| "any query about virtuals" |
| |
| # Virtuals by profile+tree preferences. |
| ptVirtuals = {} |
| |
| for virt, installed_list in self._treeVirtuals.items(): |
| profile_list = self._dirVirtuals.get(virt, None) |
| if not profile_list: |
| continue |
| for cp in installed_list: |
| if cp in profile_list: |
| ptVirtuals.setdefault(virt, []) |
| ptVirtuals[virt].append(cp) |
| |
| virtuals = stack_dictlist([ptVirtuals, self._treeVirtuals, |
| self._dirVirtuals, self._depgraphVirtuals]) |
| self._virtuals = virtuals |
| self._virts_p = None |
| |
| def getvirtuals(self): |
| """ |
| Computes self._virtuals if necessary and returns it. |
| self._virtuals is only computed on the first call. |
| """ |
| if self._virtuals is None: |
| self._compile_virtuals() |
| |
| return self._virtuals |
| |
| def _populate_treeVirtuals(self, vartree): |
| """ |
| Initialize _treeVirtuals from the given vartree. |
| It must not have been initialized already, otherwise |
| our assumptions about immutability don't hold. |
| """ |
| assert self._treeVirtuals is None, "treeVirtuals must not be reinitialized" |
| |
| self._treeVirtuals = {} |
| |
| for provide, cpv_list in vartree.get_all_provides().items(): |
| try: |
| provide = Atom(provide) |
| except InvalidAtom: |
| continue |
| self._treeVirtuals[provide.cp] = \ |
| [Atom(cpv_getkey(cpv)) for cpv in cpv_list] |
| |
| def populate_treeVirtuals_if_needed(self, vartree): |
| """ |
| Initialize _treeVirtuals if it hasn't been done already. |
| This is a hack for consumers that already have an populated vartree. |
| """ |
| if self._treeVirtuals is not None: |
| return |
| |
| self._populate_treeVirtuals(vartree) |
| |
| def add_depgraph_virtuals(self, mycpv, virts): |
| """This updates the preferences for old-style virtuals, |
| affecting the behavior of dep_expand() and dep_check() |
| calls. It can change dbapi.match() behavior since that |
| calls dep_expand(). However, dbapi instances have |
| internal match caches that are not invalidated when |
| preferences are updated here. This can potentially |
| lead to some inconsistency (relevant to bug #1343).""" |
| |
| #Ensure that self._virtuals is populated. |
| if self._virtuals is None: |
| self.getvirtuals() |
| |
| modified = False |
| cp = Atom(cpv_getkey(mycpv)) |
| for virt in virts: |
| try: |
| virt = Atom(virt).cp |
| except InvalidAtom: |
| continue |
| providers = self._virtuals.get(virt) |
| if providers and cp in providers: |
| continue |
| providers = self._depgraphVirtuals.get(virt) |
| if providers is None: |
| providers = [] |
| self._depgraphVirtuals[virt] = providers |
| if cp not in providers: |
| providers.append(cp) |
| modified = True |
| |
| if modified: |
| self._compile_virtuals() |
| |
| def get_virts_p(self): |
| if self._virts_p is not None: |
| return self._virts_p |
| |
| virts = self.getvirtuals() |
| virts_p = {} |
| for x in virts: |
| vkeysplit = x.split("/") |
| if vkeysplit[1] not in virts_p: |
| virts_p[vkeysplit[1]] = virts[x] |
| self._virts_p = virts_p |
| return virts_p |