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