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