1 # Copyright 1998-2007 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
5 __all__ = ["bindbapi", "binarytree"]
7 from portage.cache.mappings import slot_dict_class
8 from portage.dep import isvalidatom, isjustname, dep_getkey, match_from_list
9 from portage.dbapi.virtual import fakedbapi
10 from portage.exception import InvalidPackageName, \
11 PermissionDenied, PortageException
12 from portage.output import green, EOutput
13 from portage.util import ensure_dirs, normalize_path, writemsg, writemsg_stdout
14 from portage.versions import best, catpkgsplit, catsplit
15 from portage.update import update_dbentries
17 from portage import dep_expand, listdir, _check_distfile, _movefile
19 import portage.xpak, portage.getbinpkg
21 import os, errno, stat
23 from itertools import chain, izip
25 class bindbapi(fakedbapi):
26 _known_keys = frozenset(list(fakedbapi._known_keys) + \
27 ["CHOST", "repository", "USE"])
28 def __init__(self, mybintree=None, **kwargs):
29 fakedbapi.__init__(self, **kwargs)
30 self.bintree = mybintree
31 self.move_ent = mybintree.move_ent
34 # Selectively cache metadata in order to optimize dep matching.
35 self._aux_cache_keys = set(
36 ["CHOST", "DEPEND", "EAPI", "IUSE", "KEYWORDS",
37 "LICENSE", "PDEPEND", "PROPERTIES", "PROVIDE",
38 "RDEPEND", "repository", "RESTRICT", "SLOT", "USE"])
39 self._aux_cache_slot_dict = slot_dict_class(self._aux_cache_keys)
42 def match(self, *pargs, **kwargs):
43 if self.bintree and not self.bintree.populated:
44 self.bintree.populate()
45 return fakedbapi.match(self, *pargs, **kwargs)
47 def aux_get(self, mycpv, wants):
48 if self.bintree and not self.bintree.populated:
49 self.bintree.populate()
51 if not self._known_keys.intersection(
52 wants).difference(self._aux_cache_keys):
53 aux_cache = self._aux_cache.get(mycpv)
54 if aux_cache is not None:
55 return [aux_cache.get(x, "") for x in wants]
57 mysplit = mycpv.split("/")
59 tbz2name = mysplit[1]+".tbz2"
60 if not self.bintree._remotepkgs or \
61 not self.bintree.isremote(mycpv):
62 tbz2_path = self.bintree.getname(mycpv)
63 if not os.path.exists(tbz2_path):
65 getitem = portage.xpak.tbz2(tbz2_path).getfile
67 getitem = self.bintree._remotepkgs[mycpv].get
71 mykeys = self._aux_cache_keys.union(wants)
74 # myval is None if the key doesn't exist
75 # or the tbz2 is corrupt.
77 mydata[x] = " ".join(myval.split())
79 if not mydata.setdefault("EAPI", "0"):
82 aux_cache = self._aux_cache_slot_dict()
83 for x in self._aux_cache_keys:
84 aux_cache[x] = mydata.get(x, "")
85 self._aux_cache[mycpv] = aux_cache
86 return [mydata.get(x, "") for x in wants]
88 def aux_update(self, cpv, values):
89 if not self.bintree.populated:
90 self.bintree.populate()
91 tbz2path = self.bintree.getname(cpv)
92 if not os.path.exists(tbz2path):
94 mytbz2 = portage.xpak.tbz2(tbz2path)
95 mydata = mytbz2.get_data()
97 for k, v in mydata.items():
100 mytbz2.recompose_mem(portage.xpak.xpak_mem(mydata))
101 self.bintree.inject(cpv)
103 def cp_list(self, *pargs, **kwargs):
104 if not self.bintree.populated:
105 self.bintree.populate()
106 return fakedbapi.cp_list(self, *pargs, **kwargs)
109 if not self.bintree.populated:
110 self.bintree.populate()
111 return fakedbapi.cp_all(self)
114 if not self.bintree.populated:
115 self.bintree.populate()
116 return fakedbapi.cpv_all(self)
118 class binarytree(object):
119 "this tree scans for a list of all packages available in PKGDIR"
120 def __init__(self, root, pkgdir, virtual=None, settings=None, clone=None):
122 writemsg("binartree.__init__(): deprecated " + \
123 "use of clone parameter\n", noiselevel=-1)
124 # XXX This isn't cloning. It's an instance of the same thing.
125 self.root = clone.root
126 self.pkgdir = clone.pkgdir
127 self.dbapi = clone.dbapi
128 self.populated = clone.populated
129 self.tree = clone.tree
130 self.remotepkgs = clone.remotepkgs
131 self.invalids = clone.invalids
132 self.settings = clone.settings
135 #self.pkgdir=settings["PKGDIR"]
136 self.pkgdir = normalize_path(pkgdir)
137 self.dbapi = bindbapi(self, settings=settings)
138 self.update_ents = self.dbapi.update_ents
139 self.move_slot_ent = self.dbapi.move_slot_ent
142 self._remote_has_index = False
143 self._remote_base_uri = None
144 self._remotepkgs = None # remote metadata indexed by cpv
145 self.remotepkgs = {} # indexed by tbz2 name (deprecated)
147 self.settings = settings
149 self._populating = False
150 self._all_directory = os.path.isdir(
151 os.path.join(self.pkgdir, "All"))
152 self._pkgindex_version = 0
153 self._pkgindex_hashes = ["MD5","SHA1"]
154 self._pkgindex_file = os.path.join(self.pkgdir, "Packages")
155 self._pkgindex_keys = self.dbapi._aux_cache_keys.copy()
156 self._pkgindex_keys.update(["CPV", "MTIME", "SIZE"])
157 self._pkgindex_aux_keys = \
158 ["CHOST", "DEPEND", "DESCRIPTION", "EAPI",
159 "IUSE", "KEYWORDS", "LICENSE", "PDEPEND", "PROPERTIES",
160 "PROVIDE", "RDEPEND", "repository", "SLOT", "USE"]
161 self._pkgindex_aux_keys = list(self._pkgindex_aux_keys)
162 self._pkgindex_use_evaluated_keys = \
163 ("LICENSE", "RDEPEND", "DEPEND",
164 "PDEPEND", "PROPERTIES", "PROVIDE")
165 self._pkgindex_header_keys = set(["ACCEPT_KEYWORDS", "CBUILD",
166 "CHOST", "CONFIG_PROTECT", "CONFIG_PROTECT_MASK", "FEATURES",
167 "GENTOO_MIRRORS", "INSTALL_MASK", "SYNC", "USE"])
168 self._pkgindex_default_pkg_data = {
183 self._pkgindex_inherited_keys = ["CHOST", "repository"]
184 self._pkgindex_default_header_data = {
187 self._pkgindex_translated_keys = (
188 ("DESCRIPTION" , "DESC"),
189 ("repository" , "REPO"),
192 self._pkgindex_allowed_pkg_keys = set(chain(
194 self._pkgindex_aux_keys,
195 self._pkgindex_hashes,
196 self._pkgindex_default_pkg_data,
197 self._pkgindex_inherited_keys,
198 self._pkgindex_default_header_data,
199 chain(*self._pkgindex_translated_keys)
202 def move_ent(self, mylist):
203 if not self.populated:
208 for cp in [origcp, newcp]:
209 if not (isvalidatom(cp) and isjustname(cp)):
210 raise InvalidPackageName(cp)
211 origcat = origcp.split("/")[0]
212 mynewcat = newcp.split("/")[0]
213 origmatches=self.dbapi.cp_list(origcp)
217 for mycpv in origmatches:
219 mycpsplit = catpkgsplit(mycpv)
220 mynewcpv = newcp + "-" + mycpsplit[2]
221 if mycpsplit[3] != "r0":
222 mynewcpv += "-" + mycpsplit[3]
223 myoldpkg = mycpv.split("/")[1]
224 mynewpkg = mynewcpv.split("/")[1]
226 if (mynewpkg != myoldpkg) and os.path.exists(self.getname(mynewcpv)):
227 writemsg("!!! Cannot update binary: Destination exists.\n",
229 writemsg("!!! "+mycpv+" -> "+mynewcpv+"\n", noiselevel=-1)
232 tbz2path = self.getname(mycpv)
233 if os.path.exists(tbz2path) and not os.access(tbz2path,os.W_OK):
234 writemsg("!!! Cannot update readonly binary: "+mycpv+"\n",
239 mytbz2 = portage.xpak.tbz2(tbz2path)
240 mydata = mytbz2.get_data()
241 updated_items = update_dbentries([mylist], mydata)
242 mydata.update(updated_items)
243 mydata["PF"] = mynewpkg + "\n"
244 mydata["CATEGORY"] = mynewcat+"\n"
245 if mynewpkg != myoldpkg:
246 ebuild_data = mydata.get(myoldpkg+".ebuild")
247 if ebuild_data is not None:
248 mydata[mynewpkg+".ebuild"] = ebuild_data
249 del mydata[myoldpkg+".ebuild"]
250 mytbz2.recompose_mem(portage.xpak.xpak_mem(mydata))
252 self.dbapi.cpv_remove(mycpv)
253 del self._pkg_paths[mycpv]
254 new_path = self.getname(mynewcpv)
255 self._pkg_paths[mynewcpv] = os.path.join(
256 *new_path.split(os.path.sep)[-2:])
257 if new_path != mytbz2:
258 self._ensure_dir(os.path.dirname(new_path))
259 _movefile(tbz2path, new_path, mysettings=self.settings)
260 self._remove_symlink(mycpv)
261 if new_path.split(os.path.sep)[-2] == "All":
262 self._create_symlink(mynewcpv)
263 self.inject(mynewcpv)
267 def _remove_symlink(self, cpv):
268 """Remove a ${PKGDIR}/${CATEGORY}/${PF}.tbz2 symlink and also remove
269 the ${PKGDIR}/${CATEGORY} directory if empty. The file will not be
270 removed if os.path.islink() returns False."""
271 mycat, mypkg = catsplit(cpv)
272 mylink = os.path.join(self.pkgdir, mycat, mypkg + ".tbz2")
273 if os.path.islink(mylink):
274 """Only remove it if it's really a link so that this method never
275 removes a real package that was placed here to avoid a collision."""
278 os.rmdir(os.path.join(self.pkgdir, mycat))
280 if e.errno not in (errno.ENOENT,
281 errno.ENOTEMPTY, errno.EEXIST):
285 def _create_symlink(self, cpv):
286 """Create a ${PKGDIR}/${CATEGORY}/${PF}.tbz2 symlink (and
287 ${PKGDIR}/${CATEGORY} directory, if necessary). Any file that may
288 exist in the location of the symlink will first be removed."""
289 mycat, mypkg = catsplit(cpv)
290 full_path = os.path.join(self.pkgdir, mycat, mypkg + ".tbz2")
291 self._ensure_dir(os.path.dirname(full_path))
295 if e.errno != errno.ENOENT:
298 os.symlink(os.path.join("..", "All", mypkg + ".tbz2"), full_path)
300 def prevent_collision(self, cpv):
301 """Make sure that the file location ${PKGDIR}/All/${PF}.tbz2 is safe to
302 use for a given cpv. If a collision will occur with an existing
303 package from another category, the existing package will be bumped to
304 ${PKGDIR}/${CATEGORY}/${PF}.tbz2 so that both can coexist."""
305 if not self._all_directory:
308 # Copy group permissions for new directories that
309 # may have been created.
310 for path in ("All", catsplit(cpv)[0]):
311 path = os.path.join(self.pkgdir, path)
312 self._ensure_dir(path)
313 if not os.access(path, os.W_OK):
314 raise PermissionDenied("access('%s', W_OK)" % path)
316 full_path = self.getname(cpv)
317 if "All" == full_path.split(os.path.sep)[-2]:
319 """Move a colliding package if it exists. Code below this point only
320 executes in rare cases."""
321 mycat, mypkg = catsplit(cpv)
322 myfile = mypkg + ".tbz2"
323 mypath = os.path.join("All", myfile)
324 dest_path = os.path.join(self.pkgdir, mypath)
327 st = os.lstat(dest_path)
331 if stat.S_ISLNK(st.st_mode):
336 if os.path.exists(dest_path):
340 # For invalid packages, other_cat could be None.
341 other_cat = portage.xpak.tbz2(dest_path).getfile("CATEGORY")
343 other_cat = other_cat.strip()
344 other_cpv = other_cat + "/" + mypkg
345 self._move_from_all(other_cpv)
346 self.inject(other_cpv)
347 self._move_to_all(cpv)
349 def _ensure_dir(self, path):
351 Create the specified directory. Also, copy gid and group mode
352 bits from self.pkgdir if possible.
353 @param cat_dir: Absolute path of the directory to be created.
354 @type cat_dir: String
357 pkgdir_st = os.stat(self.pkgdir)
361 pkgdir_gid = pkgdir_st.st_gid
362 pkgdir_grp_mode = 02070 & pkgdir_st.st_mode
364 ensure_dirs(path, gid=pkgdir_gid, mode=pkgdir_grp_mode, mask=0)
365 except PortageException:
366 if not os.path.isdir(path):
369 def _move_to_all(self, cpv):
370 """If the file exists, move it. Whether or not it exists, update state
371 for future getname() calls."""
372 mycat, mypkg = catsplit(cpv)
373 myfile = mypkg + ".tbz2"
374 self._pkg_paths[cpv] = os.path.join("All", myfile)
375 src_path = os.path.join(self.pkgdir, mycat, myfile)
377 mystat = os.lstat(src_path)
380 if mystat and stat.S_ISREG(mystat.st_mode):
381 self._ensure_dir(os.path.join(self.pkgdir, "All"))
382 dest_path = os.path.join(self.pkgdir, "All", myfile)
383 _movefile(src_path, dest_path, mysettings=self.settings)
384 self._create_symlink(cpv)
387 def _move_from_all(self, cpv):
388 """Move a package from ${PKGDIR}/All/${PF}.tbz2 to
389 ${PKGDIR}/${CATEGORY}/${PF}.tbz2 and update state from getname calls."""
390 self._remove_symlink(cpv)
391 mycat, mypkg = catsplit(cpv)
392 myfile = mypkg + ".tbz2"
393 mypath = os.path.join(mycat, myfile)
394 dest_path = os.path.join(self.pkgdir, mypath)
395 self._ensure_dir(os.path.dirname(dest_path))
396 src_path = os.path.join(self.pkgdir, "All", myfile)
397 _movefile(src_path, dest_path, mysettings=self.settings)
398 self._pkg_paths[cpv] = mypath
400 def populate(self, getbinpkgs=0, getbinpkgsonly=0):
401 "populates the binarytree"
404 from portage.locks import lockfile, unlockfile
407 if os.access(self.pkgdir, os.W_OK):
408 pkgindex_lock = lockfile(self._pkgindex_file,
410 self._populating = True
411 self._populate(getbinpkgs, getbinpkgsonly)
414 unlockfile(pkgindex_lock)
415 self._populating = False
417 def _populate(self, getbinpkgs=0, getbinpkgsonly=0):
418 if (not os.path.isdir(self.pkgdir) and not getbinpkgs):
423 self._pkg_paths = pkg_paths
424 dirs = listdir(self.pkgdir, dirsonly=True, EmptyOnError=True)
428 dirs.insert(0, "All")
429 pkgindex = self._new_pkgindex()
432 f = open(self._pkgindex_file)
433 except EnvironmentError:
441 if not self._pkgindex_version_supported(pkgindex):
442 pkgindex = self._new_pkgindex()
443 header = pkgindex.header
445 for d in pkgindex.packages:
446 metadata[d["CPV"]] = d
447 update_pkgindex = False
449 for myfile in listdir(os.path.join(self.pkgdir, mydir)):
450 if not myfile.endswith(".tbz2"):
452 mypath = os.path.join(mydir, myfile)
453 full_path = os.path.join(self.pkgdir, mypath)
454 s = os.lstat(full_path)
455 if stat.S_ISLNK(s.st_mode):
458 # Validate data from the package index and try to avoid
459 # reading the xpak if possible.
462 d = metadata.get(mydir+"/"+myfile[:-5])
468 for mycpv in metadata:
469 mycat, mypf = catsplit(mycpv)
471 mypf, []).append(metadata[mycpv])
472 possibilities = pf_index.get(myfile[:-5])
475 for d in possibilities:
477 if long(d["MTIME"]) != long(s.st_mtime):
479 except (KeyError, ValueError):
482 if long(d["SIZE"]) != long(s.st_size):
484 except (KeyError, ValueError):
486 if not self._pkgindex_keys.difference(d):
491 if mycpv in pkg_paths:
492 # discard duplicates (All/ is preferred)
494 pkg_paths[mycpv] = mypath
495 # update the path if the package has been moved
496 oldpath = d.get("PATH")
497 if oldpath and oldpath != mypath:
498 update_pkgindex = True
499 if mypath != mycpv + ".tbz2":
502 update_pkgindex = True
506 update_pkgindex = True
507 self.dbapi.cpv_inject(mycpv)
508 if not self.dbapi._aux_cache_keys.difference(d):
509 aux_cache = self.dbapi._aux_cache_slot_dict()
510 for k in self.dbapi._aux_cache_keys:
512 self.dbapi._aux_cache[mycpv] = aux_cache
514 if not os.access(full_path, os.R_OK):
515 writemsg("!!! Permission denied to read " + \
516 "binary package: '%s'\n" % full_path,
518 self.invalids.append(myfile[:-5])
520 mytbz2 = portage.xpak.tbz2(full_path)
521 # For invalid packages, mycat could be None.
522 mycat = mytbz2.getfile("CATEGORY")
523 mypf = mytbz2.getfile("PF")
524 slot = mytbz2.getfile("SLOT")
526 if not mycat or not mypf or not slot:
527 #old-style or corrupt package
528 writemsg("\n!!! Invalid binary package: '%s'\n" % full_path,
532 missing_keys.append("CATEGORY")
534 missing_keys.append("PF")
536 missing_keys.append("SLOT")
540 msg.append("Missing metadata key(s): %s." % \
541 ", ".join(missing_keys))
542 msg.append(" This binary package is not " + \
543 "recoverable and should be deleted.")
544 from textwrap import wrap
545 for line in wrap("".join(msg), 72):
546 writemsg("!!! %s\n" % line, noiselevel=-1)
547 self.invalids.append(mypkg)
549 mycat = mycat.strip()
551 if mycat != mydir and mydir != "All":
553 if mypkg != mypf.strip():
555 mycpv = mycat + "/" + mypkg
556 if mycpv in pkg_paths:
557 # All is first, so it's preferred.
559 if not self.dbapi._category_re.match(mycat):
560 writemsg(("!!! Binary package has an " + \
561 "unrecognized category: '%s'\n") % full_path,
563 writemsg(("!!! '%s' has a category that is not" + \
564 " listed in %setc/portage/categories\n") % \
565 (mycpv, self.settings["PORTAGE_CONFIGROOT"]),
568 pkg_paths[mycpv] = mypath
569 self.dbapi.cpv_inject(mycpv)
570 update_pkgindex = True
571 d = metadata.get(mycpv, {})
574 if long(d["MTIME"]) != long(s.st_mtime):
576 except (KeyError, ValueError):
580 if long(d["SIZE"]) != long(s.st_size):
582 except (KeyError, ValueError):
587 d["MTIME"] = str(long(s.st_mtime))
588 d["SIZE"] = str(s.st_size)
590 d.update(izip(self._pkgindex_aux_keys,
591 self.dbapi.aux_get(mycpv, self._pkgindex_aux_keys)))
593 self._eval_use_flags(mycpv, d)
594 except portage.exception.InvalidDependString:
595 writemsg("!!! Invalid binary package: '%s'\n" % \
596 self.getname(mycpv), noiselevel=-1)
597 self.dbapi.cpv_remove(mycpv)
600 # record location if it's non-default
601 if mypath != mycpv + ".tbz2":
606 if not self.dbapi._aux_cache_keys.difference(d):
607 aux_cache = self.dbapi._aux_cache_slot_dict()
608 for k in self.dbapi._aux_cache_keys:
610 self.dbapi._aux_cache[mycpv] = aux_cache
612 for cpv in list(metadata):
613 if cpv not in pkg_paths:
616 # Do not bother to write the Packages index if $PKGDIR/All/ exists
617 # since it will provide no benefit due to the need to read CATEGORY
619 if update_pkgindex and os.access(self.pkgdir, os.W_OK):
620 del pkgindex.packages[:]
621 pkgindex.packages.extend(metadata.itervalues())
622 self._update_pkgindex_header(pkgindex.header)
623 from portage.util import atomic_ofstream
624 f = atomic_ofstream(self._pkgindex_file)
630 if getbinpkgs and not self.settings["PORTAGE_BINHOST"]:
631 writemsg("!!! PORTAGE_BINHOST unset, but use is requested.\n",
635 "PORTAGE_BINHOST" in self.settings and \
636 not self._remotepkgs:
638 base_url = self.settings["PORTAGE_BINHOST"]
639 from portage.const import CACHE_PATH
640 from urlparse import urlparse
641 urldata = urlparse(base_url)
642 pkgindex_file = os.path.join(CACHE_PATH, "binhost",
643 urldata[1] + urldata[2], "Packages")
644 pkgindex = self._new_pkgindex()
646 f = open(pkgindex_file)
651 except EnvironmentError, e:
652 if e.errno != errno.ENOENT:
654 local_timestamp = pkgindex.header.get("TIMESTAMP", None)
655 import urllib, urlparse
656 rmt_idx = self._new_pkgindex()
658 # urlparse.urljoin() only works correctly with recognized
659 # protocols and requires the base url to have a trailing
660 # slash, so join manually...
661 f = urllib.urlopen(base_url.rstrip("/") + "/Packages")
663 rmt_idx.readHeader(f)
664 remote_timestamp = rmt_idx.header.get("TIMESTAMP", None)
665 if not remote_timestamp:
666 # no timestamp in the header, something's wrong
669 if not self._pkgindex_version_supported(rmt_idx):
670 writemsg("\n\n!!! Binhost package index version" + \
671 " is not supported: '%s'\n" % \
672 rmt_idx.header.get("VERSION"), noiselevel=-1)
674 elif local_timestamp != remote_timestamp:
679 except EnvironmentError, e:
680 writemsg("\n\n!!! Error fetching binhost package" + \
681 " info from '%s'\n" % base_url)
682 writemsg("!!! %s\n\n" % str(e))
685 if pkgindex is rmt_idx:
686 pkgindex.modified = False # don't update the header
687 from portage.util import atomic_ofstream, ensure_dirs
688 ensure_dirs(os.path.dirname(pkgindex_file))
689 f = atomic_ofstream(pkgindex_file)
695 self._remotepkgs = {}
696 for d in pkgindex.packages:
697 self._remotepkgs[d["CPV"]] = d
698 self._remote_has_index = True
699 self._remote_base_uri = pkgindex.header.get("URI", base_url)
701 for cpv in self._remotepkgs:
702 self.dbapi.cpv_inject(cpv)
705 # Remote package instances override local package
706 # if they are not identical.
707 hash_names = ["SIZE"] + self._pkgindex_hashes
708 for cpv, local_metadata in metadata.iteritems():
709 remote_metadata = self._remotepkgs.get(cpv)
710 if remote_metadata is None:
712 # Use digests to compare identity.
714 for hash_name in hash_names:
715 local_value = local_metadata.get(hash_name)
716 if local_value is None:
718 remote_value = remote_metadata.get(hash_name)
719 if remote_value is None:
721 if local_value != remote_value:
725 del self._remotepkgs[cpv]
727 # Local package instances override remote instances.
729 self._remotepkgs.pop(cpv, None)
731 self._remotepkgs = {}
733 chunk_size = long(self.settings["PORTAGE_BINHOST_CHUNKSIZE"])
736 except (ValueError, KeyError):
738 writemsg_stdout("\n")
740 green("Fetching bininfo from ") + \
741 re.sub(r'//(.+):.+@(.+)/', r'//\1:*password*@\2/', base_url) + "\n")
742 self.remotepkgs = portage.getbinpkg.dir_get_metadata(
743 self.settings["PORTAGE_BINHOST"], chunk_size=chunk_size)
744 #writemsg(green(" -- DONE!\n\n"))
746 for mypkg in self.remotepkgs.keys():
747 if "CATEGORY" not in self.remotepkgs[mypkg]:
748 #old-style or corrupt package
749 writemsg("!!! Invalid remote binary package: "+mypkg+"\n",
751 del self.remotepkgs[mypkg]
753 mycat = self.remotepkgs[mypkg]["CATEGORY"].strip()
754 fullpkg = mycat+"/"+mypkg[:-5]
756 if fullpkg in metadata:
757 # When using this old protocol, comparison with the remote
758 # package isn't supported, so the local package is always
759 # preferred even if getbinpkgsonly is enabled.
762 if not self.dbapi._category_re.match(mycat):
763 writemsg(("!!! Remote binary package has an " + \
764 "unrecognized category: '%s'\n") % fullpkg,
766 writemsg(("!!! '%s' has a category that is not" + \
767 " listed in %setc/portage/categories\n") % \
768 (fullpkg, self.settings["PORTAGE_CONFIGROOT"]),
771 mykey = dep_getkey(fullpkg)
773 # invalid tbz2's can hurt things.
774 #print "cpv_inject("+str(fullpkg)+")"
775 self.dbapi.cpv_inject(fullpkg)
776 remote_metadata = self.remotepkgs[mypkg]
777 for k, v in remote_metadata.items():
778 remote_metadata[k] = v.strip()
779 self._remotepkgs[fullpkg] = remote_metadata
780 #print " -- Injected"
781 except SystemExit, e:
784 writemsg("!!! Failed to inject remote binary package:"+str(fullpkg)+"\n",
786 del self.remotepkgs[mypkg]
790 def inject(self, cpv, filename=None):
791 """Add a freshly built package to the database. This updates
792 $PKGDIR/Packages with the new package metadata (including MD5).
793 @param cpv: The cpv of the new package to inject
795 @param filename: File path of the package to inject, or None if it's
796 already in the location returned by getname()
797 @type filename: string
800 mycat, mypkg = catsplit(cpv)
801 if not self.populated:
804 full_path = self.getname(cpv)
808 s = os.stat(full_path)
810 if e.errno != errno.ENOENT:
813 writemsg("!!! Binary package does not exist: '%s'\n" % full_path,
816 mytbz2 = portage.xpak.tbz2(full_path)
817 slot = mytbz2.getfile("SLOT")
819 writemsg("!!! Invalid binary package: '%s'\n" % full_path,
823 self.dbapi.cpv_inject(cpv)
824 self.dbapi._aux_cache.pop(cpv, None)
826 # Reread the Packages index (in case it's been changed by another
827 # process) and then updated it, all while holding a lock.
828 from portage.locks import lockfile, unlockfile
831 pkgindex_lock = lockfile(self._pkgindex_file,
833 if filename is not None:
834 new_filename = self.getname(cpv)
835 self._ensure_dir(os.path.dirname(new_filename))
836 _movefile(filename, new_filename, mysettings=self.settings)
837 if self._all_directory and \
838 self.getname(cpv).split(os.path.sep)[-2] == "All":
839 self._create_symlink(cpv)
840 pkgindex = self._new_pkgindex()
842 f = open(self._pkgindex_file)
843 except EnvironmentError:
851 if not self._pkgindex_version_supported(pkgindex):
852 pkgindex = self._new_pkgindex()
855 d = self._pkgindex_entry(cpv)
856 except portage.exception.InvalidDependString:
857 writemsg("!!! Invalid binary package: '%s'\n" % \
858 self.getname(cpv), noiselevel=-1)
859 self.dbapi.cpv_remove(cpv)
860 del self._pkg_paths[cpv]
863 # If found, remove package(s) with duplicate path.
864 for i in xrange(len(pkgindex.packages) - 1, -1, -1):
865 d2 = pkgindex.packages[i]
868 if d2.get("PATH", "") == d.get("PATH", ""):
869 del pkgindex.packages[i]
870 pkgindex.packages.append(d)
872 self._update_pkgindex_header(pkgindex.header)
873 from portage.util import atomic_ofstream
874 f = atomic_ofstream(os.path.join(self.pkgdir, "Packages"))
881 unlockfile(pkgindex_lock)
883 if self._remotepkgs is not None:
884 # When a remote package is downloaded and injected,
885 # update state so self.isremote() returns False.
886 self._remotepkgs.pop(cpv, None)
888 def _pkgindex_entry(self, cpv):
890 Performs checksums and evaluates USE flag conditionals.
891 Raises InvalidDependString if necessary.
893 @returns: a dict containing entry for the give cpv.
896 pkg_path = self.getname(cpv)
897 from portage.checksum import perform_multiple_checksums
899 d = dict(izip(self._pkgindex_aux_keys,
900 self.dbapi.aux_get(cpv, self._pkgindex_aux_keys)))
902 d.update(perform_multiple_checksums(
903 pkg_path, hashes=self._pkgindex_hashes))
906 st = os.stat(pkg_path)
907 d["MTIME"] = str(long(st.st_mtime))
908 d["SIZE"] = str(st.st_size)
910 rel_path = self._pkg_paths[cpv]
911 # record location if it's non-default
912 if rel_path != cpv + ".tbz2":
915 self._eval_use_flags(cpv, d)
918 def _new_pkgindex(self):
919 return portage.getbinpkg.PackageIndex(
920 allowed_pkg_keys=self._pkgindex_allowed_pkg_keys,
921 default_header_data=self._pkgindex_default_header_data,
922 default_pkg_data=self._pkgindex_default_pkg_data,
923 inherited_keys=self._pkgindex_inherited_keys,
924 translated_keys=self._pkgindex_translated_keys)
926 def _update_pkgindex_header(self, header):
927 portdir = normalize_path(os.path.realpath(self.settings["PORTDIR"]))
928 profiles_base = os.path.join(portdir, "profiles") + os.path.sep
929 if self.settings.profile_path:
930 profile_path = normalize_path(
931 os.path.realpath(self.settings.profile_path))
932 if profile_path.startswith(profiles_base):
933 profile_path = profile_path[len(profiles_base):]
934 header["PROFILE"] = profile_path
935 header["VERSION"] = str(self._pkgindex_version)
936 base_uri = self.settings.get("PORTAGE_BINHOST_HEADER_URI")
938 header["URI"] = base_uri
940 header.pop("URI", None)
941 for k in self._pkgindex_header_keys:
942 v = self.settings.get(k, None)
948 def _pkgindex_version_supported(self, pkgindex):
949 version = pkgindex.header.get("VERSION")
952 if int(version) <= self._pkgindex_version:
958 def _eval_use_flags(self, cpv, metadata):
959 use = metadata["USE"].split()
961 iuse = set(f.lstrip("-+") for f in metadata["IUSE"].split())
962 use = [f for f in use if f in iuse]
964 metadata["USE"] = " ".join(use)
965 from portage.dep import paren_reduce, use_reduce, \
966 paren_normalize, paren_enclose
967 for k in self._pkgindex_use_evaluated_keys:
969 deps = paren_reduce(metadata[k])
970 deps = use_reduce(deps, uselist=raw_use)
971 deps = paren_normalize(deps)
972 deps = paren_enclose(deps)
973 except portage.exception.InvalidDependString, e:
974 writemsg("%s: %s\n" % (k, str(e)),
979 def exists_specific(self, cpv):
980 if not self.populated:
982 return self.dbapi.match(
983 dep_expand("="+cpv, mydb=self.dbapi, settings=self.settings))
985 def dep_bestmatch(self, mydep):
986 "compatibility method -- all matches, not just visible ones"
987 if not self.populated:
990 writemsg("mydep: %s\n" % mydep, 1)
991 mydep = dep_expand(mydep, mydb=self.dbapi, settings=self.settings)
992 writemsg("mydep: %s\n" % mydep, 1)
993 mykey = dep_getkey(mydep)
994 writemsg("mykey: %s\n" % mykey, 1)
995 mymatch = best(match_from_list(mydep,self.dbapi.cp_list(mykey)))
996 writemsg("mymatch: %s\n" % mymatch, 1)
1001 def getname(self, pkgname):
1002 """Returns a file location for this package. The default location is
1003 ${PKGDIR}/All/${PF}.tbz2, but will be ${PKGDIR}/${CATEGORY}/${PF}.tbz2
1004 in the rare event of a collision. The prevent_collision() method can
1005 be called to ensure that ${PKGDIR}/All/${PF}.tbz2 is available for a
1007 if not self.populated:
1010 mypath = self._pkg_paths.get(mycpv, None)
1012 return os.path.join(self.pkgdir, mypath)
1013 mycat, mypkg = catsplit(mycpv)
1014 if self._all_directory:
1015 mypath = os.path.join("All", mypkg + ".tbz2")
1016 if mypath in self._pkg_paths.values():
1017 mypath = os.path.join(mycat, mypkg + ".tbz2")
1019 mypath = os.path.join(mycat, mypkg + ".tbz2")
1020 self._pkg_paths[mycpv] = mypath # cache for future lookups
1021 return os.path.join(self.pkgdir, mypath)
1023 def isremote(self, pkgname):
1024 """Returns true if the package is kept remotely and it has not been
1025 downloaded (or it is only partially downloaded)."""
1026 if self._remotepkgs is None or pkgname not in self._remotepkgs:
1028 # Presence in self._remotepkgs implies that it's remote. When a
1029 # package is downloaded, state is updated by self.inject().
1032 def get_use(self, pkgname):
1033 writemsg("deprecated use of binarytree.get_use()," + \
1034 " use dbapi.aux_get() instead", noiselevel=-1)
1035 return self.dbapi.aux_get(pkgname, ["USE"])[0].split()
1037 def gettbz2(self, pkgname):
1038 """Fetches the package from a remote site, if necessary. Attempts to
1039 resume if the file appears to be partially downloaded."""
1040 print "Fetching '"+str(pkgname)+"'"
1041 tbz2_path = self.getname(pkgname)
1042 tbz2name = os.path.basename(tbz2_path)
1044 if os.path.exists(tbz2_path):
1045 if (tbz2name not in self.invalids):
1049 writemsg("Resuming download of this tbz2, but it is possible that it is corrupt.\n",
1052 mydest = os.path.dirname(self.getname(pkgname))
1053 self._ensure_dir(mydest)
1054 from urlparse import urlparse
1055 # urljoin doesn't work correctly with unrecognized protocols like sftp
1056 if self._remote_has_index:
1057 rel_url = self._remotepkgs[pkgname].get("PATH")
1059 rel_url = pkgname+".tbz2"
1060 url = self._remote_base_uri.rstrip("/") + "/" + rel_url.lstrip("/")
1062 url = self.settings["PORTAGE_BINHOST"].rstrip("/") + "/" + tbz2name
1063 protocol = urlparse(url)[0]
1064 fcmd_prefix = "FETCHCOMMAND"
1066 fcmd_prefix = "RESUMECOMMAND"
1067 fcmd = self.settings.get(fcmd_prefix + "_" + protocol.upper())
1069 fcmd = self.settings.get(fcmd_prefix)
1070 success = portage.getbinpkg.file_get(url, mydest, fcmd=fcmd)
1073 os.unlink(self.getname(pkgname))
1076 raise portage.exception.FileNotFound(mydest)
1077 self.inject(pkgname)
1079 def _load_pkgindex(self):
1080 pkgindex = self._new_pkgindex()
1082 f = open(self._pkgindex_file)
1083 except EnvironmentError:
1092 def digestCheck(self, pkg):
1094 Verify digests for the given package and raise DigestException
1095 if verification fails.
1097 @returns: True if digests could be located, False otherwise.
1100 if not isinstance(cpv, basestring):
1104 pkg_path = self.getname(cpv)
1106 if self._remotepkgs is None or cpv not in self._remotepkgs:
1107 for d in self._load_pkgindex().packages:
1112 metadata = self._remotepkgs[cpv]
1113 if metadata is None:
1117 from portage.checksum import hashfunc_map, verify_all
1118 for k in hashfunc_map:
1124 if "SIZE" in metadata:
1126 digests["size"] = int(metadata["SIZE"])
1128 writemsg("!!! Malformed SIZE attribute in remote " + \
1129 "metadata for '%s'\n" % cpv)
1135 eout.quiet = self.settings.get("PORTAGE_QUIET") == "1"
1136 ok, st = _check_distfile(pkg_path, digests, eout, show_errors=0)
1138 ok, reason = verify_all(pkg_path, digests)
1140 raise portage.exception.DigestException(
1141 (pkg_path,) + tuple(reason))
1145 def getslot(self, mycatpkg):
1146 "Get a slot for a catpkg; assume it exists."
1149 myslot = self.dbapi.aux_get(mycatpkg,["SLOT"])[0]
1150 except SystemExit, e:
1152 except Exception, e: