aboutsummaryrefslogtreecommitdiff
blob: c4d1e363504963a20469b439890611db2222b171 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# Copyright 2010 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

__all__ = (
	'VirtualsManager',
)

from copy import deepcopy

from portage import os
from portage.dep import Atom
from portage.exception import InvalidAtom
from portage.localization import _
from portage.util import grabdict, stack_dictlist, writemsg
from portage.versions import cpv_getkey

class VirtualsManager(object):

	def __init__(self, *args, **kwargs):
		if kwargs.get("_copy"):
			return

		assert len(args) == 1, "VirtualsManager.__init__ takes one positional argument"
		assert not kwargs, "unknown keyword argument(s) '%s' passed to VirtualsManager.__init__" % \
			", ".join(kwargs)

		profiles = args[0]
		self._virtuals = None
		self._dirVirtuals = None
		self._virts_p = None

		# Virtuals obtained from the vartree
		self._treeVirtuals = None
		# Virtuals added by the depgraph via self.add_depgraph_virtuals().
		self._depgraphVirtuals = {}

		#Initialise _dirVirtuals.
		self._read_dirVirtuals(profiles)

		#We could initialise _treeVirtuals here, but some consumers want to
		#pass their own vartree.

	def _read_dirVirtuals(self, profiles):
		"""
		Read the 'virtuals' file in all profiles.
		"""
		virtuals_list = []
		for x in profiles:
			virtuals_file = os.path.join(x, "virtuals")
			virtuals_dict = grabdict(virtuals_file)
			atoms_dict = {}
			for k, v in virtuals_dict.items():
				try:
					virt_atom = Atom(k)
				except InvalidAtom:
					virt_atom = None
				else:
					if virt_atom.blocker or \
						str(virt_atom) != str(virt_atom.cp):
						virt_atom = None
				if virt_atom is None:
					writemsg(_("--- Invalid virtuals atom in %s: %s\n") % \
						(virtuals_file, k), noiselevel=-1)
					continue
				providers = []
				for atom in v:
					atom_orig = atom
					if atom[:1] == '-':
						# allow incrementals
						atom = atom[1:]
					try:
						atom = Atom(atom)
					except InvalidAtom:
						atom = None
					else:
						if atom.blocker:
							atom = None
					if atom is None:
						writemsg(_("--- Invalid atom in %s: %s\n") % \
							(virtuals_file, atom_orig), noiselevel=-1)
					else:
						if atom_orig == str(atom):
							# normal atom, so return as Atom instance
							providers.append(atom)
						else:
							# atom has special prefix, so return as string
							providers.append(atom_orig)
				if providers:
					atoms_dict[virt_atom] = providers
			if atoms_dict:
				virtuals_list.append(atoms_dict)

		self._dirVirtuals = stack_dictlist(virtuals_list, incremental=True)

		for virt in self._dirVirtuals:
			# Preference for virtuals decreases from left to right.
			self._dirVirtuals[virt].reverse()

	def __deepcopy__(self, memo=None):
		if memo is None:
			memo = {}
		result = VirtualsManager(_copy=True)
		memo[id(self)] = result

		# immutable attributes (internal policy ensures lack of mutation)
		# _treeVirtuals is initilised by _populate_treeVirtuals().
		# Before that it's 'None'.
		result._treeVirtuals = self._treeVirtuals
		memo[id(self._treeVirtuals)] = self._treeVirtuals
		# _dirVirtuals is initilised by __init__.
		result._dirVirtuals = self._dirVirtuals
		memo[id(self._dirVirtuals)] = self._dirVirtuals

		# mutable attributes (change when add_depgraph_virtuals() is called)
		result._virtuals = deepcopy(self._virtuals, memo)
		result._depgraphVirtuals = deepcopy(self._depgraphVirtuals, memo)
		result._virts_p = deepcopy(self._virts_p, memo)

		return result

	def _compile_virtuals(self):
		"""Stack installed and profile virtuals.  Preference for virtuals
		decreases from left to right.
		Order of preference:
		1. installed and in profile
		2. installed only
		3. profile only
		"""

		assert self._treeVirtuals is not None, "_populate_treeVirtuals() must be called before " + \
			"any query about virtuals"

		# Virtuals by profile+tree preferences.
		ptVirtuals   = {}

		for virt, installed_list in self._treeVirtuals.items():
			profile_list = self._dirVirtuals.get(virt, None)
			if not profile_list:
				continue
			for cp in installed_list:
				if cp in profile_list:
					ptVirtuals.setdefault(virt, [])
					ptVirtuals[virt].append(cp)

		virtuals = stack_dictlist([ptVirtuals, self._treeVirtuals,
			self._dirVirtuals, self._depgraphVirtuals])
		self._virtuals = virtuals
		self._virts_p = None

	def getvirtuals(self):
		"""
		Computes self._virtuals if necessary and returns it.
		self._virtuals is only computed on the first call.
		"""
		if self._virtuals is None:
			self._compile_virtuals()

		return self._virtuals

	def _populate_treeVirtuals(self, vartree):
		"""
		Initialize _treeVirtuals from the given vartree.
		It must not have been initialized already, otherwise
		our assumptions about immutability don't hold.
		"""
		assert self._treeVirtuals is None, "treeVirtuals must not be reinitialized"

		self._treeVirtuals = {}

		for provide, cpv_list in vartree.get_all_provides().items():
			try:
				provide = Atom(provide)
			except InvalidAtom:
				continue
			self._treeVirtuals[provide.cp] = \
				[Atom(cpv_getkey(cpv)) for cpv in cpv_list]

	def populate_treeVirtuals_if_needed(self, vartree):
		"""
		Initialize _treeVirtuals if it hasn't been done already.
		This is a hack for consumers that already have an populated vartree.
		"""
		if self._treeVirtuals is not None:
			return

		self._populate_treeVirtuals(vartree)

	def add_depgraph_virtuals(self, mycpv, virts):
		"""This updates the preferences for old-style virtuals,
		affecting the behavior of dep_expand() and dep_check()
		calls. It can change dbapi.match() behavior since that
		calls dep_expand(). However, dbapi instances have
		internal match caches that are not invalidated when
		preferences are updated here. This can potentially
		lead to some inconsistency (relevant to bug #1343)."""

		#Ensure that self._virtuals is populated.
		if self._virtuals is None:
			self.getvirtuals()

		modified = False
		cp = Atom(cpv_getkey(mycpv))
		for virt in virts:
			try:
				virt = Atom(virt).cp
			except InvalidAtom:
				continue
			providers = self._virtuals.get(virt)
			if providers and cp in providers:
				continue
			providers = self._depgraphVirtuals.get(virt)
			if providers is None:
				providers = []
				self._depgraphVirtuals[virt] = providers
			if cp not in providers:
				providers.append(cp)
				modified = True

		if modified:
			self._compile_virtuals()

	def get_virts_p(self):
		if self._virts_p is not None:
			return self._virts_p

		virts = self.getvirtuals()
		virts_p = {}
		for x in virts:
			vkeysplit = x.split("/")
			if vkeysplit[1] not in virts_p:
				virts_p[vkeysplit[1]] = virts[x]
		self._virts_p = virts_p
		return virts_p