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