blob: 3b8389e0a2565ab3e4f78e4679312e9c3501ca8e [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
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
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:
780 raise FetchError("The SRCREV_FORMAT variable must be set when multiple SCMs are used.")
781
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600782 name_to_rev = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500783 seenautoinc = False
784 for scm in scms:
785 ud = urldata[scm]
786 for name in ud.names:
787 autoinc, rev = getattr(ud.method, method_name)(ud, d, name)
788 seenautoinc = seenautoinc or autoinc
789 if len(rev) > 10:
790 rev = rev[:10]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600791 name_to_rev[name] = rev
792 # Replace names by revisions in the SRCREV_FORMAT string. The approach used
793 # here can handle names being prefixes of other names and names appearing
794 # as substrings in revisions (in which case the name should not be
795 # expanded). The '|' regular expression operator tries matches from left to
796 # right, so we need to sort the names with the longest ones first.
797 names_descending_len = sorted(name_to_rev, key=len, reverse=True)
798 name_to_rev_re = "|".join(re.escape(name) for name in names_descending_len)
799 format = re.sub(name_to_rev_re, lambda match: name_to_rev[match.group(0)], format)
800
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500801 if seenautoinc:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500802 format = "AUTOINC+" + format
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500803
804 return format
805
806def localpath(url, d):
807 fetcher = bb.fetch2.Fetch([url], d)
808 return fetcher.localpath(url)
809
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600810def runfetchcmd(cmd, d, quiet=False, cleanup=None, log=None, workdir=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500811 """
812 Run cmd returning the command output
813 Raise an error if interrupted or cmd fails
814 Optionally echo command output to stdout
815 Optionally remove the files/directories listed in cleanup upon failure
816 """
817
818 # Need to export PATH as binary could be in metadata paths
819 # rather than host provided
820 # Also include some other variables.
821 # FIXME: Should really include all export varaiables?
822 exportvars = ['HOME', 'PATH',
823 'HTTP_PROXY', 'http_proxy',
824 'HTTPS_PROXY', 'https_proxy',
825 'FTP_PROXY', 'ftp_proxy',
826 'FTPS_PROXY', 'ftps_proxy',
827 'NO_PROXY', 'no_proxy',
828 'ALL_PROXY', 'all_proxy',
829 'GIT_PROXY_COMMAND',
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800830 'GIT_SSH',
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500831 '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 Bishop1a4b7ee2018-12-16 17:11:34 -0800841 # If PATH contains WORKDIR which contains PV-PR which contains SRCPV we
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500842 # 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")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800846 d.getVar("PR")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500847 except bb.data_smart.ExpansionError:
848 d = bb.data.createCopy(d)
849 d.setVar("PV", "fetcheravoidrecurse")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800850 d.setVar("PR", "fetcheravoidrecurse")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500851
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600852 origenv = d.getVar("BB_ORIGENV", False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500853 for var in exportvars:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500854 val = d.getVar(var) or (origenv and origenv.getVar(var))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500855 if val:
856 cmd = 'export ' + var + '=\"%s\"; %s' % (val, cmd)
857
Brad Bishop316dfdd2018-06-25 12:45:53 -0400858 # Disable pseudo as it may affect ssh, potentially causing it to hang.
859 cmd = 'export PSEUDO_DISABLED=1; ' + cmd
860
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500861 logger.debug(1, "Running %s", cmd)
862
863 success = False
864 error_message = ""
865
866 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600867 (output, errors) = bb.process.run(cmd, log=log, shell=True, stderr=subprocess.PIPE, cwd=workdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500868 success = True
869 except bb.process.NotFoundError as e:
870 error_message = "Fetch command %s" % (e.command)
871 except bb.process.ExecutionError as e:
872 if e.stdout:
873 output = "output:\n%s\n%s" % (e.stdout, e.stderr)
874 elif e.stderr:
875 output = "output:\n%s" % e.stderr
876 else:
877 output = "no output"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600878 error_message = "Fetch command %s failed with exit code %s, %s" % (e.command, e.exitcode, output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500879 except bb.process.CmdError as e:
880 error_message = "Fetch command %s could not be run:\n%s" % (e.command, e.msg)
881 if not success:
882 for f in cleanup:
883 try:
884 bb.utils.remove(f, True)
885 except OSError:
886 pass
887
888 raise FetchError(error_message)
889
890 return output
891
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500892def check_network_access(d, info, url):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500893 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500894 log remote network access, and error if BB_NO_NETWORK is set or the given
895 URI is untrusted
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500896 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500897 if d.getVar("BB_NO_NETWORK") == "1":
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500898 raise NetworkAccess(url, info)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500899 elif not trusted_network(d, url):
900 raise UntrustedUrl(url, info)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500901 else:
902 logger.debug(1, "Fetcher accessed the network with the command %s" % info)
903
904def build_mirroruris(origud, mirrors, ld):
905 uris = []
906 uds = []
907
908 replacements = {}
909 replacements["TYPE"] = origud.type
910 replacements["HOST"] = origud.host
911 replacements["PATH"] = origud.path
912 replacements["BASENAME"] = origud.path.split("/")[-1]
913 replacements["MIRRORNAME"] = origud.host.replace(':','.') + origud.path.replace('/', '.').replace('*', '.')
914
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500915 def adduri(ud, uris, uds, mirrors, tarballs):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500916 for line in mirrors:
917 try:
918 (find, replace) = line
919 except ValueError:
920 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500921
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500922 for tarball in tarballs:
923 newuri = uri_replace(ud, find, replace, replacements, ld, tarball)
924 if not newuri or newuri in uris or newuri == origud.url:
925 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500926
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500927 if not trusted_network(ld, newuri):
928 logger.debug(1, "Mirror %s not in the list of trusted networks, skipping" % (newuri))
929 continue
Patrick Williamsd7e96312015-09-22 08:09:05 -0500930
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500931 # Create a local copy of the mirrors minus the current line
932 # this will prevent us from recursively processing the same line
933 # as well as indirect recursion A -> B -> C -> A
934 localmirrors = list(mirrors)
935 localmirrors.remove(line)
936
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500937 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500938 newud = FetchData(newuri, ld)
939 newud.setup_localpath(ld)
940 except bb.fetch2.BBFetchException as e:
941 logger.debug(1, "Mirror fetch failure for url %s (original url: %s)" % (newuri, origud.url))
942 logger.debug(1, str(e))
943 try:
944 # setup_localpath of file:// urls may fail, we should still see
945 # if mirrors of the url exist
946 adduri(newud, uris, uds, localmirrors, tarballs)
947 except UnboundLocalError:
948 pass
949 continue
950 uris.append(newuri)
951 uds.append(newud)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500952
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500953 adduri(newud, uris, uds, localmirrors, tarballs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500954
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500955 adduri(origud, uris, uds, mirrors, origud.mirrortarballs or [None])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500956
957 return uris, uds
958
959def rename_bad_checksum(ud, suffix):
960 """
961 Renames files to have suffix from parameter
962 """
963
964 if ud.localpath is None:
965 return
966
967 new_localpath = "%s_bad-checksum_%s" % (ud.localpath, suffix)
968 bb.warn("Renaming %s to %s" % (ud.localpath, new_localpath))
969 bb.utils.movefile(ud.localpath, new_localpath)
970
971
972def try_mirror_url(fetch, origud, ud, ld, check = False):
973 # Return of None or a value means we're finished
974 # False means try another url
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500975
976 if ud.lockfile and ud.lockfile != origud.lockfile:
977 lf = bb.utils.lockfile(ud.lockfile)
978
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500979 try:
980 if check:
981 found = ud.method.checkstatus(fetch, ud, ld)
982 if found:
983 return found
984 return False
985
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500986 if not verify_donestamp(ud, ld, origud) or ud.method.need_update(ud, ld):
987 ud.method.download(ud, ld)
988 if hasattr(ud.method,"build_mirror_data"):
989 ud.method.build_mirror_data(ud, ld)
990
991 if not ud.localpath or not os.path.exists(ud.localpath):
992 return False
993
994 if ud.localpath == origud.localpath:
995 return ud.localpath
996
997 # We may be obtaining a mirror tarball which needs further processing by the real fetcher
998 # If that tarball is a local file:// we need to provide a symlink to it
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500999 dldir = ld.getVar("DL_DIR")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001000
1001 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 -05001002 # Create donestamp in old format to avoid triggering a re-download
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001003 if ud.donestamp:
1004 bb.utils.mkdirhier(os.path.dirname(ud.donestamp))
1005 open(ud.donestamp, 'w').close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001006 dest = os.path.join(dldir, os.path.basename(ud.localpath))
1007 if not os.path.exists(dest):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001008 # In case this is executing without any file locks held (as is
1009 # the case for file:// URLs), two tasks may end up here at the
1010 # same time, in which case we do not want the second task to
1011 # fail when the link has already been created by the first task.
1012 try:
1013 os.symlink(ud.localpath, dest)
1014 except FileExistsError:
1015 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001016 if not verify_donestamp(origud, ld) or origud.method.need_update(origud, ld):
1017 origud.method.download(origud, ld)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001018 if hasattr(origud.method, "build_mirror_data"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001019 origud.method.build_mirror_data(origud, ld)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001020 return origud.localpath
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001021 # Otherwise the result is a local file:// and we symlink to it
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001022 ensure_symlink(ud.localpath, origud.localpath)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001023 update_stamp(origud, ld)
1024 return ud.localpath
1025
1026 except bb.fetch2.NetworkAccess:
1027 raise
1028
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001029 except IOError as e:
1030 if e.errno in [os.errno.ESTALE]:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001031 logger.warning("Stale Error Observed %s." % ud.url)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001032 return False
1033 raise
1034
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001035 except bb.fetch2.BBFetchException as e:
1036 if isinstance(e, ChecksumError):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001037 logger.warning("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (ud.url, origud.url))
1038 logger.warning(str(e))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001039 if os.path.exists(ud.localpath):
1040 rename_bad_checksum(ud, e.checksum)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001041 elif isinstance(e, NoChecksumError):
1042 raise
1043 else:
1044 logger.debug(1, "Mirror fetch failure for url %s (original url: %s)" % (ud.url, origud.url))
1045 logger.debug(1, str(e))
1046 try:
1047 ud.method.clean(ud, ld)
1048 except UnboundLocalError:
1049 pass
1050 return False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001051 finally:
1052 if ud.lockfile and ud.lockfile != origud.lockfile:
1053 bb.utils.unlockfile(lf)
1054
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001055
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001056def ensure_symlink(target, link_name):
1057 if not os.path.exists(link_name):
1058 if os.path.islink(link_name):
1059 # Broken symbolic link
1060 os.unlink(link_name)
1061
1062 # In case this is executing without any file locks held (as is
1063 # the case for file:// URLs), two tasks may end up here at the
1064 # same time, in which case we do not want the second task to
1065 # fail when the link has already been created by the first task.
1066 try:
1067 os.symlink(target, link_name)
1068 except FileExistsError:
1069 pass
1070
1071
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001072def try_mirrors(fetch, d, origud, mirrors, check = False):
1073 """
1074 Try to use a mirrored version of the sources.
1075 This method will be automatically called before the fetchers go.
1076
1077 d Is a bb.data instance
1078 uri is the original uri we're trying to download
1079 mirrors is the list of mirrors we're going to try
1080 """
1081 ld = d.createCopy()
1082
1083 uris, uds = build_mirroruris(origud, mirrors, ld)
1084
1085 for index, uri in enumerate(uris):
1086 ret = try_mirror_url(fetch, origud, uds[index], ld, check)
1087 if ret != False:
1088 return ret
1089 return None
1090
1091def trusted_network(d, url):
1092 """
1093 Use a trusted url during download if networking is enabled and
1094 BB_ALLOWED_NETWORKS is set globally or for a specific recipe.
1095 Note: modifies SRC_URI & mirrors.
1096 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001097 if d.getVar('BB_NO_NETWORK') == "1":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001098 return True
1099
1100 pkgname = d.expand(d.getVar('PN', False))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001101 trusted_hosts = None
1102 if pkgname:
1103 trusted_hosts = d.getVarFlag('BB_ALLOWED_NETWORKS', pkgname, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001104
1105 if not trusted_hosts:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001106 trusted_hosts = d.getVar('BB_ALLOWED_NETWORKS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001107
1108 # Not enabled.
1109 if not trusted_hosts:
1110 return True
1111
1112 scheme, network, path, user, passwd, param = decodeurl(url)
1113
1114 if not network:
1115 return True
1116
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001117 network = network.split(':')[0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001118 network = network.lower()
1119
1120 for host in trusted_hosts.split(" "):
1121 host = host.lower()
1122 if host.startswith("*.") and ("." + network).endswith(host[1:]):
1123 return True
1124 if host == network:
1125 return True
1126
1127 return False
1128
1129def srcrev_internal_helper(ud, d, name):
1130 """
1131 Return:
1132 a) a source revision if specified
1133 b) latest revision if SRCREV="AUTOINC"
1134 c) None if not specified
1135 """
1136
1137 srcrev = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001138 pn = d.getVar("PN")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001139 attempts = []
1140 if name != '' and pn:
1141 attempts.append("SRCREV_%s_pn-%s" % (name, pn))
1142 if name != '':
1143 attempts.append("SRCREV_%s" % name)
1144 if pn:
1145 attempts.append("SRCREV_pn-%s" % pn)
1146 attempts.append("SRCREV")
1147
1148 for a in attempts:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001149 srcrev = d.getVar(a)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001150 if srcrev and srcrev != "INVALID":
1151 break
1152
1153 if 'rev' in ud.parm and 'tag' in ud.parm:
1154 raise FetchError("Please specify a ;rev= parameter or a ;tag= parameter in the url %s but not both." % (ud.url))
1155
1156 if 'rev' in ud.parm or 'tag' in ud.parm:
1157 if 'rev' in ud.parm:
1158 parmrev = ud.parm['rev']
1159 else:
1160 parmrev = ud.parm['tag']
1161 if srcrev == "INVALID" or not srcrev:
1162 return parmrev
1163 if srcrev != parmrev:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001164 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 -05001165 return parmrev
1166
1167 if srcrev == "INVALID" or not srcrev:
1168 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)
1169 if srcrev == "AUTOINC":
1170 srcrev = ud.method.latest_revision(ud, d, name)
1171
1172 return srcrev
1173
1174def get_checksum_file_list(d):
1175 """ Get a list of files checksum in SRC_URI
1176
1177 Returns the resolved local paths of all local file entries in
1178 SRC_URI as a space-separated string
1179 """
1180 fetch = Fetch([], d, cache = False, localonly = True)
1181
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001182 dl_dir = d.getVar('DL_DIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001183 filelist = []
1184 for u in fetch.urls:
1185 ud = fetch.ud[u]
1186
1187 if ud and isinstance(ud.method, local.Local):
1188 paths = ud.method.localpaths(ud, d)
1189 for f in paths:
1190 pth = ud.decodedurl
1191 if '*' in pth:
1192 f = os.path.join(os.path.abspath(f), pth)
1193 if f.startswith(dl_dir):
1194 # The local fetcher's behaviour is to return a path under DL_DIR if it couldn't find the file anywhere else
1195 if os.path.exists(f):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001196 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 -05001197 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001198 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 -05001199 filelist.append(f + ":" + str(os.path.exists(f)))
1200
1201 return " ".join(filelist)
1202
1203def get_file_checksums(filelist, pn):
1204 """Get a list of the checksums for a list of local files
1205
1206 Returns the checksums for a list of local files, caching the results as
1207 it proceeds
1208
1209 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001210 return _checksum_cache.get_checksums(filelist, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001211
1212
1213class FetchData(object):
1214 """
1215 A class which represents the fetcher state for a given URI.
1216 """
1217 def __init__(self, url, d, localonly = False):
1218 # localpath is the location of a downloaded result. If not set, the file is local.
1219 self.donestamp = None
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001220 self.needdonestamp = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001221 self.localfile = ""
1222 self.localpath = None
1223 self.lockfile = None
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001224 self.mirrortarballs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001225 self.basename = None
1226 self.basepath = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001227 (self.type, self.host, self.path, self.user, self.pswd, self.parm) = decodeurl(d.expand(url))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001228 self.date = self.getSRCDate(d)
1229 self.url = url
1230 if not self.user and "user" in self.parm:
1231 self.user = self.parm["user"]
1232 if not self.pswd and "pswd" in self.parm:
1233 self.pswd = self.parm["pswd"]
1234 self.setup = False
1235
1236 if "name" in self.parm:
1237 self.md5_name = "%s.md5sum" % self.parm["name"]
1238 self.sha256_name = "%s.sha256sum" % self.parm["name"]
1239 else:
1240 self.md5_name = "md5sum"
1241 self.sha256_name = "sha256sum"
1242 if self.md5_name in self.parm:
1243 self.md5_expected = self.parm[self.md5_name]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001244 elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001245 self.md5_expected = None
1246 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001247 self.md5_expected = d.getVarFlag("SRC_URI", self.md5_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001248 if self.sha256_name in self.parm:
1249 self.sha256_expected = self.parm[self.sha256_name]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001250 elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001251 self.sha256_expected = None
1252 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001253 self.sha256_expected = d.getVarFlag("SRC_URI", self.sha256_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001254 self.ignore_checksums = False
1255
1256 self.names = self.parm.get("name",'default').split(',')
1257
1258 self.method = None
1259 for m in methods:
1260 if m.supports(self, d):
1261 self.method = m
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001262 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001263
1264 if not self.method:
1265 raise NoMethodError(url)
1266
1267 if localonly and not isinstance(self.method, local.Local):
1268 raise NonLocalMethod()
1269
1270 if self.parm.get("proto", None) and "protocol" not in self.parm:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001271 logger.warning('Consider updating %s recipe to use "protocol" not "proto" in SRC_URI.', d.getVar('PN'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001272 self.parm["protocol"] = self.parm.get("proto", None)
1273
1274 if hasattr(self.method, "urldata_init"):
1275 self.method.urldata_init(self, d)
1276
1277 if "localpath" in self.parm:
1278 # if user sets localpath for file, use it instead.
1279 self.localpath = self.parm["localpath"]
1280 self.basename = os.path.basename(self.localpath)
1281 elif self.localfile:
1282 self.localpath = self.method.localpath(self, d)
1283
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001284 dldir = d.getVar("DL_DIR")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001285
1286 if not self.needdonestamp:
1287 return
1288
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001289 # Note: .done and .lock files should always be in DL_DIR whereas localpath may not be.
1290 if self.localpath and self.localpath.startswith(dldir):
1291 basepath = self.localpath
1292 elif self.localpath:
1293 basepath = dldir + os.sep + os.path.basename(self.localpath)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001294 elif self.basepath or self.basename:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001295 basepath = dldir + os.sep + (self.basepath or self.basename)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001296 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001297 bb.fatal("Can't determine lock path for url %s" % url)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001298
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001299 self.donestamp = basepath + '.done'
1300 self.lockfile = basepath + '.lock'
1301
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001302 def setup_revisions(self, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001303 self.revisions = {}
1304 for name in self.names:
1305 self.revisions[name] = srcrev_internal_helper(self, d, name)
1306
1307 # add compatibility code for non name specified case
1308 if len(self.names) == 1:
1309 self.revision = self.revisions[self.names[0]]
1310
1311 def setup_localpath(self, d):
1312 if not self.localpath:
1313 self.localpath = self.method.localpath(self, d)
1314
1315 def getSRCDate(self, d):
1316 """
1317 Return the SRC Date for the component
1318
1319 d the bb.data module
1320 """
1321 if "srcdate" in self.parm:
1322 return self.parm['srcdate']
1323
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001324 pn = d.getVar("PN")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001325
1326 if pn:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001327 return d.getVar("SRCDATE_%s" % pn) or d.getVar("SRCDATE") or d.getVar("DATE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001328
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001329 return d.getVar("SRCDATE") or d.getVar("DATE")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001330
1331class FetchMethod(object):
1332 """Base class for 'fetch'ing data"""
1333
1334 def __init__(self, urls=None):
1335 self.urls = []
1336
1337 def supports(self, urldata, d):
1338 """
1339 Check to see if this fetch class supports a given url.
1340 """
1341 return 0
1342
1343 def localpath(self, urldata, d):
1344 """
1345 Return the local filename of a given url assuming a successful fetch.
1346 Can also setup variables in urldata for use in go (saving code duplication
1347 and duplicate code execution)
1348 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001349 return os.path.join(d.getVar("DL_DIR"), urldata.localfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001350
1351 def supports_checksum(self, urldata):
1352 """
1353 Is localpath something that can be represented by a checksum?
1354 """
1355
1356 # We cannot compute checksums for directories
1357 if os.path.isdir(urldata.localpath) == True:
1358 return False
1359 if urldata.localpath.find("*") != -1:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001360 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001361
1362 return True
1363
1364 def recommends_checksum(self, urldata):
1365 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001366 Is the backend on where checksumming is recommended (should warnings
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001367 be displayed if there is no checksum)?
1368 """
1369 return False
1370
1371 def _strip_leading_slashes(self, relpath):
1372 """
1373 Remove leading slash as os.path.join can't cope
1374 """
1375 while os.path.isabs(relpath):
1376 relpath = relpath[1:]
1377 return relpath
1378
1379 def setUrls(self, urls):
1380 self.__urls = urls
1381
1382 def getUrls(self):
1383 return self.__urls
1384
1385 urls = property(getUrls, setUrls, None, "Urls property")
1386
1387 def need_update(self, ud, d):
1388 """
1389 Force a fetch, even if localpath exists?
1390 """
1391 if os.path.exists(ud.localpath):
1392 return False
1393 return True
1394
1395 def supports_srcrev(self):
1396 """
1397 The fetcher supports auto source revisions (SRCREV)
1398 """
1399 return False
1400
1401 def download(self, urldata, d):
1402 """
1403 Fetch urls
1404 Assumes localpath was called first
1405 """
1406 raise NoMethodError(url)
1407
1408 def unpack(self, urldata, rootdir, data):
1409 iterate = False
1410 file = urldata.localpath
1411
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001412 # Localpath can't deal with 'dir/*' entries, so it converts them to '.',
1413 # but it must be corrected back for local files copying
1414 if urldata.basename == '*' and file.endswith('/.'):
1415 file = '%s/%s' % (file.rstrip('/.'), urldata.path)
1416
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001417 try:
1418 unpack = bb.utils.to_boolean(urldata.parm.get('unpack'), True)
1419 except ValueError as exc:
1420 bb.fatal("Invalid value for 'unpack' parameter for %s: %s" %
1421 (file, urldata.parm.get('unpack')))
1422
1423 base, ext = os.path.splitext(file)
1424 if ext in ['.gz', '.bz2', '.Z', '.xz', '.lz']:
1425 efile = os.path.join(rootdir, os.path.basename(base))
1426 else:
1427 efile = file
1428 cmd = None
1429
1430 if unpack:
1431 if file.endswith('.tar'):
1432 cmd = 'tar x --no-same-owner -f %s' % file
1433 elif file.endswith('.tgz') or file.endswith('.tar.gz') or file.endswith('.tar.Z'):
1434 cmd = 'tar xz --no-same-owner -f %s' % file
1435 elif file.endswith('.tbz') or file.endswith('.tbz2') or file.endswith('.tar.bz2'):
1436 cmd = 'bzip2 -dc %s | tar x --no-same-owner -f -' % file
1437 elif file.endswith('.gz') or file.endswith('.Z') or file.endswith('.z'):
1438 cmd = 'gzip -dc %s > %s' % (file, efile)
1439 elif file.endswith('.bz2'):
1440 cmd = 'bzip2 -dc %s > %s' % (file, efile)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001441 elif file.endswith('.txz') or file.endswith('.tar.xz'):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001442 cmd = 'xz -dc %s | tar x --no-same-owner -f -' % file
1443 elif file.endswith('.xz'):
1444 cmd = 'xz -dc %s > %s' % (file, efile)
1445 elif file.endswith('.tar.lz'):
1446 cmd = 'lzip -dc %s | tar x --no-same-owner -f -' % file
1447 elif file.endswith('.lz'):
1448 cmd = 'lzip -dc %s > %s' % (file, efile)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001449 elif file.endswith('.tar.7z'):
1450 cmd = '7z x -so %s | tar x --no-same-owner -f -' % file
1451 elif file.endswith('.7z'):
1452 cmd = '7za x -y %s 1>/dev/null' % file
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001453 elif file.endswith('.zip') or file.endswith('.jar'):
1454 try:
1455 dos = bb.utils.to_boolean(urldata.parm.get('dos'), False)
1456 except ValueError as exc:
1457 bb.fatal("Invalid value for 'dos' parameter for %s: %s" %
1458 (file, urldata.parm.get('dos')))
1459 cmd = 'unzip -q -o'
1460 if dos:
1461 cmd = '%s -a' % cmd
1462 cmd = "%s '%s'" % (cmd, file)
1463 elif file.endswith('.rpm') or file.endswith('.srpm'):
1464 if 'extract' in urldata.parm:
1465 unpack_file = urldata.parm.get('extract')
1466 cmd = 'rpm2cpio.sh %s | cpio -id %s' % (file, unpack_file)
1467 iterate = True
1468 iterate_file = unpack_file
1469 else:
1470 cmd = 'rpm2cpio.sh %s | cpio -id' % (file)
1471 elif file.endswith('.deb') or file.endswith('.ipk'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001472 output = subprocess.check_output('ar -t %s' % file, preexec_fn=subprocess_setup, shell=True)
1473 datafile = None
1474 if output:
1475 for line in output.decode().splitlines():
1476 if line.startswith('data.tar.'):
1477 datafile = line
1478 break
1479 else:
1480 raise UnpackError("Unable to unpack deb/ipk package - does not contain data.tar.* file", urldata.url)
1481 else:
1482 raise UnpackError("Unable to unpack deb/ipk package - could not list contents", urldata.url)
1483 cmd = 'ar x %s %s && tar --no-same-owner -xpf %s && rm %s' % (file, datafile, datafile, datafile)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001484
1485 # If 'subdir' param exists, create a dir and use it as destination for unpack cmd
1486 if 'subdir' in urldata.parm:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001487 subdir = urldata.parm.get('subdir')
1488 if os.path.isabs(subdir):
1489 if not os.path.realpath(subdir).startswith(os.path.realpath(rootdir)):
1490 raise UnpackError("subdir argument isn't a subdirectory of unpack root %s" % rootdir, urldata.url)
1491 unpackdir = subdir
1492 else:
1493 unpackdir = os.path.join(rootdir, subdir)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001494 bb.utils.mkdirhier(unpackdir)
1495 else:
1496 unpackdir = rootdir
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001497
1498 if not unpack or not cmd:
1499 # If file == dest, then avoid any copies, as we already put the file into dest!
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001500 dest = os.path.join(unpackdir, os.path.basename(file))
1501 if file != dest and not (os.path.exists(dest) and os.path.samefile(file, dest)):
1502 destdir = '.'
1503 # For file:// entries all intermediate dirs in path must be created at destination
1504 if urldata.type == "file":
1505 # Trailing '/' does a copying to wrong place
1506 urlpath = urldata.path.rstrip('/')
1507 # Want files places relative to cwd so no leading '/'
1508 urlpath = urlpath.lstrip('/')
1509 if urlpath.find("/") != -1:
1510 destdir = urlpath.rsplit("/", 1)[0] + '/'
1511 bb.utils.mkdirhier("%s/%s" % (unpackdir, destdir))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001512 cmd = 'cp -fpPRH %s %s' % (file, destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001513
1514 if not cmd:
1515 return
1516
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001517 path = data.getVar('PATH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001518 if path:
1519 cmd = "PATH=\"%s\" %s" % (path, cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001520 bb.note("Unpacking %s to %s/" % (file, unpackdir))
1521 ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True, cwd=unpackdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001522
1523 if ret != 0:
1524 raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), urldata.url)
1525
1526 if iterate is True:
1527 iterate_urldata = urldata
1528 iterate_urldata.localpath = "%s/%s" % (rootdir, iterate_file)
1529 self.unpack(urldata, rootdir, data)
1530
1531 return
1532
1533 def clean(self, urldata, d):
1534 """
1535 Clean any existing full or partial download
1536 """
1537 bb.utils.remove(urldata.localpath)
1538
1539 def try_premirror(self, urldata, d):
1540 """
1541 Should premirrors be used?
1542 """
1543 return True
1544
1545 def checkstatus(self, fetch, urldata, d):
1546 """
1547 Check the status of a URL
1548 Assumes localpath was called first
1549 """
1550 logger.info("URL %s could not be checked for status since no method exists.", url)
1551 return True
1552
1553 def latest_revision(self, ud, d, name):
1554 """
1555 Look in the cache for the latest revision, if not present ask the SCM.
1556 """
1557 if not hasattr(self, "_latest_revision"):
1558 raise ParameterError("The fetcher for this URL does not support _latest_revision", url)
1559
1560 revs = bb.persist_data.persist('BB_URI_HEADREVS', d)
1561 key = self.generate_revision_key(ud, d, name)
1562 try:
1563 return revs[key]
1564 except KeyError:
1565 revs[key] = rev = self._latest_revision(ud, d, name)
1566 return rev
1567
1568 def sortable_revision(self, ud, d, name):
1569 latest_rev = self._build_revision(ud, d, name)
1570 return True, str(latest_rev)
1571
1572 def generate_revision_key(self, ud, d, name):
1573 key = self._revision_key(ud, d, name)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001574 return "%s-%s" % (key, d.getVar("PN") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001575
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001576 def latest_versionstring(self, ud, d):
1577 """
1578 Compute the latest release name like "x.y.x" in "x.y.x+gitHASH"
1579 by searching through the tags output of ls-remote, comparing
1580 versions and returning the highest match as a (version, revision) pair.
1581 """
1582 return ('', '')
1583
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001584class Fetch(object):
1585 def __init__(self, urls, d, cache = True, localonly = False, connection_cache = None):
1586 if localonly and cache:
1587 raise Exception("bb.fetch2.Fetch.__init__: cannot set cache and localonly at same time")
1588
1589 if len(urls) == 0:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001590 urls = d.getVar("SRC_URI").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001591 self.urls = urls
1592 self.d = d
1593 self.ud = {}
1594 self.connection_cache = connection_cache
1595
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001596 fn = d.getVar('FILE')
1597 mc = d.getVar('__BBMULTICONFIG') or ""
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001598 if cache and fn and mc + fn in urldata_cache:
1599 self.ud = urldata_cache[mc + fn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001600
1601 for url in urls:
1602 if url not in self.ud:
1603 try:
1604 self.ud[url] = FetchData(url, d, localonly)
1605 except NonLocalMethod:
1606 if localonly:
1607 self.ud[url] = None
1608 pass
1609
1610 if fn and cache:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001611 urldata_cache[mc + fn] = self.ud
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001612
1613 def localpath(self, url):
1614 if url not in self.urls:
1615 self.ud[url] = FetchData(url, self.d)
1616
1617 self.ud[url].setup_localpath(self.d)
1618 return self.d.expand(self.ud[url].localpath)
1619
1620 def localpaths(self):
1621 """
1622 Return a list of the local filenames, assuming successful fetch
1623 """
1624 local = []
1625
1626 for u in self.urls:
1627 ud = self.ud[u]
1628 ud.setup_localpath(self.d)
1629 local.append(ud.localpath)
1630
1631 return local
1632
1633 def download(self, urls=None):
1634 """
1635 Fetch all urls
1636 """
1637 if not urls:
1638 urls = self.urls
1639
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001640 network = self.d.getVar("BB_NO_NETWORK")
1641 premirroronly = (self.d.getVar("BB_FETCH_PREMIRRORONLY") == "1")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001642
1643 for u in urls:
1644 ud = self.ud[u]
1645 ud.setup_localpath(self.d)
1646 m = ud.method
1647 localpath = ""
1648
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001649 if ud.lockfile:
1650 lf = bb.utils.lockfile(ud.lockfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001651
1652 try:
1653 self.d.setVar("BB_NO_NETWORK", network)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001654
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001655 if verify_donestamp(ud, self.d) and not m.need_update(ud, self.d):
1656 localpath = ud.localpath
1657 elif m.try_premirror(ud, self.d):
1658 logger.debug(1, "Trying PREMIRRORS")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001659 mirrors = mirror_from_string(self.d.getVar('PREMIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001660 localpath = try_mirrors(self, self.d, ud, mirrors, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001661 if localpath:
1662 try:
1663 # early checksum verification so that if the checksum of the premirror
1664 # contents mismatch the fetcher can still try upstream and mirrors
1665 update_stamp(ud, self.d)
1666 except ChecksumError as e:
1667 logger.warning("Checksum failure encountered with premirror download of %s - will attempt other sources." % u)
1668 logger.debug(1, str(e))
1669 localpath = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001670
1671 if premirroronly:
1672 self.d.setVar("BB_NO_NETWORK", "1")
1673
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001674 firsterr = None
1675 verified_stamp = verify_donestamp(ud, self.d)
1676 if not localpath and (not verified_stamp or m.need_update(ud, self.d)):
1677 try:
1678 if not trusted_network(self.d, ud.url):
1679 raise UntrustedUrl(ud.url)
1680 logger.debug(1, "Trying Upstream")
1681 m.download(ud, self.d)
1682 if hasattr(m, "build_mirror_data"):
1683 m.build_mirror_data(ud, self.d)
1684 localpath = ud.localpath
1685 # early checksum verify, so that if checksum mismatched,
1686 # fetcher still have chance to fetch from mirror
1687 update_stamp(ud, self.d)
1688
1689 except bb.fetch2.NetworkAccess:
1690 raise
1691
1692 except BBFetchException as e:
1693 if isinstance(e, ChecksumError):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001694 logger.warning("Checksum failure encountered with download of %s - will attempt other sources if available" % u)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001695 logger.debug(1, str(e))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001696 if os.path.exists(ud.localpath):
1697 rename_bad_checksum(ud, e.checksum)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001698 elif isinstance(e, NoChecksumError):
1699 raise
1700 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001701 logger.warning('Failed to fetch URL %s, attempting MIRRORS if available' % u)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001702 logger.debug(1, str(e))
1703 firsterr = e
1704 # Remove any incomplete fetch
1705 if not verified_stamp:
1706 m.clean(ud, self.d)
1707 logger.debug(1, "Trying MIRRORS")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001708 mirrors = mirror_from_string(self.d.getVar('MIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001709 localpath = try_mirrors(self, self.d, ud, mirrors)
1710
1711 if not localpath or ((not os.path.exists(localpath)) and localpath.find("*") == -1):
1712 if firsterr:
1713 logger.error(str(firsterr))
1714 raise FetchError("Unable to fetch URL from any source.", u)
1715
1716 update_stamp(ud, self.d)
1717
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001718 except IOError as e:
1719 if e.errno in [os.errno.ESTALE]:
1720 logger.error("Stale Error Observed %s." % u)
1721 raise ChecksumError("Stale Error Detected")
1722
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001723 except BBFetchException as e:
1724 if isinstance(e, ChecksumError):
1725 logger.error("Checksum failure fetching %s" % u)
1726 raise
1727
1728 finally:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001729 if ud.lockfile:
1730 bb.utils.unlockfile(lf)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001731
1732 def checkstatus(self, urls=None):
1733 """
1734 Check all urls exist upstream
1735 """
1736
1737 if not urls:
1738 urls = self.urls
1739
1740 for u in urls:
1741 ud = self.ud[u]
1742 ud.setup_localpath(self.d)
1743 m = ud.method
1744 logger.debug(1, "Testing URL %s", u)
1745 # First try checking uri, u, from PREMIRRORS
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001746 mirrors = mirror_from_string(self.d.getVar('PREMIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001747 ret = try_mirrors(self, self.d, ud, mirrors, True)
1748 if not ret:
1749 # Next try checking from the original uri, u
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001750 ret = m.checkstatus(self, ud, self.d)
1751 if not ret:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001752 # Finally, try checking uri, u, from MIRRORS
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001753 mirrors = mirror_from_string(self.d.getVar('MIRRORS'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001754 ret = try_mirrors(self, self.d, ud, mirrors, True)
1755
1756 if not ret:
1757 raise FetchError("URL %s doesn't work" % u, u)
1758
1759 def unpack(self, root, urls=None):
1760 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001761 Unpack urls to root
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001762 """
1763
1764 if not urls:
1765 urls = self.urls
1766
1767 for u in urls:
1768 ud = self.ud[u]
1769 ud.setup_localpath(self.d)
1770
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001771 if ud.lockfile:
1772 lf = bb.utils.lockfile(ud.lockfile)
1773
1774 ud.method.unpack(ud, root, self.d)
1775
1776 if ud.lockfile:
1777 bb.utils.unlockfile(lf)
1778
1779 def clean(self, urls=None):
1780 """
1781 Clean files that the fetcher gets or places
1782 """
1783
1784 if not urls:
1785 urls = self.urls
1786
1787 for url in urls:
1788 if url not in self.ud:
1789 self.ud[url] = FetchData(url, d)
1790 ud = self.ud[url]
1791 ud.setup_localpath(self.d)
1792
1793 if not ud.localfile and ud.localpath is None:
1794 continue
1795
1796 if ud.lockfile:
1797 lf = bb.utils.lockfile(ud.lockfile)
1798
1799 ud.method.clean(ud, self.d)
1800 if ud.donestamp:
1801 bb.utils.remove(ud.donestamp)
1802
1803 if ud.lockfile:
1804 bb.utils.unlockfile(lf)
1805
1806class FetchConnectionCache(object):
1807 """
1808 A class which represents an container for socket connections.
1809 """
1810 def __init__(self):
1811 self.cache = {}
1812
1813 def get_connection_name(self, host, port):
1814 return host + ':' + str(port)
1815
1816 def add_connection(self, host, port, connection):
1817 cn = self.get_connection_name(host, port)
1818
1819 if cn not in self.cache:
1820 self.cache[cn] = connection
1821
1822 def get_connection(self, host, port):
1823 connection = None
1824
1825 cn = self.get_connection_name(host, port)
1826 if cn in self.cache:
1827 connection = self.cache[cn]
1828
1829 return connection
1830
1831 def remove_connection(self, host, port):
1832 cn = self.get_connection_name(host, port)
1833 if cn in self.cache:
1834 self.cache[cn].close()
1835 del self.cache[cn]
1836
1837 def close_connections(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001838 for cn in list(self.cache.keys()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001839 self.cache[cn].close()
1840 del self.cache[cn]
1841
1842from . import cvs
1843from . import git
1844from . import gitsm
1845from . import gitannex
1846from . import local
1847from . import svn
1848from . import wget
1849from . import ssh
1850from . import sftp
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001851from . import s3
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001852from . import perforce
1853from . import bzr
1854from . import hg
1855from . import osc
1856from . import repo
1857from . import clearcase
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001858from . import npm
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001859
1860methods.append(local.Local())
1861methods.append(wget.Wget())
1862methods.append(svn.Svn())
1863methods.append(git.Git())
1864methods.append(gitsm.GitSM())
1865methods.append(gitannex.GitANNEX())
1866methods.append(cvs.Cvs())
1867methods.append(ssh.SSH())
1868methods.append(sftp.SFTP())
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001869methods.append(s3.S3())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001870methods.append(perforce.Perforce())
1871methods.append(bzr.Bzr())
1872methods.append(hg.Hg())
1873methods.append(osc.Osc())
1874methods.append(repo.Repo())
1875methods.append(clearcase.ClearCase())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001876methods.append(npm.Npm())