xref: /openbmc/openbmc/poky/bitbake/lib/bb/fetch2/clearcase.py (revision 43a6b7c2a48b0cb1381af4d3192d22a12ead65f0)
1"""
2BitBake 'Fetch' clearcase implementation
3
4The clearcase fetcher is used to retrieve files from a ClearCase repository.
5
6Usage in the recipe:
7
8    SRC_URI = "ccrc://cc.example.org/ccrc;vob=/example_vob;module=/example_module"
9    SRCREV = "EXAMPLE_CLEARCASE_TAG"
10    PV = "${@d.getVar("SRCREV", False).replace("/", "+")}"
11
12The fetcher uses the rcleartool or cleartool remote client, depending on which one is available.
13
14Supported SRC_URI options are:
15
16- vob
17    (required) The name of the clearcase VOB (with prepending "/")
18
19- module
20    The module in the selected VOB (with prepending "/")
21
22    The module and vob parameters are combined to create
23    the following load rule in the view config spec:
24                load <vob><module>
25
26- proto
27    http or https
28
29Related variables:
30
31    CCASE_CUSTOM_CONFIG_SPEC
32            Write a config spec to this variable in your recipe to use it instead
33            of the default config spec generated by this fetcher.
34            Please note that the SRCREV loses its functionality if you specify
35            this variable. SRCREV is still used to label the archive after a fetch,
36            but it doesn't define what's fetched.
37
38User credentials:
39    cleartool:
40            The login of cleartool is handled by the system. No special steps needed.
41
42    rcleartool:
43            In order to use rcleartool with authenticated users an `rcleartool login` is
44            necessary before using the fetcher.
45"""
46# Copyright (C) 2014 Siemens AG
47#
48# SPDX-License-Identifier: GPL-2.0-only
49#
50
51import os
52import shutil
53import bb
54from   bb.fetch2 import FetchMethod
55from   bb.fetch2 import FetchError
56from   bb.fetch2 import MissingParameterError
57from   bb.fetch2 import ParameterError
58from   bb.fetch2 import runfetchcmd
59from   bb.fetch2 import logger
60
61class ClearCase(FetchMethod):
62    """Class to fetch urls via 'clearcase'"""
63    def init(self, d):
64        pass
65
66    def supports(self, ud, d):
67        """
68        Check to see if a given url can be fetched with Clearcase.
69        """
70        return ud.type in ['ccrc']
71
72    def debug(self, msg):
73        logger.debug("ClearCase: %s", msg)
74
75    def urldata_init(self, ud, d):
76        """
77        init ClearCase specific variable within url data
78        """
79        ud.proto = "https"
80        if 'protocol' in ud.parm:
81            ud.proto = ud.parm['protocol']
82        if not ud.proto in ('http', 'https'):
83            raise ParameterError("Invalid protocol type", ud.url)
84
85        ud.vob = ''
86        if 'vob' in ud.parm:
87            ud.vob = ud.parm['vob']
88        else:
89            msg = ud.url+": vob must be defined so the fetcher knows what to get."
90            raise MissingParameterError('vob', msg)
91
92        if 'module' in ud.parm:
93            ud.module = ud.parm['module']
94        else:
95            ud.module = ""
96
97        ud.basecmd = d.getVar("FETCHCMD_ccrc") or "/usr/bin/env cleartool || rcleartool"
98
99        if d.getVar("SRCREV") == "INVALID":
100          raise FetchError("Set a valid SRCREV for the clearcase fetcher in your recipe, e.g. SRCREV = \"/main/LATEST\" or any other label of your choice.")
101
102        ud.label = d.getVar("SRCREV", False)
103        ud.customspec = d.getVar("CCASE_CUSTOM_CONFIG_SPEC")
104
105        ud.server     = "%s://%s%s" % (ud.proto, ud.host, ud.path)
106
107        ud.identifier = "clearcase-%s%s-%s" % ( ud.vob.replace("/", ""),
108                                                ud.module.replace("/", "."),
109                                                ud.label.replace("/", "."))
110
111        ud.viewname         = "%s-view%s" % (ud.identifier, d.getVar("DATETIME"))
112        ud.csname           = "%s-config-spec" % (ud.identifier)
113        ud.ccasedir         = os.path.join(d.getVar("DL_DIR"), ud.type)
114        ud.viewdir          = os.path.join(ud.ccasedir, ud.viewname)
115        ud.configspecfile   = os.path.join(ud.ccasedir, ud.csname)
116        ud.localfile        = "%s.tar.gz" % (ud.identifier)
117
118        self.debug("host            = %s" % ud.host)
119        self.debug("path            = %s" % ud.path)
120        self.debug("server          = %s" % ud.server)
121        self.debug("proto           = %s" % ud.proto)
122        self.debug("type            = %s" % ud.type)
123        self.debug("vob             = %s" % ud.vob)
124        self.debug("module          = %s" % ud.module)
125        self.debug("basecmd         = %s" % ud.basecmd)
126        self.debug("label           = %s" % ud.label)
127        self.debug("ccasedir        = %s" % ud.ccasedir)
128        self.debug("viewdir         = %s" % ud.viewdir)
129        self.debug("viewname        = %s" % ud.viewname)
130        self.debug("configspecfile  = %s" % ud.configspecfile)
131        self.debug("localfile       = %s" % ud.localfile)
132
133    def _build_ccase_command(self, ud, command):
134        """
135        Build up a commandline based on ud
136        command is: mkview, setcs, rmview
137        """
138        options = []
139
140        if "rcleartool" in ud.basecmd:
141            options.append("-server %s" % ud.server)
142
143        basecmd = "%s %s" % (ud.basecmd, command)
144
145        if command == 'mkview':
146            if not "rcleartool" in ud.basecmd:
147                # Cleartool needs a -snapshot view
148                options.append("-snapshot")
149            options.append("-tag %s" % ud.viewname)
150            options.append(ud.viewdir)
151
152        elif command == 'rmview':
153            options.append("-force")
154            options.append("%s" % ud.viewdir)
155
156        elif command == 'setcs':
157            options.append("-overwrite")
158            options.append(ud.configspecfile)
159
160        else:
161            raise FetchError("Invalid ccase command %s" % command)
162
163        ccasecmd = "%s %s" % (basecmd, " ".join(options))
164        self.debug("ccasecmd = %s" % ccasecmd)
165        return ccasecmd
166
167    def _write_configspec(self, ud, d):
168        """
169        Create config spec file (ud.configspecfile) for ccase view
170        """
171        config_spec = ""
172        custom_config_spec = d.getVar("CCASE_CUSTOM_CONFIG_SPEC", d)
173        if custom_config_spec is not None:
174            for line in custom_config_spec.split("\\n"):
175                config_spec += line+"\n"
176            bb.warn("A custom config spec has been set, SRCREV is only relevant for the tarball name.")
177        else:
178            config_spec += "element * CHECKEDOUT\n"
179            config_spec += "element * %s\n" % ud.label
180            config_spec += "load %s%s\n" % (ud.vob, ud.module)
181
182        logger.info("Using config spec: \n%s" % config_spec)
183
184        with open(ud.configspecfile, 'w') as f:
185            f.write(config_spec)
186
187    def _remove_view(self, ud, d):
188        if os.path.exists(ud.viewdir):
189            cmd = self._build_ccase_command(ud, 'rmview');
190            logger.info("cleaning up [VOB=%s label=%s view=%s]", ud.vob, ud.label, ud.viewname)
191            bb.fetch2.check_network_access(d, cmd, ud.url)
192            output = runfetchcmd(cmd, d, workdir=ud.ccasedir)
193            logger.info("rmview output: %s", output)
194
195    def need_update(self, ud, d):
196        if ("LATEST" in ud.label) or (ud.customspec and "LATEST" in ud.customspec):
197            ud.identifier += "-%s" % d.getVar("DATETIME")
198            return True
199        if os.path.exists(ud.localpath):
200            return False
201        return True
202
203    def supports_srcrev(self):
204        return True
205
206    def sortable_revision(self, ud, d, name):
207        return False, ud.identifier
208
209    def download(self, ud, d):
210        """Fetch url"""
211
212        # Make a fresh view
213        bb.utils.mkdirhier(ud.ccasedir)
214        self._write_configspec(ud, d)
215        cmd = self._build_ccase_command(ud, 'mkview')
216        logger.info("creating view [VOB=%s label=%s view=%s]", ud.vob, ud.label, ud.viewname)
217        bb.fetch2.check_network_access(d, cmd, ud.url)
218        try:
219            runfetchcmd(cmd, d)
220        except FetchError as e:
221            if "CRCLI2008E" in e.msg:
222                raise FetchError("%s\n%s\n" % (e.msg, "Call `rcleartool login` in your console to authenticate to the clearcase server before running bitbake."))
223            else:
224                raise e
225
226        # Set configspec: Setting the configspec effectively fetches the files as defined in the configspec
227        cmd = self._build_ccase_command(ud, 'setcs');
228        logger.info("fetching data [VOB=%s label=%s view=%s]", ud.vob, ud.label, ud.viewname)
229        bb.fetch2.check_network_access(d, cmd, ud.url)
230        output = runfetchcmd(cmd, d, workdir=ud.viewdir)
231        logger.info("%s", output)
232
233        # Copy the configspec to the viewdir so we have it in our source tarball later
234        shutil.copyfile(ud.configspecfile, os.path.join(ud.viewdir, ud.csname))
235
236        # Clean clearcase meta-data before tar
237
238        runfetchcmd('tar -czf "%s" .' % (ud.localpath), d, cleanup = [ud.localpath], workdir = ud.viewdir)
239
240        # Clean up so we can create a new view next time
241        self.clean(ud, d);
242
243    def clean(self, ud, d):
244        self._remove_view(ud, d)
245        bb.utils.remove(ud.configspecfile)
246