|  | """ | 
|  | BitBake 'Fetch' implementation for svn. | 
|  |  | 
|  | """ | 
|  |  | 
|  | # Copyright (C) 2003, 2004  Chris Larson | 
|  | # Copyright (C) 2004        Marcin Juszkiewicz | 
|  | # | 
|  | # SPDX-License-Identifier: GPL-2.0-only | 
|  | # | 
|  | # Based on functions from the base bb module, Copyright 2003 Holger Schurig | 
|  |  | 
|  | import os | 
|  | import sys | 
|  | import logging | 
|  | import bb | 
|  | import re | 
|  | from   bb.fetch2 import FetchMethod | 
|  | from   bb.fetch2 import FetchError | 
|  | from   bb.fetch2 import MissingParameterError | 
|  | from   bb.fetch2 import runfetchcmd | 
|  | from   bb.fetch2 import logger | 
|  |  | 
|  | class Svn(FetchMethod): | 
|  | """Class to fetch a module or modules from svn repositories""" | 
|  | def supports(self, ud, d): | 
|  | """ | 
|  | Check to see if a given url can be fetched with svn. | 
|  | """ | 
|  | return ud.type in ['svn'] | 
|  |  | 
|  | def urldata_init(self, ud, d): | 
|  | """ | 
|  | init svn specific variable within url data | 
|  | """ | 
|  | if not "module" in ud.parm: | 
|  | raise MissingParameterError('module', ud.url) | 
|  |  | 
|  | ud.basecmd = d.getVar("FETCHCMD_svn") or "/usr/bin/env svn --non-interactive --trust-server-cert" | 
|  |  | 
|  | ud.module = ud.parm["module"] | 
|  |  | 
|  | if not "path_spec" in ud.parm: | 
|  | ud.path_spec = ud.module | 
|  | else: | 
|  | ud.path_spec = ud.parm["path_spec"] | 
|  |  | 
|  | # Create paths to svn checkouts | 
|  | svndir = d.getVar("SVNDIR") or (d.getVar("DL_DIR") + "/svn") | 
|  | relpath = self._strip_leading_slashes(ud.path) | 
|  | ud.pkgdir = os.path.join(svndir, ud.host, relpath) | 
|  | ud.moddir = os.path.join(ud.pkgdir, ud.module) | 
|  | # Protects the repository from concurrent updates, e.g. from two | 
|  | # recipes fetching different revisions at the same time | 
|  | ud.svnlock = os.path.join(ud.pkgdir, "svn.lock") | 
|  |  | 
|  | ud.setup_revisions(d) | 
|  |  | 
|  | if 'rev' in ud.parm: | 
|  | ud.revision = ud.parm['rev'] | 
|  |  | 
|  | ud.localfile = d.expand('%s_%s_%s_%s_.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.path.replace('/', '.'), ud.revision)) | 
|  |  | 
|  | def _buildsvncommand(self, ud, d, command): | 
|  | """ | 
|  | Build up an svn commandline based on ud | 
|  | command is "fetch", "update", "info" | 
|  | """ | 
|  |  | 
|  | proto = ud.parm.get('protocol', 'svn') | 
|  |  | 
|  | svn_ssh = None | 
|  | if proto == "svn+ssh" and "ssh" in ud.parm: | 
|  | svn_ssh = ud.parm["ssh"] | 
|  |  | 
|  | svnroot = ud.host + ud.path | 
|  |  | 
|  | options = [] | 
|  |  | 
|  | options.append("--no-auth-cache") | 
|  |  | 
|  | if ud.user: | 
|  | options.append("--username %s" % ud.user) | 
|  |  | 
|  | if ud.pswd: | 
|  | options.append("--password %s" % ud.pswd) | 
|  |  | 
|  | if command == "info": | 
|  | svncmd = "%s info %s %s://%s/%s/" % (ud.basecmd, " ".join(options), proto, svnroot, ud.module) | 
|  | elif command == "log1": | 
|  | svncmd = "%s log --limit 1 %s %s://%s/%s/" % (ud.basecmd, " ".join(options), proto, svnroot, ud.module) | 
|  | else: | 
|  | suffix = "" | 
|  | if ud.revision: | 
|  | options.append("-r %s" % ud.revision) | 
|  | suffix = "@%s" % (ud.revision) | 
|  |  | 
|  | if command == "fetch": | 
|  | transportuser = ud.parm.get("transportuser", "") | 
|  | svncmd = "%s co %s %s://%s%s/%s%s %s" % (ud.basecmd, " ".join(options), proto, transportuser, svnroot, ud.module, suffix, ud.path_spec) | 
|  | elif command == "update": | 
|  | svncmd = "%s update %s" % (ud.basecmd, " ".join(options)) | 
|  | else: | 
|  | raise FetchError("Invalid svn command %s" % command, ud.url) | 
|  |  | 
|  | if svn_ssh: | 
|  | svncmd = "SVN_SSH=\"%s\" %s" % (svn_ssh, svncmd) | 
|  |  | 
|  | return svncmd | 
|  |  | 
|  | def download(self, ud, d): | 
|  | """Fetch url""" | 
|  |  | 
|  | logger.debug(2, "Fetch: checking for module directory '" + ud.moddir + "'") | 
|  |  | 
|  | lf = bb.utils.lockfile(ud.svnlock) | 
|  |  | 
|  | try: | 
|  | if os.access(os.path.join(ud.moddir, '.svn'), os.R_OK): | 
|  | svnupdatecmd = self._buildsvncommand(ud, d, "update") | 
|  | logger.info("Update " + ud.url) | 
|  | # We need to attempt to run svn upgrade first in case its an older working format | 
|  | try: | 
|  | runfetchcmd(ud.basecmd + " upgrade", d, workdir=ud.moddir) | 
|  | except FetchError: | 
|  | pass | 
|  | logger.debug(1, "Running %s", svnupdatecmd) | 
|  | bb.fetch2.check_network_access(d, svnupdatecmd, ud.url) | 
|  | runfetchcmd(svnupdatecmd, d, workdir=ud.moddir) | 
|  | else: | 
|  | svnfetchcmd = self._buildsvncommand(ud, d, "fetch") | 
|  | logger.info("Fetch " + ud.url) | 
|  | # check out sources there | 
|  | bb.utils.mkdirhier(ud.pkgdir) | 
|  | logger.debug(1, "Running %s", svnfetchcmd) | 
|  | bb.fetch2.check_network_access(d, svnfetchcmd, ud.url) | 
|  | runfetchcmd(svnfetchcmd, d, workdir=ud.pkgdir) | 
|  |  | 
|  | scmdata = ud.parm.get("scmdata", "") | 
|  | if scmdata == "keep": | 
|  | tar_flags = "" | 
|  | else: | 
|  | tar_flags = "--exclude='.svn'" | 
|  |  | 
|  | # tar them up to a defined filename | 
|  | runfetchcmd("tar %s -czf %s %s" % (tar_flags, ud.localpath, ud.path_spec), d, | 
|  | cleanup=[ud.localpath], workdir=ud.pkgdir) | 
|  | finally: | 
|  | bb.utils.unlockfile(lf) | 
|  |  | 
|  | def clean(self, ud, d): | 
|  | """ Clean SVN specific files and dirs """ | 
|  |  | 
|  | bb.utils.remove(ud.localpath) | 
|  | bb.utils.remove(ud.moddir, True) | 
|  |  | 
|  |  | 
|  | def supports_srcrev(self): | 
|  | return True | 
|  |  | 
|  | def _revision_key(self, ud, d, name): | 
|  | """ | 
|  | Return a unique key for the url | 
|  | """ | 
|  | return "svn:" + ud.moddir | 
|  |  | 
|  | def _latest_revision(self, ud, d, name): | 
|  | """ | 
|  | Return the latest upstream revision number | 
|  | """ | 
|  | bb.fetch2.check_network_access(d, self._buildsvncommand(ud, d, "log1"), ud.url) | 
|  |  | 
|  | output = runfetchcmd("LANG=C LC_ALL=C " + self._buildsvncommand(ud, d, "log1"), d, True) | 
|  |  | 
|  | # skip the first line, as per output of svn log | 
|  | # then we expect the revision on the 2nd line | 
|  | revision = re.search('^r([0-9]*)', output.splitlines()[1]).group(1) | 
|  |  | 
|  | return revision | 
|  |  | 
|  | def sortable_revision(self, ud, d, name): | 
|  | """ | 
|  | Return a sortable revision number which in our case is the revision number | 
|  | """ | 
|  |  | 
|  | return False, self._build_revision(ud, d) | 
|  |  | 
|  | def _build_revision(self, ud, d): | 
|  | return ud.revision |