blob: 95b67a662144215a156389efec3f1b823f7f002f [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001#
2# SPDX-License-Identifier: GPL-2.0-only
3#
4
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08005import layerindexlib
6
Patrick Williamsc0f7c042017-02-23 20:41:17 -06007import argparse
Patrick Williamsc0f7c042017-02-23 20:41:17 -06008import logging
9import os
10import subprocess
Patrick Williamsc0f7c042017-02-23 20:41:17 -060011
12from bblayers.action import ActionPlugin
13
14logger = logging.getLogger('bitbake-layers')
15
16
17def plugin_init(plugins):
18 return LayerIndexPlugin()
19
20
21class LayerIndexPlugin(ActionPlugin):
22 """Subcommands for interacting with the layer index.
23
24 This class inherits ActionPlugin to get do_add_layer.
25 """
26
Andrew Geissler82c905d2020-04-13 13:39:40 -050027 def get_fetch_layer(self, fetchdir, url, subdir, fetch_layer, branch, shallow=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060028 layername = self.get_layer_name(url)
29 if os.path.splitext(layername)[1] == '.git':
30 layername = os.path.splitext(layername)[0]
31 repodir = os.path.join(fetchdir, layername)
32 layerdir = os.path.join(repodir, subdir)
33 if not os.path.exists(repodir):
34 if fetch_layer:
Andrew Geissler82c905d2020-04-13 13:39:40 -050035 cmd = ['git', 'clone']
36 if shallow:
37 cmd.extend(['--depth', '1'])
38 if branch:
39 cmd.extend(['-b' , branch])
40 cmd.extend([url, repodir])
41 result = subprocess.call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060042 if result:
Andrew Geissler82c905d2020-04-13 13:39:40 -050043 logger.error("Failed to download %s (%s)" % (url, branch))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080044 return None, None, None
Patrick Williamsc0f7c042017-02-23 20:41:17 -060045 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080046 return subdir, layername, layerdir
Patrick Williamsc0f7c042017-02-23 20:41:17 -060047 else:
48 logger.plain("Repository %s needs to be fetched" % url)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080049 return subdir, layername, layerdir
Patrick Williamsc0f7c042017-02-23 20:41:17 -060050 elif os.path.exists(layerdir):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080051 return subdir, layername, layerdir
Patrick Williamsc0f7c042017-02-23 20:41:17 -060052 else:
53 logger.error("%s is not in %s" % (url, subdir))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080054 return None, None, None
Patrick Williamsc0f7c042017-02-23 20:41:17 -060055
56 def do_layerindex_fetch(self, args):
57 """Fetches a layer from a layer index along with its dependent layers, and adds them to conf/bblayers.conf.
58"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -060059
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080060 def _construct_url(baseurls, branches):
61 urls = []
62 for baseurl in baseurls:
63 if baseurl[-1] != '/':
64 baseurl += '/'
65
66 if not baseurl.startswith('cooker'):
67 baseurl += "api/"
68
69 if branches:
70 baseurl += ";branch=%s" % ','.join(branches)
71
72 urls.append(baseurl)
73
74 return urls
75
76
77 # Set the default...
78 if args.branch:
79 branches = [args.branch]
80 else:
81 branches = (self.tinfoil.config_data.getVar('LAYERSERIES_CORENAMES') or 'master').split()
82 logger.debug(1, 'Trying branches: %s' % branches)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060083
84 ignore_layers = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -060085 if args.ignore:
86 ignore_layers.extend(args.ignore.split(','))
87
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080088 # Load the cooker DB
89 cookerIndex = layerindexlib.LayerIndex(self.tinfoil.config_data)
90 cookerIndex.load_layerindex('cooker://', load='layerDependencies')
Patrick Williamsc0f7c042017-02-23 20:41:17 -060091
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080092 # Fast path, check if we already have what has been requested!
93 (dependencies, invalidnames) = cookerIndex.find_dependencies(names=args.layername, ignores=ignore_layers)
94 if not args.show_only and not invalidnames:
95 logger.plain("You already have the requested layer(s): %s" % args.layername)
96 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -060097
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080098 # The information to show is already in the cookerIndex
99 if invalidnames:
100 # General URL to use to access the layer index
101 # While there is ONE right now, we're expect users could enter several
102 apiurl = self.tinfoil.config_data.getVar('BBLAYERS_LAYERINDEX_URL').split()
103 if not apiurl:
104 logger.error("Cannot get BBLAYERS_LAYERINDEX_URL")
105 return 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600106
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800107 remoteIndex = layerindexlib.LayerIndex(self.tinfoil.config_data)
108
109 for remoteurl in _construct_url(apiurl, branches):
110 logger.plain("Loading %s..." % remoteurl)
111 remoteIndex.load_layerindex(remoteurl)
112
113 if remoteIndex.is_empty():
114 logger.error("Remote layer index %s is empty for branches %s" % (apiurl, branches))
115 return 1
116
117 lIndex = cookerIndex + remoteIndex
118
119 (dependencies, invalidnames) = lIndex.find_dependencies(names=args.layername, ignores=ignore_layers)
120
121 if invalidnames:
122 for invaluename in invalidnames:
123 logger.error('Layer "%s" not found in layer index' % invaluename)
124 return 1
125
126 logger.plain("%s %s %s" % ("Layer".ljust(49), "Git repository (branch)".ljust(54), "Subdirectory"))
127 logger.plain('=' * 125)
128
129 for deplayerbranch in dependencies:
130 layerBranch = dependencies[deplayerbranch][0]
131
132 # TODO: Determine display behavior
133 # This is the local content, uncomment to hide local
134 # layers from the display.
135 #if layerBranch.index.config['TYPE'] == 'cooker':
136 # continue
137
138 layerDeps = dependencies[deplayerbranch][1:]
139
140 requiredby = []
141 recommendedby = []
142 for dep in layerDeps:
143 if dep.required:
144 requiredby.append(dep.layer.name)
145 else:
146 recommendedby.append(dep.layer.name)
147
148 logger.plain('%s %s %s' % (("%s:%s:%s" %
149 (layerBranch.index.config['DESCRIPTION'],
150 layerBranch.branch.name,
151 layerBranch.layer.name)).ljust(50),
152 ("%s (%s)" % (layerBranch.layer.vcs_url,
153 layerBranch.actual_branch)).ljust(55),
154 layerBranch.vcs_subdir
155 ))
156 if requiredby:
157 logger.plain(' required by: %s' % ' '.join(requiredby))
158 if recommendedby:
159 logger.plain(' recommended by: %s' % ' '.join(recommendedby))
160
161 if dependencies:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500162 fetchdir = self.tinfoil.config_data.getVar('BBLAYERS_FETCH_DIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600163 if not fetchdir:
164 logger.error("Cannot get BBLAYERS_FETCH_DIR")
165 return 1
166 if not os.path.exists(fetchdir):
167 os.makedirs(fetchdir)
168 addlayers = []
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800169
170 for deplayerbranch in dependencies:
171 layerBranch = dependencies[deplayerbranch][0]
172
173 if layerBranch.index.config['TYPE'] == 'cooker':
174 # Anything loaded via cooker is already local, skip it
175 continue
176
177 subdir, name, layerdir = self.get_fetch_layer(fetchdir,
178 layerBranch.layer.vcs_url,
179 layerBranch.vcs_subdir,
Andrew Geissler82c905d2020-04-13 13:39:40 -0500180 not args.show_only,
181 layerBranch.actual_branch,
182 args.shallow)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600183 if not name:
184 # Error already shown
185 return 1
186 addlayers.append((subdir, name, layerdir))
187 if not args.show_only:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800188 localargs = argparse.Namespace()
189 localargs.layerdir = []
190 localargs.force = args.force
191 for subdir, name, layerdir in addlayers:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600192 if os.path.exists(layerdir):
193 if subdir:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800194 logger.plain("Adding layer \"%s\" (%s) to conf/bblayers.conf" % (subdir, layerdir))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600195 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800196 logger.plain("Adding layer \"%s\" (%s) to conf/bblayers.conf" % (name, layerdir))
197 localargs.layerdir.append(layerdir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600198 else:
199 break
200
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800201 if localargs.layerdir:
202 self.do_add_layer(localargs)
203
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600204 def do_layerindex_show_depends(self, args):
205 """Find layer dependencies from layer index.
206"""
207 args.show_only = True
208 args.ignore = []
209 self.do_layerindex_fetch(args)
210
211 def register_commands(self, sp):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800212 parser_layerindex_fetch = self.add_command(sp, 'layerindex-fetch', self.do_layerindex_fetch, parserecipes=False)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600213 parser_layerindex_fetch.add_argument('-n', '--show-only', help='show dependencies and do nothing else', action='store_true')
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800214 parser_layerindex_fetch.add_argument('-b', '--branch', help='branch name to fetch')
Andrew Geissler82c905d2020-04-13 13:39:40 -0500215 parser_layerindex_fetch.add_argument('-s', '--shallow', help='do only shallow clones (--depth=1)', action='store_true')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600216 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')
217 parser_layerindex_fetch.add_argument('layername', nargs='+', help='layer to fetch')
218
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800219 parser_layerindex_show_depends = self.add_command(sp, 'layerindex-show-depends', self.do_layerindex_show_depends, parserecipes=False)
220 parser_layerindex_show_depends.add_argument('-b', '--branch', help='branch name to fetch')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600221 parser_layerindex_show_depends.add_argument('layername', nargs='+', help='layer to query')