Package: remove unused profile.system mask type
[portage.git] / pym / _emerge / Package.py
1 # Copyright 1999-2011 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3
4 import sys
5 from itertools import chain
6 import portage
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
16
17 if sys.hexversion >= 0x3000000:
18         basestring = str
19         long = int
20
21 class Package(Task):
22
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",)
31
32         metadata_keys = [
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"]
38
39         _dep_keys = ('DEPEND', 'PDEPEND', 'RDEPEND',)
40         _use_conditional_misc_keys = ('LICENSE', 'PROPERTIES', 'RESTRICT')
41         UNKNOWN_REPO = "__unknown__"
42
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)
50                 if not self.built:
51                         self.metadata['CHOST'] = self.root_config.settings.get('CHOST', '')
52                 self.cp = portage.cpv_getkey(self.cpv)
53                 slot = self.slot
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.
59                         slot = '0'
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', ''))
72                 if not repo:
73                         repo = self.UNKNOWN_REPO
74                 self.metadata['repository'] = repo
75
76                 self._validate_deps()
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"
82                         else:
83                                 self.operation = "merge"
84
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)
91
92         @classmethod
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):
96
97                 if operation is None:
98                         if installed or onlydeps:
99                                 operation = "nomerge"
100                         else:
101                                 operation = "merge"
102
103                 root = None
104                 if root_config is not None:
105                         root = root_config.root
106                 else:
107                         raise TypeError("root_config argument is required")
108
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")
119                         repo_key = repo_name
120                 else:
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.
124                         repo_key = type_name
125
126                 return (type_name, root, cpv, operation, repo_key)
127
128         def _validate_deps(self):
129                 """
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).
133                 """
134                 eapi = self.metadata['EAPI']
135                 dep_eapi = eapi
136                 dep_valid_flag = self.iuse.is_valid_flag
137                 if self.installed:
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
141                         # deployed).
142                         dep_eapi = None
143                         dep_valid_flag = None
144
145                 for k in self._dep_keys:
146                         v = self.metadata.get(k)
147                         if not v:
148                                 continue
149                         try:
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)
154
155                 k = 'PROVIDE'
156                 v = self.metadata.get(k)
157                 if v:
158                         try:
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))
164
165                 for k in self._use_conditional_misc_keys:
166                         v = self.metadata.get(k)
167                         if not v:
168                                 continue
169                         try:
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)
174
175                 k = 'REQUIRED_USE'
176                 v = self.metadata.get(k)
177                 if v:
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)
181                         else:
182                                 try:
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
188                                         # when necessary.
189                                         self._invalid_metadata(k + ".syntax",
190                                                 _unicode_decode("%s: %s") % (k, e))
191
192                 k = 'SRC_URI'
193                 v = self.metadata.get(k)
194                 if v:
195                         try:
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)
201
202         def copy(self):
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)
207
208         def _masks(self):
209                 masks = {}
210                 settings = self.root_config.settings
211
212                 if self.invalid is not None:
213                         masks['invalid'] = self.invalid
214
215                 if not settings._accept_chost(self.cpv, self.metadata):
216                         masks['CHOST'] = self.metadata['CHOST']
217
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
223
224                 missing_keywords = settings._getMissingKeywords(
225                         self.cpv, self.metadata)
226                 if missing_keywords:
227                         masks['KEYWORDS'] = missing_keywords
228
229                 try:
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'
236                         pass
237
238                 mask_atom = settings._getMaskAtom(self.cpv, self.metadata)
239                 if mask_atom is not None:
240                         masks['package.mask'] = mask_atom
241
242                 try:
243                         missing_licenses = settings._getMissingLicenses(
244                                 self.cpv, self.metadata)
245                         if missing_licenses:
246                                 masks['LICENSE'] = missing_licenses
247                 except InvalidDependString:
248                         # already recorded as 'invalid'
249                         pass
250
251                 if not masks:
252                         masks = None
253
254                 return masks
255
256         def _visible(self, masks):
257
258                 if masks is not None:
259
260                         if 'EAPI.unsupported' in masks:
261                                 return False
262
263                         if 'invalid' in masks:
264                                 return False
265
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):
271                                 return False
272
273                         if 'package.mask' in masks or \
274                                 'LICENSE' in masks:
275                                 return False
276
277                 return True
278
279         def get_keyword_mask(self):
280                 """returns None, 'missing', or 'unstable'."""
281
282                 missing = self.root_config.settings._getRawMissingKeywords(
283                                 self.cpv, self.metadata)
284
285                 if not missing:
286                         return None
287
288                 if '**' in missing:
289                         return 'missing'
290
291                 global_accept_keywords = frozenset(
292                         self.root_config.settings.get("ACCEPT_KEYWORDS", "").split())
293
294                 for keyword in missing:
295                         if keyword.lstrip("~") in global_accept_keywords:
296                                 return 'unstable'
297
298                 return 'missing'
299
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
306
307         def _metadata_exception(self, k, e):
308
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
317                 # in python-2.x.
318                 if not self.installed:
319                         categorized_error = False
320                         if e.errors:
321                                 for error in e.errors:
322                                         if getattr(error, 'category', None) is None:
323                                                 continue
324                                         categorized_error = True
325                                         self._invalid_metadata(error.category,
326                                                 _unicode_decode("%s: %s") % (k, error))
327
328                         if not categorized_error:
329                                 self._invalid_metadata(k + ".syntax",
330                                         _unicode_decode("%s: %s") % (k, e))
331                 else:
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))
339
340         def _invalid_metadata(self, msg_type, msg):
341                 if self.invalid is None:
342                         self.invalid = {}
343                 msgs = self.invalid.get(msg_type)
344                 if msgs is None:
345                         msgs = []
346                         self.invalid[msg_type] = msgs
347                 msgs.append(msg)
348
349         def __str__(self):
350                 if self.operation == "merge":
351                         if self.type_name == "binary":
352                                 cpv_color = "PKG_BINARY_MERGE"
353                         else:
354                                 cpv_color = "PKG_MERGE"
355                 elif self.operation == "uninstall":
356                         cpv_color = "PKG_UNINSTALL"
357                 else:
358                         cpv_color = "PKG_NOMERGE"
359
360                 s = "(%s, %s" \
361                         % (portage.output.colorize(cpv_color, self.cpv + _repo_separator + self.repo) , self.type_name)
362
363                 if self.type_name == "installed":
364                         if self.root != "/":
365                                 s += " in '%s'" % self.root
366                         if self.operation == "uninstall":
367                                 s += " scheduled for uninstall"
368                 else:
369                         if self.operation == "merge":
370                                 s += " scheduled for merge"
371                                 if self.root != "/":
372                                         s += " to '%s'" % self.root
373                 s += ")"
374                 return s
375
376         if sys.hexversion < 0x3000000:
377
378                 __unicode__ = __str__
379
380                 def __str__(self):
381                         return _unicode_encode(self.__unicode__(),
382                                 encoding=_encodings['content'])
383
384         class _use_class(object):
385
386                 __slots__ = ("enabled", "_expand", "_expand_hidden",
387                         "_force", "_pkg", "_mask")
388
389                 # Share identical frozenset instances when available.
390                 _frozensets = {}
391
392                 def __init__(self, pkg, use_str):
393                         self._pkg = pkg
394                         self._expand = None
395                         self._expand_hidden = None
396                         self._force = None
397                         self._mask = None
398                         self.enabled = frozenset(use_str.split())
399                         if pkg.built:
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
403                                 # data corruption).
404                                 missing_iuse = pkg.iuse.get_missing_iuse(self.enabled)
405                                 if missing_iuse:
406                                         self.enabled = self.enabled.difference(missing_iuse)
407
408                 def _init_force_mask(self):
409                         pkgsettings = self._pkg._get_pkgsettings()
410                         frozensets = self._frozensets
411                         s = frozenset(
412                                 pkgsettings.get("USE_EXPAND", "").lower().split())
413                         self._expand = frozensets.setdefault(s, s)
414                         s = frozenset(
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)
421
422                 @property
423                 def expand(self):
424                         if self._expand is None:
425                                 self._init_force_mask()
426                         return self._expand
427
428                 @property
429                 def expand_hidden(self):
430                         if self._expand_hidden is None:
431                                 self._init_force_mask()
432                         return self._expand_hidden
433
434                 @property
435                 def force(self):
436                         if self._force is None:
437                                 self._init_force_mask()
438                         return self._force
439
440                 @property
441                 def mask(self):
442                         if self._mask is None:
443                                 self._init_force_mask()
444                         return self._mask
445
446         @property
447         def repo(self):
448                 return self.metadata['repository']
449
450         @property
451         def repo_priority(self):
452                 repo_info = self.root_config.settings.repositories.prepos.get(self.repo)
453                 if repo_info is None:
454                         return None
455                 return repo_info.priority
456
457         @property
458         def use(self):
459                 if self._use is None:
460                         self.metadata._init_use()
461                 return self._use
462
463         def _get_pkgsettings(self):
464                 pkgsettings = self.root_config.trees[
465                         'porttree'].dbapi.doebuild_settings
466                 pkgsettings.setcpv(self)
467                 return pkgsettings
468
469         class _iuse(object):
470
471                 __slots__ = ("__weakref__", "all", "enabled", "disabled",
472                         "tokens") + ("_iuse_implicit_match",)
473
474                 def __init__(self, tokens, iuse_implicit_match):
475                         self.tokens = tuple(tokens)
476                         self._iuse_implicit_match = iuse_implicit_match
477                         enabled = []
478                         disabled = []
479                         other = []
480                         for x in tokens:
481                                 prefix = x[:1]
482                                 if prefix == "+":
483                                         enabled.append(x[1:])
484                                 elif prefix == "-":
485                                         disabled.append(x[1:])
486                                 else:
487                                         other.append(x)
488                         self.enabled = frozenset(enabled)
489                         self.disabled = frozenset(disabled)
490                         self.all = frozenset(chain(enabled, disabled, other))
491
492                 def is_valid_flag(self, flags):
493                         """
494                         @returns: True if all flags are valid USE values which may
495                                 be specified in USE dependencies, False otherwise.
496                         """
497                         if isinstance(flags, basestring):
498                                 flags = [flags]
499
500                         for flag in flags:
501                                 if not flag in self.all and \
502                                         not self._iuse_implicit_match(flag):
503                                         return False
504                         return True
505
506                 def get_missing_iuse(self, flags):
507                         """
508                         @returns: A list of flags missing from IUSE.
509                         """
510                         if isinstance(flags, basestring):
511                                 flags = [flags]
512                         missing_iuse = []
513                         for flag in flags:
514                                 if not flag in self.all and \
515                                         not self._iuse_implicit_match(flag):
516                                         missing_iuse.append(flag)
517                         return missing_iuse
518
519         def __len__(self):
520                 return 4
521
522         def __iter__(self):
523                 """
524                 This is used to generate mtimedb resume mergelist entries, so we
525                 limit it to 4 items for backward compatibility.
526                 """
527                 return iter(self._hash_key[:4])
528
529         def __lt__(self, other):
530                 if other.cp != self.cp:
531                         return False
532                 if portage.pkgcmp(self.pv_split, other.pv_split) < 0:
533                         return True
534                 return False
535
536         def __le__(self, other):
537                 if other.cp != self.cp:
538                         return False
539                 if portage.pkgcmp(self.pv_split, other.pv_split) <= 0:
540                         return True
541                 return False
542
543         def __gt__(self, other):
544                 if other.cp != self.cp:
545                         return False
546                 if portage.pkgcmp(self.pv_split, other.pv_split) > 0:
547                         return True
548                 return False
549
550         def __ge__(self, other):
551                 if other.cp != self.cp:
552                         return False
553                 if portage.pkgcmp(self.pv_split, other.pv_split) >= 0:
554                         return True
555                 return False
556
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)
561
562 _PackageMetadataWrapperBase = slot_dict_class(_all_metadata_keys)
563
564 class _PackageMetadataWrapper(_PackageMetadataWrapperBase):
565         """
566         Detect metadata updates and synchronize Package attributes.
567         """
568
569         __slots__ = ("_pkg",)
570         _wrapped_keys = frozenset(
571                 ["COUNTER", "INHERITED", "IUSE", "SLOT", "USE", "_mtime_"])
572         _use_conditional_keys = frozenset(
573                 ['LICENSE', 'PROPERTIES', 'PROVIDE', 'RESTRICT',])
574
575         def __init__(self, pkg, metadata):
576                 _PackageMetadataWrapperBase.__init__(self)
577                 self._pkg = pkg
578                 if not pkg.built:
579                         # USE is lazy, but we want it to show up in self.keys().
580                         _PackageMetadataWrapperBase.__setitem__(self, 'USE', '')
581
582                 self.update(metadata)
583
584         def _init_use(self):
585                 if self._pkg.built:
586                         use_str = self['USE']
587                         self._pkg._use = self._pkg._use_class(
588                                 self._pkg, use_str)
589                 else:
590                         try:
591                                 use_str = _PackageMetadataWrapperBase.__getitem__(self, 'USE')
592                         except KeyError:
593                                 use_str = None
594                         calculated_use = False
595                         if not use_str:
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(
600                                 self._pkg, use_str)
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.
604                         if calculated_use:
605                                 self._pkg._use._init_force_mask()
606
607                 return use_str
608
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:
613                                 try:
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().
619                                         pass
620                                 else:
621                                         self[k] = v
622
623                 elif k == 'USE' and not self._pkg.built:
624                         if not v:
625                                 # This is lazy because it's expensive.
626                                 v = self._init_use()
627
628                 return v
629
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)
634
635         def _set_inherited(self, k, v):
636                 if isinstance(v, basestring):
637                         v = frozenset(v.split())
638                 self._pkg.inherited = v
639
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)
643
644         def _set_slot(self, k, v):
645                 self._pkg.slot = v
646
647         def _set_counter(self, k, v):
648                 if isinstance(v, basestring):
649                         try:
650                                 v = long(v.strip())
651                         except ValueError:
652                                 v = 0
653                 self._pkg.counter = v
654
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:
662                         try:
663                                 self[x] = raw_metadata[x]
664                         except KeyError:
665                                 pass
666
667         def _set__mtime_(self, k, v):
668                 if isinstance(v, basestring):
669                         try:
670                                 v = long(v.strip())
671                         except ValueError:
672                                 v = 0
673                 self._pkg.mtime = v
674
675         @property
676         def properties(self):
677                 return self['PROPERTIES'].split()
678
679         @property
680         def restrict(self):
681                 return self['RESTRICT'].split()
682
683         @property
684         def defined_phases(self):
685                 """
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.
690                 """
691                 s = self['DEFINED_PHASES']
692                 if s:
693                         return s.split()
694                 return EBUILD_PHASES