1 # ex:ts=4:sw=4:sts=4:et
2 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4 BitBake 'Fetch' implementations
6 Classes for obtaining upstream sources for the
10 # Copyright (C) 2003, 2004 Chris Larson
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License version 2 as
14 # published by the Free Software Foundation.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License along
22 # with this program; if not, write to the Free Software Foundation, Inc.,
23 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 # Based on functions from the base bb module, Copyright 2003 Holger Schurig
30 from bb import persist_data
33 import cPickle as pickle
37 class FetchError(Exception):
38 """Exception raised when a download fails"""
40 class NoMethodError(Exception):
41 """Exception raised when there is no method to obtain a supplied url or set of urls"""
43 class MissingParameterError(Exception):
44 """Exception raised when a fetch method is missing a critical parameter in the url"""
46 class ParameterError(Exception):
47 """Exception raised when a url cannot be proccessed due to invalid parameters."""
49 class MD5SumError(Exception):
50 """Exception raised when a MD5SUM of a file does not match the expected one"""
52 def uri_replace(uri, uri_find, uri_replace, d):
53 # bb.msg.note(1, bb.msg.domain.Fetcher, "uri_replace: operating on %s" % uri)
54 if not uri or not uri_find or not uri_replace:
55 bb.msg.debug(1, bb.msg.domain.Fetcher, "uri_replace: passed an undefined value, not replacing")
56 uri_decoded = list(bb.decodeurl(uri))
57 uri_find_decoded = list(bb.decodeurl(uri_find))
58 uri_replace_decoded = list(bb.decodeurl(uri_replace))
59 result_decoded = ['','','','','',{}]
60 for i in uri_find_decoded:
61 loc = uri_find_decoded.index(i)
62 result_decoded[loc] = uri_decoded[loc]
64 if type(i) == types.StringType:
66 if (re.match(i, uri_decoded[loc])):
67 result_decoded[loc] = re.sub(i, uri_replace_decoded[loc], uri_decoded[loc])
68 if uri_find_decoded.index(i) == 2:
70 localfn = bb.fetch.localpath(uri, d)
72 result_decoded[loc] = os.path.dirname(result_decoded[loc]) + "/" + os.path.basename(bb.fetch.localpath(uri, d))
73 # bb.msg.note(1, bb.msg.domain.Fetcher, "uri_replace: matching %s against %s and replacing with %s" % (i, uri_decoded[loc], uri_replace_decoded[loc]))
75 # bb.msg.note(1, bb.msg.domain.Fetcher, "uri_replace: no match")
79 # FIXME: apply replacements against options
80 return bb.encodeurl(result_decoded)
86 Called to initilize the fetchers once the configuration data is known
87 Calls before this must not hit the cache.
89 pd = persist_data.PersistData(d)
90 # Clear any cached url data
91 pd.delDomain("BB_URLDATA")
92 # When to drop SCM head revisions should be controled by user policy
93 pd.delDomain("BB_URI_HEADREVS")
94 # Make sure our domains exist
95 pd.addDomain("BB_URLDATA")
96 pd.addDomain("BB_URI_HEADREVS")
97 pd.addDomain("BB_URI_LOCALCOUNT")
99 # Function call order is usually:
103 # localpath can be called at any time
105 def init(urls, d, cache = True):
109 urldata, pd, fn = getdata(d)
112 if url not in urldata:
113 ud = FetchData(url, d)
115 if m.supports(url, ud, d):
121 pd.setValue("BB_URLDATA", fn, pickle.dumps(urldata, 0))
127 fn = bb.data.getVar('FILE', d, 1)
128 pd = persist_data.PersistData(d)
129 encdata = pd.getValue("BB_URLDATA", fn)
131 urldata = pickle.loads(str(encdata))
133 return urldata, pd, fn
135 def go(d, urldata = None):
140 urldata, pd, fn = getdata(d)
145 if ud.localfile and not m.forcefetch(u, ud, d) and os.path.exists(ud.md5):
146 # File already present along with md5 stamp file
147 # Touch md5 file to show activity
148 os.utime(ud.md5, None)
150 # RP - is olddir needed?
151 # olddir = os.path.abspath(os.getcwd())
154 if ud.localfile and not m.forcefetch(u, ud, d):
155 Fetch.write_md5sum(u, ud, d)
157 def localpaths(d, urldata = None):
159 Return a list of the local filenames, assuming successful fetch
163 urldata, pd, fn = getdata(d)
167 local.append(ud.localpath)
173 Return the version string for the current package
174 (usually to be used as PV)
175 Most packages usually only have one SCM so we just pass on the call.
176 In the multi SCM case, we build a value based on SRCREV_FORMAT which must
179 urldata, pd, fn = getdata(d)
180 if len(urldata) == 0:
181 src_uri = bb.data.getVar('SRC_URI', d, 1).split(" ")
182 urldata = init(src_uri, d, True)
187 if ud.method.suppports_srcrev():
191 bb.msg.error(bb.msg.domain.Fetcher, "SRCREV was used yet no valid SCM was found in SRC_URI")
195 return ud.method.sortable_revision(scms[0], urldata[scms[0]], d)
197 bb.msg.error(bb.msg.domain.Fetcher, "Sorry, support for SRCREV_FORMAT still needs to be written")
200 def localpath(url, d, cache = True):
202 Called from the parser with cache=False since the cache isn't ready
203 at this point. Also called from classed in OE e.g. patch.bbclass
205 ud = init([url], d, cache)
207 return ud[url].localpath
210 def runfetchcmd(cmd, d, quiet = False):
212 Run cmd returning the command output
213 Raise an error if interrupted or cmd fails
214 Optionally echo command output to stdout
216 bb.msg.debug(1, bb.msg.domain.Fetcher, "Running %s" % cmd)
218 # Need to export PATH as binary could be in metadata paths
219 # rather than host provided
220 pathcmd = 'export PATH=%s; %s' % (data.expand('${PATH}', d), cmd)
222 stdout_handle = os.popen(pathcmd, "r")
226 line = stdout_handle.readline()
233 status = stdout_handle.close() or 0
235 exitstatus = status & 0xff
238 raise FetchError("Fetch command %s failed with signal %s, output:\n%s" % (pathcmd, signal, output))
240 raise FetchError("Fetch command %s failed with exit code %s, output:\n%s" % (pathcmd, status, output))
244 class FetchData(object):
245 """Class for fetcher variable store"""
246 def __init__(self, url, d):
248 (self.type, self.host, self.path, self.user, self.pswd, self.parm) = bb.decodeurl(data.expand(url, d))
249 self.date = Fetch.getSRCDate(self, d)
253 def init(self, method, d):
255 self.localpath = method.localpath(self.url, self, d)
256 self.md5 = self.localpath + '.md5'
257 # if user sets localpath for file, use it instead.
258 if "localpath" in self.parm:
259 self.localpath = self.parm["localpath"]
262 """Base class for 'fetch'ing data"""
264 def __init__(self, urls = []):
267 def supports(self, url, urldata, d):
269 Check to see if this fetch class supports a given url.
273 def localpath(self, url, urldata, d):
275 Return the local filename of a given url assuming a successful fetch.
276 Can also setup variables in urldata for use in go (saving code duplication
277 and duplicate code execution)
281 def setUrls(self, urls):
287 urls = property(getUrls, setUrls, None, "Urls property")
289 def forcefetch(self, url, urldata, d):
291 Force a fetch, even if localpath exists?
295 def suppports_srcrev(self):
297 The fetcher supports auto source revisions (SRCREV)
301 def go(self, url, urldata, d):
304 Assumes localpath was called first
306 raise NoMethodError("Missing implementation for url")
308 def getSRCDate(urldata, d):
310 Return the SRC Date for the component
314 if "srcdate" in urldata.parm:
315 return urldata.parm['srcdate']
317 pn = data.getVar("PN", d, 1)
320 return data.getVar("SRCDATE_%s" % pn, d, 1) or data.getVar("CVSDATE_%s" % pn, d, 1) or data.getVar("DATE", d, 1)
322 return data.getVar("SRCDATE", d, 1) or data.getVar("CVSDATE", d, 1) or data.getVar("DATE", d, 1)
323 getSRCDate = staticmethod(getSRCDate)
325 def try_mirror(d, tarfn):
327 Try to use a mirrored version of the sources. We do this
328 to avoid massive loads on foreign cvs and svn servers.
329 This method will be used by the different fetcher
332 d Is a bb.data instance
333 tarfn is the name of the tarball
335 tarpath = os.path.join(data.getVar("DL_DIR", d, 1), tarfn)
336 if os.access(tarpath, os.R_OK):
337 bb.msg.debug(1, bb.msg.domain.Fetcher, "%s already exists, skipping checkout." % tarfn)
340 pn = data.getVar('PN', d, True)
341 src_tarball_stash = None
343 src_tarball_stash = (data.getVar('SRC_TARBALL_STASH_%s' % pn, d, True) or data.getVar('CVS_TARBALL_STASH_%s' % pn, d, True) or data.getVar('SRC_TARBALL_STASH', d, True) or data.getVar('CVS_TARBALL_STASH', d, True) or "").split()
345 for stash in src_tarball_stash:
346 fetchcmd = data.getVar("FETCHCOMMAND_mirror", d, True) or data.getVar("FETCHCOMMAND_wget", d, True)
348 bb.msg.note(1, bb.msg.domain.Fetcher, "fetch " + uri)
349 fetchcmd = fetchcmd.replace("${URI}", uri)
350 ret = os.system(fetchcmd)
352 bb.msg.note(1, bb.msg.domain.Fetcher, "Fetched %s from tarball stash, skipping checkout" % tarfn)
355 try_mirror = staticmethod(try_mirror)
357 def verify_md5sum(ud, got_sum):
359 Verify the md5sum we wanted with the one we got
362 if 'md5sum' in ud.parm:
363 wanted_sum = ud.parm['md5sum']
367 return wanted_sum == got_sum
368 verify_md5sum = staticmethod(verify_md5sum)
370 def write_md5sum(url, ud, d):
371 if bb.which(data.getVar('PATH', d), 'md5sum'):
373 md5pipe = os.popen('md5sum ' + ud.localpath)
374 md5data = (md5pipe.readline().split() or [ "" ])[0]
380 if not Fetch.verify_md5sum(ud, md5data):
381 raise MD5SumError(url)
383 md5out = file(ud.md5, 'w')
384 md5out.write(md5data)
386 write_md5sum = staticmethod(write_md5sum)
388 def latest_revision(self, url, ud, d):
390 Look in the cache for the latest revision, if not present ask the SCM.
392 if not self._latest_revision:
395 pd = persist_data.PersistData(d)
396 key = self._revision_key(url, ud, d)
397 rev = pd.getValue("BB_URI_HEADREVS", key)
401 rev = self._latest_revision(url, ud, d)
402 pd.setValue("BB_URI_HEADREVS", key, rev)
405 def sortable_revision(self, url, ud, d):
409 if not self._sortable_revision:
412 return self._sortable_revision(url, ud, d)
423 methods.append(local.Local())
424 methods.append(wget.Wget())
425 methods.append(svn.Svn())
426 methods.append(git.Git())
427 methods.append(cvs.Cvs())
428 methods.append(svk.Svk())
429 methods.append(ssh.SSH())
430 methods.append(perforce.Perforce())