blob: 07de6c2693fe1b7b4feedcd28f33debdbfdd1472 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"""
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#
Brad Bishopc342db32019-05-15 21:57:59 -040011# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050012#
13# Based on functions from the base bb module, Copyright 2003 Holger Schurig
14
Patrick Williamsc124f4f2015-09-15 14:41:29 -050015import os, re
16import signal
Patrick Williamsc124f4f2015-09-15 14:41:29 -050017import logging
Patrick Williamsc0f7c042017-02-23 20:41:17 -060018import 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
Brad Bishop6e60e8b2018-02-01 10:27:11 -050025import errno
Patrick Williamsc124f4f2015-09-15 14:41:29 -050026import bb.persist_data, bb.utils
27import bb.checksum
Patrick Williamsc124f4f2015-09-15 14:41:29 -050028import bb.process
Brad Bishopd7bf8c12018-02-25 22:55:05 -050029import bb.event
Patrick Williamsc124f4f2015-09-15 14:41:29 -050030
31__version__ = "2"
32_checksum_cache = bb.checksum.FileChecksumCache()
33
34logger = logging.getLogger("BitBake.Fetcher")
35
Patrick Williamsc124f4f2015-09-15 14:41:29 -050036class BBFetchException(Exception):
37 """Class all fetch exceptions inherit from"""
38 def __init__(self, message):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050039 self.msg = message
40 Exception.__init__(self, message)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050041
42 def __str__(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050043 return self.msg
Patrick Williamsc124f4f2015-09-15 14:41:29 -050044
45class UntrustedUrl(BBFetchException):
46 """Exception raised when encountering a host not listed in BB_ALLOWED_NETWORKS"""
47 def __init__(self, url, message=''):
48 if message:
49 msg = message
50 else:
51 msg = "The URL: '%s' is not trusted and cannot be used" % url
52 self.url = url
53 BBFetchException.__init__(self, msg)
54 self.args = (url,)
55
56class MalformedUrl(BBFetchException):
57 """Exception raised when encountering an invalid url"""
58 def __init__(self, url, message=''):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050059 if message:
60 msg = message
61 else:
62 msg = "The URL: '%s' is invalid and cannot be interpreted" % url
63 self.url = url
64 BBFetchException.__init__(self, msg)
65 self.args = (url,)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050066
67class FetchError(BBFetchException):
68 """General fetcher exception when something happens incorrectly"""
69 def __init__(self, message, url = None):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050070 if url:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050071 msg = "Fetcher failure for URL: '%s'. %s" % (url, message)
Brad Bishopd7bf8c12018-02-25 22:55:05 -050072 else:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050073 msg = "Fetcher failure: %s" % message
Brad Bishopd7bf8c12018-02-25 22:55:05 -050074 self.url = url
75 BBFetchException.__init__(self, msg)
76 self.args = (message, url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050077
78class ChecksumError(FetchError):
79 """Exception when mismatched checksum encountered"""
80 def __init__(self, message, url = None, checksum = None):
81 self.checksum = checksum
82 FetchError.__init__(self, message, url)
83
84class NoChecksumError(FetchError):
85 """Exception when no checksum is specified, but BB_STRICT_CHECKSUM is set"""
86
87class UnpackError(BBFetchException):
88 """General fetcher exception when something happens incorrectly when unpacking"""
89 def __init__(self, message, url):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050090 msg = "Unpack failure for URL: '%s'. %s" % (url, message)
91 self.url = url
92 BBFetchException.__init__(self, msg)
93 self.args = (message, url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050094
95class NoMethodError(BBFetchException):
96 """Exception raised when there is no method to obtain a supplied url or set of urls"""
97 def __init__(self, url):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050098 msg = "Could not find a fetcher which supports the URL: '%s'" % url
99 self.url = url
100 BBFetchException.__init__(self, msg)
101 self.args = (url,)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500102
103class MissingParameterError(BBFetchException):
104 """Exception raised when a fetch method is missing a critical parameter in the url"""
105 def __init__(self, missing, url):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500106 msg = "URL: '%s' is missing the required parameter '%s'" % (url, missing)
107 self.url = url
108 self.missing = missing
109 BBFetchException.__init__(self, msg)
110 self.args = (missing, url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500111
112class ParameterError(BBFetchException):
113 """Exception raised when a url cannot be proccessed due to invalid parameters."""
114 def __init__(self, message, url):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500115 msg = "URL: '%s' has invalid parameters. %s" % (url, message)
116 self.url = url
117 BBFetchException.__init__(self, msg)
118 self.args = (message, url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500119
120class NetworkAccess(BBFetchException):
121 """Exception raised when network access is disabled but it is required."""
122 def __init__(self, url, cmd):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500123 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)
124 self.url = url
125 self.cmd = cmd
126 BBFetchException.__init__(self, msg)
127 self.args = (url, cmd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500128
129class NonLocalMethod(Exception):
130 def __init__(self):
131 Exception.__init__(self)
132
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500133class MissingChecksumEvent(bb.event.Event):
134 def __init__(self, url, md5sum, sha256sum):
135 self.url = url
136 self.checksums = {'md5sum': md5sum,
137 'sha256sum': sha256sum}
138 bb.event.Event.__init__(self)
139
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500140
141class URI(object):
142 """
143 A class representing a generic URI, with methods for
144 accessing the URI components, and stringifies to the
145 URI.
146
147 It is constructed by calling it with a URI, or setting
148 the attributes manually:
149
150 uri = URI("http://example.com/")
151
152 uri = URI()
153 uri.scheme = 'http'
154 uri.hostname = 'example.com'
155 uri.path = '/'
156
157 It has the following attributes:
158
159 * scheme (read/write)
160 * userinfo (authentication information) (read/write)
161 * username (read/write)
162 * password (read/write)
163
164 Note, password is deprecated as of RFC 3986.
165
166 * hostname (read/write)
167 * port (read/write)
168 * hostport (read only)
169 "hostname:port", if both are set, otherwise just "hostname"
170 * path (read/write)
171 * path_quoted (read/write)
172 A URI quoted version of path
173 * params (dict) (read/write)
174 * query (dict) (read/write)
175 * relative (bool) (read only)
176 True if this is a "relative URI", (e.g. file:foo.diff)
177
178 It stringifies to the URI itself.
179
180 Some notes about relative URIs: while it's specified that
181 a URI beginning with <scheme>:// should either be directly
182 followed by a hostname or a /, the old URI handling of the
183 fetch2 library did not comform to this. Therefore, this URI
184 class has some kludges to make sure that URIs are parsed in
185 a way comforming to bitbake's current usage. This URI class
186 supports the following:
187
188 file:relative/path.diff (IETF compliant)
189 git:relative/path.git (IETF compliant)
190 git:///absolute/path.git (IETF compliant)
191 file:///absolute/path.diff (IETF compliant)
192
193 file://relative/path.diff (not IETF compliant)
194
195 But it does not support the following:
196
197 file://hostname/absolute/path.diff (would be IETF compliant)
198
199 Note that the last case only applies to a list of
200 "whitelisted" schemes (currently only file://), that requires
201 its URIs to not have a network location.
202 """
203
204 _relative_schemes = ['file', 'git']
205 _netloc_forbidden = ['file']
206
207 def __init__(self, uri=None):
208 self.scheme = ''
209 self.userinfo = ''
210 self.hostname = ''
211 self.port = None
212 self._path = ''
213 self.params = {}
214 self.query = {}
215 self.relative = False
216
217 if not uri:
218 return
219
220 # We hijack the URL parameters, since the way bitbake uses
221 # them are not quite RFC compliant.
222 uri, param_str = (uri.split(";", 1) + [None])[:2]
223
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600224 urlp = urllib.parse.urlparse(uri)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500225 self.scheme = urlp.scheme
226
227 reparse = 0
228
229 # Coerce urlparse to make URI scheme use netloc
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600230 if not self.scheme in urllib.parse.uses_netloc:
231 urllib.parse.uses_params.append(self.scheme)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500232 reparse = 1
233
234 # Make urlparse happy(/ier) by converting local resources
235 # to RFC compliant URL format. E.g.:
236 # file://foo.diff -> file:foo.diff
237 if urlp.scheme in self._netloc_forbidden:
238 uri = re.sub("(?<=:)//(?!/)", "", uri, 1)
239 reparse = 1
240
241 if reparse:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600242 urlp = urllib.parse.urlparse(uri)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500243
244 # Identify if the URI is relative or not
245 if urlp.scheme in self._relative_schemes and \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800246 re.compile(r"^\w+:(?!//)").match(uri):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500247 self.relative = True
248
249 if not self.relative:
250 self.hostname = urlp.hostname or ''
251 self.port = urlp.port
252
253 self.userinfo += urlp.username or ''
254
255 if urlp.password:
256 self.userinfo += ':%s' % urlp.password
257
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600258 self.path = urllib.parse.unquote(urlp.path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500259
260 if param_str:
261 self.params = self._param_str_split(param_str, ";")
262 if urlp.query:
263 self.query = self._param_str_split(urlp.query, "&")
264
265 def __str__(self):
266 userinfo = self.userinfo
267 if userinfo:
268 userinfo += '@'
269
270 return "%s:%s%s%s%s%s%s" % (
271 self.scheme,
272 '' if self.relative else '//',
273 userinfo,
274 self.hostport,
275 self.path_quoted,
276 self._query_str(),
277 self._param_str())
278
279 def _param_str(self):
280 return (
281 ''.join([';', self._param_str_join(self.params, ";")])
282 if self.params else '')
283
284 def _query_str(self):
285 return (
286 ''.join(['?', self._param_str_join(self.query, "&")])
287 if self.query else '')
288
289 def _param_str_split(self, string, elmdelim, kvdelim="="):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600290 ret = collections.OrderedDict()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500291 for k, v in [x.split(kvdelim, 1) for x in string.split(elmdelim)]:
292 ret[k] = v
293 return ret
294
295 def _param_str_join(self, dict_, elmdelim, kvdelim="="):
296 return elmdelim.join([kvdelim.join([k, v]) for k, v in dict_.items()])
297
298 @property
299 def hostport(self):
300 if not self.port:
301 return self.hostname
302 return "%s:%d" % (self.hostname, self.port)
303
304 @property
305 def path_quoted(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600306 return urllib.parse.quote(self.path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500307
308 @path_quoted.setter
309 def path_quoted(self, path):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600310 self.path = urllib.parse.unquote(path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311
312 @property
313 def path(self):
314 return self._path
315
316 @path.setter
317 def path(self, path):
318 self._path = path
319
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500320 if not path or re.compile("^/").match(path):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500321 self.relative = False
322 else:
323 self.relative = True
324
325 @property
326 def username(self):
327 if self.userinfo:
328 return (self.userinfo.split(":", 1))[0]
329 return ''
330
331 @username.setter
332 def username(self, username):
333 password = self.password
334 self.userinfo = username
335 if password:
336 self.userinfo += ":%s" % password
337
338 @property
339 def password(self):
340 if self.userinfo and ":" in self.userinfo:
341 return (self.userinfo.split(":", 1))[1]
342 return ''
343
344 @password.setter
345 def password(self, password):
346 self.userinfo = "%s:%s" % (self.username, password)
347
348def decodeurl(url):
349 """Decodes an URL into the tokens (scheme, network location, path,
350 user, password, parameters).
351 """
352
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500353 m = re.compile('(?P<type>[^:]*)://((?P<user>[^/;]+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500354 if not m:
355 raise MalformedUrl(url)
356
357 type = m.group('type')
358 location = m.group('location')
359 if not location:
360 raise MalformedUrl(url)
361 user = m.group('user')
362 parm = m.group('parm')
363
364 locidx = location.find('/')
365 if locidx != -1 and type.lower() != 'file':
366 host = location[:locidx]
367 path = location[locidx:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500368 elif type.lower() == 'file':
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500369 host = ""
370 path = location
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500371 else:
372 host = location
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800373 path = "/"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500374 if user:
375 m = re.compile('(?P<user>[^:]+)(:?(?P<pswd>.*))').match(user)
376 if m:
377 user = m.group('user')
378 pswd = m.group('pswd')
379 else:
380 user = ''
381 pswd = ''
382
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600383 p = collections.OrderedDict()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500384 if parm:
385 for s in parm.split(';'):
386 if s:
387 if not '=' in s:
388 raise MalformedUrl(url, "The URL: '%s' is invalid: parameter %s does not specify a value (missing '=')" % (url, s))
389 s1, s2 = s.split('=')
390 p[s1] = s2
391
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600392 return type, host, urllib.parse.unquote(path), user, pswd, p
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500393
394def encodeurl(decoded):
395 """Encodes a URL from tokens (scheme, network location, path,
396 user, password, parameters).
397 """
398
399 type, host, path, user, pswd, p = decoded
400
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500401 if not type:
402 raise MissingParameterError('type', "encoded from the data %s" % str(decoded))
403 url = '%s://' % type
404 if user and type != "file":
405 url += "%s" % user
406 if pswd:
407 url += ":%s" % pswd
408 url += "@"
409 if host and type != "file":
410 url += "%s" % host
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500411 if path:
412 # Standardise path to ensure comparisons work
413 while '//' in path:
414 path = path.replace("//", "/")
415 url += "%s" % urllib.parse.quote(path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500416 if p:
417 for parm in p:
418 url += ";%s=%s" % (parm, p[parm])
419
420 return url
421
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500422def uri_replace(ud, uri_find, uri_replace, replacements, d, mirrortarball=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500423 if not ud.url or not uri_find or not uri_replace:
424 logger.error("uri_replace: passed an undefined value, not replacing")
425 return None
426 uri_decoded = list(decodeurl(ud.url))
427 uri_find_decoded = list(decodeurl(uri_find))
428 uri_replace_decoded = list(decodeurl(uri_replace))
429 logger.debug(2, "For url %s comparing %s to %s" % (uri_decoded, uri_find_decoded, uri_replace_decoded))
430 result_decoded = ['', '', '', '', '', {}]
431 for loc, i in enumerate(uri_find_decoded):
432 result_decoded[loc] = uri_decoded[loc]
433 regexp = i
434 if loc == 0 and regexp and not regexp.endswith("$"):
435 # Leaving the type unanchored can mean "https" matching "file" can become "files"
436 # which is clearly undesirable.
437 regexp += "$"
438 if loc == 5:
439 # Handle URL parameters
440 if i:
441 # Any specified URL parameters must match
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800442 for k in uri_find_decoded[loc]:
443 if uri_decoded[loc][k] != uri_find_decoded[loc][k]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444 return None
445 # Overwrite any specified replacement parameters
446 for k in uri_replace_decoded[loc]:
447 for l in replacements:
448 uri_replace_decoded[loc][k] = uri_replace_decoded[loc][k].replace(l, replacements[l])
449 result_decoded[loc][k] = uri_replace_decoded[loc][k]
450 elif (re.match(regexp, uri_decoded[loc])):
451 if not uri_replace_decoded[loc]:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500452 result_decoded[loc] = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500453 else:
454 for k in replacements:
455 uri_replace_decoded[loc] = uri_replace_decoded[loc].replace(k, replacements[k])
456 #bb.note("%s %s %s" % (regexp, uri_replace_decoded[loc], uri_decoded[loc]))
Patrick Williamsd7e96312015-09-22 08:09:05 -0500457 result_decoded[loc] = re.sub(regexp, uri_replace_decoded[loc], uri_decoded[loc], 1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500458 if loc == 2:
459 # Handle path manipulations
460 basename = None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500461 if uri_decoded[0] != uri_replace_decoded[0] and mirrortarball:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500462 # If the source and destination url types differ, must be a mirrortarball mapping
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500463 basename = os.path.basename(mirrortarball)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500464 # Kill parameters, they make no sense for mirror tarballs
465 uri_decoded[5] = {}
466 elif ud.localpath and ud.method.supports_checksum(ud):
467 basename = os.path.basename(ud.localpath)
468 if basename and not result_decoded[loc].endswith(basename):
469 result_decoded[loc] = os.path.join(result_decoded[loc], basename)
470 else:
471 return None
472 result = encodeurl(result_decoded)
473 if result == ud.url:
474 return None
475 logger.debug(2, "For url %s returning %s" % (ud.url, result))
476 return result
477
478methods = []
479urldata_cache = {}
480saved_headrevs = {}
481
482def fetcher_init(d):
483 """
484 Called to initialize the fetchers once the configuration data is known.
485 Calls before this must not hit the cache.
486 """
487 # When to drop SCM head revisions controlled by user policy
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500488 srcrev_policy = d.getVar('BB_SRCREV_POLICY') or "clear"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500489 if srcrev_policy == "cache":
490 logger.debug(1, "Keeping SRCREV cache due to cache policy of: %s", srcrev_policy)
491 elif srcrev_policy == "clear":
492 logger.debug(1, "Clearing SRCREV cache due to cache policy of: %s", srcrev_policy)
493 revs = bb.persist_data.persist('BB_URI_HEADREVS', d)
494 try:
495 bb.fetch2.saved_headrevs = revs.items()
496 except:
497 pass
498 revs.clear()
499 else:
500 raise FetchError("Invalid SRCREV cache policy of: %s" % srcrev_policy)
501
502 _checksum_cache.init_cache(d)
503
504 for m in methods:
505 if hasattr(m, "init"):
506 m.init(d)
507
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500508def fetcher_parse_save():
509 _checksum_cache.save_extras()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500510
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500511def fetcher_parse_done():
512 _checksum_cache.save_merge()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500513
Brad Bishop19323692019-04-05 15:28:33 -0400514def fetcher_compare_revisions(d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500515 """
516 Compare the revisions in the persistant cache with current values and
517 return true/false on whether they've changed.
518 """
519
520 data = bb.persist_data.persist('BB_URI_HEADREVS', d).items()
521 data2 = bb.fetch2.saved_headrevs
522
523 changed = False
524 for key in data:
525 if key not in data2 or data2[key] != data[key]:
526 logger.debug(1, "%s changed", key)
527 changed = True
528 return True
529 else:
530 logger.debug(2, "%s did not change", key)
531 return False
532
533def mirror_from_string(data):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500534 mirrors = (data or "").replace('\\n',' ').split()
535 # Split into pairs
536 if len(mirrors) % 2 != 0:
537 bb.warn('Invalid mirror data %s, should have paired members.' % data)
538 return list(zip(*[iter(mirrors)]*2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500539
540def verify_checksum(ud, d, precomputed={}):
541 """
542 verify the MD5 and SHA256 checksum for downloaded src
543
544 Raises a FetchError if one or both of the SRC_URI checksums do not match
545 the downloaded file, or if BB_STRICT_CHECKSUM is set and there are no
546 checksums specified.
547
548 Returns a dict of checksums that can be stored in a done stamp file and
549 passed in as precomputed parameter in a later call to avoid re-computing
550 the checksums from the file. This allows verifying the checksums of the
551 file against those in the recipe each time, rather than only after
552 downloading. See https://bugzilla.yoctoproject.org/show_bug.cgi?id=5571.
553 """
554
555 _MD5_KEY = "md5"
556 _SHA256_KEY = "sha256"
557
558 if ud.ignore_checksums or not ud.method.supports_checksum(ud):
559 return {}
560
561 if _MD5_KEY in precomputed:
562 md5data = precomputed[_MD5_KEY]
563 else:
564 md5data = bb.utils.md5_file(ud.localpath)
565
566 if _SHA256_KEY in precomputed:
567 sha256data = precomputed[_SHA256_KEY]
568 else:
569 sha256data = bb.utils.sha256_file(ud.localpath)
570
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500571 if ud.method.recommends_checksum(ud) and not ud.md5_expected and not ud.sha256_expected:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500572 # If strict checking enabled and neither sum defined, raise error
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500573 strict = d.getVar("BB_STRICT_CHECKSUM") or "0"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500574 if strict == "1":
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500575 logger.error('No checksum specified for %s, please add at least one to the recipe:\n'
576 'SRC_URI[%s] = "%s"\nSRC_URI[%s] = "%s"' %
577 (ud.localpath, ud.md5_name, md5data,
578 ud.sha256_name, sha256data))
579 raise NoChecksumError('Missing SRC_URI checksum', ud.url)
580
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500581 bb.event.fire(MissingChecksumEvent(ud.url, md5data, sha256data), d)
582
583 if strict == "ignore":
584 return {
585 _MD5_KEY: md5data,
586 _SHA256_KEY: sha256data
587 }
588
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500589 # Log missing sums so user can more easily add them
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600590 logger.warning('Missing md5 SRC_URI checksum for %s, consider adding to the recipe:\n'
591 'SRC_URI[%s] = "%s"',
592 ud.localpath, ud.md5_name, md5data)
593 logger.warning('Missing sha256 SRC_URI checksum for %s, consider adding to the recipe:\n'
594 'SRC_URI[%s] = "%s"',
595 ud.localpath, ud.sha256_name, sha256data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500596
597 # We want to alert the user if a checksum is defined in the recipe but
598 # it does not match.
599 msg = ""
600 mismatch = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500601 if ud.md5_expected and ud.md5_expected != md5data:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500602 msg = msg + "\nFile: '%s' has %s checksum %s when %s was expected" % (ud.localpath, 'md5', md5data, ud.md5_expected)
603 mismatch = True;
604
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500605 if ud.sha256_expected and ud.sha256_expected != sha256data:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500606 msg = msg + "\nFile: '%s' has %s checksum %s when %s was expected" % (ud.localpath, 'sha256', sha256data, ud.sha256_expected)
607 mismatch = True;
608
609 if mismatch:
610 msg = msg + '\nIf this change is expected (e.g. you have upgraded to a new version without updating the checksums) then you can use these lines within the recipe:\nSRC_URI[%s] = "%s"\nSRC_URI[%s] = "%s"\nOtherwise you should retry the download and/or check with upstream to determine if the file has become corrupted or otherwise unexpectedly modified.\n' % (ud.md5_name, md5data, ud.sha256_name, sha256data)
611
612 if len(msg):
613 raise ChecksumError('Checksum mismatch!%s' % msg, ud.url, md5data)
614
615 return {
616 _MD5_KEY: md5data,
617 _SHA256_KEY: sha256data
618 }
619
620
621def verify_donestamp(ud, d, origud=None):
622 """
623 Check whether the done stamp file has the right checksums (if the fetch
624 method supports them). If it doesn't, delete the done stamp and force
625 a re-download.
626
627 Returns True, if the donestamp exists and is valid, False otherwise. When
628 returning False, any existing done stamps are removed.
629 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500630 if not ud.needdonestamp or (origud and not origud.needdonestamp):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500631 return True
632
Brad Bishop316dfdd2018-06-25 12:45:53 -0400633 if not os.path.exists(ud.localpath):
634 # local path does not exist
635 if os.path.exists(ud.donestamp):
636 # done stamp exists, but the downloaded file does not; the done stamp
637 # must be incorrect, re-trigger the download
638 bb.utils.remove(ud.donestamp)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500639 return False
640
641 if (not ud.method.supports_checksum(ud) or
642 (origud and not origud.method.supports_checksum(origud))):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400643 # if done stamp exists and checksums not supported; assume the local
644 # file is current
645 return os.path.exists(ud.donestamp)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500646
647 precomputed_checksums = {}
648 # Only re-use the precomputed checksums if the donestamp is newer than the
649 # file. Do not rely on the mtime of directories, though. If ud.localpath is
650 # a directory, there will probably not be any checksums anyway.
Brad Bishop316dfdd2018-06-25 12:45:53 -0400651 if os.path.exists(ud.donestamp) and (os.path.isdir(ud.localpath) or
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500652 os.path.getmtime(ud.localpath) < os.path.getmtime(ud.donestamp)):
653 try:
654 with open(ud.donestamp, "rb") as cachefile:
655 pickled = pickle.Unpickler(cachefile)
656 precomputed_checksums.update(pickled.load())
657 except Exception as e:
658 # Avoid the warnings on the upgrade path from emtpy done stamp
659 # files to those containing the checksums.
660 if not isinstance(e, EOFError):
661 # Ignore errors, they aren't fatal
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600662 logger.warning("Couldn't load checksums from donestamp %s: %s "
663 "(msg: %s)" % (ud.donestamp, type(e).__name__,
664 str(e)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500665
666 try:
667 checksums = verify_checksum(ud, d, precomputed_checksums)
668 # If the cache file did not have the checksums, compute and store them
669 # as an upgrade path from the previous done stamp file format.
670 if checksums != precomputed_checksums:
671 with open(ud.donestamp, "wb") as cachefile:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600672 p = pickle.Pickler(cachefile, 2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500673 p.dump(checksums)
674 return True
675 except ChecksumError as e:
676 # Checksums failed to verify, trigger re-download and remove the
677 # incorrect stamp file.
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600678 logger.warning("Checksum mismatch for local file %s\n"
679 "Cleaning and trying again." % ud.localpath)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500680 if os.path.exists(ud.localpath):
681 rename_bad_checksum(ud, e.checksum)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500682 bb.utils.remove(ud.donestamp)
683 return False
684
685
686def update_stamp(ud, d):
687 """
688 donestamp is file stamp indicating the whole fetching is done
689 this function update the stamp after verifying the checksum
690 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500691 if not ud.needdonestamp:
692 return
693
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500694 if os.path.exists(ud.donestamp):
695 # Touch the done stamp file to show active use of the download
696 try:
697 os.utime(ud.donestamp, None)
698 except:
699 # Errors aren't fatal here
700 pass
701 else:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500702 try:
703 checksums = verify_checksum(ud, d)
704 # Store the checksums for later re-verification against the recipe
705 with open(ud.donestamp, "wb") as cachefile:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600706 p = pickle.Pickler(cachefile, 2)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500707 p.dump(checksums)
708 except ChecksumError as e:
709 # Checksums failed to verify, trigger re-download and remove the
710 # incorrect stamp file.
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600711 logger.warning("Checksum mismatch for local file %s\n"
712 "Cleaning and trying again." % ud.localpath)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500713 if os.path.exists(ud.localpath):
714 rename_bad_checksum(ud, e.checksum)
715 bb.utils.remove(ud.donestamp)
716 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500717
718def subprocess_setup():
719 # Python installs a SIGPIPE handler by default. This is usually not what
720 # non-Python subprocesses expect.
721 # SIGPIPE errors are known issues with gzip/bash
722 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
723
724def get_autorev(d):
725 # only not cache src rev in autorev case
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500726 if d.getVar('BB_SRCREV_POLICY') != "cache":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500727 d.setVar('BB_DONT_CACHE', '1')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500728 return "AUTOINC"
729
730def get_srcrev(d, method_name='sortable_revision'):
731 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500732 Return the revision string, usually for use in the version string (PV) of the current package
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500733 Most packages usually only have one SCM so we just pass on the call.
734 In the multi SCM case, we build a value based on SRCREV_FORMAT which must
735 have been set.
736
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500737 The idea here is that we put the string "AUTOINC+" into return value if the revisions are not
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500738 incremental, other code is then responsible for turning that into an increasing value (if needed)
739
740 A method_name can be supplied to retrieve an alternatively formatted revision from a fetcher, if
741 that fetcher provides a method with the given name and the same signature as sortable_revision.
742 """
743
744 scms = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500745 fetcher = Fetch(d.getVar('SRC_URI').split(), d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746 urldata = fetcher.ud
747 for u in urldata:
748 if urldata[u].method.supports_srcrev():
749 scms.append(u)
750
751 if len(scms) == 0:
752 raise FetchError("SRCREV was used yet no valid SCM was found in SRC_URI")
753
754 if len(scms) == 1 and len(urldata[scms[0]].names) == 1:
755 autoinc, rev = getattr(urldata[scms[0]].method, method_name)(urldata[scms[0]], d, urldata[scms[0]].names[0])
756 if len(rev) > 10:
757 rev = rev[:10]
758 if autoinc:
759 return "AUTOINC+" + rev
760 return rev
761
762 #
763 # Mutiple SCMs are in SRC_URI so we resort to SRCREV_FORMAT
764 #
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500765 format = d.getVar('SRCREV_FORMAT')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500766 if not format:
Brad Bishop19323692019-04-05 15:28:33 -0400767 raise FetchError("The SRCREV_FORMAT variable must be set when multiple SCMs are used.\n"\
768 "The SCMs are:\n%s" % '\n'.join(scms))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500769
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600770 name_to_rev = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500771 seenautoinc = False
772 for scm in scms:
773 ud = urldata[scm]
774 for name in ud.names:
775 autoinc, rev = getattr(ud.method, method_name)(ud, d, name)
776 seenautoinc = seenautoinc or autoinc
777 if len(rev) > 10:
778 rev = rev[:10]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600779 name_to_rev[name] = rev
780 # Replace names by revisions in the SRCREV_FORMAT string. The approach used
781 # here can handle names being prefixes of other names and names appearing
782 # as substrings in revisions (in which case the name should not be
783 # expanded). The '|' regular expression operator tries matches from left to
784 # right, so we need to sort the names with the longest ones first.
785 names_descending_len = sorted(name_to_rev, key=len, reverse=True)
786 name_to_rev_re = "|".join(re.escape(name) for name in names_descending_len)
787 format = re.sub(name_to_rev_re, lambda match: name_to_rev[match.group(0)], format)
788
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500789 if seenautoinc:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500790 format = "AUTOINC+" + format
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791
792 return format
793
794def localpath(url, d):
795 fetcher = bb.fetch2.Fetch([url], d)
796 return fetcher.localpath(url)
797
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600798def runfetchcmd(cmd, d, quiet=False, cleanup=None, log=None, workdir=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500799 """
800 Run cmd returning the command output
801 Raise an error if interrupted or cmd fails
802 Optionally echo command output to stdout
803 Optionally remove the files/directories listed in cleanup upon failure
804 """
805
806 # Need to export PATH as binary could be in metadata paths
807 # rather than host provided
808 # Also include some other variables.
809 # FIXME: Should really include all export varaiables?
810 exportvars = ['HOME', 'PATH',
811 'HTTP_PROXY', 'http_proxy',
812 'HTTPS_PROXY', 'https_proxy',
813 'FTP_PROXY', 'ftp_proxy',
814 'FTPS_PROXY', 'ftps_proxy',
815 'NO_PROXY', 'no_proxy',
816 'ALL_PROXY', 'all_proxy',
817 'GIT_PROXY_COMMAND',
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800818 'GIT_SSH',
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500819 'GIT_SSL_CAINFO',
820 'GIT_SMART_HTTP',
821 'SSH_AUTH_SOCK', 'SSH_AGENT_PID',
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600822 'SOCKS5_USER', 'SOCKS5_PASSWD',
823 'DBUS_SESSION_BUS_ADDRESS',
824 'P4CONFIG']
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500825
826 if not cleanup:
827 cleanup = []
828
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800829 # If PATH contains WORKDIR which contains PV-PR which contains SRCPV we
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500830 # can end up in circular recursion here so give the option of breaking it
831 # in a data store copy.
832 try:
833 d.getVar("PV")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800834 d.getVar("PR")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500835 except bb.data_smart.ExpansionError:
836 d = bb.data.createCopy(d)
837 d.setVar("PV", "fetcheravoidrecurse")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800838 d.setVar("PR", "fetcheravoidrecurse")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500839
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600840 origenv = d.getVar("BB_ORIGENV", False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500841 for var in exportvars:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500842 val = d.getVar(var) or (origenv and origenv.getVar(var))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500843 if val:
844 cmd = 'export ' + var + '=\"%s\"; %s' % (val, cmd)
845
Brad Bishop15ae2502019-06-18 21:44:24 -0400846 # Ensure that a _PYTHON_SYSCONFIGDATA_NAME value set by a recipe
847 # (for example via python3native.bbclass since warrior) is not set for
848 # host Python (otherwise tools like git-make-shallow will fail)
849 cmd = 'unset _PYTHON_SYSCONFIGDATA_NAME; ' + cmd
850
Brad Bishop316dfdd2018-06-25 12:45:53 -0400851 # Disable pseudo as it may affect ssh, potentially causing it to hang.
852 cmd = 'export PSEUDO_DISABLED=1; ' + cmd
853
Brad Bishop19323692019-04-05 15:28:33 -0400854 if workdir:
855 logger.debug(1, "Running '%s' in %s" % (cmd, workdir))
856 else:
857 logger.debug(1, "Running %s", cmd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500858
859 success = False
860 error_message = ""
861
862 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600863 (output, errors) = bb.process.run(cmd, log=log, shell=True, stderr=subprocess.PIPE, cwd=workdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500864 success = True
865 except bb.process.NotFoundError as e:
866 error_message = "Fetch command %s" % (e.command)
867 except bb.process.ExecutionError as e:
868 if e.stdout:
869 output = "output:\n%s\n%s" % (e.stdout, e.stderr)
870 elif e.stderr:
871 output = "output:\n%s" % e.stderr
872 else:
873 output = "no output"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600874 error_message = "Fetch command %s failed with exit code %s, %s" % (e.command, e.exitcode, output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500875 except bb.process.CmdError as e:
876 error_message = "Fetch command %s could not be run:\n%s" % (e.command, e.msg)
877 if not success:
878 for f in cleanup:
879 try:
880 bb.utils.remove(f, True)
881 except OSError:
882 pass
883
884 raise FetchError(error_message)
885
886 return output
887
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500888def check_network_access(d, info, url):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500889 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500890 log remote network access, and error if BB_NO_NETWORK is set or the given
891 URI is untrusted
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500892 """
Brad Bishop19323692019-04-05 15:28:33 -0400893 if bb.utils.to_boolean(d.getVar("BB_NO_NETWORK")):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500894 raise NetworkAccess(url, info)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500895 elif not trusted_network(d, url):
896 raise UntrustedUrl(url, info)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500897 else:
898 logger.debug(1, "Fetcher accessed the network with the command %s" % info)
899
900def build_mirroruris(origud, mirrors, ld):
901 uris = []
902 uds = []
903
904 replacements = {}
905 replacements["TYPE"] = origud.type
906 replacements["HOST"] = origud.host
907 replacements["PATH"] = origud.path
908 replacements["BASENAME"] = origud.path.split("/")[-1]
909 replacements["MIRRORNAME"] = origud.host.replace(':','.') + origud.path.replace('/', '.').replace('*', '.')
910
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500911 def adduri(ud, uris, uds, mirrors, tarballs):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500912 for line in mirrors:
913 try:
914 (find, replace) = line
915 except ValueError:
916 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500917
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500918 for tarball in tarballs:
919 newuri = uri_replace(ud, find, replace, replacements, ld, tarball)
920 if not newuri or newuri in uris or newuri == origud.url:
921 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500922
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500923 if not trusted_network(ld, newuri):
924 logger.debug(1, "Mirror %s not in the list of trusted networks, skipping" % (newuri))
925 continue
Patrick Williamsd7e96312015-09-22 08:09:05 -0500926
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500927 # Create a local copy of the mirrors minus the current line
928 # this will prevent us from recursively processing the same line
929 # as well as indirect recursion A -> B -> C -> A
930 localmirrors = list(mirrors)
931 localmirrors.remove(line)
932
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500933 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500934 newud = FetchData(newuri, ld)
935 newud.setup_localpath(ld)
936 except bb.fetch2.BBFetchException as e:
937 logger.debug(1, "Mirror fetch failure for url %s (original url: %s)" % (newuri, origud.url))
938 logger.debug(1, str(e))
939 try:
940 # setup_localpath of file:// urls may fail, we should still see
941 # if mirrors of the url exist
942 adduri(newud, uris, uds, localmirrors, tarballs)
943 except UnboundLocalError:
944 pass
945 continue
946 uris.append(newuri)
947 uds.append(newud)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500948
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500949 adduri(newud, uris, uds, localmirrors, tarballs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500950
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500951 adduri(origud, uris, uds, mirrors, origud.mirrortarballs or [None])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500952
953 return uris, uds
954
955def rename_bad_checksum(ud, suffix):
956 """
957 Renames files to have suffix from parameter
958 """
959
960 if ud.localpath is None:
961 return
962
963 new_localpath = "%s_bad-checksum_%s" % (ud.localpath, suffix)
964 bb.warn("Renaming %s to %s" % (ud.localpath, new_localpath))
Brad Bishop79641f22019-09-10 07:20:22 -0400965 if not bb.utils.movefile(ud.localpath, new_localpath):
966 bb.warn("Renaming %s to %s failed, grep movefile in log.do_fetch to see why" % (ud.localpath, new_localpath))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500967
968
969def try_mirror_url(fetch, origud, ud, ld, check = False):
970 # Return of None or a value means we're finished
971 # False means try another url
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500972
973 if ud.lockfile and ud.lockfile != origud.lockfile:
974 lf = bb.utils.lockfile(ud.lockfile)
975
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500976 try:
977 if check:
978 found = ud.method.checkstatus(fetch, ud, ld)
979 if found:
980 return found
981 return False
982
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500983 if not verify_donestamp(ud, ld, origud) or ud.method.need_update(ud, ld):
984 ud.method.download(ud, ld)
985 if hasattr(ud.method,"build_mirror_data"):
986 ud.method.build_mirror_data(ud, ld)
987
988 if not ud.localpath or not os.path.exists(ud.localpath):
989 return False
990
991 if ud.localpath == origud.localpath:
992 return ud.localpath
993
994 # We may be obtaining a mirror tarball which needs further processing by the real fetcher
995 # If that tarball is a local file:// we need to provide a symlink to it
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500996 dldir = ld.getVar("DL_DIR")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500997
998 if origud.mirrortarballs and os.path.basename(ud.localpath) in origud.mirrortarballs and os.path.basename(ud.localpath) != os.path.basename(origud.localpath):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500999 # Create donestamp in old format to avoid triggering a re-download
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001000 if ud.donestamp:
1001 bb.utils.mkdirhier(os.path.dirname(ud.donestamp))
1002 open(ud.donestamp, 'w').close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001003 dest = os.path.join(dldir, os.path.basename(ud.localpath))
1004 if not os.path.exists(dest):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001005 # In case this is executing without any file locks held (as is
1006 # the case for file:// URLs), two tasks may end up here at the
1007 # same time, in which case we do not want the second task to
1008 # fail when the link has already been created by the first task.
1009 try:
1010 os.symlink(ud.localpath, dest)
1011 except FileExistsError:
1012 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001013 if not verify_donestamp(origud, ld) or origud.method.need_update(origud, ld):
1014 origud.method.download(origud, ld)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001015 if hasattr(origud.method, "build_mirror_data"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001016 origud.method.build_mirror_data(origud, ld)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001017 return origud.localpath
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001018 # Otherwise the result is a local file:// and we symlink to it
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001019 ensure_symlink(ud.localpath, origud.localpath)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001020 update_stamp(origud, ld)
1021 return ud.localpath
1022
1023 except bb.fetch2.NetworkAccess:
1024 raise
1025
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001026 except IOError as e:
Brad Bishop19323692019-04-05 15:28:33 -04001027 if e.errno in [errno.ESTALE]:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001028 logger.warning("Stale Error Observed %s." % ud.url)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001029 return False
1030 raise
1031
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001032 except bb.fetch2.BBFetchException as e:
1033 if isinstance(e, ChecksumError):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001034 logger.warning("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (ud.url, origud.url))
1035 logger.warning(str(e))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001036 if os.path.exists(ud.localpath):
1037 rename_bad_checksum(ud, e.checksum)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001038 elif isinstance(e, NoChecksumError):
1039 raise
1040 else:
1041 logger.debug(1, "Mirror fetch failure for url %s (original url: %s)" % (ud.url, origud.url))
1042 logger.debug(1, str(e))
1043 try:
1044 ud.method.clean(ud, ld)
1045 except UnboundLocalError:
1046 pass
1047 return False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001048 finally:
1049 if ud.lockfile and ud.lockfile != origud.lockfile:
1050 bb.utils.unlockfile(lf)
1051
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001052
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001053def ensure_symlink(target, link_name):
1054 if not os.path.exists(link_name):
1055 if os.path.islink(link_name):
1056 # Broken symbolic link
1057 os.unlink(link_name)
1058
1059 # In case this is executing without any file locks held (as is
1060 # the case for file:// URLs), two tasks may end up here at the
1061 # same time, in which case we do not want the second task to
1062 # fail when the link has already been created by the first task.
1063 try:
1064 os.symlink(target, link_name)
1065 except FileExistsError:
1066 pass
1067
1068
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001069def try_mirrors(fetch, d, origud, mirrors, check = False):
1070 """
1071 Try to use a mirrored version of the sources.
1072 This method will be automatically called before the fetchers go.
1073
1074 d Is a bb.data instance
1075 uri is the original uri we're trying to download
1076 mirrors is the list of mirrors we're going to try
1077 """
1078 ld = d.createCopy()
1079
1080 uris, uds = build_mirroruris(origud, mirrors, ld)
1081
1082 for index, uri in enumerate(uris):
1083 ret = try_mirror_url(fetch, origud, uds[index], ld, check)
1084 if ret != False:
1085 return ret
1086 return None
1087
1088def trusted_network(d, url):
1089 """
1090 Use a trusted url during download if networking is enabled and
1091 BB_ALLOWED_NETWORKS is set globally or for a specific recipe.
1092 Note: modifies SRC_URI & mirrors.
1093 """
Brad Bishop19323692019-04-05 15:28:33 -04001094 if bb.utils.to_boolean(d.getVar("BB_NO_NETWORK")):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001095 return True
1096
1097 pkgname = d.expand(d.getVar('PN', False))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001098 trusted_hosts = None
1099 if pkgname:
1100 trusted_hosts = d.getVarFlag('BB_ALLOWED_NETWORKS', pkgname, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001101
1102 if not trusted_hosts:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001103 trusted_hosts = d.getVar('BB_ALLOWED_NETWORKS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001104
1105 # Not enabled.
1106 if not trusted_hosts:
1107 return True
1108
1109 scheme, network, path, user, passwd, param = decodeurl(url)
1110
1111 if not network:
1112 return True
1113
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001114 network = network.split(':')[0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001115 network = network.lower()
1116
1117 for host in trusted_hosts.split(" "):
1118 host = host.lower()
1119 if host.startswith("*.") and ("." + network).endswith(host[1:]):
1120 return True
1121 if host == network:
1122 return True
1123
1124 return False
1125
1126def srcrev_internal_helper(ud, d, name):
1127 """
1128 Return:
1129 a) a source revision if specified
1130 b) latest revision if SRCREV="AUTOINC"
1131 c) None if not specified
1132 """
1133
1134 srcrev = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001135 pn = d.getVar("PN")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001136 attempts = []
1137 if name != '' and pn:
1138 attempts.append("SRCREV_%s_pn-%s" % (name, pn))
1139 if name != '':
1140 attempts.append("SRCREV_%s" % name)
1141 if pn:
1142 attempts.append("SRCREV_pn-%s" % pn)
1143 attempts.append("SRCREV")
1144
1145 for a in attempts:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001146 srcrev = d.getVar(a)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001147 if srcrev and srcrev != "INVALID":
1148 break
1149
1150 if 'rev' in ud.parm and 'tag' in ud.parm:
1151 raise FetchError("Please specify a ;rev= parameter or a ;tag= parameter in the url %s but not both." % (ud.url))
1152
1153 if 'rev' in ud.parm or 'tag' in ud.parm:
1154 if 'rev' in ud.parm:
1155 parmrev = ud.parm['rev']
1156 else:
1157 parmrev = ud.parm['tag']
1158 if srcrev == "INVALID" or not srcrev:
1159 return parmrev
1160 if srcrev != parmrev:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001161 raise FetchError("Conflicting revisions (%s from SRCREV and %s from the url) found, please specify one valid value" % (srcrev, parmrev))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001162 return parmrev
1163
1164 if srcrev == "INVALID" or not srcrev:
1165 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)
1166 if srcrev == "AUTOINC":
1167 srcrev = ud.method.latest_revision(ud, d, name)
1168
1169 return srcrev
1170
1171def get_checksum_file_list(d):
1172 """ Get a list of files checksum in SRC_URI
1173
1174 Returns the resolved local paths of all local file entries in
1175 SRC_URI as a space-separated string
1176 """
1177 fetch = Fetch([], d, cache = False, localonly = True)
1178
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001179 dl_dir = d.getVar('DL_DIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001180 filelist = []
1181 for u in fetch.urls:
1182 ud = fetch.ud[u]
1183
1184 if ud and isinstance(ud.method, local.Local):
1185 paths = ud.method.localpaths(ud, d)
1186 for f in paths:
1187 pth = ud.decodedurl
1188 if '*' in pth:
1189 f = os.path.join(os.path.abspath(f), pth)
1190 if f.startswith(dl_dir):
1191 # The local fetcher's behaviour is to return a path under DL_DIR if it couldn't find the file anywhere else
1192 if os.path.exists(f):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001193 bb.warn("Getting checksum for %s SRC_URI entry %s: file not found except in DL_DIR" % (d.getVar('PN'), os.path.basename(f)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001194 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001195 bb.warn("Unable to get checksum for %s SRC_URI entry %s: file could not be found" % (d.getVar('PN'), os.path.basename(f)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001196 filelist.append(f + ":" + str(os.path.exists(f)))
1197
1198 return " ".join(filelist)
1199
1200def get_file_checksums(filelist, pn):
1201 """Get a list of the checksums for a list of local files
1202
1203 Returns the checksums for a list of local files, caching the results as
1204 it proceeds
1205
1206 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001207 return _checksum_cache.get_checksums(filelist, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001208
1209
1210class FetchData(object):
1211 """
1212 A class which represents the fetcher state for a given URI.
1213 """
1214 def __init__(self, url, d, localonly = False):
1215 # localpath is the location of a downloaded result. If not set, the file is local.
1216 self.donestamp = None
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001217 self.needdonestamp = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001218 self.localfile = ""
1219 self.localpath = None
1220 self.lockfile = None
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001221 self.mirrortarballs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001222 self.basename = None
1223 self.basepath = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001224 (self.type, self.host, self.path, self.user, self.pswd, self.parm) = decodeurl(d.expand(url))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001225 self.date = self.getSRCDate(d)
1226 self.url = url
1227 if not self.user and "user" in self.parm:
1228 self.user = self.parm["user"]
1229 if not self.pswd and "pswd" in self.parm:
1230 self.pswd = self.parm["pswd"]
1231 self.setup = False
1232
1233 if "name" in self.parm:
1234 self.md5_name = "%s.md5sum" % self.parm["name"]
1235 self.sha256_name = "%s.sha256sum" % self.parm["name"]
1236 else:
1237 self.md5_name = "md5sum"
1238 self.sha256_name = "sha256sum"
1239 if self.md5_name in self.parm:
1240 self.md5_expected = self.parm[self.md5_name]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001241 elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001242 self.md5_expected = None
1243 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001244 self.md5_expected = d.getVarFlag("SRC_URI", self.md5_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001245 if self.sha256_name in self.parm:
1246 self.sha256_expected = self.parm[self.sha256_name]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001247 elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001248 self.sha256_expected = None
1249 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001250 self.sha256_expected = d.getVarFlag("SRC_URI", self.sha256_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001251 self.ignore_checksums = False
1252
1253 self.names = self.parm.get("name",'default').split(',')
1254
1255 self.method = None
1256 for m in methods:
1257 if m.supports(self, d):
1258 self.method = m
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001259 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001260
1261 if not self.method:
1262 raise NoMethodError(url)
1263
1264 if localonly and not isinstance(self.method, local.Local):
1265 raise NonLocalMethod()
1266
1267 if self.parm.get("proto", None) and "protocol" not in self.parm:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001268 logger.warning('Consider updating %s recipe to use "protocol" not "proto" in SRC_URI.', d.getVar('PN'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001269 self.parm["protocol"] = self.parm.get("proto", None)
1270
1271 if hasattr(self.method, "urldata_init"):
1272 self.method.urldata_init(self, d)
1273
1274 if "localpath" in self.parm:
1275 # if user sets localpath for file, use it instead.
1276 self.localpath = self.parm["localpath"]
1277 self.basename = os.path.basename(self.localpath)
1278 elif self.localfile:
1279 self.localpath = self.method.localpath(self, d)
1280
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001281 dldir = d.getVar("DL_DIR")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001282
1283 if not self.needdonestamp:
1284 return
1285
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001286 # Note: .done and .lock files should always be in DL_DIR whereas localpath may not be.
1287 if self.localpath and self.localpath.startswith(dldir):
1288 basepath = self.localpath
1289 elif self.localpath:
1290 basepath = dldir + os.sep + os.path.basename(self.localpath)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001291 elif self.basepath or self.basename:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001292 basepath = dldir + os.sep + (self.basepath or self.basename)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001293 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001294 bb.fatal("Can't determine lock path for url %s" % url)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001295
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001296 self.donestamp = basepath + '.done'
1297 self.lockfile = basepath + '.lock'
1298
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001299 def setup_revisions(self, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001300 self.revisions = {}
1301 for name in self.names:
1302 self.revisions[name] = srcrev_internal_helper(self, d, name)
1303
1304 # add compatibility code for non name specified case
1305 if len(self.names) == 1:
1306 self.revision = self.revisions[self.names[0]]
1307
1308 def setup_localpath(self, d):
1309 if not self.localpath:
1310 self.localpath = self.method.localpath(self, d)
1311
1312 def getSRCDate(self, d):
1313 """
1314 Return the SRC Date for the component
1315
1316 d the bb.data module
1317 """
1318 if "srcdate" in self.parm:
1319 return self.parm['srcdate']
1320
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001321 pn = d.getVar("PN")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001322
1323 if pn:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001324 return d.getVar("SRCDATE_%s" % pn) or d.getVar("SRCDATE") or d.getVar("DATE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001325
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001326 return d.getVar("SRCDATE") or d.getVar("DATE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001327
1328class FetchMethod(object):
1329 """Base class for 'fetch'ing data"""
1330
1331 def __init__(self, urls=None):
1332 self.urls = []
1333
1334 def supports(self, urldata, d):
1335 """
1336 Check to see if this fetch class supports a given url.
1337 """
1338 return 0
1339
1340 def localpath(self, urldata, d):
1341 """
1342 Return the local filename of a given url assuming a successful fetch.
1343 Can also setup variables in urldata for use in go (saving code duplication
1344 and duplicate code execution)
1345 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001346 return os.path.join(d.getVar("DL_DIR"), urldata.localfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001347
1348 def supports_checksum(self, urldata):
1349 """
1350 Is localpath something that can be represented by a checksum?
1351 """
1352
1353 # We cannot compute checksums for directories
1354 if os.path.isdir(urldata.localpath) == True:
1355 return False
1356 if urldata.localpath.find("*") != -1:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001357 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001358
1359 return True
1360
1361 def recommends_checksum(self, urldata):
1362 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001363 Is the backend on where checksumming is recommended (should warnings
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001364 be displayed if there is no checksum)?
1365 """
1366 return False
1367
1368 def _strip_leading_slashes(self, relpath):
1369 """
1370 Remove leading slash as os.path.join can't cope
1371 """
1372 while os.path.isabs(relpath):
1373 relpath = relpath[1:]
1374 return relpath
1375
1376 def setUrls(self, urls):
1377 self.__urls = urls
1378
1379 def getUrls(self):
1380 return self.__urls
1381
1382 urls = property(getUrls, setUrls, None, "Urls property")
1383
1384 def need_update(self, ud, d):
1385 """
1386 Force a fetch, even if localpath exists?
1387 """
1388 if os.path.exists(ud.localpath):
1389 return False
1390 return True
1391
1392 def supports_srcrev(self):
1393 """
1394 The fetcher supports auto source revisions (SRCREV)
1395 """
1396 return False
1397
1398 def download(self, urldata, d):
1399 """
1400 Fetch urls
1401 Assumes localpath was called first
1402 """
Brad Bishop19323692019-04-05 15:28:33 -04001403 raise NoMethodError(urldata.url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001404
1405 def unpack(self, urldata, rootdir, data):
1406 iterate = False
1407 file = urldata.localpath
1408
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001409 # Localpath can't deal with 'dir/*' entries, so it converts them to '.',
1410 # but it must be corrected back for local files copying
1411 if urldata.basename == '*' and file.endswith('/.'):
1412 file = '%s/%s' % (file.rstrip('/.'), urldata.path)
1413
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001414 try:
1415 unpack = bb.utils.to_boolean(urldata.parm.get('unpack'), True)
1416 except ValueError as exc:
1417 bb.fatal("Invalid value for 'unpack' parameter for %s: %s" %
1418 (file, urldata.parm.get('unpack')))
1419
1420 base, ext = os.path.splitext(file)
1421 if ext in ['.gz', '.bz2', '.Z', '.xz', '.lz']:
1422 efile = os.path.join(rootdir, os.path.basename(base))
1423 else:
1424 efile = file
1425 cmd = None
1426
1427 if unpack:
1428 if file.endswith('.tar'):
1429 cmd = 'tar x --no-same-owner -f %s' % file
1430 elif file.endswith('.tgz') or file.endswith('.tar.gz') or file.endswith('.tar.Z'):
1431 cmd = 'tar xz --no-same-owner -f %s' % file
1432 elif file.endswith('.tbz') or file.endswith('.tbz2') or file.endswith('.tar.bz2'):
1433 cmd = 'bzip2 -dc %s | tar x --no-same-owner -f -' % file
1434 elif file.endswith('.gz') or file.endswith('.Z') or file.endswith('.z'):
1435 cmd = 'gzip -dc %s > %s' % (file, efile)
1436 elif file.endswith('.bz2'):
1437 cmd = 'bzip2 -dc %s > %s' % (file, efile)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001438 elif file.endswith('.txz') or file.endswith('.tar.xz'):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001439 cmd = 'xz -dc %s | tar x --no-same-owner -f -' % file
1440 elif file.endswith('.xz'):
1441 cmd = 'xz -dc %s > %s' % (file, efile)
1442 elif file.endswith('.tar.lz'):
1443 cmd = 'lzip -dc %s | tar x --no-same-owner -f -' % file
1444 elif file.endswith('.lz'):
1445 cmd = 'lzip -dc %s > %s' % (file, efile)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001446 elif file.endswith('.tar.7z'):
1447 cmd = '7z x -so %s | tar x --no-same-owner -f -' % file
1448 elif file.endswith('.7z'):
1449 cmd = '7za x -y %s 1>/dev/null' % file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001450 elif file.endswith('.zip') or file.endswith('.jar'):
1451 try:
1452 dos = bb.utils.to_boolean(urldata.parm.get('dos'), False)
1453 except ValueError as exc:
1454 bb.fatal("Invalid value for 'dos' parameter for %s: %s" %
1455 (file, urldata.parm.get('dos')))
1456 cmd = 'unzip -q -o'
1457 if dos:
1458 cmd = '%s -a' % cmd
1459 cmd = "%s '%s'" % (cmd, file)
1460 elif file.endswith('.rpm') or file.endswith('.srpm'):
1461 if 'extract' in urldata.parm:
1462 unpack_file = urldata.parm.get('extract')
1463 cmd = 'rpm2cpio.sh %s | cpio -id %s' % (file, unpack_file)
1464 iterate = True
1465 iterate_file = unpack_file
1466 else:
1467 cmd = 'rpm2cpio.sh %s | cpio -id' % (file)
1468 elif file.endswith('.deb') or file.endswith('.ipk'):
Brad Bishopa5c52ff2018-11-23 10:55:50 +13001469 output = subprocess.check_output(['ar', '-t', file], preexec_fn=subprocess_setup)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001470 datafile = None
1471 if output:
1472 for line in output.decode().splitlines():
1473 if line.startswith('data.tar.'):
1474 datafile = line
1475 break
1476 else:
1477 raise UnpackError("Unable to unpack deb/ipk package - does not contain data.tar.* file", urldata.url)
1478 else:
1479 raise UnpackError("Unable to unpack deb/ipk package - could not list contents", urldata.url)
1480 cmd = 'ar x %s %s && tar --no-same-owner -xpf %s && rm %s' % (file, datafile, datafile, datafile)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001481
1482 # If 'subdir' param exists, create a dir and use it as destination for unpack cmd
1483 if 'subdir' in urldata.parm:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001484 subdir = urldata.parm.get('subdir')
1485 if os.path.isabs(subdir):
1486 if not os.path.realpath(subdir).startswith(os.path.realpath(rootdir)):
1487 raise UnpackError("subdir argument isn't a subdirectory of unpack root %s" % rootdir, urldata.url)
1488 unpackdir = subdir
1489 else:
1490 unpackdir = os.path.join(rootdir, subdir)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001491 bb.utils.mkdirhier(unpackdir)
1492 else:
1493 unpackdir = rootdir
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001494
1495 if not unpack or not cmd:
1496 # If file == dest, then avoid any copies, as we already put the file into dest!
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001497 dest = os.path.join(unpackdir, os.path.basename(file))
1498 if file != dest and not (os.path.exists(dest) and os.path.samefile(file, dest)):
1499 destdir = '.'
1500 # For file:// entries all intermediate dirs in path must be created at destination
1501 if urldata.type == "file":
1502 # Trailing '/' does a copying to wrong place
1503 urlpath = urldata.path.rstrip('/')
1504 # Want files places relative to cwd so no leading '/'
1505 urlpath = urlpath.lstrip('/')
1506 if urlpath.find("/") != -1:
1507 destdir = urlpath.rsplit("/", 1)[0] + '/'
1508 bb.utils.mkdirhier("%s/%s" % (unpackdir, destdir))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001509 cmd = 'cp -fpPRH %s %s' % (file, destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001510
1511 if not cmd:
1512 return
1513
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001514 path = data.getVar('PATH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001515 if path:
1516 cmd = "PATH=\"%s\" %s" % (path, cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001517 bb.note("Unpacking %s to %s/" % (file, unpackdir))
1518 ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True, cwd=unpackdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001519
1520 if ret != 0:
1521 raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), urldata.url)
1522
1523 if iterate is True:
1524 iterate_urldata = urldata
1525 iterate_urldata.localpath = "%s/%s" % (rootdir, iterate_file)
1526 self.unpack(urldata, rootdir, data)
1527
1528 return
1529
1530 def clean(self, urldata, d):
1531 """
1532 Clean any existing full or partial download
1533 """
1534 bb.utils.remove(urldata.localpath)
1535
1536 def try_premirror(self, urldata, d):
1537 """
1538 Should premirrors be used?
1539 """
1540 return True
1541
1542 def checkstatus(self, fetch, urldata, d):
1543 """
1544 Check the status of a URL
1545 Assumes localpath was called first
1546 """
Brad Bishop19323692019-04-05 15:28:33 -04001547 logger.info("URL %s could not be checked for status since no method exists.", urldata.url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001548 return True
1549
1550 def latest_revision(self, ud, d, name):
1551 """
1552 Look in the cache for the latest revision, if not present ask the SCM.
1553 """
1554 if not hasattr(self, "_latest_revision"):
Brad Bishop19323692019-04-05 15:28:33 -04001555 raise ParameterError("The fetcher for this URL does not support _latest_revision", ud.url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001556
1557 revs = bb.persist_data.persist('BB_URI_HEADREVS', d)
1558 key = self.generate_revision_key(ud, d, name)
1559 try:
1560 return revs[key]
1561 except KeyError:
1562 revs[key] = rev = self._latest_revision(ud, d, name)
1563 return rev
1564
1565 def sortable_revision(self, ud, d, name):
1566 latest_rev = self._build_revision(ud, d, name)
1567 return True, str(latest_rev)
1568
1569 def generate_revision_key(self, ud, d, name):
1570 key = self._revision_key(ud, d, name)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001571 return "%s-%s" % (key, d.getVar("PN") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001572
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001573 def latest_versionstring(self, ud, d):
1574 """
1575 Compute the latest release name like "x.y.x" in "x.y.x+gitHASH"
1576 by searching through the tags output of ls-remote, comparing
1577 versions and returning the highest match as a (version, revision) pair.
1578 """
1579 return ('', '')
1580
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001581class Fetch(object):
1582 def __init__(self, urls, d, cache = True, localonly = False, connection_cache = None):
1583 if localonly and cache:
1584 raise Exception("bb.fetch2.Fetch.__init__: cannot set cache and localonly at same time")
1585
1586 if len(urls) == 0:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001587 urls = d.getVar("SRC_URI").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001588 self.urls = urls
1589 self.d = d
1590 self.ud = {}
1591 self.connection_cache = connection_cache
1592
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001593 fn = d.getVar('FILE')
1594 mc = d.getVar('__BBMULTICONFIG') or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001595 if cache and fn and mc + fn in urldata_cache:
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001596 self.ud = urldata_cache[mc + fn + str(id(d))]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001597
1598 for url in urls:
1599 if url not in self.ud:
1600 try:
1601 self.ud[url] = FetchData(url, d, localonly)
1602 except NonLocalMethod:
1603 if localonly:
1604 self.ud[url] = None
1605 pass
1606
1607 if fn and cache:
Brad Bishop1d80a2e2019-11-15 16:35:03 -05001608 urldata_cache[mc + fn + str(id(d))] = self.ud
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001609
1610 def localpath(self, url):
1611 if url not in self.urls:
1612 self.ud[url] = FetchData(url, self.d)
1613
1614 self.ud[url].setup_localpath(self.d)
1615 return self.d.expand(self.ud[url].localpath)
1616
1617 def localpaths(self):
1618 """
1619 Return a list of the local filenames, assuming successful fetch
1620 """
1621 local = []
1622
1623 for u in self.urls:
1624 ud = self.ud[u]
1625 ud.setup_localpath(self.d)
1626 local.append(ud.localpath)
1627
1628 return local
1629
1630 def download(self, urls=None):
1631 """
1632 Fetch all urls
1633 """
1634 if not urls:
1635 urls = self.urls
1636
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001637 network = self.d.getVar("BB_NO_NETWORK")
Brad Bishop19323692019-04-05 15:28:33 -04001638 premirroronly = bb.utils.to_boolean(self.d.getVar("BB_FETCH_PREMIRRORONLY"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001639
1640 for u in urls:
1641 ud = self.ud[u]
1642 ud.setup_localpath(self.d)
1643 m = ud.method
1644 localpath = ""
1645
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001646 if ud.lockfile:
1647 lf = bb.utils.lockfile(ud.lockfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001648
1649 try:
1650 self.d.setVar("BB_NO_NETWORK", network)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001651
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001652 if verify_donestamp(ud, self.d) and not m.need_update(ud, self.d):
1653 localpath = ud.localpath
1654 elif m.try_premirror(ud, self.d):
1655 logger.debug(1, "Trying PREMIRRORS")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001656 mirrors = mirror_from_string(self.d.getVar('PREMIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001657 localpath = try_mirrors(self, self.d, ud, mirrors, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001658 if localpath:
1659 try:
1660 # early checksum verification so that if the checksum of the premirror
1661 # contents mismatch the fetcher can still try upstream and mirrors
1662 update_stamp(ud, self.d)
1663 except ChecksumError as e:
1664 logger.warning("Checksum failure encountered with premirror download of %s - will attempt other sources." % u)
1665 logger.debug(1, str(e))
1666 localpath = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001667
1668 if premirroronly:
1669 self.d.setVar("BB_NO_NETWORK", "1")
1670
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001671 firsterr = None
1672 verified_stamp = verify_donestamp(ud, self.d)
1673 if not localpath and (not verified_stamp or m.need_update(ud, self.d)):
1674 try:
1675 if not trusted_network(self.d, ud.url):
1676 raise UntrustedUrl(ud.url)
1677 logger.debug(1, "Trying Upstream")
1678 m.download(ud, self.d)
1679 if hasattr(m, "build_mirror_data"):
1680 m.build_mirror_data(ud, self.d)
1681 localpath = ud.localpath
1682 # early checksum verify, so that if checksum mismatched,
1683 # fetcher still have chance to fetch from mirror
1684 update_stamp(ud, self.d)
1685
1686 except bb.fetch2.NetworkAccess:
1687 raise
1688
1689 except BBFetchException as e:
1690 if isinstance(e, ChecksumError):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001691 logger.warning("Checksum failure encountered with download of %s - will attempt other sources if available" % u)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001692 logger.debug(1, str(e))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001693 if os.path.exists(ud.localpath):
1694 rename_bad_checksum(ud, e.checksum)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001695 elif isinstance(e, NoChecksumError):
1696 raise
1697 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001698 logger.warning('Failed to fetch URL %s, attempting MIRRORS if available' % u)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001699 logger.debug(1, str(e))
1700 firsterr = e
1701 # Remove any incomplete fetch
1702 if not verified_stamp:
1703 m.clean(ud, self.d)
1704 logger.debug(1, "Trying MIRRORS")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001705 mirrors = mirror_from_string(self.d.getVar('MIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001706 localpath = try_mirrors(self, self.d, ud, mirrors)
1707
1708 if not localpath or ((not os.path.exists(localpath)) and localpath.find("*") == -1):
1709 if firsterr:
1710 logger.error(str(firsterr))
1711 raise FetchError("Unable to fetch URL from any source.", u)
1712
1713 update_stamp(ud, self.d)
1714
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001715 except IOError as e:
Brad Bishop19323692019-04-05 15:28:33 -04001716 if e.errno in [errno.ESTALE]:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001717 logger.error("Stale Error Observed %s." % u)
1718 raise ChecksumError("Stale Error Detected")
1719
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001720 except BBFetchException as e:
1721 if isinstance(e, ChecksumError):
1722 logger.error("Checksum failure fetching %s" % u)
1723 raise
1724
1725 finally:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001726 if ud.lockfile:
1727 bb.utils.unlockfile(lf)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001728
1729 def checkstatus(self, urls=None):
1730 """
1731 Check all urls exist upstream
1732 """
1733
1734 if not urls:
1735 urls = self.urls
1736
1737 for u in urls:
1738 ud = self.ud[u]
1739 ud.setup_localpath(self.d)
1740 m = ud.method
1741 logger.debug(1, "Testing URL %s", u)
1742 # First try checking uri, u, from PREMIRRORS
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001743 mirrors = mirror_from_string(self.d.getVar('PREMIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001744 ret = try_mirrors(self, self.d, ud, mirrors, True)
1745 if not ret:
1746 # Next try checking from the original uri, u
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001747 ret = m.checkstatus(self, ud, self.d)
1748 if not ret:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001749 # Finally, try checking uri, u, from MIRRORS
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001750 mirrors = mirror_from_string(self.d.getVar('MIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001751 ret = try_mirrors(self, self.d, ud, mirrors, True)
1752
1753 if not ret:
1754 raise FetchError("URL %s doesn't work" % u, u)
1755
1756 def unpack(self, root, urls=None):
1757 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001758 Unpack urls to root
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001759 """
1760
1761 if not urls:
1762 urls = self.urls
1763
1764 for u in urls:
1765 ud = self.ud[u]
1766 ud.setup_localpath(self.d)
1767
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001768 if ud.lockfile:
1769 lf = bb.utils.lockfile(ud.lockfile)
1770
1771 ud.method.unpack(ud, root, self.d)
1772
1773 if ud.lockfile:
1774 bb.utils.unlockfile(lf)
1775
1776 def clean(self, urls=None):
1777 """
1778 Clean files that the fetcher gets or places
1779 """
1780
1781 if not urls:
1782 urls = self.urls
1783
1784 for url in urls:
1785 if url not in self.ud:
Brad Bishop19323692019-04-05 15:28:33 -04001786 self.ud[url] = FetchData(url, self.d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001787 ud = self.ud[url]
1788 ud.setup_localpath(self.d)
1789
1790 if not ud.localfile and ud.localpath is None:
1791 continue
1792
1793 if ud.lockfile:
1794 lf = bb.utils.lockfile(ud.lockfile)
1795
1796 ud.method.clean(ud, self.d)
1797 if ud.donestamp:
1798 bb.utils.remove(ud.donestamp)
1799
1800 if ud.lockfile:
1801 bb.utils.unlockfile(lf)
1802
1803class FetchConnectionCache(object):
1804 """
1805 A class which represents an container for socket connections.
1806 """
1807 def __init__(self):
1808 self.cache = {}
1809
1810 def get_connection_name(self, host, port):
1811 return host + ':' + str(port)
1812
1813 def add_connection(self, host, port, connection):
1814 cn = self.get_connection_name(host, port)
1815
1816 if cn not in self.cache:
1817 self.cache[cn] = connection
1818
1819 def get_connection(self, host, port):
1820 connection = None
1821
1822 cn = self.get_connection_name(host, port)
1823 if cn in self.cache:
1824 connection = self.cache[cn]
1825
1826 return connection
1827
1828 def remove_connection(self, host, port):
1829 cn = self.get_connection_name(host, port)
1830 if cn in self.cache:
1831 self.cache[cn].close()
1832 del self.cache[cn]
1833
1834 def close_connections(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001835 for cn in list(self.cache.keys()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001836 self.cache[cn].close()
1837 del self.cache[cn]
1838
1839from . import cvs
1840from . import git
1841from . import gitsm
1842from . import gitannex
1843from . import local
1844from . import svn
1845from . import wget
1846from . import ssh
1847from . import sftp
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001848from . import s3
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001849from . import perforce
1850from . import bzr
1851from . import hg
1852from . import osc
1853from . import repo
1854from . import clearcase
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001855from . import npm
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001856
1857methods.append(local.Local())
1858methods.append(wget.Wget())
1859methods.append(svn.Svn())
1860methods.append(git.Git())
1861methods.append(gitsm.GitSM())
1862methods.append(gitannex.GitANNEX())
1863methods.append(cvs.Cvs())
1864methods.append(ssh.SSH())
1865methods.append(sftp.SFTP())
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001866methods.append(s3.S3())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001867methods.append(perforce.Perforce())
1868methods.append(bzr.Bzr())
1869methods.append(hg.Hg())
1870methods.append(osc.Osc())
1871methods.append(repo.Repo())
1872methods.append(clearcase.ClearCase())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001873methods.append(npm.Npm())