cb97dfc653efa99ab3e5bce7bb7cd30eb4e8b717
[vuplus_bitbake] / lib / bb / fetch / git.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' git implementation
6
7 Copyright (C) 2005 Richard Purdie
8
9 This program is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free Software
11 Foundation; either version 2 of the License, or (at your option) any later
12 version.
13
14 This program is distributed in the hope that it will be useful, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License along with
19 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
20 Place, Suite 330, Boston, MA 02111-1307 USA. 
21 """
22
23 import os, re
24 import bb
25 from   bb    import data
26 from   bb.fetch import Fetch
27 from   bb.fetch import FetchError
28
29 def prunedir(topdir):
30     # Delete everything reachable from the directory named in 'topdir'.
31     # CAUTION:  This is dangerous!
32     for root, dirs, files in os.walk(topdir, topdown=False):
33         for name in files:
34             os.remove(os.path.join(root, name))
35         for name in dirs:
36             os.rmdir(os.path.join(root, name))
37
38 def runfetchcmd(cmd, d, quiet = False):
39
40     bb.debug(1, "Running %s" % cmd)
41
42     # Need to export PATH as git is likely to be in metadata paths 
43     # rather than host provided
44     cmd = 'export PATH=%s; %s' % (data.expand('${PATH}', d), cmd)
45
46     # redirect stderr to stdout
47     stdout_handle = os.popen(cmd + " 2>&1", "r")
48     output = ""
49
50     while 1:
51         line = stdout_handle.readline()
52         if not line:
53             break
54         if not quiet:
55             print line
56         output += line
57
58     status =  stdout_handle.close() or 0
59     signal = status >> 8
60     exitstatus = status & 0xff
61
62     if signal:
63         raise FetchError("Fetch command %s failed with signal %s, output:\n%s" % (cmd, signal, output))
64     elif status != 0:
65         raise FetchError("Fetch command %s failed with exit code %s, output:\n%s" % (cmd, status, output))
66
67     return output
68
69 def contains_ref(tag, d):
70     output = runfetchcmd("git log --pretty=oneline -n 1 %s -- 2> /dev/null | wc -l" % tag, d, True)
71     return output.split()[0] != "0"
72
73 def latest_revision(proto, user, host, path, branch, d):
74     """
75     Compute the HEAD revision for the url
76     """
77     if user:
78         username = user + '@'
79     else:
80         username = ""
81
82     output = runfetchcmd("git ls-remote %s://%s%s%s %s" % (proto, username, host, path, branch), d, True)
83     return output.split()[0]
84
85 def getbranch(parm):
86     if 'branch' in parm:
87         branch = parm['branch']
88     else:
89         branch = "master"
90     if not branch:
91         branch = "master"
92
93     return branch
94
95 def gettag(parm, proto, user, host, path, branch, d):
96     if 'tag' in parm:
97         tag = parm['tag']
98     else:
99         tag = latest_revision(proto, user, host, path, branch, d)
100     if not tag or tag == "master":
101         tag = latest_revision(proto, user, host, path, branch, d)
102
103     return tag
104
105 def getprotocol(parm):
106     if 'protocol' in parm:
107         proto = parm['protocol']
108     else:
109         proto = ""
110     if not proto:
111         proto = "rsync"
112
113     return proto
114
115 def localfile(url, d):
116     """Return the filename to cache the checkout in"""
117     (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
118
119     #if user sets localpath for file, use it instead.
120     if "localpath" in parm:
121         return parm["localpath"]
122
123     proto = getprotocol(parm)
124     branch = getbranch(parm)
125     tag = gettag(parm, proto, user, host, path, branch, d)
126
127     return data.expand('git_%s%s_%s.tar.gz' % (host, path.replace('/', '.'), tag), d)
128
129 class Git(Fetch):
130     """Class to fetch a module or modules from git repositories"""
131     def supports(url, d):
132         """Check to see if a given url can be fetched with cvs.
133            Expects supplied url in list form, as outputted by bb.decodeurl().
134         """
135         (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(url, d))
136         return type in ['git']
137     supports = staticmethod(supports)
138
139     def localpath(url, d):
140
141         return os.path.join(data.getVar("DL_DIR", d, 1), localfile(url, d))
142
143     localpath = staticmethod(localpath)
144
145     def go(self, d, urls = []):
146         """Fetch urls"""
147         if not urls:
148             urls = self.urls
149
150         for loc in urls:
151             (type, host, path, user, pswd, parm) = bb.decodeurl(data.expand(loc, d))
152
153             proto = getprotocol(parm)
154             branch = getbranch(parm)
155             tag = gettag(parm, proto, user, host, path, branch, d)
156
157             if user:
158                 username = user + '@'
159             else:
160                 username = ""
161
162             gitsrcname = '%s%s' % (host, path.replace('/', '.'))
163
164             repofilename = 'git_%s.tar.gz' % (gitsrcname)
165             repofile = os.path.join(data.getVar("DL_DIR", d, 1), repofilename)
166             repodir = os.path.join(data.expand('${GITDIR}', d), gitsrcname)
167
168             coname = '%s' % (tag)
169             codir = os.path.join(repodir, coname)
170
171             if not os.path.exists(repodir):
172                 if Fetch.try_mirror(d, repofilename):    
173                     bb.mkdirhier(repodir)
174                     os.chdir(repodir)
175                     runfetchcmd("tar -xzf %s" % (repofile),d)
176                 else:
177                     runfetchcmd("git clone -n %s://%s%s%s %s" % (proto, username, host, path, repodir),d)
178
179             os.chdir(repodir)
180             # Remove all but the .git directory
181             if not contains_ref(tag, d):
182                 runfetchcmd("rm * -Rf", d)
183                 runfetchcmd("git fetch %s://%s%s%s %s" % (proto, username, host, path, branch), d)
184                 runfetchcmd("git fetch --tags %s://%s%s%s" % (proto, username, host, path), d)
185                 runfetchcmd("git prune-packed", d)
186                 runfetchcmd("git pack-redundant --all | xargs -r rm", d)
187
188             os.chdir(repodir)
189             bb.note("Creating tarball of git repository")
190             runfetchcmd("tar -czf %s %s" % (repofile, os.path.join(".", ".git", "*") ),d)
191
192             if os.path.exists(codir):
193                 prunedir(codir)
194
195             bb.mkdirhier(codir)
196             os.chdir(repodir)
197             copath = os.path.join(codir, "git", "")
198             runfetchcmd("git read-tree %s" % (tag),d)
199             runfetchcmd("git checkout-index -q -f --prefix=%s -a" % (copath),d)
200             bb.mkdirhier(os.path.join(copath, ".git", ""))
201             runfetchcmd("git log %s --max-count=1 --date=iso -- > %s.git/last_commit_info" % (tag, copath),d)
202             runfetchcmd("echo %s > %s.git/branch" % (branch, copath),d)
203
204             os.chdir(codir)
205             bb.note("Creating tarball of git checkout")
206             runfetchcmd("tar -czf %s %s" % (self.localpath(loc, d), os.path.join(".", "*") ),d)