blob: d978630baca9303323092c8b389e4861b5f7cccc [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3"""
4BitBake 'Fetch' implementation for mercurial DRCS (hg).
5
6"""
7
8# Copyright (C) 2003, 2004 Chris Larson
9# Copyright (C) 2004 Marcin Juszkiewicz
10# Copyright (C) 2007 Robert Schuster
11#
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.
15#
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.
20#
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.
24#
25# Based on functions from the base bb module, Copyright 2003 Holger Schurig
26
27import os
28import sys
29import logging
30import bb
31from bb import data
32from bb.fetch2 import FetchMethod
33from bb.fetch2 import FetchError
34from bb.fetch2 import MissingParameterError
35from bb.fetch2 import runfetchcmd
36from bb.fetch2 import logger
37
38class Hg(FetchMethod):
39 """Class to fetch from mercurial repositories"""
40 def supports(self, ud, d):
41 """
42 Check to see if a given url can be fetched with mercurial.
43 """
44 return ud.type in ['hg']
45
46 def supports_checksum(self, urldata):
47 """
48 Don't require checksums for local archives created from
49 repository checkouts.
50 """
51 return False
52
53 def urldata_init(self, ud, d):
54 """
55 init hg specific variable within url data
56 """
57 if not "module" in ud.parm:
58 raise MissingParameterError('module', ud.url)
59
60 ud.module = ud.parm["module"]
61
62 if 'protocol' in ud.parm:
63 ud.proto = ud.parm['protocol']
64 elif not ud.host:
65 ud.proto = 'file'
66 else:
67 ud.proto = "hg"
68
69 ud.setup_revisons(d)
70
71 if 'rev' in ud.parm:
72 ud.revision = ud.parm['rev']
73 elif not ud.revision:
74 ud.revision = self.latest_revision(ud, d)
75
76 # Create paths to mercurial checkouts
77 hgsrcname = '%s_%s_%s' % (ud.module.replace('/', '.'), \
78 ud.host, ud.path.replace('/', '.'))
79 ud.mirrortarball = 'hg_%s.tar.gz' % hgsrcname
80 ud.fullmirror = os.path.join(d.getVar("DL_DIR", True), ud.mirrortarball)
81
82 hgdir = d.getVar("HGDIR", True) or (d.getVar("DL_DIR", True) + "/hg/")
83 ud.pkgdir = os.path.join(hgdir, hgsrcname)
84 ud.moddir = os.path.join(ud.pkgdir, ud.module)
85 ud.localfile = ud.moddir
86 ud.basecmd = data.getVar("FETCHCMD_hg", d, True) or "/usr/bin/env hg"
87
88 ud.write_tarballs = d.getVar("BB_GENERATE_MIRROR_TARBALLS", True)
89
90 def need_update(self, ud, d):
91 revTag = ud.parm.get('rev', 'tip')
92 if revTag == "tip":
93 return True
94 if not os.path.exists(ud.localpath):
95 return True
96 return False
97
98 def try_premirror(self, ud, d):
99 # If we don't do this, updating an existing checkout with only premirrors
100 # is not possible
101 if d.getVar("BB_FETCH_PREMIRRORONLY", True) is not None:
102 return True
103 if os.path.exists(ud.moddir):
104 return False
105 return True
106
107 def _buildhgcommand(self, ud, d, command):
108 """
109 Build up an hg commandline based on ud
110 command is "fetch", "update", "info"
111 """
112
113 proto = ud.parm.get('protocol', 'http')
114
115 host = ud.host
116 if proto == "file":
117 host = "/"
118 ud.host = "localhost"
119
120 if not ud.user:
121 hgroot = host + ud.path
122 else:
123 if ud.pswd:
124 hgroot = ud.user + ":" + ud.pswd + "@" + host + ud.path
125 else:
126 hgroot = ud.user + "@" + host + ud.path
127
128 if command == "info":
129 return "%s identify -i %s://%s/%s" % (ud.basecmd, proto, hgroot, ud.module)
130
131 options = [];
132
133 # Don't specify revision for the fetch; clone the entire repo.
134 # This avoids an issue if the specified revision is a tag, because
135 # the tag actually exists in the specified revision + 1, so it won't
136 # be available when used in any successive commands.
137 if ud.revision and command != "fetch":
138 options.append("-r %s" % ud.revision)
139
140 if command == "fetch":
141 if ud.user and ud.pswd:
142 cmd = "%s --config auth.default.prefix=* --config auth.default.username=%s --config auth.default.password=%s --config \"auth.default.schemes=%s\" clone %s %s://%s/%s %s" % (ud.basecmd, ud.user, ud.pswd, proto, " ".join(options), proto, hgroot, ud.module, ud.module)
143 else:
144 cmd = "%s clone %s %s://%s/%s %s" % (ud.basecmd, " ".join(options), proto, hgroot, ud.module, ud.module)
145 elif command == "pull":
146 # do not pass options list; limiting pull to rev causes the local
147 # repo not to contain it and immediately following "update" command
148 # will crash
149 if ud.user and ud.pswd:
150 cmd = "%s --config auth.default.prefix=* --config auth.default.username=%s --config auth.default.password=%s --config \"auth.default.schemes=%s\" pull" % (ud.basecmd, ud.user, ud.pswd, proto)
151 else:
152 cmd = "%s pull" % (ud.basecmd)
153 elif command == "update":
154 if ud.user and ud.pswd:
155 cmd = "%s --config auth.default.prefix=* --config auth.default.username=%s --config auth.default.password=%s --config \"auth.default.schemes=%s\" update -C %s" % (ud.basecmd, ud.user, ud.pswd, proto, " ".join(options))
156 else:
157 cmd = "%s update -C %s" % (ud.basecmd, " ".join(options))
158 else:
159 raise FetchError("Invalid hg command %s" % command, ud.url)
160
161 return cmd
162
163 def download(self, ud, d):
164 """Fetch url"""
165
166 ud.repochanged = not os.path.exists(ud.fullmirror)
167
168 logger.debug(2, "Fetch: checking for module directory '" + ud.moddir + "'")
169
170 # If the checkout doesn't exist and the mirror tarball does, extract it
171 if not os.path.exists(ud.pkgdir) and os.path.exists(ud.fullmirror):
172 bb.utils.mkdirhier(ud.pkgdir)
173 os.chdir(ud.pkgdir)
174 runfetchcmd("tar -xzf %s" % (ud.fullmirror), d)
175
176 if os.access(os.path.join(ud.moddir, '.hg'), os.R_OK):
177 # Found the source, check whether need pull
178 updatecmd = self._buildhgcommand(ud, d, "update")
179 os.chdir(ud.moddir)
180 logger.debug(1, "Running %s", updatecmd)
181 try:
182 runfetchcmd(updatecmd, d)
183 except bb.fetch2.FetchError:
184 # Runnning pull in the repo
185 pullcmd = self._buildhgcommand(ud, d, "pull")
186 logger.info("Pulling " + ud.url)
187 # update sources there
188 os.chdir(ud.moddir)
189 logger.debug(1, "Running %s", pullcmd)
190 bb.fetch2.check_network_access(d, pullcmd, ud.url)
191 runfetchcmd(pullcmd, d)
192 ud.repochanged = True
193
194 # No source found, clone it.
195 if not os.path.exists(ud.moddir):
196 fetchcmd = self._buildhgcommand(ud, d, "fetch")
197 logger.info("Fetch " + ud.url)
198 # check out sources there
199 bb.utils.mkdirhier(ud.pkgdir)
200 os.chdir(ud.pkgdir)
201 logger.debug(1, "Running %s", fetchcmd)
202 bb.fetch2.check_network_access(d, fetchcmd, ud.url)
203 runfetchcmd(fetchcmd, d)
204
205 # Even when we clone (fetch), we still need to update as hg's clone
206 # won't checkout the specified revision if its on a branch
207 updatecmd = self._buildhgcommand(ud, d, "update")
208 os.chdir(ud.moddir)
209 logger.debug(1, "Running %s", updatecmd)
210 runfetchcmd(updatecmd, d)
211
212 def clean(self, ud, d):
213 """ Clean the hg dir """
214
215 bb.utils.remove(ud.localpath, True)
216 bb.utils.remove(ud.fullmirror)
217 bb.utils.remove(ud.fullmirror + ".done")
218
219 def supports_srcrev(self):
220 return True
221
222 def _latest_revision(self, ud, d, name):
223 """
224 Compute tip revision for the url
225 """
226 bb.fetch2.check_network_access(d, self._buildhgcommand(ud, d, "info"))
227 output = runfetchcmd(self._buildhgcommand(ud, d, "info"), d)
228 return output.strip()
229
230 def _build_revision(self, ud, d, name):
231 return ud.revision
232
233 def _revision_key(self, ud, d, name):
234 """
235 Return a unique key for the url
236 """
237 return "hg:" + ud.moddir
238
239 def build_mirror_data(self, ud, d):
240 # Generate a mirror tarball if needed
241 if ud.write_tarballs == "1" and (ud.repochanged or not os.path.exists(ud.fullmirror)):
242 # it's possible that this symlink points to read-only filesystem with PREMIRROR
243 if os.path.islink(ud.fullmirror):
244 os.unlink(ud.fullmirror)
245
246 os.chdir(ud.pkgdir)
247 logger.info("Creating tarball of hg repository")
248 runfetchcmd("tar -czf %s %s" % (ud.fullmirror, ud.module), d)
249 runfetchcmd("touch %s.done" % (ud.fullmirror), d)
250
251 def localpath(self, ud, d):
252 return ud.pkgdir
253
254 def unpack(self, ud, destdir, d):
255 """
256 Make a local clone or export for the url
257 """
258
259 revflag = "-r %s" % ud.revision
260 subdir = ud.parm.get("destsuffix", ud.module)
261 codir = "%s/%s" % (destdir, subdir)
262
263 scmdata = ud.parm.get("scmdata", "")
264 if scmdata != "nokeep":
265 if not os.access(os.path.join(codir, '.hg'), os.R_OK):
266 logger.debug(2, "Unpack: creating new hg repository in '" + codir + "'")
267 runfetchcmd("%s init %s" % (ud.basecmd, codir), d)
268 logger.debug(2, "Unpack: updating source in '" + codir + "'")
269 os.chdir(codir)
270 runfetchcmd("%s pull %s" % (ud.basecmd, ud.moddir), d)
271 runfetchcmd("%s up -C %s" % (ud.basecmd, revflag), d)
272 else:
273 logger.debug(2, "Unpack: extracting source to '" + codir + "'")
274 os.chdir(ud.moddir)
275 runfetchcmd("%s archive -t files %s %s" % (ud.basecmd, revflag, codir), d)