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