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