blob: bbb4ed95dc9582a48d20076cf55e0ddeac9d64c1 [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
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500166 logger.debug(2, "Fetch: checking for module directory '" + ud.moddir + "'")
167
168 # If the checkout doesn't exist and the mirror tarball does, extract it
169 if not os.path.exists(ud.pkgdir) and os.path.exists(ud.fullmirror):
170 bb.utils.mkdirhier(ud.pkgdir)
171 os.chdir(ud.pkgdir)
172 runfetchcmd("tar -xzf %s" % (ud.fullmirror), d)
173
174 if os.access(os.path.join(ud.moddir, '.hg'), os.R_OK):
175 # Found the source, check whether need pull
176 updatecmd = self._buildhgcommand(ud, d, "update")
177 os.chdir(ud.moddir)
178 logger.debug(1, "Running %s", updatecmd)
179 try:
180 runfetchcmd(updatecmd, d)
181 except bb.fetch2.FetchError:
182 # Runnning pull in the repo
183 pullcmd = self._buildhgcommand(ud, d, "pull")
184 logger.info("Pulling " + ud.url)
185 # update sources there
186 os.chdir(ud.moddir)
187 logger.debug(1, "Running %s", pullcmd)
188 bb.fetch2.check_network_access(d, pullcmd, ud.url)
189 runfetchcmd(pullcmd, d)
Patrick Williamsd7e96312015-09-22 08:09:05 -0500190 try:
191 os.unlink(ud.fullmirror)
192 except OSError as exc:
193 if exc.errno != errno.ENOENT:
194 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500195
196 # No source found, clone it.
197 if not os.path.exists(ud.moddir):
198 fetchcmd = self._buildhgcommand(ud, d, "fetch")
199 logger.info("Fetch " + ud.url)
200 # check out sources there
201 bb.utils.mkdirhier(ud.pkgdir)
202 os.chdir(ud.pkgdir)
203 logger.debug(1, "Running %s", fetchcmd)
204 bb.fetch2.check_network_access(d, fetchcmd, ud.url)
205 runfetchcmd(fetchcmd, d)
206
207 # Even when we clone (fetch), we still need to update as hg's clone
208 # won't checkout the specified revision if its on a branch
209 updatecmd = self._buildhgcommand(ud, d, "update")
210 os.chdir(ud.moddir)
211 logger.debug(1, "Running %s", updatecmd)
212 runfetchcmd(updatecmd, d)
213
214 def clean(self, ud, d):
215 """ Clean the hg dir """
216
217 bb.utils.remove(ud.localpath, True)
218 bb.utils.remove(ud.fullmirror)
219 bb.utils.remove(ud.fullmirror + ".done")
220
221 def supports_srcrev(self):
222 return True
223
224 def _latest_revision(self, ud, d, name):
225 """
226 Compute tip revision for the url
227 """
228 bb.fetch2.check_network_access(d, self._buildhgcommand(ud, d, "info"))
229 output = runfetchcmd(self._buildhgcommand(ud, d, "info"), d)
230 return output.strip()
231
232 def _build_revision(self, ud, d, name):
233 return ud.revision
234
235 def _revision_key(self, ud, d, name):
236 """
237 Return a unique key for the url
238 """
239 return "hg:" + ud.moddir
240
241 def build_mirror_data(self, ud, d):
242 # Generate a mirror tarball if needed
Patrick Williamsd7e96312015-09-22 08:09:05 -0500243 if ud.write_tarballs == "1" and not os.path.exists(ud.fullmirror):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500244 # it's possible that this symlink points to read-only filesystem with PREMIRROR
245 if os.path.islink(ud.fullmirror):
246 os.unlink(ud.fullmirror)
247
248 os.chdir(ud.pkgdir)
249 logger.info("Creating tarball of hg repository")
250 runfetchcmd("tar -czf %s %s" % (ud.fullmirror, ud.module), d)
251 runfetchcmd("touch %s.done" % (ud.fullmirror), d)
252
253 def localpath(self, ud, d):
254 return ud.pkgdir
255
256 def unpack(self, ud, destdir, d):
257 """
258 Make a local clone or export for the url
259 """
260
261 revflag = "-r %s" % ud.revision
262 subdir = ud.parm.get("destsuffix", ud.module)
263 codir = "%s/%s" % (destdir, subdir)
264
265 scmdata = ud.parm.get("scmdata", "")
266 if scmdata != "nokeep":
267 if not os.access(os.path.join(codir, '.hg'), os.R_OK):
268 logger.debug(2, "Unpack: creating new hg repository in '" + codir + "'")
269 runfetchcmd("%s init %s" % (ud.basecmd, codir), d)
270 logger.debug(2, "Unpack: updating source in '" + codir + "'")
271 os.chdir(codir)
272 runfetchcmd("%s pull %s" % (ud.basecmd, ud.moddir), d)
273 runfetchcmd("%s up -C %s" % (ud.basecmd, revflag), d)
274 else:
275 logger.debug(2, "Unpack: extracting source to '" + codir + "'")
276 os.chdir(ud.moddir)
277 runfetchcmd("%s archive -t files %s %s" % (ud.basecmd, revflag, codir), d)