Bug #337180 - Add a --unmatched-removal option for stricter checking
[portage.git] / pym / portage / package / ebuild / _config / MaskManager.py
1 # Copyright 2010 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3
4 __all__ = (
5         'MaskManager',
6 )
7
8 from itertools import chain
9 from portage import os
10 from portage.dep import ExtendedAtomDict, match_from_list
11 from portage.util import grabfile_package, stack_lists
12 from portage.versions import cpv_getkey
13
14 class MaskManager(object):
15
16         def __init__(self, pmask_locations, abs_user_config,
17                 user_config=True, strict_umatched_removal=False):
18                 self._punmaskdict = ExtendedAtomDict(list)
19                 self._pmaskdict = ExtendedAtomDict(list)
20
21                 repo_profiles, profiles = pmask_locations
22
23                 #Read profile/package.mask form every repo. Stack them immediately
24                 #to make sure that -atoms don't effect other repos.
25                 repo_pkgmasklines = []
26                 repo_pkgunmasklines = []
27                 for x in repo_profiles:
28                         repo_pkgmasklines.append(stack_lists([grabfile_package(
29                                 os.path.join(x, "package.mask"), recursive=1, remember_source_file=True, verify_eapi=True)], \
30                                         incremental=1, remember_source_file=True,
31                                         warn_for_unmatched_removal=True,
32                                         strict_warn_for_unmatched_removal=strict_umatched_removal))
33                         repo_pkgunmasklines.append(stack_lists([grabfile_package(
34                                 os.path.join(x, "package.unmask"), recursive=1, remember_source_file=True, verify_eapi=True)], \
35                                 incremental=1, remember_source_file=True,
36                                 warn_for_unmatched_removal=True,
37                                 strict_warn_for_unmatched_removal=strict_umatched_removal))
38                 repo_pkgmasklines = list(chain.from_iterable(repo_pkgmasklines))
39                 repo_pkgunmasklines = list(chain.from_iterable(repo_pkgunmasklines))
40
41                 #Read package.mask form the user's profile. Stack them in the end
42                 #to allow profiles to override masks from their parent profiles.
43                 profile_pkgmasklines = []
44                 profile_pkgunmasklines = []
45                 for x in profiles:
46                         profile_pkgmasklines.append(grabfile_package(
47                                 os.path.join(x, "package.mask"), recursive=1, remember_source_file=True, verify_eapi=True))
48                         profile_pkgunmasklines.append(grabfile_package(
49                                 os.path.join(x, "package.unmask"), recursive=1, remember_source_file=True, verify_eapi=True))
50                 profile_pkgmasklines = stack_lists(profile_pkgmasklines, incremental=1, \
51                         remember_source_file=True, warn_for_unmatched_removal=True,
52                         strict_warn_for_unmatched_removal=strict_umatched_removal)
53                 profile_pkgunmasklines = stack_lists(profile_pkgunmasklines, incremental=1, \
54                         remember_source_file=True, warn_for_unmatched_removal=True,
55                         strict_warn_for_unmatched_removal=strict_umatched_removal)
56
57                 #Read /etc/portage/package.mask. Don't stack it to allow the user to
58                 #remove mask atoms from everywhere with -atoms.
59                 user_pkgmasklines = []
60                 user_pkgunmasklines = []
61                 if user_config:
62                         user_pkgmasklines = grabfile_package(
63                                 os.path.join(abs_user_config, "package.mask"), recursive=1, \
64                                 allow_wildcard=True, remember_source_file=True, verify_eapi=False)
65                         user_pkgunmasklines = grabfile_package(
66                                 os.path.join(abs_user_config, "package.unmask"), recursive=1, \
67                                 allow_wildcard=True, remember_source_file=True, verify_eapi=False)
68
69                 #Stack everything together. At this point, only user_pkgmasklines may contain -atoms.
70                 #Don't warn for unmatched -atoms here, since we don't do it for any other user config file.
71                 pkgmasklines = stack_lists([repo_pkgmasklines, profile_pkgmasklines, user_pkgmasklines], \
72                         incremental=1, remember_source_file=True, warn_for_unmatched_removal=False)
73                 pkgunmasklines = stack_lists([repo_pkgunmasklines, profile_pkgunmasklines, user_pkgunmasklines], \
74                         incremental=1, remember_source_file=True, warn_for_unmatched_removal=False)
75
76                 for x, source_file in pkgmasklines:
77                         self._pmaskdict.setdefault(x.cp, []).append(x)
78
79                 for x, source_file in pkgunmasklines:
80                         self._punmaskdict.setdefault(x.cp, []).append(x)
81
82                 for d in (self._pmaskdict, self._punmaskdict):
83                         for k, v in d.items():
84                                 d[k] = tuple(v)
85
86         def getMaskAtom(self, cpv, slot):
87                 """
88                 Take a package and return a matching package.mask atom, or None if no
89                 such atom exists or it has been cancelled by package.unmask. PROVIDE
90                 is not checked, so atoms will not be found for old-style virtuals.
91
92                 @param cpv: The package name
93                 @type cpv: String
94                 @param slot: The package's slot
95                 @type slot: String
96                 @rtype: String
97                 @return: An matching atom string or None if one is not found.
98                 """
99
100                 cp = cpv_getkey(cpv)
101                 mask_atoms = self._pmaskdict.get(cp)
102                 if mask_atoms:
103                         pkg_list = ["%s:%s" % (cpv, slot)]
104                         unmask_atoms = self._punmaskdict.get(cp)
105                         for x in mask_atoms:
106                                 if not match_from_list(x, pkg_list):
107                                         continue
108                                 if unmask_atoms:
109                                         for y in unmask_atoms:
110                                                 if match_from_list(y, pkg_list):
111                                                         return None
112                                 return x
113                 return None