1 # Copyright 1999-2011 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
8 from _emerge.Package import Package
9 from _emerge.PackageVirtualDbapi import PackageVirtualDbapi
10 from portage.const import VDB_PATH
11 from portage.dbapi.vartree import vartree
12 from portage.repository.config import _gen_valid_repo
13 from portage.update import grab_updates, parse_updates, update_dbentries
15 if sys.hexversion >= 0x3000000:
18 class FakeVardbapi(PackageVirtualDbapi):
20 Implements the vardbapi.getpath() method which is used in error handling
21 code for the Package class and vartree.get_provide().
23 def getpath(self, cpv, filename=None):
24 path = os.path.join(self.settings['EROOT'], VDB_PATH, cpv)
25 if filename is not None:
26 path =os.path.join(path, filename)
29 class FakeVartree(vartree):
30 """This is implements an in-memory copy of a vartree instance that provides
31 all the interfaces required for use by the depgraph. The vardb is locked
32 during the constructor call just long enough to read a copy of the
33 installed package information. This allows the depgraph to do it's
34 dependency calculations without holding a lock on the vardb. It also
35 allows things like vardb global updates to be done in memory so that the
36 user doesn't necessarily need write access to the vardb in cases where
37 global updates are necessary (updates are performed when necessary if there
38 is not a matching ebuild in the tree). Instances of this class are not
39 populated until the sync() method is called."""
40 def __init__(self, root_config, pkg_cache=None, pkg_root_config=None):
41 self._root_config = root_config
42 if pkg_root_config is None:
43 pkg_root_config = self._root_config
44 self._pkg_root_config = pkg_root_config
47 real_vartree = root_config.trees["vartree"]
48 self._real_vardb = real_vartree.dbapi
49 portdb = root_config.trees["porttree"].dbapi
50 self.root = real_vartree.root
51 self.settings = real_vartree.settings
52 mykeys = list(real_vartree.dbapi._aux_cache_keys)
53 if "_mtime_" not in mykeys:
54 mykeys.append("_mtime_")
55 self._db_keys = mykeys
56 self._pkg_cache = pkg_cache
57 self.dbapi = FakeVardbapi(real_vartree.settings)
59 # Initialize variables needed for lazy cache pulls of the live ebuild
60 # metadata. This ensures that the vardb lock is released ASAP, without
61 # being delayed in case cache generation is triggered.
62 self._aux_get = self.dbapi.aux_get
63 self.dbapi.aux_get = self._aux_get_wrapper
64 self._match = self.dbapi.match
65 self.dbapi.match = self._match_wrapper
66 self._aux_get_history = set()
67 self._portdb_keys = ["EAPI", "DEPEND", "RDEPEND", "PDEPEND"]
69 self._global_updates = None
71 def _match_wrapper(self, cpv, use_cache=1):
73 Make sure the metadata in Package instances gets updated for any
74 cpv that is returned from a match() call, since the metadata can
75 be accessed directly from the Package instance instead of via
78 matches = self._match(cpv, use_cache=use_cache)
80 if cpv in self._aux_get_history:
82 self._aux_get_wrapper(cpv, [])
85 def _aux_get_wrapper(self, pkg, wants, myrepo=None):
86 if pkg in self._aux_get_history:
87 return self._aux_get(pkg, wants)
88 self._aux_get_history.add(pkg)
89 # We need to check the EAPI, and this also raises
90 # a KeyError to the caller if appropriate.
91 installed_eapi, repo = self._aux_get(pkg, ["EAPI", "repository"])
93 # Use the live ebuild metadata if possible.
94 repo = _gen_valid_repo(repo)
95 live_metadata = dict(zip(self._portdb_keys,
96 self._portdb.aux_get(pkg, self._portdb_keys, myrepo=repo)))
97 if not portage.eapi_is_supported(live_metadata["EAPI"]) or \
98 installed_eapi != live_metadata["EAPI"]:
100 self.dbapi.aux_update(pkg, live_metadata)
101 except (KeyError, portage.exception.PortageException):
102 if self._global_updates is None:
103 self._global_updates = \
104 grab_global_updates(self._portdb)
105 perform_global_updates(
106 pkg, self.dbapi, self._global_updates)
107 return self._aux_get(pkg, wants)
109 def cpv_discard(self, pkg):
111 Discard a package from the fake vardb if it exists.
113 old_pkg = self.dbapi.get(pkg)
114 if old_pkg is not None:
115 self.dbapi.cpv_remove(old_pkg)
116 self._pkg_cache.pop(old_pkg, None)
117 self._aux_get_history.discard(old_pkg.cpv)
119 def sync(self, acquire_lock=1):
121 Call this method to synchronize state with the real vardb
122 after one or more packages may have been installed or
127 if acquire_lock and os.access(self._real_vardb._dbroot, os.W_OK):
128 self._real_vardb.lock()
133 self._real_vardb.unlock()
135 # Populate the old-style virtuals using the cached values.
136 # Skip the aux_get wrapper here, to avoid unwanted
139 self.dbapi.aux_get = self._aux_get
140 self.settings._populate_treeVirtuals_if_needed(self)
142 self.dbapi.aux_get = self._aux_get_wrapper
146 real_vardb = self._root_config.trees["vartree"].dbapi
147 current_cpv_set = frozenset(real_vardb.cpv_all())
148 pkg_vardb = self.dbapi
149 pkg_cache = self._pkg_cache
150 aux_get_history = self._aux_get_history
152 # Remove any packages that have been uninstalled.
153 for pkg in list(pkg_vardb):
154 if pkg.cpv not in current_cpv_set:
155 self.cpv_discard(pkg)
157 # Validate counters and timestamps.
159 root_config = self._pkg_root_config
160 validation_keys = ["COUNTER", "_mtime_"]
161 for cpv in current_cpv_set:
163 pkg_hash_key = Package._gen_hash_key(cpv=cpv,
164 installed=True, root_config=root_config,
165 type_name="installed")
166 pkg = pkg_vardb.get(pkg_hash_key)
168 counter, mtime = real_vardb.aux_get(cpv, validation_keys)
170 counter = long(counter)
174 if counter != pkg.counter or \
176 self.cpv_discard(pkg)
182 other_counter = slot_counters.get(pkg.slot_atom)
183 if other_counter is not None:
184 if other_counter > pkg.counter:
187 slot_counters[pkg.slot_atom] = pkg.counter
188 pkg_vardb.cpv_inject(pkg)
190 real_vardb.flush_cache()
194 The RootConfig instance that will become the Package.root_config
195 attribute can be overridden by the FakeVartree pkg_root_config
196 constructory argument, since we want to be consistent with the
197 depgraph._pkg() method which uses a specially optimized
198 RootConfig that has a FakeVartree instead of a real vartree.
200 pkg = Package(cpv=cpv, built=True, installed=True,
201 metadata=zip(self._db_keys,
202 self._real_vardb.aux_get(cpv, self._db_keys)),
203 root_config=self._pkg_root_config,
204 type_name="installed")
207 mycounter = long(pkg.metadata["COUNTER"])
210 pkg.metadata["COUNTER"] = str(mycounter)
212 self._pkg_cache[pkg] = pkg
215 def grab_global_updates(portdb):
218 for repo_name in portdb.getRepositories():
219 repo = portdb.getRepositoryPath(repo_name)
220 updpath = os.path.join(repo, "profiles", "updates")
221 if not os.path.isdir(updpath):
225 rawupdates = grab_updates(updpath)
226 except portage.exception.DirectoryNotFound:
229 for mykey, mystat, mycontent in rawupdates:
230 commands, errors = parse_updates(mycontent)
231 upd_commands.extend(commands)
232 retupdates[repo_name] = upd_commands
234 master_repo = portdb.getRepositoryName(portdb.porttree_root)
235 if master_repo in retupdates:
236 retupdates['DEFAULT'] = retupdates[master_repo]
240 def perform_global_updates(mycpv, mydb, myupdates):
241 aux_keys = ["DEPEND", "RDEPEND", "PDEPEND", 'repository']
242 aux_dict = dict(zip(aux_keys, mydb.aux_get(mycpv, aux_keys)))
243 repository = aux_dict.pop('repository')
245 mycommands = myupdates[repository]
248 mycommands = myupdates['DEFAULT']
255 updates = update_dbentries(mycommands, aux_dict)
257 mydb.aux_update(mycpv, updates)