| """ | |
| BitBake 'Fetch' implementation for mercurial DRCS (hg). | |
| """ | |
| # Copyright (C) 2003, 2004 Chris Larson | |
| # Copyright (C) 2004 Marcin Juszkiewicz | |
| # Copyright (C) 2007 Robert Schuster | |
| # | |
| # SPDX-License-Identifier: GPL-2.0-only | |
| # | |
| # Based on functions from the base bb module, Copyright 2003 Holger Schurig | |
| # | |
| import os | |
| import bb | |
| import errno | |
| import shlex | |
| from bb.fetch import FetchMethod | |
| from bb.fetch import FetchError | |
| from bb.fetch import MissingParameterError | |
| from bb.fetch import runfetchcmd | |
| from bb.fetch import logger | |
| class Hg(FetchMethod): | |
| """Class to fetch from mercurial repositories""" | |
| def supports(self, ud, d): | |
| """ | |
| Check to see if a given url can be fetched with mercurial. | |
| """ | |
| return ud.type in ['hg'] | |
| def supports_checksum(self, urldata): | |
| """ | |
| Don't require checksums for local archives created from | |
| repository checkouts. | |
| """ | |
| return False | |
| def urldata_init(self, ud, d): | |
| """ | |
| init hg specific variable within url data | |
| """ | |
| if not "module" in ud.parm: | |
| raise MissingParameterError('module', ud.url) | |
| ud.module = ud.parm["module"] | |
| if 'protocol' in ud.parm: | |
| ud.proto = ud.parm['protocol'] | |
| elif not ud.host: | |
| ud.proto = 'file' | |
| else: | |
| ud.proto = "hg" | |
| # Create paths to mercurial checkouts | |
| hgsrcname = '%s_%s_%s' % (ud.module.replace('/', '.'), \ | |
| ud.host, ud.path.replace('/', '.')) | |
| mirrortarball = 'hg_%s.tar.gz' % hgsrcname | |
| ud.fullmirror = os.path.join(d.getVar("DL_DIR"), mirrortarball) | |
| ud.mirrortarballs = [mirrortarball] | |
| hgdir = d.getVar("HGDIR") or (d.getVar("DL_DIR") + "/hg") | |
| ud.pkgdir = os.path.join(hgdir, hgsrcname) | |
| ud.moddir = os.path.join(ud.pkgdir, ud.module) | |
| ud.localfile = ud.moddir | |
| ud.basecmd = shlex.split(d.getVar("FETCHCMD_hg") or "") or ["hg"] | |
| ud.setup_revisions(d) | |
| if 'rev' in ud.parm: | |
| ud.revision = ud.parm['rev'] | |
| elif not ud.revision: | |
| ud.revision = self.latest_revision(ud, d) | |
| ud.write_tarballs = d.getVar("BB_GENERATE_MIRROR_TARBALLS") | |
| def need_update(self, ud, d): | |
| revTag = ud.parm.get('rev', 'tip') | |
| if revTag == "tip": | |
| return True | |
| if not os.path.exists(ud.localpath): | |
| return True | |
| return False | |
| def try_premirror(self, ud, d): | |
| # If we don't do this, updating an existing checkout with only premirrors | |
| # is not possible | |
| if bb.utils.to_boolean(d.getVar("BB_FETCH_PREMIRRORONLY")): | |
| return True | |
| if os.path.exists(ud.moddir): | |
| return False | |
| return True | |
| def _buildhgcommand(self, ud, d, command): | |
| """ | |
| Build up an hg commandline based on ud | |
| command is "fetch", "update", "info" | |
| """ | |
| proto = ud.parm.get('protocol', 'http') | |
| host = ud.host | |
| if proto == "file": | |
| host = "/" | |
| ud.host = "localhost" | |
| if not ud.user: | |
| hgroot = host + ud.path | |
| else: | |
| if ud.pswd: | |
| hgroot = ud.user + ":" + ud.pswd + "@" + host + ud.path | |
| else: | |
| hgroot = ud.user + "@" + host + ud.path | |
| if command == "info": | |
| return ud.basecmd + ['identify', '-i', '%s://%s/%s' % (proto, hgroot, ud.module)] | |
| options = []; | |
| # Don't specify revision for the fetch; clone the entire repo. | |
| # This avoids an issue if the specified revision is a tag, because | |
| # the tag actually exists in the specified revision + 1, so it won't | |
| # be available when used in any successive commands. | |
| if ud.revision and command != "fetch": | |
| options += ["-r", ud.revision] | |
| authconfig = [] | |
| if ud.user and ud.pswd: | |
| authconfig = ['--config', 'auth.default.prefix=*', '--config', 'auth.default.username=' + ud.user, '--config', 'auth.default.password=' + ud.pswd, '--config', 'auth.default.schemes=' + proto] | |
| if command == "fetch": | |
| cmd = ud.basecmd + authconfig + ['clone'] + options + ['%s://%s/%s' % (proto, hgroot, ud.module), ud.module] | |
| elif command == "pull": | |
| # do not pass options list; limiting pull to rev causes the local | |
| # repo not to contain it and immediately following "update" command | |
| # will crash | |
| cmd = ud.basecmd + authconfig + ['pull'] | |
| elif command == "update" or command == "up": | |
| cmd = ud.basecmd + authconfig + ['update', '-C'] + options | |
| else: | |
| raise FetchError("Invalid hg command %s" % command, ud.url) | |
| return cmd | |
| def download(self, ud, d): | |
| """Fetch url""" | |
| logger.debug2("Fetch: checking for module directory '" + ud.moddir + "'") | |
| # If the checkout doesn't exist and the mirror tarball does, extract it | |
| if not os.path.exists(ud.pkgdir) and os.path.exists(ud.fullmirror): | |
| bb.utils.mkdirhier(ud.pkgdir) | |
| runfetchcmd(['tar', '-xzf', ud.fullmirror], d, workdir=ud.pkgdir) | |
| if os.access(os.path.join(ud.moddir, '.hg'), os.R_OK): | |
| # Found the source, check whether need pull | |
| updatecmd = self._buildhgcommand(ud, d, "update") | |
| logger.debug("Running %s", updatecmd) | |
| try: | |
| runfetchcmd(updatecmd, d, workdir=ud.moddir) | |
| except bb.fetch.FetchError: | |
| # Runnning pull in the repo | |
| pullcmd = self._buildhgcommand(ud, d, "pull") | |
| logger.info("Pulling " + ud.url) | |
| # update sources there | |
| logger.debug("Running %s", pullcmd) | |
| bb.fetch.check_network_access(d, pullcmd, ud.url) | |
| runfetchcmd(pullcmd, d, workdir=ud.moddir) | |
| try: | |
| os.unlink(ud.fullmirror) | |
| except OSError as exc: | |
| if exc.errno != errno.ENOENT: | |
| raise | |
| # No source found, clone it. | |
| if not os.path.exists(ud.moddir): | |
| fetchcmd = self._buildhgcommand(ud, d, "fetch") | |
| logger.info("Fetch " + ud.url) | |
| # check out sources there | |
| bb.utils.mkdirhier(ud.pkgdir) | |
| logger.debug("Running %s", fetchcmd) | |
| bb.fetch.check_network_access(d, fetchcmd, ud.url) | |
| runfetchcmd(fetchcmd, d, workdir=ud.pkgdir) | |
| # Even when we clone (fetch), we still need to update as hg's clone | |
| # won't checkout the specified revision if its on a branch | |
| updatecmd = self._buildhgcommand(ud, d, "update") | |
| logger.debug("Running %s", updatecmd) | |
| runfetchcmd(updatecmd, d, workdir=ud.moddir) | |
| def clean(self, ud, d): | |
| """ Clean the hg dir """ | |
| bb.utils.remove(ud.localpath, True) | |
| bb.utils.remove(ud.fullmirror) | |
| bb.utils.remove(ud.fullmirror + ".done") | |
| def supports_srcrev(self): | |
| return True | |
| def _latest_revision(self, ud, d, name): | |
| """ | |
| Compute tip revision for the url | |
| """ | |
| bb.fetch.check_network_access(d, self._buildhgcommand(ud, d, "info"), ud.url) | |
| output = runfetchcmd(self._buildhgcommand(ud, d, "info"), d) | |
| return output.strip() | |
| def _build_revision(self, ud, d, name): | |
| return ud.revision | |
| def _revision_key(self, ud, d, name): | |
| """ | |
| Return a unique key for the url | |
| """ | |
| return "hg:" + ud.moddir | |
| def build_mirror_data(self, ud, d): | |
| # Generate a mirror tarball if needed | |
| if ud.write_tarballs == "1" and not os.path.exists(ud.fullmirror): | |
| # it's possible that this symlink points to read-only filesystem with PREMIRROR | |
| if os.path.islink(ud.fullmirror): | |
| os.unlink(ud.fullmirror) | |
| logger.info("Creating tarball of hg repository") | |
| runfetchcmd(['tar', '-czf', ud.fullmirror, ud.module], d, workdir=ud.pkgdir) | |
| runfetchcmd(['touch', '%s.done' % ud.fullmirror], d, workdir=ud.pkgdir) | |
| def localpath(self, ud, d): | |
| return ud.pkgdir | |
| def unpack(self, ud, destdir, d): | |
| """ | |
| Make a local clone or export for the url | |
| """ | |
| revflag = "-r %s" % ud.revision | |
| subdir = ud.parm.get("destsuffix", ud.module) | |
| codir = "%s/%s" % (destdir, subdir) | |
| ud.unpack_tracer.unpack("hg", codir) | |
| scmdata = ud.parm.get("scmdata", "") | |
| if scmdata != "nokeep": | |
| proto = ud.parm.get('protocol', 'http') | |
| if not os.access(os.path.join(codir, '.hg'), os.R_OK): | |
| logger.debug2("Unpack: creating new hg repository in '" + codir + "'") | |
| runfetchcmd(ud.basecmd + ['init', codir], d) | |
| logger.debug2("Unpack: updating source in '" + codir + "'") | |
| authconfig = [] | |
| if ud.user and ud.pswd: | |
| authconfig = ['--config', 'auth.default.prefix=*', '--config', 'auth.default.username=' + ud.user, '--config', 'auth.default.password=' + ud.pswd, '--config', 'auth.default.schemes=' + proto] | |
| runfetchcmd(ud.basecmd + authconfig + ['pull', ud.moddir], d, workdir=codir) | |
| runfetchcmd(ud.basecmd + authconfig + ['up', '-C', revflag], d, workdir=codir) | |
| else: | |
| logger.debug2("Unpack: extracting source to '" + codir + "'") | |
| runfetchcmd(ud.basecmd + ['archive', '-t', 'files', revflag, codir], d, workdir=ud.moddir) |