fetchers: Only call createCopy and update_data after checking if the download already...
[bitbake.git] / lib / bb / fetch / __init__.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 class ParameterError(Exception):
42     """Exception raised when a url cannot be proccessed due to invalid parameters."""
43
44 class MD5SumError(Exception):
45     """Exception raised when a MD5SUM of a file does not match the expected one"""
46
47 def uri_replace(uri, uri_find, uri_replace, d):
48 #   bb.msg.note(1, bb.msg.domain.Fetcher, "uri_replace: operating on %s" % uri)
49     if not uri or not uri_find or not uri_replace:
50         bb.msg.debug(1, bb.msg.domain.Fetcher, "uri_replace: passed an undefined value, not replacing")
51     uri_decoded = list(bb.decodeurl(uri))
52     uri_find_decoded = list(bb.decodeurl(uri_find))
53     uri_replace_decoded = list(bb.decodeurl(uri_replace))
54     result_decoded = ['','','','','',{}]
55     for i in uri_find_decoded:
56         loc = uri_find_decoded.index(i)
57         result_decoded[loc] = uri_decoded[loc]
58         import types
59         if type(i) == types.StringType:
60             import re
61             if (re.match(i, uri_decoded[loc])):
62                 result_decoded[loc] = re.sub(i, uri_replace_decoded[loc], uri_decoded[loc])
63                 if uri_find_decoded.index(i) == 2:
64                     if d:
65                         localfn = bb.fetch.localpath(uri, d)
66                         if localfn:
67                             result_decoded[loc] = os.path.dirname(result_decoded[loc]) + "/" + os.path.basename(bb.fetch.localpath(uri, d))
68 #                       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]))
69             else:
70 #               bb.msg.note(1, bb.msg.domain.Fetcher, "uri_replace: no match")
71                 return uri
72 #           else:
73 #               for j in i.keys():
74 #                   FIXME: apply replacements against options
75     return bb.encodeurl(result_decoded)
76
77 methods = []
78 urldata = {}
79
80 def init(urls = [], d = None):
81     if d == None:
82         bb.msg.debug(2, bb.msg.domain.Fetcher, "BUG init called with None as data object!!!")
83         return
84
85     for m in methods:
86         m.urls = []
87
88     for u in urls:
89         ud = initdata(u, d)
90         if ud.method:
91             ud.method.urls.append(u)
92
93 def initdata(url, d):
94     if url not in urldata:
95         ud = FetchData()
96         (ud.type, ud.host, ud.path, ud.user, ud.pswd, ud.parm) = bb.decodeurl(data.expand(url, d))
97         ud.date = Fetch.getSRCDate(d)
98         for m in methods:
99             if m.supports(url, ud, d):
100                 ud.localpath = m.localpath(url, ud, d)
101                 ud.md5 = ud.localpath + '.md5'
102                 # if user sets localpath for file, use it instead.
103                 if "localpath" in ud.parm:
104                     ud.localpath = ud.parm["localpath"]
105                 ud.method = m
106                 break
107         urldata[url] = ud
108     return urldata[url]
109
110 def go(d):
111     """Fetch all urls"""
112     for m in methods:
113         for u in m.urls:
114             # RP - is olddir needed?
115             olddir = os.path.abspath(os.getcwd())
116             m.go(u, urldata[u], d)
117             os.chdir(olddir)
118
119 def localpaths(d):
120     """Return a list of the local filenames, assuming successful fetch"""
121     local = []
122     for m in methods:
123         for u in m.urls:
124             local.append(urldata[u].localpath)
125     return local
126
127 def localpath(url, d):
128     ud = initdata(url, d)
129     if ud.method:
130         return ud.localpath
131     return url
132
133 class FetchData(object):
134     """Class for fetcher variable store"""
135
136 class Fetch(object):
137     """Base class for 'fetch'ing data"""
138
139     def __init__(self, urls = []):
140         self.urls = []
141
142     def supports(self, url, urldata, d):
143         """
144         Check to see if this fetch class supports a given url.
145         """
146         return 0
147
148     def localpath(self, url, urldata, d):
149         """
150         Return the local filename of a given url assuming a successful fetch.
151         Can also setup variables in urldata for use in go (saving code duplication 
152         and duplicate code execution)
153         """
154         return url
155
156     def setUrls(self, urls):
157         self.__urls = urls
158
159     def getUrls(self):
160         return self.__urls
161
162     urls = property(getUrls, setUrls, None, "Urls property")
163
164     def go(self, url, urldata, d):
165         """
166         Fetch urls
167         Assumes localpath was called first
168         """
169         raise NoMethodError("Missing implementation for url")
170
171     def getSRCDate(d):
172         """
173         Return the SRC Date for the component
174
175         d the bb.data module
176         """
177         return data.getVar("SRCDATE", d, 1) or data.getVar("CVSDATE", d, 1) or data.getVar("DATE", d, 1 )
178     getSRCDate = staticmethod(getSRCDate)
179
180     def try_mirror(d, tarfn):
181         """
182         Try to use a mirrored version of the sources. We do this
183         to avoid massive loads on foreign cvs and svn servers.
184         This method will be used by the different fetcher
185         implementations.
186
187         d Is a bb.data instance
188         tarfn is the name of the tarball
189         """
190         tarpath = os.path.join(data.getVar("DL_DIR", d, 1), tarfn)
191         if os.access(tarpath, os.R_OK):
192             bb.msg.debug(1, bb.msg.domain.Fetcher, "%s already exists, skipping checkout." % tarfn)
193             return True
194
195         pn = data.getVar('PN', d, True)
196         src_tarball_stash = None
197         if pn:
198             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()
199
200         for stash in src_tarball_stash:
201             fetchcmd = data.getVar("FETCHCOMMAND_mirror", d, True) or data.getVar("FETCHCOMMAND_wget", d, True)
202             uri = stash + tarfn
203             bb.msg.note(1, bb.msg.domain.Fetcher, "fetch " + uri)
204             fetchcmd = fetchcmd.replace("${URI}", uri)
205             ret = os.system(fetchcmd)
206             if ret == 0:
207                 bb.msg.note(1, bb.msg.domain.Fetcher, "Fetched %s from tarball stash, skipping checkout" % tarfn)
208                 return True
209         return False
210     try_mirror = staticmethod(try_mirror)
211
212 import cvs
213 import git
214 import local
215 import svn
216 import wget
217 import svk
218 import ssh
219
220 methods.append(cvs.Cvs())
221 methods.append(git.Git())
222 methods.append(local.Local())
223 methods.append(svn.Svn())
224 methods.append(wget.Wget())
225 methods.append(svk.Svk())
226 methods.append(ssh.SSH())