1 # Copyright 1999-2011 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
4 from __future__ import print_function
9 from portage.dbapi.porttree import _parse_uri_map
10 from portage.output import bold, bold as white, darkgreen, green, red
11 from portage.util import writemsg_stdout
13 from _emerge.Package import Package
26 def __init__(self, root_config, spinner, searchdesc,
27 verbose, usepkg, usepkgonly):
28 """Searches the available and installed packages for the supplied search key.
29 The list of available and installed packages is created at object instantiation.
30 This makes successive searches faster."""
31 self.settings = root_config.settings
32 self.vartree = root_config.trees["vartree"]
33 self.spinner = spinner
34 self.verbose = verbose
35 self.searchdesc = searchdesc
36 self.root_config = root_config
37 self.setconfig = root_config.setconfig
38 self.matches = {"pkg" : []}
43 portdb = root_config.trees["porttree"].dbapi
44 bindb = root_config.trees["bintree"].dbapi
45 vardb = root_config.trees["vartree"].dbapi
47 if not usepkgonly and portdb._have_root_eclass_dir:
48 self._dbs.append(portdb)
50 if (usepkg or usepkgonly) and bindb.cp_all():
51 self._dbs.append(bindb)
53 self._dbs.append(vardb)
56 def _spinner_update(self):
63 cp_all.update(db.cp_all())
64 return list(sorted(cp_all))
66 def _aux_get(self, *args, **kwargs):
69 return db.aux_get(*args, **kwargs)
74 def _findname(self, *args, **kwargs):
76 if db is not self._portdb:
77 # We don't want findname to return anything
78 # unless it's an ebuild in a portage tree.
79 # Otherwise, it's already built and we don't
82 func = getattr(db, "findname", None)
84 value = func(*args, **kwargs)
89 def _getFetchMap(self, *args, **kwargs):
91 func = getattr(db, "getFetchMap", None)
93 value = func(*args, **kwargs)
98 def _visible(self, db, cpv, metadata):
99 installed = db is self.vartree.dbapi
100 built = installed or db is not self._portdb
103 pkg_type = "installed"
106 return Package(type_name=pkg_type,
107 root_config=self.root_config,
108 cpv=cpv, built=built, installed=installed,
109 metadata=metadata).visible
111 def _xmatch(self, level, atom):
113 This method does not expand old-style virtuals because it
114 is restricted to returning matches for a single ${CATEGORY}/${PN}
115 and old-style virual matches unreliable for that when querying
116 multiple package databases. If necessary, old-style virtuals
117 can be performed on atoms prior to calling this method.
119 cp = portage.dep_getkey(atom)
120 if level == "match-all":
123 if hasattr(db, "xmatch"):
124 matches.update(db.xmatch(level, atom))
126 matches.update(db.match(atom))
127 result = list(x for x in matches if portage.cpv_getkey(x) == cp)
128 db._cpv_sort_ascending(result)
129 elif level == "match-visible":
132 if hasattr(db, "xmatch"):
133 matches.update(db.xmatch(level, atom))
135 db_keys = list(db._aux_cache_keys)
136 for cpv in db.match(atom):
137 metadata = zip(db_keys,
138 db.aux_get(cpv, db_keys))
139 if not self._visible(db, cpv, metadata):
142 result = list(x for x in matches if portage.cpv_getkey(x) == cp)
143 db._cpv_sort_ascending(result)
144 elif level == "bestmatch-visible":
147 if hasattr(db, "xmatch"):
148 cpv = db.xmatch("bestmatch-visible", atom)
149 if not cpv or portage.cpv_getkey(cpv) != cp:
151 if not result or cpv == portage.best([cpv, result]):
154 db_keys = list(db._aux_cache_keys)
155 # break out of this loop with highest visible
156 # match, checked in descending order
157 for cpv in reversed(db.match(atom)):
158 if portage.cpv_getkey(cpv) != cp:
160 metadata = zip(db_keys,
161 db.aux_get(cpv, db_keys))
162 if not self._visible(db, cpv, metadata):
164 if not result or cpv == portage.best([cpv, result]):
168 raise NotImplementedError(level)
171 def execute(self,searchkey):
172 """Performs the search for the supplied search key"""
174 self.searchkey=searchkey
175 self.packagematches = []
178 self.matches = {"pkg":[], "desc":[], "set":[]}
181 self.matches = {"pkg":[], "set":[]}
182 print("Searching... ", end=' ')
185 if self.searchkey.startswith('%'):
187 self.searchkey = self.searchkey[1:]
188 if self.searchkey.startswith('@'):
190 self.searchkey = self.searchkey[1:]
192 self.searchre=re.compile(self.searchkey,re.I)
194 self.searchre=re.compile(re.escape(self.searchkey), re.I)
196 for package in self._cp_all():
197 self._spinner_update()
200 match_string = package[:]
202 match_string = package.split("/")[-1]
205 if self.searchre.search(match_string):
206 if not self._xmatch("match-visible", package):
208 self.matches["pkg"].append([package,masked])
209 elif self.searchdesc: # DESCRIPTION searching
210 full_package = self._xmatch("bestmatch-visible", package)
212 #no match found; we don't want to query description
213 full_package = portage.best(
214 self._xmatch("match-all", package))
220 full_desc = self._aux_get(
221 full_package, ["DESCRIPTION"])[0]
223 print("emerge: search: aux_get() failed, skipping")
225 if self.searchre.search(full_desc):
226 self.matches["desc"].append([full_package,masked])
228 self.sdict = self.setconfig.getSets()
229 for setname in self.sdict:
230 self._spinner_update()
232 match_string = setname
234 match_string = setname.split("/")[-1]
236 if self.searchre.search(match_string):
237 self.matches["set"].append([setname, False])
238 elif self.searchdesc:
239 if self.searchre.search(
240 self.sdict[setname].getMetadata("DESCRIPTION")):
241 self.matches["set"].append([setname, False])
244 for mtype in self.matches:
245 self.matches[mtype].sort()
246 self.mlen += len(self.matches[mtype])
249 if not self._xmatch("match-all", cp):
252 if not self._xmatch("bestmatch-visible", cp):
254 self.matches["pkg"].append([cp, masked])
258 """Outputs the results of the search."""
260 msg.append("\b\b \n[ Results for search key : " + \
261 bold(self.searchkey) + " ]\n")
262 msg.append("[ Applications found : " + \
263 bold(str(self.mlen)) + " ]\n\n")
264 vardb = self.vartree.dbapi
265 metadata_keys = set(Package.metadata_keys)
266 metadata_keys.update(["DESCRIPTION", "HOMEPAGE", "LICENSE", "SRC_URI"])
267 metadata_keys = tuple(metadata_keys)
268 for mtype in self.matches:
269 for match,masked in self.matches[mtype]:
272 full_package = self._xmatch(
273 "bestmatch-visible", match)
275 #no match found; we don't want to query description
277 full_package = portage.best(
278 self._xmatch("match-all",match))
279 elif mtype == "desc":
281 match = portage.cpv_getkey(match)
283 msg.append(green("*") + " " + bold(match) + "\n")
285 msg.append(" " + darkgreen("Description:") + \
287 self.sdict[match].getMetadata("DESCRIPTION") \
291 metadata = dict(zip(metadata_keys,
292 self._aux_get(full_package, metadata_keys)))
294 msg.append("emerge: search: aux_get() failed, skipping\n")
297 desc = metadata["DESCRIPTION"]
298 homepage = metadata["HOMEPAGE"]
299 license = metadata["LICENSE"]
302 msg.append(green("*") + " " + \
303 white(match) + " " + red("[ Masked ]") + "\n")
305 msg.append(green("*") + " " + bold(match) + "\n")
306 myversion = self.getVersion(full_package, search.VERSION_RELEASE)
310 mycat = match.split("/")[0]
311 mypkg = match.split("/")[1]
312 mycpv = match + "-" + myversion
313 myebuild = self._findname(mycpv)
315 pkg = Package(built=False, cpv=mycpv,
316 installed=False, metadata=metadata,
317 root_config=self.root_config, type_name="ebuild")
318 pkgdir = os.path.dirname(myebuild)
319 mf = self.settings.repositories.get_repo_for_location(
320 os.path.dirname(os.path.dirname(pkgdir)))
321 mf = mf.load_manifest(
322 pkgdir, self.settings["DISTDIR"])
324 uri_map = _parse_uri_map(mycpv, metadata,
326 except portage.exception.InvalidDependString as e:
327 file_size_str = "Unknown (%s)" % (e,)
331 mysum[0] = mf.getDistfilesSize(uri_map)
332 except KeyError as e:
333 file_size_str = "Unknown (missing " + \
334 "digest for %s)" % (e,)
339 if db is not vardb and \
340 db.cpv_exists(mycpv):
342 if not myebuild and hasattr(db, "bintree"):
343 myebuild = db.bintree.getname(mycpv)
345 mysum[0] = os.stat(myebuild).st_size
350 if myebuild and file_size_str is None:
351 mystr = str(mysum[0] // 1024)
355 mystr = mystr[:mycount] + "," + mystr[mycount:]
356 file_size_str = mystr + " kB"
360 msg.append(" %s %s\n" % \
361 (darkgreen("Latest version available:"),
363 msg.append(" %s\n" % \
364 self.getInstallationStatus(mycat+'/'+mypkg))
366 msg.append(" %s %s\n" % \
367 (darkgreen("Size of files:"), file_size_str))
368 msg.append(" " + darkgreen("Homepage:") + \
369 " " + homepage + "\n")
370 msg.append(" " + darkgreen("Description:") \
372 msg.append(" " + darkgreen("License:") + \
373 " " + license + "\n\n")
374 writemsg_stdout(''.join(msg), noiselevel=-1)
378 def getInstallationStatus(self,package):
379 installed_package = self.vartree.dep_bestmatch(package)
381 version = self.getVersion(installed_package,search.VERSION_RELEASE)
383 result = darkgreen("Latest version installed:")+" "+version
385 result = darkgreen("Latest version installed:")+" [ Not Installed ]"
388 def getVersion(self,full_package,detail):
389 if len(full_package) > 1:
390 package_parts = portage.catpkgsplit(full_package)
391 if detail == search.VERSION_RELEASE and package_parts[3] != 'r0':
392 result = package_parts[2]+ "-" + package_parts[3]
394 result = package_parts[2]