repoman: fix stable mask to use correct profile
[portage.git] / pym / portage / package / ebuild / _config / UseManager.py
1 # Copyright 2010-2012 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3
4 __all__ = (
5         'UseManager',
6 )
7
8 from _emerge.Package import Package
9 from portage import os
10 from portage.dep import Atom, dep_getrepo, dep_getslot, ExtendedAtomDict, remove_slot, _get_useflag_re, _repo_separator
11 from portage.eapi import eapi_has_use_aliases, eapi_supports_stable_use_forcing_and_masking
12 from portage.exception import InvalidAtom
13 from portage.localization import _
14 from portage.util import grabfile, grabdict, grabdict_package, read_corresponding_eapi_file, stack_lists, writemsg
15 from portage.versions import _pkg_str
16
17 from portage.package.ebuild._config.helper import ordered_by_atom_specificity
18
19 class UseManager(object):
20
21         def __init__(self, repositories, profiles, abs_user_config, is_stable,
22                 user_config=True):
23                 #       file                            variable
24                 #--------------------------------
25                 #       repositories
26                 #--------------------------------
27                 #       use.mask                        _repo_usemask_dict
28                 #       use.stable.mask                 _repo_usestablemask_dict
29                 #       use.force                       _repo_useforce_dict
30                 #       use.stable.force                _repo_usestableforce_dict
31                 #       use.aliases                     _repo_usealiases_dict
32                 #       package.use.mask                _repo_pusemask_dict
33                 #       package.use.stable.mask         _repo_pusestablemask_dict
34                 #       package.use.force               _repo_puseforce_dict
35                 #       package.use.stable.force        _repo_pusestableforce_dict
36                 #       package.use.aliases             _repo_pusealiases_dict
37                 #--------------------------------
38                 #       profiles
39                 #--------------------------------
40                 #       use.mask                        _usemask_list
41                 #       use.stable.mask                 _usestablemask_list
42                 #       use.force                       _useforce_list
43                 #       use.stable.force                _usestableforce_list
44                 #       package.use.mask                _pusemask_list
45                 #       package.use.stable.mask         _pusestablemask_list
46                 #       package.use                     _pkgprofileuse
47                 #       package.use.force               _puseforce_list
48                 #       package.use.stable.force        _pusestableforce_list
49                 #--------------------------------
50                 #       user config
51                 #--------------------------------
52                 #       package.use                     _pusedict
53
54                 # Dynamic variables tracked by the config class
55                 #--------------------------------
56                 #       profiles
57                 #--------------------------------
58                 #       usemask
59                 #       useforce
60                 #--------------------------------
61                 #       user config
62                 #--------------------------------
63                 #       puse
64
65                 self._user_config = user_config
66                 self._is_stable = is_stable
67                 self._repo_usemask_dict = self._parse_repository_files_to_dict_of_tuples("use.mask", repositories)
68                 self._repo_usestablemask_dict = \
69                         self._parse_repository_files_to_dict_of_tuples("use.stable.mask",
70                                 repositories, eapi_filter=eapi_supports_stable_use_forcing_and_masking)
71                 self._repo_useforce_dict = self._parse_repository_files_to_dict_of_tuples("use.force", repositories)
72                 self._repo_usestableforce_dict = \
73                         self._parse_repository_files_to_dict_of_tuples("use.stable.force",
74                                 repositories, eapi_filter=eapi_supports_stable_use_forcing_and_masking)
75                 self._repo_pusemask_dict = self._parse_repository_files_to_dict_of_dicts("package.use.mask", repositories)
76                 self._repo_pusestablemask_dict = \
77                         self._parse_repository_files_to_dict_of_dicts("package.use.stable.mask",
78                                 repositories, eapi_filter=eapi_supports_stable_use_forcing_and_masking)
79                 self._repo_puseforce_dict = self._parse_repository_files_to_dict_of_dicts("package.use.force", repositories)
80                 self._repo_pusestableforce_dict = \
81                         self._parse_repository_files_to_dict_of_dicts("package.use.stable.force",
82                                 repositories, eapi_filter=eapi_supports_stable_use_forcing_and_masking)
83                 self._repo_puse_dict = self._parse_repository_files_to_dict_of_dicts("package.use", repositories)
84
85                 self._usemask_list = self._parse_profile_files_to_tuple_of_tuples("use.mask", profiles)
86                 self._usestablemask_list = \
87                         self._parse_profile_files_to_tuple_of_tuples("use.stable.mask",
88                                 profiles, eapi_filter=eapi_supports_stable_use_forcing_and_masking)
89                 self._useforce_list = self._parse_profile_files_to_tuple_of_tuples("use.force", profiles)
90                 self._usestableforce_list = \
91                         self._parse_profile_files_to_tuple_of_tuples("use.stable.force",
92                                 profiles, eapi_filter=eapi_supports_stable_use_forcing_and_masking)
93                 self._pusemask_list = self._parse_profile_files_to_tuple_of_dicts("package.use.mask", profiles)
94                 self._pusestablemask_list = \
95                         self._parse_profile_files_to_tuple_of_dicts("package.use.stable.mask",
96                                 profiles, eapi_filter=eapi_supports_stable_use_forcing_and_masking)
97                 self._pkgprofileuse = self._parse_profile_files_to_tuple_of_dicts("package.use", profiles, juststrings=True)
98                 self._puseforce_list = self._parse_profile_files_to_tuple_of_dicts("package.use.force", profiles)
99                 self._pusestableforce_list = \
100                         self._parse_profile_files_to_tuple_of_dicts("package.use.stable.force",
101                                 profiles, eapi_filter=eapi_supports_stable_use_forcing_and_masking)
102
103                 self._pusedict = self._parse_user_files_to_extatomdict("package.use", abs_user_config, user_config)
104
105                 self._repo_usealiases_dict = self._parse_repository_usealiases(repositories)
106                 self._repo_pusealiases_dict = self._parse_repository_packageusealiases(repositories)
107
108                 self.repositories = repositories
109
110         def _parse_file_to_tuple(self, file_name, recursive=True, eapi_filter=None):
111                 ret = []
112                 lines = grabfile(file_name, recursive=recursive)
113                 eapi = read_corresponding_eapi_file(file_name)
114                 if eapi_filter is not None and not eapi_filter(eapi):
115                         if lines:
116                                 writemsg(_("--- EAPI '%s' does not support '%s': '%s'\n") %
117                                         (eapi, os.path.basename(file_name), file_name),
118                                         noiselevel=-1)
119                         return ()
120                 useflag_re = _get_useflag_re(eapi)
121                 for prefixed_useflag in lines:
122                         if prefixed_useflag[:1] == "-":
123                                 useflag = prefixed_useflag[1:]
124                         else:
125                                 useflag = prefixed_useflag
126                         if useflag_re.match(useflag) is None:
127                                 writemsg(_("--- Invalid USE flag in '%s': '%s'\n") %
128                                         (file_name, prefixed_useflag), noiselevel=-1)
129                         else:
130                                 ret.append(prefixed_useflag)
131                 return tuple(ret)
132
133         def _parse_file_to_dict(self, file_name, juststrings=False, recursive=True,
134                 eapi_filter=None, user_config=False):
135                 ret = {}
136                 location_dict = {}
137                 eapi = read_corresponding_eapi_file(file_name, default=None)
138                 if eapi is None and not user_config:
139                         eapi = "0"
140                 if eapi is None:
141                         ret = ExtendedAtomDict(dict)
142                 else:
143                         ret = {}
144                 file_dict = grabdict_package(file_name, recursive=recursive,
145                         allow_wildcard=(eapi is None), allow_repo=(eapi is None),
146                         verify_eapi=(eapi is not None))
147                 if eapi is not None and eapi_filter is not None and not eapi_filter(eapi):
148                         if file_dict:
149                                 writemsg(_("--- EAPI '%s' does not support '%s': '%s'\n") %
150                                         (eapi, os.path.basename(file_name), file_name),
151                                         noiselevel=-1)
152                         return ret
153                 useflag_re = _get_useflag_re(eapi)
154                 for k, v in file_dict.items():
155                         useflags = []
156                         for prefixed_useflag in v:
157                                 if prefixed_useflag[:1] == "-":
158                                         useflag = prefixed_useflag[1:]
159                                 else:
160                                         useflag = prefixed_useflag
161                                 if useflag_re.match(useflag) is None:
162                                         writemsg(_("--- Invalid USE flag for '%s' in '%s': '%s'\n") %
163                                                 (k, file_name, prefixed_useflag), noiselevel=-1)
164                                 else:
165                                         useflags.append(prefixed_useflag)
166                         location_dict.setdefault(k, []).extend(useflags)
167                 for k, v in location_dict.items():
168                         if juststrings:
169                                 v = " ".join(v)
170                         else:
171                                 v = tuple(v)
172                         ret.setdefault(k.cp, {})[k] = v
173                 return ret
174
175         def _parse_user_files_to_extatomdict(self, file_name, location, user_config):
176                 ret = ExtendedAtomDict(dict)
177                 if user_config:
178                         pusedict = grabdict_package(
179                                 os.path.join(location, file_name), recursive=1, allow_wildcard=True, allow_repo=True, verify_eapi=False)
180                         for k, v in pusedict.items():
181                                 ret.setdefault(k.cp, {})[k] = tuple(v)
182
183                 return ret
184
185         def _parse_repository_files_to_dict_of_tuples(self, file_name, repositories, eapi_filter=None):
186                 ret = {}
187                 for repo in repositories.repos_with_profiles():
188                         ret[repo.name] = self._parse_file_to_tuple(os.path.join(repo.location, "profiles", file_name), eapi_filter=eapi_filter)
189                 return ret
190
191         def _parse_repository_files_to_dict_of_dicts(self, file_name, repositories, eapi_filter=None):
192                 ret = {}
193                 for repo in repositories.repos_with_profiles():
194                         ret[repo.name] = self._parse_file_to_dict(os.path.join(repo.location, "profiles", file_name), eapi_filter=eapi_filter)
195                 return ret
196
197         def _parse_profile_files_to_tuple_of_tuples(self, file_name, locations,
198                 eapi_filter=None):
199                 return tuple(self._parse_file_to_tuple(
200                         os.path.join(profile.location, file_name),
201                         recursive=profile.portage1_directories, eapi_filter=eapi_filter)
202                         for profile in locations)
203
204         def _parse_profile_files_to_tuple_of_dicts(self, file_name, locations,
205                 juststrings=False, eapi_filter=None):
206                 return tuple(self._parse_file_to_dict(
207                         os.path.join(profile.location, file_name), juststrings,
208                         recursive=profile.portage1_directories, eapi_filter=eapi_filter,
209                         user_config=profile.user_config)
210                         for profile in locations)
211
212         def _parse_repository_usealiases(self, repositories):
213                 ret = {}
214                 for repo in repositories.repos_with_profiles():
215                         file_name = os.path.join(repo.location, "profiles", "use.aliases")
216                         eapi = read_corresponding_eapi_file(file_name)
217                         useflag_re = _get_useflag_re(eapi)
218                         raw_file_dict = grabdict(file_name, recursive=True)
219                         file_dict = {}
220                         for real_flag, aliases in raw_file_dict.items():
221                                 if useflag_re.match(real_flag) is None:
222                                         writemsg(_("--- Invalid real USE flag in '%s': '%s'\n") % (file_name, real_flag), noiselevel=-1)
223                                 else:
224                                         for alias in aliases:
225                                                 if useflag_re.match(alias) is None:
226                                                         writemsg(_("--- Invalid USE flag alias for '%s' real USE flag in '%s': '%s'\n") %
227                                                                 (real_flag, file_name, alias), noiselevel=-1)
228                                                 else:
229                                                         if any(alias in v for k, v in file_dict.items() if k != real_flag):
230                                                                 writemsg(_("--- Duplicated USE flag alias in '%s': '%s'\n") %
231                                                                         (file_name, alias), noiselevel=-1)
232                                                         else:
233                                                                 file_dict.setdefault(real_flag, []).append(alias)
234                         ret[repo.name] = file_dict
235                 return ret
236
237         def _parse_repository_packageusealiases(self, repositories):
238                 ret = {}
239                 for repo in repositories.repos_with_profiles():
240                         file_name = os.path.join(repo.location, "profiles", "package.use.aliases")
241                         eapi = read_corresponding_eapi_file(file_name)
242                         useflag_re = _get_useflag_re(eapi)
243                         lines = grabfile(file_name, recursive=True)
244                         file_dict = {}
245                         for line in lines:
246                                 elements = line.split()
247                                 atom = elements[0]
248                                 try:
249                                         atom = Atom(atom, eapi=eapi)
250                                 except InvalidAtom:
251                                         writemsg(_("--- Invalid atom in '%s': '%s'\n") % (file_name, atom))
252                                         continue
253                                 if len(elements) == 1:
254                                         writemsg(_("--- Missing real USE flag for '%s' in '%s'\n") % (atom, file_name), noiselevel=-1)
255                                         continue
256                                 real_flag = elements[1]
257                                 if useflag_re.match(real_flag) is None:
258                                         writemsg(_("--- Invalid real USE flag for '%s' in '%s': '%s'\n") % (atom, file_name, real_flag), noiselevel=-1)
259                                 else:
260                                         for alias in elements[2:]:
261                                                 if useflag_re.match(alias) is None:
262                                                         writemsg(_("--- Invalid USE flag alias for '%s' real USE flag for '%s' in '%s': '%s'\n") %
263                                                                 (real_flag, atom, file_name, alias), noiselevel=-1)
264                                                 else:
265                                                         # Duplicated USE flag aliases in entries for different atoms
266                                                         # matching the same package version are detected in getUseAliases().
267                                                         if any(alias in v for k, v in file_dict.get(atom.cp, {}).get(atom, {}).items() if k != real_flag):
268                                                                 writemsg(_("--- Duplicated USE flag alias for '%s' in '%s': '%s'\n") %
269                                                                         (atom, file_name, alias), noiselevel=-1)
270                                                         else:
271                                                                 file_dict.setdefault(atom.cp, {}).setdefault(atom, {}).setdefault(real_flag, []).append(alias)
272                         ret[repo.name] = file_dict
273                 return ret
274
275         def _isStable(self, pkg):
276                 if self._user_config:
277                         try:
278                                 return pkg.stable
279                         except AttributeError:
280                                 # KEYWORDS is unavailable (prior to "depend" phase)
281                                 return False
282
283                 try:
284                         pkg._metadata
285                 except AttributeError:
286                         # KEYWORDS is unavailable (prior to "depend" phase)
287                         return False
288
289                 # Since repoman uses different config instances for
290                 # different profiles, we have to be careful to do the
291                 # stable check against the correct profile here.
292                 return self._is_stable(pkg)
293
294         def getUseMask(self, pkg=None):
295                 if pkg is None:
296                         return frozenset(stack_lists(
297                                 self._usemask_list, incremental=True))
298
299                 slot = None
300                 cp = getattr(pkg, "cp", None)
301                 if cp is None:
302                         slot = dep_getslot(pkg)
303                         repo = dep_getrepo(pkg)
304                         pkg = _pkg_str(remove_slot(pkg), slot=slot, repo=repo)
305                         cp = pkg.cp
306
307                 stable = self._isStable(pkg)
308
309                 usemask = []
310
311                 if hasattr(pkg, "repo") and pkg.repo != Package.UNKNOWN_REPO:
312                         repos = []
313                         try:
314                                 repos.extend(repo.name for repo in
315                                         self.repositories[pkg.repo].masters)
316                         except KeyError:
317                                 pass
318                         repos.append(pkg.repo)
319                         for repo in repos:
320                                 usemask.append(self._repo_usemask_dict.get(repo, {}))
321                                 if stable:
322                                         usemask.append(self._repo_usestablemask_dict.get(repo, {}))
323                                 cpdict = self._repo_pusemask_dict.get(repo, {}).get(cp)
324                                 if cpdict:
325                                         pkg_usemask = ordered_by_atom_specificity(cpdict, pkg)
326                                         if pkg_usemask:
327                                                 usemask.extend(pkg_usemask)
328                                 if stable:
329                                         cpdict = self._repo_pusestablemask_dict.get(repo, {}).get(cp)
330                                         if cpdict:
331                                                 pkg_usemask = ordered_by_atom_specificity(cpdict, pkg)
332                                                 if pkg_usemask:
333                                                         usemask.extend(pkg_usemask)
334
335                 for i, pusemask_dict in enumerate(self._pusemask_list):
336                         if self._usemask_list[i]:
337                                 usemask.append(self._usemask_list[i])
338                         if stable and self._usestablemask_list[i]:
339                                 usemask.append(self._usestablemask_list[i])
340                         cpdict = pusemask_dict.get(cp)
341                         if cpdict:
342                                 pkg_usemask = ordered_by_atom_specificity(cpdict, pkg)
343                                 if pkg_usemask:
344                                         usemask.extend(pkg_usemask)
345                         if stable:
346                                 cpdict = self._pusestablemask_list[i].get(cp)
347                                 if cpdict:
348                                         pkg_usemask = ordered_by_atom_specificity(cpdict, pkg)
349                                         if pkg_usemask:
350                                                 usemask.extend(pkg_usemask)
351
352                 return frozenset(stack_lists(usemask, incremental=True))
353
354         def getUseForce(self, pkg=None):
355                 if pkg is None:
356                         return frozenset(stack_lists(
357                                 self._useforce_list, incremental=True))
358
359                 cp = getattr(pkg, "cp", None)
360                 if cp is None:
361                         slot = dep_getslot(pkg)
362                         repo = dep_getrepo(pkg)
363                         pkg = _pkg_str(remove_slot(pkg), slot=slot, repo=repo)
364                         cp = pkg.cp
365
366                 stable = self._isStable(pkg)
367
368                 useforce = []
369
370                 if hasattr(pkg, "repo") and pkg.repo != Package.UNKNOWN_REPO:
371                         repos = []
372                         try:
373                                 repos.extend(repo.name for repo in
374                                         self.repositories[pkg.repo].masters)
375                         except KeyError:
376                                 pass
377                         repos.append(pkg.repo)
378                         for repo in repos:
379                                 useforce.append(self._repo_useforce_dict.get(repo, {}))
380                                 if stable:
381                                         useforce.append(self._repo_usestableforce_dict.get(repo, {}))
382                                 cpdict = self._repo_puseforce_dict.get(repo, {}).get(cp)
383                                 if cpdict:
384                                         pkg_useforce = ordered_by_atom_specificity(cpdict, pkg)
385                                         if pkg_useforce:
386                                                 useforce.extend(pkg_useforce)
387                                 if stable:
388                                         cpdict = self._repo_pusestableforce_dict.get(repo, {}).get(cp)
389                                         if cpdict:
390                                                 pkg_useforce = ordered_by_atom_specificity(cpdict, pkg)
391                                                 if pkg_useforce:
392                                                         useforce.extend(pkg_useforce)
393
394                 for i, puseforce_dict in enumerate(self._puseforce_list):
395                         if self._useforce_list[i]:
396                                 useforce.append(self._useforce_list[i])
397                         if stable and self._usestableforce_list[i]:
398                                 useforce.append(self._usestableforce_list[i])
399                         cpdict = puseforce_dict.get(cp)
400                         if cpdict:
401                                 pkg_useforce = ordered_by_atom_specificity(cpdict, pkg)
402                                 if pkg_useforce:
403                                         useforce.extend(pkg_useforce)
404                         if stable:
405                                 cpdict = self._pusestableforce_list[i].get(cp)
406                                 if cpdict:
407                                         pkg_useforce = ordered_by_atom_specificity(cpdict, pkg)
408                                         if pkg_useforce:
409                                                 useforce.extend(pkg_useforce)
410
411                 return frozenset(stack_lists(useforce, incremental=True))
412
413         def getUseAliases(self, pkg):
414                 if not eapi_has_use_aliases(pkg.eapi):
415                         return {}
416
417                 cp = getattr(pkg, "cp", None)
418                 if cp is None:
419                         slot = dep_getslot(pkg)
420                         repo = dep_getrepo(pkg)
421                         pkg = _pkg_str(remove_slot(pkg), slot=slot, repo=repo)
422                         cp = pkg.cp
423
424                 usealiases = {}
425
426                 if hasattr(pkg, "repo") and pkg.repo != Package.UNKNOWN_REPO:
427                         repos = []
428                         try:
429                                 repos.extend(repo.name for repo in
430                                         self.repositories[pkg.repo].masters)
431                         except KeyError:
432                                 pass
433                         repos.append(pkg.repo)
434                         for repo in repos:
435                                 usealiases_dict = self._repo_usealiases_dict.get(repo, {})
436                                 for real_flag, aliases in usealiases_dict.items():
437                                         for alias in aliases:
438                                                 if any(alias in v for k, v in usealiases.items() if k != real_flag):
439                                                         writemsg(_("--- Duplicated USE flag alias for '%s%s%s': '%s'\n") %
440                                                                 (pkg.cpv, _repo_separator, pkg.repo, alias), noiselevel=-1)
441                                                 else:
442                                                         usealiases.setdefault(real_flag, []).append(alias)
443                                 cp_usealiases_dict = self._repo_pusealiases_dict.get(repo, {}).get(cp)
444                                 if cp_usealiases_dict:
445                                         usealiases_dict_list = ordered_by_atom_specificity(cp_usealiases_dict, pkg)
446                                         for usealiases_dict in usealiases_dict_list:
447                                                 for real_flag, aliases in usealiases_dict.items():
448                                                         for alias in aliases:
449                                                                 if any(alias in v for k, v in usealiases.items() if k != real_flag):
450                                                                         writemsg(_("--- Duplicated USE flag alias for '%s%s%s': '%s'\n") %
451                                                                                 (pkg.cpv, _repo_separator, pkg.repo, alias), noiselevel=-1)
452                                                                 else:
453                                                                         usealiases.setdefault(real_flag, []).append(alias)
454
455                 return usealiases
456
457         def getPUSE(self, pkg):
458                 cp = getattr(pkg, "cp", None)
459                 if cp is None:
460                         slot = dep_getslot(pkg)
461                         repo = dep_getrepo(pkg)
462                         pkg = _pkg_str(remove_slot(pkg), slot=slot, repo=repo)
463                         cp = pkg.cp
464                 ret = ""
465                 cpdict = self._pusedict.get(cp)
466                 if cpdict:
467                         puse_matches = ordered_by_atom_specificity(cpdict, pkg)
468                         if puse_matches:
469                                 puse_list = []
470                                 for x in puse_matches:
471                                         puse_list.extend(x)
472                                 ret = " ".join(puse_list)
473                 return ret
474
475         def extract_global_USE_changes(self, old=""):
476                 ret = old
477                 cpdict = self._pusedict.get("*/*")
478                 if cpdict is not None:
479                         v = cpdict.pop("*/*", None)
480                         if v is not None:
481                                 ret = " ".join(v)
482                                 if old:
483                                         ret = old + " " + ret
484                                 if not cpdict:
485                                         #No tokens left in atom_license_map, remove it.
486                                         del self._pusedict["*/*"]
487                 return ret