1""" 2BitBake 'Fetch' implementations 3 4Classes for obtaining upstream sources for the 5BitBake build tools. 6""" 7 8# Copyright (C) 2003, 2004 Chris Larson 9# Copyright (C) 2012 Intel Corporation 10# 11# SPDX-License-Identifier: GPL-2.0-only 12# 13# Based on functions from the base bb module, Copyright 2003 Holger Schurig 14 15import os, re 16import signal 17import logging 18import urllib.request, urllib.parse, urllib.error 19if 'git' not in urllib.parse.uses_netloc: 20 urllib.parse.uses_netloc.append('git') 21import operator 22import collections 23import subprocess 24import pickle 25import errno 26import bb.utils 27import bb.checksum 28import bb.process 29import bb.event 30 31__version__ = "2" 32_checksum_cache = bb.checksum.FileChecksumCache() 33_revisions_cache = bb.checksum.RevisionsCache() 34 35logger = logging.getLogger("BitBake.Fetcher") 36 37CHECKSUM_LIST = [ "goh1", "md5", "sha256", "sha1", "sha384", "sha512" ] 38SHOWN_CHECKSUM_LIST = ["sha256"] 39 40class BBFetchException(Exception): 41 """Class all fetch exceptions inherit from""" 42 def __init__(self, message): 43 self.msg = message 44 Exception.__init__(self, message) 45 46 def __str__(self): 47 return self.msg 48 49class UntrustedUrl(BBFetchException): 50 """Exception raised when encountering a host not listed in BB_ALLOWED_NETWORKS""" 51 def __init__(self, url, message=''): 52 if message: 53 msg = message 54 else: 55 msg = "The URL: '%s' is not trusted and cannot be used" % url 56 self.url = url 57 BBFetchException.__init__(self, msg) 58 self.args = (url,) 59 60class MalformedUrl(BBFetchException): 61 """Exception raised when encountering an invalid url""" 62 def __init__(self, url, message=''): 63 if message: 64 msg = message 65 else: 66 msg = "The URL: '%s' is invalid and cannot be interpreted" % url 67 self.url = url 68 BBFetchException.__init__(self, msg) 69 self.args = (url,) 70 71class FetchError(BBFetchException): 72 """General fetcher exception when something happens incorrectly""" 73 def __init__(self, message, url = None): 74 if url: 75 msg = "Fetcher failure for URL: '%s'. %s" % (url, message) 76 else: 77 msg = "Fetcher failure: %s" % message 78 self.url = url 79 BBFetchException.__init__(self, msg) 80 self.args = (message, url) 81 82class ChecksumError(FetchError): 83 """Exception when mismatched checksum encountered""" 84 def __init__(self, message, url = None, checksum = None): 85 self.checksum = checksum 86 FetchError.__init__(self, message, url) 87 88class NoChecksumError(FetchError): 89 """Exception when no checksum is specified, but BB_STRICT_CHECKSUM is set""" 90 91class UnpackError(BBFetchException): 92 """General fetcher exception when something happens incorrectly when unpacking""" 93 def __init__(self, message, url): 94 msg = "Unpack failure for URL: '%s'. %s" % (url, message) 95 self.url = url 96 BBFetchException.__init__(self, msg) 97 self.args = (message, url) 98 99class NoMethodError(BBFetchException): 100 """Exception raised when there is no method to obtain a supplied url or set of urls""" 101 def __init__(self, url): 102 msg = "Could not find a fetcher which supports the URL: '%s'" % url 103 self.url = url 104 BBFetchException.__init__(self, msg) 105 self.args = (url,) 106 107class MissingParameterError(BBFetchException): 108 """Exception raised when a fetch method is missing a critical parameter in the url""" 109 def __init__(self, missing, url): 110 msg = "URL: '%s' is missing the required parameter '%s'" % (url, missing) 111 self.url = url 112 self.missing = missing 113 BBFetchException.__init__(self, msg) 114 self.args = (missing, url) 115 116class ParameterError(BBFetchException): 117 """Exception raised when a url cannot be processed due to invalid parameters.""" 118 def __init__(self, message, url): 119 msg = "URL: '%s' has invalid parameters. %s" % (url, message) 120 self.url = url 121 BBFetchException.__init__(self, msg) 122 self.args = (message, url) 123 124class NetworkAccess(BBFetchException): 125 """Exception raised when network access is disabled but it is required.""" 126 def __init__(self, url, cmd): 127 msg = "Network access disabled through BB_NO_NETWORK (or set indirectly due to use of BB_FETCH_PREMIRRORONLY) but access requested with command %s (for url %s)" % (cmd, url) 128 self.url = url 129 self.cmd = cmd 130 BBFetchException.__init__(self, msg) 131 self.args = (url, cmd) 132 133class NonLocalMethod(Exception): 134 def __init__(self): 135 Exception.__init__(self) 136 137class MissingChecksumEvent(bb.event.Event): 138 def __init__(self, url, **checksums): 139 self.url = url 140 self.checksums = checksums 141 bb.event.Event.__init__(self) 142 143 144class URI(object): 145 """ 146 A class representing a generic URI, with methods for 147 accessing the URI components, and stringifies to the 148 URI. 149 150 It is constructed by calling it with a URI, or setting 151 the attributes manually: 152 153 uri = URI("http://example.com/") 154 155 uri = URI() 156 uri.scheme = 'http' 157 uri.hostname = 'example.com' 158 uri.path = '/' 159 160 It has the following attributes: 161 162 * scheme (read/write) 163 * userinfo (authentication information) (read/write) 164 * username (read/write) 165 * password (read/write) 166 167 Note, password is deprecated as of RFC 3986. 168 169 * hostname (read/write) 170 * port (read/write) 171 * hostport (read only) 172 "hostname:port", if both are set, otherwise just "hostname" 173 * path (read/write) 174 * path_quoted (read/write) 175 A URI quoted version of path 176 * params (dict) (read/write) 177 * query (dict) (read/write) 178 * relative (bool) (read only) 179 True if this is a "relative URI", (e.g. file:foo.diff) 180 181 It stringifies to the URI itself. 182 183 Some notes about relative URIs: while it's specified that 184 a URI beginning with <scheme>:// should either be directly 185 followed by a hostname or a /, the old URI handling of the 186 fetch2 library did not conform to this. Therefore, this URI 187 class has some kludges to make sure that URIs are parsed in 188 a way comforming to bitbake's current usage. This URI class 189 supports the following: 190 191 file:relative/path.diff (IETF compliant) 192 git:relative/path.git (IETF compliant) 193 git:///absolute/path.git (IETF compliant) 194 file:///absolute/path.diff (IETF compliant) 195 196 file://relative/path.diff (not IETF compliant) 197 198 But it does not support the following: 199 200 file://hostname/absolute/path.diff (would be IETF compliant) 201 202 Note that the last case only applies to a list of 203 explicitly allowed schemes (currently only file://), that requires 204 its URIs to not have a network location. 205 """ 206 207 _relative_schemes = ['file', 'git'] 208 _netloc_forbidden = ['file'] 209 210 def __init__(self, uri=None): 211 self.scheme = '' 212 self.userinfo = '' 213 self.hostname = '' 214 self.port = None 215 self._path = '' 216 self.params = {} 217 self.query = {} 218 self.relative = False 219 220 if not uri: 221 return 222 223 # We hijack the URL parameters, since the way bitbake uses 224 # them are not quite RFC compliant. 225 uri, param_str = (uri.split(";", 1) + [None])[:2] 226 227 urlp = urllib.parse.urlparse(uri) 228 self.scheme = urlp.scheme 229 230 reparse = 0 231 232 # Coerce urlparse to make URI scheme use netloc 233 if not self.scheme in urllib.parse.uses_netloc: 234 urllib.parse.uses_params.append(self.scheme) 235 reparse = 1 236 237 # Make urlparse happy(/ier) by converting local resources 238 # to RFC compliant URL format. E.g.: 239 # file://foo.diff -> file:foo.diff 240 if urlp.scheme in self._netloc_forbidden: 241 uri = re.sub(r"(?<=:)//(?!/)", "", uri, count=1) 242 reparse = 1 243 244 if reparse: 245 urlp = urllib.parse.urlparse(uri) 246 247 # Identify if the URI is relative or not 248 if urlp.scheme in self._relative_schemes and \ 249 re.compile(r"^\w+:(?!//)").match(uri): 250 self.relative = True 251 252 if not self.relative: 253 self.hostname = urlp.hostname or '' 254 self.port = urlp.port 255 256 self.userinfo += urlp.username or '' 257 258 if urlp.password: 259 self.userinfo += ':%s' % urlp.password 260 261 self.path = urllib.parse.unquote(urlp.path) 262 263 if param_str: 264 self.params = self._param_str_split(param_str, ";") 265 if urlp.query: 266 self.query = self._param_str_split(urlp.query, "&") 267 268 def __str__(self): 269 userinfo = self.userinfo 270 if userinfo: 271 userinfo += '@' 272 273 return "%s:%s%s%s%s%s%s" % ( 274 self.scheme, 275 '' if self.relative else '//', 276 userinfo, 277 self.hostport, 278 self.path_quoted, 279 self._query_str(), 280 self._param_str()) 281 282 def _param_str(self): 283 return ( 284 ''.join([';', self._param_str_join(self.params, ";")]) 285 if self.params else '') 286 287 def _query_str(self): 288 return ( 289 ''.join(['?', self._param_str_join(self.query, "&")]) 290 if self.query else '') 291 292 def _param_str_split(self, string, elmdelim, kvdelim="="): 293 ret = collections.OrderedDict() 294 for k, v in [x.split(kvdelim, 1) if kvdelim in x else (x, None) for x in string.split(elmdelim) if x]: 295 ret[k] = v 296 return ret 297 298 def _param_str_join(self, dict_, elmdelim, kvdelim="="): 299 return elmdelim.join([kvdelim.join([k, v]) if v else k for k, v in dict_.items()]) 300 301 @property 302 def hostport(self): 303 if not self.port: 304 return self.hostname 305 return "%s:%d" % (self.hostname, self.port) 306 307 @property 308 def path_quoted(self): 309 return urllib.parse.quote(self.path) 310 311 @path_quoted.setter 312 def path_quoted(self, path): 313 self.path = urllib.parse.unquote(path) 314 315 @property 316 def path(self): 317 return self._path 318 319 @path.setter 320 def path(self, path): 321 self._path = path 322 323 if not path or re.compile("^/").match(path): 324 self.relative = False 325 else: 326 self.relative = True 327 328 @property 329 def username(self): 330 if self.userinfo: 331 return (self.userinfo.split(":", 1))[0] 332 return '' 333 334 @username.setter 335 def username(self, username): 336 password = self.password 337 self.userinfo = username 338 if password: 339 self.userinfo += ":%s" % password 340 341 @property 342 def password(self): 343 if self.userinfo and ":" in self.userinfo: 344 return (self.userinfo.split(":", 1))[1] 345 return '' 346 347 @password.setter 348 def password(self, password): 349 self.userinfo = "%s:%s" % (self.username, password) 350 351def decodeurl(url): 352 """Decodes an URL into the tokens (scheme, network location, path, 353 user, password, parameters). 354 """ 355 356 m = re.compile('(?P<type>[^:]*)://((?P<user>[^/;]+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url) 357 if not m: 358 raise MalformedUrl(url) 359 360 type = m.group('type') 361 location = m.group('location') 362 if not location: 363 raise MalformedUrl(url) 364 user = m.group('user') 365 parm = m.group('parm') 366 367 locidx = location.find('/') 368 if locidx != -1 and type.lower() != 'file': 369 host = location[:locidx] 370 path = location[locidx:] 371 elif type.lower() == 'file': 372 host = "" 373 path = location 374 else: 375 host = location 376 path = "/" 377 if user: 378 m = re.compile('(?P<user>[^:]+)(:?(?P<pswd>.*))').match(user) 379 if m: 380 user = m.group('user') 381 pswd = m.group('pswd') 382 else: 383 user = '' 384 pswd = '' 385 386 p = collections.OrderedDict() 387 if parm: 388 for s in parm.split(';'): 389 if s: 390 if not '=' in s: 391 raise MalformedUrl(url, "The URL: '%s' is invalid: parameter %s does not specify a value (missing '=')" % (url, s)) 392 s1, s2 = s.split('=', 1) 393 p[s1] = s2 394 395 return type, host, urllib.parse.unquote(path), user, pswd, p 396 397def encodeurl(decoded): 398 """Encodes a URL from tokens (scheme, network location, path, 399 user, password, parameters). 400 """ 401 402 type, host, path, user, pswd, p = decoded 403 404 if not type: 405 raise MissingParameterError('type', "encoded from the data %s" % str(decoded)) 406 url = ['%s://' % type] 407 if user and type != "file": 408 url.append("%s" % user) 409 if pswd: 410 url.append(":%s" % pswd) 411 url.append("@") 412 if host and type != "file": 413 url.append("%s" % host) 414 if path: 415 # Standardise path to ensure comparisons work 416 while '//' in path: 417 path = path.replace("//", "/") 418 url.append("%s" % urllib.parse.quote(path)) 419 if p: 420 for parm in p: 421 url.append(";%s=%s" % (parm, p[parm])) 422 423 return "".join(url) 424 425def uri_replace(ud, uri_find, uri_replace, replacements, d, mirrortarball=None): 426 if not ud.url or not uri_find or not uri_replace: 427 logger.error("uri_replace: passed an undefined value, not replacing") 428 return None 429 uri_decoded = list(decodeurl(ud.url)) 430 uri_find_decoded = list(decodeurl(uri_find)) 431 uri_replace_decoded = list(decodeurl(uri_replace)) 432 logger.debug2("For url %s comparing %s to %s" % (uri_decoded, uri_find_decoded, uri_replace_decoded)) 433 result_decoded = ['', '', '', '', '', {}] 434 # 0 - type, 1 - host, 2 - path, 3 - user, 4- pswd, 5 - params 435 for loc, i in enumerate(uri_find_decoded): 436 result_decoded[loc] = uri_decoded[loc] 437 regexp = i 438 if loc == 0 and regexp and not regexp.endswith("$"): 439 # Leaving the type unanchored can mean "https" matching "file" can become "files" 440 # which is clearly undesirable. 441 regexp += "$" 442 if loc == 5: 443 # Handle URL parameters 444 if i: 445 # Any specified URL parameters must match 446 for k in uri_find_decoded[loc]: 447 if uri_decoded[loc][k] != uri_find_decoded[loc][k]: 448 return None 449 # Overwrite any specified replacement parameters 450 for k in uri_replace_decoded[loc]: 451 for l in replacements: 452 uri_replace_decoded[loc][k] = uri_replace_decoded[loc][k].replace(l, replacements[l]) 453 result_decoded[loc][k] = uri_replace_decoded[loc][k] 454 elif (loc == 3 or loc == 4) and uri_replace_decoded[loc]: 455 # User/password in the replacement is just a straight replacement 456 result_decoded[loc] = uri_replace_decoded[loc] 457 elif (re.match(regexp, uri_decoded[loc])): 458 if not uri_replace_decoded[loc]: 459 result_decoded[loc] = "" 460 else: 461 for k in replacements: 462 uri_replace_decoded[loc] = uri_replace_decoded[loc].replace(k, replacements[k]) 463 #bb.note("%s %s %s" % (regexp, uri_replace_decoded[loc], uri_decoded[loc])) 464 result_decoded[loc] = re.sub(regexp, uri_replace_decoded[loc], uri_decoded[loc], count=1) 465 if loc == 2: 466 # Handle path manipulations 467 basename = None 468 if uri_decoded[0] != uri_replace_decoded[0] and mirrortarball: 469 # If the source and destination url types differ, must be a mirrortarball mapping 470 basename = os.path.basename(mirrortarball) 471 # Kill parameters, they make no sense for mirror tarballs 472 uri_decoded[5] = {} 473 uri_find_decoded[5] = {} 474 elif ud.localpath and ud.method.supports_checksum(ud): 475 basename = os.path.basename(ud.localpath) 476 if basename: 477 uri_basename = os.path.basename(uri_decoded[loc]) 478 # Prefix with a slash as a sentinel in case 479 # result_decoded[loc] does not contain one. 480 path = "/" + result_decoded[loc] 481 if uri_basename and basename != uri_basename and path.endswith("/" + uri_basename): 482 result_decoded[loc] = path[1:-len(uri_basename)] + basename 483 elif not path.endswith("/" + basename): 484 result_decoded[loc] = os.path.join(path[1:], basename) 485 else: 486 return None 487 result = encodeurl(result_decoded) 488 if result == ud.url: 489 return None 490 logger.debug2("For url %s returning %s" % (ud.url, result)) 491 return result 492 493methods = [] 494urldata_cache = {} 495saved_headrevs = {} 496 497def fetcher_init(d, servercontext=True): 498 """ 499 Called to initialize the fetchers once the configuration data is known. 500 Calls before this must not hit the cache. 501 """ 502 503 _checksum_cache.init_cache(d.getVar("BB_CACHEDIR")) 504 _revisions_cache.init_cache(d.getVar("BB_CACHEDIR")) 505 506 if not servercontext: 507 return 508 509 try: 510 # fetcher_init is called multiple times, so make sure we only save the 511 # revs the first time it is called. 512 if not bb.fetch2.saved_headrevs: 513 bb.fetch2.saved_headrevs = _revisions_cache.get_revs() 514 except: 515 pass 516 517 # When to drop SCM head revisions controlled by user policy 518 srcrev_policy = d.getVar('BB_SRCREV_POLICY') or "clear" 519 if srcrev_policy == "cache": 520 logger.debug("Keeping SRCREV cache due to cache policy of: %s", srcrev_policy) 521 elif srcrev_policy == "clear": 522 logger.debug("Clearing SRCREV cache due to cache policy of: %s", srcrev_policy) 523 _revisions_cache.clear_cache() 524 else: 525 raise FetchError("Invalid SRCREV cache policy of: %s" % srcrev_policy) 526 527 528 for m in methods: 529 if hasattr(m, "init"): 530 m.init(d) 531 532def fetcher_parse_save(): 533 _checksum_cache.save_extras() 534 _revisions_cache.save_extras() 535 536def fetcher_parse_done(): 537 _checksum_cache.save_merge() 538 _revisions_cache.save_merge() 539 540def fetcher_compare_revisions(d): 541 """ 542 Compare the revisions in the persistent cache with the saved values from 543 when bitbake was started and return true if they have changed. 544 """ 545 546 headrevs = _revisions_cache.get_revs() 547 return headrevs != bb.fetch2.saved_headrevs 548 549def mirror_from_string(data): 550 mirrors = (data or "").replace('\\n',' ').split() 551 # Split into pairs 552 if len(mirrors) % 2 != 0: 553 bb.warn('Invalid mirror data %s, should have paired members.' % data) 554 return list(zip(*[iter(mirrors)]*2)) 555 556def verify_checksum(ud, d, precomputed={}, localpath=None, fatal_nochecksum=True): 557 """ 558 verify the MD5 and SHA256 checksum for downloaded src 559 560 Raises a FetchError if one or both of the SRC_URI checksums do not match 561 the downloaded file, or if BB_STRICT_CHECKSUM is set and there are no 562 checksums specified. 563 564 Returns a dict of checksums that can be stored in a done stamp file and 565 passed in as precomputed parameter in a later call to avoid re-computing 566 the checksums from the file. This allows verifying the checksums of the 567 file against those in the recipe each time, rather than only after 568 downloading. See https://bugzilla.yoctoproject.org/show_bug.cgi?id=5571. 569 """ 570 if ud.ignore_checksums or not ud.method.supports_checksum(ud): 571 return {} 572 573 if localpath is None: 574 localpath = ud.localpath 575 576 def compute_checksum_info(checksum_id): 577 checksum_name = getattr(ud, "%s_name" % checksum_id) 578 579 if checksum_id in precomputed: 580 checksum_data = precomputed[checksum_id] 581 else: 582 checksum_data = getattr(bb.utils, "%s_file" % checksum_id)(localpath) 583 584 checksum_expected = getattr(ud, "%s_expected" % checksum_id) 585 586 if checksum_expected == '': 587 checksum_expected = None 588 589 return { 590 "id": checksum_id, 591 "name": checksum_name, 592 "data": checksum_data, 593 "expected": checksum_expected 594 } 595 596 checksum_infos = [] 597 for checksum_id in CHECKSUM_LIST: 598 checksum_infos.append(compute_checksum_info(checksum_id)) 599 600 checksum_dict = {ci["id"] : ci["data"] for ci in checksum_infos} 601 checksum_event = {"%ssum" % ci["id"] : ci["data"] for ci in checksum_infos} 602 603 for ci in checksum_infos: 604 if ci["id"] in SHOWN_CHECKSUM_LIST: 605 checksum_lines = ["SRC_URI[%s] = \"%s\"" % (ci["name"], ci["data"])] 606 607 # If no checksum has been provided 608 if fatal_nochecksum and ud.method.recommends_checksum(ud) and all(ci["expected"] is None for ci in checksum_infos): 609 messages = [] 610 strict = d.getVar("BB_STRICT_CHECKSUM") or "0" 611 612 # If strict checking enabled and neither sum defined, raise error 613 if strict == "1": 614 raise NoChecksumError("\n".join(checksum_lines)) 615 616 bb.event.fire(MissingChecksumEvent(ud.url, **checksum_event), d) 617 618 if strict == "ignore": 619 return checksum_dict 620 621 # Log missing sums so user can more easily add them 622 messages.append("Missing checksum for '%s', consider adding at " \ 623 "least one to the recipe:" % ud.localpath) 624 messages.extend(checksum_lines) 625 logger.warning("\n".join(messages)) 626 627 # We want to alert the user if a checksum is defined in the recipe but 628 # it does not match. 629 messages = [] 630 messages.append("Checksum mismatch!") 631 bad_checksum = None 632 633 for ci in checksum_infos: 634 if ci["expected"] and ci["expected"] != ci["data"]: 635 messages.append("File: '%s' has %s checksum '%s' when '%s' was " \ 636 "expected" % (localpath, ci["id"], ci["data"], ci["expected"])) 637 bad_checksum = ci["data"] 638 639 if bad_checksum: 640 messages.append("If this change is expected (e.g. you have upgraded " \ 641 "to a new version without updating the checksums) " \ 642 "then you can use these lines within the recipe:") 643 messages.extend(checksum_lines) 644 messages.append("Otherwise you should retry the download and/or " \ 645 "check with upstream to determine if the file has " \ 646 "become corrupted or otherwise unexpectedly modified.") 647 raise ChecksumError("\n".join(messages), ud.url, bad_checksum) 648 649 return checksum_dict 650 651def verify_donestamp(ud, d, origud=None): 652 """ 653 Check whether the done stamp file has the right checksums (if the fetch 654 method supports them). If it doesn't, delete the done stamp and force 655 a re-download. 656 657 Returns True, if the donestamp exists and is valid, False otherwise. When 658 returning False, any existing done stamps are removed. 659 """ 660 if not ud.needdonestamp or (origud and not origud.needdonestamp): 661 return True 662 663 if not os.path.exists(ud.localpath): 664 # local path does not exist 665 if os.path.exists(ud.donestamp): 666 # done stamp exists, but the downloaded file does not; the done stamp 667 # must be incorrect, re-trigger the download 668 bb.utils.remove(ud.donestamp) 669 return False 670 671 if (not ud.method.supports_checksum(ud) or 672 (origud and not origud.method.supports_checksum(origud))): 673 # if done stamp exists and checksums not supported; assume the local 674 # file is current 675 return os.path.exists(ud.donestamp) 676 677 precomputed_checksums = {} 678 # Only re-use the precomputed checksums if the donestamp is newer than the 679 # file. Do not rely on the mtime of directories, though. If ud.localpath is 680 # a directory, there will probably not be any checksums anyway. 681 if os.path.exists(ud.donestamp) and (os.path.isdir(ud.localpath) or 682 os.path.getmtime(ud.localpath) < os.path.getmtime(ud.donestamp)): 683 try: 684 with open(ud.donestamp, "rb") as cachefile: 685 pickled = pickle.Unpickler(cachefile) 686 precomputed_checksums.update(pickled.load()) 687 except Exception as e: 688 # Avoid the warnings on the upgrade path from emtpy done stamp 689 # files to those containing the checksums. 690 if not isinstance(e, EOFError): 691 # Ignore errors, they aren't fatal 692 logger.warning("Couldn't load checksums from donestamp %s: %s " 693 "(msg: %s)" % (ud.donestamp, type(e).__name__, 694 str(e))) 695 696 try: 697 checksums = verify_checksum(ud, d, precomputed_checksums) 698 # If the cache file did not have the checksums, compute and store them 699 # as an upgrade path from the previous done stamp file format. 700 if checksums != precomputed_checksums: 701 with open(ud.donestamp, "wb") as cachefile: 702 p = pickle.Pickler(cachefile, 2) 703 p.dump(checksums) 704 return True 705 except ChecksumError as e: 706 # Checksums failed to verify, trigger re-download and remove the 707 # incorrect stamp file. 708 logger.warning("Checksum mismatch for local file %s\n" 709 "Cleaning and trying again." % ud.localpath) 710 if os.path.exists(ud.localpath): 711 rename_bad_checksum(ud, e.checksum) 712 bb.utils.remove(ud.donestamp) 713 return False 714 715 716def update_stamp(ud, d): 717 """ 718 donestamp is file stamp indicating the whole fetching is done 719 this function update the stamp after verifying the checksum 720 """ 721 if not ud.needdonestamp: 722 return 723 724 if os.path.exists(ud.donestamp): 725 # Touch the done stamp file to show active use of the download 726 try: 727 os.utime(ud.donestamp, None) 728 except: 729 # Errors aren't fatal here 730 pass 731 else: 732 try: 733 checksums = verify_checksum(ud, d) 734 # Store the checksums for later re-verification against the recipe 735 with open(ud.donestamp, "wb") as cachefile: 736 p = pickle.Pickler(cachefile, 2) 737 p.dump(checksums) 738 except ChecksumError as e: 739 # Checksums failed to verify, trigger re-download and remove the 740 # incorrect stamp file. 741 logger.warning("Checksum mismatch for local file %s\n" 742 "Cleaning and trying again." % ud.localpath) 743 if os.path.exists(ud.localpath): 744 rename_bad_checksum(ud, e.checksum) 745 bb.utils.remove(ud.donestamp) 746 raise 747 748def subprocess_setup(): 749 # Python installs a SIGPIPE handler by default. This is usually not what 750 # non-Python subprocesses expect. 751 # SIGPIPE errors are known issues with gzip/bash 752 signal.signal(signal.SIGPIPE, signal.SIG_DFL) 753 754def mark_recipe_nocache(d): 755 if d.getVar('BB_SRCREV_POLICY') != "cache": 756 d.setVar('BB_DONT_CACHE', '1') 757 758def get_autorev(d): 759 mark_recipe_nocache(d) 760 d.setVar("__BBAUTOREV_SEEN", True) 761 return "AUTOINC" 762 763def _get_srcrev(d, method_name='sortable_revision'): 764 """ 765 Return the revision string, usually for use in the version string (PV) of the current package 766 Most packages usually only have one SCM so we just pass on the call. 767 In the multi SCM case, we build a value based on SRCREV_FORMAT which must 768 have been set. 769 770 The idea here is that we put the string "AUTOINC+" into return value if the revisions are not 771 incremental, other code is then responsible for turning that into an increasing value (if needed) 772 773 A method_name can be supplied to retrieve an alternatively formatted revision from a fetcher, if 774 that fetcher provides a method with the given name and the same signature as sortable_revision. 775 """ 776 777 d.setVar("__BBSRCREV_SEEN", "1") 778 recursion = d.getVar("__BBINSRCREV") 779 if recursion: 780 raise FetchError("There are recursive references in fetcher variables, likely through SRC_URI") 781 d.setVar("__BBINSRCREV", True) 782 783 scms = [] 784 revs = [] 785 fetcher = Fetch(d.getVar('SRC_URI').split(), d) 786 urldata = fetcher.ud 787 for u in urldata: 788 if urldata[u].method.supports_srcrev(): 789 scms.append(u) 790 791 if not scms: 792 d.delVar("__BBINSRCREV") 793 return "", revs 794 795 796 if len(scms) == 1 and len(urldata[scms[0]].names) == 1: 797 autoinc, rev = getattr(urldata[scms[0]].method, method_name)(urldata[scms[0]], d, urldata[scms[0]].names[0]) 798 revs.append(rev) 799 if len(rev) > 10: 800 rev = rev[:10] 801 d.delVar("__BBINSRCREV") 802 if autoinc: 803 return "AUTOINC+" + rev, revs 804 return rev, revs 805 806 # 807 # Mutiple SCMs are in SRC_URI so we resort to SRCREV_FORMAT 808 # 809 format = d.getVar('SRCREV_FORMAT') 810 if not format: 811 raise FetchError("The SRCREV_FORMAT variable must be set when multiple SCMs are used.\n"\ 812 "The SCMs are:\n%s" % '\n'.join(scms)) 813 814 name_to_rev = {} 815 seenautoinc = False 816 for scm in scms: 817 ud = urldata[scm] 818 for name in ud.names: 819 autoinc, rev = getattr(ud.method, method_name)(ud, d, name) 820 revs.append(rev) 821 seenautoinc = seenautoinc or autoinc 822 if len(rev) > 10: 823 rev = rev[:10] 824 name_to_rev[name] = rev 825 # Replace names by revisions in the SRCREV_FORMAT string. The approach used 826 # here can handle names being prefixes of other names and names appearing 827 # as substrings in revisions (in which case the name should not be 828 # expanded). The '|' regular expression operator tries matches from left to 829 # right, so we need to sort the names with the longest ones first. 830 names_descending_len = sorted(name_to_rev, key=len, reverse=True) 831 name_to_rev_re = "|".join(re.escape(name) for name in names_descending_len) 832 format = re.sub(name_to_rev_re, lambda match: name_to_rev[match.group(0)], format) 833 834 if seenautoinc: 835 format = "AUTOINC+" + format 836 837 d.delVar("__BBINSRCREV") 838 return format, revs 839 840def get_hashvalue(d, method_name='sortable_revision'): 841 pkgv, revs = _get_srcrev(d, method_name=method_name) 842 return " ".join(revs) 843 844def get_pkgv_string(d, method_name='sortable_revision'): 845 pkgv, revs = _get_srcrev(d, method_name=method_name) 846 return pkgv 847 848def get_srcrev(d, method_name='sortable_revision'): 849 pkgv, revs = _get_srcrev(d, method_name=method_name) 850 if not pkgv: 851 raise FetchError("SRCREV was used yet no valid SCM was found in SRC_URI") 852 return pkgv 853 854def localpath(url, d): 855 fetcher = bb.fetch2.Fetch([url], d) 856 return fetcher.localpath(url) 857 858# Need to export PATH as binary could be in metadata paths 859# rather than host provided 860# Also include some other variables. 861FETCH_EXPORT_VARS = ['HOME', 'PATH', 862 'HTTP_PROXY', 'http_proxy', 863 'HTTPS_PROXY', 'https_proxy', 864 'FTP_PROXY', 'ftp_proxy', 865 'FTPS_PROXY', 'ftps_proxy', 866 'NO_PROXY', 'no_proxy', 867 'ALL_PROXY', 'all_proxy', 868 'GIT_PROXY_COMMAND', 869 'GIT_SSH', 870 'GIT_SSH_COMMAND', 871 'GIT_SSL_CAINFO', 872 'GIT_SMART_HTTP', 873 'SSH_AUTH_SOCK', 'SSH_AGENT_PID', 874 'SOCKS5_USER', 'SOCKS5_PASSWD', 875 'DBUS_SESSION_BUS_ADDRESS', 876 'P4CONFIG', 877 'SSL_CERT_FILE', 878 'NODE_EXTRA_CA_CERTS', 879 'AWS_PROFILE', 880 'AWS_ACCESS_KEY_ID', 881 'AWS_SECRET_ACCESS_KEY', 882 'AWS_ROLE_ARN', 883 'AWS_WEB_IDENTITY_TOKEN_FILE', 884 'AWS_DEFAULT_REGION', 885 'AWS_SESSION_TOKEN', 886 'GIT_CACHE_PATH', 887 'REMOTE_CONTAINERS_IPC', 888 'GITHUB_TOKEN', 889 'SSL_CERT_DIR'] 890 891def get_fetcher_environment(d): 892 newenv = {} 893 origenv = d.getVar("BB_ORIGENV") 894 for name in bb.fetch2.FETCH_EXPORT_VARS: 895 value = d.getVar(name) 896 if not value and origenv: 897 value = origenv.getVar(name) 898 if value: 899 newenv[name] = value 900 return newenv 901 902def runfetchcmd(cmd, d, quiet=False, cleanup=None, log=None, workdir=None): 903 """ 904 Run cmd returning the command output 905 Raise an error if interrupted or cmd fails 906 Optionally echo command output to stdout 907 Optionally remove the files/directories listed in cleanup upon failure 908 """ 909 910 exportvars = FETCH_EXPORT_VARS 911 912 if not cleanup: 913 cleanup = [] 914 915 # If PATH contains WORKDIR which contains PV-PR which contains SRCPV we 916 # can end up in circular recursion here so give the option of breaking it 917 # in a data store copy. 918 try: 919 d.getVar("PV") 920 d.getVar("PR") 921 except bb.data_smart.ExpansionError: 922 d = bb.data.createCopy(d) 923 d.setVar("PV", "fetcheravoidrecurse") 924 d.setVar("PR", "fetcheravoidrecurse") 925 926 origenv = d.getVar("BB_ORIGENV", False) 927 for var in exportvars: 928 val = d.getVar(var) or (origenv and origenv.getVar(var)) 929 if val: 930 cmd = 'export ' + var + '=\"%s\"; %s' % (val, cmd) 931 932 # Disable pseudo as it may affect ssh, potentially causing it to hang. 933 cmd = 'export PSEUDO_DISABLED=1; ' + cmd 934 935 if workdir: 936 logger.debug("Running '%s' in %s" % (cmd, workdir)) 937 else: 938 logger.debug("Running %s", cmd) 939 940 success = False 941 error_message = "" 942 943 try: 944 (output, errors) = bb.process.run(cmd, log=log, shell=True, stderr=subprocess.PIPE, cwd=workdir) 945 success = True 946 except bb.process.NotFoundError as e: 947 error_message = "Fetch command %s not found" % (e.command) 948 except bb.process.ExecutionError as e: 949 if e.stdout: 950 output = "output:\n%s\n%s" % (e.stdout, e.stderr) 951 elif e.stderr: 952 output = "output:\n%s" % e.stderr 953 else: 954 if log: 955 output = "see logfile for output" 956 else: 957 output = "no output" 958 error_message = "Fetch command %s failed with exit code %s, %s" % (e.command, e.exitcode, output) 959 except bb.process.CmdError as e: 960 error_message = "Fetch command %s could not be run:\n%s" % (e.command, e.msg) 961 if not success: 962 for f in cleanup: 963 try: 964 bb.utils.remove(f, True) 965 except OSError: 966 pass 967 968 raise FetchError(error_message) 969 970 return output 971 972def check_network_access(d, info, url): 973 """ 974 log remote network access, and error if BB_NO_NETWORK is set or the given 975 URI is untrusted 976 """ 977 if bb.utils.to_boolean(d.getVar("BB_NO_NETWORK")): 978 raise NetworkAccess(url, info) 979 elif not trusted_network(d, url): 980 raise UntrustedUrl(url, info) 981 else: 982 logger.debug("Fetcher accessed the network with the command %s" % info) 983 984def build_mirroruris(origud, mirrors, ld): 985 uris = [] 986 uds = [] 987 988 replacements = {} 989 replacements["TYPE"] = origud.type 990 replacements["HOST"] = origud.host 991 replacements["PATH"] = origud.path 992 replacements["BASENAME"] = origud.path.split("/")[-1] 993 replacements["MIRRORNAME"] = origud.host.replace(':','.') + origud.path.replace('/', '.').replace('*', '.') 994 995 def adduri(ud, uris, uds, mirrors, tarballs): 996 for line in mirrors: 997 try: 998 (find, replace) = line 999 except ValueError: 1000 continue 1001 1002 for tarball in tarballs: 1003 newuri = uri_replace(ud, find, replace, replacements, ld, tarball) 1004 if not newuri or newuri in uris or newuri == origud.url: 1005 continue 1006 1007 if not trusted_network(ld, newuri): 1008 logger.debug("Mirror %s not in the list of trusted networks, skipping" % (newuri)) 1009 continue 1010 1011 # Create a local copy of the mirrors minus the current line 1012 # this will prevent us from recursively processing the same line 1013 # as well as indirect recursion A -> B -> C -> A 1014 localmirrors = list(mirrors) 1015 localmirrors.remove(line) 1016 1017 try: 1018 newud = FetchData(newuri, ld) 1019 newud.ignore_checksums = True 1020 newud.setup_localpath(ld) 1021 except bb.fetch2.BBFetchException as e: 1022 logger.debug("Mirror fetch failure for url %s (original url: %s)" % (newuri, origud.url)) 1023 logger.debug(str(e)) 1024 try: 1025 # setup_localpath of file:// urls may fail, we should still see 1026 # if mirrors of the url exist 1027 adduri(newud, uris, uds, localmirrors, tarballs) 1028 except UnboundLocalError: 1029 pass 1030 continue 1031 uris.append(newuri) 1032 uds.append(newud) 1033 1034 adduri(newud, uris, uds, localmirrors, tarballs) 1035 1036 adduri(origud, uris, uds, mirrors, origud.mirrortarballs or [None]) 1037 1038 return uris, uds 1039 1040def rename_bad_checksum(ud, suffix): 1041 """ 1042 Renames files to have suffix from parameter 1043 """ 1044 1045 if ud.localpath is None: 1046 return 1047 1048 new_localpath = "%s_bad-checksum_%s" % (ud.localpath, suffix) 1049 bb.warn("Renaming %s to %s" % (ud.localpath, new_localpath)) 1050 if not bb.utils.movefile(ud.localpath, new_localpath): 1051 bb.warn("Renaming %s to %s failed, grep movefile in log.do_fetch to see why" % (ud.localpath, new_localpath)) 1052 1053 1054def try_mirror_url(fetch, origud, ud, ld, check = False): 1055 # Return of None or a value means we're finished 1056 # False means try another url 1057 1058 if ud.lockfile and ud.lockfile != origud.lockfile: 1059 lf = bb.utils.lockfile(ud.lockfile) 1060 1061 try: 1062 if check: 1063 found = ud.method.checkstatus(fetch, ud, ld) 1064 if found: 1065 return found 1066 return False 1067 1068 if not verify_donestamp(ud, ld, origud) or ud.method.need_update(ud, ld): 1069 ud.method.download(ud, ld) 1070 if hasattr(ud.method,"build_mirror_data"): 1071 ud.method.build_mirror_data(ud, ld) 1072 1073 if not ud.localpath or not os.path.exists(ud.localpath): 1074 return False 1075 1076 if ud.localpath == origud.localpath: 1077 return ud.localpath 1078 1079 # We may be obtaining a mirror tarball which needs further processing by the real fetcher 1080 # If that tarball is a local file:// we need to provide a symlink to it 1081 dldir = ld.getVar("DL_DIR") 1082 1083 if origud.mirrortarballs and os.path.basename(ud.localpath) in origud.mirrortarballs and os.path.basename(ud.localpath) != os.path.basename(origud.localpath): 1084 # Create donestamp in old format to avoid triggering a re-download 1085 if ud.donestamp: 1086 bb.utils.mkdirhier(os.path.dirname(ud.donestamp)) 1087 open(ud.donestamp, 'w').close() 1088 dest = os.path.join(dldir, os.path.basename(ud.localpath)) 1089 if not os.path.exists(dest): 1090 # In case this is executing without any file locks held (as is 1091 # the case for file:// URLs), two tasks may end up here at the 1092 # same time, in which case we do not want the second task to 1093 # fail when the link has already been created by the first task. 1094 try: 1095 os.symlink(ud.localpath, dest) 1096 except FileExistsError: 1097 pass 1098 if not verify_donestamp(origud, ld) or origud.method.need_update(origud, ld): 1099 origud.method.download(origud, ld) 1100 if hasattr(origud.method, "build_mirror_data"): 1101 origud.method.build_mirror_data(origud, ld) 1102 return origud.localpath 1103 # Otherwise the result is a local file:// and we symlink to it 1104 ensure_symlink(ud.localpath, origud.localpath) 1105 update_stamp(origud, ld) 1106 return ud.localpath 1107 1108 except bb.fetch2.NetworkAccess: 1109 raise 1110 1111 except IOError as e: 1112 if e.errno in [errno.ESTALE]: 1113 logger.warning("Stale Error Observed %s." % ud.url) 1114 return False 1115 raise 1116 1117 except bb.fetch2.BBFetchException as e: 1118 if isinstance(e, ChecksumError): 1119 logger.warning("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (ud.url, origud.url)) 1120 logger.warning(str(e)) 1121 if os.path.exists(ud.localpath): 1122 rename_bad_checksum(ud, e.checksum) 1123 elif isinstance(e, NoChecksumError): 1124 raise 1125 else: 1126 logger.debug("Mirror fetch failure for url %s (original url: %s)" % (ud.url, origud.url)) 1127 logger.debug(str(e)) 1128 try: 1129 if ud.method.cleanup_upon_failure(): 1130 ud.method.clean(ud, ld) 1131 except UnboundLocalError: 1132 pass 1133 return False 1134 finally: 1135 if ud.lockfile and ud.lockfile != origud.lockfile: 1136 bb.utils.unlockfile(lf) 1137 1138 1139def ensure_symlink(target, link_name): 1140 if not os.path.exists(link_name): 1141 dirname = os.path.dirname(link_name) 1142 bb.utils.mkdirhier(dirname) 1143 if os.path.islink(link_name): 1144 # Broken symbolic link 1145 os.unlink(link_name) 1146 1147 # In case this is executing without any file locks held (as is 1148 # the case for file:// URLs), two tasks may end up here at the 1149 # same time, in which case we do not want the second task to 1150 # fail when the link has already been created by the first task. 1151 try: 1152 os.symlink(target, link_name) 1153 except FileExistsError: 1154 pass 1155 1156 1157def try_mirrors(fetch, d, origud, mirrors, check = False): 1158 """ 1159 Try to use a mirrored version of the sources. 1160 This method will be automatically called before the fetchers go. 1161 1162 d Is a bb.data instance 1163 uri is the original uri we're trying to download 1164 mirrors is the list of mirrors we're going to try 1165 """ 1166 ld = d.createCopy() 1167 1168 uris, uds = build_mirroruris(origud, mirrors, ld) 1169 1170 for index, uri in enumerate(uris): 1171 ret = try_mirror_url(fetch, origud, uds[index], ld, check) 1172 if ret: 1173 return ret 1174 return None 1175 1176def trusted_network(d, url): 1177 """ 1178 Use a trusted url during download if networking is enabled and 1179 BB_ALLOWED_NETWORKS is set globally or for a specific recipe. 1180 Note: modifies SRC_URI & mirrors. 1181 """ 1182 if bb.utils.to_boolean(d.getVar("BB_NO_NETWORK")): 1183 return True 1184 1185 pkgname = d.expand(d.getVar('PN', False)) 1186 trusted_hosts = None 1187 if pkgname: 1188 trusted_hosts = d.getVarFlag('BB_ALLOWED_NETWORKS', pkgname, False) 1189 1190 if not trusted_hosts: 1191 trusted_hosts = d.getVar('BB_ALLOWED_NETWORKS') 1192 1193 # Not enabled. 1194 if not trusted_hosts: 1195 return True 1196 1197 scheme, network, path, user, passwd, param = decodeurl(url) 1198 1199 if not network: 1200 return True 1201 1202 network = network.split(':')[0] 1203 network = network.lower() 1204 1205 for host in trusted_hosts.split(" "): 1206 host = host.lower() 1207 if host.startswith("*.") and ("." + network).endswith(host[1:]): 1208 return True 1209 if host == network: 1210 return True 1211 1212 return False 1213 1214def srcrev_internal_helper(ud, d, name): 1215 """ 1216 Return: 1217 a) a source revision if specified 1218 b) latest revision if SRCREV="AUTOINC" 1219 c) None if not specified 1220 """ 1221 1222 srcrev = None 1223 pn = d.getVar("PN") 1224 attempts = [] 1225 if name != '' and pn: 1226 attempts.append("SRCREV_%s:pn-%s" % (name, pn)) 1227 if name != '': 1228 attempts.append("SRCREV_%s" % name) 1229 if pn: 1230 attempts.append("SRCREV:pn-%s" % pn) 1231 attempts.append("SRCREV") 1232 1233 for a in attempts: 1234 srcrev = d.getVar(a) 1235 if srcrev and srcrev != "INVALID": 1236 break 1237 1238 if 'rev' in ud.parm and 'tag' in ud.parm: 1239 raise FetchError("Please specify a ;rev= parameter or a ;tag= parameter in the url %s but not both." % (ud.url)) 1240 1241 if 'rev' in ud.parm or 'tag' in ud.parm: 1242 if 'rev' in ud.parm: 1243 parmrev = ud.parm['rev'] 1244 else: 1245 parmrev = ud.parm['tag'] 1246 if srcrev == "INVALID" or not srcrev: 1247 return parmrev 1248 if srcrev != parmrev: 1249 raise FetchError("Conflicting revisions (%s from SRCREV and %s from the url) found, please specify one valid value" % (srcrev, parmrev)) 1250 return parmrev 1251 1252 if srcrev == "INVALID" or not srcrev: 1253 raise FetchError("Please set a valid SRCREV for url %s (possible key names are %s, or use a ;rev=X URL parameter)" % (str(attempts), ud.url), ud.url) 1254 if srcrev == "AUTOINC": 1255 d.setVar("__BBAUTOREV_ACTED_UPON", True) 1256 srcrev = ud.method.latest_revision(ud, d, name) 1257 1258 return srcrev 1259 1260def get_checksum_file_list(d): 1261 """ Get a list of files checksum in SRC_URI 1262 1263 Returns the resolved local paths of all local file entries in 1264 SRC_URI as a space-separated string 1265 """ 1266 fetch = Fetch([], d, cache = False, localonly = True) 1267 filelist = [] 1268 for u in fetch.urls: 1269 ud = fetch.ud[u] 1270 if ud and isinstance(ud.method, local.Local): 1271 found = False 1272 paths = ud.method.localfile_searchpaths(ud, d) 1273 for f in paths: 1274 pth = ud.decodedurl 1275 if os.path.exists(f): 1276 found = True 1277 filelist.append(f + ":" + str(os.path.exists(f))) 1278 if not found: 1279 bb.fatal(("Unable to get checksum for %s SRC_URI entry %s: file could not be found" 1280 "\nThe following paths were searched:" 1281 "\n%s") % (d.getVar('PN'), os.path.basename(f), '\n'.join(paths))) 1282 1283 return " ".join(filelist) 1284 1285def get_file_checksums(filelist, pn, localdirsexclude): 1286 """Get a list of the checksums for a list of local files 1287 1288 Returns the checksums for a list of local files, caching the results as 1289 it proceeds 1290 1291 """ 1292 return _checksum_cache.get_checksums(filelist, pn, localdirsexclude) 1293 1294 1295class FetchData(object): 1296 """ 1297 A class which represents the fetcher state for a given URI. 1298 """ 1299 def __init__(self, url, d, localonly = False): 1300 # localpath is the location of a downloaded result. If not set, the file is local. 1301 self.donestamp = None 1302 self.needdonestamp = True 1303 self.localfile = "" 1304 self.localpath = None 1305 self.lockfile = None 1306 self.mirrortarballs = [] 1307 self.basename = None 1308 self.basepath = None 1309 (self.type, self.host, self.path, self.user, self.pswd, self.parm) = decodeurl(d.expand(url)) 1310 self.date = self.getSRCDate(d) 1311 self.url = url 1312 if not self.user and "user" in self.parm: 1313 self.user = self.parm["user"] 1314 if not self.pswd and "pswd" in self.parm: 1315 self.pswd = self.parm["pswd"] 1316 self.setup = False 1317 1318 def configure_checksum(checksum_id): 1319 checksum_plain_name = "%ssum" % checksum_id 1320 if "name" in self.parm: 1321 checksum_name = "%s.%ssum" % (self.parm["name"], checksum_id) 1322 else: 1323 checksum_name = checksum_plain_name 1324 1325 if checksum_name in self.parm: 1326 checksum_expected = self.parm[checksum_name] 1327 elif checksum_plain_name in self.parm: 1328 checksum_expected = self.parm[checksum_plain_name] 1329 checksum_name = checksum_plain_name 1330 elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3", "az", "crate", "gs", "gomod", "npm"]: 1331 checksum_expected = None 1332 else: 1333 checksum_expected = d.getVarFlag("SRC_URI", checksum_name) 1334 1335 setattr(self, "%s_name" % checksum_id, checksum_name) 1336 setattr(self, "%s_expected" % checksum_id, checksum_expected) 1337 1338 self.names = self.parm.get("name",'default').split(',') 1339 1340 self.method = None 1341 for m in methods: 1342 if m.supports(self, d): 1343 self.method = m 1344 break 1345 1346 if not self.method: 1347 raise NoMethodError(url) 1348 1349 if localonly and not isinstance(self.method, local.Local): 1350 raise NonLocalMethod() 1351 1352 if self.parm.get("proto", None) and "protocol" not in self.parm: 1353 logger.warning('Consider updating %s recipe to use "protocol" not "proto" in SRC_URI.', d.getVar('PN')) 1354 self.parm["protocol"] = self.parm.get("proto", None) 1355 1356 if hasattr(self.method, "urldata_init"): 1357 self.method.urldata_init(self, d) 1358 1359 for checksum_id in CHECKSUM_LIST: 1360 configure_checksum(checksum_id) 1361 1362 self.ignore_checksums = False 1363 1364 if "localpath" in self.parm: 1365 # if user sets localpath for file, use it instead. 1366 self.localpath = self.parm["localpath"] 1367 self.basename = os.path.basename(self.localpath) 1368 elif self.localfile: 1369 self.localpath = self.method.localpath(self, d) 1370 1371 dldir = d.getVar("DL_DIR") 1372 1373 if not self.needdonestamp: 1374 return 1375 1376 # Note: .done and .lock files should always be in DL_DIR whereas localpath may not be. 1377 if self.localpath and self.localpath.startswith(dldir): 1378 basepath = self.localpath 1379 elif self.localpath: 1380 basepath = dldir + os.sep + os.path.basename(self.localpath) 1381 elif self.basepath or self.basename: 1382 basepath = dldir + os.sep + (self.basepath or self.basename) 1383 else: 1384 bb.fatal("Can't determine lock path for url %s" % url) 1385 1386 self.donestamp = basepath + '.done' 1387 self.lockfile = basepath + '.lock' 1388 1389 def setup_revisions(self, d): 1390 self.revisions = {} 1391 for name in self.names: 1392 self.revisions[name] = srcrev_internal_helper(self, d, name) 1393 1394 # add compatibility code for non name specified case 1395 if len(self.names) == 1: 1396 self.revision = self.revisions[self.names[0]] 1397 1398 def setup_localpath(self, d): 1399 if not self.localpath: 1400 self.localpath = self.method.localpath(self, d) 1401 1402 def getSRCDate(self, d): 1403 """ 1404 Return the SRC Date for the component 1405 1406 d the bb.data module 1407 """ 1408 if "srcdate" in self.parm: 1409 return self.parm['srcdate'] 1410 1411 pn = d.getVar("PN") 1412 1413 if pn: 1414 return d.getVar("SRCDATE_%s" % pn) or d.getVar("SRCDATE") or d.getVar("DATE") 1415 1416 return d.getVar("SRCDATE") or d.getVar("DATE") 1417 1418class FetchMethod(object): 1419 """Base class for 'fetch'ing data""" 1420 1421 def __init__(self, urls=None): 1422 self.urls = [] 1423 1424 def supports(self, urldata, d): 1425 """ 1426 Check to see if this fetch class supports a given url. 1427 """ 1428 return 0 1429 1430 def localpath(self, urldata, d): 1431 """ 1432 Return the local filename of a given url assuming a successful fetch. 1433 Can also setup variables in urldata for use in go (saving code duplication 1434 and duplicate code execution) 1435 """ 1436 return os.path.join(d.getVar("DL_DIR"), urldata.localfile) 1437 1438 def supports_checksum(self, urldata): 1439 """ 1440 Is localpath something that can be represented by a checksum? 1441 """ 1442 1443 # We cannot compute checksums for None 1444 if urldata.localpath is None: 1445 return False 1446 # We cannot compute checksums for directories 1447 if os.path.isdir(urldata.localpath): 1448 return False 1449 return True 1450 1451 def recommends_checksum(self, urldata): 1452 """ 1453 Is the backend on where checksumming is recommended (should warnings 1454 be displayed if there is no checksum)? 1455 """ 1456 return False 1457 1458 def cleanup_upon_failure(self): 1459 """ 1460 When a fetch fails, should clean() be called? 1461 """ 1462 return True 1463 1464 def verify_donestamp(self, ud, d): 1465 """ 1466 Verify the donestamp file 1467 """ 1468 return verify_donestamp(ud, d) 1469 1470 def update_donestamp(self, ud, d): 1471 """ 1472 Update the donestamp file 1473 """ 1474 update_stamp(ud, d) 1475 1476 def _strip_leading_slashes(self, relpath): 1477 """ 1478 Remove leading slash as os.path.join can't cope 1479 """ 1480 while os.path.isabs(relpath): 1481 relpath = relpath[1:] 1482 return relpath 1483 1484 def setUrls(self, urls): 1485 self.__urls = urls 1486 1487 def getUrls(self): 1488 return self.__urls 1489 1490 urls = property(getUrls, setUrls, None, "Urls property") 1491 1492 def need_update(self, ud, d): 1493 """ 1494 Force a fetch, even if localpath exists? 1495 """ 1496 if os.path.exists(ud.localpath): 1497 return False 1498 return True 1499 1500 def supports_srcrev(self): 1501 """ 1502 The fetcher supports auto source revisions (SRCREV) 1503 """ 1504 return False 1505 1506 def download(self, urldata, d): 1507 """ 1508 Fetch urls 1509 Assumes localpath was called first 1510 """ 1511 raise NoMethodError(urldata.url) 1512 1513 def unpack(self, urldata, rootdir, data): 1514 iterate = False 1515 file = urldata.localpath 1516 1517 try: 1518 unpack = bb.utils.to_boolean(urldata.parm.get('unpack'), True) 1519 except ValueError as exc: 1520 bb.fatal("Invalid value for 'unpack' parameter for %s: %s" % 1521 (file, urldata.parm.get('unpack'))) 1522 1523 base, ext = os.path.splitext(file) 1524 if ext in ['.gz', '.bz2', '.Z', '.xz', '.lz', '.zst']: 1525 efile = os.path.join(rootdir, os.path.basename(base)) 1526 else: 1527 efile = file 1528 cmd = None 1529 1530 if unpack: 1531 tar_cmd = 'tar --extract --no-same-owner' 1532 if 'striplevel' in urldata.parm: 1533 tar_cmd += ' --strip-components=%s' % urldata.parm['striplevel'] 1534 if file.endswith('.tar'): 1535 cmd = '%s -f %s' % (tar_cmd, file) 1536 elif file.endswith('.tgz') or file.endswith('.tar.gz') or file.endswith('.tar.Z'): 1537 cmd = '%s -z -f %s' % (tar_cmd, file) 1538 elif file.endswith('.tbz') or file.endswith('.tbz2') or file.endswith('.tar.bz2'): 1539 cmd = 'bzip2 -dc %s | %s -f -' % (file, tar_cmd) 1540 elif file.endswith('.gz') or file.endswith('.Z') or file.endswith('.z'): 1541 cmd = 'gzip -dc %s > %s' % (file, efile) 1542 elif file.endswith('.bz2'): 1543 cmd = 'bzip2 -dc %s > %s' % (file, efile) 1544 elif file.endswith('.txz') or file.endswith('.tar.xz'): 1545 cmd = 'xz -dc %s | %s -f -' % (file, tar_cmd) 1546 elif file.endswith('.xz'): 1547 cmd = 'xz -dc %s > %s' % (file, efile) 1548 elif file.endswith('.tar.lz'): 1549 cmd = 'lzip -dc %s | %s -f -' % (file, tar_cmd) 1550 elif file.endswith('.lz'): 1551 cmd = 'lzip -dc %s > %s' % (file, efile) 1552 elif file.endswith('.tar.7z'): 1553 cmd = '7z x -so %s | %s -f -' % (file, tar_cmd) 1554 elif file.endswith('.7z'): 1555 cmd = '7za x -y %s 1>/dev/null' % file 1556 elif file.endswith('.tzst') or file.endswith('.tar.zst'): 1557 cmd = 'zstd --decompress --stdout %s | %s -f -' % (file, tar_cmd) 1558 elif file.endswith('.zst'): 1559 cmd = 'zstd --decompress --stdout %s > %s' % (file, efile) 1560 elif file.endswith('.zip') or file.endswith('.jar'): 1561 try: 1562 dos = bb.utils.to_boolean(urldata.parm.get('dos'), False) 1563 except ValueError as exc: 1564 bb.fatal("Invalid value for 'dos' parameter for %s: %s" % 1565 (file, urldata.parm.get('dos'))) 1566 cmd = 'unzip -q -o' 1567 if dos: 1568 cmd = '%s -a' % cmd 1569 cmd = "%s '%s'" % (cmd, file) 1570 elif file.endswith('.rpm') or file.endswith('.srpm'): 1571 if 'extract' in urldata.parm: 1572 unpack_file = urldata.parm.get('extract') 1573 cmd = 'rpm2cpio.sh %s | cpio -id %s' % (file, unpack_file) 1574 iterate = True 1575 iterate_file = unpack_file 1576 else: 1577 cmd = 'rpm2cpio.sh %s | cpio -id' % (file) 1578 elif file.endswith('.deb') or file.endswith('.ipk'): 1579 output = subprocess.check_output(['ar', '-t', file], preexec_fn=subprocess_setup) 1580 datafile = None 1581 if output: 1582 for line in output.decode().splitlines(): 1583 if line.startswith('data.tar.'): 1584 datafile = line 1585 break 1586 else: 1587 raise UnpackError("Unable to unpack deb/ipk package - does not contain data.tar.* file", urldata.url) 1588 else: 1589 raise UnpackError("Unable to unpack deb/ipk package - could not list contents", urldata.url) 1590 cmd = 'ar x %s %s && %s -p -f %s && rm %s' % (file, datafile, tar_cmd, datafile, datafile) 1591 1592 # If 'subdir' param exists, create a dir and use it as destination for unpack cmd 1593 if 'subdir' in urldata.parm: 1594 subdir = urldata.parm.get('subdir') 1595 if os.path.isabs(subdir): 1596 if not os.path.realpath(subdir).startswith(os.path.realpath(rootdir)): 1597 raise UnpackError("subdir argument isn't a subdirectory of unpack root %s" % rootdir, urldata.url) 1598 unpackdir = subdir 1599 else: 1600 unpackdir = os.path.join(rootdir, subdir) 1601 bb.utils.mkdirhier(unpackdir) 1602 else: 1603 unpackdir = rootdir 1604 1605 if not unpack or not cmd: 1606 urldata.unpack_tracer.unpack("file-copy", unpackdir) 1607 # If file == dest, then avoid any copies, as we already put the file into dest! 1608 dest = os.path.join(unpackdir, os.path.basename(file)) 1609 if file != dest and not (os.path.exists(dest) and os.path.samefile(file, dest)): 1610 destdir = '.' 1611 # For file:// entries all intermediate dirs in path must be created at destination 1612 if urldata.type == "file": 1613 # Trailing '/' does a copying to wrong place 1614 urlpath = urldata.path.rstrip('/') 1615 # Want files places relative to cwd so no leading '/' 1616 urlpath = urlpath.lstrip('/') 1617 if urlpath.find("/") != -1: 1618 destdir = urlpath.rsplit("/", 1)[0] + '/' 1619 bb.utils.mkdirhier("%s/%s" % (unpackdir, destdir)) 1620 cmd = 'cp --force --preserve=timestamps --no-dereference --recursive -H "%s" "%s"' % (file, destdir) 1621 else: 1622 urldata.unpack_tracer.unpack("archive-extract", unpackdir) 1623 1624 if not cmd: 1625 return 1626 1627 path = data.getVar('PATH') 1628 if path: 1629 cmd = "PATH=\"%s\" %s" % (path, cmd) 1630 bb.note("Unpacking %s to %s/" % (file, unpackdir)) 1631 ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True, cwd=unpackdir) 1632 1633 if ret != 0: 1634 raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), urldata.url) 1635 1636 if iterate is True: 1637 iterate_urldata = urldata 1638 iterate_urldata.localpath = "%s/%s" % (rootdir, iterate_file) 1639 self.unpack(urldata, rootdir, data) 1640 1641 return 1642 1643 def clean(self, urldata, d): 1644 """ 1645 Clean any existing full or partial download 1646 """ 1647 bb.utils.remove(urldata.localpath) 1648 1649 def try_premirror(self, urldata, d): 1650 """ 1651 Should premirrors be used? 1652 """ 1653 return True 1654 1655 def try_mirrors(self, fetch, urldata, d, mirrors, check=False): 1656 """ 1657 Try to use a mirror 1658 """ 1659 return bool(try_mirrors(fetch, d, urldata, mirrors, check)) 1660 1661 def checkstatus(self, fetch, urldata, d): 1662 """ 1663 Check the status of a URL 1664 Assumes localpath was called first 1665 """ 1666 logger.info("URL %s could not be checked for status since no method exists.", urldata.url) 1667 return True 1668 1669 def latest_revision(self, ud, d, name): 1670 """ 1671 Look in the cache for the latest revision, if not present ask the SCM. 1672 """ 1673 if not hasattr(self, "_latest_revision"): 1674 raise ParameterError("The fetcher for this URL does not support _latest_revision", ud.url) 1675 1676 key = self.generate_revision_key(ud, d, name) 1677 1678 rev = _revisions_cache.get_rev(key) 1679 if rev is None: 1680 rev = self._latest_revision(ud, d, name) 1681 _revisions_cache.set_rev(key, rev) 1682 return rev 1683 1684 def sortable_revision(self, ud, d, name): 1685 latest_rev = self._build_revision(ud, d, name) 1686 return True, str(latest_rev) 1687 1688 def generate_revision_key(self, ud, d, name): 1689 return self._revision_key(ud, d, name) 1690 1691 def latest_versionstring(self, ud, d): 1692 """ 1693 Compute the latest release name like "x.y.x" in "x.y.x+gitHASH" 1694 by searching through the tags output of ls-remote, comparing 1695 versions and returning the highest match as a (version, revision) pair. 1696 """ 1697 return ('', '') 1698 1699 def done(self, ud, d): 1700 """ 1701 Is the download done ? 1702 """ 1703 if os.path.exists(ud.localpath): 1704 return True 1705 return False 1706 1707 def implicit_urldata(self, ud, d): 1708 """ 1709 Get a list of FetchData objects for any implicit URLs that will also 1710 be downloaded when we fetch the given URL. 1711 """ 1712 return [] 1713 1714 1715class DummyUnpackTracer(object): 1716 """ 1717 Abstract API definition for a class that traces unpacked source files back 1718 to their respective upstream SRC_URI entries, for software composition 1719 analysis, license compliance and detailed SBOM generation purposes. 1720 User may load their own unpack tracer class (instead of the dummy 1721 one) by setting the BB_UNPACK_TRACER_CLASS config parameter. 1722 """ 1723 def start(self, unpackdir, urldata_dict, d): 1724 """ 1725 Start tracing the core Fetch.unpack process, using an index to map 1726 unpacked files to each SRC_URI entry. 1727 This method is called by Fetch.unpack and it may receive nested calls by 1728 gitsm and npmsw fetchers, that expand SRC_URI entries by adding implicit 1729 URLs and by recursively calling Fetch.unpack from new (nested) Fetch 1730 instances. 1731 """ 1732 return 1733 def start_url(self, url): 1734 """Start tracing url unpack process. 1735 This method is called by Fetch.unpack before the fetcher-specific unpack 1736 method starts, and it may receive nested calls by gitsm and npmsw 1737 fetchers. 1738 """ 1739 return 1740 def unpack(self, unpack_type, destdir): 1741 """ 1742 Set unpack_type and destdir for current url. 1743 This method is called by the fetcher-specific unpack method after url 1744 tracing started. 1745 """ 1746 return 1747 def finish_url(self, url): 1748 """Finish tracing url unpack process and update the file index. 1749 This method is called by Fetch.unpack after the fetcher-specific unpack 1750 method finished its job, and it may receive nested calls by gitsm 1751 and npmsw fetchers. 1752 """ 1753 return 1754 def complete(self): 1755 """ 1756 Finish tracing the Fetch.unpack process, and check if all nested 1757 Fecth.unpack calls (if any) have been completed; if so, save collected 1758 metadata. 1759 """ 1760 return 1761 1762 1763class Fetch(object): 1764 def __init__(self, urls, d, cache = True, localonly = False, connection_cache = None): 1765 if localonly and cache: 1766 raise Exception("bb.fetch2.Fetch.__init__: cannot set cache and localonly at same time") 1767 1768 if not urls: 1769 urls = d.getVar("SRC_URI").split() 1770 self.urls = urls 1771 self.d = d 1772 self.ud = {} 1773 self.connection_cache = connection_cache 1774 1775 fn = d.getVar('FILE') 1776 mc = d.getVar('__BBMULTICONFIG') or "" 1777 key = None 1778 if cache and fn: 1779 key = mc + fn + str(id(d)) 1780 if key in urldata_cache: 1781 self.ud = urldata_cache[key] 1782 1783 # the unpack_tracer object needs to be made available to possible nested 1784 # Fetch instances (when those are created by gitsm and npmsw fetchers) 1785 # so we set it as a global variable 1786 global unpack_tracer 1787 try: 1788 unpack_tracer 1789 except NameError: 1790 class_path = d.getVar("BB_UNPACK_TRACER_CLASS") 1791 if class_path: 1792 # use user-defined unpack tracer class 1793 import importlib 1794 module_name, _, class_name = class_path.rpartition(".") 1795 module = importlib.import_module(module_name) 1796 class_ = getattr(module, class_name) 1797 unpack_tracer = class_() 1798 else: 1799 # fall back to the dummy/abstract class 1800 unpack_tracer = DummyUnpackTracer() 1801 1802 for url in urls: 1803 if url not in self.ud: 1804 try: 1805 self.ud[url] = FetchData(url, d, localonly) 1806 self.ud[url].unpack_tracer = unpack_tracer 1807 except NonLocalMethod: 1808 if localonly: 1809 self.ud[url] = None 1810 pass 1811 1812 if key: 1813 urldata_cache[key] = self.ud 1814 1815 def localpath(self, url): 1816 if url not in self.urls: 1817 self.ud[url] = FetchData(url, self.d) 1818 1819 self.ud[url].setup_localpath(self.d) 1820 return self.d.expand(self.ud[url].localpath) 1821 1822 def localpaths(self): 1823 """ 1824 Return a list of the local filenames, assuming successful fetch 1825 """ 1826 local = [] 1827 1828 for u in self.urls: 1829 ud = self.ud[u] 1830 ud.setup_localpath(self.d) 1831 local.append(ud.localpath) 1832 1833 return local 1834 1835 def download(self, urls=None): 1836 """ 1837 Fetch all urls 1838 """ 1839 if not urls: 1840 urls = self.urls 1841 1842 network = self.d.getVar("BB_NO_NETWORK") 1843 premirroronly = bb.utils.to_boolean(self.d.getVar("BB_FETCH_PREMIRRORONLY")) 1844 1845 checksum_missing_messages = [] 1846 for u in urls: 1847 ud = self.ud[u] 1848 ud.setup_localpath(self.d) 1849 m = ud.method 1850 done = False 1851 1852 if ud.lockfile: 1853 lf = bb.utils.lockfile(ud.lockfile) 1854 1855 try: 1856 self.d.setVar("BB_NO_NETWORK", network) 1857 if m.verify_donestamp(ud, self.d) and not m.need_update(ud, self.d): 1858 done = True 1859 elif m.try_premirror(ud, self.d): 1860 logger.debug("Trying PREMIRRORS") 1861 mirrors = mirror_from_string(self.d.getVar('PREMIRRORS')) 1862 done = m.try_mirrors(self, ud, self.d, mirrors) 1863 if done: 1864 try: 1865 # early checksum verification so that if the checksum of the premirror 1866 # contents mismatch the fetcher can still try upstream and mirrors 1867 m.update_donestamp(ud, self.d) 1868 except ChecksumError as e: 1869 logger.warning("Checksum failure encountered with premirror download of %s - will attempt other sources." % u) 1870 logger.debug(str(e)) 1871 done = False 1872 1873 if premirroronly: 1874 self.d.setVar("BB_NO_NETWORK", "1") 1875 1876 firsterr = None 1877 verified_stamp = False 1878 if done: 1879 verified_stamp = m.verify_donestamp(ud, self.d) 1880 if not done and (not verified_stamp or m.need_update(ud, self.d)): 1881 try: 1882 if not trusted_network(self.d, ud.url): 1883 raise UntrustedUrl(ud.url) 1884 logger.debug("Trying Upstream") 1885 m.download(ud, self.d) 1886 if hasattr(m, "build_mirror_data"): 1887 m.build_mirror_data(ud, self.d) 1888 done = True 1889 # early checksum verify, so that if checksum mismatched, 1890 # fetcher still have chance to fetch from mirror 1891 m.update_donestamp(ud, self.d) 1892 1893 except bb.fetch2.NetworkAccess: 1894 raise 1895 1896 except BBFetchException as e: 1897 if isinstance(e, ChecksumError): 1898 logger.warning("Checksum failure encountered with download of %s - will attempt other sources if available" % u) 1899 logger.debug(str(e)) 1900 if os.path.exists(ud.localpath): 1901 rename_bad_checksum(ud, e.checksum) 1902 elif isinstance(e, NoChecksumError): 1903 raise 1904 else: 1905 logger.warning('Failed to fetch URL %s, attempting MIRRORS if available' % u) 1906 logger.debug(str(e)) 1907 firsterr = e 1908 # Remove any incomplete fetch 1909 if not verified_stamp and m.cleanup_upon_failure(): 1910 m.clean(ud, self.d) 1911 logger.debug("Trying MIRRORS") 1912 mirrors = mirror_from_string(self.d.getVar('MIRRORS')) 1913 done = m.try_mirrors(self, ud, self.d, mirrors) 1914 1915 if not done or not m.done(ud, self.d): 1916 if firsterr: 1917 logger.error(str(firsterr)) 1918 raise FetchError("Unable to fetch URL from any source.", u) 1919 1920 m.update_donestamp(ud, self.d) 1921 1922 except IOError as e: 1923 if e.errno in [errno.ESTALE]: 1924 logger.error("Stale Error Observed %s." % u) 1925 raise ChecksumError("Stale Error Detected") 1926 1927 except BBFetchException as e: 1928 if isinstance(e, NoChecksumError): 1929 (message, _) = e.args 1930 checksum_missing_messages.append(message) 1931 continue 1932 elif isinstance(e, ChecksumError): 1933 logger.error("Checksum failure fetching %s" % u) 1934 raise 1935 1936 finally: 1937 if ud.lockfile: 1938 bb.utils.unlockfile(lf) 1939 if checksum_missing_messages: 1940 logger.error("Missing SRC_URI checksum, please add those to the recipe: \n%s", "\n".join(checksum_missing_messages)) 1941 raise BBFetchException("There was some missing checksums in the recipe") 1942 1943 def checkstatus(self, urls=None): 1944 """ 1945 Check all URLs exist upstream. 1946 1947 Returns None if the URLs exist, raises FetchError if the check wasn't 1948 successful but there wasn't an error (such as file not found), and 1949 raises other exceptions in error cases. 1950 """ 1951 1952 if not urls: 1953 urls = self.urls 1954 1955 for u in urls: 1956 ud = self.ud[u] 1957 ud.setup_localpath(self.d) 1958 m = ud.method 1959 logger.debug("Testing URL %s", u) 1960 # First try checking uri, u, from PREMIRRORS 1961 mirrors = mirror_from_string(self.d.getVar('PREMIRRORS')) 1962 ret = m.try_mirrors(self, ud, self.d, mirrors, True) 1963 if not ret: 1964 # Next try checking from the original uri, u 1965 ret = m.checkstatus(self, ud, self.d) 1966 if not ret: 1967 # Finally, try checking uri, u, from MIRRORS 1968 mirrors = mirror_from_string(self.d.getVar('MIRRORS')) 1969 ret = m.try_mirrors(self, ud, self.d, mirrors, True) 1970 1971 if not ret: 1972 raise FetchError("URL doesn't work", u) 1973 1974 def unpack(self, root, urls=None): 1975 """ 1976 Unpack urls to root 1977 """ 1978 1979 if not urls: 1980 urls = self.urls 1981 1982 unpack_tracer.start(root, self.ud, self.d) 1983 1984 for u in urls: 1985 ud = self.ud[u] 1986 ud.setup_localpath(self.d) 1987 1988 if ud.lockfile: 1989 lf = bb.utils.lockfile(ud.lockfile) 1990 1991 unpack_tracer.start_url(u) 1992 ud.method.unpack(ud, root, self.d) 1993 unpack_tracer.finish_url(u) 1994 1995 if ud.lockfile: 1996 bb.utils.unlockfile(lf) 1997 1998 unpack_tracer.complete() 1999 2000 def clean(self, urls=None): 2001 """ 2002 Clean files that the fetcher gets or places 2003 """ 2004 2005 if not urls: 2006 urls = self.urls 2007 2008 for url in urls: 2009 if url not in self.ud: 2010 self.ud[url] = FetchData(url, self.d) 2011 ud = self.ud[url] 2012 ud.setup_localpath(self.d) 2013 2014 if not ud.localfile and ud.localpath is None: 2015 continue 2016 2017 if ud.lockfile: 2018 lf = bb.utils.lockfile(ud.lockfile) 2019 2020 ud.method.clean(ud, self.d) 2021 if ud.donestamp: 2022 bb.utils.remove(ud.donestamp) 2023 2024 if ud.lockfile: 2025 bb.utils.unlockfile(lf) 2026 2027 def expanded_urldata(self, urls=None): 2028 """ 2029 Get an expanded list of FetchData objects covering both the given 2030 URLS and any additional implicit URLs that are added automatically by 2031 the appropriate FetchMethod. 2032 """ 2033 2034 if not urls: 2035 urls = self.urls 2036 2037 urldata = [] 2038 for url in urls: 2039 ud = self.ud[url] 2040 urldata.append(ud) 2041 urldata += ud.method.implicit_urldata(ud, self.d) 2042 2043 return urldata 2044 2045class FetchConnectionCache(object): 2046 """ 2047 A class which represents an container for socket connections. 2048 """ 2049 def __init__(self): 2050 self.cache = {} 2051 2052 def get_connection_name(self, host, port): 2053 return host + ':' + str(port) 2054 2055 def add_connection(self, host, port, connection): 2056 cn = self.get_connection_name(host, port) 2057 2058 if cn not in self.cache: 2059 self.cache[cn] = connection 2060 2061 def get_connection(self, host, port): 2062 connection = None 2063 2064 cn = self.get_connection_name(host, port) 2065 if cn in self.cache: 2066 connection = self.cache[cn] 2067 2068 return connection 2069 2070 def remove_connection(self, host, port): 2071 cn = self.get_connection_name(host, port) 2072 if cn in self.cache: 2073 self.cache[cn].close() 2074 del self.cache[cn] 2075 2076 def close_connections(self): 2077 for cn in list(self.cache.keys()): 2078 self.cache[cn].close() 2079 del self.cache[cn] 2080 2081from . import cvs 2082from . import git 2083from . import gitsm 2084from . import gitannex 2085from . import local 2086from . import svn 2087from . import wget 2088from . import ssh 2089from . import sftp 2090from . import s3 2091from . import perforce 2092from . import bzr 2093from . import hg 2094from . import osc 2095from . import repo 2096from . import clearcase 2097from . import npm 2098from . import npmsw 2099from . import az 2100from . import crate 2101from . import gcp 2102from . import gomod 2103 2104methods.append(local.Local()) 2105methods.append(wget.Wget()) 2106methods.append(svn.Svn()) 2107methods.append(git.Git()) 2108methods.append(gitsm.GitSM()) 2109methods.append(gitannex.GitANNEX()) 2110methods.append(cvs.Cvs()) 2111methods.append(ssh.SSH()) 2112methods.append(sftp.SFTP()) 2113methods.append(s3.S3()) 2114methods.append(perforce.Perforce()) 2115methods.append(bzr.Bzr()) 2116methods.append(hg.Hg()) 2117methods.append(osc.Osc()) 2118methods.append(repo.Repo()) 2119methods.append(clearcase.ClearCase()) 2120methods.append(npm.Npm()) 2121methods.append(npmsw.NpmShrinkWrap()) 2122methods.append(az.Az()) 2123methods.append(crate.Crate()) 2124methods.append(gcp.GCP()) 2125methods.append(gomod.GoMod()) 2126methods.append(gomod.GoModGit()) 2127