layerindex.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. import argparse
  2. import http.client
  3. import json
  4. import logging
  5. import os
  6. import subprocess
  7. import urllib.parse
  8. from bblayers.action import ActionPlugin
  9. logger = logging.getLogger('bitbake-layers')
  10. def plugin_init(plugins):
  11. return LayerIndexPlugin()
  12. class LayerIndexPlugin(ActionPlugin):
  13. """Subcommands for interacting with the layer index.
  14. This class inherits ActionPlugin to get do_add_layer.
  15. """
  16. def get_json_data(self, apiurl):
  17. proxy_settings = os.environ.get("http_proxy", None)
  18. conn = None
  19. _parsedurl = urllib.parse.urlparse(apiurl)
  20. path = _parsedurl.path
  21. query = _parsedurl.query
  22. def parse_url(url):
  23. parsedurl = urllib.parse.urlparse(url)
  24. if parsedurl.netloc[0] == '[':
  25. host, port = parsedurl.netloc[1:].split(']', 1)
  26. if ':' in port:
  27. port = port.rsplit(':', 1)[1]
  28. else:
  29. port = None
  30. else:
  31. if parsedurl.netloc.count(':') == 1:
  32. (host, port) = parsedurl.netloc.split(":")
  33. else:
  34. host = parsedurl.netloc
  35. port = None
  36. return (host, 80 if port is None else int(port))
  37. if proxy_settings is None:
  38. host, port = parse_url(apiurl)
  39. conn = http.client.HTTPConnection(host, port)
  40. conn.request("GET", path + "?" + query)
  41. else:
  42. host, port = parse_url(proxy_settings)
  43. conn = http.client.HTTPConnection(host, port)
  44. conn.request("GET", apiurl)
  45. r = conn.getresponse()
  46. if r.status != 200:
  47. raise Exception("Failed to read " + path + ": %d %s" % (r.status, r.reason))
  48. return json.loads(r.read())
  49. def get_layer_deps(self, layername, layeritems, layerbranches, layerdependencies, branchnum, selfname=False):
  50. def layeritems_info_id(items_name, layeritems):
  51. litems_id = None
  52. for li in layeritems:
  53. if li['name'] == items_name:
  54. litems_id = li['id']
  55. break
  56. return litems_id
  57. def layerbranches_info(items_id, layerbranches):
  58. lbranch = {}
  59. for lb in layerbranches:
  60. if lb['layer'] == items_id and lb['branch'] == branchnum:
  61. lbranch['id'] = lb['id']
  62. lbranch['vcs_subdir'] = lb['vcs_subdir']
  63. break
  64. return lbranch
  65. def layerdependencies_info(lb_id, layerdependencies):
  66. ld_deps = []
  67. for ld in layerdependencies:
  68. if ld['layerbranch'] == lb_id and not ld['dependency'] in ld_deps:
  69. ld_deps.append(ld['dependency'])
  70. if not ld_deps:
  71. logger.error("The dependency of layerDependencies is not found.")
  72. return ld_deps
  73. def layeritems_info_name_subdir(items_id, layeritems):
  74. litems = {}
  75. for li in layeritems:
  76. if li['id'] == items_id:
  77. litems['vcs_url'] = li['vcs_url']
  78. litems['name'] = li['name']
  79. break
  80. return litems
  81. if selfname:
  82. selfid = layeritems_info_id(layername, layeritems)
  83. lbinfo = layerbranches_info(selfid, layerbranches)
  84. if lbinfo:
  85. selfsubdir = lbinfo['vcs_subdir']
  86. else:
  87. logger.error("%s is not found in the specified branch" % layername)
  88. return
  89. selfurl = layeritems_info_name_subdir(selfid, layeritems)['vcs_url']
  90. if selfurl:
  91. return selfurl, selfsubdir
  92. else:
  93. logger.error("Cannot get layer %s git repo and subdir" % layername)
  94. return
  95. ldict = {}
  96. itemsid = layeritems_info_id(layername, layeritems)
  97. if not itemsid:
  98. return layername, None
  99. lbid = layerbranches_info(itemsid, layerbranches)
  100. if lbid:
  101. lbid = layerbranches_info(itemsid, layerbranches)['id']
  102. else:
  103. logger.error("%s is not found in the specified branch" % layername)
  104. return None, None
  105. for dependency in layerdependencies_info(lbid, layerdependencies):
  106. lname = layeritems_info_name_subdir(dependency, layeritems)['name']
  107. lurl = layeritems_info_name_subdir(dependency, layeritems)['vcs_url']
  108. lsubdir = layerbranches_info(dependency, layerbranches)['vcs_subdir']
  109. ldict[lname] = lurl, lsubdir
  110. return None, ldict
  111. def get_fetch_layer(self, fetchdir, url, subdir, fetch_layer):
  112. layername = self.get_layer_name(url)
  113. if os.path.splitext(layername)[1] == '.git':
  114. layername = os.path.splitext(layername)[0]
  115. repodir = os.path.join(fetchdir, layername)
  116. layerdir = os.path.join(repodir, subdir)
  117. if not os.path.exists(repodir):
  118. if fetch_layer:
  119. result = subprocess.call('git clone %s %s' % (url, repodir), shell = True)
  120. if result:
  121. logger.error("Failed to download %s" % url)
  122. return None, None
  123. else:
  124. return layername, layerdir
  125. else:
  126. logger.plain("Repository %s needs to be fetched" % url)
  127. return layername, layerdir
  128. elif os.path.exists(layerdir):
  129. return layername, layerdir
  130. else:
  131. logger.error("%s is not in %s" % (url, subdir))
  132. return None, None
  133. def do_layerindex_fetch(self, args):
  134. """Fetches a layer from a layer index along with its dependent layers, and adds them to conf/bblayers.conf.
  135. """
  136. apiurl = self.tinfoil.config_data.getVar('BBLAYERS_LAYERINDEX_URL', True)
  137. if not apiurl:
  138. logger.error("Cannot get BBLAYERS_LAYERINDEX_URL")
  139. return 1
  140. else:
  141. if apiurl[-1] != '/':
  142. apiurl += '/'
  143. apiurl += "api/"
  144. apilinks = self.get_json_data(apiurl)
  145. branches = self.get_json_data(apilinks['branches'])
  146. branchnum = 0
  147. for branch in branches:
  148. if branch['name'] == args.branch:
  149. branchnum = branch['id']
  150. break
  151. if branchnum == 0:
  152. validbranches = ', '.join([branch['name'] for branch in branches])
  153. logger.error('Invalid layer branch name "%s". Valid branches: %s' % (args.branch, validbranches))
  154. return 1
  155. ignore_layers = []
  156. for collection in self.tinfoil.config_data.getVar('BBFILE_COLLECTIONS', True).split():
  157. lname = self.tinfoil.config_data.getVar('BBLAYERS_LAYERINDEX_NAME_%s' % collection, True)
  158. if lname:
  159. ignore_layers.append(lname)
  160. if args.ignore:
  161. ignore_layers.extend(args.ignore.split(','))
  162. layeritems = self.get_json_data(apilinks['layerItems'])
  163. layerbranches = self.get_json_data(apilinks['layerBranches'])
  164. layerdependencies = self.get_json_data(apilinks['layerDependencies'])
  165. invaluenames = []
  166. repourls = {}
  167. printlayers = []
  168. def query_dependencies(layers, layeritems, layerbranches, layerdependencies, branchnum):
  169. depslayer = []
  170. for layername in layers:
  171. invaluename, layerdict = self.get_layer_deps(layername, layeritems, layerbranches, layerdependencies, branchnum)
  172. if layerdict:
  173. repourls[layername] = self.get_layer_deps(layername, layeritems, layerbranches, layerdependencies, branchnum, selfname=True)
  174. for layer in layerdict:
  175. if not layer in ignore_layers:
  176. depslayer.append(layer)
  177. printlayers.append((layername, layer, layerdict[layer][0], layerdict[layer][1]))
  178. if not layer in ignore_layers and not layer in repourls:
  179. repourls[layer] = (layerdict[layer][0], layerdict[layer][1])
  180. if invaluename and not invaluename in invaluenames:
  181. invaluenames.append(invaluename)
  182. return depslayer
  183. depslayers = query_dependencies(args.layername, layeritems, layerbranches, layerdependencies, branchnum)
  184. while depslayers:
  185. depslayer = query_dependencies(depslayers, layeritems, layerbranches, layerdependencies, branchnum)
  186. depslayers = depslayer
  187. if invaluenames:
  188. for invaluename in invaluenames:
  189. logger.error('Layer "%s" not found in layer index' % invaluename)
  190. return 1
  191. logger.plain("%s %s %s %s" % ("Layer".ljust(19), "Required by".ljust(19), "Git repository".ljust(54), "Subdirectory"))
  192. logger.plain('=' * 115)
  193. for layername in args.layername:
  194. layerurl = repourls[layername]
  195. logger.plain("%s %s %s %s" % (layername.ljust(20), '-'.ljust(20), layerurl[0].ljust(55), layerurl[1]))
  196. printedlayers = []
  197. for layer, dependency, gitrepo, subdirectory in printlayers:
  198. if dependency in printedlayers:
  199. continue
  200. logger.plain("%s %s %s %s" % (dependency.ljust(20), layer.ljust(20), gitrepo.ljust(55), subdirectory))
  201. printedlayers.append(dependency)
  202. if repourls:
  203. fetchdir = self.tinfoil.config_data.getVar('BBLAYERS_FETCH_DIR', True)
  204. if not fetchdir:
  205. logger.error("Cannot get BBLAYERS_FETCH_DIR")
  206. return 1
  207. if not os.path.exists(fetchdir):
  208. os.makedirs(fetchdir)
  209. addlayers = []
  210. for repourl, subdir in repourls.values():
  211. name, layerdir = self.get_fetch_layer(fetchdir, repourl, subdir, not args.show_only)
  212. if not name:
  213. # Error already shown
  214. return 1
  215. addlayers.append((subdir, name, layerdir))
  216. if not args.show_only:
  217. for subdir, name, layerdir in set(addlayers):
  218. if os.path.exists(layerdir):
  219. if subdir:
  220. logger.plain("Adding layer \"%s\" to conf/bblayers.conf" % subdir)
  221. else:
  222. logger.plain("Adding layer \"%s\" to conf/bblayers.conf" % name)
  223. localargs = argparse.Namespace()
  224. localargs.layerdir = layerdir
  225. self.do_add_layer(localargs)
  226. else:
  227. break
  228. def do_layerindex_show_depends(self, args):
  229. """Find layer dependencies from layer index.
  230. """
  231. args.show_only = True
  232. args.ignore = []
  233. self.do_layerindex_fetch(args)
  234. def register_commands(self, sp):
  235. parser_layerindex_fetch = self.add_command(sp, 'layerindex-fetch', self.do_layerindex_fetch)
  236. parser_layerindex_fetch.add_argument('-n', '--show-only', help='show dependencies and do nothing else', action='store_true')
  237. parser_layerindex_fetch.add_argument('-b', '--branch', help='branch name to fetch (default %(default)s)', default='master')
  238. 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')
  239. parser_layerindex_fetch.add_argument('layername', nargs='+', help='layer to fetch')
  240. parser_layerindex_show_depends = self.add_command(sp, 'layerindex-show-depends', self.do_layerindex_show_depends)
  241. parser_layerindex_show_depends.add_argument('-b', '--branch', help='branch name to fetch (default %(default)s)', default='master')
  242. parser_layerindex_show_depends.add_argument('layername', nargs='+', help='layer to query')