diff options
Diffstat (limited to 'bitbake/lib/bblayers/layerindex.py')
-rw-r--r-- | bitbake/lib/bblayers/layerindex.py | 304 |
1 files changed, 119 insertions, 185 deletions
diff --git a/bitbake/lib/bblayers/layerindex.py b/bitbake/lib/bblayers/layerindex.py index 53c858db16..121c7d515a 100644 --- a/bitbake/lib/bblayers/layerindex.py +++ b/bitbake/lib/bblayers/layerindex.py | |||
@@ -1,10 +1,9 @@ | |||
1 | import layerindexlib | ||
2 | |||
1 | import argparse | 3 | import argparse |
2 | import http.client | ||
3 | import json | ||
4 | import logging | 4 | import logging |
5 | import os | 5 | import os |
6 | import subprocess | 6 | import subprocess |
7 | import urllib.parse | ||
8 | 7 | ||
9 | from bblayers.action import ActionPlugin | 8 | from bblayers.action import ActionPlugin |
10 | 9 | ||
@@ -21,110 +20,6 @@ class LayerIndexPlugin(ActionPlugin): | |||
21 | This class inherits ActionPlugin to get do_add_layer. | 20 | This class inherits ActionPlugin to get do_add_layer. |
22 | """ | 21 | """ |
23 | 22 | ||
24 | def get_json_data(self, apiurl): | ||
25 | proxy_settings = os.environ.get("http_proxy", None) | ||
26 | conn = None | ||
27 | _parsedurl = urllib.parse.urlparse(apiurl) | ||
28 | path = _parsedurl.path | ||
29 | query = _parsedurl.query | ||
30 | |||
31 | def parse_url(url): | ||
32 | parsedurl = urllib.parse.urlparse(url) | ||
33 | if parsedurl.netloc[0] == '[': | ||
34 | host, port = parsedurl.netloc[1:].split(']', 1) | ||
35 | if ':' in port: | ||
36 | port = port.rsplit(':', 1)[1] | ||
37 | else: | ||
38 | port = None | ||
39 | else: | ||
40 | if parsedurl.netloc.count(':') == 1: | ||
41 | (host, port) = parsedurl.netloc.split(":") | ||
42 | else: | ||
43 | host = parsedurl.netloc | ||
44 | port = None | ||
45 | return (host, 80 if port is None else int(port)) | ||
46 | |||
47 | if proxy_settings is None: | ||
48 | host, port = parse_url(apiurl) | ||
49 | conn = http.client.HTTPConnection(host, port) | ||
50 | conn.request("GET", path + "?" + query) | ||
51 | else: | ||
52 | host, port = parse_url(proxy_settings) | ||
53 | conn = http.client.HTTPConnection(host, port) | ||
54 | conn.request("GET", apiurl) | ||
55 | |||
56 | r = conn.getresponse() | ||
57 | if r.status != 200: | ||
58 | raise Exception("Failed to read " + path + ": %d %s" % (r.status, r.reason)) | ||
59 | return json.loads(r.read().decode()) | ||
60 | |||
61 | def get_layer_deps(self, layername, layeritems, layerbranches, layerdependencies, branchnum, selfname=False): | ||
62 | def layeritems_info_id(items_name, layeritems): | ||
63 | litems_id = None | ||
64 | for li in layeritems: | ||
65 | if li['name'] == items_name: | ||
66 | litems_id = li['id'] | ||
67 | break | ||
68 | return litems_id | ||
69 | |||
70 | def layerbranches_info(items_id, layerbranches): | ||
71 | lbranch = {} | ||
72 | for lb in layerbranches: | ||
73 | if lb['layer'] == items_id and lb['branch'] == branchnum: | ||
74 | lbranch['id'] = lb['id'] | ||
75 | lbranch['vcs_subdir'] = lb['vcs_subdir'] | ||
76 | break | ||
77 | return lbranch | ||
78 | |||
79 | def layerdependencies_info(lb_id, layerdependencies): | ||
80 | ld_deps = [] | ||
81 | for ld in layerdependencies: | ||
82 | if ld['layerbranch'] == lb_id and not ld['dependency'] in ld_deps: | ||
83 | ld_deps.append(ld['dependency']) | ||
84 | if not ld_deps: | ||
85 | logger.error("The dependency of layerDependencies is not found.") | ||
86 | return ld_deps | ||
87 | |||
88 | def layeritems_info_name_subdir(items_id, layeritems): | ||
89 | litems = {} | ||
90 | for li in layeritems: | ||
91 | if li['id'] == items_id: | ||
92 | litems['vcs_url'] = li['vcs_url'] | ||
93 | litems['name'] = li['name'] | ||
94 | break | ||
95 | return litems | ||
96 | |||
97 | if selfname: | ||
98 | selfid = layeritems_info_id(layername, layeritems) | ||
99 | lbinfo = layerbranches_info(selfid, layerbranches) | ||
100 | if lbinfo: | ||
101 | selfsubdir = lbinfo['vcs_subdir'] | ||
102 | else: | ||
103 | logger.error("%s is not found in the specified branch" % layername) | ||
104 | return | ||
105 | selfurl = layeritems_info_name_subdir(selfid, layeritems)['vcs_url'] | ||
106 | if selfurl: | ||
107 | return selfurl, selfsubdir | ||
108 | else: | ||
109 | logger.error("Cannot get layer %s git repo and subdir" % layername) | ||
110 | return | ||
111 | ldict = {} | ||
112 | itemsid = layeritems_info_id(layername, layeritems) | ||
113 | if not itemsid: | ||
114 | return layername, None | ||
115 | lbid = layerbranches_info(itemsid, layerbranches) | ||
116 | if lbid: | ||
117 | lbid = layerbranches_info(itemsid, layerbranches)['id'] | ||
118 | else: | ||
119 | logger.error("%s is not found in the specified branch" % layername) | ||
120 | return None, None | ||
121 | for dependency in layerdependencies_info(lbid, layerdependencies): | ||
122 | lname = layeritems_info_name_subdir(dependency, layeritems)['name'] | ||
123 | lurl = layeritems_info_name_subdir(dependency, layeritems)['vcs_url'] | ||
124 | lsubdir = layerbranches_info(dependency, layerbranches)['vcs_subdir'] | ||
125 | ldict[lname] = lurl, lsubdir | ||
126 | return None, ldict | ||
127 | |||
128 | def get_fetch_layer(self, fetchdir, url, subdir, fetch_layer): | 23 | def get_fetch_layer(self, fetchdir, url, subdir, fetch_layer): |
129 | layername = self.get_layer_name(url) | 24 | layername = self.get_layer_name(url) |
130 | if os.path.splitext(layername)[1] == '.git': | 25 | if os.path.splitext(layername)[1] == '.git': |
@@ -136,95 +31,124 @@ class LayerIndexPlugin(ActionPlugin): | |||
136 | result = subprocess.call('git clone %s %s' % (url, repodir), shell = True) | 31 | result = subprocess.call('git clone %s %s' % (url, repodir), shell = True) |
137 | if result: | 32 | if result: |
138 | logger.error("Failed to download %s" % url) | 33 | logger.error("Failed to download %s" % url) |
139 | return None, None | 34 | return None, None, None |
140 | else: | 35 | else: |
141 | return layername, layerdir | 36 | return subdir, layername, layerdir |
142 | else: | 37 | else: |
143 | logger.plain("Repository %s needs to be fetched" % url) | 38 | logger.plain("Repository %s needs to be fetched" % url) |
144 | return layername, layerdir | 39 | return subdir, layername, layerdir |
145 | elif os.path.exists(layerdir): | 40 | elif os.path.exists(layerdir): |
146 | return layername, layerdir | 41 | return subdir, layername, layerdir |
147 | else: | 42 | else: |
148 | logger.error("%s is not in %s" % (url, subdir)) | 43 | logger.error("%s is not in %s" % (url, subdir)) |
149 | return None, None | 44 | return None, None, None |
150 | 45 | ||
151 | def do_layerindex_fetch(self, args): | 46 | def do_layerindex_fetch(self, args): |
152 | """Fetches a layer from a layer index along with its dependent layers, and adds them to conf/bblayers.conf. | 47 | """Fetches a layer from a layer index along with its dependent layers, and adds them to conf/bblayers.conf. |
153 | """ | 48 | """ |
154 | apiurl = self.tinfoil.config_data.getVar('BBLAYERS_LAYERINDEX_URL') | 49 | |
155 | if not apiurl: | 50 | def _construct_url(baseurls, branches): |
156 | logger.error("Cannot get BBLAYERS_LAYERINDEX_URL") | 51 | urls = [] |
157 | return 1 | 52 | for baseurl in baseurls: |
53 | if baseurl[-1] != '/': | ||
54 | baseurl += '/' | ||
55 | |||
56 | if not baseurl.startswith('cooker'): | ||
57 | baseurl += "api/" | ||
58 | |||
59 | if branches: | ||
60 | baseurl += ";branch=%s" % ','.join(branches) | ||
61 | |||
62 | urls.append(baseurl) | ||
63 | |||
64 | return urls | ||
65 | |||
66 | |||
67 | # Set the default... | ||
68 | if args.branch: | ||
69 | branches = [args.branch] | ||
158 | else: | 70 | else: |
159 | if apiurl[-1] != '/': | 71 | branches = (self.tinfoil.config_data.getVar('LAYERSERIES_CORENAMES') or 'master').split() |
160 | apiurl += '/' | 72 | logger.debug(1, 'Trying branches: %s' % branches) |
161 | apiurl += "api/" | ||
162 | apilinks = self.get_json_data(apiurl) | ||
163 | branches = self.get_json_data(apilinks['branches']) | ||
164 | |||
165 | branchnum = 0 | ||
166 | for branch in branches: | ||
167 | if branch['name'] == args.branch: | ||
168 | branchnum = branch['id'] | ||
169 | break | ||
170 | if branchnum == 0: | ||
171 | validbranches = ', '.join([branch['name'] for branch in branches]) | ||
172 | logger.error('Invalid layer branch name "%s". Valid branches: %s' % (args.branch, validbranches)) | ||
173 | return 1 | ||
174 | 73 | ||
175 | ignore_layers = [] | 74 | ignore_layers = [] |
176 | for collection in self.tinfoil.config_data.getVar('BBFILE_COLLECTIONS').split(): | ||
177 | lname = self.tinfoil.config_data.getVar('BBLAYERS_LAYERINDEX_NAME_%s' % collection) | ||
178 | if lname: | ||
179 | ignore_layers.append(lname) | ||
180 | |||
181 | if args.ignore: | 75 | if args.ignore: |
182 | ignore_layers.extend(args.ignore.split(',')) | 76 | ignore_layers.extend(args.ignore.split(',')) |
183 | 77 | ||
184 | layeritems = self.get_json_data(apilinks['layerItems']) | 78 | # Load the cooker DB |
185 | layerbranches = self.get_json_data(apilinks['layerBranches']) | 79 | cookerIndex = layerindexlib.LayerIndex(self.tinfoil.config_data) |
186 | layerdependencies = self.get_json_data(apilinks['layerDependencies']) | 80 | cookerIndex.load_layerindex('cooker://', load='layerDependencies') |
187 | invaluenames = [] | 81 | |
188 | repourls = {} | 82 | # Fast path, check if we already have what has been requested! |
189 | printlayers = [] | 83 | (dependencies, invalidnames) = cookerIndex.find_dependencies(names=args.layername, ignores=ignore_layers) |
190 | 84 | if not args.show_only and not invalidnames: | |
191 | def query_dependencies(layers, layeritems, layerbranches, layerdependencies, branchnum): | 85 | logger.plain("You already have the requested layer(s): %s" % args.layername) |
192 | depslayer = [] | 86 | return 0 |
193 | for layername in layers: | 87 | |
194 | invaluename, layerdict = self.get_layer_deps(layername, layeritems, layerbranches, layerdependencies, branchnum) | 88 | # The information to show is already in the cookerIndex |
195 | if layerdict: | 89 | if invalidnames: |
196 | repourls[layername] = self.get_layer_deps(layername, layeritems, layerbranches, layerdependencies, branchnum, selfname=True) | 90 | # General URL to use to access the layer index |
197 | for layer in layerdict: | 91 | # While there is ONE right now, we're expect users could enter several |
198 | if not layer in ignore_layers: | 92 | apiurl = self.tinfoil.config_data.getVar('BBLAYERS_LAYERINDEX_URL').split() |
199 | depslayer.append(layer) | 93 | if not apiurl: |
200 | printlayers.append((layername, layer, layerdict[layer][0], layerdict[layer][1])) | 94 | logger.error("Cannot get BBLAYERS_LAYERINDEX_URL") |
201 | if not layer in ignore_layers and not layer in repourls: | 95 | return 1 |
202 | repourls[layer] = (layerdict[layer][0], layerdict[layer][1]) | 96 | |
203 | if invaluename and not invaluename in invaluenames: | 97 | remoteIndex = layerindexlib.LayerIndex(self.tinfoil.config_data) |
204 | invaluenames.append(invaluename) | 98 | |
205 | return depslayer | 99 | for remoteurl in _construct_url(apiurl, branches): |
206 | 100 | logger.plain("Loading %s..." % remoteurl) | |
207 | depslayers = query_dependencies(args.layername, layeritems, layerbranches, layerdependencies, branchnum) | 101 | remoteIndex.load_layerindex(remoteurl) |
208 | while depslayers: | 102 | |
209 | depslayer = query_dependencies(depslayers, layeritems, layerbranches, layerdependencies, branchnum) | 103 | if remoteIndex.is_empty(): |
210 | depslayers = depslayer | 104 | logger.error("Remote layer index %s is empty for branches %s" % (apiurl, branches)) |
211 | if invaluenames: | 105 | return 1 |
212 | for invaluename in invaluenames: | 106 | |
213 | logger.error('Layer "%s" not found in layer index' % invaluename) | 107 | lIndex = cookerIndex + remoteIndex |
214 | return 1 | 108 | |
215 | logger.plain("%s %s %s %s" % ("Layer".ljust(19), "Required by".ljust(19), "Git repository".ljust(54), "Subdirectory")) | 109 | (dependencies, invalidnames) = lIndex.find_dependencies(names=args.layername, ignores=ignore_layers) |
216 | logger.plain('=' * 115) | 110 | |
217 | for layername in args.layername: | 111 | if invalidnames: |
218 | layerurl = repourls[layername] | 112 | for invaluename in invalidnames: |
219 | logger.plain("%s %s %s %s" % (layername.ljust(20), '-'.ljust(20), layerurl[0].ljust(55), layerurl[1])) | 113 | logger.error('Layer "%s" not found in layer index' % invaluename) |
220 | printedlayers = [] | 114 | return 1 |
221 | for layer, dependency, gitrepo, subdirectory in printlayers: | 115 | |
222 | if dependency in printedlayers: | 116 | logger.plain("%s %s %s" % ("Layer".ljust(49), "Git repository (branch)".ljust(54), "Subdirectory")) |
223 | continue | 117 | logger.plain('=' * 125) |
224 | logger.plain("%s %s %s %s" % (dependency.ljust(20), layer.ljust(20), gitrepo.ljust(55), subdirectory)) | 118 | |
225 | printedlayers.append(dependency) | 119 | for deplayerbranch in dependencies: |
226 | 120 | layerBranch = dependencies[deplayerbranch][0] | |
227 | if repourls: | 121 | |
122 | # TODO: Determine display behavior | ||
123 | # This is the local content, uncomment to hide local | ||
124 | # layers from the display. | ||
125 | #if layerBranch.index.config['TYPE'] == 'cooker': | ||
126 | # continue | ||
127 | |||
128 | layerDeps = dependencies[deplayerbranch][1:] | ||
129 | |||
130 | requiredby = [] | ||
131 | recommendedby = [] | ||
132 | for dep in layerDeps: | ||
133 | if dep.required: | ||
134 | requiredby.append(dep.layer.name) | ||
135 | else: | ||
136 | recommendedby.append(dep.layer.name) | ||
137 | |||
138 | logger.plain('%s %s %s' % (("%s:%s:%s" % | ||
139 | (layerBranch.index.config['DESCRIPTION'], | ||
140 | layerBranch.branch.name, | ||
141 | layerBranch.layer.name)).ljust(50), | ||
142 | ("%s (%s)" % (layerBranch.layer.vcs_url, | ||
143 | layerBranch.actual_branch)).ljust(55), | ||
144 | layerBranch.vcs_subdir | ||
145 | )) | ||
146 | if requiredby: | ||
147 | logger.plain(' required by: %s' % ' '.join(requiredby)) | ||
148 | if recommendedby: | ||
149 | logger.plain(' recommended by: %s' % ' '.join(recommendedby)) | ||
150 | |||
151 | if dependencies: | ||
228 | fetchdir = self.tinfoil.config_data.getVar('BBLAYERS_FETCH_DIR') | 152 | fetchdir = self.tinfoil.config_data.getVar('BBLAYERS_FETCH_DIR') |
229 | if not fetchdir: | 153 | if not fetchdir: |
230 | logger.error("Cannot get BBLAYERS_FETCH_DIR") | 154 | logger.error("Cannot get BBLAYERS_FETCH_DIR") |
@@ -232,8 +156,18 @@ class LayerIndexPlugin(ActionPlugin): | |||
232 | if not os.path.exists(fetchdir): | 156 | if not os.path.exists(fetchdir): |
233 | os.makedirs(fetchdir) | 157 | os.makedirs(fetchdir) |
234 | addlayers = [] | 158 | addlayers = [] |
235 | for repourl, subdir in repourls.values(): | 159 | |
236 | name, layerdir = self.get_fetch_layer(fetchdir, repourl, subdir, not args.show_only) | 160 | for deplayerbranch in dependencies: |
161 | layerBranch = dependencies[deplayerbranch][0] | ||
162 | |||
163 | if layerBranch.index.config['TYPE'] == 'cooker': | ||
164 | # Anything loaded via cooker is already local, skip it | ||
165 | continue | ||
166 | |||
167 | subdir, name, layerdir = self.get_fetch_layer(fetchdir, | ||
168 | layerBranch.layer.vcs_url, | ||
169 | layerBranch.vcs_subdir, | ||
170 | not args.show_only) | ||
237 | if not name: | 171 | if not name: |
238 | # Error already shown | 172 | # Error already shown |
239 | return 1 | 173 | return 1 |
@@ -242,7 +176,7 @@ class LayerIndexPlugin(ActionPlugin): | |||
242 | localargs = argparse.Namespace() | 176 | localargs = argparse.Namespace() |
243 | localargs.layerdir = [] | 177 | localargs.layerdir = [] |
244 | localargs.force = args.force | 178 | localargs.force = args.force |
245 | for subdir, name, layerdir in set(addlayers): | 179 | for subdir, name, layerdir in addlayers: |
246 | if os.path.exists(layerdir): | 180 | if os.path.exists(layerdir): |
247 | if subdir: | 181 | if subdir: |
248 | logger.plain("Adding layer \"%s\" (%s) to conf/bblayers.conf" % (subdir, layerdir)) | 182 | logger.plain("Adding layer \"%s\" (%s) to conf/bblayers.conf" % (subdir, layerdir)) |
@@ -265,10 +199,10 @@ class LayerIndexPlugin(ActionPlugin): | |||
265 | def register_commands(self, sp): | 199 | def register_commands(self, sp): |
266 | parser_layerindex_fetch = self.add_command(sp, 'layerindex-fetch', self.do_layerindex_fetch) | 200 | parser_layerindex_fetch = self.add_command(sp, 'layerindex-fetch', self.do_layerindex_fetch) |
267 | parser_layerindex_fetch.add_argument('-n', '--show-only', help='show dependencies and do nothing else', action='store_true') | 201 | parser_layerindex_fetch.add_argument('-n', '--show-only', help='show dependencies and do nothing else', action='store_true') |
268 | parser_layerindex_fetch.add_argument('-b', '--branch', help='branch name to fetch (default %(default)s)', default='master') | 202 | parser_layerindex_fetch.add_argument('-b', '--branch', help='branch name to fetch') |
269 | 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') | 203 | 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') |
270 | parser_layerindex_fetch.add_argument('layername', nargs='+', help='layer to fetch') | 204 | parser_layerindex_fetch.add_argument('layername', nargs='+', help='layer to fetch') |
271 | 205 | ||
272 | parser_layerindex_show_depends = self.add_command(sp, 'layerindex-show-depends', self.do_layerindex_show_depends) | 206 | parser_layerindex_show_depends = self.add_command(sp, 'layerindex-show-depends', self.do_layerindex_show_depends) |
273 | parser_layerindex_show_depends.add_argument('-b', '--branch', help='branch name to fetch (default %(default)s)', default='master') | 207 | parser_layerindex_show_depends.add_argument('-b', '--branch', help='branch name to fetch') |
274 | parser_layerindex_show_depends.add_argument('layername', nargs='+', help='layer to query') | 208 | parser_layerindex_show_depends.add_argument('layername', nargs='+', help='layer to query') |