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