Remove fallback imports for set(), and note minimum requirement of python-2.4
[portage.git] / pym / portage / manifest.py
1 # Copyright 1999-2006 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3 # $Header: $
4
5 import errno, os
6
7 import portage.versions, portage.const
8 from portage.checksum import *
9 from portage.exception import *
10 from portage.util import write_atomic
11
12 class FileNotInManifestException(PortageException):
13         pass
14
15 def manifest2AuxfileFilter(filename):
16         filename = filename.strip(os.sep)
17         mysplit = filename.split(os.path.sep)
18         if "CVS" in mysplit:
19                 return False
20         for x in mysplit:
21                 if x.startswith("."):
22                         return False
23         return not filename.startswith("digest-")
24
25 def manifest2MiscfileFilter(filename):
26         filename = filename.strip(os.sep)
27         return not (filename in ["CVS", ".svn", "files", "Manifest"] or filename.endswith(".ebuild"))
28
29 def guessManifestFileType(filename):
30         """ Perform a best effort guess of which type the given filename is, avoid using this if possible """
31         if filename.startswith("files" + os.sep + "digest-"):
32                 return None
33         if filename.startswith("files" + os.sep):
34                 return "AUX"
35         elif filename.endswith(".ebuild"):
36                 return "EBUILD"
37         elif filename in ["ChangeLog", "metadata.xml"]:
38                 return "MISC"
39         else:
40                 return "DIST"
41
42 def parseManifest2(mysplit):
43         myentry = None
44         if len(mysplit) > 4 and mysplit[0] in portage.const.MANIFEST2_IDENTIFIERS:
45                 mytype = mysplit[0]
46                 myname = mysplit[1]
47                 mysize = int(mysplit[2])
48                 myhashes = dict(zip(mysplit[3::2], mysplit[4::2]))
49                 myhashes["size"] = mysize
50                 myentry = Manifest2Entry(type=mytype, name=myname, hashes=myhashes)
51         return myentry
52
53 def parseManifest1(mysplit):
54         myentry = None
55         if len(mysplit) == 4 and mysplit[0] in ["size"] + portage.const.MANIFEST1_HASH_FUNCTIONS:
56                 myname = mysplit[2]
57                 mytype = None
58                 mytype = guessManifestFileType(myname)
59                 if mytype == "AUX":
60                         if myname.startswith("files" + os.path.sep):
61                                 myname = myname[6:]
62                 mysize = int(mysplit[3])
63                 myhashes = {mysplit[0]: mysplit[1]}
64                 myhashes["size"] = mysize
65                 myentry = Manifest1Entry(type=mytype, name=myname, hashes=myhashes)
66         return myentry
67
68 class ManifestEntry(object):
69         __slots__ = ("type", "name", "hashes")
70         def __init__(self, **kwargs):
71                 for k, v in kwargs.iteritems():
72                         setattr(self, k, v)
73         def __cmp__(self, other):
74                 if str(self) == str(other):
75                         return 0
76                 return 1
77
78 class Manifest1Entry(ManifestEntry):
79         def __str__(self):
80                 for hashkey in self.hashes:
81                         if hashkey != "size":
82                                 break
83                 hashvalue = self.hashes[hashkey]
84                 myname = self.name
85                 if self.type == "AUX" and not myname.startswith("files" + os.sep):
86                         myname = os.path.join("files", myname)
87                 return " ".join([hashkey, str(hashvalue), myname, str(self.hashes["size"])])
88
89 class Manifest2Entry(ManifestEntry):
90         def __str__(self):
91                 myline = " ".join([self.type, self.name, str(self.hashes["size"])])
92                 myhashkeys = self.hashes.keys()
93                 myhashkeys.remove("size")
94                 myhashkeys.sort()
95                 for h in myhashkeys:
96                         myline += " " + h + " " + str(self.hashes[h])
97                 return myline
98
99 class Manifest(object):
100         parsers = (parseManifest2, parseManifest1)
101         def __init__(self, pkgdir, distdir, fetchlist_dict=None,
102                 manifest1_compat=True, from_scratch=False):
103                 """ create new Manifest instance for package in pkgdir
104                     and add compability entries for old portage versions if manifest1_compat == True.
105                     Do not parse Manifest file if from_scratch == True (only for internal use)
106                         The fetchlist_dict parameter is required only for generation of
107                         a Manifest (not needed for parsing and checking sums)."""
108                 self.pkgdir = pkgdir.rstrip(os.sep) + os.sep
109                 self.fhashdict = {}
110                 self.hashes = portage.const.MANIFEST2_HASH_FUNCTIONS[:]
111                 self.hashes.append("size")
112                 if manifest1_compat:
113                         self.hashes.extend(portage.const.MANIFEST1_HASH_FUNCTIONS)
114                 self.hashes = set(self.hashes)
115                 for t in portage.const.MANIFEST2_IDENTIFIERS:
116                         self.fhashdict[t] = {}
117                 self.compat = manifest1_compat
118                 if not from_scratch:
119                         self._read()
120                 if fetchlist_dict != None:
121                         self.fetchlist_dict = fetchlist_dict
122                 else:
123                         self.fetchlist_dict = {}
124                 self.distdir = distdir
125                 self.guessType = guessManifestFileType
126
127         def getFullname(self):
128                 """ Returns the absolute path to the Manifest file for this instance """
129                 return os.path.join(self.pkgdir, "Manifest")
130         
131         def getDigests(self):
132                 """ Compability function for old digest/manifest code, returns dict of filename:{hashfunction:hashvalue} """
133                 rval = {}
134                 for t in portage.const.MANIFEST2_IDENTIFIERS:
135                         rval.update(self.fhashdict[t])
136                 return rval
137         
138         def getTypeDigests(self, ftype):
139                 """ Similar to getDigests(), but restricted to files of the given type. """
140                 return self.fhashdict[ftype]
141
142         def _readDigests(self, myhashdict=None):
143                 """ Parse old style digest files for this Manifest instance """
144                 if myhashdict is None:
145                         myhashdict = {}
146                 try:
147                         for d in os.listdir(os.path.join(self.pkgdir, "files")):
148                                 if d.startswith("digest-"):
149                                         self._readManifest(os.path.join(self.pkgdir, "files", d), mytype="DIST",
150                                                 myhashdict=myhashdict)
151                 except (IOError, OSError), e:
152                         if e.errno == errno.ENOENT:
153                                 pass
154                         else:
155                                 raise
156                 return myhashdict
157
158         def _readManifest(self, file_path, myhashdict=None, **kwargs):
159                 """Parse a manifest or an old style digest.  If myhashdict is given
160                 then data will be added too it.  Otherwise, a new dict will be created
161                 and returned."""
162                 try:
163                         fd = open(file_path, "r")
164                         if myhashdict is None:
165                                 myhashdict = {}
166                         self._parseDigests(fd, myhashdict=myhashdict, **kwargs)
167                         fd.close()
168                         return myhashdict
169                 except (OSError, IOError), e:
170                         if e.errno == errno.ENOENT:
171                                 raise FileNotFound(file_path)
172                         else:
173                                 raise
174
175         def _read(self):
176                 """ Parse Manifest file for this instance """
177                 try:
178                         self._readManifest(self.getFullname(), myhashdict=self.fhashdict)
179                 except FileNotFound:
180                         pass
181                 if self.compat:
182                         self._readDigests(myhashdict=self.fhashdict)
183
184         def _parseManifestLines(self, mylines):
185                 """Parse manifest lines and return a list of manifest entries."""
186                 for myline in mylines:
187                         myentry = None
188                         mysplit = myline.split()
189                         for parser in self.parsers:
190                                 myentry = parser(mysplit)
191                                 if myentry is not None:
192                                         yield myentry
193                                         break # go to the next line
194
195         def _parseDigests(self, mylines, myhashdict=None, mytype=None):
196                 """Parse manifest entries and store the data in myhashdict.  If mytype
197                 is specified, it will override the type for all parsed entries."""
198                 if myhashdict is None:
199                         myhashdict = {}
200                 for myentry in self._parseManifestLines(mylines):
201                         if mytype is None:
202                                 myentry_type = myentry.type
203                         else:
204                                 myentry_type = mytype
205                         myhashdict.setdefault(myentry_type, {})
206                         myhashdict[myentry_type].setdefault(myentry.name, {})
207                         myhashdict[myentry_type][myentry.name].update(myentry.hashes)
208                 return myhashdict
209
210         def _writeDigests(self, force=False):
211                 """ Create old style digest files for this Manifest instance """
212                 cpvlist = [os.path.join(self._pkgdir_category(), x[:-7]) for x in os.listdir(self.pkgdir) if x.endswith(".ebuild")]
213                 rval = []
214                 try:
215                         os.makedirs(os.path.join(self.pkgdir, "files"))
216                 except OSError, oe:
217                         if oe.errno == errno.EEXIST:
218                                 pass
219                         else:
220                                 raise
221                 for cpv in cpvlist:
222                         dname = os.path.join(self.pkgdir, "files", "digest-%s" % self._catsplit(cpv)[1])
223                         distlist = self._getCpvDistfiles(cpv)
224                         missing_digests = set()
225                         for f in distlist:
226                                 if f not in self.fhashdict["DIST"] or len(self.fhashdict["DIST"][f]) == 0:
227                                         missing_digests.add(f)
228                         if missing_digests:
229                                 # This allows us to force remove of stale digests for the
230                                 # ebuild --force digest option.
231                                 distlist = [f for f in distlist if f not in missing_digests]
232                         update_digest = True
233                         if not force:
234                                 try:
235                                         f = open(dname, "r")
236                                         old_data = self._parseDigests(f)
237                                         f.close()
238                                         new_data = self._getDigestData(distlist)
239                                         if not old_data and not new_data:
240                                                 # SRC_URI is empty
241                                                 update_digest = False
242                                         elif len(old_data) == 1 and "DIST" in old_data:
243                                                 if "DIST" in new_data:
244                                                         for myfile in new_data["DIST"]:
245                                                                 for hashname in \
246                                                                         new_data["DIST"][myfile].keys():
247                                                                         if hashname != "size" and hashname not in \
248                                                                                 portage.const.MANIFEST1_HASH_FUNCTIONS:
249                                                                                 del new_data["DIST"][myfile][hashname]
250                                                         if new_data["DIST"] == old_data["DIST"]:
251                                                                 update_digest = False
252                                 except (IOError, OSError), e:
253                                         if errno.ENOENT == e.errno:
254                                                 pass
255                                         else:
256                                                 raise
257                         if update_digest:
258                                 mylines = self._createDigestLines1(distlist, self.fhashdict)
259                                 if mylines:
260                                         mylines = "\n".join(mylines) + "\n"
261                                 else:
262                                         mylines = ""
263                                 write_atomic(dname, mylines)
264                         rval.append(dname)
265                 return rval
266
267         def _getDigestData(self, distlist):
268                 """create a hash dict for a specific list of files"""
269                 myhashdict = {}
270                 for myname in distlist:
271                         for mytype in self.fhashdict:
272                                 if myname in self.fhashdict[mytype]:
273                                         myhashdict.setdefault(mytype, {})
274                                         myhashdict[mytype].setdefault(myname, {})
275                                         myhashdict[mytype][myname].update(self.fhashdict[mytype][myname])
276                 return myhashdict
277
278         def _createDigestLines1(self, distlist, myhashdict):
279                 """ Create an old style digest file."""
280                 mylines = []
281                 myfiles = myhashdict["DIST"].keys()
282                 myfiles.sort()
283                 for f in myfiles:
284                         if f in distlist:
285                                 myhashkeys = myhashdict["DIST"][f].keys()
286                                 myhashkeys.sort()
287                                 for h in myhashkeys:
288                                         if h not in portage.const.MANIFEST1_HASH_FUNCTIONS:
289                                                 continue
290                                         myline = " ".join([h, str(myhashdict["DIST"][f][h]), f, str(myhashdict["DIST"][f]["size"])])
291                                         mylines.append(myline)
292                 return mylines
293         
294         def _addDigestsToManifest(self, digests, fd):
295                 """ Add entries for old style digest files to Manifest file """
296                 mylines = []
297                 for dname in digests:
298                         myhashes = perform_multiple_checksums(dname, portage.const.MANIFEST1_HASH_FUNCTIONS+["size"])
299                         for h in myhashes:
300                                 mylines.append((" ".join([h, str(myhashes[h]), os.path.join("files", os.path.basename(dname)), str(myhashes["size"])])))
301                 fd.write("\n".join(mylines))
302                 fd.write("\n")
303
304         def _createManifestEntries(self):
305                 mytypes = self.fhashdict.keys()
306                 mytypes.sort()
307                 for t in mytypes:
308                         myfiles = self.fhashdict[t].keys()
309                         myfiles.sort()
310                         for f in myfiles:
311                                 myentry = Manifest2Entry(
312                                         type=t, name=f, hashes=self.fhashdict[t][f].copy())
313                                 myhashkeys = myentry.hashes.keys()
314                                 for h in myhashkeys:
315                                         if h not in ["size"] + portage.const.MANIFEST2_HASH_FUNCTIONS:
316                                                 del myentry.hashes[h]
317                                 yield myentry
318                                 if self.compat and t != "DIST":
319                                         mysize = self.fhashdict[t][f]["size"]
320                                         myhashes = self.fhashdict[t][f]
321                                         for h in myhashkeys:
322                                                 if h not in portage.const.MANIFEST1_HASH_FUNCTIONS:
323                                                         continue
324                                                 yield Manifest1Entry(
325                                                         type=t, name=f, hashes={"size":mysize, h:myhashes[h]})
326
327                 if self.compat:
328                         cvp_list = self.fetchlist_dict.keys()
329                         cvp_list.sort()
330                         for cpv in cvp_list:
331                                 digest_path = os.path.join("files", "digest-%s" % self._catsplit(cpv)[1])
332                                 dname = os.path.join(self.pkgdir, digest_path)
333                                 try:
334                                         myhashes = perform_multiple_checksums(dname, portage.const.MANIFEST1_HASH_FUNCTIONS+["size"])
335                                         myhashkeys = myhashes.keys()
336                                         myhashkeys.sort()
337                                         for h in myhashkeys:
338                                                 if h in portage.const.MANIFEST1_HASH_FUNCTIONS:
339                                                         yield Manifest1Entry(type="AUX", name=digest_path,
340                                                                 hashes={"size":myhashes["size"], h:myhashes[h]})
341                                 except FileNotFound:
342                                         pass
343
344         def checkIntegrity(self):
345                 for t in self.fhashdict:
346                         for f in self.fhashdict[t]:
347                                 if portage.const.MANIFEST2_REQUIRED_HASH not in self.fhashdict[t][f]:
348                                         raise MissingParameter("Missing %s checksum: %s %s" % (portage.const.MANIFEST2_REQUIRED_HASH, t, f))
349
350         def write(self, sign=False, force=False):
351                 """ Write Manifest instance to disk, optionally signing it """
352                 self.checkIntegrity()
353                 try:
354                         if self.compat:
355                                 self._writeDigests()
356                         myentries = list(self._createManifestEntries())
357                         update_manifest = True
358                         if not force:
359                                 try:
360                                         f = open(self.getFullname(), "r")
361                                         oldentries = list(self._parseManifestLines(f))
362                                         f.close()
363                                         if len(oldentries) == len(myentries):
364                                                 update_manifest = False
365                                                 for i in xrange(len(oldentries)):
366                                                         if oldentries[i] != myentries[i]:
367                                                                 update_manifest = True
368                                                                 break
369                                 except (IOError, OSError), e:
370                                         if e.errno == errno.ENOENT:
371                                                 pass
372                                         else:
373                                                 raise
374                         if update_manifest:
375                                 fd = open(self.getFullname(), "w")
376                                 for myentry in myentries:
377                                         fd.write("%s\n" % str(myentry))
378                                 fd.close()
379                         if sign:
380                                 self.sign()
381                 except (IOError, OSError), e:
382                         if e.errno == errno.EACCES:
383                                 raise PermissionDenied(str(e))
384                         raise
385
386         def sign(self):
387                 """ Sign the Manifest """
388                 raise NotImplementedError()
389         
390         def validateSignature(self):
391                 """ Validate signature on Manifest """
392                 raise NotImplementedError()
393         
394         def addFile(self, ftype, fname, hashdict=None, ignoreMissing=False):
395                 """ Add entry to Manifest optionally using hashdict to avoid recalculation of hashes """
396                 if ftype == "AUX" and not fname.startswith("files/"):
397                         fname = os.path.join("files", fname)
398                 if not os.path.exists(self.pkgdir+fname) and not ignoreMissing:
399                         raise FileNotFound(fname)
400                 if not ftype in portage.const.MANIFEST2_IDENTIFIERS:
401                         raise InvalidDataType(ftype)
402                 if ftype == "AUX" and fname.startswith("files"):
403                         fname = fname[6:]
404                 self.fhashdict[ftype][fname] = {}
405                 if hashdict != None:
406                         self.fhashdict[ftype][fname].update(hashdict)
407                 if not portage.const.MANIFEST2_REQUIRED_HASH in self.fhashdict[ftype][fname]:
408                         self.updateFileHashes(ftype, fname, checkExisting=False, ignoreMissing=ignoreMissing)
409         
410         def removeFile(self, ftype, fname):
411                 """ Remove given entry from Manifest """
412                 del self.fhashdict[ftype][fname]
413         
414         def hasFile(self, ftype, fname):
415                 """ Return wether the Manifest contains an entry for the given type,filename pair """
416                 return (fname in self.fhashdict[ftype])
417         
418         def findFile(self, fname):
419                 """ Return entrytype of the given file if present in Manifest or None if not present """
420                 for t in portage.const.MANIFEST2_IDENTIFIERS:
421                         if fname in self.fhashdict[t]:
422                                 return t
423                 return None
424         
425         def create(self, checkExisting=False, assumeDistHashesSometimes=False,
426                 assumeDistHashesAlways=False, requiredDistfiles=[]):
427                 """ Recreate this Manifest from scratch.  This will not use any
428                 existing checksums unless assumeDistHashesSometimes or
429                 assumeDistHashesAlways is true (assumeDistHashesSometimes will only
430                 cause DIST checksums to be reused if the file doesn't exist in
431                 DISTDIR).  The requiredDistfiles parameter specifies a list of
432                 distfiles to raise a FileNotFound exception for (if no file or existing
433                 checksums are available), and defaults to all distfiles when not
434                 specified."""
435                 if checkExisting:
436                         self.checkAllHashes()
437                 if assumeDistHashesSometimes or assumeDistHashesAlways:
438                         distfilehashes = self.fhashdict["DIST"]
439                 else:
440                         distfilehashes = {}
441                 self.__init__(self.pkgdir, self.distdir,
442                         fetchlist_dict=self.fetchlist_dict, from_scratch=True,
443                         manifest1_compat=self.compat)
444                 for pkgdir, pkgdir_dirs, pkgdir_files in os.walk(self.pkgdir):
445                         break
446                 for f in pkgdir_files:
447                         if f.endswith(".ebuild"):
448                                 mytype = "EBUILD"
449                         elif manifest2MiscfileFilter(f):
450                                 mytype = "MISC"
451                         else:
452                                 continue
453                         self.fhashdict[mytype][f] = perform_multiple_checksums(self.pkgdir+f, self.hashes)
454                 recursive_files = []
455                 cut_len = len(os.path.join(self.pkgdir, "files") + os.sep)
456                 for parentdir, dirs, files in os.walk(os.path.join(self.pkgdir, "files")):
457                         for f in files:
458                                 full_path = os.path.join(parentdir, f)
459                                 recursive_files.append(full_path[cut_len:])
460                 for f in recursive_files:
461                         if not manifest2AuxfileFilter(f):
462                                 continue
463                         self.fhashdict["AUX"][f] = perform_multiple_checksums(
464                                 os.path.join(self.pkgdir, "files", f.lstrip(os.sep)), self.hashes)
465                 cpvlist = [os.path.join(self._pkgdir_category(), x[:-7]) for x in os.listdir(self.pkgdir) if x.endswith(".ebuild")]
466                 distlist = set()
467                 for cpv in cpvlist:
468                         distlist.update(self._getCpvDistfiles(cpv))
469                 if requiredDistfiles is None:
470                         # This allows us to force removal of stale digests for the
471                         # ebuild --force digest option (no distfiles are required).
472                         requiredDistfiles = set()
473                 elif len(requiredDistfiles) == 0:
474                         # repoman passes in an empty list, which implies that all distfiles
475                         # are required.
476                         requiredDistfiles = distlist.copy()
477                 required_hash_types = set()
478                 required_hash_types.add("size")
479                 required_hash_types.add(portage.const.MANIFEST2_REQUIRED_HASH)
480                 for f in distlist:
481                         fname = os.path.join(self.distdir, f)
482                         mystat = None
483                         try:
484                                 mystat = os.stat(fname)
485                         except OSError:
486                                 pass
487                         if f in distfilehashes and \
488                                 not required_hash_types.difference(distfilehashes[f]) and \
489                                 ((assumeDistHashesSometimes and mystat is None) or \
490                                 (assumeDistHashesAlways and mystat is None) or \
491                                 (assumeDistHashesAlways and mystat is not None and \
492                                 len(distfilehashes[f]) == len(self.hashes) and \
493                                 distfilehashes[f]["size"] == mystat.st_size)):
494                                 self.fhashdict["DIST"][f] = distfilehashes[f]
495                         else:
496                                 try:
497                                         self.fhashdict["DIST"][f] = perform_multiple_checksums(fname, self.hashes)
498                                 except FileNotFound:
499                                         if f in requiredDistfiles:
500                                                 raise
501
502         def _pkgdir_category(self):
503                 return self.pkgdir.rstrip(os.sep).split(os.sep)[-2]
504
505         def _getAbsname(self, ftype, fname):
506                 if ftype == "DIST":
507                         absname = os.path.join(self.distdir, fname)
508                 elif ftype == "AUX":
509                         absname = os.path.join(self.pkgdir, "files", fname)
510                 else:
511                         absname = os.path.join(self.pkgdir, fname)
512                 return absname  
513         
514         def checkAllHashes(self, ignoreMissingFiles=False):
515                 for t in portage.const.MANIFEST2_IDENTIFIERS:
516                         self.checkTypeHashes(t, ignoreMissingFiles=ignoreMissingFiles)
517         
518         def checkTypeHashes(self, idtype, ignoreMissingFiles=False):
519                 for f in self.fhashdict[idtype]:
520                         self.checkFileHashes(idtype, f, ignoreMissing=ignoreMissingFiles)
521         
522         def checkFileHashes(self, ftype, fname, ignoreMissing=False):
523                 myhashes = self.fhashdict[ftype][fname]
524                 try:
525                         ok,reason = verify_all(self._getAbsname(ftype, fname), self.fhashdict[ftype][fname])
526                         if not ok:
527                                 raise DigestException(tuple([self._getAbsname(ftype, fname)]+list(reason)))
528                         return ok, reason
529                 except FileNotFound, e:
530                         if not ignoreMissing:
531                                 raise
532                         return False, "File Not Found: '%s'" % str(e)
533
534         def checkCpvHashes(self, cpv, checkDistfiles=True, onlyDistfiles=False, checkMiscfiles=False):
535                 """ check the hashes for all files associated to the given cpv, include all
536                 AUX files and optionally all MISC files. """
537                 if not onlyDistfiles:
538                         self.checkTypeHashes("AUX", ignoreMissingFiles=False)
539                         if checkMiscfiles:
540                                 self.checkTypeHashes("MISC", ignoreMissingFiles=False)
541                         ebuildname = "%s.ebuild" % self._catsplit(cpv)[1]
542                         self.checkFileHashes("EBUILD", ebuildname, ignoreMissing=False)
543                 if checkDistfiles or onlyDistfiles:
544                         for f in self._getCpvDistfiles(cpv):
545                                 self.checkFileHashes("DIST", f, ignoreMissing=False)
546         
547         def _getCpvDistfiles(self, cpv):
548                 """ Get a list of all DIST files associated to the given cpv """
549                 return self.fetchlist_dict[cpv]
550
551         def getDistfilesSize(self, fetchlist):
552                 total_bytes = 0
553                 for f in fetchlist:
554                         total_bytes += int(self.fhashdict["DIST"][f]["size"])
555                 return total_bytes
556
557         def updateFileHashes(self, ftype, fname, checkExisting=True, ignoreMissing=True, reuseExisting=False):
558                 """ Regenerate hashes for the given file """
559                 if checkExisting:
560                         self.checkFileHashes(ftype, fname, ignoreMissing=ignoreMissing)
561                 if not ignoreMissing and not self.fhashdict[ftype].has_key(fname):
562                         raise FileNotInManifestException(fname)
563                 if not self.fhashdict[ftype].has_key(fname):
564                         self.fhashdict[ftype][fname] = {}
565                 myhashkeys = list(self.hashes)
566                 if reuseExisting:
567                         for k in [h for h in self.fhashdict[ftype][fname] if h in myhashkeys]:
568                                 myhashkeys.remove(k)
569                 myhashes = perform_multiple_checksums(self._getAbsname(ftype, fname), myhashkeys)
570                 self.fhashdict[ftype][fname].update(myhashes)
571         
572         def updateTypeHashes(self, idtype, checkExisting=False, ignoreMissingFiles=True):
573                 """ Regenerate all hashes for all files of the given type """
574                 for fname in self.fhashdict[idtype]:
575                         self.updateFileHashes(idtype, fname, checkExisting)
576         
577         def updateAllHashes(self, checkExisting=False, ignoreMissingFiles=True):
578                 """ Regenerate all hashes for all files in this Manifest. """
579                 for ftype in portage.const.MANIFEST2_IDENTIFIERS:
580                         self.updateTypeHashes(idtype, fname, checkExisting)
581
582         def updateCpvHashes(self, cpv, ignoreMissingFiles=True):
583                 """ Regenerate all hashes associated to the given cpv (includes all AUX and MISC
584                 files)."""
585                 self.updateTypeHashes("AUX", ignoreMissingFiles=ignoreMissingFiles)
586                 self.updateTypeHashes("MISC", ignoreMissingFiles=ignoreMissingFiles)
587                 ebuildname = "%s.ebuild" % self._catsplit(cpv)[1]
588                 self.updateFileHashes("EBUILD", ebuildname, ignoreMissingFiles=ignoreMissingFiles)
589                 for f in self._getCpvDistfiles(cpv):
590                         self.updateFileHashes("DIST", f, ignoreMissingFiles=ignoreMissingFiles)
591
592         def updateHashesGuessType(self, fname, *args, **kwargs):
593                 """ Regenerate hashes for the given file (guesses the type and then
594                 calls updateFileHashes)."""
595                 mytype = self.guessType(fname)
596                 if mytype == "AUX":
597                         fname = fname[len("files" + os.sep):]
598                 elif mytype is None:
599                         return
600                 myrealtype = self.findFile(fname)
601                 if myrealtype is not None:
602                         mytype = myrealtype
603                 return self.updateFileHashes(mytype, fname, *args, **kwargs)
604
605         def getFileData(self, ftype, fname, key):
606                 """ Return the value of a specific (type,filename,key) triple, mainly useful
607                 to get the size for distfiles."""
608                 return self.fhashdict[ftype][fname][key]
609
610         def getVersions(self):
611                 """ Returns a list of manifest versions present in the manifest file. """
612                 rVal = []
613                 mfname = self.getFullname()
614                 if not os.path.exists(mfname):
615                         return rVal
616                 myfile = open(mfname, "r")
617                 lines = myfile.readlines()
618                 myfile.close()
619                 for l in lines:
620                         mysplit = l.split()
621                         if len(mysplit) == 4 and mysplit[0] in portage.const.MANIFEST1_HASH_FUNCTIONS and not 1 in rVal:
622                                 rVal.append(1)
623                         elif len(mysplit) > 4 and mysplit[0] in portage.const.MANIFEST2_IDENTIFIERS and ((len(mysplit) - 3) % 2) == 0 and not 2 in rVal:
624                                 rVal.append(2)
625                 return rVal
626
627         def _catsplit(self, pkg_key):
628                 """Split a category and package, returning a list of [cat, pkg].
629                 This is compatible with portage.catsplit()"""
630                 return pkg_key.split("/", 1)