1 # Copyright 1999-2011 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
5 from itertools import chain
7 from portage import _encodings, _unicode_decode, _unicode_encode
8 from portage.cache.mappings import slot_dict_class
9 from portage.const import EBUILD_PHASES
10 from portage.dep import Atom, check_required_use, use_reduce, \
11 paren_enclose, _slot_re, _slot_separator, _repo_separator
12 from portage.eapi import eapi_has_iuse_defaults, eapi_has_required_use
13 from portage.exception import InvalidDependString
14 from portage.repository.config import _gen_valid_repo
15 from _emerge.Task import Task
17 if sys.hexversion >= 0x3000000:
23 __hash__ = Task.__hash__
24 __slots__ = ("built", "cpv", "depth",
25 "installed", "metadata", "onlydeps", "operation",
26 "root_config", "type_name",
27 "category", "counter", "cp", "cpv_split",
28 "inherited", "invalid", "iuse", "masks", "mtime",
29 "pf", "pv_split", "root", "slot", "slot_atom", "visible",) + \
30 ("_raw_metadata", "_use",)
33 "BUILD_TIME", "CHOST", "COUNTER", "DEPEND", "EAPI",
34 "INHERITED", "IUSE", "KEYWORDS",
35 "LICENSE", "PDEPEND", "PROVIDE", "RDEPEND",
36 "repository", "PROPERTIES", "RESTRICT", "SLOT", "USE",
37 "_mtime_", "DEFINED_PHASES", "REQUIRED_USE"]
39 _dep_keys = ('DEPEND', 'PDEPEND', 'RDEPEND',)
40 _use_conditional_misc_keys = ('LICENSE', 'PROPERTIES', 'RESTRICT')
41 UNKNOWN_REPO = "__unknown__"
43 def __init__(self, **kwargs):
44 Task.__init__(self, **kwargs)
45 # the SlotObject constructor assigns self.root_config from keyword args
46 # and is an instance of a '_emerge.RootConfig.RootConfig class
47 self.root = self.root_config.root
48 self._raw_metadata = _PackageMetadataWrapperBase(self.metadata)
49 self.metadata = _PackageMetadataWrapper(self, self._raw_metadata)
51 self.metadata['CHOST'] = self.root_config.settings.get('CHOST', '')
52 self.cp = portage.cpv_getkey(self.cpv)
54 if _slot_re.match(slot) is None:
55 self._invalid_metadata('SLOT.invalid',
56 "SLOT: invalid value: '%s'" % slot)
57 # Avoid an InvalidAtom exception when creating slot_atom.
58 # This package instance will be masked due to empty SLOT.
60 if (self.iuse.enabled or self.iuse.disabled) and \
61 not eapi_has_iuse_defaults(self.metadata["EAPI"]):
62 if not self.installed:
63 self._invalid_metadata('EAPI.incompatible',
64 "IUSE contains defaults, but EAPI doesn't allow them")
65 self.slot_atom = portage.dep.Atom("%s%s%s" % (self.cp, _slot_separator, slot))
66 self.category, self.pf = portage.catsplit(self.cpv)
67 self.cpv_split = portage.catpkgsplit(self.cpv)
68 self.pv_split = self.cpv_split[1:]
69 if self.inherited is None:
70 self.inherited = frozenset()
71 repo = _gen_valid_repo(self.metadata.get('repository', ''))
73 repo = self.UNKNOWN_REPO
74 self.metadata['repository'] = repo
77 self.masks = self._masks()
78 self.visible = self._visible(self.masks)
79 if self.operation is None:
80 if self.onlydeps or self.installed:
81 self.operation = "nomerge"
83 self.operation = "merge"
85 self._hash_key = Package._gen_hash_key(cpv=self.cpv,
86 installed=self.installed, onlydeps=self.onlydeps,
87 operation=self.operation, repo_name=repo,
88 root_config=self.root_config,
89 type_name=self.type_name)
90 self._hash_value = hash(self._hash_key)
93 def _gen_hash_key(cls, cpv=None, installed=None, onlydeps=None,
94 operation=None, repo_name=None, root_config=None,
95 type_name=None, **kwargs):
98 if installed or onlydeps:
104 if root_config is not None:
105 root = root_config.root
107 raise TypeError("root_config argument is required")
109 # For installed (and binary) packages we don't care for the repo
110 # when it comes to hashing, because there can only be one cpv.
111 # So overwrite the repo_key with type_name.
112 if type_name is None:
113 raise TypeError("type_name argument is required")
114 elif type_name == "ebuild":
115 if repo_name is None:
116 raise AssertionError(
117 "Package._gen_hash_key() " + \
118 "called without 'repo_name' argument")
121 # For installed (and binary) packages we don't care for the repo
122 # when it comes to hashing, because there can only be one cpv.
123 # So overwrite the repo_key with type_name.
126 return (type_name, root, cpv, operation, repo_key)
128 def _validate_deps(self):
130 Validate deps. This does not trigger USE calculation since that
131 is expensive for ebuilds and therefore we want to avoid doing
132 in unnecessarily (like for masked packages).
134 eapi = self.metadata['EAPI']
136 dep_valid_flag = self.iuse.is_valid_flag
138 # Ignore EAPI.incompatible and conditionals missing
139 # from IUSE for installed packages since these issues
140 # aren't relevant now (re-evaluate when new EAPIs are
143 dep_valid_flag = None
145 for k in self._dep_keys:
146 v = self.metadata.get(k)
150 use_reduce(v, eapi=dep_eapi, matchall=True,
151 is_valid_flag=dep_valid_flag, token_class=Atom)
152 except InvalidDependString as e:
153 self._metadata_exception(k, e)
156 v = self.metadata.get(k)
159 use_reduce(v, eapi=dep_eapi, matchall=True,
160 is_valid_flag=dep_valid_flag, token_class=Atom)
161 except InvalidDependString as e:
162 self._invalid_metadata("PROVIDE.syntax",
163 _unicode_decode("%s: %s") % (k, e))
165 for k in self._use_conditional_misc_keys:
166 v = self.metadata.get(k)
170 use_reduce(v, eapi=dep_eapi, matchall=True,
171 is_valid_flag=dep_valid_flag)
172 except InvalidDependString as e:
173 self._metadata_exception(k, e)
176 v = self.metadata.get(k)
178 if not eapi_has_required_use(eapi):
179 self._invalid_metadata('EAPI.incompatible',
180 "REQUIRED_USE set, but EAPI='%s' doesn't allow it" % eapi)
183 check_required_use(v, (),
184 self.iuse.is_valid_flag)
185 except InvalidDependString as e:
186 # Force unicode format string for python-2.x safety,
187 # ensuring that PortageException.__unicode__() is used
189 self._invalid_metadata(k + ".syntax",
190 _unicode_decode("%s: %s") % (k, e))
193 v = self.metadata.get(k)
196 use_reduce(v, is_src_uri=True, eapi=eapi, matchall=True,
197 is_valid_flag=self.iuse.is_valid_flag)
198 except InvalidDependString as e:
199 if not self.installed:
200 self._metadata_exception(k, e)
203 return Package(built=self.built, cpv=self.cpv, depth=self.depth,
204 installed=self.installed, metadata=self._raw_metadata,
205 onlydeps=self.onlydeps, operation=self.operation,
206 root_config=self.root_config, type_name=self.type_name)
210 settings = self.root_config.settings
212 if self.invalid is not None:
213 masks['invalid'] = self.invalid
215 if not settings._accept_chost(self.cpv, self.metadata):
216 masks['CHOST'] = self.metadata['CHOST']
218 eapi = self.metadata["EAPI"]
219 if not portage.eapi_is_supported(eapi):
220 masks['EAPI.unsupported'] = eapi
221 if portage._eapi_is_deprecated(eapi):
222 masks['EAPI.deprecated'] = eapi
224 missing_keywords = settings._getMissingKeywords(
225 self.cpv, self.metadata)
227 masks['KEYWORDS'] = missing_keywords
230 missing_properties = settings._getMissingProperties(
231 self.cpv, self.metadata)
232 if missing_properties:
233 masks['PROPERTIES'] = missing_properties
234 except InvalidDependString:
235 # already recorded as 'invalid'
238 mask_atom = settings._getMaskAtom(self.cpv, self.metadata)
239 if mask_atom is not None:
240 masks['package.mask'] = mask_atom
243 missing_licenses = settings._getMissingLicenses(
244 self.cpv, self.metadata)
246 masks['LICENSE'] = missing_licenses
247 except InvalidDependString:
248 # already recorded as 'invalid'
256 def _visible(self, masks):
258 if masks is not None:
260 if 'EAPI.unsupported' in masks:
263 if 'invalid' in masks:
266 if not self.installed and ( \
267 'CHOST' in masks or \
268 'EAPI.deprecated' in masks or \
269 'KEYWORDS' in masks or \
270 'PROPERTIES' in masks):
273 if 'package.mask' in masks or \
279 def get_keyword_mask(self):
280 """returns None, 'missing', or 'unstable'."""
282 missing = self.root_config.settings._getRawMissingKeywords(
283 self.cpv, self.metadata)
291 global_accept_keywords = frozenset(
292 self.root_config.settings.get("ACCEPT_KEYWORDS", "").split())
294 for keyword in missing:
295 if keyword.lstrip("~") in global_accept_keywords:
300 def isHardMasked(self):
301 """returns a bool if the cpv is in the list of
302 expanded pmaskdict[cp] available ebuilds"""
303 pmask = self.root_config.settings._getRawMaskAtom(
304 self.cpv, self.metadata)
305 return pmask is not None
307 def _metadata_exception(self, k, e):
309 # For unicode safety with python-2.x we need to avoid
310 # using the string format operator with a non-unicode
311 # format string, since that will result in the
312 # PortageException.__str__() method being invoked,
313 # followed by unsafe decoding that may result in a
314 # UnicodeDecodeError. Therefore, use _unicode_decode()
315 # to ensure that format strings are unicode, so that
316 # PortageException.__unicode__() is used when necessary
318 if not self.installed:
319 categorized_error = False
321 for error in e.errors:
322 if getattr(error, 'category', None) is None:
324 categorized_error = True
325 self._invalid_metadata(error.category,
326 _unicode_decode("%s: %s") % (k, error))
328 if not categorized_error:
329 self._invalid_metadata(k + ".syntax",
330 _unicode_decode("%s: %s") % (k, e))
332 # For installed packages, show the path of the file
333 # containing the invalid metadata, since the user may
334 # want to fix the deps by hand.
335 vardb = self.root_config.trees['vartree'].dbapi
336 path = vardb.getpath(self.cpv, filename=k)
337 self._invalid_metadata(k + ".syntax",
338 _unicode_decode("%s: %s in '%s'") % (k, e, path))
340 def _invalid_metadata(self, msg_type, msg):
341 if self.invalid is None:
343 msgs = self.invalid.get(msg_type)
346 self.invalid[msg_type] = msgs
350 if self.operation == "merge":
351 if self.type_name == "binary":
352 cpv_color = "PKG_BINARY_MERGE"
354 cpv_color = "PKG_MERGE"
355 elif self.operation == "uninstall":
356 cpv_color = "PKG_UNINSTALL"
358 cpv_color = "PKG_NOMERGE"
361 % (portage.output.colorize(cpv_color, self.cpv + _repo_separator + self.repo) , self.type_name)
363 if self.type_name == "installed":
365 s += " in '%s'" % self.root
366 if self.operation == "uninstall":
367 s += " scheduled for uninstall"
369 if self.operation == "merge":
370 s += " scheduled for merge"
372 s += " to '%s'" % self.root
376 if sys.hexversion < 0x3000000:
378 __unicode__ = __str__
381 return _unicode_encode(self.__unicode__(),
382 encoding=_encodings['content'])
384 class _use_class(object):
386 __slots__ = ("enabled", "_expand", "_expand_hidden",
387 "_force", "_pkg", "_mask")
389 # Share identical frozenset instances when available.
392 def __init__(self, pkg, use_str):
395 self._expand_hidden = None
398 self.enabled = frozenset(use_str.split())
400 # Use IUSE to validate USE settings for built packages,
401 # in case the package manager that built this package
402 # failed to do that for some reason (or in case of
404 missing_iuse = pkg.iuse.get_missing_iuse(self.enabled)
406 self.enabled = self.enabled.difference(missing_iuse)
408 def _init_force_mask(self):
409 pkgsettings = self._pkg._get_pkgsettings()
410 frozensets = self._frozensets
412 pkgsettings.get("USE_EXPAND", "").lower().split())
413 self._expand = frozensets.setdefault(s, s)
415 pkgsettings.get("USE_EXPAND_HIDDEN", "").lower().split())
416 self._expand_hidden = frozensets.setdefault(s, s)
417 s = pkgsettings.useforce
418 self._force = frozensets.setdefault(s, s)
419 s = pkgsettings.usemask
420 self._mask = frozensets.setdefault(s, s)
424 if self._expand is None:
425 self._init_force_mask()
429 def expand_hidden(self):
430 if self._expand_hidden is None:
431 self._init_force_mask()
432 return self._expand_hidden
436 if self._force is None:
437 self._init_force_mask()
442 if self._mask is None:
443 self._init_force_mask()
448 return self.metadata['repository']
451 def repo_priority(self):
452 repo_info = self.root_config.settings.repositories.prepos.get(self.repo)
453 if repo_info is None:
455 return repo_info.priority
459 if self._use is None:
460 self.metadata._init_use()
463 def _get_pkgsettings(self):
464 pkgsettings = self.root_config.trees[
465 'porttree'].dbapi.doebuild_settings
466 pkgsettings.setcpv(self)
471 __slots__ = ("__weakref__", "all", "enabled", "disabled",
472 "tokens") + ("_iuse_implicit_match",)
474 def __init__(self, tokens, iuse_implicit_match):
475 self.tokens = tuple(tokens)
476 self._iuse_implicit_match = iuse_implicit_match
483 enabled.append(x[1:])
485 disabled.append(x[1:])
488 self.enabled = frozenset(enabled)
489 self.disabled = frozenset(disabled)
490 self.all = frozenset(chain(enabled, disabled, other))
492 def is_valid_flag(self, flags):
494 @returns: True if all flags are valid USE values which may
495 be specified in USE dependencies, False otherwise.
497 if isinstance(flags, basestring):
501 if not flag in self.all and \
502 not self._iuse_implicit_match(flag):
506 def get_missing_iuse(self, flags):
508 @returns: A list of flags missing from IUSE.
510 if isinstance(flags, basestring):
514 if not flag in self.all and \
515 not self._iuse_implicit_match(flag):
516 missing_iuse.append(flag)
524 This is used to generate mtimedb resume mergelist entries, so we
525 limit it to 4 items for backward compatibility.
527 return iter(self._hash_key[:4])
529 def __lt__(self, other):
530 if other.cp != self.cp:
532 if portage.pkgcmp(self.pv_split, other.pv_split) < 0:
536 def __le__(self, other):
537 if other.cp != self.cp:
539 if portage.pkgcmp(self.pv_split, other.pv_split) <= 0:
543 def __gt__(self, other):
544 if other.cp != self.cp:
546 if portage.pkgcmp(self.pv_split, other.pv_split) > 0:
550 def __ge__(self, other):
551 if other.cp != self.cp:
553 if portage.pkgcmp(self.pv_split, other.pv_split) >= 0:
557 _all_metadata_keys = set(x for x in portage.auxdbkeys \
558 if not x.startswith("UNUSED_"))
559 _all_metadata_keys.update(Package.metadata_keys)
560 _all_metadata_keys = frozenset(_all_metadata_keys)
562 _PackageMetadataWrapperBase = slot_dict_class(_all_metadata_keys)
564 class _PackageMetadataWrapper(_PackageMetadataWrapperBase):
566 Detect metadata updates and synchronize Package attributes.
569 __slots__ = ("_pkg",)
570 _wrapped_keys = frozenset(
571 ["COUNTER", "INHERITED", "IUSE", "SLOT", "USE", "_mtime_"])
572 _use_conditional_keys = frozenset(
573 ['LICENSE', 'PROPERTIES', 'PROVIDE', 'RESTRICT',])
575 def __init__(self, pkg, metadata):
576 _PackageMetadataWrapperBase.__init__(self)
579 # USE is lazy, but we want it to show up in self.keys().
580 _PackageMetadataWrapperBase.__setitem__(self, 'USE', '')
582 self.update(metadata)
586 use_str = self['USE']
587 self._pkg._use = self._pkg._use_class(
591 use_str = _PackageMetadataWrapperBase.__getitem__(self, 'USE')
594 calculated_use = False
596 use_str = self._pkg._get_pkgsettings()["PORTAGE_USE"]
597 calculated_use = True
598 _PackageMetadataWrapperBase.__setitem__(self, 'USE', use_str)
599 self._pkg._use = self._pkg._use_class(
601 # Initialize these now, since USE access has just triggered
602 # setcpv, and we want to cache the result of the force/mask
603 # calculations that were done.
605 self._pkg._use._init_force_mask()
609 def __getitem__(self, k):
610 v = _PackageMetadataWrapperBase.__getitem__(self, k)
611 if k in self._use_conditional_keys:
612 if self._pkg.root_config.settings.local_config and '?' in v:
614 v = paren_enclose(use_reduce(v, uselist=self._pkg.use.enabled, \
615 is_valid_flag=self._pkg.iuse.is_valid_flag))
616 except InvalidDependString:
617 # This error should already have been registered via
618 # self._pkg._invalid_metadata().
623 elif k == 'USE' and not self._pkg.built:
625 # This is lazy because it's expensive.
630 def __setitem__(self, k, v):
631 _PackageMetadataWrapperBase.__setitem__(self, k, v)
632 if k in self._wrapped_keys:
633 getattr(self, "_set_" + k.lower())(k, v)
635 def _set_inherited(self, k, v):
636 if isinstance(v, basestring):
637 v = frozenset(v.split())
638 self._pkg.inherited = v
640 def _set_iuse(self, k, v):
641 self._pkg.iuse = self._pkg._iuse(
642 v.split(), self._pkg.root_config.settings._iuse_implicit_match)
644 def _set_slot(self, k, v):
647 def _set_counter(self, k, v):
648 if isinstance(v, basestring):
653 self._pkg.counter = v
655 def _set_use(self, k, v):
656 # Force regeneration of _use attribute
657 self._pkg._use = None
658 # Use raw metadata to restore USE conditional values
659 # to unevaluated state
660 raw_metadata = self._pkg._raw_metadata
661 for x in self._use_conditional_keys:
663 self[x] = raw_metadata[x]
667 def _set__mtime_(self, k, v):
668 if isinstance(v, basestring):
676 def properties(self):
677 return self['PROPERTIES'].split()
681 return self['RESTRICT'].split()
684 def defined_phases(self):
686 Returns tokens from DEFINED_PHASES metadata if it is defined,
687 otherwise returns a tuple containing all possible phases. This
688 makes it easy to do containment checks to see if it's safe to
689 skip execution of a given phase.
691 s = self['DEFINED_PHASES']