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