xref: /OK3568_Linux_fs/yocto/poky/bitbake/lib/bb/fetch2/svn.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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