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