Bug #285979 - Replace references to CDEPEND with UNUSED_00, to make it clear
[portage.git] / pym / _emerge / Package.py
1 # Copyright 1999-2009 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3 # $Id$
4
5 import re
6 import sys
7 from itertools import chain
8 import portage
9 from portage.dep import paren_reduce, use_reduce, \
10         paren_normalize, paren_enclose
11 from _emerge.Task import Task
12
13 if sys.hexversion >= 0x3000000:
14         basestring = str
15         long = int
16
17 class Package(Task):
18
19         __hash__ = Task.__hash__
20         __slots__ = ("built", "cpv", "depth",
21                 "installed", "metadata", "onlydeps", "operation",
22                 "root_config", "type_name",
23                 "category", "counter", "cp", "cpv_split",
24                 "inherited", "invalid", "iuse", "mtime",
25                 "pf", "pv_split", "root", "slot", "slot_atom",) + \
26         ("_use",)
27
28         metadata_keys = [
29                 "CHOST", "COUNTER", "DEPEND", "EAPI",
30                 "INHERITED", "IUSE", "KEYWORDS",
31                 "LICENSE", "PDEPEND", "PROVIDE", "RDEPEND",
32                 "repository", "PROPERTIES", "RESTRICT", "SLOT", "USE", "_mtime_"]
33
34         def __init__(self, **kwargs):
35                 Task.__init__(self, **kwargs)
36                 self.root = self.root_config.root
37                 self.metadata = _PackageMetadataWrapper(self, self.metadata)
38                 if not self.built:
39                         self.metadata['CHOST'] = self.root_config.settings.get('CHOST', '')
40                 self.cp = portage.cpv_getkey(self.cpv)
41                 slot = self.slot
42                 if not slot:
43                         # Avoid an InvalidAtom exception when creating slot_atom.
44                         # This package instance will be masked due to empty SLOT.
45                         slot = '0'
46                 self.slot_atom = portage.dep.Atom("%s:%s" % (self.cp, slot))
47                 self.category, self.pf = portage.catsplit(self.cpv)
48                 self.cpv_split = portage.catpkgsplit(self.cpv)
49                 self.pv_split = self.cpv_split[1:]
50
51         def _invalid_metadata(self, msg_type, msg):
52                 if self.invalid is None:
53                         self.invalid = {}
54                 msgs = self.invalid.get(msg_type)
55                 if msgs is None:
56                         msgs = []
57                         self.invalid[msg_type] = msgs
58                 msgs.append(msg)
59
60         class _use_class(object):
61
62                 __slots__ = ("__weakref__", "enabled")
63
64                 def __init__(self, use):
65                         self.enabled = frozenset(use)
66
67         @property
68         def use(self):
69                 if self._use is None:
70                         self._use = self._use_class(self.metadata['USE'].split())
71                 return self._use
72
73         class _iuse(object):
74
75                 __slots__ = ("__weakref__", "all", "enabled", "disabled",
76                         "iuse_implicit", "tokens") + \
77                         ('_regex',)
78
79                 def __init__(self, tokens, iuse_implicit):
80                         self.tokens = tuple(tokens)
81                         self.iuse_implicit = iuse_implicit
82                         enabled = []
83                         disabled = []
84                         other = []
85                         for x in tokens:
86                                 prefix = x[:1]
87                                 if prefix == "+":
88                                         enabled.append(x[1:])
89                                 elif prefix == "-":
90                                         disabled.append(x[1:])
91                                 else:
92                                         other.append(x)
93                         self.enabled = frozenset(enabled)
94                         self.disabled = frozenset(disabled)
95                         self.all = frozenset(chain(enabled, disabled, other))
96
97                 @property
98                 def regex(self):
99                         """
100                         @returns: A regular expression that matches valid USE values which
101                                 may be specified in USE dependencies.
102                         """
103                         try:
104                                 return self._regex
105                         except AttributeError:
106                                 # Escape anything except ".*" which is supposed
107                                 # to pass through from _get_implicit_iuse()
108                                 regex = (re.escape(x) for x in \
109                                         chain(self.all, self.iuse_implicit))
110                                 regex = "^(%s)$" % "|".join(regex)
111                                 regex = re.compile(regex.replace("\\.\\*", ".*"))
112                                 self._regex = regex
113                                 return regex
114
115         def _get_hash_key(self):
116                 hash_key = getattr(self, "_hash_key", None)
117                 if hash_key is None:
118                         if self.operation is None:
119                                 self.operation = "merge"
120                                 if self.onlydeps or self.installed:
121                                         self.operation = "nomerge"
122                         self._hash_key = \
123                                 (self.type_name, self.root, self.cpv, self.operation)
124                 return self._hash_key
125
126         def __lt__(self, other):
127                 if other.cp != self.cp:
128                         return False
129                 if portage.pkgcmp(self.pv_split, other.pv_split) < 0:
130                         return True
131                 return False
132
133         def __le__(self, other):
134                 if other.cp != self.cp:
135                         return False
136                 if portage.pkgcmp(self.pv_split, other.pv_split) <= 0:
137                         return True
138                 return False
139
140         def __gt__(self, other):
141                 if other.cp != self.cp:
142                         return False
143                 if portage.pkgcmp(self.pv_split, other.pv_split) > 0:
144                         return True
145                 return False
146
147         def __ge__(self, other):
148                 if other.cp != self.cp:
149                         return False
150                 if portage.pkgcmp(self.pv_split, other.pv_split) >= 0:
151                         return True
152                 return False
153
154 _all_metadata_keys = set(x for x in portage.auxdbkeys \
155         if not x.startswith("UNUSED_"))
156 _all_metadata_keys.update(Package.metadata_keys)
157 _all_metadata_keys = frozenset(_all_metadata_keys)
158
159 class _PackageMetadataWrapper(dict):
160         """
161         Detect metadata updates and synchronize Package attributes.
162         """
163
164         __slots__ = ("_pkg",)
165         _use_conditional_keys = frozenset(
166                 ['LICENSE', 'PROPERTIES', 'PROVIDE', 'RESTRICT',])
167
168         def __init__(self, pkg, metadata):
169                 self._pkg = pkg
170                 if not pkg.built:
171                         # USE is lazy, but we want it to show up in self.keys().
172                         self['USE'] = ''
173                 self.update(metadata)
174                 for k, v in self.items():
175                         if k == 'INHERITED':
176                                 if isinstance(v, basestring):
177                                         v = frozenset(v.split())
178                                 self._pkg.inherited = v
179                         elif k == 'SLOT':
180                                 self._pkg.slot = v
181                         elif k == 'IUSE':
182                                 self._pkg.iuse = self._pkg._iuse(
183                                         v.split(), self._pkg.root_config.iuse_implicit)
184                         elif k == 'COUNTER':
185                                 if isinstance(v, basestring):
186                                         try:
187                                                 v = long(v.strip())
188                                         except ValueError:
189                                                 v = 0
190                                                 self['COUNTER'] = str(v)
191                                 self._pkg.counter = v
192                         elif k == '_mtime_':
193                                 if isinstance(v, basestring):
194                                         try:
195                                                 v = long(v.strip())
196                                         except ValueError:
197                                                 v = 0
198                                 self._pkg.mtime = v
199                         elif k in self._use_conditional_keys:
200                                 try:
201                                         use_reduce(paren_reduce(v), matchall=1)
202                                 except portage.exception.InvalidDependString as e:
203                                         self._pkg._invalid_metadata(k + ".syntax", "%s: %s" % (k, e))
204
205         def __getitem__(self, k):
206                 v = dict.__getitem__(self, k)
207                 if k in self._use_conditional_keys:
208                         if self._pkg.root_config.settings.local_config and '?' in v:
209                                 try:
210                                         v = paren_enclose(paren_normalize(use_reduce(
211                                                 paren_reduce(v), uselist=self._pkg.use.enabled)))
212                                 except portage.exception.InvalidDependString:
213                                         # This error should already have been registered via
214                                         # self._pkg._invalid_metadata().
215                                         pass
216                                 else:
217                                         self[k] = v
218
219                 elif k == 'USE' and not self._pkg.built:
220                         if not v:
221                                 # This is lazy because it's expensive.
222                                 pkgsettings = self._pkg.root_config.trees[
223                                         'porttree'].dbapi.doebuild_settings
224                                 pkgsettings.setcpv(self._pkg)
225                                 v = pkgsettings["PORTAGE_USE"]
226                                 self['USE'] = v
227
228                 return v
229
230         @property
231         def properties(self):
232                 return self['PROPERTIES'].split()
233
234         @property
235         def restrict(self):
236                 return self['RESTRICT'].split()