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 _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", "iuse", "mtime",
29 "pf", "root", "slot", "slot_atom", "version") + \
30 ("_invalid", "_raw_metadata", "_masks", "_use", "_visible")
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_repo
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 eapi_attrs = _get_eapi_attrs(self.metadata["EAPI"])
53 self.cpv = _pkg_str(self.cpv, slot=self.metadata["SLOT"],
54 repo=self.metadata.get('repository', ''),
55 eapi=self.metadata["EAPI"])
56 if hasattr(self.cpv, 'slot_invalid'):
57 self._invalid_metadata('SLOT.invalid',
58 "SLOT: invalid value: '%s'" % self.metadata["SLOT"])
60 self.slot = self.cpv.slot
61 # sync metadata with validated repo (may be UNKNOWN_REPO)
62 self.metadata['repository'] = self.cpv.repo
63 if (self.iuse.enabled or self.iuse.disabled) and \
64 not eapi_attrs.iuse_defaults:
65 if not self.installed:
66 self._invalid_metadata('EAPI.incompatible',
67 "IUSE contains defaults, but EAPI doesn't allow them")
68 self.slot_atom = Atom("%s%s%s" % (self.cp, _slot_separator, self.slot))
69 self.category, self.pf = portage.catsplit(self.cpv)
70 self.cpv_split = self.cpv.cpv_split
71 self.version = self.cpv.version
72 if self.inherited is None:
73 self.inherited = frozenset()
75 if self.operation is None:
76 if self.onlydeps or self.installed:
77 self.operation = "nomerge"
79 self.operation = "merge"
81 self._hash_key = Package._gen_hash_key(cpv=self.cpv,
82 installed=self.installed, onlydeps=self.onlydeps,
83 operation=self.operation, repo_name=self.cpv.repo,
84 root_config=self.root_config,
85 type_name=self.type_name)
86 self._hash_value = hash(self._hash_key)
88 # These are calculated on-demand, so that they are calculated
89 # after FakeVartree applies its metadata tweaks.
92 if self._invalid is None:
94 if self._invalid is None:
100 if self._masks is None:
101 self._masks = self._eval_masks()
106 if self._visible is None:
107 self._visible = self._eval_visiblity(self.masks)
111 def _gen_hash_key(cls, cpv=None, installed=None, onlydeps=None,
112 operation=None, repo_name=None, root_config=None,
113 type_name=None, **kwargs):
115 if operation is None:
116 if installed or onlydeps:
117 operation = "nomerge"
122 if root_config is not None:
123 root = root_config.root
125 raise TypeError("root_config argument is required")
127 # For installed (and binary) packages we don't care for the repo
128 # when it comes to hashing, because there can only be one cpv.
129 # So overwrite the repo_key with type_name.
130 if type_name is None:
131 raise TypeError("type_name argument is required")
132 elif type_name == "ebuild":
133 if repo_name is None:
134 raise AssertionError(
135 "Package._gen_hash_key() " + \
136 "called without 'repo_name' argument")
139 # For installed (and binary) packages we don't care for the repo
140 # when it comes to hashing, because there can only be one cpv.
141 # So overwrite the repo_key with type_name.
144 return (type_name, root, cpv, operation, repo_key)
146 def _validate_deps(self):
148 Validate deps. This does not trigger USE calculation since that
149 is expensive for ebuilds and therefore we want to avoid doing
150 in unnecessarily (like for masked packages).
152 eapi = self.metadata['EAPI']
154 dep_valid_flag = self.iuse.is_valid_flag
156 # Ignore EAPI.incompatible and conditionals missing
157 # from IUSE for installed packages since these issues
158 # aren't relevant now (re-evaluate when new EAPIs are
161 dep_valid_flag = None
163 for k in self._dep_keys:
164 v = self.metadata.get(k)
168 use_reduce(v, eapi=dep_eapi, matchall=True,
169 is_valid_flag=dep_valid_flag, token_class=Atom)
170 except InvalidDependString as e:
171 self._metadata_exception(k, e)
174 v = self.metadata.get(k)
177 use_reduce(v, eapi=dep_eapi, matchall=True,
178 is_valid_flag=dep_valid_flag, token_class=Atom)
179 except InvalidDependString as e:
180 self._invalid_metadata("PROVIDE.syntax",
181 _unicode_decode("%s: %s") % (k, e))
183 for k in self._use_conditional_misc_keys:
184 v = self.metadata.get(k)
188 use_reduce(v, eapi=dep_eapi, matchall=True,
189 is_valid_flag=dep_valid_flag)
190 except InvalidDependString as e:
191 self._metadata_exception(k, e)
194 v = self.metadata.get(k)
196 if not _get_eapi_attrs(eapi).required_use:
197 self._invalid_metadata('EAPI.incompatible',
198 "REQUIRED_USE set, but EAPI='%s' doesn't allow it" % eapi)
201 check_required_use(v, (),
202 self.iuse.is_valid_flag)
203 except InvalidDependString as e:
204 # Force unicode format string for python-2.x safety,
205 # ensuring that PortageException.__unicode__() is used
207 self._invalid_metadata(k + ".syntax",
208 _unicode_decode("%s: %s") % (k, e))
211 v = self.metadata.get(k)
214 use_reduce(v, is_src_uri=True, eapi=eapi, matchall=True,
215 is_valid_flag=self.iuse.is_valid_flag)
216 except InvalidDependString as e:
217 if not self.installed:
218 self._metadata_exception(k, e)
221 return Package(built=self.built, cpv=self.cpv, depth=self.depth,
222 installed=self.installed, metadata=self._raw_metadata,
223 onlydeps=self.onlydeps, operation=self.operation,
224 root_config=self.root_config, type_name=self.type_name)
226 def _eval_masks(self):
228 settings = self.root_config.settings
230 if self.invalid is not False:
231 masks['invalid'] = self.invalid
233 if not settings._accept_chost(self.cpv, self.metadata):
234 masks['CHOST'] = self.metadata['CHOST']
236 eapi = self.metadata["EAPI"]
237 if not portage.eapi_is_supported(eapi):
238 masks['EAPI.unsupported'] = eapi
239 if portage._eapi_is_deprecated(eapi):
240 masks['EAPI.deprecated'] = eapi
242 missing_keywords = settings._getMissingKeywords(
243 self.cpv, self.metadata)
245 masks['KEYWORDS'] = missing_keywords
248 missing_properties = settings._getMissingProperties(
249 self.cpv, self.metadata)
250 if missing_properties:
251 masks['PROPERTIES'] = missing_properties
252 except InvalidDependString:
253 # already recorded as 'invalid'
256 mask_atom = settings._getMaskAtom(self.cpv, self.metadata)
257 if mask_atom is not None:
258 masks['package.mask'] = mask_atom
261 missing_licenses = settings._getMissingLicenses(
262 self.cpv, self.metadata)
264 masks['LICENSE'] = missing_licenses
265 except InvalidDependString:
266 # already recorded as 'invalid'
274 def _eval_visiblity(self, masks):
276 if masks is not False:
278 if 'EAPI.unsupported' in masks:
281 if 'invalid' in masks:
284 if not self.installed and ( \
285 'CHOST' in masks or \
286 'EAPI.deprecated' in masks or \
287 'KEYWORDS' in masks or \
288 'PROPERTIES' in masks):
291 if 'package.mask' in masks or \
297 def get_keyword_mask(self):
298 """returns None, 'missing', or 'unstable'."""
300 missing = self.root_config.settings._getRawMissingKeywords(
301 self.cpv, self.metadata)
309 global_accept_keywords = frozenset(
310 self.root_config.settings.get("ACCEPT_KEYWORDS", "").split())
312 for keyword in missing:
313 if keyword.lstrip("~") in global_accept_keywords:
318 def isHardMasked(self):
319 """returns a bool if the cpv is in the list of
320 expanded pmaskdict[cp] available ebuilds"""
321 pmask = self.root_config.settings._getRawMaskAtom(
322 self.cpv, self.metadata)
323 return pmask is not None
325 def _metadata_exception(self, k, e):
327 # For unicode safety with python-2.x we need to avoid
328 # using the string format operator with a non-unicode
329 # format string, since that will result in the
330 # PortageException.__str__() method being invoked,
331 # followed by unsafe decoding that may result in a
332 # UnicodeDecodeError. Therefore, use _unicode_decode()
333 # to ensure that format strings are unicode, so that
334 # PortageException.__unicode__() is used when necessary
336 if not self.installed:
337 categorized_error = False
339 for error in e.errors:
340 if getattr(error, 'category', None) is None:
342 categorized_error = True
343 self._invalid_metadata(error.category,
344 _unicode_decode("%s: %s") % (k, error))
346 if not categorized_error:
347 self._invalid_metadata(k + ".syntax",
348 _unicode_decode("%s: %s") % (k, e))
350 # For installed packages, show the path of the file
351 # containing the invalid metadata, since the user may
352 # want to fix the deps by hand.
353 vardb = self.root_config.trees['vartree'].dbapi
354 path = vardb.getpath(self.cpv, filename=k)
355 self._invalid_metadata(k + ".syntax",
356 _unicode_decode("%s: %s in '%s'") % (k, e, path))
358 def _invalid_metadata(self, msg_type, msg):
359 if self._invalid is None:
361 msgs = self._invalid.get(msg_type)
364 self._invalid[msg_type] = msgs
368 if self.operation == "merge":
369 if self.type_name == "binary":
370 cpv_color = "PKG_BINARY_MERGE"
372 cpv_color = "PKG_MERGE"
373 elif self.operation == "uninstall":
374 cpv_color = "PKG_UNINSTALL"
376 cpv_color = "PKG_NOMERGE"
379 % (portage.output.colorize(cpv_color, self.cpv + _repo_separator + self.repo) , self.type_name)
381 if self.type_name == "installed":
382 if self.root_config.settings['ROOT'] != "/":
383 s += " in '%s'" % self.root_config.settings['ROOT']
384 if self.operation == "uninstall":
385 s += " scheduled for uninstall"
387 if self.operation == "merge":
388 s += " scheduled for merge"
389 if self.root_config.settings['ROOT'] != "/":
390 s += " to '%s'" % self.root_config.settings['ROOT']
394 if sys.hexversion < 0x3000000:
396 __unicode__ = __str__
399 return _unicode_encode(self.__unicode__(),
400 encoding=_encodings['content'])
402 class _use_class(object):
404 __slots__ = ("enabled", "_expand", "_expand_hidden",
405 "_force", "_pkg", "_mask")
407 # Share identical frozenset instances when available.
410 def __init__(self, pkg, use_str):
413 self._expand_hidden = None
416 self.enabled = frozenset(use_str.split())
418 # Use IUSE to validate USE settings for built packages,
419 # in case the package manager that built this package
420 # failed to do that for some reason (or in case of
422 missing_iuse = pkg.iuse.get_missing_iuse(self.enabled)
424 self.enabled = self.enabled.difference(missing_iuse)
426 def _init_force_mask(self):
427 pkgsettings = self._pkg._get_pkgsettings()
428 frozensets = self._frozensets
430 pkgsettings.get("USE_EXPAND", "").lower().split())
431 self._expand = frozensets.setdefault(s, s)
433 pkgsettings.get("USE_EXPAND_HIDDEN", "").lower().split())
434 self._expand_hidden = frozensets.setdefault(s, s)
435 s = pkgsettings.useforce
436 self._force = frozensets.setdefault(s, s)
437 s = pkgsettings.usemask
438 self._mask = frozensets.setdefault(s, s)
442 if self._expand is None:
443 self._init_force_mask()
447 def expand_hidden(self):
448 if self._expand_hidden is None:
449 self._init_force_mask()
450 return self._expand_hidden
454 if self._force is None:
455 self._init_force_mask()
460 if self._mask is None:
461 self._init_force_mask()
466 return self.metadata['repository']
469 def repo_priority(self):
470 repo_info = self.root_config.settings.repositories.prepos.get(self.repo)
471 if repo_info is None:
473 return repo_info.priority
477 if self._use is None:
478 self.metadata._init_use()
481 def _get_pkgsettings(self):
482 pkgsettings = self.root_config.trees[
483 'porttree'].dbapi.doebuild_settings
484 pkgsettings.setcpv(self)
489 __slots__ = ("__weakref__", "all", "enabled", "disabled",
490 "tokens") + ("_iuse_implicit_match",)
492 def __init__(self, tokens, iuse_implicit_match):
493 self.tokens = tuple(tokens)
494 self._iuse_implicit_match = iuse_implicit_match
501 enabled.append(x[1:])
503 disabled.append(x[1:])
506 self.enabled = frozenset(enabled)
507 self.disabled = frozenset(disabled)
508 self.all = frozenset(chain(enabled, disabled, other))
510 def is_valid_flag(self, flags):
512 @return: True if all flags are valid USE values which may
513 be specified in USE dependencies, False otherwise.
515 if isinstance(flags, basestring):
519 if not flag in self.all and \
520 not self._iuse_implicit_match(flag):
524 def get_missing_iuse(self, flags):
526 @return: A list of flags missing from IUSE.
528 if isinstance(flags, basestring):
532 if not flag in self.all and \
533 not self._iuse_implicit_match(flag):
534 missing_iuse.append(flag)
542 This is used to generate mtimedb resume mergelist entries, so we
543 limit it to 4 items for backward compatibility.
545 return iter(self._hash_key[:4])
547 def __lt__(self, other):
548 if other.cp != self.cp:
550 if portage.vercmp(self.version, other.version) < 0:
554 def __le__(self, other):
555 if other.cp != self.cp:
557 if portage.vercmp(self.version, other.version) <= 0:
561 def __gt__(self, other):
562 if other.cp != self.cp:
564 if portage.vercmp(self.version, other.version) > 0:
568 def __ge__(self, other):
569 if other.cp != self.cp:
571 if portage.vercmp(self.version, other.version) >= 0:
575 _all_metadata_keys = set(x for x in portage.auxdbkeys \
576 if not x.startswith("UNUSED_"))
577 _all_metadata_keys.update(Package.metadata_keys)
578 _all_metadata_keys = frozenset(_all_metadata_keys)
580 _PackageMetadataWrapperBase = slot_dict_class(_all_metadata_keys)
582 class _PackageMetadataWrapper(_PackageMetadataWrapperBase):
584 Detect metadata updates and synchronize Package attributes.
587 __slots__ = ("_pkg",)
588 _wrapped_keys = frozenset(
589 ["COUNTER", "INHERITED", "IUSE", "USE", "_mtime_"])
590 _use_conditional_keys = frozenset(
591 ['LICENSE', 'PROPERTIES', 'PROVIDE', 'RESTRICT',])
593 def __init__(self, pkg, metadata):
594 _PackageMetadataWrapperBase.__init__(self)
597 # USE is lazy, but we want it to show up in self.keys().
598 _PackageMetadataWrapperBase.__setitem__(self, 'USE', '')
600 self.update(metadata)
604 use_str = self['USE']
605 self._pkg._use = self._pkg._use_class(
609 use_str = _PackageMetadataWrapperBase.__getitem__(self, 'USE')
612 calculated_use = False
614 use_str = self._pkg._get_pkgsettings()["PORTAGE_USE"]
615 calculated_use = True
616 _PackageMetadataWrapperBase.__setitem__(self, 'USE', use_str)
617 self._pkg._use = self._pkg._use_class(
619 # Initialize these now, since USE access has just triggered
620 # setcpv, and we want to cache the result of the force/mask
621 # calculations that were done.
623 self._pkg._use._init_force_mask()
627 def __getitem__(self, k):
628 v = _PackageMetadataWrapperBase.__getitem__(self, k)
629 if k in self._use_conditional_keys:
630 if self._pkg.root_config.settings.local_config and '?' in v:
632 v = paren_enclose(use_reduce(v, uselist=self._pkg.use.enabled, \
633 is_valid_flag=self._pkg.iuse.is_valid_flag))
634 except InvalidDependString:
635 # This error should already have been registered via
636 # self._pkg._invalid_metadata().
641 elif k == 'USE' and not self._pkg.built:
643 # This is lazy because it's expensive.
648 def __setitem__(self, k, v):
649 _PackageMetadataWrapperBase.__setitem__(self, k, v)
650 if k in self._wrapped_keys:
651 getattr(self, "_set_" + k.lower())(k, v)
653 def _set_inherited(self, k, v):
654 if isinstance(v, basestring):
655 v = frozenset(v.split())
656 self._pkg.inherited = v
658 def _set_iuse(self, k, v):
659 self._pkg.iuse = self._pkg._iuse(
660 v.split(), self._pkg.root_config.settings._iuse_implicit_match)
662 def _set_counter(self, k, v):
663 if isinstance(v, basestring):
668 self._pkg.counter = v
670 def _set_use(self, k, v):
671 # Force regeneration of _use attribute
672 self._pkg._use = None
673 # Use raw metadata to restore USE conditional values
674 # to unevaluated state
675 raw_metadata = self._pkg._raw_metadata
676 for x in self._use_conditional_keys:
678 self[x] = raw_metadata[x]
682 def _set__mtime_(self, k, v):
683 if isinstance(v, basestring):
691 def properties(self):
692 return self['PROPERTIES'].split()
696 return self['RESTRICT'].split()
699 def defined_phases(self):
701 Returns tokens from DEFINED_PHASES metadata if it is defined,
702 otherwise returns a tuple containing all possible phases. This
703 makes it easy to do containment checks to see if it's safe to
704 skip execution of a given phase.
706 s = self['DEFINED_PHASES']