blob: f70f1b515751dfaf4638ec73883e1665c80bf12e [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 \
259 re.compile("^\w+:(?!//)").match(uri):
260 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
386 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
455 for k in uri_replace_decoded[loc]:
456 if uri_decoded[loc][k] != uri_replace_decoded[loc][k]:
457 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
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500527def fetcher_compare_revisions():
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
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500646 if not os.path.exists(ud.donestamp):
647 return False
648
649 if (not ud.method.supports_checksum(ud) or
650 (origud and not origud.method.supports_checksum(origud))):
651 # done stamp exists, checksums not supported; assume the local file is
652 # current
653 return True
654
655 if not os.path.exists(ud.localpath):
656 # done stamp exists, but the downloaded file does not; the done stamp
657 # must be incorrect, re-trigger the download
658 bb.utils.remove(ud.donestamp)
659 return False
660
661 precomputed_checksums = {}
662 # Only re-use the precomputed checksums if the donestamp is newer than the
663 # file. Do not rely on the mtime of directories, though. If ud.localpath is
664 # a directory, there will probably not be any checksums anyway.
665 if (os.path.isdir(ud.localpath) or
666 os.path.getmtime(ud.localpath) < os.path.getmtime(ud.donestamp)):
667 try:
668 with open(ud.donestamp, "rb") as cachefile:
669 pickled = pickle.Unpickler(cachefile)
670 precomputed_checksums.update(pickled.load())
671 except Exception as e:
672 # Avoid the warnings on the upgrade path from emtpy done stamp
673 # files to those containing the checksums.
674 if not isinstance(e, EOFError):
675 # Ignore errors, they aren't fatal
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600676 logger.warning("Couldn't load checksums from donestamp %s: %s "
677 "(msg: %s)" % (ud.donestamp, type(e).__name__,
678 str(e)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500679
680 try:
681 checksums = verify_checksum(ud, d, precomputed_checksums)
682 # If the cache file did not have the checksums, compute and store them
683 # as an upgrade path from the previous done stamp file format.
684 if checksums != precomputed_checksums:
685 with open(ud.donestamp, "wb") as cachefile:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600686 p = pickle.Pickler(cachefile, 2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500687 p.dump(checksums)
688 return True
689 except ChecksumError as e:
690 # Checksums failed to verify, trigger re-download and remove the
691 # incorrect stamp file.
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600692 logger.warning("Checksum mismatch for local file %s\n"
693 "Cleaning and trying again." % ud.localpath)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500694 if os.path.exists(ud.localpath):
695 rename_bad_checksum(ud, e.checksum)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500696 bb.utils.remove(ud.donestamp)
697 return False
698
699
700def update_stamp(ud, d):
701 """
702 donestamp is file stamp indicating the whole fetching is done
703 this function update the stamp after verifying the checksum
704 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500705 if not ud.needdonestamp:
706 return
707
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500708 if os.path.exists(ud.donestamp):
709 # Touch the done stamp file to show active use of the download
710 try:
711 os.utime(ud.donestamp, None)
712 except:
713 # Errors aren't fatal here
714 pass
715 else:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500716 try:
717 checksums = verify_checksum(ud, d)
718 # Store the checksums for later re-verification against the recipe
719 with open(ud.donestamp, "wb") as cachefile:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600720 p = pickle.Pickler(cachefile, 2)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500721 p.dump(checksums)
722 except ChecksumError as e:
723 # Checksums failed to verify, trigger re-download and remove the
724 # incorrect stamp file.
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600725 logger.warning("Checksum mismatch for local file %s\n"
726 "Cleaning and trying again." % ud.localpath)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500727 if os.path.exists(ud.localpath):
728 rename_bad_checksum(ud, e.checksum)
729 bb.utils.remove(ud.donestamp)
730 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500731
732def subprocess_setup():
733 # Python installs a SIGPIPE handler by default. This is usually not what
734 # non-Python subprocesses expect.
735 # SIGPIPE errors are known issues with gzip/bash
736 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
737
738def get_autorev(d):
739 # only not cache src rev in autorev case
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500740 if d.getVar('BB_SRCREV_POLICY') != "cache":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500741 d.setVar('BB_DONT_CACHE', '1')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500742 return "AUTOINC"
743
744def get_srcrev(d, method_name='sortable_revision'):
745 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500746 Return the revision string, usually for use in the version string (PV) of the current package
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500747 Most packages usually only have one SCM so we just pass on the call.
748 In the multi SCM case, we build a value based on SRCREV_FORMAT which must
749 have been set.
750
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500751 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 -0500752 incremental, other code is then responsible for turning that into an increasing value (if needed)
753
754 A method_name can be supplied to retrieve an alternatively formatted revision from a fetcher, if
755 that fetcher provides a method with the given name and the same signature as sortable_revision.
756 """
757
758 scms = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500759 fetcher = Fetch(d.getVar('SRC_URI').split(), d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500760 urldata = fetcher.ud
761 for u in urldata:
762 if urldata[u].method.supports_srcrev():
763 scms.append(u)
764
765 if len(scms) == 0:
766 raise FetchError("SRCREV was used yet no valid SCM was found in SRC_URI")
767
768 if len(scms) == 1 and len(urldata[scms[0]].names) == 1:
769 autoinc, rev = getattr(urldata[scms[0]].method, method_name)(urldata[scms[0]], d, urldata[scms[0]].names[0])
770 if len(rev) > 10:
771 rev = rev[:10]
772 if autoinc:
773 return "AUTOINC+" + rev
774 return rev
775
776 #
777 # Mutiple SCMs are in SRC_URI so we resort to SRCREV_FORMAT
778 #
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500779 format = d.getVar('SRCREV_FORMAT')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500780 if not format:
781 raise FetchError("The SRCREV_FORMAT variable must be set when multiple SCMs are used.")
782
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',
831 'GIT_SSL_CAINFO',
832 'GIT_SMART_HTTP',
833 'SSH_AUTH_SOCK', 'SSH_AGENT_PID',
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600834 'SOCKS5_USER', 'SOCKS5_PASSWD',
835 'DBUS_SESSION_BUS_ADDRESS',
836 'P4CONFIG']
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500837
838 if not cleanup:
839 cleanup = []
840
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500841 # If PATH contains WORKDIR which contains PV which contains SRCPV we
842 # can end up in circular recursion here so give the option of breaking it
843 # in a data store copy.
844 try:
845 d.getVar("PV")
846 except bb.data_smart.ExpansionError:
847 d = bb.data.createCopy(d)
848 d.setVar("PV", "fetcheravoidrecurse")
849
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600850 origenv = d.getVar("BB_ORIGENV", False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500851 for var in exportvars:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500852 val = d.getVar(var) or (origenv and origenv.getVar(var))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500853 if val:
854 cmd = 'export ' + var + '=\"%s\"; %s' % (val, cmd)
855
856 logger.debug(1, "Running %s", cmd)
857
858 success = False
859 error_message = ""
860
861 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600862 (output, errors) = bb.process.run(cmd, log=log, shell=True, stderr=subprocess.PIPE, cwd=workdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500863 success = True
864 except bb.process.NotFoundError as e:
865 error_message = "Fetch command %s" % (e.command)
866 except bb.process.ExecutionError as e:
867 if e.stdout:
868 output = "output:\n%s\n%s" % (e.stdout, e.stderr)
869 elif e.stderr:
870 output = "output:\n%s" % e.stderr
871 else:
872 output = "no output"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600873 error_message = "Fetch command %s failed with exit code %s, %s" % (e.command, e.exitcode, output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500874 except bb.process.CmdError as e:
875 error_message = "Fetch command %s could not be run:\n%s" % (e.command, e.msg)
876 if not success:
877 for f in cleanup:
878 try:
879 bb.utils.remove(f, True)
880 except OSError:
881 pass
882
883 raise FetchError(error_message)
884
885 return output
886
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500887def check_network_access(d, info, url):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500888 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500889 log remote network access, and error if BB_NO_NETWORK is set or the given
890 URI is untrusted
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500891 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500892 if d.getVar("BB_NO_NETWORK") == "1":
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500893 raise NetworkAccess(url, info)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500894 elif not trusted_network(d, url):
895 raise UntrustedUrl(url, info)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500896 else:
897 logger.debug(1, "Fetcher accessed the network with the command %s" % info)
898
899def build_mirroruris(origud, mirrors, ld):
900 uris = []
901 uds = []
902
903 replacements = {}
904 replacements["TYPE"] = origud.type
905 replacements["HOST"] = origud.host
906 replacements["PATH"] = origud.path
907 replacements["BASENAME"] = origud.path.split("/")[-1]
908 replacements["MIRRORNAME"] = origud.host.replace(':','.') + origud.path.replace('/', '.').replace('*', '.')
909
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500910 def adduri(ud, uris, uds, mirrors, tarballs):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500911 for line in mirrors:
912 try:
913 (find, replace) = line
914 except ValueError:
915 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500916
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500917 for tarball in tarballs:
918 newuri = uri_replace(ud, find, replace, replacements, ld, tarball)
919 if not newuri or newuri in uris or newuri == origud.url:
920 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500921
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500922 if not trusted_network(ld, newuri):
923 logger.debug(1, "Mirror %s not in the list of trusted networks, skipping" % (newuri))
924 continue
Patrick Williamsd7e96312015-09-22 08:09:05 -0500925
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500926 # Create a local copy of the mirrors minus the current line
927 # this will prevent us from recursively processing the same line
928 # as well as indirect recursion A -> B -> C -> A
929 localmirrors = list(mirrors)
930 localmirrors.remove(line)
931
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500932 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500933 newud = FetchData(newuri, ld)
934 newud.setup_localpath(ld)
935 except bb.fetch2.BBFetchException as e:
936 logger.debug(1, "Mirror fetch failure for url %s (original url: %s)" % (newuri, origud.url))
937 logger.debug(1, str(e))
938 try:
939 # setup_localpath of file:// urls may fail, we should still see
940 # if mirrors of the url exist
941 adduri(newud, uris, uds, localmirrors, tarballs)
942 except UnboundLocalError:
943 pass
944 continue
945 uris.append(newuri)
946 uds.append(newud)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500947
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500948 adduri(newud, uris, uds, localmirrors, tarballs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500949
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500950 adduri(origud, uris, uds, mirrors, origud.mirrortarballs or [None])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500951
952 return uris, uds
953
954def rename_bad_checksum(ud, suffix):
955 """
956 Renames files to have suffix from parameter
957 """
958
959 if ud.localpath is None:
960 return
961
962 new_localpath = "%s_bad-checksum_%s" % (ud.localpath, suffix)
963 bb.warn("Renaming %s to %s" % (ud.localpath, new_localpath))
964 bb.utils.movefile(ud.localpath, new_localpath)
965
966
967def try_mirror_url(fetch, origud, ud, ld, check = False):
968 # Return of None or a value means we're finished
969 # False means try another url
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500970
971 if ud.lockfile and ud.lockfile != origud.lockfile:
972 lf = bb.utils.lockfile(ud.lockfile)
973
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500974 try:
975 if check:
976 found = ud.method.checkstatus(fetch, ud, ld)
977 if found:
978 return found
979 return False
980
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500981 if not verify_donestamp(ud, ld, origud) or ud.method.need_update(ud, ld):
982 ud.method.download(ud, ld)
983 if hasattr(ud.method,"build_mirror_data"):
984 ud.method.build_mirror_data(ud, ld)
985
986 if not ud.localpath or not os.path.exists(ud.localpath):
987 return False
988
989 if ud.localpath == origud.localpath:
990 return ud.localpath
991
992 # We may be obtaining a mirror tarball which needs further processing by the real fetcher
993 # If that tarball is a local file:// we need to provide a symlink to it
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500994 dldir = ld.getVar("DL_DIR")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500995
996 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 -0500997 # Create donestamp in old format to avoid triggering a re-download
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500998 if ud.donestamp:
999 bb.utils.mkdirhier(os.path.dirname(ud.donestamp))
1000 open(ud.donestamp, 'w').close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001001 dest = os.path.join(dldir, os.path.basename(ud.localpath))
1002 if not os.path.exists(dest):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001003 # In case this is executing without any file locks held (as is
1004 # the case for file:// URLs), two tasks may end up here at the
1005 # same time, in which case we do not want the second task to
1006 # fail when the link has already been created by the first task.
1007 try:
1008 os.symlink(ud.localpath, dest)
1009 except FileExistsError:
1010 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001011 if not verify_donestamp(origud, ld) or origud.method.need_update(origud, ld):
1012 origud.method.download(origud, ld)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001013 if hasattr(origud.method, "build_mirror_data"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001014 origud.method.build_mirror_data(origud, ld)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001015 return origud.localpath
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001016 # Otherwise the result is a local file:// and we symlink to it
1017 if not os.path.exists(origud.localpath):
1018 if os.path.islink(origud.localpath):
1019 # Broken symbolic link
1020 os.unlink(origud.localpath)
1021
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001022 # As per above, in case two tasks end up here simultaneously.
1023 try:
1024 os.symlink(ud.localpath, origud.localpath)
1025 except FileExistsError:
1026 pass
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:
1034 if e.errno in [os.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
1060def try_mirrors(fetch, d, origud, mirrors, check = False):
1061 """
1062 Try to use a mirrored version of the sources.
1063 This method will be automatically called before the fetchers go.
1064
1065 d Is a bb.data instance
1066 uri is the original uri we're trying to download
1067 mirrors is the list of mirrors we're going to try
1068 """
1069 ld = d.createCopy()
1070
1071 uris, uds = build_mirroruris(origud, mirrors, ld)
1072
1073 for index, uri in enumerate(uris):
1074 ret = try_mirror_url(fetch, origud, uds[index], ld, check)
1075 if ret != False:
1076 return ret
1077 return None
1078
1079def trusted_network(d, url):
1080 """
1081 Use a trusted url during download if networking is enabled and
1082 BB_ALLOWED_NETWORKS is set globally or for a specific recipe.
1083 Note: modifies SRC_URI & mirrors.
1084 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001085 if d.getVar('BB_NO_NETWORK') == "1":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001086 return True
1087
1088 pkgname = d.expand(d.getVar('PN', False))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001089 trusted_hosts = d.getVarFlag('BB_ALLOWED_NETWORKS', pkgname, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001090
1091 if not trusted_hosts:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001092 trusted_hosts = d.getVar('BB_ALLOWED_NETWORKS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001093
1094 # Not enabled.
1095 if not trusted_hosts:
1096 return True
1097
1098 scheme, network, path, user, passwd, param = decodeurl(url)
1099
1100 if not network:
1101 return True
1102
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001103 network = network.split(':')[0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001104 network = network.lower()
1105
1106 for host in trusted_hosts.split(" "):
1107 host = host.lower()
1108 if host.startswith("*.") and ("." + network).endswith(host[1:]):
1109 return True
1110 if host == network:
1111 return True
1112
1113 return False
1114
1115def srcrev_internal_helper(ud, d, name):
1116 """
1117 Return:
1118 a) a source revision if specified
1119 b) latest revision if SRCREV="AUTOINC"
1120 c) None if not specified
1121 """
1122
1123 srcrev = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001124 pn = d.getVar("PN")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001125 attempts = []
1126 if name != '' and pn:
1127 attempts.append("SRCREV_%s_pn-%s" % (name, pn))
1128 if name != '':
1129 attempts.append("SRCREV_%s" % name)
1130 if pn:
1131 attempts.append("SRCREV_pn-%s" % pn)
1132 attempts.append("SRCREV")
1133
1134 for a in attempts:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001135 srcrev = d.getVar(a)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001136 if srcrev and srcrev != "INVALID":
1137 break
1138
1139 if 'rev' in ud.parm and 'tag' in ud.parm:
1140 raise FetchError("Please specify a ;rev= parameter or a ;tag= parameter in the url %s but not both." % (ud.url))
1141
1142 if 'rev' in ud.parm or 'tag' in ud.parm:
1143 if 'rev' in ud.parm:
1144 parmrev = ud.parm['rev']
1145 else:
1146 parmrev = ud.parm['tag']
1147 if srcrev == "INVALID" or not srcrev:
1148 return parmrev
1149 if srcrev != parmrev:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001150 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 -05001151 return parmrev
1152
1153 if srcrev == "INVALID" or not srcrev:
1154 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)
1155 if srcrev == "AUTOINC":
1156 srcrev = ud.method.latest_revision(ud, d, name)
1157
1158 return srcrev
1159
1160def get_checksum_file_list(d):
1161 """ Get a list of files checksum in SRC_URI
1162
1163 Returns the resolved local paths of all local file entries in
1164 SRC_URI as a space-separated string
1165 """
1166 fetch = Fetch([], d, cache = False, localonly = True)
1167
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001168 dl_dir = d.getVar('DL_DIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001169 filelist = []
1170 for u in fetch.urls:
1171 ud = fetch.ud[u]
1172
1173 if ud and isinstance(ud.method, local.Local):
1174 paths = ud.method.localpaths(ud, d)
1175 for f in paths:
1176 pth = ud.decodedurl
1177 if '*' in pth:
1178 f = os.path.join(os.path.abspath(f), pth)
1179 if f.startswith(dl_dir):
1180 # The local fetcher's behaviour is to return a path under DL_DIR if it couldn't find the file anywhere else
1181 if os.path.exists(f):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001182 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 -05001183 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001184 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 -05001185 filelist.append(f + ":" + str(os.path.exists(f)))
1186
1187 return " ".join(filelist)
1188
1189def get_file_checksums(filelist, pn):
1190 """Get a list of the checksums for a list of local files
1191
1192 Returns the checksums for a list of local files, caching the results as
1193 it proceeds
1194
1195 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001196 return _checksum_cache.get_checksums(filelist, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001197
1198
1199class FetchData(object):
1200 """
1201 A class which represents the fetcher state for a given URI.
1202 """
1203 def __init__(self, url, d, localonly = False):
1204 # localpath is the location of a downloaded result. If not set, the file is local.
1205 self.donestamp = None
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001206 self.needdonestamp = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001207 self.localfile = ""
1208 self.localpath = None
1209 self.lockfile = None
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001210 self.mirrortarballs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001211 self.basename = None
1212 self.basepath = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001213 (self.type, self.host, self.path, self.user, self.pswd, self.parm) = decodeurl(d.expand(url))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001214 self.date = self.getSRCDate(d)
1215 self.url = url
1216 if not self.user and "user" in self.parm:
1217 self.user = self.parm["user"]
1218 if not self.pswd and "pswd" in self.parm:
1219 self.pswd = self.parm["pswd"]
1220 self.setup = False
1221
1222 if "name" in self.parm:
1223 self.md5_name = "%s.md5sum" % self.parm["name"]
1224 self.sha256_name = "%s.sha256sum" % self.parm["name"]
1225 else:
1226 self.md5_name = "md5sum"
1227 self.sha256_name = "sha256sum"
1228 if self.md5_name in self.parm:
1229 self.md5_expected = self.parm[self.md5_name]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001230 elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001231 self.md5_expected = None
1232 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001233 self.md5_expected = d.getVarFlag("SRC_URI", self.md5_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001234 if self.sha256_name in self.parm:
1235 self.sha256_expected = self.parm[self.sha256_name]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001236 elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001237 self.sha256_expected = None
1238 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001239 self.sha256_expected = d.getVarFlag("SRC_URI", self.sha256_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001240 self.ignore_checksums = False
1241
1242 self.names = self.parm.get("name",'default').split(',')
1243
1244 self.method = None
1245 for m in methods:
1246 if m.supports(self, d):
1247 self.method = m
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001248 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001249
1250 if not self.method:
1251 raise NoMethodError(url)
1252
1253 if localonly and not isinstance(self.method, local.Local):
1254 raise NonLocalMethod()
1255
1256 if self.parm.get("proto", None) and "protocol" not in self.parm:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001257 logger.warning('Consider updating %s recipe to use "protocol" not "proto" in SRC_URI.', d.getVar('PN'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001258 self.parm["protocol"] = self.parm.get("proto", None)
1259
1260 if hasattr(self.method, "urldata_init"):
1261 self.method.urldata_init(self, d)
1262
1263 if "localpath" in self.parm:
1264 # if user sets localpath for file, use it instead.
1265 self.localpath = self.parm["localpath"]
1266 self.basename = os.path.basename(self.localpath)
1267 elif self.localfile:
1268 self.localpath = self.method.localpath(self, d)
1269
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001270 dldir = d.getVar("DL_DIR")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001271
1272 if not self.needdonestamp:
1273 return
1274
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001275 # Note: .done and .lock files should always be in DL_DIR whereas localpath may not be.
1276 if self.localpath and self.localpath.startswith(dldir):
1277 basepath = self.localpath
1278 elif self.localpath:
1279 basepath = dldir + os.sep + os.path.basename(self.localpath)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001280 elif self.basepath or self.basename:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001281 basepath = dldir + os.sep + (self.basepath or self.basename)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001282 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001283 bb.fatal("Can't determine lock path for url %s" % url)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001284
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001285 self.donestamp = basepath + '.done'
1286 self.lockfile = basepath + '.lock'
1287
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001288 def setup_revisions(self, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001289 self.revisions = {}
1290 for name in self.names:
1291 self.revisions[name] = srcrev_internal_helper(self, d, name)
1292
1293 # add compatibility code for non name specified case
1294 if len(self.names) == 1:
1295 self.revision = self.revisions[self.names[0]]
1296
1297 def setup_localpath(self, d):
1298 if not self.localpath:
1299 self.localpath = self.method.localpath(self, d)
1300
1301 def getSRCDate(self, d):
1302 """
1303 Return the SRC Date for the component
1304
1305 d the bb.data module
1306 """
1307 if "srcdate" in self.parm:
1308 return self.parm['srcdate']
1309
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001310 pn = d.getVar("PN")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001311
1312 if pn:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001313 return d.getVar("SRCDATE_%s" % pn) or d.getVar("SRCDATE") or d.getVar("DATE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001314
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001315 return d.getVar("SRCDATE") or d.getVar("DATE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001316
1317class FetchMethod(object):
1318 """Base class for 'fetch'ing data"""
1319
1320 def __init__(self, urls=None):
1321 self.urls = []
1322
1323 def supports(self, urldata, d):
1324 """
1325 Check to see if this fetch class supports a given url.
1326 """
1327 return 0
1328
1329 def localpath(self, urldata, d):
1330 """
1331 Return the local filename of a given url assuming a successful fetch.
1332 Can also setup variables in urldata for use in go (saving code duplication
1333 and duplicate code execution)
1334 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001335 return os.path.join(d.getVar("DL_DIR"), urldata.localfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001336
1337 def supports_checksum(self, urldata):
1338 """
1339 Is localpath something that can be represented by a checksum?
1340 """
1341
1342 # We cannot compute checksums for directories
1343 if os.path.isdir(urldata.localpath) == True:
1344 return False
1345 if urldata.localpath.find("*") != -1:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001346 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001347
1348 return True
1349
1350 def recommends_checksum(self, urldata):
1351 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001352 Is the backend on where checksumming is recommended (should warnings
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001353 be displayed if there is no checksum)?
1354 """
1355 return False
1356
1357 def _strip_leading_slashes(self, relpath):
1358 """
1359 Remove leading slash as os.path.join can't cope
1360 """
1361 while os.path.isabs(relpath):
1362 relpath = relpath[1:]
1363 return relpath
1364
1365 def setUrls(self, urls):
1366 self.__urls = urls
1367
1368 def getUrls(self):
1369 return self.__urls
1370
1371 urls = property(getUrls, setUrls, None, "Urls property")
1372
1373 def need_update(self, ud, d):
1374 """
1375 Force a fetch, even if localpath exists?
1376 """
1377 if os.path.exists(ud.localpath):
1378 return False
1379 return True
1380
1381 def supports_srcrev(self):
1382 """
1383 The fetcher supports auto source revisions (SRCREV)
1384 """
1385 return False
1386
1387 def download(self, urldata, d):
1388 """
1389 Fetch urls
1390 Assumes localpath was called first
1391 """
1392 raise NoMethodError(url)
1393
1394 def unpack(self, urldata, rootdir, data):
1395 iterate = False
1396 file = urldata.localpath
1397
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001398 # Localpath can't deal with 'dir/*' entries, so it converts them to '.',
1399 # but it must be corrected back for local files copying
1400 if urldata.basename == '*' and file.endswith('/.'):
1401 file = '%s/%s' % (file.rstrip('/.'), urldata.path)
1402
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001403 try:
1404 unpack = bb.utils.to_boolean(urldata.parm.get('unpack'), True)
1405 except ValueError as exc:
1406 bb.fatal("Invalid value for 'unpack' parameter for %s: %s" %
1407 (file, urldata.parm.get('unpack')))
1408
1409 base, ext = os.path.splitext(file)
1410 if ext in ['.gz', '.bz2', '.Z', '.xz', '.lz']:
1411 efile = os.path.join(rootdir, os.path.basename(base))
1412 else:
1413 efile = file
1414 cmd = None
1415
1416 if unpack:
1417 if file.endswith('.tar'):
1418 cmd = 'tar x --no-same-owner -f %s' % file
1419 elif file.endswith('.tgz') or file.endswith('.tar.gz') or file.endswith('.tar.Z'):
1420 cmd = 'tar xz --no-same-owner -f %s' % file
1421 elif file.endswith('.tbz') or file.endswith('.tbz2') or file.endswith('.tar.bz2'):
1422 cmd = 'bzip2 -dc %s | tar x --no-same-owner -f -' % file
1423 elif file.endswith('.gz') or file.endswith('.Z') or file.endswith('.z'):
1424 cmd = 'gzip -dc %s > %s' % (file, efile)
1425 elif file.endswith('.bz2'):
1426 cmd = 'bzip2 -dc %s > %s' % (file, efile)
1427 elif file.endswith('.tar.xz'):
1428 cmd = 'xz -dc %s | tar x --no-same-owner -f -' % file
1429 elif file.endswith('.xz'):
1430 cmd = 'xz -dc %s > %s' % (file, efile)
1431 elif file.endswith('.tar.lz'):
1432 cmd = 'lzip -dc %s | tar x --no-same-owner -f -' % file
1433 elif file.endswith('.lz'):
1434 cmd = 'lzip -dc %s > %s' % (file, efile)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001435 elif file.endswith('.tar.7z'):
1436 cmd = '7z x -so %s | tar x --no-same-owner -f -' % file
1437 elif file.endswith('.7z'):
1438 cmd = '7za x -y %s 1>/dev/null' % file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001439 elif file.endswith('.zip') or file.endswith('.jar'):
1440 try:
1441 dos = bb.utils.to_boolean(urldata.parm.get('dos'), False)
1442 except ValueError as exc:
1443 bb.fatal("Invalid value for 'dos' parameter for %s: %s" %
1444 (file, urldata.parm.get('dos')))
1445 cmd = 'unzip -q -o'
1446 if dos:
1447 cmd = '%s -a' % cmd
1448 cmd = "%s '%s'" % (cmd, file)
1449 elif file.endswith('.rpm') or file.endswith('.srpm'):
1450 if 'extract' in urldata.parm:
1451 unpack_file = urldata.parm.get('extract')
1452 cmd = 'rpm2cpio.sh %s | cpio -id %s' % (file, unpack_file)
1453 iterate = True
1454 iterate_file = unpack_file
1455 else:
1456 cmd = 'rpm2cpio.sh %s | cpio -id' % (file)
1457 elif file.endswith('.deb') or file.endswith('.ipk'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001458 output = subprocess.check_output('ar -t %s' % file, preexec_fn=subprocess_setup, shell=True)
1459 datafile = None
1460 if output:
1461 for line in output.decode().splitlines():
1462 if line.startswith('data.tar.'):
1463 datafile = line
1464 break
1465 else:
1466 raise UnpackError("Unable to unpack deb/ipk package - does not contain data.tar.* file", urldata.url)
1467 else:
1468 raise UnpackError("Unable to unpack deb/ipk package - could not list contents", urldata.url)
1469 cmd = 'ar x %s %s && tar --no-same-owner -xpf %s && rm %s' % (file, datafile, datafile, datafile)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001470
1471 # If 'subdir' param exists, create a dir and use it as destination for unpack cmd
1472 if 'subdir' in urldata.parm:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001473 subdir = urldata.parm.get('subdir')
1474 if os.path.isabs(subdir):
1475 if not os.path.realpath(subdir).startswith(os.path.realpath(rootdir)):
1476 raise UnpackError("subdir argument isn't a subdirectory of unpack root %s" % rootdir, urldata.url)
1477 unpackdir = subdir
1478 else:
1479 unpackdir = os.path.join(rootdir, subdir)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001480 bb.utils.mkdirhier(unpackdir)
1481 else:
1482 unpackdir = rootdir
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001483
1484 if not unpack or not cmd:
1485 # If file == dest, then avoid any copies, as we already put the file into dest!
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001486 dest = os.path.join(unpackdir, os.path.basename(file))
1487 if file != dest and not (os.path.exists(dest) and os.path.samefile(file, dest)):
1488 destdir = '.'
1489 # For file:// entries all intermediate dirs in path must be created at destination
1490 if urldata.type == "file":
1491 # Trailing '/' does a copying to wrong place
1492 urlpath = urldata.path.rstrip('/')
1493 # Want files places relative to cwd so no leading '/'
1494 urlpath = urlpath.lstrip('/')
1495 if urlpath.find("/") != -1:
1496 destdir = urlpath.rsplit("/", 1)[0] + '/'
1497 bb.utils.mkdirhier("%s/%s" % (unpackdir, destdir))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001498 cmd = 'cp -fpPRH %s %s' % (file, destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001499
1500 if not cmd:
1501 return
1502
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001503 path = data.getVar('PATH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001504 if path:
1505 cmd = "PATH=\"%s\" %s" % (path, cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001506 bb.note("Unpacking %s to %s/" % (file, unpackdir))
1507 ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True, cwd=unpackdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001508
1509 if ret != 0:
1510 raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), urldata.url)
1511
1512 if iterate is True:
1513 iterate_urldata = urldata
1514 iterate_urldata.localpath = "%s/%s" % (rootdir, iterate_file)
1515 self.unpack(urldata, rootdir, data)
1516
1517 return
1518
1519 def clean(self, urldata, d):
1520 """
1521 Clean any existing full or partial download
1522 """
1523 bb.utils.remove(urldata.localpath)
1524
1525 def try_premirror(self, urldata, d):
1526 """
1527 Should premirrors be used?
1528 """
1529 return True
1530
1531 def checkstatus(self, fetch, urldata, d):
1532 """
1533 Check the status of a URL
1534 Assumes localpath was called first
1535 """
1536 logger.info("URL %s could not be checked for status since no method exists.", url)
1537 return True
1538
1539 def latest_revision(self, ud, d, name):
1540 """
1541 Look in the cache for the latest revision, if not present ask the SCM.
1542 """
1543 if not hasattr(self, "_latest_revision"):
1544 raise ParameterError("The fetcher for this URL does not support _latest_revision", url)
1545
1546 revs = bb.persist_data.persist('BB_URI_HEADREVS', d)
1547 key = self.generate_revision_key(ud, d, name)
1548 try:
1549 return revs[key]
1550 except KeyError:
1551 revs[key] = rev = self._latest_revision(ud, d, name)
1552 return rev
1553
1554 def sortable_revision(self, ud, d, name):
1555 latest_rev = self._build_revision(ud, d, name)
1556 return True, str(latest_rev)
1557
1558 def generate_revision_key(self, ud, d, name):
1559 key = self._revision_key(ud, d, name)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001560 return "%s-%s" % (key, d.getVar("PN") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001561
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001562 def latest_versionstring(self, ud, d):
1563 """
1564 Compute the latest release name like "x.y.x" in "x.y.x+gitHASH"
1565 by searching through the tags output of ls-remote, comparing
1566 versions and returning the highest match as a (version, revision) pair.
1567 """
1568 return ('', '')
1569
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001570class Fetch(object):
1571 def __init__(self, urls, d, cache = True, localonly = False, connection_cache = None):
1572 if localonly and cache:
1573 raise Exception("bb.fetch2.Fetch.__init__: cannot set cache and localonly at same time")
1574
1575 if len(urls) == 0:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001576 urls = d.getVar("SRC_URI").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001577 self.urls = urls
1578 self.d = d
1579 self.ud = {}
1580 self.connection_cache = connection_cache
1581
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001582 fn = d.getVar('FILE')
1583 mc = d.getVar('__BBMULTICONFIG') or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001584 if cache and fn and mc + fn in urldata_cache:
1585 self.ud = urldata_cache[mc + fn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001586
1587 for url in urls:
1588 if url not in self.ud:
1589 try:
1590 self.ud[url] = FetchData(url, d, localonly)
1591 except NonLocalMethod:
1592 if localonly:
1593 self.ud[url] = None
1594 pass
1595
1596 if fn and cache:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001597 urldata_cache[mc + fn] = self.ud
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001598
1599 def localpath(self, url):
1600 if url not in self.urls:
1601 self.ud[url] = FetchData(url, self.d)
1602
1603 self.ud[url].setup_localpath(self.d)
1604 return self.d.expand(self.ud[url].localpath)
1605
1606 def localpaths(self):
1607 """
1608 Return a list of the local filenames, assuming successful fetch
1609 """
1610 local = []
1611
1612 for u in self.urls:
1613 ud = self.ud[u]
1614 ud.setup_localpath(self.d)
1615 local.append(ud.localpath)
1616
1617 return local
1618
1619 def download(self, urls=None):
1620 """
1621 Fetch all urls
1622 """
1623 if not urls:
1624 urls = self.urls
1625
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001626 network = self.d.getVar("BB_NO_NETWORK")
1627 premirroronly = (self.d.getVar("BB_FETCH_PREMIRRORONLY") == "1")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001628
1629 for u in urls:
1630 ud = self.ud[u]
1631 ud.setup_localpath(self.d)
1632 m = ud.method
1633 localpath = ""
1634
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001635 if ud.lockfile:
1636 lf = bb.utils.lockfile(ud.lockfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001637
1638 try:
1639 self.d.setVar("BB_NO_NETWORK", network)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001640
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001641 if verify_donestamp(ud, self.d) and not m.need_update(ud, self.d):
1642 localpath = ud.localpath
1643 elif m.try_premirror(ud, self.d):
1644 logger.debug(1, "Trying PREMIRRORS")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001645 mirrors = mirror_from_string(self.d.getVar('PREMIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001646 localpath = try_mirrors(self, self.d, ud, mirrors, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001647 if localpath:
1648 try:
1649 # early checksum verification so that if the checksum of the premirror
1650 # contents mismatch the fetcher can still try upstream and mirrors
1651 update_stamp(ud, self.d)
1652 except ChecksumError as e:
1653 logger.warning("Checksum failure encountered with premirror download of %s - will attempt other sources." % u)
1654 logger.debug(1, str(e))
1655 localpath = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001656
1657 if premirroronly:
1658 self.d.setVar("BB_NO_NETWORK", "1")
1659
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001660 firsterr = None
1661 verified_stamp = verify_donestamp(ud, self.d)
1662 if not localpath and (not verified_stamp or m.need_update(ud, self.d)):
1663 try:
1664 if not trusted_network(self.d, ud.url):
1665 raise UntrustedUrl(ud.url)
1666 logger.debug(1, "Trying Upstream")
1667 m.download(ud, self.d)
1668 if hasattr(m, "build_mirror_data"):
1669 m.build_mirror_data(ud, self.d)
1670 localpath = ud.localpath
1671 # early checksum verify, so that if checksum mismatched,
1672 # fetcher still have chance to fetch from mirror
1673 update_stamp(ud, self.d)
1674
1675 except bb.fetch2.NetworkAccess:
1676 raise
1677
1678 except BBFetchException as e:
1679 if isinstance(e, ChecksumError):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001680 logger.warning("Checksum failure encountered with download of %s - will attempt other sources if available" % u)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001681 logger.debug(1, str(e))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001682 if os.path.exists(ud.localpath):
1683 rename_bad_checksum(ud, e.checksum)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001684 elif isinstance(e, NoChecksumError):
1685 raise
1686 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001687 logger.warning('Failed to fetch URL %s, attempting MIRRORS if available' % u)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001688 logger.debug(1, str(e))
1689 firsterr = e
1690 # Remove any incomplete fetch
1691 if not verified_stamp:
1692 m.clean(ud, self.d)
1693 logger.debug(1, "Trying MIRRORS")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001694 mirrors = mirror_from_string(self.d.getVar('MIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001695 localpath = try_mirrors(self, self.d, ud, mirrors)
1696
1697 if not localpath or ((not os.path.exists(localpath)) and localpath.find("*") == -1):
1698 if firsterr:
1699 logger.error(str(firsterr))
1700 raise FetchError("Unable to fetch URL from any source.", u)
1701
1702 update_stamp(ud, self.d)
1703
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001704 except IOError as e:
1705 if e.errno in [os.errno.ESTALE]:
1706 logger.error("Stale Error Observed %s." % u)
1707 raise ChecksumError("Stale Error Detected")
1708
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001709 except BBFetchException as e:
1710 if isinstance(e, ChecksumError):
1711 logger.error("Checksum failure fetching %s" % u)
1712 raise
1713
1714 finally:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001715 if ud.lockfile:
1716 bb.utils.unlockfile(lf)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001717
1718 def checkstatus(self, urls=None):
1719 """
1720 Check all urls exist upstream
1721 """
1722
1723 if not urls:
1724 urls = self.urls
1725
1726 for u in urls:
1727 ud = self.ud[u]
1728 ud.setup_localpath(self.d)
1729 m = ud.method
1730 logger.debug(1, "Testing URL %s", u)
1731 # First try checking uri, u, from PREMIRRORS
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001732 mirrors = mirror_from_string(self.d.getVar('PREMIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001733 ret = try_mirrors(self, self.d, ud, mirrors, True)
1734 if not ret:
1735 # Next try checking from the original uri, u
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001736 ret = m.checkstatus(self, ud, self.d)
1737 if not ret:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001738 # Finally, try checking uri, u, from MIRRORS
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001739 mirrors = mirror_from_string(self.d.getVar('MIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001740 ret = try_mirrors(self, self.d, ud, mirrors, True)
1741
1742 if not ret:
1743 raise FetchError("URL %s doesn't work" % u, u)
1744
1745 def unpack(self, root, urls=None):
1746 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001747 Unpack urls to root
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001748 """
1749
1750 if not urls:
1751 urls = self.urls
1752
1753 for u in urls:
1754 ud = self.ud[u]
1755 ud.setup_localpath(self.d)
1756
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001757 if ud.lockfile:
1758 lf = bb.utils.lockfile(ud.lockfile)
1759
1760 ud.method.unpack(ud, root, self.d)
1761
1762 if ud.lockfile:
1763 bb.utils.unlockfile(lf)
1764
1765 def clean(self, urls=None):
1766 """
1767 Clean files that the fetcher gets or places
1768 """
1769
1770 if not urls:
1771 urls = self.urls
1772
1773 for url in urls:
1774 if url not in self.ud:
1775 self.ud[url] = FetchData(url, d)
1776 ud = self.ud[url]
1777 ud.setup_localpath(self.d)
1778
1779 if not ud.localfile and ud.localpath is None:
1780 continue
1781
1782 if ud.lockfile:
1783 lf = bb.utils.lockfile(ud.lockfile)
1784
1785 ud.method.clean(ud, self.d)
1786 if ud.donestamp:
1787 bb.utils.remove(ud.donestamp)
1788
1789 if ud.lockfile:
1790 bb.utils.unlockfile(lf)
1791
1792class FetchConnectionCache(object):
1793 """
1794 A class which represents an container for socket connections.
1795 """
1796 def __init__(self):
1797 self.cache = {}
1798
1799 def get_connection_name(self, host, port):
1800 return host + ':' + str(port)
1801
1802 def add_connection(self, host, port, connection):
1803 cn = self.get_connection_name(host, port)
1804
1805 if cn not in self.cache:
1806 self.cache[cn] = connection
1807
1808 def get_connection(self, host, port):
1809 connection = None
1810
1811 cn = self.get_connection_name(host, port)
1812 if cn in self.cache:
1813 connection = self.cache[cn]
1814
1815 return connection
1816
1817 def remove_connection(self, host, port):
1818 cn = self.get_connection_name(host, port)
1819 if cn in self.cache:
1820 self.cache[cn].close()
1821 del self.cache[cn]
1822
1823 def close_connections(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001824 for cn in list(self.cache.keys()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001825 self.cache[cn].close()
1826 del self.cache[cn]
1827
1828from . import cvs
1829from . import git
1830from . import gitsm
1831from . import gitannex
1832from . import local
1833from . import svn
1834from . import wget
1835from . import ssh
1836from . import sftp
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001837from . import s3
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001838from . import perforce
1839from . import bzr
1840from . import hg
1841from . import osc
1842from . import repo
1843from . import clearcase
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001844from . import npm
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001845
1846methods.append(local.Local())
1847methods.append(wget.Wget())
1848methods.append(svn.Svn())
1849methods.append(git.Git())
1850methods.append(gitsm.GitSM())
1851methods.append(gitannex.GitANNEX())
1852methods.append(cvs.Cvs())
1853methods.append(ssh.SSH())
1854methods.append(sftp.SFTP())
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001855methods.append(s3.S3())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001856methods.append(perforce.Perforce())
1857methods.append(bzr.Bzr())
1858methods.append(hg.Hg())
1859methods.append(osc.Osc())
1860methods.append(repo.Repo())
1861methods.append(clearcase.ClearCase())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001862methods.append(npm.Npm())