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