xref: /openbmc/openbmc/poky/bitbake/lib/bb/fetch2/hg.py (revision 19323693)
1# 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 bb.utils.to_boolean(d.getVar("BB_FETCH_PREMIRRORONLY")):
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)
271