1 # Copyright 1999-2012 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_separator, _repo_separator
12 from portage.versions import _pkg_str, _unknown_repo
13 from portage.eapi import _get_eapi_attrs
14 from portage.exception import InvalidDependString
15 from portage.localization import _
16 from _emerge.Task import Task
18 if sys.hexversion >= 0x3000000:
27 __hash__ = Task.__hash__
28 __slots__ = ("built", "cpv", "depth",
29 "installed", "metadata", "onlydeps", "operation",
30 "root_config", "type_name",
31 "category", "counter", "cp", "cpv_split",
32 "inherited", "iuse", "mtime",
33 "pf", "root", "slot", "sub_slot", "slot_atom", "version") + \
34 ("_invalid", "_raw_metadata", "_masks", "_use",
35 "_validated_atoms", "_visible")
38 "BUILD_TIME", "CHOST", "COUNTER", "DEPEND", "EAPI",
39 "HDEPEND", "INHERITED", "IUSE", "KEYWORDS",
40 "LICENSE", "PDEPEND", "PROVIDE", "RDEPEND",
41 "repository", "PROPERTIES", "RESTRICT", "SLOT", "USE",
42 "_mtime_", "DEFINED_PHASES", "REQUIRED_USE"]
44 _dep_keys = ('DEPEND', 'HDEPEND' 'PDEPEND', 'RDEPEND',)
45 _use_conditional_misc_keys = ('LICENSE', 'PROPERTIES', 'RESTRICT')
46 UNKNOWN_REPO = _unknown_repo
48 def __init__(self, **kwargs):
49 Task.__init__(self, **kwargs)
50 # the SlotObject constructor assigns self.root_config from keyword args
51 # and is an instance of a '_emerge.RootConfig.RootConfig class
52 self.root = self.root_config.root
53 self._raw_metadata = _PackageMetadataWrapperBase(self.metadata)
54 self.metadata = _PackageMetadataWrapper(self, self._raw_metadata)
56 self.metadata['CHOST'] = self.root_config.settings.get('CHOST', '')
57 eapi_attrs = _get_eapi_attrs(self.metadata["EAPI"])
58 self.cpv = _pkg_str(self.cpv, metadata=self.metadata,
59 settings=self.root_config.settings)
60 if hasattr(self.cpv, 'slot_invalid'):
61 self._invalid_metadata('SLOT.invalid',
62 "SLOT: invalid value: '%s'" % self.metadata["SLOT"])
64 self.slot = self.cpv.slot
65 self.sub_slot = self.cpv.sub_slot
66 # sync metadata with validated repo (may be UNKNOWN_REPO)
67 self.metadata['repository'] = self.cpv.repo
69 if eapi_attrs.iuse_effective:
70 implicit_match = self.root_config.settings._iuse_effective_match
72 implicit_match = self.root_config.settings._iuse_implicit_match
73 self.iuse = self._iuse(self.metadata["IUSE"].split(), implicit_match)
75 if (self.iuse.enabled or self.iuse.disabled) and \
76 not eapi_attrs.iuse_defaults:
77 if not self.installed:
78 self._invalid_metadata('EAPI.incompatible',
79 "IUSE contains defaults, but EAPI doesn't allow them")
80 self.slot_atom = Atom("%s%s%s" % (self.cp, _slot_separator, self.slot))
81 self.category, self.pf = portage.catsplit(self.cpv)
82 self.cpv_split = self.cpv.cpv_split
83 self.version = self.cpv.version
84 if self.inherited is None:
85 self.inherited = frozenset()
87 if self.operation is None:
88 if self.onlydeps or self.installed:
89 self.operation = "nomerge"
91 self.operation = "merge"
93 self._hash_key = Package._gen_hash_key(cpv=self.cpv,
94 installed=self.installed, onlydeps=self.onlydeps,
95 operation=self.operation, repo_name=self.cpv.repo,
96 root_config=self.root_config,
97 type_name=self.type_name)
98 self._hash_value = hash(self._hash_key)
100 # For consistency with _pkg_str
105 # These are calculated on-demand, so that they are calculated
106 # after FakeVartree applies its metadata tweaks.
109 if self._invalid is None:
110 self._validate_deps()
111 if self._invalid is None:
112 self._invalid = False
117 if self._masks is None:
118 self._masks = self._eval_masks()
123 if self._visible is None:
124 self._visible = self._eval_visiblity(self.masks)
128 def validated_atoms(self):
130 Returns *all* validated atoms from the deps, regardless
131 of USE conditionals, with USE conditionals inside
132 atoms left unevaluated.
134 if self._validated_atoms is None:
135 self._validate_deps()
136 return self._validated_atoms
140 return self.cpv.stable
143 def _gen_hash_key(cls, cpv=None, installed=None, onlydeps=None,
144 operation=None, repo_name=None, root_config=None,
145 type_name=None, **kwargs):
147 if operation is None:
148 if installed or onlydeps:
149 operation = "nomerge"
154 if root_config is not None:
155 root = root_config.root
157 raise TypeError("root_config argument is required")
159 # For installed (and binary) packages we don't care for the repo
160 # when it comes to hashing, because there can only be one cpv.
161 # So overwrite the repo_key with type_name.
162 if type_name is None:
163 raise TypeError("type_name argument is required")
164 elif type_name == "ebuild":
165 if repo_name is None:
166 raise AssertionError(
167 "Package._gen_hash_key() " + \
168 "called without 'repo_name' argument")
171 # For installed (and binary) packages we don't care for the repo
172 # when it comes to hashing, because there can only be one cpv.
173 # So overwrite the repo_key with type_name.
176 return (type_name, root, _unicode(cpv), operation, repo_key)
178 def _validate_deps(self):
180 Validate deps. This does not trigger USE calculation since that
181 is expensive for ebuilds and therefore we want to avoid doing
182 in unnecessarily (like for masked packages).
184 eapi = self.metadata['EAPI']
186 dep_valid_flag = self.iuse.is_valid_flag
188 # Ignore EAPI.incompatible and conditionals missing
189 # from IUSE for installed packages since these issues
190 # aren't relevant now (re-evaluate when new EAPIs are
193 dep_valid_flag = None
196 for k in self._dep_keys:
197 v = self.metadata.get(k)
201 atoms = use_reduce(v, eapi=dep_eapi,
202 matchall=True, is_valid_flag=dep_valid_flag,
203 token_class=Atom, flat=True)
204 except InvalidDependString as e:
205 self._metadata_exception(k, e)
207 validated_atoms.extend(atoms)
210 if not isinstance(atom, Atom):
212 if atom.slot_operator_built:
213 e = InvalidDependString(
214 _("Improper context for slot-operator "
215 "\"built\" atom syntax: %s") %
216 (atom.unevaluated_atom,))
217 self._metadata_exception(k, e)
219 self._validated_atoms = tuple(set(atom for atom in
220 validated_atoms if isinstance(atom, Atom)))
223 v = self.metadata.get(k)
226 use_reduce(v, eapi=dep_eapi, matchall=True,
227 is_valid_flag=dep_valid_flag, token_class=Atom)
228 except InvalidDependString as e:
229 self._invalid_metadata("PROVIDE.syntax",
230 _unicode_decode("%s: %s") % (k, e))
232 for k in self._use_conditional_misc_keys:
233 v = self.metadata.get(k)
237 use_reduce(v, eapi=dep_eapi, matchall=True,
238 is_valid_flag=dep_valid_flag)
239 except InvalidDependString as e:
240 self._metadata_exception(k, e)
243 v = self.metadata.get(k)
244 if v and not self.built:
245 if not _get_eapi_attrs(eapi).required_use:
246 self._invalid_metadata('EAPI.incompatible',
247 "REQUIRED_USE set, but EAPI='%s' doesn't allow it" % eapi)
250 check_required_use(v, (),
251 self.iuse.is_valid_flag, eapi=eapi)
252 except InvalidDependString as e:
253 # Force unicode format string for python-2.x safety,
254 # ensuring that PortageException.__unicode__() is used
256 self._invalid_metadata(k + ".syntax",
257 _unicode_decode("%s: %s") % (k, e))
260 v = self.metadata.get(k)
263 use_reduce(v, is_src_uri=True, eapi=eapi, matchall=True,
264 is_valid_flag=self.iuse.is_valid_flag)
265 except InvalidDependString as e:
266 if not self.installed:
267 self._metadata_exception(k, e)
270 return Package(built=self.built, cpv=self.cpv, depth=self.depth,
271 installed=self.installed, metadata=self._raw_metadata,
272 onlydeps=self.onlydeps, operation=self.operation,
273 root_config=self.root_config, type_name=self.type_name)
275 def _eval_masks(self):
277 settings = self.root_config.settings
279 if self.invalid is not False:
280 masks['invalid'] = self.invalid
282 if not settings._accept_chost(self.cpv, self.metadata):
283 masks['CHOST'] = self.metadata['CHOST']
285 eapi = self.metadata["EAPI"]
286 if not portage.eapi_is_supported(eapi):
287 masks['EAPI.unsupported'] = eapi
288 if portage._eapi_is_deprecated(eapi):
289 masks['EAPI.deprecated'] = eapi
291 missing_keywords = settings._getMissingKeywords(
292 self.cpv, self.metadata)
294 masks['KEYWORDS'] = missing_keywords
297 missing_properties = settings._getMissingProperties(
298 self.cpv, self.metadata)
299 if missing_properties:
300 masks['PROPERTIES'] = missing_properties
301 except InvalidDependString:
302 # already recorded as 'invalid'
305 mask_atom = settings._getMaskAtom(self.cpv, self.metadata)
306 if mask_atom is not None:
307 masks['package.mask'] = mask_atom
310 missing_licenses = settings._getMissingLicenses(
311 self.cpv, self.metadata)
313 masks['LICENSE'] = missing_licenses
314 except InvalidDependString:
315 # already recorded as 'invalid'
323 def _eval_visiblity(self, masks):
325 if masks is not False:
327 if 'EAPI.unsupported' in masks:
330 if 'invalid' in masks:
333 if not self.installed and ( \
334 'CHOST' in masks or \
335 'EAPI.deprecated' in masks or \
336 'KEYWORDS' in masks or \
337 'PROPERTIES' in masks):
340 if 'package.mask' in masks or \
346 def get_keyword_mask(self):
347 """returns None, 'missing', or 'unstable'."""
349 missing = self.root_config.settings._getRawMissingKeywords(
350 self.cpv, self.metadata)
358 global_accept_keywords = frozenset(
359 self.root_config.settings.get("ACCEPT_KEYWORDS", "").split())
361 for keyword in missing:
362 if keyword.lstrip("~") in global_accept_keywords:
367 def isHardMasked(self):
368 """returns a bool if the cpv is in the list of
369 expanded pmaskdict[cp] available ebuilds"""
370 pmask = self.root_config.settings._getRawMaskAtom(
371 self.cpv, self.metadata)
372 return pmask is not None
374 def _metadata_exception(self, k, e):
376 if k.endswith('DEPEND'):
377 qacat = 'dependency.syntax'
379 qacat = k + ".syntax"
381 # For unicode safety with python-2.x we need to avoid
382 # using the string format operator with a non-unicode
383 # format string, since that will result in the
384 # PortageException.__str__() method being invoked,
385 # followed by unsafe decoding that may result in a
386 # UnicodeDecodeError. Therefore, use _unicode_decode()
387 # to ensure that format strings are unicode, so that
388 # PortageException.__unicode__() is used when necessary
390 if not self.installed:
391 categorized_error = False
393 for error in e.errors:
394 if getattr(error, 'category', None) is None:
396 categorized_error = True
397 self._invalid_metadata(error.category,
398 _unicode_decode("%s: %s") % (k, error))
400 if not categorized_error:
401 self._invalid_metadata(qacat,
402 _unicode_decode("%s: %s") % (k, e))
404 # For installed packages, show the path of the file
405 # containing the invalid metadata, since the user may
406 # want to fix the deps by hand.
407 vardb = self.root_config.trees['vartree'].dbapi
408 path = vardb.getpath(self.cpv, filename=k)
409 self._invalid_metadata(qacat,
410 _unicode_decode("%s: %s in '%s'") % (k, e, path))
412 def _invalid_metadata(self, msg_type, msg):
413 if self._invalid is None:
415 msgs = self._invalid.get(msg_type)
418 self._invalid[msg_type] = msgs
422 if self.operation == "merge":
423 if self.type_name == "binary":
424 cpv_color = "PKG_BINARY_MERGE"
426 cpv_color = "PKG_MERGE"
427 elif self.operation == "uninstall":
428 cpv_color = "PKG_UNINSTALL"
430 cpv_color = "PKG_NOMERGE"
433 % (portage.output.colorize(cpv_color, self.cpv + _repo_separator + self.repo) , self.type_name)
435 if self.type_name == "installed":
436 if self.root_config.settings['ROOT'] != "/":
437 s += " in '%s'" % self.root_config.settings['ROOT']
438 if self.operation == "uninstall":
439 s += " scheduled for uninstall"
441 if self.operation == "merge":
442 s += " scheduled for merge"
443 if self.root_config.settings['ROOT'] != "/":
444 s += " to '%s'" % self.root_config.settings['ROOT']
448 if sys.hexversion < 0x3000000:
450 __unicode__ = __str__
453 return _unicode_encode(self.__unicode__(),
454 encoding=_encodings['content'])
456 class _use_class(object):
458 __slots__ = ("enabled", "_expand", "_expand_hidden",
459 "_force", "_pkg", "_mask")
461 # Share identical frozenset instances when available.
464 def __init__(self, pkg, use_str):
467 self._expand_hidden = None
470 self.enabled = frozenset(use_str.split())
472 # Use IUSE to validate USE settings for built packages,
473 # in case the package manager that built this package
474 # failed to do that for some reason (or in case of
476 missing_iuse = pkg.iuse.get_missing_iuse(self.enabled)
478 self.enabled = self.enabled.difference(missing_iuse)
480 def _init_force_mask(self):
481 pkgsettings = self._pkg._get_pkgsettings()
482 frozensets = self._frozensets
484 pkgsettings.get("USE_EXPAND", "").lower().split())
485 self._expand = frozensets.setdefault(s, s)
487 pkgsettings.get("USE_EXPAND_HIDDEN", "").lower().split())
488 self._expand_hidden = frozensets.setdefault(s, s)
489 s = pkgsettings.useforce
490 self._force = frozensets.setdefault(s, s)
491 s = pkgsettings.usemask
492 self._mask = frozensets.setdefault(s, s)
496 if self._expand is None:
497 self._init_force_mask()
501 def expand_hidden(self):
502 if self._expand_hidden is None:
503 self._init_force_mask()
504 return self._expand_hidden
508 if self._force is None:
509 self._init_force_mask()
514 if self._mask is None:
515 self._init_force_mask()
520 return self.metadata['repository']
523 def repo_priority(self):
524 repo_info = self.root_config.settings.repositories.prepos.get(self.repo)
525 if repo_info is None:
527 return repo_info.priority
531 if self._use is None:
532 self.metadata._init_use()
535 def _get_pkgsettings(self):
536 pkgsettings = self.root_config.trees[
537 'porttree'].dbapi.doebuild_settings
538 pkgsettings.setcpv(self)
543 __slots__ = ("__weakref__", "all", "enabled", "disabled",
544 "tokens") + ("_iuse_implicit_match",)
546 def __init__(self, tokens, iuse_implicit_match):
547 self.tokens = tuple(tokens)
548 self._iuse_implicit_match = iuse_implicit_match
555 enabled.append(x[1:])
557 disabled.append(x[1:])
560 self.enabled = frozenset(enabled)
561 self.disabled = frozenset(disabled)
562 self.all = frozenset(chain(enabled, disabled, other))
564 def is_valid_flag(self, flags):
566 @return: True if all flags are valid USE values which may
567 be specified in USE dependencies, False otherwise.
569 if isinstance(flags, basestring):
573 if not flag in self.all and \
574 not self._iuse_implicit_match(flag):
578 def get_missing_iuse(self, flags):
580 @return: A list of flags missing from IUSE.
582 if isinstance(flags, basestring):
586 if not flag in self.all and \
587 not self._iuse_implicit_match(flag):
588 missing_iuse.append(flag)
596 This is used to generate mtimedb resume mergelist entries, so we
597 limit it to 4 items for backward compatibility.
599 return iter(self._hash_key[:4])
601 def __lt__(self, other):
602 if other.cp != self.cp:
604 if portage.vercmp(self.version, other.version) < 0:
608 def __le__(self, other):
609 if other.cp != self.cp:
611 if portage.vercmp(self.version, other.version) <= 0:
615 def __gt__(self, other):
616 if other.cp != self.cp:
618 if portage.vercmp(self.version, other.version) > 0:
622 def __ge__(self, other):
623 if other.cp != self.cp:
625 if portage.vercmp(self.version, other.version) >= 0:
629 _all_metadata_keys = set(x for x in portage.auxdbkeys \
630 if not x.startswith("UNUSED_"))
631 _all_metadata_keys.update(Package.metadata_keys)
632 _all_metadata_keys = frozenset(_all_metadata_keys)
634 _PackageMetadataWrapperBase = slot_dict_class(_all_metadata_keys)
636 class _PackageMetadataWrapper(_PackageMetadataWrapperBase):
638 Detect metadata updates and synchronize Package attributes.
641 __slots__ = ("_pkg",)
642 _wrapped_keys = frozenset(
643 ["COUNTER", "INHERITED", "USE", "_mtime_"])
644 _use_conditional_keys = frozenset(
645 ['LICENSE', 'PROPERTIES', 'PROVIDE', 'RESTRICT',])
647 def __init__(self, pkg, metadata):
648 _PackageMetadataWrapperBase.__init__(self)
651 # USE is lazy, but we want it to show up in self.keys().
652 _PackageMetadataWrapperBase.__setitem__(self, 'USE', '')
654 self.update(metadata)
658 use_str = self['USE']
659 self._pkg._use = self._pkg._use_class(
663 use_str = _PackageMetadataWrapperBase.__getitem__(self, 'USE')
666 calculated_use = False
668 use_str = self._pkg._get_pkgsettings()["PORTAGE_USE"]
669 calculated_use = True
670 _PackageMetadataWrapperBase.__setitem__(self, 'USE', use_str)
671 self._pkg._use = self._pkg._use_class(
673 # Initialize these now, since USE access has just triggered
674 # setcpv, and we want to cache the result of the force/mask
675 # calculations that were done.
677 self._pkg._use._init_force_mask()
681 def __getitem__(self, k):
682 v = _PackageMetadataWrapperBase.__getitem__(self, k)
683 if k in self._use_conditional_keys:
684 if self._pkg.root_config.settings.local_config and '?' in v:
686 v = paren_enclose(use_reduce(v, uselist=self._pkg.use.enabled, \
687 is_valid_flag=self._pkg.iuse.is_valid_flag))
688 except InvalidDependString:
689 # This error should already have been registered via
690 # self._pkg._invalid_metadata().
695 elif k == 'USE' and not self._pkg.built:
697 # This is lazy because it's expensive.
702 def __setitem__(self, k, v):
703 _PackageMetadataWrapperBase.__setitem__(self, k, v)
704 if k in self._wrapped_keys:
705 getattr(self, "_set_" + k.lower())(k, v)
707 def _set_inherited(self, k, v):
708 if isinstance(v, basestring):
709 v = frozenset(v.split())
710 self._pkg.inherited = v
712 def _set_counter(self, k, v):
713 if isinstance(v, basestring):
718 self._pkg.counter = v
720 def _set_use(self, k, v):
721 # Force regeneration of _use attribute
722 self._pkg._use = None
723 # Use raw metadata to restore USE conditional values
724 # to unevaluated state
725 raw_metadata = self._pkg._raw_metadata
726 for x in self._use_conditional_keys:
728 self[x] = raw_metadata[x]
732 def _set__mtime_(self, k, v):
733 if isinstance(v, basestring):
741 def properties(self):
742 return self['PROPERTIES'].split()
746 return self['RESTRICT'].split()
749 def defined_phases(self):
751 Returns tokens from DEFINED_PHASES metadata if it is defined,
752 otherwise returns a tuple containing all possible phases. This
753 makes it easy to do containment checks to see if it's safe to
754 skip execution of a given phase.
756 s = self['DEFINED_PHASES']