tbake/lib/bb/fetch.py:
[vuplus_bitbake] / lib / bb / fetch.py
1 #!/usr/bin/env python
2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4 """
5 BitBake 'Fetch' implementations
6
7 Classes for obtaining upstream sources for the
8 BitBake build tools.
9
10 Copyright (C) 2003, 2004  Chris Larson
11
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
15 version.
16
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.
20
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. 
24
25 Based on functions from the base bb module, Copyright 2003 Holger Schurig
26 """
27
28 import os, re
29 import bb
30 from   bb import data
31
32 class FetchError(Exception):
33     """Exception raised when a download fails"""
34
35 class NoMethodError(Exception):
36     """Exception raised when there is no method to obtain a supplied url or set of urls"""
37
38 class MissingParameterError(Exception):
39     """Exception raised when a fetch method is missing a critical parameter in the url"""
40
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'})
43
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]
55         import types
56         if type(i) == types.StringType:
57             import re
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:
61                     if d:
62                         localfn = bb.fetch.localpath(uri, d)
63                         if localfn:
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]))
66             else:
67 #               bb.note("uri_replace: no match")
68                 return uri
69 #           else:
70 #               for j in i.keys():
71 #                   FIXME: apply replacements against options
72     return bb.encodeurl(result_decoded)
73
74 methods = []
75
76 def init(urls = [], d = None):
77     for m in methods:
78         m.urls = []
79
80     for u in urls:
81         for m in methods:
82             m.data = d
83             if m.supports(u, d):
84                 m.urls.append(u)
85
86 def go(d):
87     """Fetch all urls"""
88     for m in methods:
89         if m.urls:
90             m.go(d)
91
92 def localpaths(d):
93     """Return a list of the local filenames, assuming successful fetch"""
94     local = []
95     for m in methods:
96         for u in m.urls:
97             local.append(m.localpath(u, d))
98     return local
99
100 def localpath(url, d):
101     for m in methods:
102         if m.supports(url, d):
103             return m.localpath(url, d)
104     return url
105
106 class Fetch(object):
107     """Base class for 'fetch'ing data"""
108
109     def __init__(self, urls = []):
110         self.urls = []
111         for url in urls:
112             if self.supports(bb.decodeurl(url), d) is 1:
113                 self.urls.append(url)
114
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().
118         """
119         return 0
120     supports = staticmethod(supports)
121
122     def localpath(url, d):
123         """Return the local filename of a given url assuming a successful fetch.
124         """
125         return url
126     localpath = staticmethod(localpath)
127
128     def setUrls(self, urls):
129         self.__urls = urls
130
131     def getUrls(self):
132         return self.__urls
133
134     urls = property(getUrls, setUrls, None, "Urls property")
135
136     def setData(self, data):
137         self.__data = data
138
139     def getData(self):
140         return self.__data
141
142     data = property(getData, setData, None, "Data property")
143
144     def go(self, urls = []):
145         """Fetch urls"""
146         raise NoMethodError("Missing implementation for url")
147
148 class Wget(Fetch):
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().
153         """
154         (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
155         return type in ['http','https','ftp']
156     supports = staticmethod(supports)
157
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, {}])
165
166         return os.path.join(data.getVar("DL_DIR", d), os.path.basename(url))
167     localpath = staticmethod(localpath)
168
169     def go(self, d, urls = []):
170         """Fetch 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)
175             else:
176                 fetchcmd = data.getVar("FETCHCOMMAND", d, 1)
177
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)
183             if ret != 0:
184                 return False
185
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)
191                 return False
192
193 #           supposedly complete.. write out md5sum
194             if bb.which(data.getVar('PATH', d), 'md5sum'):
195                 try:
196                     md5pipe = os.popen('md5sum ' + dl)
197                     md5data = (md5pipe.readline().split() or [ "" ])[0]
198                     md5pipe.close()
199                 except OSError:
200                     md5data = ""
201                 md5out = file(md5, 'w')
202                 md5out.write(md5data)
203                 md5out.close()
204             else:
205                 md5out = file(md5, 'w')
206                 md5out.write("")
207                 md5out.close()
208             return True
209
210         if not urls:
211             urls = self.urls
212
213         localdata = data.createCopy(d)
214         data.setVar('OVERRIDES', "wget:" + data.getVar('OVERRIDES', localdata), localdata)
215         data.update_data(localdata)
216
217         for uri in urls:
218             completed = 0
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)
223             md5 = dl + '.md5'
224
225             if os.path.exists(md5):
226 #               complete, nothing to see here..
227                 continue
228
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)
232                 if newuri != uri:
233                     if fetch_uri(newuri, basename, dl, md5, localdata):
234                         completed = 1
235                         break
236
237             if completed:
238                 continue
239
240             if fetch_uri(uri, basename, dl, md5, localdata):
241                 continue
242
243 #           try mirrors
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)
247                 if newuri != uri:
248                     if fetch_uri(newuri, basename, dl, md5, localdata):
249                         completed = 1
250                         break
251
252             if not completed:
253                 raise FetchError(uri)
254
255         del localdata
256
257
258 methods.append(Wget())
259
260 class Cvs(Fetch):
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().
265         """
266         (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
267         return type in ['cvs', 'pserver']
268     supports = staticmethod(supports)
269
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"]
275
276         if not "module" in parm:
277             raise MissingParameterError("cvs method needs a 'module' parameter")
278         else:
279             module = parm["module"]
280         if 'tag' in parm:
281             tag = parm['tag']
282         else:
283             tag = ""
284         if 'date' in parm:
285             date = parm['date']
286         else:
287             if not tag:
288                 date = data.getVar("CVSDATE", d, 1) or data.getVar("DATE", d, 1)
289             else:
290                 date = ""
291
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)
294
295     def go(self, d, urls = []):
296         """Fetch urls"""
297         if not urls:
298             urls = self.urls
299
300         localdata = data.createCopy(d)
301         data.setVar('OVERRIDES', "cvs:%s" % data.getVar('OVERRIDES', localdata), localdata)
302         data.update_data(localdata)
303
304         for loc in urls:
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")
308             else:
309                 module = parm["module"]
310
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
315 #           download directory
316 #           pos = dlfile.find(module)
317 #           if pos:
318 #               dldir = dlfile[:pos]
319 #           else:
320 #               dldir = os.path.dirname(dlfile)
321
322 #           setup cvs options
323             options = []
324             if 'tag' in parm:
325                 tag = parm['tag']
326             else:
327                 tag = ""
328
329             if 'date' in parm:
330                 date = parm['date']
331             else:
332                 if not tag:
333                     date = data.getVar("CVSDATE", d, 1) or data.getVar("DATE", d, 1)
334                 else:
335                     date = ""
336
337             if "method" in parm:
338                 method = parm["method"]
339             else:
340                 method = "pserver"
341
342             if "localdir" in parm:
343                 localdir = parm["localdir"]
344             else:
345                 localdir = module
346
347             cvs_rsh = None
348             if method == "ext":
349                 if "rsh" in parm:
350                     cvs_rsh = parm["rsh"]
351
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)
355
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)
359                 continue
360
361             pn = data.getVar('PN', d, 1)
362             cvs_tarball_stash = None
363             if pn:
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)
373                 if ret == 0:
374                     bb.note("Fetched %s from tarball stash, skipping checkout" % tarfn)
375                     continue
376
377             if date:
378                 options.append("-D %s" % date)
379             if tag:
380                 options.append("-r %s" % tag)
381
382             olddir = os.path.abspath(os.getcwd())
383             os.chdir(data.expand(dldir, localdata))
384
385 #           setup cvsroot
386             if method == "dir":
387                 cvsroot = path
388             else:
389                 cvsroot = ":" + method + ":" + user
390                 if pswd:
391                     cvsroot += ":" + pswd
392                 cvsroot += "@" + host + ":" + path
393
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)
399
400             if cvs_rsh:
401                 cvscmd = "CVS_RSH=\"%s\" %s" % (cvs_rsh, cvscmd)
402                 cvsupdatecmd = "CVS_RSH=\"%s\" %s" % (cvs_rsh, cvsupdatecmd)
403
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
412                 os.chdir(moddir)
413                 myret = os.system(cvsupdatecmd)
414             else:
415                 bb.note("Fetch " + loc)
416 #               check out sources there
417                 bb.mkdirhier(pkgdir)
418                 os.chdir(pkgdir)
419                 bb.debug(1, "Running %s" % cvscmd)
420                 myret = os.system(cvscmd)
421
422             if myret != 0:
423                 try:
424                     os.rmdir(moddir)
425                 except OSError:
426                     pass
427                 raise FetchError(module)
428
429             os.chdir(moddir)
430             os.chdir('..')
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)))
433             if myret != 0:
434                 try:
435                     os.unlink(tarfn)
436                 except OSError:
437                     pass
438             os.chdir(olddir)
439         del localdata
440
441 methods.append(Cvs())
442
443 class Bk(Fetch):
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().
447         """
448         (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
449         return type in ['bk']
450     supports = staticmethod(supports)
451
452 methods.append(Bk())
453
454 class Local(Fetch):
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().
458         """
459         (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
460         return type in ['file','patch']
461     supports = staticmethod(supports)
462
463     def localpath(url, d):
464         """Return the local filename of a given url assuming a successful fetch.
465         """
466         path = url.split("://")[1]
467         newpath = path
468         if path[0] != "/":
469             filespath = data.getVar('FILESPATH', d, 1)
470             if filespath:
471                 newpath = bb.which(filespath, path)
472             if not newpath:
473                 filesdir = data.getVar('FILESDIR', d, 1)
474                 if filesdir:
475                     newpath = os.path.join(filesdir, path)
476         return newpath
477     localpath = staticmethod(localpath)
478
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.
482         return 1
483
484 methods.append(Local())
485
486 class Svn(Fetch):
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().
491         """
492         (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
493         return type in ['svn']
494     supports = staticmethod(supports)
495
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"]
501
502         if not "module" in parm:
503             raise MissingParameterError("svn method needs a 'module' parameter")
504         else:
505             module = parm["module"]
506         if 'rev' in parm:
507             revision = parm['rev']
508         else:
509             revision = ""
510
511         date = data.getVar("CVSDATE", d, 1) or data.getVar("DATE", d, 1)
512
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)
515
516     def go(self, d, urls = []):
517         """Fetch urls"""
518         if not urls:
519             urls = self.urls
520
521         localdata = data.createCopy(d)
522         data.setVar('OVERRIDES', "svn:%s" % data.getVar('OVERRIDES', localdata), localdata)
523         data.update_data(localdata)
524
525         for loc in urls:
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")
529             else:
530                 module = parm["module"]
531
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
536 #           download directory
537 #           pos = dlfile.find(module)
538 #           if pos:
539 #               dldir = dlfile[:pos]
540 #           else:
541 #               dldir = os.path.dirname(dlfile)
542
543 #           setup svn options
544             options = []
545             if 'rev' in parm:
546                 revision = parm['rev']
547             else:
548                 revision = ""
549
550             date = data.getVar("CVSDATE", d, 1) or data.getVar("DATE", d, 1)
551
552             if "method" in parm:
553                 method = parm["method"]
554             else:
555                 method = "pserver"
556
557             if "proto" in parm:
558                 proto = parm["proto"]
559             else:
560                 proto = "svn"
561
562             svn_rsh = None
563             if method == "ext":
564                 if "rsh" in parm:
565                     svn_rsh = parm["rsh"]
566
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)
570
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)
574                 continue
575
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)
583                 if ret == 0:
584                     bb.note("Fetched %s from tarball stash, skipping checkout" % tarfn)
585                     continue
586
587             olddir = os.path.abspath(os.getcwd())
588             os.chdir(data.expand(dldir, localdata))
589
590 #           setup svnroot
591 #            svnroot = ":" + method + ":" + user
592 #            if pswd:
593 #                svnroot += ":" + pswd
594             svnroot = host + path
595
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)
601
602             if revision:
603                 svncmd = "svn co -r %s %s://%s/%s" % (revision, proto, svnroot, module)
604             if svn_rsh:
605                 svncmd = "svn_RSH=\"%s\" %s" % (svn_rsh, svncmd)
606
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()
613             if not tmpfile:
614                 bb.error("Fetch: unable to create temporary directory.. make sure 'mktemp' is in the PATH.")
615                 raise FetchError(module)
616
617 #           check out sources there
618             os.chdir(tmpfile)
619             bb.note("Fetch " + loc)
620             bb.debug(1, "Running %s" % svncmd)
621             myret = os.system(svncmd)
622             if myret != 0:
623                 try:
624                     os.rmdir(tmpfile)
625                 except OSError:
626                     pass
627                 raise FetchError(module)
628
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)))
632             if myret != 0:
633                 try:
634                     os.unlink(tarfn)
635                 except OSError:
636                     pass
637 #           cleanup
638             os.system('rm -rf %s' % tmpfile)
639             os.chdir(olddir)
640         del localdata
641
642 methods.append(Svn())