Package: move SLOT validation to _pkg_str
[portage.git] / pym / _emerge / Package.py
1 # Copyright 1999-2012 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_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
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", "iuse", "mtime",
29                 "pf", "root", "slot", "slot_atom", "version") + \
30         ("_invalid", "_raw_metadata", "_masks", "_use", "_visible")
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_repo
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                 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"])
59                 self.cp = self.cpv.cp
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()
74
75                 if self.operation is None:
76                         if self.onlydeps or self.installed:
77                                 self.operation = "nomerge"
78                         else:
79                                 self.operation = "merge"
80
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)
87
88         # These are calculated on-demand, so that they are calculated
89         # after FakeVartree applies its metadata tweaks.
90         @property
91         def invalid(self):
92                 if self._invalid is None:
93                         self._validate_deps()
94                         if self._invalid is None:
95                                 self._invalid = False
96                 return self._invalid
97
98         @property
99         def masks(self):
100                 if self._masks is None:
101                         self._masks = self._eval_masks()
102                 return self._masks
103
104         @property
105         def visible(self):
106                 if self._visible is None:
107                         self._visible = self._eval_visiblity(self.masks)
108                 return self._visible
109
110         @classmethod
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):
114
115                 if operation is None:
116                         if installed or onlydeps:
117                                 operation = "nomerge"
118                         else:
119                                 operation = "merge"
120
121                 root = None
122                 if root_config is not None:
123                         root = root_config.root
124                 else:
125                         raise TypeError("root_config argument is required")
126
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")
137                         repo_key = repo_name
138                 else:
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.
142                         repo_key = type_name
143
144                 return (type_name, root, cpv, operation, repo_key)
145
146         def _validate_deps(self):
147                 """
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).
151                 """
152                 eapi = self.metadata['EAPI']
153                 dep_eapi = eapi
154                 dep_valid_flag = self.iuse.is_valid_flag
155                 if self.installed:
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
159                         # deployed).
160                         dep_eapi = None
161                         dep_valid_flag = None
162
163                 for k in self._dep_keys:
164                         v = self.metadata.get(k)
165                         if not v:
166                                 continue
167                         try:
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)
172
173                 k = 'PROVIDE'
174                 v = self.metadata.get(k)
175                 if v:
176                         try:
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))
182
183                 for k in self._use_conditional_misc_keys:
184                         v = self.metadata.get(k)
185                         if not v:
186                                 continue
187                         try:
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)
192
193                 k = 'REQUIRED_USE'
194                 v = self.metadata.get(k)
195                 if v:
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)
199                         else:
200                                 try:
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
206                                         # when necessary.
207                                         self._invalid_metadata(k + ".syntax",
208                                                 _unicode_decode("%s: %s") % (k, e))
209
210                 k = 'SRC_URI'
211                 v = self.metadata.get(k)
212                 if v:
213                         try:
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)
219
220         def copy(self):
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)
225
226         def _eval_masks(self):
227                 masks = {}
228                 settings = self.root_config.settings
229
230                 if self.invalid is not False:
231                         masks['invalid'] = self.invalid
232
233                 if not settings._accept_chost(self.cpv, self.metadata):
234                         masks['CHOST'] = self.metadata['CHOST']
235
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
241
242                 missing_keywords = settings._getMissingKeywords(
243                         self.cpv, self.metadata)
244                 if missing_keywords:
245                         masks['KEYWORDS'] = missing_keywords
246
247                 try:
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'
254                         pass
255
256                 mask_atom = settings._getMaskAtom(self.cpv, self.metadata)
257                 if mask_atom is not None:
258                         masks['package.mask'] = mask_atom
259
260                 try:
261                         missing_licenses = settings._getMissingLicenses(
262                                 self.cpv, self.metadata)
263                         if missing_licenses:
264                                 masks['LICENSE'] = missing_licenses
265                 except InvalidDependString:
266                         # already recorded as 'invalid'
267                         pass
268
269                 if not masks:
270                         masks = False
271
272                 return masks
273
274         def _eval_visiblity(self, masks):
275
276                 if masks is not False:
277
278                         if 'EAPI.unsupported' in masks:
279                                 return False
280
281                         if 'invalid' in masks:
282                                 return False
283
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):
289                                 return False
290
291                         if 'package.mask' in masks or \
292                                 'LICENSE' in masks:
293                                 return False
294
295                 return True
296
297         def get_keyword_mask(self):
298                 """returns None, 'missing', or 'unstable'."""
299
300                 missing = self.root_config.settings._getRawMissingKeywords(
301                                 self.cpv, self.metadata)
302
303                 if not missing:
304                         return None
305
306                 if '**' in missing:
307                         return 'missing'
308
309                 global_accept_keywords = frozenset(
310                         self.root_config.settings.get("ACCEPT_KEYWORDS", "").split())
311
312                 for keyword in missing:
313                         if keyword.lstrip("~") in global_accept_keywords:
314                                 return 'unstable'
315
316                 return 'missing'
317
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
324
325         def _metadata_exception(self, k, e):
326
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
335                 # in python-2.x.
336                 if not self.installed:
337                         categorized_error = False
338                         if e.errors:
339                                 for error in e.errors:
340                                         if getattr(error, 'category', None) is None:
341                                                 continue
342                                         categorized_error = True
343                                         self._invalid_metadata(error.category,
344                                                 _unicode_decode("%s: %s") % (k, error))
345
346                         if not categorized_error:
347                                 self._invalid_metadata(k + ".syntax",
348                                         _unicode_decode("%s: %s") % (k, e))
349                 else:
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))
357
358         def _invalid_metadata(self, msg_type, msg):
359                 if self._invalid is None:
360                         self._invalid = {}
361                 msgs = self._invalid.get(msg_type)
362                 if msgs is None:
363                         msgs = []
364                         self._invalid[msg_type] = msgs
365                 msgs.append(msg)
366
367         def __str__(self):
368                 if self.operation == "merge":
369                         if self.type_name == "binary":
370                                 cpv_color = "PKG_BINARY_MERGE"
371                         else:
372                                 cpv_color = "PKG_MERGE"
373                 elif self.operation == "uninstall":
374                         cpv_color = "PKG_UNINSTALL"
375                 else:
376                         cpv_color = "PKG_NOMERGE"
377
378                 s = "(%s, %s" \
379                         % (portage.output.colorize(cpv_color, self.cpv + _repo_separator + self.repo) , self.type_name)
380
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"
386                 else:
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']
391                 s += ")"
392                 return s
393
394         if sys.hexversion < 0x3000000:
395
396                 __unicode__ = __str__
397
398                 def __str__(self):
399                         return _unicode_encode(self.__unicode__(),
400                                 encoding=_encodings['content'])
401
402         class _use_class(object):
403
404                 __slots__ = ("enabled", "_expand", "_expand_hidden",
405                         "_force", "_pkg", "_mask")
406
407                 # Share identical frozenset instances when available.
408                 _frozensets = {}
409
410                 def __init__(self, pkg, use_str):
411                         self._pkg = pkg
412                         self._expand = None
413                         self._expand_hidden = None
414                         self._force = None
415                         self._mask = None
416                         self.enabled = frozenset(use_str.split())
417                         if pkg.built:
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
421                                 # data corruption).
422                                 missing_iuse = pkg.iuse.get_missing_iuse(self.enabled)
423                                 if missing_iuse:
424                                         self.enabled = self.enabled.difference(missing_iuse)
425
426                 def _init_force_mask(self):
427                         pkgsettings = self._pkg._get_pkgsettings()
428                         frozensets = self._frozensets
429                         s = frozenset(
430                                 pkgsettings.get("USE_EXPAND", "").lower().split())
431                         self._expand = frozensets.setdefault(s, s)
432                         s = frozenset(
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)
439
440                 @property
441                 def expand(self):
442                         if self._expand is None:
443                                 self._init_force_mask()
444                         return self._expand
445
446                 @property
447                 def expand_hidden(self):
448                         if self._expand_hidden is None:
449                                 self._init_force_mask()
450                         return self._expand_hidden
451
452                 @property
453                 def force(self):
454                         if self._force is None:
455                                 self._init_force_mask()
456                         return self._force
457
458                 @property
459                 def mask(self):
460                         if self._mask is None:
461                                 self._init_force_mask()
462                         return self._mask
463
464         @property
465         def repo(self):
466                 return self.metadata['repository']
467
468         @property
469         def repo_priority(self):
470                 repo_info = self.root_config.settings.repositories.prepos.get(self.repo)
471                 if repo_info is None:
472                         return None
473                 return repo_info.priority
474
475         @property
476         def use(self):
477                 if self._use is None:
478                         self.metadata._init_use()
479                 return self._use
480
481         def _get_pkgsettings(self):
482                 pkgsettings = self.root_config.trees[
483                         'porttree'].dbapi.doebuild_settings
484                 pkgsettings.setcpv(self)
485                 return pkgsettings
486
487         class _iuse(object):
488
489                 __slots__ = ("__weakref__", "all", "enabled", "disabled",
490                         "tokens") + ("_iuse_implicit_match",)
491
492                 def __init__(self, tokens, iuse_implicit_match):
493                         self.tokens = tuple(tokens)
494                         self._iuse_implicit_match = iuse_implicit_match
495                         enabled = []
496                         disabled = []
497                         other = []
498                         for x in tokens:
499                                 prefix = x[:1]
500                                 if prefix == "+":
501                                         enabled.append(x[1:])
502                                 elif prefix == "-":
503                                         disabled.append(x[1:])
504                                 else:
505                                         other.append(x)
506                         self.enabled = frozenset(enabled)
507                         self.disabled = frozenset(disabled)
508                         self.all = frozenset(chain(enabled, disabled, other))
509
510                 def is_valid_flag(self, flags):
511                         """
512                         @return: True if all flags are valid USE values which may
513                                 be specified in USE dependencies, False otherwise.
514                         """
515                         if isinstance(flags, basestring):
516                                 flags = [flags]
517
518                         for flag in flags:
519                                 if not flag in self.all and \
520                                         not self._iuse_implicit_match(flag):
521                                         return False
522                         return True
523
524                 def get_missing_iuse(self, flags):
525                         """
526                         @return: A list of flags missing from IUSE.
527                         """
528                         if isinstance(flags, basestring):
529                                 flags = [flags]
530                         missing_iuse = []
531                         for flag in flags:
532                                 if not flag in self.all and \
533                                         not self._iuse_implicit_match(flag):
534                                         missing_iuse.append(flag)
535                         return missing_iuse
536
537         def __len__(self):
538                 return 4
539
540         def __iter__(self):
541                 """
542                 This is used to generate mtimedb resume mergelist entries, so we
543                 limit it to 4 items for backward compatibility.
544                 """
545                 return iter(self._hash_key[:4])
546
547         def __lt__(self, other):
548                 if other.cp != self.cp:
549                         return False
550                 if portage.vercmp(self.version, other.version) < 0:
551                         return True
552                 return False
553
554         def __le__(self, other):
555                 if other.cp != self.cp:
556                         return False
557                 if portage.vercmp(self.version, other.version) <= 0:
558                         return True
559                 return False
560
561         def __gt__(self, other):
562                 if other.cp != self.cp:
563                         return False
564                 if portage.vercmp(self.version, other.version) > 0:
565                         return True
566                 return False
567
568         def __ge__(self, other):
569                 if other.cp != self.cp:
570                         return False
571                 if portage.vercmp(self.version, other.version) >= 0:
572                         return True
573                 return False
574
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)
579
580 _PackageMetadataWrapperBase = slot_dict_class(_all_metadata_keys)
581
582 class _PackageMetadataWrapper(_PackageMetadataWrapperBase):
583         """
584         Detect metadata updates and synchronize Package attributes.
585         """
586
587         __slots__ = ("_pkg",)
588         _wrapped_keys = frozenset(
589                 ["COUNTER", "INHERITED", "IUSE", "USE", "_mtime_"])
590         _use_conditional_keys = frozenset(
591                 ['LICENSE', 'PROPERTIES', 'PROVIDE', 'RESTRICT',])
592
593         def __init__(self, pkg, metadata):
594                 _PackageMetadataWrapperBase.__init__(self)
595                 self._pkg = pkg
596                 if not pkg.built:
597                         # USE is lazy, but we want it to show up in self.keys().
598                         _PackageMetadataWrapperBase.__setitem__(self, 'USE', '')
599
600                 self.update(metadata)
601
602         def _init_use(self):
603                 if self._pkg.built:
604                         use_str = self['USE']
605                         self._pkg._use = self._pkg._use_class(
606                                 self._pkg, use_str)
607                 else:
608                         try:
609                                 use_str = _PackageMetadataWrapperBase.__getitem__(self, 'USE')
610                         except KeyError:
611                                 use_str = None
612                         calculated_use = False
613                         if not use_str:
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(
618                                 self._pkg, use_str)
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.
622                         if calculated_use:
623                                 self._pkg._use._init_force_mask()
624
625                 return use_str
626
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:
631                                 try:
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().
637                                         pass
638                                 else:
639                                         self[k] = v
640
641                 elif k == 'USE' and not self._pkg.built:
642                         if not v:
643                                 # This is lazy because it's expensive.
644                                 v = self._init_use()
645
646                 return v
647
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)
652
653         def _set_inherited(self, k, v):
654                 if isinstance(v, basestring):
655                         v = frozenset(v.split())
656                 self._pkg.inherited = v
657
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)
661
662         def _set_counter(self, k, v):
663                 if isinstance(v, basestring):
664                         try:
665                                 v = long(v.strip())
666                         except ValueError:
667                                 v = 0
668                 self._pkg.counter = v
669
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:
677                         try:
678                                 self[x] = raw_metadata[x]
679                         except KeyError:
680                                 pass
681
682         def _set__mtime_(self, k, v):
683                 if isinstance(v, basestring):
684                         try:
685                                 v = long(v.strip())
686                         except ValueError:
687                                 v = 0
688                 self._pkg.mtime = v
689
690         @property
691         def properties(self):
692                 return self['PROPERTIES'].split()
693
694         @property
695         def restrict(self):
696                 return self['RESTRICT'].split()
697
698         @property
699         def defined_phases(self):
700                 """
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.
705                 """
706                 s = self['DEFINED_PHASES']
707                 if s:
708                         return s.split()
709                 return EBUILD_PHASES