xref: /OK3568_Linux_fs/yocto/poky/bitbake/lib/bblayers/layerindex.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1#
2# Copyright BitBake Contributors
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6
7import layerindexlib
8
9import argparse
10import logging
11import os
12import subprocess
13
14from bblayers.action import ActionPlugin
15
16logger = logging.getLogger('bitbake-layers')
17
18
19def plugin_init(plugins):
20    return LayerIndexPlugin()
21
22
23class LayerIndexPlugin(ActionPlugin):
24    """Subcommands for interacting with the layer index.
25
26    This class inherits ActionPlugin to get do_add_layer.
27    """
28
29    def get_fetch_layer(self, fetchdir, url, subdir, fetch_layer, branch, shallow=False):
30        layername = self.get_layer_name(url)
31        if os.path.splitext(layername)[1] == '.git':
32            layername = os.path.splitext(layername)[0]
33        repodir = os.path.join(fetchdir, layername)
34        layerdir = os.path.join(repodir, subdir)
35        if not os.path.exists(repodir):
36            if fetch_layer:
37                cmd = ['git', 'clone']
38                if shallow:
39                    cmd.extend(['--depth', '1'])
40                if branch:
41                    cmd.extend(['-b' , branch])
42                cmd.extend([url, repodir])
43                result = subprocess.call(cmd)
44                if result:
45                    logger.error("Failed to download %s (%s)" % (url, branch))
46                    return None, None, None
47                else:
48                    return subdir, layername, layerdir
49            else:
50                logger.plain("Repository %s needs to be fetched" % url)
51                return subdir, layername, layerdir
52        elif os.path.exists(layerdir):
53            return subdir, layername, layerdir
54        else:
55            logger.error("%s is not in %s" % (url, subdir))
56        return None, None, None
57
58    def do_layerindex_fetch(self, args):
59        """Fetches a layer from a layer index along with its dependent layers, and adds them to conf/bblayers.conf.
60"""
61
62        def _construct_url(baseurls, branches):
63            urls = []
64            for baseurl in baseurls:
65                if baseurl[-1] != '/':
66                    baseurl += '/'
67
68                if not baseurl.startswith('cooker'):
69                    baseurl += "api/"
70
71                if branches:
72                    baseurl += ";branch=%s" % ','.join(branches)
73
74                urls.append(baseurl)
75
76            return urls
77
78
79        # Set the default...
80        if args.branch:
81            branches = [args.branch]
82        else:
83            branches = (self.tinfoil.config_data.getVar('LAYERSERIES_CORENAMES') or 'master').split()
84        logger.debug('Trying branches: %s' % branches)
85
86        ignore_layers = []
87        if args.ignore:
88            ignore_layers.extend(args.ignore.split(','))
89
90        # Load the cooker DB
91        cookerIndex = layerindexlib.LayerIndex(self.tinfoil.config_data)
92        cookerIndex.load_layerindex('cooker://', load='layerDependencies')
93
94        # Fast path, check if we already have what has been requested!
95        (dependencies, invalidnames) = cookerIndex.find_dependencies(names=args.layername, ignores=ignore_layers)
96        if not args.show_only and not invalidnames:
97            logger.plain("You already have the requested layer(s): %s" % args.layername)
98            return 0
99
100        # The information to show is already in the cookerIndex
101        if invalidnames:
102            # General URL to use to access the layer index
103            # While there is ONE right now, we're expect users could enter several
104            apiurl = self.tinfoil.config_data.getVar('BBLAYERS_LAYERINDEX_URL').split()
105            if not apiurl:
106                logger.error("Cannot get BBLAYERS_LAYERINDEX_URL")
107                return 1
108
109            remoteIndex = layerindexlib.LayerIndex(self.tinfoil.config_data)
110
111            for remoteurl in _construct_url(apiurl, branches):
112                logger.plain("Loading %s..." % remoteurl)
113                remoteIndex.load_layerindex(remoteurl)
114
115            if remoteIndex.is_empty():
116                logger.error("Remote layer index %s is empty for branches %s" % (apiurl, branches))
117                return 1
118
119            lIndex = cookerIndex + remoteIndex
120
121            (dependencies, invalidnames) = lIndex.find_dependencies(names=args.layername, ignores=ignore_layers)
122
123            if invalidnames:
124                for invaluename in invalidnames:
125                    logger.error('Layer "%s" not found in layer index' % invaluename)
126                return 1
127
128        logger.plain("%s  %s  %s" % ("Layer".ljust(49), "Git repository (branch)".ljust(54), "Subdirectory"))
129        logger.plain('=' * 125)
130
131        for deplayerbranch in dependencies:
132            layerBranch = dependencies[deplayerbranch][0]
133
134            # TODO: Determine display behavior
135            # This is the local content, uncomment to hide local
136            # layers from the display.
137            #if layerBranch.index.config['TYPE'] == 'cooker':
138            #    continue
139
140            layerDeps = dependencies[deplayerbranch][1:]
141
142            requiredby = []
143            recommendedby = []
144            for dep in layerDeps:
145                if dep.required:
146                    requiredby.append(dep.layer.name)
147                else:
148                    recommendedby.append(dep.layer.name)
149
150            logger.plain('%s %s %s' % (("%s:%s:%s" %
151                                  (layerBranch.index.config['DESCRIPTION'],
152                                  layerBranch.branch.name,
153                                  layerBranch.layer.name)).ljust(50),
154                                  ("%s (%s)" % (layerBranch.layer.vcs_url,
155                                  layerBranch.actual_branch)).ljust(55),
156                                  layerBranch.vcs_subdir
157                                               ))
158            if requiredby:
159                logger.plain('  required by: %s' % ' '.join(requiredby))
160            if recommendedby:
161                logger.plain('  recommended by: %s' % ' '.join(recommendedby))
162
163        if dependencies:
164            if args.fetchdir:
165                fetchdir = args.fetchdir
166            else:
167                fetchdir = self.tinfoil.config_data.getVar('BBLAYERS_FETCH_DIR')
168                if not fetchdir:
169                    logger.error("Cannot get BBLAYERS_FETCH_DIR")
170                    return 1
171
172            if not os.path.exists(fetchdir):
173                os.makedirs(fetchdir)
174
175            addlayers = []
176
177            for deplayerbranch in dependencies:
178                layerBranch = dependencies[deplayerbranch][0]
179
180                if layerBranch.index.config['TYPE'] == 'cooker':
181                    # Anything loaded via cooker is already local, skip it
182                    continue
183
184                subdir, name, layerdir = self.get_fetch_layer(fetchdir,
185                                                      layerBranch.layer.vcs_url,
186                                                      layerBranch.vcs_subdir,
187                                                      not args.show_only,
188                                                      layerBranch.actual_branch,
189                                                      args.shallow)
190                if not name:
191                    # Error already shown
192                    return 1
193                addlayers.append((subdir, name, layerdir))
194        if not args.show_only:
195            localargs = argparse.Namespace()
196            localargs.layerdir = []
197            localargs.force = args.force
198            for subdir, name, layerdir in addlayers:
199                if os.path.exists(layerdir):
200                    if subdir:
201                        logger.plain("Adding layer \"%s\" (%s) to conf/bblayers.conf" % (subdir, layerdir))
202                    else:
203                        logger.plain("Adding layer \"%s\" (%s) to conf/bblayers.conf" % (name, layerdir))
204                    localargs.layerdir.append(layerdir)
205                else:
206                    break
207
208            if localargs.layerdir:
209                self.do_add_layer(localargs)
210
211    def do_layerindex_show_depends(self, args):
212        """Find layer dependencies from layer index.
213"""
214        args.show_only = True
215        args.ignore = []
216        args.fetchdir = ""
217        args.shallow = True
218        self.do_layerindex_fetch(args)
219
220    def register_commands(self, sp):
221        parser_layerindex_fetch = self.add_command(sp, 'layerindex-fetch', self.do_layerindex_fetch, parserecipes=False)
222        parser_layerindex_fetch.add_argument('-n', '--show-only', help='show dependencies and do nothing else', action='store_true')
223        parser_layerindex_fetch.add_argument('-b', '--branch', help='branch name to fetch')
224        parser_layerindex_fetch.add_argument('-s', '--shallow', help='do only shallow clones (--depth=1)', action='store_true')
225        parser_layerindex_fetch.add_argument('-i', '--ignore', help='assume the specified layers do not need to be fetched/added (separate multiple layers with commas, no spaces)', metavar='LAYER')
226        parser_layerindex_fetch.add_argument('-f', '--fetchdir', help='directory to fetch the layer(s) into (will be created if it does not exist)')
227        parser_layerindex_fetch.add_argument('layername', nargs='+', help='layer to fetch')
228
229        parser_layerindex_show_depends = self.add_command(sp, 'layerindex-show-depends', self.do_layerindex_show_depends, parserecipes=False)
230        parser_layerindex_show_depends.add_argument('-b', '--branch', help='branch name to fetch')
231        parser_layerindex_show_depends.add_argument('layername', nargs='+', help='layer to query')
232