blob: 936d0431125d6f7c8ad8034b45942cf9efe09bd5 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001# 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
31import errno
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_revisions(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 mirrortarball = 'hg_%s.tar.gz' % hgsrcname
80 ud.fullmirror = os.path.join(d.getVar("DL_DIR"), mirrortarball)
81 ud.mirrortarballs = [mirrortarball]
82
83 hgdir = d.getVar("HGDIR") or (d.getVar("DL_DIR") + "/hg")
84 ud.pkgdir = os.path.join(hgdir, hgsrcname)
85 ud.moddir = os.path.join(ud.pkgdir, ud.module)
86 ud.localfile = ud.moddir
87 ud.basecmd = d.getVar("FETCHCMD_hg") or "/usr/bin/env hg"
88
89 ud.write_tarballs = d.getVar("BB_GENERATE_MIRROR_TARBALLS")
90
91 def need_update(self, ud, d):
92 revTag = ud.parm.get('rev', 'tip')
93 if revTag == "tip":
94 return True
95 if not os.path.exists(ud.localpath):
96 return True
97 return False
98
99 def try_premirror(self, ud, d):
100 # If we don't do this, updating an existing checkout with only premirrors
101 # is not possible
102 if d.getVar("BB_FETCH_PREMIRRORONLY") is not None:
103 return True
104 if os.path.exists(ud.moddir):
105 return False
106 return True
107
108 def _buildhgcommand(self, ud, d, command):
109 """
110 Build up an hg commandline based on ud
111 command is "fetch", "update", "info"
112 """
113
114 proto = ud.parm.get('protocol', 'http')
115
116 host = ud.host
117 if proto == "file":
118 host = "/"
119 ud.host = "localhost"
120
121 if not ud.user:
122 hgroot = host + ud.path
123 else:
124 if ud.pswd:
125 hgroot = ud.user + ":" + ud.pswd + "@" + host + ud.path
126 else:
127 hgroot = ud.user + "@" + host + ud.path
128
129 if command == "info":
130 return "%s identify -i %s://%s/%s" % (ud.basecmd, proto, hgroot, ud.module)
131
132 options = [];
133
134 # Don't specify revision for the fetch; clone the entire repo.
135 # This avoids an issue if the specified revision is a tag, because
136 # the tag actually exists in the specified revision + 1, so it won't
137 # be available when used in any successive commands.
138 if ud.revision and command != "fetch":
139 options.append("-r %s" % ud.revision)
140
141 if command == "fetch":
142 if ud.user and ud.pswd:
143 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)
144 else:
145 cmd = "%s clone %s %s://%s/%s %s" % (ud.basecmd, " ".join(options), proto, hgroot, ud.module, ud.module)
146 elif command == "pull":
147 # do not pass options list; limiting pull to rev causes the local
148 # repo not to contain it and immediately following "update" command
149 # will crash
150 if ud.user and ud.pswd:
151 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)
152 else:
153 cmd = "%s pull" % (ud.basecmd)
154 elif command == "update":
155 if ud.user and ud.pswd:
156 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))
157 else:
158 cmd = "%s update -C %s" % (ud.basecmd, " ".join(options))
159 else:
160 raise FetchError("Invalid hg command %s" % command, ud.url)
161
162 return cmd
163
164 def download(self, ud, d):
165 """Fetch url"""
166
167 logger.debug(2, "Fetch: checking for module directory '" + ud.moddir + "'")
168
169 # If the checkout doesn't exist and the mirror tarball does, extract it
170 if not os.path.exists(ud.pkgdir) and os.path.exists(ud.fullmirror):
171 bb.utils.mkdirhier(ud.pkgdir)
172 runfetchcmd("tar -xzf %s" % (ud.fullmirror), d, workdir=ud.pkgdir)
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 logger.debug(1, "Running %s", updatecmd)
178 try:
179 runfetchcmd(updatecmd, d, workdir=ud.moddir)
180 except bb.fetch2.FetchError:
181 # Runnning pull in the repo
182 pullcmd = self._buildhgcommand(ud, d, "pull")
183 logger.info("Pulling " + ud.url)
184 # update sources there
185 logger.debug(1, "Running %s", pullcmd)
186 bb.fetch2.check_network_access(d, pullcmd, ud.url)
187 runfetchcmd(pullcmd, d, workdir=ud.moddir)
188 try:
189 os.unlink(ud.fullmirror)
190 except OSError as exc:
191 if exc.errno != errno.ENOENT:
192 raise
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 logger.debug(1, "Running %s", fetchcmd)
201 bb.fetch2.check_network_access(d, fetchcmd, ud.url)
202 runfetchcmd(fetchcmd, d, workdir=ud.pkgdir)
203
204 # Even when we clone (fetch), we still need to update as hg's clone
205 # won't checkout the specified revision if its on a branch
206 updatecmd = self._buildhgcommand(ud, d, "update")
207 logger.debug(1, "Running %s", updatecmd)
208 runfetchcmd(updatecmd, d, workdir=ud.moddir)
209
210 def clean(self, ud, d):
211 """ Clean the hg dir """
212
213 bb.utils.remove(ud.localpath, True)
214 bb.utils.remove(ud.fullmirror)
215 bb.utils.remove(ud.fullmirror + ".done")
216
217 def supports_srcrev(self):
218 return True
219
220 def _latest_revision(self, ud, d, name):
221 """
222 Compute tip revision for the url
223 """
224 bb.fetch2.check_network_access(d, self._buildhgcommand(ud, d, "info"), ud.url)
225 output = runfetchcmd(self._buildhgcommand(ud, d, "info"), d)
226 return output.strip()
227
228 def _build_revision(self, ud, d, name):
229 return ud.revision
230
231 def _revision_key(self, ud, d, name):
232 """
233 Return a unique key for the url
234 """
235 return "hg:" + ud.moddir
236
237 def build_mirror_data(self, ud, d):
238 # Generate a mirror tarball if needed
239 if ud.write_tarballs == "1" and not os.path.exists(ud.fullmirror):
240 # it's possible that this symlink points to read-only filesystem with PREMIRROR
241 if os.path.islink(ud.fullmirror):
242 os.unlink(ud.fullmirror)
243
244 logger.info("Creating tarball of hg repository")
245 runfetchcmd("tar -czf %s %s" % (ud.fullmirror, ud.module), d, workdir=ud.pkgdir)
246 runfetchcmd("touch %s.done" % (ud.fullmirror), d, workdir=ud.pkgdir)
247
248 def localpath(self, ud, d):
249 return ud.pkgdir
250
251 def unpack(self, ud, destdir, d):
252 """
253 Make a local clone or export for the url
254 """
255
256 revflag = "-r %s" % ud.revision
257 subdir = ud.parm.get("destsuffix", ud.module)
258 codir = "%s/%s" % (destdir, subdir)
259
260 scmdata = ud.parm.get("scmdata", "")
261 if scmdata != "nokeep":
262 if not os.access(os.path.join(codir, '.hg'), os.R_OK):
263 logger.debug(2, "Unpack: creating new hg repository in '" + codir + "'")
264 runfetchcmd("%s init %s" % (ud.basecmd, codir), d)
265 logger.debug(2, "Unpack: updating source in '" + codir + "'")
266 runfetchcmd("%s pull %s" % (ud.basecmd, ud.moddir), d, workdir=codir)
267 runfetchcmd("%s up -C %s" % (ud.basecmd, revflag), d, workdir=codir)
268 else:
269 logger.debug(2, "Unpack: extracting source to '" + codir + "'")
270 runfetchcmd("%s archive -t files %s %s" % (ud.basecmd, revflag, codir), d, workdir=ud.moddir)