1*4882a593Smuzhiyun""" 2*4882a593SmuzhiyunBitBake 'Fetch' implementation for svn. 3*4882a593Smuzhiyun 4*4882a593Smuzhiyun""" 5*4882a593Smuzhiyun 6*4882a593Smuzhiyun# Copyright (C) 2003, 2004 Chris Larson 7*4882a593Smuzhiyun# Copyright (C) 2004 Marcin Juszkiewicz 8*4882a593Smuzhiyun# 9*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 10*4882a593Smuzhiyun# 11*4882a593Smuzhiyun# Based on functions from the base bb module, Copyright 2003 Holger Schurig 12*4882a593Smuzhiyun 13*4882a593Smuzhiyunimport os 14*4882a593Smuzhiyunimport bb 15*4882a593Smuzhiyunimport re 16*4882a593Smuzhiyunfrom bb.fetch2 import FetchMethod 17*4882a593Smuzhiyunfrom bb.fetch2 import FetchError 18*4882a593Smuzhiyunfrom bb.fetch2 import MissingParameterError 19*4882a593Smuzhiyunfrom bb.fetch2 import runfetchcmd 20*4882a593Smuzhiyunfrom bb.fetch2 import logger 21*4882a593Smuzhiyun 22*4882a593Smuzhiyunclass Svn(FetchMethod): 23*4882a593Smuzhiyun """Class to fetch a module or modules from svn repositories""" 24*4882a593Smuzhiyun def supports(self, ud, d): 25*4882a593Smuzhiyun """ 26*4882a593Smuzhiyun Check to see if a given url can be fetched with svn. 27*4882a593Smuzhiyun """ 28*4882a593Smuzhiyun return ud.type in ['svn'] 29*4882a593Smuzhiyun 30*4882a593Smuzhiyun def urldata_init(self, ud, d): 31*4882a593Smuzhiyun """ 32*4882a593Smuzhiyun init svn specific variable within url data 33*4882a593Smuzhiyun """ 34*4882a593Smuzhiyun if not "module" in ud.parm: 35*4882a593Smuzhiyun raise MissingParameterError('module', ud.url) 36*4882a593Smuzhiyun 37*4882a593Smuzhiyun ud.basecmd = d.getVar("FETCHCMD_svn") or "/usr/bin/env svn --non-interactive --trust-server-cert" 38*4882a593Smuzhiyun 39*4882a593Smuzhiyun ud.module = ud.parm["module"] 40*4882a593Smuzhiyun 41*4882a593Smuzhiyun if not "path_spec" in ud.parm: 42*4882a593Smuzhiyun ud.path_spec = ud.module 43*4882a593Smuzhiyun else: 44*4882a593Smuzhiyun ud.path_spec = ud.parm["path_spec"] 45*4882a593Smuzhiyun 46*4882a593Smuzhiyun # Create paths to svn checkouts 47*4882a593Smuzhiyun svndir = d.getVar("SVNDIR") or (d.getVar("DL_DIR") + "/svn") 48*4882a593Smuzhiyun relpath = self._strip_leading_slashes(ud.path) 49*4882a593Smuzhiyun ud.pkgdir = os.path.join(svndir, ud.host, relpath) 50*4882a593Smuzhiyun ud.moddir = os.path.join(ud.pkgdir, ud.path_spec) 51*4882a593Smuzhiyun # Protects the repository from concurrent updates, e.g. from two 52*4882a593Smuzhiyun # recipes fetching different revisions at the same time 53*4882a593Smuzhiyun ud.svnlock = os.path.join(ud.pkgdir, "svn.lock") 54*4882a593Smuzhiyun 55*4882a593Smuzhiyun ud.setup_revisions(d) 56*4882a593Smuzhiyun 57*4882a593Smuzhiyun if 'rev' in ud.parm: 58*4882a593Smuzhiyun ud.revision = ud.parm['rev'] 59*4882a593Smuzhiyun 60*4882a593Smuzhiyun # Whether to use the @REV peg-revision syntax in the svn command or not 61*4882a593Smuzhiyun ud.pegrevision = True 62*4882a593Smuzhiyun if 'nopegrevision' in ud.parm: 63*4882a593Smuzhiyun ud.pegrevision = False 64*4882a593Smuzhiyun 65*4882a593Smuzhiyun ud.localfile = d.expand('%s_%s_%s_%s_%s.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.path.replace('/', '.'), ud.revision, ["0", "1"][ud.pegrevision])) 66*4882a593Smuzhiyun 67*4882a593Smuzhiyun def _buildsvncommand(self, ud, d, command): 68*4882a593Smuzhiyun """ 69*4882a593Smuzhiyun Build up an svn commandline based on ud 70*4882a593Smuzhiyun command is "fetch", "update", "info" 71*4882a593Smuzhiyun """ 72*4882a593Smuzhiyun 73*4882a593Smuzhiyun proto = ud.parm.get('protocol', 'svn') 74*4882a593Smuzhiyun 75*4882a593Smuzhiyun svn_ssh = None 76*4882a593Smuzhiyun if proto == "svn+ssh" and "ssh" in ud.parm: 77*4882a593Smuzhiyun svn_ssh = ud.parm["ssh"] 78*4882a593Smuzhiyun 79*4882a593Smuzhiyun svnroot = ud.host + ud.path 80*4882a593Smuzhiyun 81*4882a593Smuzhiyun options = [] 82*4882a593Smuzhiyun 83*4882a593Smuzhiyun options.append("--no-auth-cache") 84*4882a593Smuzhiyun 85*4882a593Smuzhiyun if ud.user: 86*4882a593Smuzhiyun options.append("--username %s" % ud.user) 87*4882a593Smuzhiyun 88*4882a593Smuzhiyun if ud.pswd: 89*4882a593Smuzhiyun options.append("--password %s" % ud.pswd) 90*4882a593Smuzhiyun 91*4882a593Smuzhiyun if command == "info": 92*4882a593Smuzhiyun svncmd = "%s info %s %s://%s/%s/" % (ud.basecmd, " ".join(options), proto, svnroot, ud.module) 93*4882a593Smuzhiyun elif command == "log1": 94*4882a593Smuzhiyun svncmd = "%s log --limit 1 --quiet %s %s://%s/%s/" % (ud.basecmd, " ".join(options), proto, svnroot, ud.module) 95*4882a593Smuzhiyun else: 96*4882a593Smuzhiyun suffix = "" 97*4882a593Smuzhiyun 98*4882a593Smuzhiyun # externals may be either 'allowed' or 'nowarn', but not both. Allowed 99*4882a593Smuzhiyun # will not issue a warning, but will log to the debug buffer what has likely 100*4882a593Smuzhiyun # been downloaded by SVN. 101*4882a593Smuzhiyun if not ("externals" in ud.parm and ud.parm["externals"] == "allowed"): 102*4882a593Smuzhiyun options.append("--ignore-externals") 103*4882a593Smuzhiyun 104*4882a593Smuzhiyun if ud.revision: 105*4882a593Smuzhiyun options.append("-r %s" % ud.revision) 106*4882a593Smuzhiyun if ud.pegrevision: 107*4882a593Smuzhiyun suffix = "@%s" % (ud.revision) 108*4882a593Smuzhiyun 109*4882a593Smuzhiyun if command == "fetch": 110*4882a593Smuzhiyun transportuser = ud.parm.get("transportuser", "") 111*4882a593Smuzhiyun svncmd = "%s co %s %s://%s%s/%s%s %s" % (ud.basecmd, " ".join(options), proto, transportuser, svnroot, ud.module, suffix, ud.path_spec) 112*4882a593Smuzhiyun elif command == "update": 113*4882a593Smuzhiyun svncmd = "%s update %s" % (ud.basecmd, " ".join(options)) 114*4882a593Smuzhiyun else: 115*4882a593Smuzhiyun raise FetchError("Invalid svn command %s" % command, ud.url) 116*4882a593Smuzhiyun 117*4882a593Smuzhiyun if svn_ssh: 118*4882a593Smuzhiyun svncmd = "SVN_SSH=\"%s\" %s" % (svn_ssh, svncmd) 119*4882a593Smuzhiyun 120*4882a593Smuzhiyun return svncmd 121*4882a593Smuzhiyun 122*4882a593Smuzhiyun def download(self, ud, d): 123*4882a593Smuzhiyun """Fetch url""" 124*4882a593Smuzhiyun 125*4882a593Smuzhiyun logger.debug2("Fetch: checking for module directory '" + ud.moddir + "'") 126*4882a593Smuzhiyun 127*4882a593Smuzhiyun lf = bb.utils.lockfile(ud.svnlock) 128*4882a593Smuzhiyun 129*4882a593Smuzhiyun try: 130*4882a593Smuzhiyun if os.access(os.path.join(ud.moddir, '.svn'), os.R_OK): 131*4882a593Smuzhiyun svncmd = self._buildsvncommand(ud, d, "update") 132*4882a593Smuzhiyun logger.info("Update " + ud.url) 133*4882a593Smuzhiyun # We need to attempt to run svn upgrade first in case its an older working format 134*4882a593Smuzhiyun try: 135*4882a593Smuzhiyun runfetchcmd(ud.basecmd + " upgrade", d, workdir=ud.moddir) 136*4882a593Smuzhiyun except FetchError: 137*4882a593Smuzhiyun pass 138*4882a593Smuzhiyun logger.debug("Running %s", svncmd) 139*4882a593Smuzhiyun bb.fetch2.check_network_access(d, svncmd, ud.url) 140*4882a593Smuzhiyun runfetchcmd(svncmd, d, workdir=ud.moddir) 141*4882a593Smuzhiyun else: 142*4882a593Smuzhiyun svncmd = self._buildsvncommand(ud, d, "fetch") 143*4882a593Smuzhiyun logger.info("Fetch " + ud.url) 144*4882a593Smuzhiyun # check out sources there 145*4882a593Smuzhiyun bb.utils.mkdirhier(ud.pkgdir) 146*4882a593Smuzhiyun logger.debug("Running %s", svncmd) 147*4882a593Smuzhiyun bb.fetch2.check_network_access(d, svncmd, ud.url) 148*4882a593Smuzhiyun runfetchcmd(svncmd, d, workdir=ud.pkgdir) 149*4882a593Smuzhiyun 150*4882a593Smuzhiyun if not ("externals" in ud.parm and ud.parm["externals"] == "nowarn"): 151*4882a593Smuzhiyun # Warn the user if this had externals (won't catch them all) 152*4882a593Smuzhiyun output = runfetchcmd("svn propget svn:externals || true", d, workdir=ud.moddir) 153*4882a593Smuzhiyun if output: 154*4882a593Smuzhiyun if "--ignore-externals" in svncmd.split(): 155*4882a593Smuzhiyun bb.warn("%s contains svn:externals." % ud.url) 156*4882a593Smuzhiyun bb.warn("These should be added to the recipe SRC_URI as necessary.") 157*4882a593Smuzhiyun bb.warn("svn fetch has ignored externals:\n%s" % output) 158*4882a593Smuzhiyun bb.warn("To disable this warning add ';externals=nowarn' to the url.") 159*4882a593Smuzhiyun else: 160*4882a593Smuzhiyun bb.debug(1, "svn repository has externals:\n%s" % output) 161*4882a593Smuzhiyun 162*4882a593Smuzhiyun scmdata = ud.parm.get("scmdata", "") 163*4882a593Smuzhiyun if scmdata == "keep": 164*4882a593Smuzhiyun tar_flags = "" 165*4882a593Smuzhiyun else: 166*4882a593Smuzhiyun tar_flags = "--exclude='.svn'" 167*4882a593Smuzhiyun 168*4882a593Smuzhiyun # tar them up to a defined filename 169*4882a593Smuzhiyun runfetchcmd("tar %s -czf %s %s" % (tar_flags, ud.localpath, ud.path_spec), d, 170*4882a593Smuzhiyun cleanup=[ud.localpath], workdir=ud.pkgdir) 171*4882a593Smuzhiyun finally: 172*4882a593Smuzhiyun bb.utils.unlockfile(lf) 173*4882a593Smuzhiyun 174*4882a593Smuzhiyun def clean(self, ud, d): 175*4882a593Smuzhiyun """ Clean SVN specific files and dirs """ 176*4882a593Smuzhiyun 177*4882a593Smuzhiyun bb.utils.remove(ud.localpath) 178*4882a593Smuzhiyun bb.utils.remove(ud.moddir, True) 179*4882a593Smuzhiyun 180*4882a593Smuzhiyun 181*4882a593Smuzhiyun def supports_srcrev(self): 182*4882a593Smuzhiyun return True 183*4882a593Smuzhiyun 184*4882a593Smuzhiyun def _revision_key(self, ud, d, name): 185*4882a593Smuzhiyun """ 186*4882a593Smuzhiyun Return a unique key for the url 187*4882a593Smuzhiyun """ 188*4882a593Smuzhiyun return "svn:" + ud.moddir 189*4882a593Smuzhiyun 190*4882a593Smuzhiyun def _latest_revision(self, ud, d, name): 191*4882a593Smuzhiyun """ 192*4882a593Smuzhiyun Return the latest upstream revision number 193*4882a593Smuzhiyun """ 194*4882a593Smuzhiyun bb.fetch2.check_network_access(d, self._buildsvncommand(ud, d, "log1"), ud.url) 195*4882a593Smuzhiyun 196*4882a593Smuzhiyun output = runfetchcmd("LANG=C LC_ALL=C " + self._buildsvncommand(ud, d, "log1"), d, True) 197*4882a593Smuzhiyun 198*4882a593Smuzhiyun # skip the first line, as per output of svn log 199*4882a593Smuzhiyun # then we expect the revision on the 2nd line 200*4882a593Smuzhiyun revision = re.search('^r([0-9]*)', output.splitlines()[1]).group(1) 201*4882a593Smuzhiyun 202*4882a593Smuzhiyun return revision 203*4882a593Smuzhiyun 204*4882a593Smuzhiyun def sortable_revision(self, ud, d, name): 205*4882a593Smuzhiyun """ 206*4882a593Smuzhiyun Return a sortable revision number which in our case is the revision number 207*4882a593Smuzhiyun """ 208*4882a593Smuzhiyun 209*4882a593Smuzhiyun return False, self._build_revision(ud, d) 210*4882a593Smuzhiyun 211*4882a593Smuzhiyun def _build_revision(self, ud, d): 212*4882a593Smuzhiyun return ud.revision 213