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