Add a _get_slot_re() function.
[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, _get_slot_re, _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                 slot = self.slot
54                 if _get_slot_re(eapi_attrs).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                 self.cpv = _pkg_str(self.cpv, slot=slot,
61                         repo=self.metadata.get('repository', ''))
62                 self.cp = self.cpv.cp
63                 # sync metadata with validated repo (may be UNKNOWN_REPO)
64                 self.metadata['repository'] = self.cpv.repo
65                 if (self.iuse.enabled or self.iuse.disabled) and \
66                         not eapi_attrs.iuse_defaults:
67                         if not self.installed:
68                                 self._invalid_metadata('EAPI.incompatible',
69                                         "IUSE contains defaults, but EAPI doesn't allow them")
70                 self.slot_atom = portage.dep.Atom("%s%s%s" % (self.cp, _slot_separator, slot))
71                 self.category, self.pf = portage.catsplit(self.cpv)
72                 self.cpv_split = self.cpv.cpv_split
73                 self.version = self.cpv.version
74                 if self.inherited is None:
75                         self.inherited = frozenset()
76
77                 if self.operation is None:
78                         if self.onlydeps or self.installed:
79                                 self.operation = "nomerge"
80                         else:
81                                 self.operation = "merge"
82
83                 self._hash_key = Package._gen_hash_key(cpv=self.cpv,
84                         installed=self.installed, onlydeps=self.onlydeps,
85                         operation=self.operation, repo_name=self.cpv.repo,
86                         root_config=self.root_config,
87                         type_name=self.type_name)
88                 self._hash_value = hash(self._hash_key)
89
90         # These are calculated on-demand, so that they are calculated
91         # after FakeVartree applies its metadata tweaks.
92         @property
93         def invalid(self):
94                 if self._invalid is None:
95                         self._validate_deps()
96                         if self._invalid is None:
97                                 self._invalid = False
98                 return self._invalid
99
100         @property
101         def masks(self):
102                 if self._masks is None:
103                         self._masks = self._eval_masks()
104                 return self._masks
105
106         @property
107         def visible(self):
108                 if self._visible is None:
109                         self._visible = self._eval_visiblity(self.masks)
110                 return self._visible
111
112         @classmethod
113         def _gen_hash_key(cls, cpv=None, installed=None, onlydeps=None,
114                 operation=None, repo_name=None, root_config=None,
115                 type_name=None, **kwargs):
116
117                 if operation is None:
118                         if installed or onlydeps:
119                                 operation = "nomerge"
120                         else:
121                                 operation = "merge"
122
123                 root = None
124                 if root_config is not None:
125                         root = root_config.root
126                 else:
127                         raise TypeError("root_config argument is required")
128
129                 # For installed (and binary) packages we don't care for the repo
130                 # when it comes to hashing, because there can only be one cpv.
131                 # So overwrite the repo_key with type_name.
132                 if type_name is None:
133                         raise TypeError("type_name argument is required")
134                 elif type_name == "ebuild":
135                         if repo_name is None:
136                                 raise AssertionError(
137                                         "Package._gen_hash_key() " + \
138                                         "called without 'repo_name' argument")
139                         repo_key = repo_name
140                 else:
141                         # For installed (and binary) packages we don't care for the repo
142                         # when it comes to hashing, because there can only be one cpv.
143                         # So overwrite the repo_key with type_name.
144                         repo_key = type_name
145
146                 return (type_name, root, cpv, operation, repo_key)
147
148         def _validate_deps(self):
149                 """
150                 Validate deps. This does not trigger USE calculation since that
151                 is expensive for ebuilds and therefore we want to avoid doing
152                 in unnecessarily (like for masked packages).
153                 """
154                 eapi = self.metadata['EAPI']
155                 dep_eapi = eapi
156                 dep_valid_flag = self.iuse.is_valid_flag
157                 if self.installed:
158                         # Ignore EAPI.incompatible and conditionals missing
159                         # from IUSE for installed packages since these issues
160                         # aren't relevant now (re-evaluate when new EAPIs are
161                         # deployed).
162                         dep_eapi = None
163                         dep_valid_flag = None
164
165                 for k in self._dep_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, token_class=Atom)
172                         except InvalidDependString as e:
173                                 self._metadata_exception(k, e)
174
175                 k = 'PROVIDE'
176                 v = self.metadata.get(k)
177                 if v:
178                         try:
179                                 use_reduce(v, eapi=dep_eapi, matchall=True,
180                                         is_valid_flag=dep_valid_flag, token_class=Atom)
181                         except InvalidDependString as e:
182                                 self._invalid_metadata("PROVIDE.syntax",
183                                         _unicode_decode("%s: %s") % (k, e))
184
185                 for k in self._use_conditional_misc_keys:
186                         v = self.metadata.get(k)
187                         if not v:
188                                 continue
189                         try:
190                                 use_reduce(v, eapi=dep_eapi, matchall=True,
191                                         is_valid_flag=dep_valid_flag)
192                         except InvalidDependString as e:
193                                 self._metadata_exception(k, e)
194
195                 k = 'REQUIRED_USE'
196                 v = self.metadata.get(k)
197                 if v:
198                         if not _get_eapi_attrs(eapi).required_use:
199                                 self._invalid_metadata('EAPI.incompatible',
200                                         "REQUIRED_USE set, but EAPI='%s' doesn't allow it" % eapi)
201                         else:
202                                 try:
203                                         check_required_use(v, (),
204                                                 self.iuse.is_valid_flag)
205                                 except InvalidDependString as e:
206                                         # Force unicode format string for python-2.x safety,
207                                         # ensuring that PortageException.__unicode__() is used
208                                         # when necessary.
209                                         self._invalid_metadata(k + ".syntax",
210                                                 _unicode_decode("%s: %s") % (k, e))
211
212                 k = 'SRC_URI'
213                 v = self.metadata.get(k)
214                 if v:
215                         try:
216                                 use_reduce(v, is_src_uri=True, eapi=eapi, matchall=True,
217                                         is_valid_flag=self.iuse.is_valid_flag)
218                         except InvalidDependString as e:
219                                 if not self.installed:
220                                         self._metadata_exception(k, e)
221
222         def copy(self):
223                 return Package(built=self.built, cpv=self.cpv, depth=self.depth,
224                         installed=self.installed, metadata=self._raw_metadata,
225                         onlydeps=self.onlydeps, operation=self.operation,
226                         root_config=self.root_config, type_name=self.type_name)
227
228         def _eval_masks(self):
229                 masks = {}
230                 settings = self.root_config.settings
231
232                 if self.invalid is not False:
233                         masks['invalid'] = self.invalid
234
235                 if not settings._accept_chost(self.cpv, self.metadata):
236                         masks['CHOST'] = self.metadata['CHOST']
237
238                 eapi = self.metadata["EAPI"]
239                 if not portage.eapi_is_supported(eapi):
240                         masks['EAPI.unsupported'] = eapi
241                 if portage._eapi_is_deprecated(eapi):
242                         masks['EAPI.deprecated'] = eapi
243
244                 missing_keywords = settings._getMissingKeywords(
245                         self.cpv, self.metadata)
246                 if missing_keywords:
247                         masks['KEYWORDS'] = missing_keywords
248
249                 try:
250                         missing_properties = settings._getMissingProperties(
251                                 self.cpv, self.metadata)
252                         if missing_properties:
253                                 masks['PROPERTIES'] = missing_properties
254                 except InvalidDependString:
255                         # already recorded as 'invalid'
256                         pass
257
258                 mask_atom = settings._getMaskAtom(self.cpv, self.metadata)
259                 if mask_atom is not None:
260                         masks['package.mask'] = mask_atom
261
262                 try:
263                         missing_licenses = settings._getMissingLicenses(
264                                 self.cpv, self.metadata)
265                         if missing_licenses:
266                                 masks['LICENSE'] = missing_licenses
267                 except InvalidDependString:
268                         # already recorded as 'invalid'
269                         pass
270
271                 if not masks:
272                         masks = False
273
274                 return masks
275
276         def _eval_visiblity(self, masks):
277
278                 if masks is not False:
279
280                         if 'EAPI.unsupported' in masks:
281                                 return False
282
283                         if 'invalid' in masks:
284                                 return False
285
286                         if not self.installed and ( \
287                                 'CHOST' in masks or \
288                                 'EAPI.deprecated' in masks or \
289                                 'KEYWORDS' in masks or \
290                                 'PROPERTIES' in masks):
291                                 return False
292
293                         if 'package.mask' in masks or \
294                                 'LICENSE' in masks:
295                                 return False
296
297                 return True
298
299         def get_keyword_mask(self):
300                 """returns None, 'missing', or 'unstable'."""
301
302                 missing = self.root_config.settings._getRawMissingKeywords(
303                                 self.cpv, self.metadata)
304
305                 if not missing:
306                         return None
307
308                 if '**' in missing:
309                         return 'missing'
310
311                 global_accept_keywords = frozenset(
312                         self.root_config.settings.get("ACCEPT_KEYWORDS", "").split())
313
314                 for keyword in missing:
315                         if keyword.lstrip("~") in global_accept_keywords:
316                                 return 'unstable'
317
318                 return 'missing'
319
320         def isHardMasked(self):
321                 """returns a bool if the cpv is in the list of
322                 expanded pmaskdict[cp] available ebuilds"""
323                 pmask = self.root_config.settings._getRawMaskAtom(
324                         self.cpv, self.metadata)
325                 return pmask is not None
326
327         def _metadata_exception(self, k, e):
328
329                 # For unicode safety with python-2.x we need to avoid
330                 # using the string format operator with a non-unicode
331                 # format string, since that will result in the
332                 # PortageException.__str__() method being invoked,
333                 # followed by unsafe decoding that may result in a
334                 # UnicodeDecodeError. Therefore, use _unicode_decode()
335                 # to ensure that format strings are unicode, so that
336                 # PortageException.__unicode__() is used when necessary
337                 # in python-2.x.
338                 if not self.installed:
339                         categorized_error = False
340                         if e.errors:
341                                 for error in e.errors:
342                                         if getattr(error, 'category', None) is None:
343                                                 continue
344                                         categorized_error = True
345                                         self._invalid_metadata(error.category,
346                                                 _unicode_decode("%s: %s") % (k, error))
347
348                         if not categorized_error:
349                                 self._invalid_metadata(k + ".syntax",
350                                         _unicode_decode("%s: %s") % (k, e))
351                 else:
352                         # For installed packages, show the path of the file
353                         # containing the invalid metadata, since the user may
354                         # want to fix the deps by hand.
355                         vardb = self.root_config.trees['vartree'].dbapi
356                         path = vardb.getpath(self.cpv, filename=k)
357                         self._invalid_metadata(k + ".syntax",
358                                 _unicode_decode("%s: %s in '%s'") % (k, e, path))
359
360         def _invalid_metadata(self, msg_type, msg):
361                 if self._invalid is None:
362                         self._invalid = {}
363                 msgs = self._invalid.get(msg_type)
364                 if msgs is None:
365                         msgs = []
366                         self._invalid[msg_type] = msgs
367                 msgs.append(msg)
368
369         def __str__(self):
370                 if self.operation == "merge":
371                         if self.type_name == "binary":
372                                 cpv_color = "PKG_BINARY_MERGE"
373                         else:
374                                 cpv_color = "PKG_MERGE"
375                 elif self.operation == "uninstall":
376                         cpv_color = "PKG_UNINSTALL"
377                 else:
378                         cpv_color = "PKG_NOMERGE"
379
380                 s = "(%s, %s" \
381                         % (portage.output.colorize(cpv_color, self.cpv + _repo_separator + self.repo) , self.type_name)
382
383                 if self.type_name == "installed":
384                         if self.root_config.settings['ROOT'] != "/":
385                                 s += " in '%s'" % self.root_config.settings['ROOT']
386                         if self.operation == "uninstall":
387                                 s += " scheduled for uninstall"
388                 else:
389                         if self.operation == "merge":
390                                 s += " scheduled for merge"
391                                 if self.root_config.settings['ROOT'] != "/":
392                                         s += " to '%s'" % self.root_config.settings['ROOT']
393                 s += ")"
394                 return s
395
396         if sys.hexversion < 0x3000000:
397
398                 __unicode__ = __str__
399
400                 def __str__(self):
401                         return _unicode_encode(self.__unicode__(),
402                                 encoding=_encodings['content'])
403
404         class _use_class(object):
405
406                 __slots__ = ("enabled", "_expand", "_expand_hidden",
407                         "_force", "_pkg", "_mask")
408
409                 # Share identical frozenset instances when available.
410                 _frozensets = {}
411
412                 def __init__(self, pkg, use_str):
413                         self._pkg = pkg
414                         self._expand = None
415                         self._expand_hidden = None
416                         self._force = None
417                         self._mask = None
418                         self.enabled = frozenset(use_str.split())
419                         if pkg.built:
420                                 # Use IUSE to validate USE settings for built packages,
421                                 # in case the package manager that built this package
422                                 # failed to do that for some reason (or in case of
423                                 # data corruption).
424                                 missing_iuse = pkg.iuse.get_missing_iuse(self.enabled)
425                                 if missing_iuse:
426                                         self.enabled = self.enabled.difference(missing_iuse)
427
428                 def _init_force_mask(self):
429                         pkgsettings = self._pkg._get_pkgsettings()
430                         frozensets = self._frozensets
431                         s = frozenset(
432                                 pkgsettings.get("USE_EXPAND", "").lower().split())
433                         self._expand = frozensets.setdefault(s, s)
434                         s = frozenset(
435                                 pkgsettings.get("USE_EXPAND_HIDDEN", "").lower().split())
436                         self._expand_hidden = frozensets.setdefault(s, s)
437                         s = pkgsettings.useforce
438                         self._force = frozensets.setdefault(s, s)
439                         s = pkgsettings.usemask
440                         self._mask = frozensets.setdefault(s, s)
441
442                 @property
443                 def expand(self):
444                         if self._expand is None:
445                                 self._init_force_mask()
446                         return self._expand
447
448                 @property
449                 def expand_hidden(self):
450                         if self._expand_hidden is None:
451                                 self._init_force_mask()
452                         return self._expand_hidden
453
454                 @property
455                 def force(self):
456                         if self._force is None:
457                                 self._init_force_mask()
458                         return self._force
459
460                 @property
461                 def mask(self):
462                         if self._mask is None:
463                                 self._init_force_mask()
464                         return self._mask
465
466         @property
467         def repo(self):
468                 return self.metadata['repository']
469
470         @property
471         def repo_priority(self):
472                 repo_info = self.root_config.settings.repositories.prepos.get(self.repo)
473                 if repo_info is None:
474                         return None
475                 return repo_info.priority
476
477         @property
478         def use(self):
479                 if self._use is None:
480                         self.metadata._init_use()
481                 return self._use
482
483         def _get_pkgsettings(self):
484                 pkgsettings = self.root_config.trees[
485                         'porttree'].dbapi.doebuild_settings
486                 pkgsettings.setcpv(self)
487                 return pkgsettings
488
489         class _iuse(object):
490
491                 __slots__ = ("__weakref__", "all", "enabled", "disabled",
492                         "tokens") + ("_iuse_implicit_match",)
493
494                 def __init__(self, tokens, iuse_implicit_match):
495                         self.tokens = tuple(tokens)
496                         self._iuse_implicit_match = iuse_implicit_match
497                         enabled = []
498                         disabled = []
499                         other = []
500                         for x in tokens:
501                                 prefix = x[:1]
502                                 if prefix == "+":
503                                         enabled.append(x[1:])
504                                 elif prefix == "-":
505                                         disabled.append(x[1:])
506                                 else:
507                                         other.append(x)
508                         self.enabled = frozenset(enabled)
509                         self.disabled = frozenset(disabled)
510                         self.all = frozenset(chain(enabled, disabled, other))
511
512                 def is_valid_flag(self, flags):
513                         """
514                         @return: True if all flags are valid USE values which may
515                                 be specified in USE dependencies, False otherwise.
516                         """
517                         if isinstance(flags, basestring):
518                                 flags = [flags]
519
520                         for flag in flags:
521                                 if not flag in self.all and \
522                                         not self._iuse_implicit_match(flag):
523                                         return False
524                         return True
525
526                 def get_missing_iuse(self, flags):
527                         """
528                         @return: A list of flags missing from IUSE.
529                         """
530                         if isinstance(flags, basestring):
531                                 flags = [flags]
532                         missing_iuse = []
533                         for flag in flags:
534                                 if not flag in self.all and \
535                                         not self._iuse_implicit_match(flag):
536                                         missing_iuse.append(flag)
537                         return missing_iuse
538
539         def __len__(self):
540                 return 4
541
542         def __iter__(self):
543                 """
544                 This is used to generate mtimedb resume mergelist entries, so we
545                 limit it to 4 items for backward compatibility.
546                 """
547                 return iter(self._hash_key[:4])
548
549         def __lt__(self, other):
550                 if other.cp != self.cp:
551                         return False
552                 if portage.vercmp(self.version, other.version) < 0:
553                         return True
554                 return False
555
556         def __le__(self, other):
557                 if other.cp != self.cp:
558                         return False
559                 if portage.vercmp(self.version, other.version) <= 0:
560                         return True
561                 return False
562
563         def __gt__(self, other):
564                 if other.cp != self.cp:
565                         return False
566                 if portage.vercmp(self.version, other.version) > 0:
567                         return True
568                 return False
569
570         def __ge__(self, other):
571                 if other.cp != self.cp:
572                         return False
573                 if portage.vercmp(self.version, other.version) >= 0:
574                         return True
575                 return False
576
577 _all_metadata_keys = set(x for x in portage.auxdbkeys \
578         if not x.startswith("UNUSED_"))
579 _all_metadata_keys.update(Package.metadata_keys)
580 _all_metadata_keys = frozenset(_all_metadata_keys)
581
582 _PackageMetadataWrapperBase = slot_dict_class(_all_metadata_keys)
583
584 class _PackageMetadataWrapper(_PackageMetadataWrapperBase):
585         """
586         Detect metadata updates and synchronize Package attributes.
587         """
588
589         __slots__ = ("_pkg",)
590         _wrapped_keys = frozenset(
591                 ["COUNTER", "INHERITED", "IUSE", "SLOT", "USE", "_mtime_"])
592         _use_conditional_keys = frozenset(
593                 ['LICENSE', 'PROPERTIES', 'PROVIDE', 'RESTRICT',])
594
595         def __init__(self, pkg, metadata):
596                 _PackageMetadataWrapperBase.__init__(self)
597                 self._pkg = pkg
598                 if not pkg.built:
599                         # USE is lazy, but we want it to show up in self.keys().
600                         _PackageMetadataWrapperBase.__setitem__(self, 'USE', '')
601
602                 self.update(metadata)
603
604         def _init_use(self):
605                 if self._pkg.built:
606                         use_str = self['USE']
607                         self._pkg._use = self._pkg._use_class(
608                                 self._pkg, use_str)
609                 else:
610                         try:
611                                 use_str = _PackageMetadataWrapperBase.__getitem__(self, 'USE')
612                         except KeyError:
613                                 use_str = None
614                         calculated_use = False
615                         if not use_str:
616                                 use_str = self._pkg._get_pkgsettings()["PORTAGE_USE"]
617                                 calculated_use = True
618                         _PackageMetadataWrapperBase.__setitem__(self, 'USE', use_str)
619                         self._pkg._use = self._pkg._use_class(
620                                 self._pkg, use_str)
621                         # Initialize these now, since USE access has just triggered
622                         # setcpv, and we want to cache the result of the force/mask
623                         # calculations that were done.
624                         if calculated_use:
625                                 self._pkg._use._init_force_mask()
626
627                 return use_str
628
629         def __getitem__(self, k):
630                 v = _PackageMetadataWrapperBase.__getitem__(self, k)
631                 if k in self._use_conditional_keys:
632                         if self._pkg.root_config.settings.local_config and '?' in v:
633                                 try:
634                                         v = paren_enclose(use_reduce(v, uselist=self._pkg.use.enabled, \
635                                                 is_valid_flag=self._pkg.iuse.is_valid_flag))
636                                 except InvalidDependString:
637                                         # This error should already have been registered via
638                                         # self._pkg._invalid_metadata().
639                                         pass
640                                 else:
641                                         self[k] = v
642
643                 elif k == 'USE' and not self._pkg.built:
644                         if not v:
645                                 # This is lazy because it's expensive.
646                                 v = self._init_use()
647
648                 return v
649
650         def __setitem__(self, k, v):
651                 _PackageMetadataWrapperBase.__setitem__(self, k, v)
652                 if k in self._wrapped_keys:
653                         getattr(self, "_set_" + k.lower())(k, v)
654
655         def _set_inherited(self, k, v):
656                 if isinstance(v, basestring):
657                         v = frozenset(v.split())
658                 self._pkg.inherited = v
659
660         def _set_iuse(self, k, v):
661                 self._pkg.iuse = self._pkg._iuse(
662                         v.split(), self._pkg.root_config.settings._iuse_implicit_match)
663
664         def _set_slot(self, k, v):
665                 self._pkg.slot = v
666
667         def _set_counter(self, k, v):
668                 if isinstance(v, basestring):
669                         try:
670                                 v = long(v.strip())
671                         except ValueError:
672                                 v = 0
673                 self._pkg.counter = v
674
675         def _set_use(self, k, v):
676                 # Force regeneration of _use attribute
677                 self._pkg._use = None
678                 # Use raw metadata to restore USE conditional values
679                 # to unevaluated state
680                 raw_metadata = self._pkg._raw_metadata
681                 for x in self._use_conditional_keys:
682                         try:
683                                 self[x] = raw_metadata[x]
684                         except KeyError:
685                                 pass
686
687         def _set__mtime_(self, k, v):
688                 if isinstance(v, basestring):
689                         try:
690                                 v = long(v.strip())
691                         except ValueError:
692                                 v = 0
693                 self._pkg.mtime = v
694
695         @property
696         def properties(self):
697                 return self['PROPERTIES'].split()
698
699         @property
700         def restrict(self):
701                 return self['RESTRICT'].split()
702
703         @property
704         def defined_phases(self):
705                 """
706                 Returns tokens from DEFINED_PHASES metadata if it is defined,
707                 otherwise returns a tuple containing all possible phases. This
708                 makes it easy to do containment checks to see if it's safe to
709                 skip execution of a given phase.
710                 """
711                 s = self['DEFINED_PHASES']
712                 if s:
713                         return s.split()
714                 return EBUILD_PHASES