blob: 8fecc809d41d398d217d958064369054addd0b5b [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3"""
4BitBake 'Fetch' implementations
5
6Classes for obtaining upstream sources for the
7BitBake build tools.
8"""
9
10# Copyright (C) 2003, 2004 Chris Larson
11# Copyright (C) 2012 Intel Corporation
12#
13# This program is free software; you can redistribute it and/or modify
14# it under the terms of the GNU General Public License version 2 as
15# published by the Free Software Foundation.
16#
17# This program is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20# GNU General Public License for more details.
21#
22# You should have received a copy of the GNU General Public License along
23# with this program; if not, write to the Free Software Foundation, Inc.,
24# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25#
26# Based on functions from the base bb module, Copyright 2003 Holger Schurig
27
Patrick Williamsc124f4f2015-09-15 14:41:29 -050028import os, re
29import signal
Patrick Williamsc124f4f2015-09-15 14:41:29 -050030import logging
Patrick Williamsc0f7c042017-02-23 20:41:17 -060031import urllib.request, urllib.parse, urllib.error
32if 'git' not in urllib.parse.uses_netloc:
33 urllib.parse.uses_netloc.append('git')
34import operator
35import collections
36import subprocess
37import pickle
Brad Bishop6e60e8b2018-02-01 10:27:11 -050038import errno
Patrick Williamsc124f4f2015-09-15 14:41:29 -050039import bb.persist_data, bb.utils
40import bb.checksum
Patrick Williamsc124f4f2015-09-15 14:41:29 -050041import bb.process
Brad Bishopd7bf8c12018-02-25 22:55:05 -050042import bb.event
Patrick Williamsc124f4f2015-09-15 14:41:29 -050043
44__version__ = "2"
45_checksum_cache = bb.checksum.FileChecksumCache()
46
47logger = logging.getLogger("BitBake.Fetcher")
48
Patrick Williamsc124f4f2015-09-15 14:41:29 -050049class BBFetchException(Exception):
50 """Class all fetch exceptions inherit from"""
51 def __init__(self, message):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050052 self.msg = message
53 Exception.__init__(self, message)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050054
55 def __str__(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050056 return self.msg
Patrick Williamsc124f4f2015-09-15 14:41:29 -050057
58class UntrustedUrl(BBFetchException):
59 """Exception raised when encountering a host not listed in BB_ALLOWED_NETWORKS"""
60 def __init__(self, url, message=''):
61 if message:
62 msg = message
63 else:
64 msg = "The URL: '%s' is not trusted and cannot be used" % url
65 self.url = url
66 BBFetchException.__init__(self, msg)
67 self.args = (url,)
68
69class MalformedUrl(BBFetchException):
70 """Exception raised when encountering an invalid url"""
71 def __init__(self, url, message=''):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050072 if message:
73 msg = message
74 else:
75 msg = "The URL: '%s' is invalid and cannot be interpreted" % url
76 self.url = url
77 BBFetchException.__init__(self, msg)
78 self.args = (url,)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050079
80class FetchError(BBFetchException):
81 """General fetcher exception when something happens incorrectly"""
82 def __init__(self, message, url = None):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050083 if url:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050084 msg = "Fetcher failure for URL: '%s'. %s" % (url, message)
Brad Bishopd7bf8c12018-02-25 22:55:05 -050085 else:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050086 msg = "Fetcher failure: %s" % message
Brad Bishopd7bf8c12018-02-25 22:55:05 -050087 self.url = url
88 BBFetchException.__init__(self, msg)
89 self.args = (message, url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050090
91class ChecksumError(FetchError):
92 """Exception when mismatched checksum encountered"""
93 def __init__(self, message, url = None, checksum = None):
94 self.checksum = checksum
95 FetchError.__init__(self, message, url)
96
97class NoChecksumError(FetchError):
98 """Exception when no checksum is specified, but BB_STRICT_CHECKSUM is set"""
99
100class UnpackError(BBFetchException):
101 """General fetcher exception when something happens incorrectly when unpacking"""
102 def __init__(self, message, url):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500103 msg = "Unpack failure for URL: '%s'. %s" % (url, message)
104 self.url = url
105 BBFetchException.__init__(self, msg)
106 self.args = (message, url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500107
108class NoMethodError(BBFetchException):
109 """Exception raised when there is no method to obtain a supplied url or set of urls"""
110 def __init__(self, url):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500111 msg = "Could not find a fetcher which supports the URL: '%s'" % url
112 self.url = url
113 BBFetchException.__init__(self, msg)
114 self.args = (url,)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500115
116class MissingParameterError(BBFetchException):
117 """Exception raised when a fetch method is missing a critical parameter in the url"""
118 def __init__(self, missing, url):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500119 msg = "URL: '%s' is missing the required parameter '%s'" % (url, missing)
120 self.url = url
121 self.missing = missing
122 BBFetchException.__init__(self, msg)
123 self.args = (missing, url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500124
125class ParameterError(BBFetchException):
126 """Exception raised when a url cannot be proccessed due to invalid parameters."""
127 def __init__(self, message, url):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500128 msg = "URL: '%s' has invalid parameters. %s" % (url, message)
129 self.url = url
130 BBFetchException.__init__(self, msg)
131 self.args = (message, url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500132
133class NetworkAccess(BBFetchException):
134 """Exception raised when network access is disabled but it is required."""
135 def __init__(self, url, cmd):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500136 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)
137 self.url = url
138 self.cmd = cmd
139 BBFetchException.__init__(self, msg)
140 self.args = (url, cmd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500141
142class NonLocalMethod(Exception):
143 def __init__(self):
144 Exception.__init__(self)
145
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500146class MissingChecksumEvent(bb.event.Event):
147 def __init__(self, url, md5sum, sha256sum):
148 self.url = url
149 self.checksums = {'md5sum': md5sum,
150 'sha256sum': sha256sum}
151 bb.event.Event.__init__(self)
152
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500153
154class URI(object):
155 """
156 A class representing a generic URI, with methods for
157 accessing the URI components, and stringifies to the
158 URI.
159
160 It is constructed by calling it with a URI, or setting
161 the attributes manually:
162
163 uri = URI("http://example.com/")
164
165 uri = URI()
166 uri.scheme = 'http'
167 uri.hostname = 'example.com'
168 uri.path = '/'
169
170 It has the following attributes:
171
172 * scheme (read/write)
173 * userinfo (authentication information) (read/write)
174 * username (read/write)
175 * password (read/write)
176
177 Note, password is deprecated as of RFC 3986.
178
179 * hostname (read/write)
180 * port (read/write)
181 * hostport (read only)
182 "hostname:port", if both are set, otherwise just "hostname"
183 * path (read/write)
184 * path_quoted (read/write)
185 A URI quoted version of path
186 * params (dict) (read/write)
187 * query (dict) (read/write)
188 * relative (bool) (read only)
189 True if this is a "relative URI", (e.g. file:foo.diff)
190
191 It stringifies to the URI itself.
192
193 Some notes about relative URIs: while it's specified that
194 a URI beginning with <scheme>:// should either be directly
195 followed by a hostname or a /, the old URI handling of the
196 fetch2 library did not comform to this. Therefore, this URI
197 class has some kludges to make sure that URIs are parsed in
198 a way comforming to bitbake's current usage. This URI class
199 supports the following:
200
201 file:relative/path.diff (IETF compliant)
202 git:relative/path.git (IETF compliant)
203 git:///absolute/path.git (IETF compliant)
204 file:///absolute/path.diff (IETF compliant)
205
206 file://relative/path.diff (not IETF compliant)
207
208 But it does not support the following:
209
210 file://hostname/absolute/path.diff (would be IETF compliant)
211
212 Note that the last case only applies to a list of
213 "whitelisted" schemes (currently only file://), that requires
214 its URIs to not have a network location.
215 """
216
217 _relative_schemes = ['file', 'git']
218 _netloc_forbidden = ['file']
219
220 def __init__(self, uri=None):
221 self.scheme = ''
222 self.userinfo = ''
223 self.hostname = ''
224 self.port = None
225 self._path = ''
226 self.params = {}
227 self.query = {}
228 self.relative = False
229
230 if not uri:
231 return
232
233 # We hijack the URL parameters, since the way bitbake uses
234 # them are not quite RFC compliant.
235 uri, param_str = (uri.split(";", 1) + [None])[:2]
236
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600237 urlp = urllib.parse.urlparse(uri)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500238 self.scheme = urlp.scheme
239
240 reparse = 0
241
242 # Coerce urlparse to make URI scheme use netloc
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600243 if not self.scheme in urllib.parse.uses_netloc:
244 urllib.parse.uses_params.append(self.scheme)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500245 reparse = 1
246
247 # Make urlparse happy(/ier) by converting local resources
248 # to RFC compliant URL format. E.g.:
249 # file://foo.diff -> file:foo.diff
250 if urlp.scheme in self._netloc_forbidden:
251 uri = re.sub("(?<=:)//(?!/)", "", uri, 1)
252 reparse = 1
253
254 if reparse:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600255 urlp = urllib.parse.urlparse(uri)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500256
257 # Identify if the URI is relative or not
258 if urlp.scheme in self._relative_schemes and \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800259 re.compile(r"^\w+:(?!//)").match(uri):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500260 self.relative = True
261
262 if not self.relative:
263 self.hostname = urlp.hostname or ''
264 self.port = urlp.port
265
266 self.userinfo += urlp.username or ''
267
268 if urlp.password:
269 self.userinfo += ':%s' % urlp.password
270
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600271 self.path = urllib.parse.unquote(urlp.path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500272
273 if param_str:
274 self.params = self._param_str_split(param_str, ";")
275 if urlp.query:
276 self.query = self._param_str_split(urlp.query, "&")
277
278 def __str__(self):
279 userinfo = self.userinfo
280 if userinfo:
281 userinfo += '@'
282
283 return "%s:%s%s%s%s%s%s" % (
284 self.scheme,
285 '' if self.relative else '//',
286 userinfo,
287 self.hostport,
288 self.path_quoted,
289 self._query_str(),
290 self._param_str())
291
292 def _param_str(self):
293 return (
294 ''.join([';', self._param_str_join(self.params, ";")])
295 if self.params else '')
296
297 def _query_str(self):
298 return (
299 ''.join(['?', self._param_str_join(self.query, "&")])
300 if self.query else '')
301
302 def _param_str_split(self, string, elmdelim, kvdelim="="):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600303 ret = collections.OrderedDict()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500304 for k, v in [x.split(kvdelim, 1) for x in string.split(elmdelim)]:
305 ret[k] = v
306 return ret
307
308 def _param_str_join(self, dict_, elmdelim, kvdelim="="):
309 return elmdelim.join([kvdelim.join([k, v]) for k, v in dict_.items()])
310
311 @property
312 def hostport(self):
313 if not self.port:
314 return self.hostname
315 return "%s:%d" % (self.hostname, self.port)
316
317 @property
318 def path_quoted(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600319 return urllib.parse.quote(self.path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500320
321 @path_quoted.setter
322 def path_quoted(self, path):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600323 self.path = urllib.parse.unquote(path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500324
325 @property
326 def path(self):
327 return self._path
328
329 @path.setter
330 def path(self, path):
331 self._path = path
332
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500333 if not path or re.compile("^/").match(path):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500334 self.relative = False
335 else:
336 self.relative = True
337
338 @property
339 def username(self):
340 if self.userinfo:
341 return (self.userinfo.split(":", 1))[0]
342 return ''
343
344 @username.setter
345 def username(self, username):
346 password = self.password
347 self.userinfo = username
348 if password:
349 self.userinfo += ":%s" % password
350
351 @property
352 def password(self):
353 if self.userinfo and ":" in self.userinfo:
354 return (self.userinfo.split(":", 1))[1]
355 return ''
356
357 @password.setter
358 def password(self, password):
359 self.userinfo = "%s:%s" % (self.username, password)
360
361def decodeurl(url):
362 """Decodes an URL into the tokens (scheme, network location, path,
363 user, password, parameters).
364 """
365
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500366 m = re.compile('(?P<type>[^:]*)://((?P<user>[^/;]+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500367 if not m:
368 raise MalformedUrl(url)
369
370 type = m.group('type')
371 location = m.group('location')
372 if not location:
373 raise MalformedUrl(url)
374 user = m.group('user')
375 parm = m.group('parm')
376
377 locidx = location.find('/')
378 if locidx != -1 and type.lower() != 'file':
379 host = location[:locidx]
380 path = location[locidx:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500381 elif type.lower() == 'file':
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500382 host = ""
383 path = location
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500384 else:
385 host = location
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800386 path = "/"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500387 if user:
388 m = re.compile('(?P<user>[^:]+)(:?(?P<pswd>.*))').match(user)
389 if m:
390 user = m.group('user')
391 pswd = m.group('pswd')
392 else:
393 user = ''
394 pswd = ''
395
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600396 p = collections.OrderedDict()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500397 if parm:
398 for s in parm.split(';'):
399 if s:
400 if not '=' in s:
401 raise MalformedUrl(url, "The URL: '%s' is invalid: parameter %s does not specify a value (missing '=')" % (url, s))
402 s1, s2 = s.split('=')
403 p[s1] = s2
404
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600405 return type, host, urllib.parse.unquote(path), user, pswd, p
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500406
407def encodeurl(decoded):
408 """Encodes a URL from tokens (scheme, network location, path,
409 user, password, parameters).
410 """
411
412 type, host, path, user, pswd, p = decoded
413
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500414 if not type:
415 raise MissingParameterError('type', "encoded from the data %s" % str(decoded))
416 url = '%s://' % type
417 if user and type != "file":
418 url += "%s" % user
419 if pswd:
420 url += ":%s" % pswd
421 url += "@"
422 if host and type != "file":
423 url += "%s" % host
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500424 if path:
425 # Standardise path to ensure comparisons work
426 while '//' in path:
427 path = path.replace("//", "/")
428 url += "%s" % urllib.parse.quote(path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500429 if p:
430 for parm in p:
431 url += ";%s=%s" % (parm, p[parm])
432
433 return url
434
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500435def uri_replace(ud, uri_find, uri_replace, replacements, d, mirrortarball=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500436 if not ud.url or not uri_find or not uri_replace:
437 logger.error("uri_replace: passed an undefined value, not replacing")
438 return None
439 uri_decoded = list(decodeurl(ud.url))
440 uri_find_decoded = list(decodeurl(uri_find))
441 uri_replace_decoded = list(decodeurl(uri_replace))
442 logger.debug(2, "For url %s comparing %s to %s" % (uri_decoded, uri_find_decoded, uri_replace_decoded))
443 result_decoded = ['', '', '', '', '', {}]
444 for loc, i in enumerate(uri_find_decoded):
445 result_decoded[loc] = uri_decoded[loc]
446 regexp = i
447 if loc == 0 and regexp and not regexp.endswith("$"):
448 # Leaving the type unanchored can mean "https" matching "file" can become "files"
449 # which is clearly undesirable.
450 regexp += "$"
451 if loc == 5:
452 # Handle URL parameters
453 if i:
454 # Any specified URL parameters must match
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800455 for k in uri_find_decoded[loc]:
456 if uri_decoded[loc][k] != uri_find_decoded[loc][k]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500457 return None
458 # Overwrite any specified replacement parameters
459 for k in uri_replace_decoded[loc]:
460 for l in replacements:
461 uri_replace_decoded[loc][k] = uri_replace_decoded[loc][k].replace(l, replacements[l])
462 result_decoded[loc][k] = uri_replace_decoded[loc][k]
463 elif (re.match(regexp, uri_decoded[loc])):
464 if not uri_replace_decoded[loc]:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500465 result_decoded[loc] = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500466 else:
467 for k in replacements:
468 uri_replace_decoded[loc] = uri_replace_decoded[loc].replace(k, replacements[k])
469 #bb.note("%s %s %s" % (regexp, uri_replace_decoded[loc], uri_decoded[loc]))
Patrick Williamsd7e96312015-09-22 08:09:05 -0500470 result_decoded[loc] = re.sub(regexp, uri_replace_decoded[loc], uri_decoded[loc], 1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500471 if loc == 2:
472 # Handle path manipulations
473 basename = None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500474 if uri_decoded[0] != uri_replace_decoded[0] and mirrortarball:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500475 # If the source and destination url types differ, must be a mirrortarball mapping
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500476 basename = os.path.basename(mirrortarball)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500477 # Kill parameters, they make no sense for mirror tarballs
478 uri_decoded[5] = {}
479 elif ud.localpath and ud.method.supports_checksum(ud):
480 basename = os.path.basename(ud.localpath)
481 if basename and not result_decoded[loc].endswith(basename):
482 result_decoded[loc] = os.path.join(result_decoded[loc], basename)
483 else:
484 return None
485 result = encodeurl(result_decoded)
486 if result == ud.url:
487 return None
488 logger.debug(2, "For url %s returning %s" % (ud.url, result))
489 return result
490
491methods = []
492urldata_cache = {}
493saved_headrevs = {}
494
495def fetcher_init(d):
496 """
497 Called to initialize the fetchers once the configuration data is known.
498 Calls before this must not hit the cache.
499 """
500 # When to drop SCM head revisions controlled by user policy
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500501 srcrev_policy = d.getVar('BB_SRCREV_POLICY') or "clear"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500502 if srcrev_policy == "cache":
503 logger.debug(1, "Keeping SRCREV cache due to cache policy of: %s", srcrev_policy)
504 elif srcrev_policy == "clear":
505 logger.debug(1, "Clearing SRCREV cache due to cache policy of: %s", srcrev_policy)
506 revs = bb.persist_data.persist('BB_URI_HEADREVS', d)
507 try:
508 bb.fetch2.saved_headrevs = revs.items()
509 except:
510 pass
511 revs.clear()
512 else:
513 raise FetchError("Invalid SRCREV cache policy of: %s" % srcrev_policy)
514
515 _checksum_cache.init_cache(d)
516
517 for m in methods:
518 if hasattr(m, "init"):
519 m.init(d)
520
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500521def fetcher_parse_save():
522 _checksum_cache.save_extras()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500523
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500524def fetcher_parse_done():
525 _checksum_cache.save_merge()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500526
Brad Bishop19323692019-04-05 15:28:33 -0400527def fetcher_compare_revisions(d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500528 """
529 Compare the revisions in the persistant cache with current values and
530 return true/false on whether they've changed.
531 """
532
533 data = bb.persist_data.persist('BB_URI_HEADREVS', d).items()
534 data2 = bb.fetch2.saved_headrevs
535
536 changed = False
537 for key in data:
538 if key not in data2 or data2[key] != data[key]:
539 logger.debug(1, "%s changed", key)
540 changed = True
541 return True
542 else:
543 logger.debug(2, "%s did not change", key)
544 return False
545
546def mirror_from_string(data):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500547 mirrors = (data or "").replace('\\n',' ').split()
548 # Split into pairs
549 if len(mirrors) % 2 != 0:
550 bb.warn('Invalid mirror data %s, should have paired members.' % data)
551 return list(zip(*[iter(mirrors)]*2))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500552
553def verify_checksum(ud, d, precomputed={}):
554 """
555 verify the MD5 and SHA256 checksum for downloaded src
556
557 Raises a FetchError if one or both of the SRC_URI checksums do not match
558 the downloaded file, or if BB_STRICT_CHECKSUM is set and there are no
559 checksums specified.
560
561 Returns a dict of checksums that can be stored in a done stamp file and
562 passed in as precomputed parameter in a later call to avoid re-computing
563 the checksums from the file. This allows verifying the checksums of the
564 file against those in the recipe each time, rather than only after
565 downloading. See https://bugzilla.yoctoproject.org/show_bug.cgi?id=5571.
566 """
567
568 _MD5_KEY = "md5"
569 _SHA256_KEY = "sha256"
570
571 if ud.ignore_checksums or not ud.method.supports_checksum(ud):
572 return {}
573
574 if _MD5_KEY in precomputed:
575 md5data = precomputed[_MD5_KEY]
576 else:
577 md5data = bb.utils.md5_file(ud.localpath)
578
579 if _SHA256_KEY in precomputed:
580 sha256data = precomputed[_SHA256_KEY]
581 else:
582 sha256data = bb.utils.sha256_file(ud.localpath)
583
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500584 if ud.method.recommends_checksum(ud) and not ud.md5_expected and not ud.sha256_expected:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500585 # If strict checking enabled and neither sum defined, raise error
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500586 strict = d.getVar("BB_STRICT_CHECKSUM") or "0"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500587 if strict == "1":
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500588 logger.error('No checksum specified for %s, please add at least one to the recipe:\n'
589 'SRC_URI[%s] = "%s"\nSRC_URI[%s] = "%s"' %
590 (ud.localpath, ud.md5_name, md5data,
591 ud.sha256_name, sha256data))
592 raise NoChecksumError('Missing SRC_URI checksum', ud.url)
593
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500594 bb.event.fire(MissingChecksumEvent(ud.url, md5data, sha256data), d)
595
596 if strict == "ignore":
597 return {
598 _MD5_KEY: md5data,
599 _SHA256_KEY: sha256data
600 }
601
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500602 # Log missing sums so user can more easily add them
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600603 logger.warning('Missing md5 SRC_URI checksum for %s, consider adding to the recipe:\n'
604 'SRC_URI[%s] = "%s"',
605 ud.localpath, ud.md5_name, md5data)
606 logger.warning('Missing sha256 SRC_URI checksum for %s, consider adding to the recipe:\n'
607 'SRC_URI[%s] = "%s"',
608 ud.localpath, ud.sha256_name, sha256data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500609
610 # We want to alert the user if a checksum is defined in the recipe but
611 # it does not match.
612 msg = ""
613 mismatch = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500614 if ud.md5_expected and ud.md5_expected != md5data:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500615 msg = msg + "\nFile: '%s' has %s checksum %s when %s was expected" % (ud.localpath, 'md5', md5data, ud.md5_expected)
616 mismatch = True;
617
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500618 if ud.sha256_expected and ud.sha256_expected != sha256data:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500619 msg = msg + "\nFile: '%s' has %s checksum %s when %s was expected" % (ud.localpath, 'sha256', sha256data, ud.sha256_expected)
620 mismatch = True;
621
622 if mismatch:
623 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)
624
625 if len(msg):
626 raise ChecksumError('Checksum mismatch!%s' % msg, ud.url, md5data)
627
628 return {
629 _MD5_KEY: md5data,
630 _SHA256_KEY: sha256data
631 }
632
633
634def verify_donestamp(ud, d, origud=None):
635 """
636 Check whether the done stamp file has the right checksums (if the fetch
637 method supports them). If it doesn't, delete the done stamp and force
638 a re-download.
639
640 Returns True, if the donestamp exists and is valid, False otherwise. When
641 returning False, any existing done stamps are removed.
642 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500643 if not ud.needdonestamp or (origud and not origud.needdonestamp):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500644 return True
645
Brad Bishop316dfdd2018-06-25 12:45:53 -0400646 if not os.path.exists(ud.localpath):
647 # local path does not exist
648 if os.path.exists(ud.donestamp):
649 # done stamp exists, but the downloaded file does not; the done stamp
650 # must be incorrect, re-trigger the download
651 bb.utils.remove(ud.donestamp)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500652 return False
653
654 if (not ud.method.supports_checksum(ud) or
655 (origud and not origud.method.supports_checksum(origud))):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400656 # if done stamp exists and checksums not supported; assume the local
657 # file is current
658 return os.path.exists(ud.donestamp)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500659
660 precomputed_checksums = {}
661 # Only re-use the precomputed checksums if the donestamp is newer than the
662 # file. Do not rely on the mtime of directories, though. If ud.localpath is
663 # a directory, there will probably not be any checksums anyway.
Brad Bishop316dfdd2018-06-25 12:45:53 -0400664 if os.path.exists(ud.donestamp) and (os.path.isdir(ud.localpath) or
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500665 os.path.getmtime(ud.localpath) < os.path.getmtime(ud.donestamp)):
666 try:
667 with open(ud.donestamp, "rb") as cachefile:
668 pickled = pickle.Unpickler(cachefile)
669 precomputed_checksums.update(pickled.load())
670 except Exception as e:
671 # Avoid the warnings on the upgrade path from emtpy done stamp
672 # files to those containing the checksums.
673 if not isinstance(e, EOFError):
674 # Ignore errors, they aren't fatal
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600675 logger.warning("Couldn't load checksums from donestamp %s: %s "
676 "(msg: %s)" % (ud.donestamp, type(e).__name__,
677 str(e)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500678
679 try:
680 checksums = verify_checksum(ud, d, precomputed_checksums)
681 # If the cache file did not have the checksums, compute and store them
682 # as an upgrade path from the previous done stamp file format.
683 if checksums != precomputed_checksums:
684 with open(ud.donestamp, "wb") as cachefile:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600685 p = pickle.Pickler(cachefile, 2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500686 p.dump(checksums)
687 return True
688 except ChecksumError as e:
689 # Checksums failed to verify, trigger re-download and remove the
690 # incorrect stamp file.
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600691 logger.warning("Checksum mismatch for local file %s\n"
692 "Cleaning and trying again." % ud.localpath)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500693 if os.path.exists(ud.localpath):
694 rename_bad_checksum(ud, e.checksum)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500695 bb.utils.remove(ud.donestamp)
696 return False
697
698
699def update_stamp(ud, d):
700 """
701 donestamp is file stamp indicating the whole fetching is done
702 this function update the stamp after verifying the checksum
703 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500704 if not ud.needdonestamp:
705 return
706
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707 if os.path.exists(ud.donestamp):
708 # Touch the done stamp file to show active use of the download
709 try:
710 os.utime(ud.donestamp, None)
711 except:
712 # Errors aren't fatal here
713 pass
714 else:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500715 try:
716 checksums = verify_checksum(ud, d)
717 # Store the checksums for later re-verification against the recipe
718 with open(ud.donestamp, "wb") as cachefile:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600719 p = pickle.Pickler(cachefile, 2)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500720 p.dump(checksums)
721 except ChecksumError as e:
722 # Checksums failed to verify, trigger re-download and remove the
723 # incorrect stamp file.
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600724 logger.warning("Checksum mismatch for local file %s\n"
725 "Cleaning and trying again." % ud.localpath)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500726 if os.path.exists(ud.localpath):
727 rename_bad_checksum(ud, e.checksum)
728 bb.utils.remove(ud.donestamp)
729 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500730
731def subprocess_setup():
732 # Python installs a SIGPIPE handler by default. This is usually not what
733 # non-Python subprocesses expect.
734 # SIGPIPE errors are known issues with gzip/bash
735 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
736
737def get_autorev(d):
738 # only not cache src rev in autorev case
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500739 if d.getVar('BB_SRCREV_POLICY') != "cache":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500740 d.setVar('BB_DONT_CACHE', '1')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500741 return "AUTOINC"
742
743def get_srcrev(d, method_name='sortable_revision'):
744 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500745 Return the revision string, usually for use in the version string (PV) of the current package
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746 Most packages usually only have one SCM so we just pass on the call.
747 In the multi SCM case, we build a value based on SRCREV_FORMAT which must
748 have been set.
749
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500750 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 -0500751 incremental, other code is then responsible for turning that into an increasing value (if needed)
752
753 A method_name can be supplied to retrieve an alternatively formatted revision from a fetcher, if
754 that fetcher provides a method with the given name and the same signature as sortable_revision.
755 """
756
757 scms = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500758 fetcher = Fetch(d.getVar('SRC_URI').split(), d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500759 urldata = fetcher.ud
760 for u in urldata:
761 if urldata[u].method.supports_srcrev():
762 scms.append(u)
763
764 if len(scms) == 0:
765 raise FetchError("SRCREV was used yet no valid SCM was found in SRC_URI")
766
767 if len(scms) == 1 and len(urldata[scms[0]].names) == 1:
768 autoinc, rev = getattr(urldata[scms[0]].method, method_name)(urldata[scms[0]], d, urldata[scms[0]].names[0])
769 if len(rev) > 10:
770 rev = rev[:10]
771 if autoinc:
772 return "AUTOINC+" + rev
773 return rev
774
775 #
776 # Mutiple SCMs are in SRC_URI so we resort to SRCREV_FORMAT
777 #
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500778 format = d.getVar('SRCREV_FORMAT')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500779 if not format:
Brad Bishop19323692019-04-05 15:28:33 -0400780 raise FetchError("The SRCREV_FORMAT variable must be set when multiple SCMs are used.\n"\
781 "The SCMs are:\n%s" % '\n'.join(scms))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500782
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600783 name_to_rev = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500784 seenautoinc = False
785 for scm in scms:
786 ud = urldata[scm]
787 for name in ud.names:
788 autoinc, rev = getattr(ud.method, method_name)(ud, d, name)
789 seenautoinc = seenautoinc or autoinc
790 if len(rev) > 10:
791 rev = rev[:10]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600792 name_to_rev[name] = rev
793 # Replace names by revisions in the SRCREV_FORMAT string. The approach used
794 # here can handle names being prefixes of other names and names appearing
795 # as substrings in revisions (in which case the name should not be
796 # expanded). The '|' regular expression operator tries matches from left to
797 # right, so we need to sort the names with the longest ones first.
798 names_descending_len = sorted(name_to_rev, key=len, reverse=True)
799 name_to_rev_re = "|".join(re.escape(name) for name in names_descending_len)
800 format = re.sub(name_to_rev_re, lambda match: name_to_rev[match.group(0)], format)
801
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500802 if seenautoinc:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500803 format = "AUTOINC+" + format
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500804
805 return format
806
807def localpath(url, d):
808 fetcher = bb.fetch2.Fetch([url], d)
809 return fetcher.localpath(url)
810
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600811def runfetchcmd(cmd, d, quiet=False, cleanup=None, log=None, workdir=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500812 """
813 Run cmd returning the command output
814 Raise an error if interrupted or cmd fails
815 Optionally echo command output to stdout
816 Optionally remove the files/directories listed in cleanup upon failure
817 """
818
819 # Need to export PATH as binary could be in metadata paths
820 # rather than host provided
821 # Also include some other variables.
822 # FIXME: Should really include all export varaiables?
823 exportvars = ['HOME', 'PATH',
824 'HTTP_PROXY', 'http_proxy',
825 'HTTPS_PROXY', 'https_proxy',
826 'FTP_PROXY', 'ftp_proxy',
827 'FTPS_PROXY', 'ftps_proxy',
828 'NO_PROXY', 'no_proxy',
829 'ALL_PROXY', 'all_proxy',
830 'GIT_PROXY_COMMAND',
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800831 'GIT_SSH',
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500832 'GIT_SSL_CAINFO',
833 'GIT_SMART_HTTP',
834 'SSH_AUTH_SOCK', 'SSH_AGENT_PID',
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600835 'SOCKS5_USER', 'SOCKS5_PASSWD',
836 'DBUS_SESSION_BUS_ADDRESS',
837 'P4CONFIG']
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500838
839 if not cleanup:
840 cleanup = []
841
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800842 # If PATH contains WORKDIR which contains PV-PR which contains SRCPV we
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500843 # can end up in circular recursion here so give the option of breaking it
844 # in a data store copy.
845 try:
846 d.getVar("PV")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800847 d.getVar("PR")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500848 except bb.data_smart.ExpansionError:
849 d = bb.data.createCopy(d)
850 d.setVar("PV", "fetcheravoidrecurse")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800851 d.setVar("PR", "fetcheravoidrecurse")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500852
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600853 origenv = d.getVar("BB_ORIGENV", False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500854 for var in exportvars:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500855 val = d.getVar(var) or (origenv and origenv.getVar(var))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500856 if val:
857 cmd = 'export ' + var + '=\"%s\"; %s' % (val, cmd)
858
Brad Bishop316dfdd2018-06-25 12:45:53 -0400859 # Disable pseudo as it may affect ssh, potentially causing it to hang.
860 cmd = 'export PSEUDO_DISABLED=1; ' + cmd
861
Brad Bishop19323692019-04-05 15:28:33 -0400862 if workdir:
863 logger.debug(1, "Running '%s' in %s" % (cmd, workdir))
864 else:
865 logger.debug(1, "Running %s", cmd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500866
867 success = False
868 error_message = ""
869
870 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600871 (output, errors) = bb.process.run(cmd, log=log, shell=True, stderr=subprocess.PIPE, cwd=workdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500872 success = True
873 except bb.process.NotFoundError as e:
874 error_message = "Fetch command %s" % (e.command)
875 except bb.process.ExecutionError as e:
876 if e.stdout:
877 output = "output:\n%s\n%s" % (e.stdout, e.stderr)
878 elif e.stderr:
879 output = "output:\n%s" % e.stderr
880 else:
881 output = "no output"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600882 error_message = "Fetch command %s failed with exit code %s, %s" % (e.command, e.exitcode, output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500883 except bb.process.CmdError as e:
884 error_message = "Fetch command %s could not be run:\n%s" % (e.command, e.msg)
885 if not success:
886 for f in cleanup:
887 try:
888 bb.utils.remove(f, True)
889 except OSError:
890 pass
891
892 raise FetchError(error_message)
893
894 return output
895
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500896def check_network_access(d, info, url):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500897 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500898 log remote network access, and error if BB_NO_NETWORK is set or the given
899 URI is untrusted
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500900 """
Brad Bishop19323692019-04-05 15:28:33 -0400901 if bb.utils.to_boolean(d.getVar("BB_NO_NETWORK")):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500902 raise NetworkAccess(url, info)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500903 elif not trusted_network(d, url):
904 raise UntrustedUrl(url, info)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500905 else:
906 logger.debug(1, "Fetcher accessed the network with the command %s" % info)
907
908def build_mirroruris(origud, mirrors, ld):
909 uris = []
910 uds = []
911
912 replacements = {}
913 replacements["TYPE"] = origud.type
914 replacements["HOST"] = origud.host
915 replacements["PATH"] = origud.path
916 replacements["BASENAME"] = origud.path.split("/")[-1]
917 replacements["MIRRORNAME"] = origud.host.replace(':','.') + origud.path.replace('/', '.').replace('*', '.')
918
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500919 def adduri(ud, uris, uds, mirrors, tarballs):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500920 for line in mirrors:
921 try:
922 (find, replace) = line
923 except ValueError:
924 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500925
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500926 for tarball in tarballs:
927 newuri = uri_replace(ud, find, replace, replacements, ld, tarball)
928 if not newuri or newuri in uris or newuri == origud.url:
929 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500930
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500931 if not trusted_network(ld, newuri):
932 logger.debug(1, "Mirror %s not in the list of trusted networks, skipping" % (newuri))
933 continue
Patrick Williamsd7e96312015-09-22 08:09:05 -0500934
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500935 # Create a local copy of the mirrors minus the current line
936 # this will prevent us from recursively processing the same line
937 # as well as indirect recursion A -> B -> C -> A
938 localmirrors = list(mirrors)
939 localmirrors.remove(line)
940
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500941 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500942 newud = FetchData(newuri, ld)
943 newud.setup_localpath(ld)
944 except bb.fetch2.BBFetchException as e:
945 logger.debug(1, "Mirror fetch failure for url %s (original url: %s)" % (newuri, origud.url))
946 logger.debug(1, str(e))
947 try:
948 # setup_localpath of file:// urls may fail, we should still see
949 # if mirrors of the url exist
950 adduri(newud, uris, uds, localmirrors, tarballs)
951 except UnboundLocalError:
952 pass
953 continue
954 uris.append(newuri)
955 uds.append(newud)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500956
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500957 adduri(newud, uris, uds, localmirrors, tarballs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500958
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500959 adduri(origud, uris, uds, mirrors, origud.mirrortarballs or [None])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500960
961 return uris, uds
962
963def rename_bad_checksum(ud, suffix):
964 """
965 Renames files to have suffix from parameter
966 """
967
968 if ud.localpath is None:
969 return
970
971 new_localpath = "%s_bad-checksum_%s" % (ud.localpath, suffix)
972 bb.warn("Renaming %s to %s" % (ud.localpath, new_localpath))
973 bb.utils.movefile(ud.localpath, new_localpath)
974
975
976def try_mirror_url(fetch, origud, ud, ld, check = False):
977 # Return of None or a value means we're finished
978 # False means try another url
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500979
980 if ud.lockfile and ud.lockfile != origud.lockfile:
981 lf = bb.utils.lockfile(ud.lockfile)
982
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500983 try:
984 if check:
985 found = ud.method.checkstatus(fetch, ud, ld)
986 if found:
987 return found
988 return False
989
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500990 if not verify_donestamp(ud, ld, origud) or ud.method.need_update(ud, ld):
991 ud.method.download(ud, ld)
992 if hasattr(ud.method,"build_mirror_data"):
993 ud.method.build_mirror_data(ud, ld)
994
995 if not ud.localpath or not os.path.exists(ud.localpath):
996 return False
997
998 if ud.localpath == origud.localpath:
999 return ud.localpath
1000
1001 # We may be obtaining a mirror tarball which needs further processing by the real fetcher
1002 # If that tarball is a local file:// we need to provide a symlink to it
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001003 dldir = ld.getVar("DL_DIR")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001004
1005 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 -05001006 # Create donestamp in old format to avoid triggering a re-download
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001007 if ud.donestamp:
1008 bb.utils.mkdirhier(os.path.dirname(ud.donestamp))
1009 open(ud.donestamp, 'w').close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001010 dest = os.path.join(dldir, os.path.basename(ud.localpath))
1011 if not os.path.exists(dest):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001012 # In case this is executing without any file locks held (as is
1013 # the case for file:// URLs), two tasks may end up here at the
1014 # same time, in which case we do not want the second task to
1015 # fail when the link has already been created by the first task.
1016 try:
1017 os.symlink(ud.localpath, dest)
1018 except FileExistsError:
1019 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001020 if not verify_donestamp(origud, ld) or origud.method.need_update(origud, ld):
1021 origud.method.download(origud, ld)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001022 if hasattr(origud.method, "build_mirror_data"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001023 origud.method.build_mirror_data(origud, ld)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001024 return origud.localpath
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001025 # Otherwise the result is a local file:// and we symlink to it
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001026 ensure_symlink(ud.localpath, origud.localpath)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001027 update_stamp(origud, ld)
1028 return ud.localpath
1029
1030 except bb.fetch2.NetworkAccess:
1031 raise
1032
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001033 except IOError as e:
Brad Bishop19323692019-04-05 15:28:33 -04001034 if e.errno in [errno.ESTALE]:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001035 logger.warning("Stale Error Observed %s." % ud.url)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001036 return False
1037 raise
1038
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001039 except bb.fetch2.BBFetchException as e:
1040 if isinstance(e, ChecksumError):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001041 logger.warning("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (ud.url, origud.url))
1042 logger.warning(str(e))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001043 if os.path.exists(ud.localpath):
1044 rename_bad_checksum(ud, e.checksum)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001045 elif isinstance(e, NoChecksumError):
1046 raise
1047 else:
1048 logger.debug(1, "Mirror fetch failure for url %s (original url: %s)" % (ud.url, origud.url))
1049 logger.debug(1, str(e))
1050 try:
1051 ud.method.clean(ud, ld)
1052 except UnboundLocalError:
1053 pass
1054 return False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001055 finally:
1056 if ud.lockfile and ud.lockfile != origud.lockfile:
1057 bb.utils.unlockfile(lf)
1058
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001059
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001060def ensure_symlink(target, link_name):
1061 if not os.path.exists(link_name):
1062 if os.path.islink(link_name):
1063 # Broken symbolic link
1064 os.unlink(link_name)
1065
1066 # In case this is executing without any file locks held (as is
1067 # the case for file:// URLs), two tasks may end up here at the
1068 # same time, in which case we do not want the second task to
1069 # fail when the link has already been created by the first task.
1070 try:
1071 os.symlink(target, link_name)
1072 except FileExistsError:
1073 pass
1074
1075
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001076def try_mirrors(fetch, d, origud, mirrors, check = False):
1077 """
1078 Try to use a mirrored version of the sources.
1079 This method will be automatically called before the fetchers go.
1080
1081 d Is a bb.data instance
1082 uri is the original uri we're trying to download
1083 mirrors is the list of mirrors we're going to try
1084 """
1085 ld = d.createCopy()
1086
1087 uris, uds = build_mirroruris(origud, mirrors, ld)
1088
1089 for index, uri in enumerate(uris):
1090 ret = try_mirror_url(fetch, origud, uds[index], ld, check)
1091 if ret != False:
1092 return ret
1093 return None
1094
1095def trusted_network(d, url):
1096 """
1097 Use a trusted url during download if networking is enabled and
1098 BB_ALLOWED_NETWORKS is set globally or for a specific recipe.
1099 Note: modifies SRC_URI & mirrors.
1100 """
Brad Bishop19323692019-04-05 15:28:33 -04001101 if bb.utils.to_boolean(d.getVar("BB_NO_NETWORK")):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001102 return True
1103
1104 pkgname = d.expand(d.getVar('PN', False))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001105 trusted_hosts = None
1106 if pkgname:
1107 trusted_hosts = d.getVarFlag('BB_ALLOWED_NETWORKS', pkgname, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001108
1109 if not trusted_hosts:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001110 trusted_hosts = d.getVar('BB_ALLOWED_NETWORKS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001111
1112 # Not enabled.
1113 if not trusted_hosts:
1114 return True
1115
1116 scheme, network, path, user, passwd, param = decodeurl(url)
1117
1118 if not network:
1119 return True
1120
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001121 network = network.split(':')[0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001122 network = network.lower()
1123
1124 for host in trusted_hosts.split(" "):
1125 host = host.lower()
1126 if host.startswith("*.") and ("." + network).endswith(host[1:]):
1127 return True
1128 if host == network:
1129 return True
1130
1131 return False
1132
1133def srcrev_internal_helper(ud, d, name):
1134 """
1135 Return:
1136 a) a source revision if specified
1137 b) latest revision if SRCREV="AUTOINC"
1138 c) None if not specified
1139 """
1140
1141 srcrev = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001142 pn = d.getVar("PN")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001143 attempts = []
1144 if name != '' and pn:
1145 attempts.append("SRCREV_%s_pn-%s" % (name, pn))
1146 if name != '':
1147 attempts.append("SRCREV_%s" % name)
1148 if pn:
1149 attempts.append("SRCREV_pn-%s" % pn)
1150 attempts.append("SRCREV")
1151
1152 for a in attempts:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001153 srcrev = d.getVar(a)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001154 if srcrev and srcrev != "INVALID":
1155 break
1156
1157 if 'rev' in ud.parm and 'tag' in ud.parm:
1158 raise FetchError("Please specify a ;rev= parameter or a ;tag= parameter in the url %s but not both." % (ud.url))
1159
1160 if 'rev' in ud.parm or 'tag' in ud.parm:
1161 if 'rev' in ud.parm:
1162 parmrev = ud.parm['rev']
1163 else:
1164 parmrev = ud.parm['tag']
1165 if srcrev == "INVALID" or not srcrev:
1166 return parmrev
1167 if srcrev != parmrev:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001168 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 -05001169 return parmrev
1170
1171 if srcrev == "INVALID" or not srcrev:
1172 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)
1173 if srcrev == "AUTOINC":
1174 srcrev = ud.method.latest_revision(ud, d, name)
1175
1176 return srcrev
1177
1178def get_checksum_file_list(d):
1179 """ Get a list of files checksum in SRC_URI
1180
1181 Returns the resolved local paths of all local file entries in
1182 SRC_URI as a space-separated string
1183 """
1184 fetch = Fetch([], d, cache = False, localonly = True)
1185
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001186 dl_dir = d.getVar('DL_DIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001187 filelist = []
1188 for u in fetch.urls:
1189 ud = fetch.ud[u]
1190
1191 if ud and isinstance(ud.method, local.Local):
1192 paths = ud.method.localpaths(ud, d)
1193 for f in paths:
1194 pth = ud.decodedurl
1195 if '*' in pth:
1196 f = os.path.join(os.path.abspath(f), pth)
1197 if f.startswith(dl_dir):
1198 # The local fetcher's behaviour is to return a path under DL_DIR if it couldn't find the file anywhere else
1199 if os.path.exists(f):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001200 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 -05001201 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001202 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 -05001203 filelist.append(f + ":" + str(os.path.exists(f)))
1204
1205 return " ".join(filelist)
1206
1207def get_file_checksums(filelist, pn):
1208 """Get a list of the checksums for a list of local files
1209
1210 Returns the checksums for a list of local files, caching the results as
1211 it proceeds
1212
1213 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001214 return _checksum_cache.get_checksums(filelist, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001215
1216
1217class FetchData(object):
1218 """
1219 A class which represents the fetcher state for a given URI.
1220 """
1221 def __init__(self, url, d, localonly = False):
1222 # localpath is the location of a downloaded result. If not set, the file is local.
1223 self.donestamp = None
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001224 self.needdonestamp = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001225 self.localfile = ""
1226 self.localpath = None
1227 self.lockfile = None
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001228 self.mirrortarballs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001229 self.basename = None
1230 self.basepath = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001231 (self.type, self.host, self.path, self.user, self.pswd, self.parm) = decodeurl(d.expand(url))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001232 self.date = self.getSRCDate(d)
1233 self.url = url
1234 if not self.user and "user" in self.parm:
1235 self.user = self.parm["user"]
1236 if not self.pswd and "pswd" in self.parm:
1237 self.pswd = self.parm["pswd"]
1238 self.setup = False
1239
1240 if "name" in self.parm:
1241 self.md5_name = "%s.md5sum" % self.parm["name"]
1242 self.sha256_name = "%s.sha256sum" % self.parm["name"]
1243 else:
1244 self.md5_name = "md5sum"
1245 self.sha256_name = "sha256sum"
1246 if self.md5_name in self.parm:
1247 self.md5_expected = self.parm[self.md5_name]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001248 elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001249 self.md5_expected = None
1250 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001251 self.md5_expected = d.getVarFlag("SRC_URI", self.md5_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001252 if self.sha256_name in self.parm:
1253 self.sha256_expected = self.parm[self.sha256_name]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001254 elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001255 self.sha256_expected = None
1256 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001257 self.sha256_expected = d.getVarFlag("SRC_URI", self.sha256_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001258 self.ignore_checksums = False
1259
1260 self.names = self.parm.get("name",'default').split(',')
1261
1262 self.method = None
1263 for m in methods:
1264 if m.supports(self, d):
1265 self.method = m
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001266 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001267
1268 if not self.method:
1269 raise NoMethodError(url)
1270
1271 if localonly and not isinstance(self.method, local.Local):
1272 raise NonLocalMethod()
1273
1274 if self.parm.get("proto", None) and "protocol" not in self.parm:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001275 logger.warning('Consider updating %s recipe to use "protocol" not "proto" in SRC_URI.', d.getVar('PN'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001276 self.parm["protocol"] = self.parm.get("proto", None)
1277
1278 if hasattr(self.method, "urldata_init"):
1279 self.method.urldata_init(self, d)
1280
1281 if "localpath" in self.parm:
1282 # if user sets localpath for file, use it instead.
1283 self.localpath = self.parm["localpath"]
1284 self.basename = os.path.basename(self.localpath)
1285 elif self.localfile:
1286 self.localpath = self.method.localpath(self, d)
1287
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001288 dldir = d.getVar("DL_DIR")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001289
1290 if not self.needdonestamp:
1291 return
1292
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001293 # Note: .done and .lock files should always be in DL_DIR whereas localpath may not be.
1294 if self.localpath and self.localpath.startswith(dldir):
1295 basepath = self.localpath
1296 elif self.localpath:
1297 basepath = dldir + os.sep + os.path.basename(self.localpath)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001298 elif self.basepath or self.basename:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001299 basepath = dldir + os.sep + (self.basepath or self.basename)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001300 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001301 bb.fatal("Can't determine lock path for url %s" % url)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001302
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001303 self.donestamp = basepath + '.done'
1304 self.lockfile = basepath + '.lock'
1305
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001306 def setup_revisions(self, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001307 self.revisions = {}
1308 for name in self.names:
1309 self.revisions[name] = srcrev_internal_helper(self, d, name)
1310
1311 # add compatibility code for non name specified case
1312 if len(self.names) == 1:
1313 self.revision = self.revisions[self.names[0]]
1314
1315 def setup_localpath(self, d):
1316 if not self.localpath:
1317 self.localpath = self.method.localpath(self, d)
1318
1319 def getSRCDate(self, d):
1320 """
1321 Return the SRC Date for the component
1322
1323 d the bb.data module
1324 """
1325 if "srcdate" in self.parm:
1326 return self.parm['srcdate']
1327
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001328 pn = d.getVar("PN")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001329
1330 if pn:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001331 return d.getVar("SRCDATE_%s" % pn) or d.getVar("SRCDATE") or d.getVar("DATE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001332
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001333 return d.getVar("SRCDATE") or d.getVar("DATE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001334
1335class FetchMethod(object):
1336 """Base class for 'fetch'ing data"""
1337
1338 def __init__(self, urls=None):
1339 self.urls = []
1340
1341 def supports(self, urldata, d):
1342 """
1343 Check to see if this fetch class supports a given url.
1344 """
1345 return 0
1346
1347 def localpath(self, urldata, d):
1348 """
1349 Return the local filename of a given url assuming a successful fetch.
1350 Can also setup variables in urldata for use in go (saving code duplication
1351 and duplicate code execution)
1352 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001353 return os.path.join(d.getVar("DL_DIR"), urldata.localfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001354
1355 def supports_checksum(self, urldata):
1356 """
1357 Is localpath something that can be represented by a checksum?
1358 """
1359
1360 # We cannot compute checksums for directories
1361 if os.path.isdir(urldata.localpath) == True:
1362 return False
1363 if urldata.localpath.find("*") != -1:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001364 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001365
1366 return True
1367
1368 def recommends_checksum(self, urldata):
1369 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001370 Is the backend on where checksumming is recommended (should warnings
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001371 be displayed if there is no checksum)?
1372 """
1373 return False
1374
1375 def _strip_leading_slashes(self, relpath):
1376 """
1377 Remove leading slash as os.path.join can't cope
1378 """
1379 while os.path.isabs(relpath):
1380 relpath = relpath[1:]
1381 return relpath
1382
1383 def setUrls(self, urls):
1384 self.__urls = urls
1385
1386 def getUrls(self):
1387 return self.__urls
1388
1389 urls = property(getUrls, setUrls, None, "Urls property")
1390
1391 def need_update(self, ud, d):
1392 """
1393 Force a fetch, even if localpath exists?
1394 """
1395 if os.path.exists(ud.localpath):
1396 return False
1397 return True
1398
1399 def supports_srcrev(self):
1400 """
1401 The fetcher supports auto source revisions (SRCREV)
1402 """
1403 return False
1404
1405 def download(self, urldata, d):
1406 """
1407 Fetch urls
1408 Assumes localpath was called first
1409 """
Brad Bishop19323692019-04-05 15:28:33 -04001410 raise NoMethodError(urldata.url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001411
1412 def unpack(self, urldata, rootdir, data):
1413 iterate = False
1414 file = urldata.localpath
1415
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001416 # Localpath can't deal with 'dir/*' entries, so it converts them to '.',
1417 # but it must be corrected back for local files copying
1418 if urldata.basename == '*' and file.endswith('/.'):
1419 file = '%s/%s' % (file.rstrip('/.'), urldata.path)
1420
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001421 try:
1422 unpack = bb.utils.to_boolean(urldata.parm.get('unpack'), True)
1423 except ValueError as exc:
1424 bb.fatal("Invalid value for 'unpack' parameter for %s: %s" %
1425 (file, urldata.parm.get('unpack')))
1426
1427 base, ext = os.path.splitext(file)
1428 if ext in ['.gz', '.bz2', '.Z', '.xz', '.lz']:
1429 efile = os.path.join(rootdir, os.path.basename(base))
1430 else:
1431 efile = file
1432 cmd = None
1433
1434 if unpack:
1435 if file.endswith('.tar'):
1436 cmd = 'tar x --no-same-owner -f %s' % file
1437 elif file.endswith('.tgz') or file.endswith('.tar.gz') or file.endswith('.tar.Z'):
1438 cmd = 'tar xz --no-same-owner -f %s' % file
1439 elif file.endswith('.tbz') or file.endswith('.tbz2') or file.endswith('.tar.bz2'):
1440 cmd = 'bzip2 -dc %s | tar x --no-same-owner -f -' % file
1441 elif file.endswith('.gz') or file.endswith('.Z') or file.endswith('.z'):
1442 cmd = 'gzip -dc %s > %s' % (file, efile)
1443 elif file.endswith('.bz2'):
1444 cmd = 'bzip2 -dc %s > %s' % (file, efile)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001445 elif file.endswith('.txz') or file.endswith('.tar.xz'):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001446 cmd = 'xz -dc %s | tar x --no-same-owner -f -' % file
1447 elif file.endswith('.xz'):
1448 cmd = 'xz -dc %s > %s' % (file, efile)
1449 elif file.endswith('.tar.lz'):
1450 cmd = 'lzip -dc %s | tar x --no-same-owner -f -' % file
1451 elif file.endswith('.lz'):
1452 cmd = 'lzip -dc %s > %s' % (file, efile)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001453 elif file.endswith('.tar.7z'):
1454 cmd = '7z x -so %s | tar x --no-same-owner -f -' % file
1455 elif file.endswith('.7z'):
1456 cmd = '7za x -y %s 1>/dev/null' % file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001457 elif file.endswith('.zip') or file.endswith('.jar'):
1458 try:
1459 dos = bb.utils.to_boolean(urldata.parm.get('dos'), False)
1460 except ValueError as exc:
1461 bb.fatal("Invalid value for 'dos' parameter for %s: %s" %
1462 (file, urldata.parm.get('dos')))
1463 cmd = 'unzip -q -o'
1464 if dos:
1465 cmd = '%s -a' % cmd
1466 cmd = "%s '%s'" % (cmd, file)
1467 elif file.endswith('.rpm') or file.endswith('.srpm'):
1468 if 'extract' in urldata.parm:
1469 unpack_file = urldata.parm.get('extract')
1470 cmd = 'rpm2cpio.sh %s | cpio -id %s' % (file, unpack_file)
1471 iterate = True
1472 iterate_file = unpack_file
1473 else:
1474 cmd = 'rpm2cpio.sh %s | cpio -id' % (file)
1475 elif file.endswith('.deb') or file.endswith('.ipk'):
Brad Bishopa5c52ff2018-11-23 10:55:50 +13001476 output = subprocess.check_output(['ar', '-t', file], preexec_fn=subprocess_setup)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001477 datafile = None
1478 if output:
1479 for line in output.decode().splitlines():
1480 if line.startswith('data.tar.'):
1481 datafile = line
1482 break
1483 else:
1484 raise UnpackError("Unable to unpack deb/ipk package - does not contain data.tar.* file", urldata.url)
1485 else:
1486 raise UnpackError("Unable to unpack deb/ipk package - could not list contents", urldata.url)
1487 cmd = 'ar x %s %s && tar --no-same-owner -xpf %s && rm %s' % (file, datafile, datafile, datafile)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001488
1489 # If 'subdir' param exists, create a dir and use it as destination for unpack cmd
1490 if 'subdir' in urldata.parm:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001491 subdir = urldata.parm.get('subdir')
1492 if os.path.isabs(subdir):
1493 if not os.path.realpath(subdir).startswith(os.path.realpath(rootdir)):
1494 raise UnpackError("subdir argument isn't a subdirectory of unpack root %s" % rootdir, urldata.url)
1495 unpackdir = subdir
1496 else:
1497 unpackdir = os.path.join(rootdir, subdir)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001498 bb.utils.mkdirhier(unpackdir)
1499 else:
1500 unpackdir = rootdir
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001501
1502 if not unpack or not cmd:
1503 # If file == dest, then avoid any copies, as we already put the file into dest!
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001504 dest = os.path.join(unpackdir, os.path.basename(file))
1505 if file != dest and not (os.path.exists(dest) and os.path.samefile(file, dest)):
1506 destdir = '.'
1507 # For file:// entries all intermediate dirs in path must be created at destination
1508 if urldata.type == "file":
1509 # Trailing '/' does a copying to wrong place
1510 urlpath = urldata.path.rstrip('/')
1511 # Want files places relative to cwd so no leading '/'
1512 urlpath = urlpath.lstrip('/')
1513 if urlpath.find("/") != -1:
1514 destdir = urlpath.rsplit("/", 1)[0] + '/'
1515 bb.utils.mkdirhier("%s/%s" % (unpackdir, destdir))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001516 cmd = 'cp -fpPRH %s %s' % (file, destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001517
1518 if not cmd:
1519 return
1520
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001521 path = data.getVar('PATH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001522 if path:
1523 cmd = "PATH=\"%s\" %s" % (path, cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001524 bb.note("Unpacking %s to %s/" % (file, unpackdir))
1525 ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True, cwd=unpackdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001526
1527 if ret != 0:
1528 raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), urldata.url)
1529
1530 if iterate is True:
1531 iterate_urldata = urldata
1532 iterate_urldata.localpath = "%s/%s" % (rootdir, iterate_file)
1533 self.unpack(urldata, rootdir, data)
1534
1535 return
1536
1537 def clean(self, urldata, d):
1538 """
1539 Clean any existing full or partial download
1540 """
1541 bb.utils.remove(urldata.localpath)
1542
1543 def try_premirror(self, urldata, d):
1544 """
1545 Should premirrors be used?
1546 """
1547 return True
1548
1549 def checkstatus(self, fetch, urldata, d):
1550 """
1551 Check the status of a URL
1552 Assumes localpath was called first
1553 """
Brad Bishop19323692019-04-05 15:28:33 -04001554 logger.info("URL %s could not be checked for status since no method exists.", urldata.url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001555 return True
1556
1557 def latest_revision(self, ud, d, name):
1558 """
1559 Look in the cache for the latest revision, if not present ask the SCM.
1560 """
1561 if not hasattr(self, "_latest_revision"):
Brad Bishop19323692019-04-05 15:28:33 -04001562 raise ParameterError("The fetcher for this URL does not support _latest_revision", ud.url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001563
1564 revs = bb.persist_data.persist('BB_URI_HEADREVS', d)
1565 key = self.generate_revision_key(ud, d, name)
1566 try:
1567 return revs[key]
1568 except KeyError:
1569 revs[key] = rev = self._latest_revision(ud, d, name)
1570 return rev
1571
1572 def sortable_revision(self, ud, d, name):
1573 latest_rev = self._build_revision(ud, d, name)
1574 return True, str(latest_rev)
1575
1576 def generate_revision_key(self, ud, d, name):
1577 key = self._revision_key(ud, d, name)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001578 return "%s-%s" % (key, d.getVar("PN") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001579
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001580 def latest_versionstring(self, ud, d):
1581 """
1582 Compute the latest release name like "x.y.x" in "x.y.x+gitHASH"
1583 by searching through the tags output of ls-remote, comparing
1584 versions and returning the highest match as a (version, revision) pair.
1585 """
1586 return ('', '')
1587
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001588class Fetch(object):
1589 def __init__(self, urls, d, cache = True, localonly = False, connection_cache = None):
1590 if localonly and cache:
1591 raise Exception("bb.fetch2.Fetch.__init__: cannot set cache and localonly at same time")
1592
1593 if len(urls) == 0:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001594 urls = d.getVar("SRC_URI").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001595 self.urls = urls
1596 self.d = d
1597 self.ud = {}
1598 self.connection_cache = connection_cache
1599
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001600 fn = d.getVar('FILE')
1601 mc = d.getVar('__BBMULTICONFIG') or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001602 if cache and fn and mc + fn in urldata_cache:
1603 self.ud = urldata_cache[mc + fn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001604
1605 for url in urls:
1606 if url not in self.ud:
1607 try:
1608 self.ud[url] = FetchData(url, d, localonly)
1609 except NonLocalMethod:
1610 if localonly:
1611 self.ud[url] = None
1612 pass
1613
1614 if fn and cache:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001615 urldata_cache[mc + fn] = self.ud
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001616
1617 def localpath(self, url):
1618 if url not in self.urls:
1619 self.ud[url] = FetchData(url, self.d)
1620
1621 self.ud[url].setup_localpath(self.d)
1622 return self.d.expand(self.ud[url].localpath)
1623
1624 def localpaths(self):
1625 """
1626 Return a list of the local filenames, assuming successful fetch
1627 """
1628 local = []
1629
1630 for u in self.urls:
1631 ud = self.ud[u]
1632 ud.setup_localpath(self.d)
1633 local.append(ud.localpath)
1634
1635 return local
1636
1637 def download(self, urls=None):
1638 """
1639 Fetch all urls
1640 """
1641 if not urls:
1642 urls = self.urls
1643
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001644 network = self.d.getVar("BB_NO_NETWORK")
Brad Bishop19323692019-04-05 15:28:33 -04001645 premirroronly = bb.utils.to_boolean(self.d.getVar("BB_FETCH_PREMIRRORONLY"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001646
1647 for u in urls:
1648 ud = self.ud[u]
1649 ud.setup_localpath(self.d)
1650 m = ud.method
1651 localpath = ""
1652
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001653 if ud.lockfile:
1654 lf = bb.utils.lockfile(ud.lockfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001655
1656 try:
1657 self.d.setVar("BB_NO_NETWORK", network)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001658
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001659 if verify_donestamp(ud, self.d) and not m.need_update(ud, self.d):
1660 localpath = ud.localpath
1661 elif m.try_premirror(ud, self.d):
1662 logger.debug(1, "Trying PREMIRRORS")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001663 mirrors = mirror_from_string(self.d.getVar('PREMIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001664 localpath = try_mirrors(self, self.d, ud, mirrors, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001665 if localpath:
1666 try:
1667 # early checksum verification so that if the checksum of the premirror
1668 # contents mismatch the fetcher can still try upstream and mirrors
1669 update_stamp(ud, self.d)
1670 except ChecksumError as e:
1671 logger.warning("Checksum failure encountered with premirror download of %s - will attempt other sources." % u)
1672 logger.debug(1, str(e))
1673 localpath = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001674
1675 if premirroronly:
1676 self.d.setVar("BB_NO_NETWORK", "1")
1677
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001678 firsterr = None
1679 verified_stamp = verify_donestamp(ud, self.d)
1680 if not localpath and (not verified_stamp or m.need_update(ud, self.d)):
1681 try:
1682 if not trusted_network(self.d, ud.url):
1683 raise UntrustedUrl(ud.url)
1684 logger.debug(1, "Trying Upstream")
1685 m.download(ud, self.d)
1686 if hasattr(m, "build_mirror_data"):
1687 m.build_mirror_data(ud, self.d)
1688 localpath = ud.localpath
1689 # early checksum verify, so that if checksum mismatched,
1690 # fetcher still have chance to fetch from mirror
1691 update_stamp(ud, self.d)
1692
1693 except bb.fetch2.NetworkAccess:
1694 raise
1695
1696 except BBFetchException as e:
1697 if isinstance(e, ChecksumError):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001698 logger.warning("Checksum failure encountered with download of %s - will attempt other sources if available" % u)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001699 logger.debug(1, str(e))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001700 if os.path.exists(ud.localpath):
1701 rename_bad_checksum(ud, e.checksum)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001702 elif isinstance(e, NoChecksumError):
1703 raise
1704 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001705 logger.warning('Failed to fetch URL %s, attempting MIRRORS if available' % u)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001706 logger.debug(1, str(e))
1707 firsterr = e
1708 # Remove any incomplete fetch
1709 if not verified_stamp:
1710 m.clean(ud, self.d)
1711 logger.debug(1, "Trying MIRRORS")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001712 mirrors = mirror_from_string(self.d.getVar('MIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001713 localpath = try_mirrors(self, self.d, ud, mirrors)
1714
1715 if not localpath or ((not os.path.exists(localpath)) and localpath.find("*") == -1):
1716 if firsterr:
1717 logger.error(str(firsterr))
1718 raise FetchError("Unable to fetch URL from any source.", u)
1719
1720 update_stamp(ud, self.d)
1721
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001722 except IOError as e:
Brad Bishop19323692019-04-05 15:28:33 -04001723 if e.errno in [errno.ESTALE]:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001724 logger.error("Stale Error Observed %s." % u)
1725 raise ChecksumError("Stale Error Detected")
1726
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001727 except BBFetchException as e:
1728 if isinstance(e, ChecksumError):
1729 logger.error("Checksum failure fetching %s" % u)
1730 raise
1731
1732 finally:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001733 if ud.lockfile:
1734 bb.utils.unlockfile(lf)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001735
1736 def checkstatus(self, urls=None):
1737 """
1738 Check all urls exist upstream
1739 """
1740
1741 if not urls:
1742 urls = self.urls
1743
1744 for u in urls:
1745 ud = self.ud[u]
1746 ud.setup_localpath(self.d)
1747 m = ud.method
1748 logger.debug(1, "Testing URL %s", u)
1749 # First try checking uri, u, from PREMIRRORS
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001750 mirrors = mirror_from_string(self.d.getVar('PREMIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001751 ret = try_mirrors(self, self.d, ud, mirrors, True)
1752 if not ret:
1753 # Next try checking from the original uri, u
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001754 ret = m.checkstatus(self, ud, self.d)
1755 if not ret:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001756 # Finally, try checking uri, u, from MIRRORS
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001757 mirrors = mirror_from_string(self.d.getVar('MIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001758 ret = try_mirrors(self, self.d, ud, mirrors, True)
1759
1760 if not ret:
1761 raise FetchError("URL %s doesn't work" % u, u)
1762
1763 def unpack(self, root, urls=None):
1764 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001765 Unpack urls to root
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001766 """
1767
1768 if not urls:
1769 urls = self.urls
1770
1771 for u in urls:
1772 ud = self.ud[u]
1773 ud.setup_localpath(self.d)
1774
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001775 if ud.lockfile:
1776 lf = bb.utils.lockfile(ud.lockfile)
1777
1778 ud.method.unpack(ud, root, self.d)
1779
1780 if ud.lockfile:
1781 bb.utils.unlockfile(lf)
1782
1783 def clean(self, urls=None):
1784 """
1785 Clean files that the fetcher gets or places
1786 """
1787
1788 if not urls:
1789 urls = self.urls
1790
1791 for url in urls:
1792 if url not in self.ud:
Brad Bishop19323692019-04-05 15:28:33 -04001793 self.ud[url] = FetchData(url, self.d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001794 ud = self.ud[url]
1795 ud.setup_localpath(self.d)
1796
1797 if not ud.localfile and ud.localpath is None:
1798 continue
1799
1800 if ud.lockfile:
1801 lf = bb.utils.lockfile(ud.lockfile)
1802
1803 ud.method.clean(ud, self.d)
1804 if ud.donestamp:
1805 bb.utils.remove(ud.donestamp)
1806
1807 if ud.lockfile:
1808 bb.utils.unlockfile(lf)
1809
1810class FetchConnectionCache(object):
1811 """
1812 A class which represents an container for socket connections.
1813 """
1814 def __init__(self):
1815 self.cache = {}
1816
1817 def get_connection_name(self, host, port):
1818 return host + ':' + str(port)
1819
1820 def add_connection(self, host, port, connection):
1821 cn = self.get_connection_name(host, port)
1822
1823 if cn not in self.cache:
1824 self.cache[cn] = connection
1825
1826 def get_connection(self, host, port):
1827 connection = None
1828
1829 cn = self.get_connection_name(host, port)
1830 if cn in self.cache:
1831 connection = self.cache[cn]
1832
1833 return connection
1834
1835 def remove_connection(self, host, port):
1836 cn = self.get_connection_name(host, port)
1837 if cn in self.cache:
1838 self.cache[cn].close()
1839 del self.cache[cn]
1840
1841 def close_connections(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001842 for cn in list(self.cache.keys()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001843 self.cache[cn].close()
1844 del self.cache[cn]
1845
1846from . import cvs
1847from . import git
1848from . import gitsm
1849from . import gitannex
1850from . import local
1851from . import svn
1852from . import wget
1853from . import ssh
1854from . import sftp
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001855from . import s3
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001856from . import perforce
1857from . import bzr
1858from . import hg
1859from . import osc
1860from . import repo
1861from . import clearcase
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001862from . import npm
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001863
1864methods.append(local.Local())
1865methods.append(wget.Wget())
1866methods.append(svn.Svn())
1867methods.append(git.Git())
1868methods.append(gitsm.GitSM())
1869methods.append(gitannex.GitANNEX())
1870methods.append(cvs.Cvs())
1871methods.append(ssh.SSH())
1872methods.append(sftp.SFTP())
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001873methods.append(s3.S3())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001874methods.append(perforce.Perforce())
1875methods.append(bzr.Bzr())
1876methods.append(hg.Hg())
1877methods.append(osc.Osc())
1878methods.append(repo.Repo())
1879methods.append(clearcase.ClearCase())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001880methods.append(npm.Npm())