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 and len(urldata[scms[0]].names) == 1: 810 autoinc, rev = getattr(urldata[scms[0]].method, method_name)(urldata[scms[0]], d, urldata[scms[0]].names[0]) 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 for name in ud.names: 832 autoinc, rev = getattr(ud.method, method_name)(ud, d, name) 833 revs.append(rev) 834 seenautoinc = seenautoinc or autoinc 835 if len(rev) > 10: 836 rev = rev[:10] 837 name_to_rev[name] = rev 838 # Replace names by revisions in the SRCREV_FORMAT string. The approach used 839 # here can handle names being prefixes of other names and names appearing 840 # as substrings in revisions (in which case the name should not be 841 # expanded). The '|' regular expression operator tries matches from left to 842 # right, so we need to sort the names with the longest ones first. 843 names_descending_len = sorted(name_to_rev, key=len, reverse=True) 844 name_to_rev_re = "|".join(re.escape(name) for name in names_descending_len) 845 format = re.sub(name_to_rev_re, lambda match: name_to_rev[match.group(0)], format) 846 847 if seenautoinc: 848 format = "AUTOINC+" + format 849 850 d.delVar("__BBINSRCREV") 851 return format, revs 852 853def get_hashvalue(d, method_name='sortable_revision'): 854 pkgv, revs = _get_srcrev(d, method_name=method_name) 855 return " ".join(revs) 856 857def get_pkgv_string(d, method_name='sortable_revision'): 858 pkgv, revs = _get_srcrev(d, method_name=method_name) 859 return pkgv 860 861def get_srcrev(d, method_name='sortable_revision'): 862 pkgv, revs = _get_srcrev(d, method_name=method_name) 863 if not pkgv: 864 raise FetchError("SRCREV was used yet no valid SCM was found in SRC_URI") 865 return pkgv 866 867def localpath(url, d): 868 fetcher = bb.fetch2.Fetch([url], d) 869 return fetcher.localpath(url) 870 871# Need to export PATH as binary could be in metadata paths 872# rather than host provided 873# Also include some other variables. 874FETCH_EXPORT_VARS = ['HOME', 'PATH', 875 'HTTP_PROXY', 'http_proxy', 876 'HTTPS_PROXY', 'https_proxy', 877 'FTP_PROXY', 'ftp_proxy', 878 'FTPS_PROXY', 'ftps_proxy', 879 'NO_PROXY', 'no_proxy', 880 'ALL_PROXY', 'all_proxy', 881 'GIT_PROXY_COMMAND', 882 'GIT_SSH', 883 'GIT_SSH_COMMAND', 884 'GIT_SSL_CAINFO', 885 'GIT_SMART_HTTP', 886 'SSH_AUTH_SOCK', 'SSH_AGENT_PID', 887 'SOCKS5_USER', 'SOCKS5_PASSWD', 888 'DBUS_SESSION_BUS_ADDRESS', 889 'P4CONFIG', 890 'SSL_CERT_FILE', 891 'NODE_EXTRA_CA_CERTS', 892 'AWS_PROFILE', 893 'AWS_ACCESS_KEY_ID', 894 'AWS_SECRET_ACCESS_KEY', 895 'AWS_ROLE_ARN', 896 'AWS_WEB_IDENTITY_TOKEN_FILE', 897 'AWS_DEFAULT_REGION', 898 'AWS_SESSION_TOKEN', 899 'GIT_CACHE_PATH', 900 'REMOTE_CONTAINERS_IPC', 901 'GITHUB_TOKEN', 902 'SSL_CERT_DIR'] 903 904def get_fetcher_environment(d): 905 newenv = {} 906 origenv = d.getVar("BB_ORIGENV") 907 for name in bb.fetch2.FETCH_EXPORT_VARS: 908 value = d.getVar(name) 909 if not value and origenv: 910 value = origenv.getVar(name) 911 if value: 912 newenv[name] = value 913 return newenv 914 915def runfetchcmd(cmd, d, quiet=False, cleanup=None, log=None, workdir=None): 916 """ 917 Run cmd returning the command output 918 Raise an error if interrupted or cmd fails 919 Optionally echo command output to stdout 920 Optionally remove the files/directories listed in cleanup upon failure 921 """ 922 923 exportvars = FETCH_EXPORT_VARS 924 925 if not cleanup: 926 cleanup = [] 927 928 # If PATH contains WORKDIR which contains PV-PR which contains SRCPV we 929 # can end up in circular recursion here so give the option of breaking it 930 # in a data store copy. 931 try: 932 d.getVar("PV") 933 d.getVar("PR") 934 except bb.data_smart.ExpansionError: 935 d = bb.data.createCopy(d) 936 d.setVar("PV", "fetcheravoidrecurse") 937 d.setVar("PR", "fetcheravoidrecurse") 938 939 origenv = d.getVar("BB_ORIGENV", False) 940 for var in exportvars: 941 val = d.getVar(var) or (origenv and origenv.getVar(var)) 942 if val: 943 cmd = 'export ' + var + '=\"%s\"; %s' % (val, cmd) 944 945 # Disable pseudo as it may affect ssh, potentially causing it to hang. 946 cmd = 'export PSEUDO_DISABLED=1; ' + cmd 947 948 if workdir: 949 logger.debug("Running '%s' in %s" % (cmd, workdir)) 950 else: 951 logger.debug("Running %s", cmd) 952 953 success = False 954 error_message = "" 955 956 try: 957 (output, errors) = bb.process.run(cmd, log=log, shell=True, stderr=subprocess.PIPE, cwd=workdir) 958 success = True 959 except bb.process.NotFoundError as e: 960 error_message = "Fetch command %s not found" % (e.command) 961 except bb.process.ExecutionError as e: 962 if e.stdout: 963 output = "output:\n%s\n%s" % (e.stdout, e.stderr) 964 elif e.stderr: 965 output = "output:\n%s" % e.stderr 966 else: 967 if log: 968 output = "see logfile for output" 969 else: 970 output = "no output" 971 error_message = "Fetch command %s failed with exit code %s, %s" % (e.command, e.exitcode, output) 972 except bb.process.CmdError as e: 973 error_message = "Fetch command %s could not be run:\n%s" % (e.command, e.msg) 974 if not success: 975 for f in cleanup: 976 try: 977 bb.utils.remove(f, True) 978 except OSError: 979 pass 980 981 raise FetchError(error_message) 982 983 return output 984 985def check_network_access(d, info, url): 986 """ 987 log remote network access, and error if BB_NO_NETWORK is set or the given 988 URI is untrusted 989 """ 990 if bb.utils.to_boolean(d.getVar("BB_NO_NETWORK")): 991 raise NetworkAccess(url, info) 992 elif not trusted_network(d, url): 993 raise UntrustedUrl(url, info) 994 else: 995 logger.debug("Fetcher accessed the network with the command %s" % info) 996 997def build_mirroruris(origud, mirrors, ld): 998 uris = [] 999 uds = [] 1000 1001 replacements = {} 1002 replacements["TYPE"] = origud.type 1003 replacements["HOST"] = origud.host 1004 replacements["PATH"] = origud.path 1005 replacements["BASENAME"] = origud.path.split("/")[-1] 1006 replacements["MIRRORNAME"] = origud.host.replace(':','.') + origud.path.replace('/', '.').replace('*', '.') 1007 1008 def adduri(ud, uris, uds, mirrors, tarballs): 1009 for line in mirrors: 1010 try: 1011 (find, replace) = line 1012 except ValueError: 1013 continue 1014 1015 for tarball in tarballs: 1016 newuri = uri_replace(ud, find, replace, replacements, ld, tarball) 1017 if not newuri or newuri in uris or newuri == origud.url: 1018 continue 1019 1020 if not trusted_network(ld, newuri): 1021 logger.debug("Mirror %s not in the list of trusted networks, skipping" % (newuri)) 1022 continue 1023 1024 # Create a local copy of the mirrors minus the current line 1025 # this will prevent us from recursively processing the same line 1026 # as well as indirect recursion A -> B -> C -> A 1027 localmirrors = list(mirrors) 1028 localmirrors.remove(line) 1029 1030 try: 1031 newud = FetchData(newuri, ld) 1032 newud.ignore_checksums = True 1033 newud.setup_localpath(ld) 1034 except bb.fetch2.BBFetchException as e: 1035 logger.debug("Mirror fetch failure for url %s (original url: %s)" % (newuri, origud.url)) 1036 logger.debug(str(e)) 1037 try: 1038 # setup_localpath of file:// urls may fail, we should still see 1039 # if mirrors of the url exist 1040 adduri(newud, uris, uds, localmirrors, tarballs) 1041 except UnboundLocalError: 1042 pass 1043 continue 1044 uris.append(newuri) 1045 uds.append(newud) 1046 1047 adduri(newud, uris, uds, localmirrors, tarballs) 1048 1049 adduri(origud, uris, uds, mirrors, origud.mirrortarballs or [None]) 1050 1051 return uris, uds 1052 1053def rename_bad_checksum(ud, suffix): 1054 """ 1055 Renames files to have suffix from parameter 1056 """ 1057 1058 if ud.localpath is None: 1059 return 1060 1061 new_localpath = "%s_bad-checksum_%s" % (ud.localpath, suffix) 1062 bb.warn("Renaming %s to %s" % (ud.localpath, new_localpath)) 1063 if not bb.utils.movefile(ud.localpath, new_localpath): 1064 bb.warn("Renaming %s to %s failed, grep movefile in log.do_fetch to see why" % (ud.localpath, new_localpath)) 1065 1066 1067def try_mirror_url(fetch, origud, ud, ld, check = False): 1068 # Return of None or a value means we're finished 1069 # False means try another url 1070 1071 if ud.lockfile and ud.lockfile != origud.lockfile: 1072 lf = bb.utils.lockfile(ud.lockfile) 1073 1074 try: 1075 if check: 1076 found = ud.method.checkstatus(fetch, ud, ld) 1077 if found: 1078 return found 1079 return False 1080 1081 if not verify_donestamp(ud, ld, origud) or ud.method.need_update(ud, ld): 1082 ud.method.download(ud, ld) 1083 if hasattr(ud.method,"build_mirror_data"): 1084 ud.method.build_mirror_data(ud, ld) 1085 1086 if not ud.localpath or not os.path.exists(ud.localpath): 1087 return False 1088 1089 if ud.localpath == origud.localpath: 1090 return ud.localpath 1091 1092 # We may be obtaining a mirror tarball which needs further processing by the real fetcher 1093 # If that tarball is a local file:// we need to provide a symlink to it 1094 dldir = ld.getVar("DL_DIR") 1095 1096 if origud.mirrortarballs and os.path.basename(ud.localpath) in origud.mirrortarballs and os.path.basename(ud.localpath) != os.path.basename(origud.localpath): 1097 # Create donestamp in old format to avoid triggering a re-download 1098 if ud.donestamp: 1099 bb.utils.mkdirhier(os.path.dirname(ud.donestamp)) 1100 open(ud.donestamp, 'w').close() 1101 dest = os.path.join(dldir, os.path.basename(ud.localpath)) 1102 if not os.path.exists(dest): 1103 # In case this is executing without any file locks held (as is 1104 # the case for file:// URLs), two tasks may end up here at the 1105 # same time, in which case we do not want the second task to 1106 # fail when the link has already been created by the first task. 1107 try: 1108 os.symlink(ud.localpath, dest) 1109 except FileExistsError: 1110 pass 1111 if not verify_donestamp(origud, ld) or origud.method.need_update(origud, ld): 1112 origud.method.download(origud, ld) 1113 if hasattr(origud.method, "build_mirror_data"): 1114 origud.method.build_mirror_data(origud, ld) 1115 return origud.localpath 1116 # Otherwise the result is a local file:// and we symlink to it 1117 ensure_symlink(ud.localpath, origud.localpath) 1118 update_stamp(origud, ld) 1119 return ud.localpath 1120 1121 except bb.fetch2.NetworkAccess: 1122 raise 1123 1124 except IOError as e: 1125 if e.errno in [errno.ESTALE]: 1126 logger.warning("Stale Error Observed %s." % ud.url) 1127 return False 1128 raise 1129 1130 except bb.fetch2.BBFetchException as e: 1131 if isinstance(e, ChecksumError): 1132 logger.warning("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (ud.url, origud.url)) 1133 logger.warning(str(e)) 1134 if os.path.exists(ud.localpath): 1135 rename_bad_checksum(ud, e.checksum) 1136 elif isinstance(e, NoChecksumError): 1137 raise 1138 else: 1139 logger.debug("Mirror fetch failure for url %s (original url: %s)" % (ud.url, origud.url)) 1140 logger.debug(str(e)) 1141 try: 1142 if ud.method.cleanup_upon_failure(): 1143 ud.method.clean(ud, ld) 1144 except UnboundLocalError: 1145 pass 1146 return False 1147 finally: 1148 if ud.lockfile and ud.lockfile != origud.lockfile: 1149 bb.utils.unlockfile(lf) 1150 1151 1152def ensure_symlink(target, link_name): 1153 if not os.path.exists(link_name): 1154 dirname = os.path.dirname(link_name) 1155 bb.utils.mkdirhier(dirname) 1156 if os.path.islink(link_name): 1157 # Broken symbolic link 1158 os.unlink(link_name) 1159 1160 # In case this is executing without any file locks held (as is 1161 # the case for file:// URLs), two tasks may end up here at the 1162 # same time, in which case we do not want the second task to 1163 # fail when the link has already been created by the first task. 1164 try: 1165 os.symlink(target, link_name) 1166 except FileExistsError: 1167 pass 1168 1169 1170def try_mirrors(fetch, d, origud, mirrors, check = False): 1171 """ 1172 Try to use a mirrored version of the sources. 1173 This method will be automatically called before the fetchers go. 1174 1175 d Is a bb.data instance 1176 uri is the original uri we're trying to download 1177 mirrors is the list of mirrors we're going to try 1178 """ 1179 ld = d.createCopy() 1180 1181 uris, uds = build_mirroruris(origud, mirrors, ld) 1182 1183 for index, uri in enumerate(uris): 1184 ret = try_mirror_url(fetch, origud, uds[index], ld, check) 1185 if ret: 1186 return ret 1187 return None 1188 1189def trusted_network(d, url): 1190 """ 1191 Use a trusted url during download if networking is enabled and 1192 BB_ALLOWED_NETWORKS is set globally or for a specific recipe. 1193 Note: modifies SRC_URI & mirrors. 1194 """ 1195 if bb.utils.to_boolean(d.getVar("BB_NO_NETWORK")): 1196 return True 1197 1198 pkgname = d.getVar('PN') 1199 trusted_hosts = None 1200 if pkgname: 1201 trusted_hosts = d.getVarFlag('BB_ALLOWED_NETWORKS', pkgname, False) 1202 1203 if not trusted_hosts: 1204 trusted_hosts = d.getVar('BB_ALLOWED_NETWORKS') 1205 1206 # Not enabled. 1207 if not trusted_hosts: 1208 return True 1209 1210 scheme, network, path, user, passwd, param = decodeurl(url) 1211 1212 if not network: 1213 return True 1214 1215 network = network.split(':')[0] 1216 network = network.lower() 1217 1218 for host in trusted_hosts.split(" "): 1219 host = host.lower() 1220 if host.startswith("*.") and ("." + network).endswith(host[1:]): 1221 return True 1222 if host == network: 1223 return True 1224 1225 return False 1226 1227def srcrev_internal_helper(ud, d, name): 1228 """ 1229 Return: 1230 a) a source revision if specified 1231 b) latest revision if SRCREV="AUTOINC" 1232 c) None if not specified 1233 """ 1234 1235 srcrev = None 1236 pn = d.getVar("PN") 1237 attempts = [] 1238 if name != '' and pn: 1239 attempts.append("SRCREV_%s:pn-%s" % (name, pn)) 1240 if name != '': 1241 attempts.append("SRCREV_%s" % name) 1242 if pn: 1243 attempts.append("SRCREV:pn-%s" % pn) 1244 attempts.append("SRCREV") 1245 1246 for a in attempts: 1247 srcrev = d.getVar(a) 1248 if srcrev and srcrev != "INVALID": 1249 break 1250 1251 if 'rev' in ud.parm and 'tag' in ud.parm: 1252 raise FetchError("Please specify a ;rev= parameter or a ;tag= parameter in the url %s but not both." % (ud.url)) 1253 1254 if 'rev' in ud.parm or 'tag' in ud.parm: 1255 if 'rev' in ud.parm: 1256 parmrev = ud.parm['rev'] 1257 else: 1258 parmrev = ud.parm['tag'] 1259 if srcrev == "INVALID" or not srcrev: 1260 return parmrev 1261 if srcrev != parmrev: 1262 raise FetchError("Conflicting revisions (%s from SRCREV and %s from the url) found, please specify one valid value" % (srcrev, parmrev)) 1263 return parmrev 1264 1265 if srcrev == "INVALID" or not srcrev: 1266 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) 1267 if srcrev == "AUTOINC": 1268 d.setVar("__BBAUTOREV_ACTED_UPON", True) 1269 srcrev = ud.method.latest_revision(ud, d, name) 1270 1271 return srcrev 1272 1273def get_checksum_file_list(d): 1274 """ Get a list of files checksum in SRC_URI 1275 1276 Returns the resolved local paths of all local file entries in 1277 SRC_URI as a space-separated string 1278 """ 1279 fetch = Fetch([], d, cache = False, localonly = True) 1280 filelist = [] 1281 for u in fetch.urls: 1282 ud = fetch.ud[u] 1283 if ud and isinstance(ud.method, local.Local): 1284 found = False 1285 paths = ud.method.localfile_searchpaths(ud, d) 1286 for f in paths: 1287 pth = ud.path 1288 if os.path.exists(f): 1289 found = True 1290 filelist.append(f + ":" + str(os.path.exists(f))) 1291 if not found: 1292 bb.fatal(("Unable to get checksum for %s SRC_URI entry %s: file could not be found" 1293 "\nThe following paths were searched:" 1294 "\n%s") % (d.getVar('PN'), os.path.basename(f), '\n'.join(paths))) 1295 1296 return " ".join(filelist) 1297 1298def get_file_checksums(filelist, pn, localdirsexclude): 1299 """Get a list of the checksums for a list of local files 1300 1301 Returns the checksums for a list of local files, caching the results as 1302 it proceeds 1303 1304 """ 1305 return _checksum_cache.get_checksums(filelist, pn, localdirsexclude) 1306 1307 1308class FetchData(object): 1309 """ 1310 A class which represents the fetcher state for a given URI. 1311 """ 1312 def __init__(self, url, d, localonly = False): 1313 # localpath is the location of a downloaded result. If not set, the file is local. 1314 self.donestamp = None 1315 self.needdonestamp = True 1316 self.localfile = "" 1317 self.localpath = None 1318 self.lockfile = None 1319 self.mirrortarballs = [] 1320 self.basename = None 1321 self.basepath = None 1322 (self.type, self.host, self.path, self.user, self.pswd, self.parm) = decodeurl(d.expand(url)) 1323 self.date = self.getSRCDate(d) 1324 self.url = url 1325 if not self.user and "user" in self.parm: 1326 self.user = self.parm["user"] 1327 if not self.pswd and "pswd" in self.parm: 1328 self.pswd = self.parm["pswd"] 1329 self.setup = False 1330 1331 def configure_checksum(checksum_id): 1332 checksum_plain_name = "%ssum" % checksum_id 1333 if "name" in self.parm: 1334 checksum_name = "%s.%ssum" % (self.parm["name"], checksum_id) 1335 else: 1336 checksum_name = checksum_plain_name 1337 1338 if checksum_name in self.parm: 1339 checksum_expected = self.parm[checksum_name] 1340 elif checksum_plain_name in self.parm: 1341 checksum_expected = self.parm[checksum_plain_name] 1342 checksum_name = checksum_plain_name 1343 elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3", "az", "crate", "gs", "gomod", "npm"]: 1344 checksum_expected = None 1345 else: 1346 checksum_expected = d.getVarFlag("SRC_URI", checksum_name) 1347 1348 setattr(self, "%s_name" % checksum_id, checksum_name) 1349 setattr(self, "%s_expected" % checksum_id, checksum_expected) 1350 1351 self.names = self.parm.get("name",'default').split(',') 1352 1353 self.method = None 1354 for m in methods: 1355 if m.supports(self, d): 1356 self.method = m 1357 break 1358 1359 if not self.method: 1360 raise NoMethodError(url) 1361 1362 if localonly and not isinstance(self.method, local.Local): 1363 raise NonLocalMethod() 1364 1365 if self.parm.get("proto", None) and "protocol" not in self.parm: 1366 logger.warning('Consider updating %s recipe to use "protocol" not "proto" in SRC_URI.', d.getVar('PN')) 1367 self.parm["protocol"] = self.parm.get("proto", None) 1368 1369 if hasattr(self.method, "urldata_init"): 1370 self.method.urldata_init(self, d) 1371 1372 for checksum_id in CHECKSUM_LIST: 1373 configure_checksum(checksum_id) 1374 1375 self.ignore_checksums = False 1376 1377 if "localpath" in self.parm: 1378 # if user sets localpath for file, use it instead. 1379 self.localpath = self.parm["localpath"] 1380 self.basename = os.path.basename(self.localpath) 1381 elif self.localfile: 1382 self.localpath = self.method.localpath(self, d) 1383 1384 dldir = d.getVar("DL_DIR") 1385 1386 if not self.needdonestamp: 1387 return 1388 1389 # Note: .done and .lock files should always be in DL_DIR whereas localpath may not be. 1390 if self.localpath and self.localpath.startswith(dldir): 1391 basepath = self.localpath 1392 elif self.localpath: 1393 basepath = dldir + os.sep + os.path.basename(self.localpath) 1394 elif self.basepath or self.basename: 1395 basepath = dldir + os.sep + (self.basepath or self.basename) 1396 else: 1397 bb.fatal("Can't determine lock path for url %s" % url) 1398 1399 self.donestamp = basepath + '.done' 1400 self.lockfile = basepath + '.lock' 1401 1402 def setup_revisions(self, d): 1403 self.revisions = {} 1404 for name in self.names: 1405 self.revisions[name] = srcrev_internal_helper(self, d, name) 1406 1407 # add compatibility code for non name specified case 1408 if len(self.names) == 1: 1409 self.revision = self.revisions[self.names[0]] 1410 1411 def setup_localpath(self, d): 1412 if not self.localpath: 1413 self.localpath = self.method.localpath(self, d) 1414 1415 def getSRCDate(self, d): 1416 """ 1417 Return the SRC Date for the component 1418 1419 d the bb.data module 1420 """ 1421 if "srcdate" in self.parm: 1422 return self.parm['srcdate'] 1423 1424 pn = d.getVar("PN") 1425 1426 if pn: 1427 return d.getVar("SRCDATE_%s" % pn) or d.getVar("SRCDATE") or d.getVar("DATE") 1428 1429 return d.getVar("SRCDATE") or d.getVar("DATE") 1430 1431class FetchMethod(object): 1432 """Base class for 'fetch'ing data""" 1433 1434 def __init__(self, urls=None): 1435 self.urls = [] 1436 1437 def supports(self, urldata, d): 1438 """ 1439 Check to see if this fetch class supports a given url. 1440 """ 1441 return 0 1442 1443 def localpath(self, urldata, d): 1444 """ 1445 Return the local filename of a given url assuming a successful fetch. 1446 Can also setup variables in urldata for use in go (saving code duplication 1447 and duplicate code execution) 1448 """ 1449 return os.path.join(d.getVar("DL_DIR"), urldata.localfile) 1450 1451 def supports_checksum(self, urldata): 1452 """ 1453 Is localpath something that can be represented by a checksum? 1454 """ 1455 1456 # We cannot compute checksums for None 1457 if urldata.localpath is None: 1458 return False 1459 # We cannot compute checksums for directories 1460 if os.path.isdir(urldata.localpath): 1461 return False 1462 return True 1463 1464 def recommends_checksum(self, urldata): 1465 """ 1466 Is the backend on where checksumming is recommended (should warnings 1467 be displayed if there is no checksum)? 1468 """ 1469 return False 1470 1471 def cleanup_upon_failure(self): 1472 """ 1473 When a fetch fails, should clean() be called? 1474 """ 1475 return True 1476 1477 def verify_donestamp(self, ud, d): 1478 """ 1479 Verify the donestamp file 1480 """ 1481 return verify_donestamp(ud, d) 1482 1483 def update_donestamp(self, ud, d): 1484 """ 1485 Update the donestamp file 1486 """ 1487 update_stamp(ud, d) 1488 1489 def _strip_leading_slashes(self, relpath): 1490 """ 1491 Remove leading slash as os.path.join can't cope 1492 """ 1493 while os.path.isabs(relpath): 1494 relpath = relpath[1:] 1495 return relpath 1496 1497 def setUrls(self, urls): 1498 self.__urls = urls 1499 1500 def getUrls(self): 1501 return self.__urls 1502 1503 urls = property(getUrls, setUrls, None, "Urls property") 1504 1505 def need_update(self, ud, d): 1506 """ 1507 Force a fetch, even if localpath exists? 1508 """ 1509 if os.path.exists(ud.localpath): 1510 return False 1511 return True 1512 1513 def supports_srcrev(self): 1514 """ 1515 The fetcher supports auto source revisions (SRCREV) 1516 """ 1517 return False 1518 1519 def download(self, urldata, d): 1520 """ 1521 Fetch urls 1522 Assumes localpath was called first 1523 """ 1524 raise NoMethodError(urldata.url) 1525 1526 def unpack(self, urldata, rootdir, data): 1527 iterate = False 1528 file = urldata.localpath 1529 1530 try: 1531 unpack = bb.utils.to_boolean(urldata.parm.get('unpack'), True) 1532 except ValueError as exc: 1533 bb.fatal("Invalid value for 'unpack' parameter for %s: %s" % 1534 (file, urldata.parm.get('unpack'))) 1535 1536 base, ext = os.path.splitext(file) 1537 if ext in ['.gz', '.bz2', '.Z', '.xz', '.lz', '.zst']: 1538 efile = os.path.join(rootdir, os.path.basename(base)) 1539 else: 1540 efile = file 1541 cmd = None 1542 1543 if unpack: 1544 tar_cmd = 'tar --extract --no-same-owner' 1545 if 'striplevel' in urldata.parm: 1546 tar_cmd += ' --strip-components=%s' % urldata.parm['striplevel'] 1547 if file.endswith('.tar'): 1548 cmd = '%s -f %s' % (tar_cmd, file) 1549 elif file.endswith('.tgz') or file.endswith('.tar.gz') or file.endswith('.tar.Z'): 1550 cmd = '%s -z -f %s' % (tar_cmd, file) 1551 elif file.endswith('.tbz') or file.endswith('.tbz2') or file.endswith('.tar.bz2'): 1552 cmd = 'bzip2 -dc %s | %s -f -' % (file, tar_cmd) 1553 elif file.endswith('.gz') or file.endswith('.Z') or file.endswith('.z'): 1554 cmd = 'gzip -dc %s > %s' % (file, efile) 1555 elif file.endswith('.bz2'): 1556 cmd = 'bzip2 -dc %s > %s' % (file, efile) 1557 elif file.endswith('.txz') or file.endswith('.tar.xz'): 1558 cmd = 'xz -dc %s | %s -f -' % (file, tar_cmd) 1559 elif file.endswith('.xz'): 1560 cmd = 'xz -dc %s > %s' % (file, efile) 1561 elif file.endswith('.tar.lz'): 1562 cmd = 'lzip -dc %s | %s -f -' % (file, tar_cmd) 1563 elif file.endswith('.lz'): 1564 cmd = 'lzip -dc %s > %s' % (file, efile) 1565 elif file.endswith('.tar.7z'): 1566 cmd = '7z x -so %s | %s -f -' % (file, tar_cmd) 1567 elif file.endswith('.7z'): 1568 cmd = '7za x -y %s 1>/dev/null' % file 1569 elif file.endswith('.tzst') or file.endswith('.tar.zst'): 1570 cmd = 'zstd --decompress --stdout %s | %s -f -' % (file, tar_cmd) 1571 elif file.endswith('.zst'): 1572 cmd = 'zstd --decompress --stdout %s > %s' % (file, efile) 1573 elif file.endswith('.zip') or file.endswith('.jar'): 1574 try: 1575 dos = bb.utils.to_boolean(urldata.parm.get('dos'), False) 1576 except ValueError as exc: 1577 bb.fatal("Invalid value for 'dos' parameter for %s: %s" % 1578 (file, urldata.parm.get('dos'))) 1579 cmd = 'unzip -q -o' 1580 if dos: 1581 cmd = '%s -a' % cmd 1582 cmd = "%s '%s'" % (cmd, file) 1583 elif file.endswith('.rpm') or file.endswith('.srpm'): 1584 if 'extract' in urldata.parm: 1585 unpack_file = urldata.parm.get('extract') 1586 cmd = 'rpm2cpio.sh %s | cpio -id %s' % (file, unpack_file) 1587 iterate = True 1588 iterate_file = unpack_file 1589 else: 1590 cmd = 'rpm2cpio.sh %s | cpio -id' % (file) 1591 elif file.endswith('.deb') or file.endswith('.ipk'): 1592 output = subprocess.check_output(['ar', '-t', file], preexec_fn=subprocess_setup) 1593 datafile = None 1594 if output: 1595 for line in output.decode().splitlines(): 1596 if line.startswith('data.tar.'): 1597 datafile = line 1598 break 1599 else: 1600 raise UnpackError("Unable to unpack deb/ipk package - does not contain data.tar.* file", urldata.url) 1601 else: 1602 raise UnpackError("Unable to unpack deb/ipk package - could not list contents", urldata.url) 1603 cmd = 'ar x %s %s && %s -p -f %s && rm %s' % (file, datafile, tar_cmd, datafile, datafile) 1604 1605 # If 'subdir' param exists, create a dir and use it as destination for unpack cmd 1606 if 'subdir' in urldata.parm: 1607 subdir = urldata.parm.get('subdir') 1608 if os.path.isabs(subdir): 1609 if not os.path.realpath(subdir).startswith(os.path.realpath(rootdir)): 1610 raise UnpackError("subdir argument isn't a subdirectory of unpack root %s" % rootdir, urldata.url) 1611 unpackdir = subdir 1612 else: 1613 unpackdir = os.path.join(rootdir, subdir) 1614 bb.utils.mkdirhier(unpackdir) 1615 else: 1616 unpackdir = rootdir 1617 1618 if not unpack or not cmd: 1619 urldata.unpack_tracer.unpack("file-copy", unpackdir) 1620 # If file == dest, then avoid any copies, as we already put the file into dest! 1621 dest = os.path.join(unpackdir, os.path.basename(file)) 1622 if file != dest and not (os.path.exists(dest) and os.path.samefile(file, dest)): 1623 destdir = '.' 1624 # For file:// entries all intermediate dirs in path must be created at destination 1625 if urldata.type == "file": 1626 # Trailing '/' does a copying to wrong place 1627 urlpath = urldata.path.rstrip('/') 1628 # Want files places relative to cwd so no leading '/' 1629 urlpath = urlpath.lstrip('/') 1630 if urlpath.find("/") != -1: 1631 destdir = urlpath.rsplit("/", 1)[0] + '/' 1632 bb.utils.mkdirhier("%s/%s" % (unpackdir, destdir)) 1633 cmd = 'cp --force --preserve=timestamps --no-dereference --recursive -H "%s" "%s"' % (file, destdir) 1634 else: 1635 urldata.unpack_tracer.unpack("archive-extract", unpackdir) 1636 1637 if not cmd: 1638 return 1639 1640 path = data.getVar('PATH') 1641 if path: 1642 cmd = "PATH=\"%s\" %s" % (path, cmd) 1643 bb.note("Unpacking %s to %s/" % (file, unpackdir)) 1644 ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True, cwd=unpackdir) 1645 1646 if ret != 0: 1647 raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), urldata.url) 1648 1649 if iterate is True: 1650 iterate_urldata = urldata 1651 iterate_urldata.localpath = "%s/%s" % (rootdir, iterate_file) 1652 self.unpack(urldata, rootdir, data) 1653 1654 return 1655 1656 def clean(self, urldata, d): 1657 """ 1658 Clean any existing full or partial download 1659 """ 1660 bb.utils.remove(urldata.localpath) 1661 1662 def try_premirror(self, urldata, d): 1663 """ 1664 Should premirrors be used? 1665 """ 1666 return True 1667 1668 def try_mirrors(self, fetch, urldata, d, mirrors, check=False): 1669 """ 1670 Try to use a mirror 1671 """ 1672 return bool(try_mirrors(fetch, d, urldata, mirrors, check)) 1673 1674 def checkstatus(self, fetch, urldata, d): 1675 """ 1676 Check the status of a URL 1677 Assumes localpath was called first 1678 """ 1679 logger.info("URL %s could not be checked for status since no method exists.", urldata.url) 1680 return True 1681 1682 def latest_revision(self, ud, d, name): 1683 """ 1684 Look in the cache for the latest revision, if not present ask the SCM. 1685 """ 1686 if not hasattr(self, "_latest_revision"): 1687 raise ParameterError("The fetcher for this URL does not support _latest_revision", ud.url) 1688 1689 key = self.generate_revision_key(ud, d, name) 1690 1691 rev = _revisions_cache.get_rev(key) 1692 if rev is None: 1693 rev = self._latest_revision(ud, d, name) 1694 _revisions_cache.set_rev(key, rev) 1695 return rev 1696 1697 def sortable_revision(self, ud, d, name): 1698 latest_rev = self._build_revision(ud, d, name) 1699 return True, str(latest_rev) 1700 1701 def generate_revision_key(self, ud, d, name): 1702 return self._revision_key(ud, d, name) 1703 1704 def latest_versionstring(self, ud, d): 1705 """ 1706 Compute the latest release name like "x.y.x" in "x.y.x+gitHASH" 1707 by searching through the tags output of ls-remote, comparing 1708 versions and returning the highest match as a (version, revision) pair. 1709 """ 1710 return ('', '') 1711 1712 def done(self, ud, d): 1713 """ 1714 Is the download done ? 1715 """ 1716 if os.path.exists(ud.localpath): 1717 return True 1718 return False 1719 1720 def implicit_urldata(self, ud, d): 1721 """ 1722 Get a list of FetchData objects for any implicit URLs that will also 1723 be downloaded when we fetch the given URL. 1724 """ 1725 return [] 1726 1727 1728class DummyUnpackTracer(object): 1729 """ 1730 Abstract API definition for a class that traces unpacked source files back 1731 to their respective upstream SRC_URI entries, for software composition 1732 analysis, license compliance and detailed SBOM generation purposes. 1733 User may load their own unpack tracer class (instead of the dummy 1734 one) by setting the BB_UNPACK_TRACER_CLASS config parameter. 1735 """ 1736 def start(self, unpackdir, urldata_dict, d): 1737 """ 1738 Start tracing the core Fetch.unpack process, using an index to map 1739 unpacked files to each SRC_URI entry. 1740 This method is called by Fetch.unpack and it may receive nested calls by 1741 gitsm and npmsw fetchers, that expand SRC_URI entries by adding implicit 1742 URLs and by recursively calling Fetch.unpack from new (nested) Fetch 1743 instances. 1744 """ 1745 return 1746 def start_url(self, url): 1747 """Start tracing url unpack process. 1748 This method is called by Fetch.unpack before the fetcher-specific unpack 1749 method starts, and it may receive nested calls by gitsm and npmsw 1750 fetchers. 1751 """ 1752 return 1753 def unpack(self, unpack_type, destdir): 1754 """ 1755 Set unpack_type and destdir for current url. 1756 This method is called by the fetcher-specific unpack method after url 1757 tracing started. 1758 """ 1759 return 1760 def finish_url(self, url): 1761 """Finish tracing url unpack process and update the file index. 1762 This method is called by Fetch.unpack after the fetcher-specific unpack 1763 method finished its job, and it may receive nested calls by gitsm 1764 and npmsw fetchers. 1765 """ 1766 return 1767 def complete(self): 1768 """ 1769 Finish tracing the Fetch.unpack process, and check if all nested 1770 Fecth.unpack calls (if any) have been completed; if so, save collected 1771 metadata. 1772 """ 1773 return 1774 1775 1776class Fetch(object): 1777 def __init__(self, urls, d, cache = True, localonly = False, connection_cache = None): 1778 if localonly and cache: 1779 raise Exception("bb.fetch2.Fetch.__init__: cannot set cache and localonly at same time") 1780 1781 if not urls: 1782 urls = d.getVar("SRC_URI").split() 1783 self.urls = urls 1784 self.d = d 1785 self.ud = {} 1786 self.connection_cache = connection_cache 1787 1788 fn = d.getVar('FILE') 1789 mc = d.getVar('__BBMULTICONFIG') or "" 1790 key = None 1791 if cache and fn: 1792 key = mc + fn + str(id(d)) 1793 if key in urldata_cache: 1794 self.ud = urldata_cache[key] 1795 1796 # the unpack_tracer object needs to be made available to possible nested 1797 # Fetch instances (when those are created by gitsm and npmsw fetchers) 1798 # so we set it as a global variable 1799 global unpack_tracer 1800 try: 1801 unpack_tracer 1802 except NameError: 1803 class_path = d.getVar("BB_UNPACK_TRACER_CLASS") 1804 if class_path: 1805 # use user-defined unpack tracer class 1806 import importlib 1807 module_name, _, class_name = class_path.rpartition(".") 1808 module = importlib.import_module(module_name) 1809 class_ = getattr(module, class_name) 1810 unpack_tracer = class_() 1811 else: 1812 # fall back to the dummy/abstract class 1813 unpack_tracer = DummyUnpackTracer() 1814 1815 for url in urls: 1816 if url not in self.ud: 1817 try: 1818 self.ud[url] = FetchData(url, d, localonly) 1819 self.ud[url].unpack_tracer = unpack_tracer 1820 except NonLocalMethod: 1821 if localonly: 1822 self.ud[url] = None 1823 pass 1824 1825 if key: 1826 urldata_cache[key] = self.ud 1827 1828 def localpath(self, url): 1829 if url not in self.urls: 1830 self.ud[url] = FetchData(url, self.d) 1831 1832 self.ud[url].setup_localpath(self.d) 1833 return self.ud[url].localpath 1834 1835 def localpaths(self): 1836 """ 1837 Return a list of the local filenames, assuming successful fetch 1838 """ 1839 local = [] 1840 1841 for u in self.urls: 1842 ud = self.ud[u] 1843 ud.setup_localpath(self.d) 1844 local.append(ud.localpath) 1845 1846 return local 1847 1848 def download(self, urls=None): 1849 """ 1850 Fetch all urls 1851 """ 1852 if not urls: 1853 urls = self.urls 1854 1855 network = self.d.getVar("BB_NO_NETWORK") 1856 premirroronly = bb.utils.to_boolean(self.d.getVar("BB_FETCH_PREMIRRORONLY")) 1857 1858 checksum_missing_messages = [] 1859 for u in urls: 1860 ud = self.ud[u] 1861 ud.setup_localpath(self.d) 1862 m = ud.method 1863 done = False 1864 1865 if ud.lockfile: 1866 lf = bb.utils.lockfile(ud.lockfile) 1867 1868 try: 1869 self.d.setVar("BB_NO_NETWORK", network) 1870 if m.verify_donestamp(ud, self.d) and not m.need_update(ud, self.d): 1871 done = True 1872 elif m.try_premirror(ud, self.d): 1873 logger.debug("Trying PREMIRRORS") 1874 mirrors = mirror_from_string(self.d.getVar('PREMIRRORS')) 1875 done = m.try_mirrors(self, ud, self.d, mirrors) 1876 if done: 1877 try: 1878 # early checksum verification so that if the checksum of the premirror 1879 # contents mismatch the fetcher can still try upstream and mirrors 1880 m.update_donestamp(ud, self.d) 1881 except ChecksumError as e: 1882 logger.warning("Checksum failure encountered with premirror download of %s - will attempt other sources." % u) 1883 logger.debug(str(e)) 1884 done = False 1885 1886 if premirroronly: 1887 self.d.setVar("BB_NO_NETWORK", "1") 1888 1889 firsterr = None 1890 verified_stamp = False 1891 if done: 1892 verified_stamp = m.verify_donestamp(ud, self.d) 1893 if not done and (not verified_stamp or m.need_update(ud, self.d)): 1894 try: 1895 if not trusted_network(self.d, ud.url): 1896 raise UntrustedUrl(ud.url) 1897 logger.debug("Trying Upstream") 1898 m.download(ud, self.d) 1899 if hasattr(m, "build_mirror_data"): 1900 m.build_mirror_data(ud, self.d) 1901 done = True 1902 # early checksum verify, so that if checksum mismatched, 1903 # fetcher still have chance to fetch from mirror 1904 m.update_donestamp(ud, self.d) 1905 1906 except bb.fetch2.NetworkAccess: 1907 raise 1908 1909 except BBFetchException as e: 1910 if isinstance(e, ChecksumError): 1911 logger.warning("Checksum failure encountered with download of %s - will attempt other sources if available" % u) 1912 logger.debug(str(e)) 1913 if os.path.exists(ud.localpath): 1914 rename_bad_checksum(ud, e.checksum) 1915 elif isinstance(e, NoChecksumError): 1916 raise 1917 else: 1918 logger.warning('Failed to fetch URL %s, attempting MIRRORS if available' % u) 1919 logger.debug(str(e)) 1920 firsterr = e 1921 # Remove any incomplete fetch 1922 if not verified_stamp and m.cleanup_upon_failure(): 1923 m.clean(ud, self.d) 1924 logger.debug("Trying MIRRORS") 1925 mirrors = mirror_from_string(self.d.getVar('MIRRORS')) 1926 done = m.try_mirrors(self, ud, self.d, mirrors) 1927 1928 if not done or not m.done(ud, self.d): 1929 if firsterr: 1930 logger.error(str(firsterr)) 1931 raise FetchError("Unable to fetch URL from any source.", u) 1932 1933 m.update_donestamp(ud, self.d) 1934 1935 except IOError as e: 1936 if e.errno in [errno.ESTALE]: 1937 logger.error("Stale Error Observed %s." % u) 1938 raise ChecksumError("Stale Error Detected") 1939 1940 except BBFetchException as e: 1941 if isinstance(e, NoChecksumError): 1942 (message, _) = e.args 1943 checksum_missing_messages.append(message) 1944 continue 1945 elif isinstance(e, ChecksumError): 1946 logger.error("Checksum failure fetching %s" % u) 1947 raise 1948 1949 finally: 1950 if ud.lockfile: 1951 bb.utils.unlockfile(lf) 1952 if checksum_missing_messages: 1953 logger.error("Missing SRC_URI checksum, please add those to the recipe: \n%s", "\n".join(checksum_missing_messages)) 1954 raise BBFetchException("There was some missing checksums in the recipe") 1955 1956 def checkstatus(self, urls=None): 1957 """ 1958 Check all URLs exist upstream. 1959 1960 Returns None if the URLs exist, raises FetchError if the check wasn't 1961 successful but there wasn't an error (such as file not found), and 1962 raises other exceptions in error cases. 1963 """ 1964 1965 if not urls: 1966 urls = self.urls 1967 1968 for u in urls: 1969 ud = self.ud[u] 1970 ud.setup_localpath(self.d) 1971 m = ud.method 1972 logger.debug("Testing URL %s", u) 1973 # First try checking uri, u, from PREMIRRORS 1974 mirrors = mirror_from_string(self.d.getVar('PREMIRRORS')) 1975 ret = m.try_mirrors(self, ud, self.d, mirrors, True) 1976 if not ret: 1977 # Next try checking from the original uri, u 1978 ret = m.checkstatus(self, ud, self.d) 1979 if not ret: 1980 # Finally, try checking uri, u, from MIRRORS 1981 mirrors = mirror_from_string(self.d.getVar('MIRRORS')) 1982 ret = m.try_mirrors(self, ud, self.d, mirrors, True) 1983 1984 if not ret: 1985 raise FetchError("URL doesn't work", u) 1986 1987 def unpack(self, root, urls=None): 1988 """ 1989 Unpack urls to root 1990 """ 1991 1992 if not urls: 1993 urls = self.urls 1994 1995 unpack_tracer.start(root, self.ud, self.d) 1996 1997 for u in urls: 1998 ud = self.ud[u] 1999 ud.setup_localpath(self.d) 2000 2001 if ud.lockfile: 2002 lf = bb.utils.lockfile(ud.lockfile) 2003 2004 unpack_tracer.start_url(u) 2005 ud.method.unpack(ud, root, self.d) 2006 unpack_tracer.finish_url(u) 2007 2008 if ud.lockfile: 2009 bb.utils.unlockfile(lf) 2010 2011 unpack_tracer.complete() 2012 2013 def clean(self, urls=None): 2014 """ 2015 Clean files that the fetcher gets or places 2016 """ 2017 2018 if not urls: 2019 urls = self.urls 2020 2021 for url in urls: 2022 if url not in self.ud: 2023 self.ud[url] = FetchData(url, self.d) 2024 ud = self.ud[url] 2025 ud.setup_localpath(self.d) 2026 2027 if not ud.localfile and ud.localpath is None: 2028 continue 2029 2030 if ud.lockfile: 2031 lf = bb.utils.lockfile(ud.lockfile) 2032 2033 ud.method.clean(ud, self.d) 2034 if ud.donestamp: 2035 bb.utils.remove(ud.donestamp) 2036 2037 if ud.lockfile: 2038 bb.utils.unlockfile(lf) 2039 2040 def expanded_urldata(self, urls=None): 2041 """ 2042 Get an expanded list of FetchData objects covering both the given 2043 URLS and any additional implicit URLs that are added automatically by 2044 the appropriate FetchMethod. 2045 """ 2046 2047 if not urls: 2048 urls = self.urls 2049 2050 urldata = [] 2051 for url in urls: 2052 ud = self.ud[url] 2053 urldata.append(ud) 2054 urldata += ud.method.implicit_urldata(ud, self.d) 2055 2056 return urldata 2057 2058class FetchConnectionCache(object): 2059 """ 2060 A class which represents an container for socket connections. 2061 """ 2062 def __init__(self): 2063 self.cache = {} 2064 2065 def get_connection_name(self, host, port): 2066 return host + ':' + str(port) 2067 2068 def add_connection(self, host, port, connection): 2069 cn = self.get_connection_name(host, port) 2070 2071 if cn not in self.cache: 2072 self.cache[cn] = connection 2073 2074 def get_connection(self, host, port): 2075 connection = None 2076 2077 cn = self.get_connection_name(host, port) 2078 if cn in self.cache: 2079 connection = self.cache[cn] 2080 2081 return connection 2082 2083 def remove_connection(self, host, port): 2084 cn = self.get_connection_name(host, port) 2085 if cn in self.cache: 2086 self.cache[cn].close() 2087 del self.cache[cn] 2088 2089 def close_connections(self): 2090 for cn in list(self.cache.keys()): 2091 self.cache[cn].close() 2092 del self.cache[cn] 2093 2094from . import cvs 2095from . import git 2096from . import gitsm 2097from . import gitannex 2098from . import local 2099from . import svn 2100from . import wget 2101from . import ssh 2102from . import sftp 2103from . import s3 2104from . import perforce 2105from . import bzr 2106from . import hg 2107from . import osc 2108from . import repo 2109from . import clearcase 2110from . import npm 2111from . import npmsw 2112from . import az 2113from . import crate 2114from . import gcp 2115from . import gomod 2116 2117methods.append(local.Local()) 2118methods.append(wget.Wget()) 2119methods.append(svn.Svn()) 2120methods.append(git.Git()) 2121methods.append(gitsm.GitSM()) 2122methods.append(gitannex.GitANNEX()) 2123methods.append(cvs.Cvs()) 2124methods.append(ssh.SSH()) 2125methods.append(sftp.SFTP()) 2126methods.append(s3.S3()) 2127methods.append(perforce.Perforce()) 2128methods.append(bzr.Bzr()) 2129methods.append(hg.Hg()) 2130methods.append(osc.Osc()) 2131methods.append(repo.Repo()) 2132methods.append(clearcase.ClearCase()) 2133methods.append(npm.Npm()) 2134methods.append(npmsw.NpmShrinkWrap()) 2135methods.append(az.Az()) 2136methods.append(crate.Crate()) 2137methods.append(gcp.GCP()) 2138methods.append(gomod.GoMod()) 2139methods.append(gomod.GoModGit()) 2140