2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
5 BitBake 'Fetch' implementations
7 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 it under
13 the terms of the GNU General Public License as published by the Free Software
14 Foundation; either version 2 of the License, or (at your option) any later
17 This program is distributed in the hope that it will be useful, but WITHOUT
18 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
23 Place, Suite 330, Boston, MA 02111-1307 USA.
25 Based on functions from the base bb module, Copyright 2003 Holger Schurig
32 class FetchError(Exception):
33 """Exception raised when a download fails"""
35 class NoMethodError(Exception):
36 """Exception raised when there is no method to obtain a supplied url or set of urls"""
38 class MissingParameterError(Exception):
39 """Exception raised when a fetch method is missing a critical parameter in the url"""
41 #decodeurl("cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;module=familiar/dist/ipkg;tag=V0-99-81")
42 #('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'})
44 def uri_replace(uri, uri_find, uri_replace, d):
45 # bb.note("uri_replace: operating on %s" % uri)
46 if not uri or not uri_find or not uri_replace:
47 bb.debug(1, "uri_replace: passed an undefined value, not replacing")
48 uri_decoded = list(bb.decodeurl(uri))
49 uri_find_decoded = list(bb.decodeurl(uri_find))
50 uri_replace_decoded = list(bb.decodeurl(uri_replace))
51 result_decoded = ['','','','','',{}]
52 for i in uri_find_decoded:
53 loc = uri_find_decoded.index(i)
54 result_decoded[loc] = uri_decoded[loc]
56 if type(i) == types.StringType:
58 if (re.match(i, uri_decoded[loc])):
59 result_decoded[loc] = re.sub(i, uri_replace_decoded[loc], uri_decoded[loc])
60 if uri_find_decoded.index(i) == 2:
62 localfn = bb.fetch.localpath(uri, d)
64 result_decoded[loc] = os.path.dirname(result_decoded[loc]) + "/" + os.path.basename(bb.fetch.localpath(uri, d))
65 # bb.note("uri_replace: matching %s against %s and replacing with %s" % (i, uri_decoded[loc], uri_replace_decoded[loc]))
67 # bb.note("uri_replace: no match")
71 # FIXME: apply replacements against options
72 return bb.encodeurl(result_decoded)
76 def init(urls = [], d = None):
93 """Return a list of the local filenames, assuming successful fetch"""
97 local.append(m.localpath(u, d))
100 def localpath(url, d):
102 if m.supports(url, d):
103 return m.localpath(url, d)
107 """Base class for 'fetch'ing data"""
109 def __init__(self, urls = []):
112 if self.supports(bb.decodeurl(url), d) is 1:
113 self.urls.append(url)
115 def supports(url, d):
116 """Check to see if this fetch class supports a given url.
117 Expects supplied url in list form, as outputted by bb.decodeurl().
120 supports = staticmethod(supports)
122 def localpath(url, d):
123 """Return the local filename of a given url assuming a successful fetch.
126 localpath = staticmethod(localpath)
128 def setUrls(self, urls):
134 urls = property(getUrls, setUrls, None, "Urls property")
136 def setData(self, data):
142 data = property(getData, setData, None, "Data property")
144 def go(self, urls = []):
146 raise NoMethodError("Missing implementation for url")
149 """Class to fetch urls via 'wget'"""
150 def supports(url, d):
151 """Check to see if a given url can be fetched using wget.
152 Expects supplied url in list form, as outputted by bb.decodeurl().
154 (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
155 return type in ['http','https','ftp']
156 supports = staticmethod(supports)
158 def localpath(url, d):
159 # strip off parameters
160 (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
161 if "localpath" in parm:
162 # if user overrides local path, use it.
163 return parm["localpath"]
164 url = bb.encodeurl([type, host, path, user, pswd, {}])
166 return os.path.join(data.getVar("DL_DIR", d), os.path.basename(url))
167 localpath = staticmethod(localpath)
169 def go(self, d, urls = []):
171 def fetch_uri(uri, basename, dl, md5, d):
172 if os.path.exists(dl):
173 # file exists, but we didnt complete it.. trying again..
174 fetchcmd = data.getVar("RESUMECOMMAND", d, 1)
176 fetchcmd = data.getVar("FETCHCOMMAND", d, 1)
178 bb.note("fetch " + uri)
179 fetchcmd = fetchcmd.replace("${URI}", uri)
180 fetchcmd = fetchcmd.replace("${FILE}", basename)
181 bb.debug(2, "executing " + fetchcmd)
182 ret = os.system(fetchcmd)
186 # check if sourceforge did send us to the mirror page
187 dl_dir = data.getVar("DL_DIR", d, True)
188 if not os.path.exists(dl):
189 os.system("rm %s*" % dl) # FIXME shell quote it
190 bb.debug(2,"sourceforge.net send us to the mirror on %s" % basename)
193 # supposedly complete.. write out md5sum
194 if bb.which(data.getVar('PATH', d), 'md5sum'):
196 md5pipe = os.popen('md5sum ' + dl)
197 md5data = (md5pipe.readline().split() or [ "" ])[0]
201 md5out = file(md5, 'w')
202 md5out.write(md5data)
205 md5out = file(md5, 'w')
213 localdata = data.createCopy(d)
214 data.setVar('OVERRIDES', "wget:" + data.getVar('OVERRIDES', localdata), localdata)
215 data.update_data(localdata)
219 (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(uri, localdata))
220 basename = os.path.basename(path)
221 dl = self.localpath(uri, d)
222 dl = data.expand(dl, localdata)
225 if os.path.exists(md5):
226 # complete, nothing to see here..
229 premirrors = [ i.split() for i in (data.getVar('PREMIRRORS', localdata, 1) or "").split('\n') if i ]
230 for (find, replace) in premirrors:
231 newuri = uri_replace(uri, find, replace, d)
233 if fetch_uri(newuri, basename, dl, md5, localdata):
240 if fetch_uri(uri, basename, dl, md5, localdata):
244 mirrors = [ i.split() for i in (data.getVar('MIRRORS', localdata, 1) or "").split('\n') if i ]
245 for (find, replace) in mirrors:
246 newuri = uri_replace(uri, find, replace, d)
248 if fetch_uri(newuri, basename, dl, md5, localdata):
253 raise FetchError(uri)
258 methods.append(Wget())
261 """Class to fetch a module or modules from cvs repositories"""
262 def supports(url, d):
263 """Check to see if a given url can be fetched with cvs.
264 Expects supplied url in list form, as outputted by bb.decodeurl().
266 (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
267 return type in ['cvs', 'pserver']
268 supports = staticmethod(supports)
270 def localpath(url, d):
271 (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
272 if "localpath" in parm:
273 # if user overrides local path, use it.
274 return parm["localpath"]
276 if not "module" in parm:
277 raise MissingParameterError("cvs method needs a 'module' parameter")
279 module = parm["module"]
288 date = data.getVar("CVSDATE", d, 1) or data.getVar("DATE", d, 1)
292 return os.path.join(data.getVar("DL_DIR", d, 1),data.expand('%s_%s_%s_%s.tar.gz' % ( module.replace('/', '.'), host, tag, date), d))
293 localpath = staticmethod(localpath)
295 def go(self, d, urls = []):
300 localdata = data.createCopy(d)
301 data.setVar('OVERRIDES', "cvs:%s" % data.getVar('OVERRIDES', localdata), localdata)
302 data.update_data(localdata)
305 (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(loc, localdata))
306 if not "module" in parm:
307 raise MissingParameterError("cvs method needs a 'module' parameter")
309 module = parm["module"]
311 dlfile = self.localpath(loc, localdata)
312 dldir = data.getVar('DL_DIR', localdata, 1)
313 # if local path contains the cvs
314 # module, consider the dir above it to be the
316 # pos = dlfile.find(module)
318 # dldir = dlfile[:pos]
320 # dldir = os.path.dirname(dlfile)
333 date = data.getVar("CVSDATE", d, 1) or data.getVar("DATE", d, 1)
338 method = parm["method"]
342 if "localdir" in parm:
343 localdir = parm["localdir"]
350 cvs_rsh = parm["rsh"]
352 tarfn = data.expand('%s_%s_%s_%s.tar.gz' % (module.replace('/', '.'), host, tag, date), localdata)
353 data.setVar('TARFILES', dlfile, localdata)
354 data.setVar('TARFN', tarfn, localdata)
356 dl = os.path.join(dldir, tarfn)
357 if os.access(dl, os.R_OK):
358 bb.debug(1, "%s already exists, skipping cvs checkout." % tarfn)
361 pn = data.getVar('PN', d, 1)
362 cvs_tarball_stash = None
364 cvs_tarball_stash = data.getVar('CVS_TARBALL_STASH_%s' % pn, d, 1)
365 if cvs_tarball_stash == None:
366 cvs_tarball_stash = data.getVar('CVS_TARBALL_STASH', d, 1)
367 if cvs_tarball_stash:
368 fetchcmd = data.getVar("FETCHCOMMAND_wget", d, 1)
369 uri = cvs_tarball_stash + tarfn
370 bb.note("fetch " + uri)
371 fetchcmd = fetchcmd.replace("${URI}", uri)
372 ret = os.system(fetchcmd)
374 bb.note("Fetched %s from tarball stash, skipping checkout" % tarfn)
378 options.append("-D %s" % date)
380 options.append("-r %s" % tag)
382 olddir = os.path.abspath(os.getcwd())
383 os.chdir(data.expand(dldir, localdata))
389 cvsroot = ":" + method + ":" + user
391 cvsroot += ":" + pswd
392 cvsroot += "@" + host + ":" + path
394 data.setVar('CVSROOT', cvsroot, localdata)
395 data.setVar('CVSCOOPTS', " ".join(options), localdata)
396 data.setVar('CVSMODULE', module, localdata)
397 cvscmd = data.getVar('FETCHCOMMAND', localdata, 1)
398 cvsupdatecmd = data.getVar('UPDATECOMMAND', localdata, 1)
401 cvscmd = "CVS_RSH=\"%s\" %s" % (cvs_rsh, cvscmd)
402 cvsupdatecmd = "CVS_RSH=\"%s\" %s" % (cvs_rsh, cvsupdatecmd)
404 # create module directory
405 bb.debug(2, "Fetch: checking for module directory")
406 pkg=data.expand('${PN}', d)
407 pkgdir=os.path.join(data.expand('${CVSDIR}', localdata), pkg)
408 moddir=os.path.join(pkgdir,localdir)
409 if os.access(os.path.join(moddir,'CVS'), os.R_OK):
410 bb.note("Update " + loc)
411 # update sources there
413 myret = os.system(cvsupdatecmd)
415 bb.note("Fetch " + loc)
416 # check out sources there
419 bb.debug(1, "Running %s" % cvscmd)
420 myret = os.system(cvscmd)
427 raise FetchError(module)
431 # tar them up to a defined filename
432 myret = os.system("tar -czf %s %s" % (os.path.join(dldir,tarfn), os.path.basename(moddir)))
441 methods.append(Cvs())
444 def supports(url, d):
445 """Check to see if a given url can be fetched via bitkeeper.
446 Expects supplied url in list form, as outputted by bb.decodeurl().
448 (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
449 return type in ['bk']
450 supports = staticmethod(supports)
455 def supports(url, d):
456 """Check to see if a given url can be fetched in the local filesystem.
457 Expects supplied url in list form, as outputted by bb.decodeurl().
459 (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
460 return type in ['file','patch']
461 supports = staticmethod(supports)
463 def localpath(url, d):
464 """Return the local filename of a given url assuming a successful fetch.
466 path = url.split("://")[1]
469 filespath = data.getVar('FILESPATH', d, 1)
471 newpath = bb.which(filespath, path)
473 filesdir = data.getVar('FILESDIR', d, 1)
475 newpath = os.path.join(filesdir, path)
477 localpath = staticmethod(localpath)
479 def go(self, urls = []):
480 """Fetch urls (no-op for Local method)"""
481 # no need to fetch local files, we'll deal with them in place.
484 methods.append(Local())
487 """Class to fetch a module or modules from svn repositories"""
488 def supports(url, d):
489 """Check to see if a given url can be fetched with svn.
490 Expects supplied url in list form, as outputted by bb.decodeurl().
492 (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
493 return type in ['svn']
494 supports = staticmethod(supports)
496 def localpath(url, d):
497 (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
498 if "localpath" in parm:
499 # if user overrides local path, use it.
500 return parm["localpath"]
502 if not "module" in parm:
503 raise MissingParameterError("svn method needs a 'module' parameter")
505 module = parm["module"]
507 revision = parm['rev']
511 date = data.getVar("CVSDATE", d, 1) or data.getVar("DATE", d, 1)
513 return os.path.join(data.getVar("DL_DIR", d, 1),data.expand('%s_%s_%s_%s.tar.gz' % ( module.replace('/', '.'), host, revision, date), d))
514 localpath = staticmethod(localpath)
516 def go(self, d, urls = []):
521 localdata = data.createCopy(d)
522 data.setVar('OVERRIDES', "svn:%s" % data.getVar('OVERRIDES', localdata), localdata)
523 data.update_data(localdata)
526 (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(loc, localdata))
527 if not "module" in parm:
528 raise MissingParameterError("svn method needs a 'module' parameter")
530 module = parm["module"]
532 dlfile = self.localpath(loc, localdata)
533 dldir = data.getVar('DL_DIR', localdata, 1)
534 # if local path contains the svn
535 # module, consider the dir above it to be the
537 # pos = dlfile.find(module)
539 # dldir = dlfile[:pos]
541 # dldir = os.path.dirname(dlfile)
546 revision = parm['rev']
550 date = data.getVar("CVSDATE", d, 1) or data.getVar("DATE", d, 1)
553 method = parm["method"]
558 proto = parm["proto"]
565 svn_rsh = parm["rsh"]
567 tarfn = data.expand('%s_%s_%s_%s.tar.gz' % (module.replace('/', '.'), host, revision, date), localdata)
568 data.setVar('TARFILES', dlfile, localdata)
569 data.setVar('TARFN', tarfn, localdata)
571 dl = os.path.join(dldir, tarfn)
572 if os.access(dl, os.R_OK):
573 bb.debug(1, "%s already exists, skipping svn checkout." % tarfn)
576 svn_tarball_stash = data.getVar('CVS_TARBALL_STASH', d, 1)
577 if svn_tarball_stash:
578 fetchcmd = data.getVar("FETCHCOMMAND_wget", d, 1)
579 uri = svn_tarball_stash + tarfn
580 bb.note("fetch " + uri)
581 fetchcmd = fetchcmd.replace("${URI}", uri)
582 ret = os.system(fetchcmd)
584 bb.note("Fetched %s from tarball stash, skipping checkout" % tarfn)
587 olddir = os.path.abspath(os.getcwd())
588 os.chdir(data.expand(dldir, localdata))
591 # svnroot = ":" + method + ":" + user
593 # svnroot += ":" + pswd
594 svnroot = host + path
596 data.setVar('SVNROOT', svnroot, localdata)
597 data.setVar('SVNCOOPTS', " ".join(options), localdata)
598 data.setVar('SVNMODULE', module, localdata)
599 svncmd = data.getVar('FETCHCOMMAND', localdata, 1)
600 svncmd = "svn co %s://%s/%s" % (proto, svnroot, module)
603 svncmd = "svn co -r %s %s://%s/%s" % (revision, proto, svnroot, module)
605 svncmd = "svn_RSH=\"%s\" %s" % (svn_rsh, svncmd)
607 # create temp directory
608 bb.debug(2, "Fetch: creating temporary directory")
609 bb.mkdirhier(data.expand('${WORKDIR}', localdata))
610 data.setVar('TMPBASE', data.expand('${WORKDIR}/oesvn.XXXXXX', localdata), localdata)
611 tmppipe = os.popen(data.getVar('MKTEMPDIRCMD', localdata, 1) or "false")
612 tmpfile = tmppipe.readline().strip()
614 bb.error("Fetch: unable to create temporary directory.. make sure 'mktemp' is in the PATH.")
615 raise FetchError(module)
617 # check out sources there
619 bb.note("Fetch " + loc)
620 bb.debug(1, "Running %s" % svncmd)
621 myret = os.system(svncmd)
627 raise FetchError(module)
629 os.chdir(os.path.join(tmpfile, os.path.dirname(module)))
630 # tar them up to a defined filename
631 myret = os.system("tar -czf %s %s" % (os.path.join(dldir,tarfn), os.path.basename(module)))
638 os.system('rm -rf %s' % tmpfile)
642 methods.append(Svn())