blob: 1fa67020c498fc59435709d0a242b2adf6a395ad [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
28from __future__ import absolute_import
29from __future__ import print_function
30import os, re
31import signal
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032import logging
33import urllib
34import urlparse
Patrick Williamsc124f4f2015-09-15 14:41:29 -050035import bb.persist_data, bb.utils
36import bb.checksum
37from bb import data
38import bb.process
39import subprocess
40
41__version__ = "2"
42_checksum_cache = bb.checksum.FileChecksumCache()
43
44logger = logging.getLogger("BitBake.Fetcher")
45
46try:
47 import cPickle as pickle
48except ImportError:
49 import pickle
50 logger.info("Importing cPickle failed. "
51 "Falling back to a very slow implementation.")
52
53class BBFetchException(Exception):
54 """Class all fetch exceptions inherit from"""
55 def __init__(self, message):
56 self.msg = message
57 Exception.__init__(self, message)
58
59 def __str__(self):
60 return self.msg
61
62class UntrustedUrl(BBFetchException):
63 """Exception raised when encountering a host not listed in BB_ALLOWED_NETWORKS"""
64 def __init__(self, url, message=''):
65 if message:
66 msg = message
67 else:
68 msg = "The URL: '%s' is not trusted and cannot be used" % url
69 self.url = url
70 BBFetchException.__init__(self, msg)
71 self.args = (url,)
72
73class MalformedUrl(BBFetchException):
74 """Exception raised when encountering an invalid url"""
75 def __init__(self, url, message=''):
76 if message:
77 msg = message
78 else:
79 msg = "The URL: '%s' is invalid and cannot be interpreted" % url
80 self.url = url
81 BBFetchException.__init__(self, msg)
82 self.args = (url,)
83
84class FetchError(BBFetchException):
85 """General fetcher exception when something happens incorrectly"""
86 def __init__(self, message, url = None):
87 if url:
88 msg = "Fetcher failure for URL: '%s'. %s" % (url, message)
89 else:
90 msg = "Fetcher failure: %s" % message
91 self.url = url
92 BBFetchException.__init__(self, msg)
93 self.args = (message, url)
94
95class ChecksumError(FetchError):
96 """Exception when mismatched checksum encountered"""
97 def __init__(self, message, url = None, checksum = None):
98 self.checksum = checksum
99 FetchError.__init__(self, message, url)
100
101class NoChecksumError(FetchError):
102 """Exception when no checksum is specified, but BB_STRICT_CHECKSUM is set"""
103
104class UnpackError(BBFetchException):
105 """General fetcher exception when something happens incorrectly when unpacking"""
106 def __init__(self, message, url):
107 msg = "Unpack failure for URL: '%s'. %s" % (url, message)
108 self.url = url
109 BBFetchException.__init__(self, msg)
110 self.args = (message, url)
111
112class NoMethodError(BBFetchException):
113 """Exception raised when there is no method to obtain a supplied url or set of urls"""
114 def __init__(self, url):
115 msg = "Could not find a fetcher which supports the URL: '%s'" % url
116 self.url = url
117 BBFetchException.__init__(self, msg)
118 self.args = (url,)
119
120class MissingParameterError(BBFetchException):
121 """Exception raised when a fetch method is missing a critical parameter in the url"""
122 def __init__(self, missing, url):
123 msg = "URL: '%s' is missing the required parameter '%s'" % (url, missing)
124 self.url = url
125 self.missing = missing
126 BBFetchException.__init__(self, msg)
127 self.args = (missing, url)
128
129class ParameterError(BBFetchException):
130 """Exception raised when a url cannot be proccessed due to invalid parameters."""
131 def __init__(self, message, url):
132 msg = "URL: '%s' has invalid parameters. %s" % (url, message)
133 self.url = url
134 BBFetchException.__init__(self, msg)
135 self.args = (message, url)
136
137class NetworkAccess(BBFetchException):
138 """Exception raised when network access is disabled but it is required."""
139 def __init__(self, url, cmd):
140 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)
141 self.url = url
142 self.cmd = cmd
143 BBFetchException.__init__(self, msg)
144 self.args = (url, cmd)
145
146class NonLocalMethod(Exception):
147 def __init__(self):
148 Exception.__init__(self)
149
150
151class URI(object):
152 """
153 A class representing a generic URI, with methods for
154 accessing the URI components, and stringifies to the
155 URI.
156
157 It is constructed by calling it with a URI, or setting
158 the attributes manually:
159
160 uri = URI("http://example.com/")
161
162 uri = URI()
163 uri.scheme = 'http'
164 uri.hostname = 'example.com'
165 uri.path = '/'
166
167 It has the following attributes:
168
169 * scheme (read/write)
170 * userinfo (authentication information) (read/write)
171 * username (read/write)
172 * password (read/write)
173
174 Note, password is deprecated as of RFC 3986.
175
176 * hostname (read/write)
177 * port (read/write)
178 * hostport (read only)
179 "hostname:port", if both are set, otherwise just "hostname"
180 * path (read/write)
181 * path_quoted (read/write)
182 A URI quoted version of path
183 * params (dict) (read/write)
184 * query (dict) (read/write)
185 * relative (bool) (read only)
186 True if this is a "relative URI", (e.g. file:foo.diff)
187
188 It stringifies to the URI itself.
189
190 Some notes about relative URIs: while it's specified that
191 a URI beginning with <scheme>:// should either be directly
192 followed by a hostname or a /, the old URI handling of the
193 fetch2 library did not comform to this. Therefore, this URI
194 class has some kludges to make sure that URIs are parsed in
195 a way comforming to bitbake's current usage. This URI class
196 supports the following:
197
198 file:relative/path.diff (IETF compliant)
199 git:relative/path.git (IETF compliant)
200 git:///absolute/path.git (IETF compliant)
201 file:///absolute/path.diff (IETF compliant)
202
203 file://relative/path.diff (not IETF compliant)
204
205 But it does not support the following:
206
207 file://hostname/absolute/path.diff (would be IETF compliant)
208
209 Note that the last case only applies to a list of
210 "whitelisted" schemes (currently only file://), that requires
211 its URIs to not have a network location.
212 """
213
214 _relative_schemes = ['file', 'git']
215 _netloc_forbidden = ['file']
216
217 def __init__(self, uri=None):
218 self.scheme = ''
219 self.userinfo = ''
220 self.hostname = ''
221 self.port = None
222 self._path = ''
223 self.params = {}
224 self.query = {}
225 self.relative = False
226
227 if not uri:
228 return
229
230 # We hijack the URL parameters, since the way bitbake uses
231 # them are not quite RFC compliant.
232 uri, param_str = (uri.split(";", 1) + [None])[:2]
233
234 urlp = urlparse.urlparse(uri)
235 self.scheme = urlp.scheme
236
237 reparse = 0
238
239 # Coerce urlparse to make URI scheme use netloc
240 if not self.scheme in urlparse.uses_netloc:
241 urlparse.uses_params.append(self.scheme)
242 reparse = 1
243
244 # Make urlparse happy(/ier) by converting local resources
245 # to RFC compliant URL format. E.g.:
246 # file://foo.diff -> file:foo.diff
247 if urlp.scheme in self._netloc_forbidden:
248 uri = re.sub("(?<=:)//(?!/)", "", uri, 1)
249 reparse = 1
250
251 if reparse:
252 urlp = urlparse.urlparse(uri)
253
254 # Identify if the URI is relative or not
255 if urlp.scheme in self._relative_schemes and \
256 re.compile("^\w+:(?!//)").match(uri):
257 self.relative = True
258
259 if not self.relative:
260 self.hostname = urlp.hostname or ''
261 self.port = urlp.port
262
263 self.userinfo += urlp.username or ''
264
265 if urlp.password:
266 self.userinfo += ':%s' % urlp.password
267
268 self.path = urllib.unquote(urlp.path)
269
270 if param_str:
271 self.params = self._param_str_split(param_str, ";")
272 if urlp.query:
273 self.query = self._param_str_split(urlp.query, "&")
274
275 def __str__(self):
276 userinfo = self.userinfo
277 if userinfo:
278 userinfo += '@'
279
280 return "%s:%s%s%s%s%s%s" % (
281 self.scheme,
282 '' if self.relative else '//',
283 userinfo,
284 self.hostport,
285 self.path_quoted,
286 self._query_str(),
287 self._param_str())
288
289 def _param_str(self):
290 return (
291 ''.join([';', self._param_str_join(self.params, ";")])
292 if self.params else '')
293
294 def _query_str(self):
295 return (
296 ''.join(['?', self._param_str_join(self.query, "&")])
297 if self.query else '')
298
299 def _param_str_split(self, string, elmdelim, kvdelim="="):
300 ret = {}
301 for k, v in [x.split(kvdelim, 1) for x in string.split(elmdelim)]:
302 ret[k] = v
303 return ret
304
305 def _param_str_join(self, dict_, elmdelim, kvdelim="="):
306 return elmdelim.join([kvdelim.join([k, v]) for k, v in dict_.items()])
307
308 @property
309 def hostport(self):
310 if not self.port:
311 return self.hostname
312 return "%s:%d" % (self.hostname, self.port)
313
314 @property
315 def path_quoted(self):
316 return urllib.quote(self.path)
317
318 @path_quoted.setter
319 def path_quoted(self, path):
320 self.path = urllib.unquote(path)
321
322 @property
323 def path(self):
324 return self._path
325
326 @path.setter
327 def path(self, path):
328 self._path = path
329
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500330 if not path or re.compile("^/").match(path):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500331 self.relative = False
332 else:
333 self.relative = True
334
335 @property
336 def username(self):
337 if self.userinfo:
338 return (self.userinfo.split(":", 1))[0]
339 return ''
340
341 @username.setter
342 def username(self, username):
343 password = self.password
344 self.userinfo = username
345 if password:
346 self.userinfo += ":%s" % password
347
348 @property
349 def password(self):
350 if self.userinfo and ":" in self.userinfo:
351 return (self.userinfo.split(":", 1))[1]
352 return ''
353
354 @password.setter
355 def password(self, password):
356 self.userinfo = "%s:%s" % (self.username, password)
357
358def decodeurl(url):
359 """Decodes an URL into the tokens (scheme, network location, path,
360 user, password, parameters).
361 """
362
363 m = re.compile('(?P<type>[^:]*)://((?P<user>[^/]+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
364 if not m:
365 raise MalformedUrl(url)
366
367 type = m.group('type')
368 location = m.group('location')
369 if not location:
370 raise MalformedUrl(url)
371 user = m.group('user')
372 parm = m.group('parm')
373
374 locidx = location.find('/')
375 if locidx != -1 and type.lower() != 'file':
376 host = location[:locidx]
377 path = location[locidx:]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500378 elif type.lower() == 'file':
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500379 host = ""
380 path = location
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500381 else:
382 host = location
383 path = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500384 if user:
385 m = re.compile('(?P<user>[^:]+)(:?(?P<pswd>.*))').match(user)
386 if m:
387 user = m.group('user')
388 pswd = m.group('pswd')
389 else:
390 user = ''
391 pswd = ''
392
393 p = {}
394 if parm:
395 for s in parm.split(';'):
396 if s:
397 if not '=' in s:
398 raise MalformedUrl(url, "The URL: '%s' is invalid: parameter %s does not specify a value (missing '=')" % (url, s))
399 s1, s2 = s.split('=')
400 p[s1] = s2
401
402 return type, host, urllib.unquote(path), user, pswd, p
403
404def encodeurl(decoded):
405 """Encodes a URL from tokens (scheme, network location, path,
406 user, password, parameters).
407 """
408
409 type, host, path, user, pswd, p = decoded
410
411 if not path:
412 raise MissingParameterError('path', "encoded from the data %s" % str(decoded))
413 if not type:
414 raise MissingParameterError('type', "encoded from the data %s" % str(decoded))
415 url = '%s://' % type
416 if user and type != "file":
417 url += "%s" % user
418 if pswd:
419 url += ":%s" % pswd
420 url += "@"
421 if host and type != "file":
422 url += "%s" % host
423 # Standardise path to ensure comparisons work
424 while '//' in path:
425 path = path.replace("//", "/")
426 url += "%s" % urllib.quote(path)
427 if p:
428 for parm in p:
429 url += ";%s=%s" % (parm, p[parm])
430
431 return url
432
433def uri_replace(ud, uri_find, uri_replace, replacements, d):
434 if not ud.url or not uri_find or not uri_replace:
435 logger.error("uri_replace: passed an undefined value, not replacing")
436 return None
437 uri_decoded = list(decodeurl(ud.url))
438 uri_find_decoded = list(decodeurl(uri_find))
439 uri_replace_decoded = list(decodeurl(uri_replace))
440 logger.debug(2, "For url %s comparing %s to %s" % (uri_decoded, uri_find_decoded, uri_replace_decoded))
441 result_decoded = ['', '', '', '', '', {}]
442 for loc, i in enumerate(uri_find_decoded):
443 result_decoded[loc] = uri_decoded[loc]
444 regexp = i
445 if loc == 0 and regexp and not regexp.endswith("$"):
446 # Leaving the type unanchored can mean "https" matching "file" can become "files"
447 # which is clearly undesirable.
448 regexp += "$"
449 if loc == 5:
450 # Handle URL parameters
451 if i:
452 # Any specified URL parameters must match
453 for k in uri_replace_decoded[loc]:
454 if uri_decoded[loc][k] != uri_replace_decoded[loc][k]:
455 return None
456 # Overwrite any specified replacement parameters
457 for k in uri_replace_decoded[loc]:
458 for l in replacements:
459 uri_replace_decoded[loc][k] = uri_replace_decoded[loc][k].replace(l, replacements[l])
460 result_decoded[loc][k] = uri_replace_decoded[loc][k]
461 elif (re.match(regexp, uri_decoded[loc])):
462 if not uri_replace_decoded[loc]:
463 result_decoded[loc] = ""
464 else:
465 for k in replacements:
466 uri_replace_decoded[loc] = uri_replace_decoded[loc].replace(k, replacements[k])
467 #bb.note("%s %s %s" % (regexp, uri_replace_decoded[loc], uri_decoded[loc]))
Patrick Williamsd7e96312015-09-22 08:09:05 -0500468 result_decoded[loc] = re.sub(regexp, uri_replace_decoded[loc], uri_decoded[loc], 1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500469 if loc == 2:
470 # Handle path manipulations
471 basename = None
472 if uri_decoded[0] != uri_replace_decoded[0] and ud.mirrortarball:
473 # If the source and destination url types differ, must be a mirrortarball mapping
474 basename = os.path.basename(ud.mirrortarball)
475 # Kill parameters, they make no sense for mirror tarballs
476 uri_decoded[5] = {}
477 elif ud.localpath and ud.method.supports_checksum(ud):
478 basename = os.path.basename(ud.localpath)
479 if basename and not result_decoded[loc].endswith(basename):
480 result_decoded[loc] = os.path.join(result_decoded[loc], basename)
481 else:
482 return None
483 result = encodeurl(result_decoded)
484 if result == ud.url:
485 return None
486 logger.debug(2, "For url %s returning %s" % (ud.url, result))
487 return result
488
489methods = []
490urldata_cache = {}
491saved_headrevs = {}
492
493def fetcher_init(d):
494 """
495 Called to initialize the fetchers once the configuration data is known.
496 Calls before this must not hit the cache.
497 """
498 # When to drop SCM head revisions controlled by user policy
499 srcrev_policy = d.getVar('BB_SRCREV_POLICY', True) or "clear"
500 if srcrev_policy == "cache":
501 logger.debug(1, "Keeping SRCREV cache due to cache policy of: %s", srcrev_policy)
502 elif srcrev_policy == "clear":
503 logger.debug(1, "Clearing SRCREV cache due to cache policy of: %s", srcrev_policy)
504 revs = bb.persist_data.persist('BB_URI_HEADREVS', d)
505 try:
506 bb.fetch2.saved_headrevs = revs.items()
507 except:
508 pass
509 revs.clear()
510 else:
511 raise FetchError("Invalid SRCREV cache policy of: %s" % srcrev_policy)
512
513 _checksum_cache.init_cache(d)
514
515 for m in methods:
516 if hasattr(m, "init"):
517 m.init(d)
518
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500519def fetcher_parse_save():
520 _checksum_cache.save_extras()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500521
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500522def fetcher_parse_done():
523 _checksum_cache.save_merge()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500524
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500525def fetcher_compare_revisions():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500526 """
527 Compare the revisions in the persistant cache with current values and
528 return true/false on whether they've changed.
529 """
530
531 data = bb.persist_data.persist('BB_URI_HEADREVS', d).items()
532 data2 = bb.fetch2.saved_headrevs
533
534 changed = False
535 for key in data:
536 if key not in data2 or data2[key] != data[key]:
537 logger.debug(1, "%s changed", key)
538 changed = True
539 return True
540 else:
541 logger.debug(2, "%s did not change", key)
542 return False
543
544def mirror_from_string(data):
545 return [ i.split() for i in (data or "").replace('\\n','\n').split('\n') if i ]
546
547def verify_checksum(ud, d, precomputed={}):
548 """
549 verify the MD5 and SHA256 checksum for downloaded src
550
551 Raises a FetchError if one or both of the SRC_URI checksums do not match
552 the downloaded file, or if BB_STRICT_CHECKSUM is set and there are no
553 checksums specified.
554
555 Returns a dict of checksums that can be stored in a done stamp file and
556 passed in as precomputed parameter in a later call to avoid re-computing
557 the checksums from the file. This allows verifying the checksums of the
558 file against those in the recipe each time, rather than only after
559 downloading. See https://bugzilla.yoctoproject.org/show_bug.cgi?id=5571.
560 """
561
562 _MD5_KEY = "md5"
563 _SHA256_KEY = "sha256"
564
565 if ud.ignore_checksums or not ud.method.supports_checksum(ud):
566 return {}
567
568 if _MD5_KEY in precomputed:
569 md5data = precomputed[_MD5_KEY]
570 else:
571 md5data = bb.utils.md5_file(ud.localpath)
572
573 if _SHA256_KEY in precomputed:
574 sha256data = precomputed[_SHA256_KEY]
575 else:
576 sha256data = bb.utils.sha256_file(ud.localpath)
577
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500578 if ud.method.recommends_checksum(ud) and not ud.md5_expected and not ud.sha256_expected:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500579 # If strict checking enabled and neither sum defined, raise error
580 strict = d.getVar("BB_STRICT_CHECKSUM", True) or "0"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500581 if strict == "1":
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500582 logger.error('No checksum specified for %s, please add at least one to the recipe:\n'
583 'SRC_URI[%s] = "%s"\nSRC_URI[%s] = "%s"' %
584 (ud.localpath, ud.md5_name, md5data,
585 ud.sha256_name, sha256data))
586 raise NoChecksumError('Missing SRC_URI checksum', ud.url)
587
588 # Log missing sums so user can more easily add them
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500589 logger.warn('Missing md5 SRC_URI checksum for %s, consider adding to the recipe:\n'
590 'SRC_URI[%s] = "%s"',
591 ud.localpath, ud.md5_name, md5data)
592 logger.warn('Missing sha256 SRC_URI checksum for %s, consider adding to the recipe:\n'
593 'SRC_URI[%s] = "%s"',
594 ud.localpath, ud.sha256_name, sha256data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500595
596 # We want to alert the user if a checksum is defined in the recipe but
597 # it does not match.
598 msg = ""
599 mismatch = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500600 if ud.md5_expected and ud.md5_expected != md5data:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500601 msg = msg + "\nFile: '%s' has %s checksum %s when %s was expected" % (ud.localpath, 'md5', md5data, ud.md5_expected)
602 mismatch = True;
603
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500604 if ud.sha256_expected and ud.sha256_expected != sha256data:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500605 msg = msg + "\nFile: '%s' has %s checksum %s when %s was expected" % (ud.localpath, 'sha256', sha256data, ud.sha256_expected)
606 mismatch = True;
607
608 if mismatch:
609 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)
610
611 if len(msg):
612 raise ChecksumError('Checksum mismatch!%s' % msg, ud.url, md5data)
613
614 return {
615 _MD5_KEY: md5data,
616 _SHA256_KEY: sha256data
617 }
618
619
620def verify_donestamp(ud, d, origud=None):
621 """
622 Check whether the done stamp file has the right checksums (if the fetch
623 method supports them). If it doesn't, delete the done stamp and force
624 a re-download.
625
626 Returns True, if the donestamp exists and is valid, False otherwise. When
627 returning False, any existing done stamps are removed.
628 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500629 if not ud.needdonestamp:
630 return True
631
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500632 if not os.path.exists(ud.donestamp):
633 return False
634
635 if (not ud.method.supports_checksum(ud) or
636 (origud and not origud.method.supports_checksum(origud))):
637 # done stamp exists, checksums not supported; assume the local file is
638 # current
639 return True
640
641 if not os.path.exists(ud.localpath):
642 # done stamp exists, but the downloaded file does not; the done stamp
643 # must be incorrect, re-trigger the download
644 bb.utils.remove(ud.donestamp)
645 return False
646
647 precomputed_checksums = {}
648 # Only re-use the precomputed checksums if the donestamp is newer than the
649 # file. Do not rely on the mtime of directories, though. If ud.localpath is
650 # a directory, there will probably not be any checksums anyway.
651 if (os.path.isdir(ud.localpath) or
652 os.path.getmtime(ud.localpath) < os.path.getmtime(ud.donestamp)):
653 try:
654 with open(ud.donestamp, "rb") as cachefile:
655 pickled = pickle.Unpickler(cachefile)
656 precomputed_checksums.update(pickled.load())
657 except Exception as e:
658 # Avoid the warnings on the upgrade path from emtpy done stamp
659 # files to those containing the checksums.
660 if not isinstance(e, EOFError):
661 # Ignore errors, they aren't fatal
662 logger.warn("Couldn't load checksums from donestamp %s: %s "
663 "(msg: %s)" % (ud.donestamp, type(e).__name__,
664 str(e)))
665
666 try:
667 checksums = verify_checksum(ud, d, precomputed_checksums)
668 # If the cache file did not have the checksums, compute and store them
669 # as an upgrade path from the previous done stamp file format.
670 if checksums != precomputed_checksums:
671 with open(ud.donestamp, "wb") as cachefile:
672 p = pickle.Pickler(cachefile, pickle.HIGHEST_PROTOCOL)
673 p.dump(checksums)
674 return True
675 except ChecksumError as e:
676 # Checksums failed to verify, trigger re-download and remove the
677 # incorrect stamp file.
678 logger.warn("Checksum mismatch for local file %s\n"
679 "Cleaning and trying again." % ud.localpath)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500680 if os.path.exists(ud.localpath):
681 rename_bad_checksum(ud, e.checksum)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500682 bb.utils.remove(ud.donestamp)
683 return False
684
685
686def update_stamp(ud, d):
687 """
688 donestamp is file stamp indicating the whole fetching is done
689 this function update the stamp after verifying the checksum
690 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500691 if not ud.needdonestamp:
692 return
693
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500694 if os.path.exists(ud.donestamp):
695 # Touch the done stamp file to show active use of the download
696 try:
697 os.utime(ud.donestamp, None)
698 except:
699 # Errors aren't fatal here
700 pass
701 else:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500702 try:
703 checksums = verify_checksum(ud, d)
704 # Store the checksums for later re-verification against the recipe
705 with open(ud.donestamp, "wb") as cachefile:
706 p = pickle.Pickler(cachefile, pickle.HIGHEST_PROTOCOL)
707 p.dump(checksums)
708 except ChecksumError as e:
709 # Checksums failed to verify, trigger re-download and remove the
710 # incorrect stamp file.
711 logger.warn("Checksum mismatch for local file %s\n"
712 "Cleaning and trying again." % ud.localpath)
713 if os.path.exists(ud.localpath):
714 rename_bad_checksum(ud, e.checksum)
715 bb.utils.remove(ud.donestamp)
716 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500717
718def subprocess_setup():
719 # Python installs a SIGPIPE handler by default. This is usually not what
720 # non-Python subprocesses expect.
721 # SIGPIPE errors are known issues with gzip/bash
722 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
723
724def get_autorev(d):
725 # only not cache src rev in autorev case
726 if d.getVar('BB_SRCREV_POLICY', True) != "cache":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500727 d.setVar('BB_DONT_CACHE', '1')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500728 return "AUTOINC"
729
730def get_srcrev(d, method_name='sortable_revision'):
731 """
732 Return the revsion string, usually for use in the version string (PV) of the current package
733 Most packages usually only have one SCM so we just pass on the call.
734 In the multi SCM case, we build a value based on SRCREV_FORMAT which must
735 have been set.
736
737 The idea here is that we put the string "AUTOINC+" into return value if the revisions are not
738 incremental, other code is then responsible for turning that into an increasing value (if needed)
739
740 A method_name can be supplied to retrieve an alternatively formatted revision from a fetcher, if
741 that fetcher provides a method with the given name and the same signature as sortable_revision.
742 """
743
744 scms = []
745 fetcher = Fetch(d.getVar('SRC_URI', True).split(), d)
746 urldata = fetcher.ud
747 for u in urldata:
748 if urldata[u].method.supports_srcrev():
749 scms.append(u)
750
751 if len(scms) == 0:
752 raise FetchError("SRCREV was used yet no valid SCM was found in SRC_URI")
753
754 if len(scms) == 1 and len(urldata[scms[0]].names) == 1:
755 autoinc, rev = getattr(urldata[scms[0]].method, method_name)(urldata[scms[0]], d, urldata[scms[0]].names[0])
756 if len(rev) > 10:
757 rev = rev[:10]
758 if autoinc:
759 return "AUTOINC+" + rev
760 return rev
761
762 #
763 # Mutiple SCMs are in SRC_URI so we resort to SRCREV_FORMAT
764 #
765 format = d.getVar('SRCREV_FORMAT', True)
766 if not format:
767 raise FetchError("The SRCREV_FORMAT variable must be set when multiple SCMs are used.")
768
769 seenautoinc = False
770 for scm in scms:
771 ud = urldata[scm]
772 for name in ud.names:
773 autoinc, rev = getattr(ud.method, method_name)(ud, d, name)
774 seenautoinc = seenautoinc or autoinc
775 if len(rev) > 10:
776 rev = rev[:10]
777 format = format.replace(name, rev)
778 if seenautoinc:
779 format = "AUTOINC+" + format
780
781 return format
782
783def localpath(url, d):
784 fetcher = bb.fetch2.Fetch([url], d)
785 return fetcher.localpath(url)
786
787def runfetchcmd(cmd, d, quiet=False, cleanup=None):
788 """
789 Run cmd returning the command output
790 Raise an error if interrupted or cmd fails
791 Optionally echo command output to stdout
792 Optionally remove the files/directories listed in cleanup upon failure
793 """
794
795 # Need to export PATH as binary could be in metadata paths
796 # rather than host provided
797 # Also include some other variables.
798 # FIXME: Should really include all export varaiables?
799 exportvars = ['HOME', 'PATH',
800 'HTTP_PROXY', 'http_proxy',
801 'HTTPS_PROXY', 'https_proxy',
802 'FTP_PROXY', 'ftp_proxy',
803 'FTPS_PROXY', 'ftps_proxy',
804 'NO_PROXY', 'no_proxy',
805 'ALL_PROXY', 'all_proxy',
806 'GIT_PROXY_COMMAND',
807 'GIT_SSL_CAINFO',
808 'GIT_SMART_HTTP',
809 'SSH_AUTH_SOCK', 'SSH_AGENT_PID',
810 'SOCKS5_USER', 'SOCKS5_PASSWD']
811
812 if not cleanup:
813 cleanup = []
814
815 for var in exportvars:
816 val = d.getVar(var, True)
817 if val:
818 cmd = 'export ' + var + '=\"%s\"; %s' % (val, cmd)
819
820 logger.debug(1, "Running %s", cmd)
821
822 success = False
823 error_message = ""
824
825 try:
826 (output, errors) = bb.process.run(cmd, shell=True, stderr=subprocess.PIPE)
827 success = True
828 except bb.process.NotFoundError as e:
829 error_message = "Fetch command %s" % (e.command)
830 except bb.process.ExecutionError as e:
831 if e.stdout:
832 output = "output:\n%s\n%s" % (e.stdout, e.stderr)
833 elif e.stderr:
834 output = "output:\n%s" % e.stderr
835 else:
836 output = "no output"
837 error_message = "Fetch command failed with exit code %s, %s" % (e.exitcode, output)
838 except bb.process.CmdError as e:
839 error_message = "Fetch command %s could not be run:\n%s" % (e.command, e.msg)
840 if not success:
841 for f in cleanup:
842 try:
843 bb.utils.remove(f, True)
844 except OSError:
845 pass
846
847 raise FetchError(error_message)
848
849 return output
850
851def check_network_access(d, info = "", url = None):
852 """
853 log remote network access, and error if BB_NO_NETWORK is set
854 """
855 if d.getVar("BB_NO_NETWORK", True) == "1":
856 raise NetworkAccess(url, info)
857 else:
858 logger.debug(1, "Fetcher accessed the network with the command %s" % info)
859
860def build_mirroruris(origud, mirrors, ld):
861 uris = []
862 uds = []
863
864 replacements = {}
865 replacements["TYPE"] = origud.type
866 replacements["HOST"] = origud.host
867 replacements["PATH"] = origud.path
868 replacements["BASENAME"] = origud.path.split("/")[-1]
869 replacements["MIRRORNAME"] = origud.host.replace(':','.') + origud.path.replace('/', '.').replace('*', '.')
870
Patrick Williamsd7e96312015-09-22 08:09:05 -0500871 def adduri(ud, uris, uds, mirrors):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500872 for line in mirrors:
873 try:
874 (find, replace) = line
875 except ValueError:
876 continue
877 newuri = uri_replace(ud, find, replace, replacements, ld)
878 if not newuri or newuri in uris or newuri == origud.url:
879 continue
880
881 if not trusted_network(ld, newuri):
882 logger.debug(1, "Mirror %s not in the list of trusted networks, skipping" % (newuri))
883 continue
884
Patrick Williamsd7e96312015-09-22 08:09:05 -0500885 # Create a local copy of the mirrors minus the current line
886 # this will prevent us from recursively processing the same line
887 # as well as indirect recursion A -> B -> C -> A
888 localmirrors = list(mirrors)
889 localmirrors.remove(line)
890
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500891 try:
892 newud = FetchData(newuri, ld)
893 newud.setup_localpath(ld)
894 except bb.fetch2.BBFetchException as e:
895 logger.debug(1, "Mirror fetch failure for url %s (original url: %s)" % (newuri, origud.url))
896 logger.debug(1, str(e))
897 try:
898 # setup_localpath of file:// urls may fail, we should still see
899 # if mirrors of the url exist
Patrick Williamsd7e96312015-09-22 08:09:05 -0500900 adduri(newud, uris, uds, localmirrors)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500901 except UnboundLocalError:
902 pass
903 continue
904 uris.append(newuri)
905 uds.append(newud)
906
Patrick Williamsd7e96312015-09-22 08:09:05 -0500907 adduri(newud, uris, uds, localmirrors)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500908
Patrick Williamsd7e96312015-09-22 08:09:05 -0500909 adduri(origud, uris, uds, mirrors)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500910
911 return uris, uds
912
913def rename_bad_checksum(ud, suffix):
914 """
915 Renames files to have suffix from parameter
916 """
917
918 if ud.localpath is None:
919 return
920
921 new_localpath = "%s_bad-checksum_%s" % (ud.localpath, suffix)
922 bb.warn("Renaming %s to %s" % (ud.localpath, new_localpath))
923 bb.utils.movefile(ud.localpath, new_localpath)
924
925
926def try_mirror_url(fetch, origud, ud, ld, check = False):
927 # Return of None or a value means we're finished
928 # False means try another url
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500929
930 if ud.lockfile and ud.lockfile != origud.lockfile:
931 lf = bb.utils.lockfile(ud.lockfile)
932
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500933 try:
934 if check:
935 found = ud.method.checkstatus(fetch, ud, ld)
936 if found:
937 return found
938 return False
939
940 os.chdir(ld.getVar("DL_DIR", True))
941
942 if not verify_donestamp(ud, ld, origud) or ud.method.need_update(ud, ld):
943 ud.method.download(ud, ld)
944 if hasattr(ud.method,"build_mirror_data"):
945 ud.method.build_mirror_data(ud, ld)
946
947 if not ud.localpath or not os.path.exists(ud.localpath):
948 return False
949
950 if ud.localpath == origud.localpath:
951 return ud.localpath
952
953 # We may be obtaining a mirror tarball which needs further processing by the real fetcher
954 # If that tarball is a local file:// we need to provide a symlink to it
955 dldir = ld.getVar("DL_DIR", True)
956 if origud.mirrortarball and os.path.basename(ud.localpath) == os.path.basename(origud.mirrortarball) \
957 and os.path.basename(ud.localpath) != os.path.basename(origud.localpath):
958 # Create donestamp in old format to avoid triggering a re-download
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500959 if ud.donestamp:
960 bb.utils.mkdirhier(os.path.dirname(ud.donestamp))
961 open(ud.donestamp, 'w').close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500962 dest = os.path.join(dldir, os.path.basename(ud.localpath))
963 if not os.path.exists(dest):
964 os.symlink(ud.localpath, dest)
965 if not verify_donestamp(origud, ld) or origud.method.need_update(origud, ld):
966 origud.method.download(origud, ld)
967 if hasattr(origud.method,"build_mirror_data"):
968 origud.method.build_mirror_data(origud, ld)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500969 return origud.localpath
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500970 # Otherwise the result is a local file:// and we symlink to it
971 if not os.path.exists(origud.localpath):
972 if os.path.islink(origud.localpath):
973 # Broken symbolic link
974 os.unlink(origud.localpath)
975
976 os.symlink(ud.localpath, origud.localpath)
977 update_stamp(origud, ld)
978 return ud.localpath
979
980 except bb.fetch2.NetworkAccess:
981 raise
982
983 except bb.fetch2.BBFetchException as e:
984 if isinstance(e, ChecksumError):
985 logger.warn("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (ud.url, origud.url))
986 logger.warn(str(e))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500987 if os.path.exists(ud.localpath):
988 rename_bad_checksum(ud, e.checksum)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500989 elif isinstance(e, NoChecksumError):
990 raise
991 else:
992 logger.debug(1, "Mirror fetch failure for url %s (original url: %s)" % (ud.url, origud.url))
993 logger.debug(1, str(e))
994 try:
995 ud.method.clean(ud, ld)
996 except UnboundLocalError:
997 pass
998 return False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500999 finally:
1000 if ud.lockfile and ud.lockfile != origud.lockfile:
1001 bb.utils.unlockfile(lf)
1002
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001003
1004def try_mirrors(fetch, d, origud, mirrors, check = False):
1005 """
1006 Try to use a mirrored version of the sources.
1007 This method will be automatically called before the fetchers go.
1008
1009 d Is a bb.data instance
1010 uri is the original uri we're trying to download
1011 mirrors is the list of mirrors we're going to try
1012 """
1013 ld = d.createCopy()
1014
1015 uris, uds = build_mirroruris(origud, mirrors, ld)
1016
1017 for index, uri in enumerate(uris):
1018 ret = try_mirror_url(fetch, origud, uds[index], ld, check)
1019 if ret != False:
1020 return ret
1021 return None
1022
1023def trusted_network(d, url):
1024 """
1025 Use a trusted url during download if networking is enabled and
1026 BB_ALLOWED_NETWORKS is set globally or for a specific recipe.
1027 Note: modifies SRC_URI & mirrors.
1028 """
1029 if d.getVar('BB_NO_NETWORK', True) == "1":
1030 return True
1031
1032 pkgname = d.expand(d.getVar('PN', False))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001033 trusted_hosts = d.getVarFlag('BB_ALLOWED_NETWORKS', pkgname, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001034
1035 if not trusted_hosts:
1036 trusted_hosts = d.getVar('BB_ALLOWED_NETWORKS', True)
1037
1038 # Not enabled.
1039 if not trusted_hosts:
1040 return True
1041
1042 scheme, network, path, user, passwd, param = decodeurl(url)
1043
1044 if not network:
1045 return True
1046
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001047 network = network.split(':')[0]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001048 network = network.lower()
1049
1050 for host in trusted_hosts.split(" "):
1051 host = host.lower()
1052 if host.startswith("*.") and ("." + network).endswith(host[1:]):
1053 return True
1054 if host == network:
1055 return True
1056
1057 return False
1058
1059def srcrev_internal_helper(ud, d, name):
1060 """
1061 Return:
1062 a) a source revision if specified
1063 b) latest revision if SRCREV="AUTOINC"
1064 c) None if not specified
1065 """
1066
1067 srcrev = None
1068 pn = d.getVar("PN", True)
1069 attempts = []
1070 if name != '' and pn:
1071 attempts.append("SRCREV_%s_pn-%s" % (name, pn))
1072 if name != '':
1073 attempts.append("SRCREV_%s" % name)
1074 if pn:
1075 attempts.append("SRCREV_pn-%s" % pn)
1076 attempts.append("SRCREV")
1077
1078 for a in attempts:
1079 srcrev = d.getVar(a, True)
1080 if srcrev and srcrev != "INVALID":
1081 break
1082
1083 if 'rev' in ud.parm and 'tag' in ud.parm:
1084 raise FetchError("Please specify a ;rev= parameter or a ;tag= parameter in the url %s but not both." % (ud.url))
1085
1086 if 'rev' in ud.parm or 'tag' in ud.parm:
1087 if 'rev' in ud.parm:
1088 parmrev = ud.parm['rev']
1089 else:
1090 parmrev = ud.parm['tag']
1091 if srcrev == "INVALID" or not srcrev:
1092 return parmrev
1093 if srcrev != parmrev:
1094 raise FetchError("Conflicting revisions (%s from SRCREV and %s from the url) found, please spcify one valid value" % (srcrev, parmrev))
1095 return parmrev
1096
1097 if srcrev == "INVALID" or not srcrev:
1098 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)
1099 if srcrev == "AUTOINC":
1100 srcrev = ud.method.latest_revision(ud, d, name)
1101
1102 return srcrev
1103
1104def get_checksum_file_list(d):
1105 """ Get a list of files checksum in SRC_URI
1106
1107 Returns the resolved local paths of all local file entries in
1108 SRC_URI as a space-separated string
1109 """
1110 fetch = Fetch([], d, cache = False, localonly = True)
1111
1112 dl_dir = d.getVar('DL_DIR', True)
1113 filelist = []
1114 for u in fetch.urls:
1115 ud = fetch.ud[u]
1116
1117 if ud and isinstance(ud.method, local.Local):
1118 paths = ud.method.localpaths(ud, d)
1119 for f in paths:
1120 pth = ud.decodedurl
1121 if '*' in pth:
1122 f = os.path.join(os.path.abspath(f), pth)
1123 if f.startswith(dl_dir):
1124 # The local fetcher's behaviour is to return a path under DL_DIR if it couldn't find the file anywhere else
1125 if os.path.exists(f):
1126 bb.warn("Getting checksum for %s SRC_URI entry %s: file not found except in DL_DIR" % (d.getVar('PN', True), os.path.basename(f)))
1127 else:
1128 bb.warn("Unable to get checksum for %s SRC_URI entry %s: file could not be found" % (d.getVar('PN', True), os.path.basename(f)))
1129 filelist.append(f + ":" + str(os.path.exists(f)))
1130
1131 return " ".join(filelist)
1132
1133def get_file_checksums(filelist, pn):
1134 """Get a list of the checksums for a list of local files
1135
1136 Returns the checksums for a list of local files, caching the results as
1137 it proceeds
1138
1139 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001140 return _checksum_cache.get_checksums(filelist, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001141
1142
1143class FetchData(object):
1144 """
1145 A class which represents the fetcher state for a given URI.
1146 """
1147 def __init__(self, url, d, localonly = False):
1148 # localpath is the location of a downloaded result. If not set, the file is local.
1149 self.donestamp = None
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001150 self.needdonestamp = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001151 self.localfile = ""
1152 self.localpath = None
1153 self.lockfile = None
1154 self.mirrortarball = None
1155 self.basename = None
1156 self.basepath = None
1157 (self.type, self.host, self.path, self.user, self.pswd, self.parm) = decodeurl(data.expand(url, d))
1158 self.date = self.getSRCDate(d)
1159 self.url = url
1160 if not self.user and "user" in self.parm:
1161 self.user = self.parm["user"]
1162 if not self.pswd and "pswd" in self.parm:
1163 self.pswd = self.parm["pswd"]
1164 self.setup = False
1165
1166 if "name" in self.parm:
1167 self.md5_name = "%s.md5sum" % self.parm["name"]
1168 self.sha256_name = "%s.sha256sum" % self.parm["name"]
1169 else:
1170 self.md5_name = "md5sum"
1171 self.sha256_name = "sha256sum"
1172 if self.md5_name in self.parm:
1173 self.md5_expected = self.parm[self.md5_name]
1174 elif self.type not in ["http", "https", "ftp", "ftps", "sftp"]:
1175 self.md5_expected = None
1176 else:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001177 self.md5_expected = d.getVarFlag("SRC_URI", self.md5_name, True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001178 if self.sha256_name in self.parm:
1179 self.sha256_expected = self.parm[self.sha256_name]
1180 elif self.type not in ["http", "https", "ftp", "ftps", "sftp"]:
1181 self.sha256_expected = None
1182 else:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001183 self.sha256_expected = d.getVarFlag("SRC_URI", self.sha256_name, True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001184 self.ignore_checksums = False
1185
1186 self.names = self.parm.get("name",'default').split(',')
1187
1188 self.method = None
1189 for m in methods:
1190 if m.supports(self, d):
1191 self.method = m
1192 break
1193
1194 if not self.method:
1195 raise NoMethodError(url)
1196
1197 if localonly and not isinstance(self.method, local.Local):
1198 raise NonLocalMethod()
1199
1200 if self.parm.get("proto", None) and "protocol" not in self.parm:
1201 logger.warn('Consider updating %s recipe to use "protocol" not "proto" in SRC_URI.', d.getVar('PN', True))
1202 self.parm["protocol"] = self.parm.get("proto", None)
1203
1204 if hasattr(self.method, "urldata_init"):
1205 self.method.urldata_init(self, d)
1206
1207 if "localpath" in self.parm:
1208 # if user sets localpath for file, use it instead.
1209 self.localpath = self.parm["localpath"]
1210 self.basename = os.path.basename(self.localpath)
1211 elif self.localfile:
1212 self.localpath = self.method.localpath(self, d)
1213
1214 dldir = d.getVar("DL_DIR", True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001215
1216 if not self.needdonestamp:
1217 return
1218
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001219 # Note: .done and .lock files should always be in DL_DIR whereas localpath may not be.
1220 if self.localpath and self.localpath.startswith(dldir):
1221 basepath = self.localpath
1222 elif self.localpath:
1223 basepath = dldir + os.sep + os.path.basename(self.localpath)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001224 elif self.basepath or self.basename:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001225 basepath = dldir + os.sep + (self.basepath or self.basename)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001226 else:
1227 bb.fatal("Can't determine lock path for url %s" % url)
1228
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001229 self.donestamp = basepath + '.done'
1230 self.lockfile = basepath + '.lock'
1231
1232 def setup_revisons(self, d):
1233 self.revisions = {}
1234 for name in self.names:
1235 self.revisions[name] = srcrev_internal_helper(self, d, name)
1236
1237 # add compatibility code for non name specified case
1238 if len(self.names) == 1:
1239 self.revision = self.revisions[self.names[0]]
1240
1241 def setup_localpath(self, d):
1242 if not self.localpath:
1243 self.localpath = self.method.localpath(self, d)
1244
1245 def getSRCDate(self, d):
1246 """
1247 Return the SRC Date for the component
1248
1249 d the bb.data module
1250 """
1251 if "srcdate" in self.parm:
1252 return self.parm['srcdate']
1253
1254 pn = d.getVar("PN", True)
1255
1256 if pn:
1257 return d.getVar("SRCDATE_%s" % pn, True) or d.getVar("SRCDATE", True) or d.getVar("DATE", True)
1258
1259 return d.getVar("SRCDATE", True) or d.getVar("DATE", True)
1260
1261class FetchMethod(object):
1262 """Base class for 'fetch'ing data"""
1263
1264 def __init__(self, urls=None):
1265 self.urls = []
1266
1267 def supports(self, urldata, d):
1268 """
1269 Check to see if this fetch class supports a given url.
1270 """
1271 return 0
1272
1273 def localpath(self, urldata, d):
1274 """
1275 Return the local filename of a given url assuming a successful fetch.
1276 Can also setup variables in urldata for use in go (saving code duplication
1277 and duplicate code execution)
1278 """
1279 return os.path.join(data.getVar("DL_DIR", d, True), urldata.localfile)
1280
1281 def supports_checksum(self, urldata):
1282 """
1283 Is localpath something that can be represented by a checksum?
1284 """
1285
1286 # We cannot compute checksums for directories
1287 if os.path.isdir(urldata.localpath) == True:
1288 return False
1289 if urldata.localpath.find("*") != -1:
1290 return False
1291
1292 return True
1293
1294 def recommends_checksum(self, urldata):
1295 """
1296 Is the backend on where checksumming is recommended (should warnings
1297 be displayed if there is no checksum)?
1298 """
1299 return False
1300
1301 def _strip_leading_slashes(self, relpath):
1302 """
1303 Remove leading slash as os.path.join can't cope
1304 """
1305 while os.path.isabs(relpath):
1306 relpath = relpath[1:]
1307 return relpath
1308
1309 def setUrls(self, urls):
1310 self.__urls = urls
1311
1312 def getUrls(self):
1313 return self.__urls
1314
1315 urls = property(getUrls, setUrls, None, "Urls property")
1316
1317 def need_update(self, ud, d):
1318 """
1319 Force a fetch, even if localpath exists?
1320 """
1321 if os.path.exists(ud.localpath):
1322 return False
1323 return True
1324
1325 def supports_srcrev(self):
1326 """
1327 The fetcher supports auto source revisions (SRCREV)
1328 """
1329 return False
1330
1331 def download(self, urldata, d):
1332 """
1333 Fetch urls
1334 Assumes localpath was called first
1335 """
1336 raise NoMethodError(url)
1337
1338 def unpack(self, urldata, rootdir, data):
1339 iterate = False
1340 file = urldata.localpath
1341
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001342 # Localpath can't deal with 'dir/*' entries, so it converts them to '.',
1343 # but it must be corrected back for local files copying
1344 if urldata.basename == '*' and file.endswith('/.'):
1345 file = '%s/%s' % (file.rstrip('/.'), urldata.path)
1346
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001347 try:
1348 unpack = bb.utils.to_boolean(urldata.parm.get('unpack'), True)
1349 except ValueError as exc:
1350 bb.fatal("Invalid value for 'unpack' parameter for %s: %s" %
1351 (file, urldata.parm.get('unpack')))
1352
1353 base, ext = os.path.splitext(file)
1354 if ext in ['.gz', '.bz2', '.Z', '.xz', '.lz']:
1355 efile = os.path.join(rootdir, os.path.basename(base))
1356 else:
1357 efile = file
1358 cmd = None
1359
1360 if unpack:
1361 if file.endswith('.tar'):
1362 cmd = 'tar x --no-same-owner -f %s' % file
1363 elif file.endswith('.tgz') or file.endswith('.tar.gz') or file.endswith('.tar.Z'):
1364 cmd = 'tar xz --no-same-owner -f %s' % file
1365 elif file.endswith('.tbz') or file.endswith('.tbz2') or file.endswith('.tar.bz2'):
1366 cmd = 'bzip2 -dc %s | tar x --no-same-owner -f -' % file
1367 elif file.endswith('.gz') or file.endswith('.Z') or file.endswith('.z'):
1368 cmd = 'gzip -dc %s > %s' % (file, efile)
1369 elif file.endswith('.bz2'):
1370 cmd = 'bzip2 -dc %s > %s' % (file, efile)
1371 elif file.endswith('.tar.xz'):
1372 cmd = 'xz -dc %s | tar x --no-same-owner -f -' % file
1373 elif file.endswith('.xz'):
1374 cmd = 'xz -dc %s > %s' % (file, efile)
1375 elif file.endswith('.tar.lz'):
1376 cmd = 'lzip -dc %s | tar x --no-same-owner -f -' % file
1377 elif file.endswith('.lz'):
1378 cmd = 'lzip -dc %s > %s' % (file, efile)
1379 elif file.endswith('.zip') or file.endswith('.jar'):
1380 try:
1381 dos = bb.utils.to_boolean(urldata.parm.get('dos'), False)
1382 except ValueError as exc:
1383 bb.fatal("Invalid value for 'dos' parameter for %s: %s" %
1384 (file, urldata.parm.get('dos')))
1385 cmd = 'unzip -q -o'
1386 if dos:
1387 cmd = '%s -a' % cmd
1388 cmd = "%s '%s'" % (cmd, file)
1389 elif file.endswith('.rpm') or file.endswith('.srpm'):
1390 if 'extract' in urldata.parm:
1391 unpack_file = urldata.parm.get('extract')
1392 cmd = 'rpm2cpio.sh %s | cpio -id %s' % (file, unpack_file)
1393 iterate = True
1394 iterate_file = unpack_file
1395 else:
1396 cmd = 'rpm2cpio.sh %s | cpio -id' % (file)
1397 elif file.endswith('.deb') or file.endswith('.ipk'):
1398 cmd = 'ar -p %s data.tar.gz | zcat | tar --no-same-owner -xpf -' % file
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001399 elif file.endswith('.tar.7z'):
1400 cmd = '7z x -so %s | tar xf - ' % file
1401 elif file.endswith('.7z'):
1402 cmd = '7za x -y %s 1>/dev/null' % file
1403
1404 # If 'subdir' param exists, create a dir and use it as destination for unpack cmd
1405 if 'subdir' in urldata.parm:
1406 unpackdir = '%s/%s' % (rootdir, urldata.parm.get('subdir'))
1407 bb.utils.mkdirhier(unpackdir)
1408 else:
1409 unpackdir = rootdir
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001410
1411 if not unpack or not cmd:
1412 # If file == dest, then avoid any copies, as we already put the file into dest!
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001413 dest = os.path.join(unpackdir, os.path.basename(file))
1414 if file != dest and not (os.path.exists(dest) and os.path.samefile(file, dest)):
1415 destdir = '.'
1416 # For file:// entries all intermediate dirs in path must be created at destination
1417 if urldata.type == "file":
1418 # Trailing '/' does a copying to wrong place
1419 urlpath = urldata.path.rstrip('/')
1420 # Want files places relative to cwd so no leading '/'
1421 urlpath = urlpath.lstrip('/')
1422 if urlpath.find("/") != -1:
1423 destdir = urlpath.rsplit("/", 1)[0] + '/'
1424 bb.utils.mkdirhier("%s/%s" % (unpackdir, destdir))
1425 cmd = 'cp -fpPR %s %s' % (file, destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001426
1427 if not cmd:
1428 return
1429
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001430 # Change to unpackdir before executing command
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001431 save_cwd = os.getcwd();
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001432 os.chdir(unpackdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001433
1434 path = data.getVar('PATH', True)
1435 if path:
1436 cmd = "PATH=\"%s\" %s" % (path, cmd)
1437 bb.note("Unpacking %s to %s/" % (file, os.getcwd()))
1438 ret = subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True)
1439
1440 os.chdir(save_cwd)
1441
1442 if ret != 0:
1443 raise UnpackError("Unpack command %s failed with return value %s" % (cmd, ret), urldata.url)
1444
1445 if iterate is True:
1446 iterate_urldata = urldata
1447 iterate_urldata.localpath = "%s/%s" % (rootdir, iterate_file)
1448 self.unpack(urldata, rootdir, data)
1449
1450 return
1451
1452 def clean(self, urldata, d):
1453 """
1454 Clean any existing full or partial download
1455 """
1456 bb.utils.remove(urldata.localpath)
1457
1458 def try_premirror(self, urldata, d):
1459 """
1460 Should premirrors be used?
1461 """
1462 return True
1463
1464 def checkstatus(self, fetch, urldata, d):
1465 """
1466 Check the status of a URL
1467 Assumes localpath was called first
1468 """
1469 logger.info("URL %s could not be checked for status since no method exists.", url)
1470 return True
1471
1472 def latest_revision(self, ud, d, name):
1473 """
1474 Look in the cache for the latest revision, if not present ask the SCM.
1475 """
1476 if not hasattr(self, "_latest_revision"):
1477 raise ParameterError("The fetcher for this URL does not support _latest_revision", url)
1478
1479 revs = bb.persist_data.persist('BB_URI_HEADREVS', d)
1480 key = self.generate_revision_key(ud, d, name)
1481 try:
1482 return revs[key]
1483 except KeyError:
1484 revs[key] = rev = self._latest_revision(ud, d, name)
1485 return rev
1486
1487 def sortable_revision(self, ud, d, name):
1488 latest_rev = self._build_revision(ud, d, name)
1489 return True, str(latest_rev)
1490
1491 def generate_revision_key(self, ud, d, name):
1492 key = self._revision_key(ud, d, name)
1493 return "%s-%s" % (key, d.getVar("PN", True) or "")
1494
1495class Fetch(object):
1496 def __init__(self, urls, d, cache = True, localonly = False, connection_cache = None):
1497 if localonly and cache:
1498 raise Exception("bb.fetch2.Fetch.__init__: cannot set cache and localonly at same time")
1499
1500 if len(urls) == 0:
1501 urls = d.getVar("SRC_URI", True).split()
1502 self.urls = urls
1503 self.d = d
1504 self.ud = {}
1505 self.connection_cache = connection_cache
1506
1507 fn = d.getVar('FILE', True)
1508 if cache and fn and fn in urldata_cache:
1509 self.ud = urldata_cache[fn]
1510
1511 for url in urls:
1512 if url not in self.ud:
1513 try:
1514 self.ud[url] = FetchData(url, d, localonly)
1515 except NonLocalMethod:
1516 if localonly:
1517 self.ud[url] = None
1518 pass
1519
1520 if fn and cache:
1521 urldata_cache[fn] = self.ud
1522
1523 def localpath(self, url):
1524 if url not in self.urls:
1525 self.ud[url] = FetchData(url, self.d)
1526
1527 self.ud[url].setup_localpath(self.d)
1528 return self.d.expand(self.ud[url].localpath)
1529
1530 def localpaths(self):
1531 """
1532 Return a list of the local filenames, assuming successful fetch
1533 """
1534 local = []
1535
1536 for u in self.urls:
1537 ud = self.ud[u]
1538 ud.setup_localpath(self.d)
1539 local.append(ud.localpath)
1540
1541 return local
1542
1543 def download(self, urls=None):
1544 """
1545 Fetch all urls
1546 """
1547 if not urls:
1548 urls = self.urls
1549
1550 network = self.d.getVar("BB_NO_NETWORK", True)
1551 premirroronly = (self.d.getVar("BB_FETCH_PREMIRRORONLY", True) == "1")
1552
1553 for u in urls:
1554 ud = self.ud[u]
1555 ud.setup_localpath(self.d)
1556 m = ud.method
1557 localpath = ""
1558
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001559 if ud.lockfile:
1560 lf = bb.utils.lockfile(ud.lockfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001561
1562 try:
1563 self.d.setVar("BB_NO_NETWORK", network)
1564
1565 if verify_donestamp(ud, self.d) and not m.need_update(ud, self.d):
1566 localpath = ud.localpath
1567 elif m.try_premirror(ud, self.d):
1568 logger.debug(1, "Trying PREMIRRORS")
1569 mirrors = mirror_from_string(self.d.getVar('PREMIRRORS', True))
1570 localpath = try_mirrors(self, self.d, ud, mirrors, False)
1571
1572 if premirroronly:
1573 self.d.setVar("BB_NO_NETWORK", "1")
1574
1575 os.chdir(self.d.getVar("DL_DIR", True))
1576
1577 firsterr = None
1578 verified_stamp = verify_donestamp(ud, self.d)
1579 if not localpath and (not verified_stamp or m.need_update(ud, self.d)):
1580 try:
1581 if not trusted_network(self.d, ud.url):
1582 raise UntrustedUrl(ud.url)
1583 logger.debug(1, "Trying Upstream")
1584 m.download(ud, self.d)
1585 if hasattr(m, "build_mirror_data"):
1586 m.build_mirror_data(ud, self.d)
1587 localpath = ud.localpath
1588 # early checksum verify, so that if checksum mismatched,
1589 # fetcher still have chance to fetch from mirror
1590 update_stamp(ud, self.d)
1591
1592 except bb.fetch2.NetworkAccess:
1593 raise
1594
1595 except BBFetchException as e:
1596 if isinstance(e, ChecksumError):
1597 logger.warn("Checksum failure encountered with download of %s - will attempt other sources if available" % u)
1598 logger.debug(1, str(e))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001599 if os.path.exists(ud.localpath):
1600 rename_bad_checksum(ud, e.checksum)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001601 elif isinstance(e, NoChecksumError):
1602 raise
1603 else:
1604 logger.warn('Failed to fetch URL %s, attempting MIRRORS if available' % u)
1605 logger.debug(1, str(e))
1606 firsterr = e
1607 # Remove any incomplete fetch
1608 if not verified_stamp:
1609 m.clean(ud, self.d)
1610 logger.debug(1, "Trying MIRRORS")
1611 mirrors = mirror_from_string(self.d.getVar('MIRRORS', True))
1612 localpath = try_mirrors(self, self.d, ud, mirrors)
1613
1614 if not localpath or ((not os.path.exists(localpath)) and localpath.find("*") == -1):
1615 if firsterr:
1616 logger.error(str(firsterr))
1617 raise FetchError("Unable to fetch URL from any source.", u)
1618
1619 update_stamp(ud, self.d)
1620
1621 except BBFetchException as e:
1622 if isinstance(e, ChecksumError):
1623 logger.error("Checksum failure fetching %s" % u)
1624 raise
1625
1626 finally:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001627 if ud.lockfile:
1628 bb.utils.unlockfile(lf)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001629
1630 def checkstatus(self, urls=None):
1631 """
1632 Check all urls exist upstream
1633 """
1634
1635 if not urls:
1636 urls = self.urls
1637
1638 for u in urls:
1639 ud = self.ud[u]
1640 ud.setup_localpath(self.d)
1641 m = ud.method
1642 logger.debug(1, "Testing URL %s", u)
1643 # First try checking uri, u, from PREMIRRORS
1644 mirrors = mirror_from_string(self.d.getVar('PREMIRRORS', True))
1645 ret = try_mirrors(self, self.d, ud, mirrors, True)
1646 if not ret:
1647 # Next try checking from the original uri, u
1648 try:
1649 ret = m.checkstatus(self, ud, self.d)
1650 except:
1651 # Finally, try checking uri, u, from MIRRORS
1652 mirrors = mirror_from_string(self.d.getVar('MIRRORS', True))
1653 ret = try_mirrors(self, self.d, ud, mirrors, True)
1654
1655 if not ret:
1656 raise FetchError("URL %s doesn't work" % u, u)
1657
1658 def unpack(self, root, urls=None):
1659 """
1660 Check all urls exist upstream
1661 """
1662
1663 if not urls:
1664 urls = self.urls
1665
1666 for u in urls:
1667 ud = self.ud[u]
1668 ud.setup_localpath(self.d)
1669
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001670 if ud.lockfile:
1671 lf = bb.utils.lockfile(ud.lockfile)
1672
1673 ud.method.unpack(ud, root, self.d)
1674
1675 if ud.lockfile:
1676 bb.utils.unlockfile(lf)
1677
1678 def clean(self, urls=None):
1679 """
1680 Clean files that the fetcher gets or places
1681 """
1682
1683 if not urls:
1684 urls = self.urls
1685
1686 for url in urls:
1687 if url not in self.ud:
1688 self.ud[url] = FetchData(url, d)
1689 ud = self.ud[url]
1690 ud.setup_localpath(self.d)
1691
1692 if not ud.localfile and ud.localpath is None:
1693 continue
1694
1695 if ud.lockfile:
1696 lf = bb.utils.lockfile(ud.lockfile)
1697
1698 ud.method.clean(ud, self.d)
1699 if ud.donestamp:
1700 bb.utils.remove(ud.donestamp)
1701
1702 if ud.lockfile:
1703 bb.utils.unlockfile(lf)
1704
1705class FetchConnectionCache(object):
1706 """
1707 A class which represents an container for socket connections.
1708 """
1709 def __init__(self):
1710 self.cache = {}
1711
1712 def get_connection_name(self, host, port):
1713 return host + ':' + str(port)
1714
1715 def add_connection(self, host, port, connection):
1716 cn = self.get_connection_name(host, port)
1717
1718 if cn not in self.cache:
1719 self.cache[cn] = connection
1720
1721 def get_connection(self, host, port):
1722 connection = None
1723
1724 cn = self.get_connection_name(host, port)
1725 if cn in self.cache:
1726 connection = self.cache[cn]
1727
1728 return connection
1729
1730 def remove_connection(self, host, port):
1731 cn = self.get_connection_name(host, port)
1732 if cn in self.cache:
1733 self.cache[cn].close()
1734 del self.cache[cn]
1735
1736 def close_connections(self):
1737 for cn in self.cache.keys():
1738 self.cache[cn].close()
1739 del self.cache[cn]
1740
1741from . import cvs
1742from . import git
1743from . import gitsm
1744from . import gitannex
1745from . import local
1746from . import svn
1747from . import wget
1748from . import ssh
1749from . import sftp
1750from . import perforce
1751from . import bzr
1752from . import hg
1753from . import osc
1754from . import repo
1755from . import clearcase
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001756from . import npm
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001757
1758methods.append(local.Local())
1759methods.append(wget.Wget())
1760methods.append(svn.Svn())
1761methods.append(git.Git())
1762methods.append(gitsm.GitSM())
1763methods.append(gitannex.GitANNEX())
1764methods.append(cvs.Cvs())
1765methods.append(ssh.SSH())
1766methods.append(sftp.SFTP())
1767methods.append(perforce.Perforce())
1768methods.append(bzr.Bzr())
1769methods.append(hg.Hg())
1770methods.append(osc.Osc())
1771methods.append(repo.Repo())
1772methods.append(clearcase.ClearCase())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001773methods.append(npm.Npm())