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