Package portage :: Package package :: Package ebuild :: Module digestgen
[hide private]

Source Code for Module portage.package.ebuild.digestgen

  1  # Copyright 2010-2011 Gentoo Foundation 
  2  # Distributed under the terms of the GNU General Public License v2 
  3   
  4  __all__ = ['digestgen'] 
  5   
  6  import errno 
  7   
  8  import portage 
  9  portage.proxy.lazyimport.lazyimport(globals(), 
 10          'portage.package.ebuild._spawn_nofetch:spawn_nofetch', 
 11  ) 
 12   
 13  from portage import os 
 14  from portage.const import MANIFEST2_REQUIRED_HASH 
 15  from portage.dbapi.porttree import FetchlistDict 
 16  from portage.dep import use_reduce 
 17  from portage.exception import InvalidDependString, FileNotFound, \ 
 18          PermissionDenied, PortagePackageException 
 19  from portage.localization import _ 
 20  from portage.output import colorize 
 21  from portage.package.ebuild.fetch import fetch 
 22  from portage.util import writemsg, writemsg_stdout 
 23  from portage.versions import catsplit 
 24   
25 -def digestgen(myarchives=None, mysettings=None, myportdb=None):
26 """ 27 Generates a digest file if missing. Fetches files if necessary. 28 NOTE: myarchives and mysettings used to be positional arguments, 29 so their order must be preserved for backward compatibility. 30 @param mysettings: the ebuild config (mysettings["O"] must correspond 31 to the ebuild's parent directory) 32 @type mysettings: config 33 @param myportdb: a portdbapi instance 34 @type myportdb: portdbapi 35 @rtype: int 36 @return: 1 on success and 0 on failure 37 """ 38 if mysettings is None or myportdb is None: 39 raise TypeError("portage.digestgen(): 'mysettings' and 'myportdb' parameter are required.") 40 41 try: 42 portage._doebuild_manifest_exempt_depend += 1 43 distfiles_map = {} 44 fetchlist_dict = FetchlistDict(mysettings["O"], mysettings, myportdb) 45 for cpv in fetchlist_dict: 46 try: 47 for myfile in fetchlist_dict[cpv]: 48 distfiles_map.setdefault(myfile, []).append(cpv) 49 except InvalidDependString as e: 50 writemsg("!!! %s\n" % str(e), noiselevel=-1) 51 del e 52 return 0 53 mytree = os.path.dirname(os.path.dirname(mysettings["O"])) 54 try: 55 mf = mysettings.repositories.get_repo_for_location(mytree) 56 except KeyError: 57 # backward compatibility 58 mytree = os.path.realpath(mytree) 59 mf = mysettings.repositories.get_repo_for_location(mytree) 60 61 mf = mf.load_manifest(mysettings["O"], mysettings["DISTDIR"], 62 fetchlist_dict=fetchlist_dict) 63 64 if not mf.allow_create: 65 writemsg_stdout(_(">>> Skipping creating Manifest for %s; " 66 "repository is configured to not use them\n") % mysettings["O"]) 67 return 1 68 69 # Don't require all hashes since that can trigger excessive 70 # fetches when sufficient digests already exist. To ease transition 71 # while Manifest 1 is being removed, only require hashes that will 72 # exist before and after the transition. 73 required_hash_types = set() 74 required_hash_types.add("size") 75 required_hash_types.add(MANIFEST2_REQUIRED_HASH) 76 dist_hashes = mf.fhashdict.get("DIST", {}) 77 78 # To avoid accidental regeneration of digests with the incorrect 79 # files (such as partially downloaded files), trigger the fetch 80 # code if the file exists and it's size doesn't match the current 81 # manifest entry. If there really is a legitimate reason for the 82 # digest to change, `ebuild --force digest` can be used to avoid 83 # triggering this code (or else the old digests can be manually 84 # removed from the Manifest). 85 missing_files = [] 86 for myfile in distfiles_map: 87 myhashes = dist_hashes.get(myfile) 88 if not myhashes: 89 try: 90 st = os.stat(os.path.join(mysettings["DISTDIR"], myfile)) 91 except OSError: 92 st = None 93 if st is None or st.st_size == 0: 94 missing_files.append(myfile) 95 continue 96 size = myhashes.get("size") 97 98 try: 99 st = os.stat(os.path.join(mysettings["DISTDIR"], myfile)) 100 except OSError as e: 101 if e.errno != errno.ENOENT: 102 raise 103 del e 104 if size == 0: 105 missing_files.append(myfile) 106 continue 107 if required_hash_types.difference(myhashes): 108 missing_files.append(myfile) 109 continue 110 else: 111 if st.st_size == 0 or size is not None and size != st.st_size: 112 missing_files.append(myfile) 113 continue 114 115 for myfile in missing_files: 116 uris = set() 117 all_restrict = set() 118 for cpv in distfiles_map[myfile]: 119 uris.update(myportdb.getFetchMap( 120 cpv, mytree=mytree)[myfile]) 121 restrict = myportdb.aux_get(cpv, ['RESTRICT'], mytree=mytree)[0] 122 # Here we ignore conditional parts of RESTRICT since 123 # they don't apply unconditionally. Assume such 124 # conditionals only apply on the client side where 125 # digestgen() does not need to be called. 126 all_restrict.update(use_reduce(restrict, 127 flat=True, matchnone=True)) 128 129 # fetch() uses CATEGORY and PF to display a message 130 # when fetch restriction is triggered. 131 cat, pf = catsplit(cpv) 132 mysettings["CATEGORY"] = cat 133 mysettings["PF"] = pf 134 135 # fetch() uses PORTAGE_RESTRICT to control fetch 136 # restriction, which is only applied to files that 137 # are not fetchable via a mirror:// URI. 138 mysettings["PORTAGE_RESTRICT"] = " ".join(all_restrict) 139 140 try: 141 st = os.stat(os.path.join(mysettings["DISTDIR"], myfile)) 142 except OSError: 143 st = None 144 145 if not fetch({myfile : uris}, mysettings): 146 myebuild = os.path.join(mysettings["O"], 147 catsplit(cpv)[1] + ".ebuild") 148 spawn_nofetch(myportdb, myebuild) 149 writemsg(_("!!! Fetch failed for %s, can't update Manifest\n") 150 % myfile, noiselevel=-1) 151 if myfile in dist_hashes and \ 152 st is not None and st.st_size > 0: 153 # stat result is obtained before calling fetch(), 154 # since fetch may rename the existing file if the 155 # digest does not match. 156 cmd = colorize("INFORM", "ebuild --force %s manifest" % 157 os.path.basename(myebuild)) 158 writemsg((_( 159 "!!! If you would like to forcefully replace the existing Manifest entry\n" 160 "!!! for %s, use the following command:\n") % myfile) + 161 "!!! %s\n" % cmd, 162 noiselevel=-1) 163 return 0 164 165 writemsg_stdout(_(">>> Creating Manifest for %s\n") % mysettings["O"]) 166 try: 167 mf.create(assumeDistHashesSometimes=True, 168 assumeDistHashesAlways=( 169 "assume-digests" in mysettings.features)) 170 except FileNotFound as e: 171 writemsg(_("!!! File %s doesn't exist, can't update Manifest\n") 172 % e, noiselevel=-1) 173 return 0 174 except PortagePackageException as e: 175 writemsg(("!!! %s\n") % (e,), noiselevel=-1) 176 return 0 177 try: 178 mf.write(sign=False) 179 except PermissionDenied as e: 180 writemsg(_("!!! Permission Denied: %s\n") % (e,), noiselevel=-1) 181 return 0 182 if "assume-digests" not in mysettings.features: 183 distlist = list(mf.fhashdict.get("DIST", {})) 184 distlist.sort() 185 auto_assumed = [] 186 for filename in distlist: 187 if not os.path.exists( 188 os.path.join(mysettings["DISTDIR"], filename)): 189 auto_assumed.append(filename) 190 if auto_assumed: 191 cp = os.path.sep.join(mysettings["O"].split(os.path.sep)[-2:]) 192 pkgs = myportdb.cp_list(cp, mytree=mytree) 193 pkgs.sort() 194 writemsg_stdout(" digest.assumed" + colorize("WARN", 195 str(len(auto_assumed)).rjust(18)) + "\n") 196 for pkg_key in pkgs: 197 fetchlist = myportdb.getFetchMap(pkg_key, mytree=mytree) 198 pv = pkg_key.split("/")[1] 199 for filename in auto_assumed: 200 if filename in fetchlist: 201 writemsg_stdout( 202 " %s::%s\n" % (pv, filename)) 203 return 1 204 finally: 205 portage._doebuild_manifest_exempt_depend -= 1
206