layerindex.py 8.5 KB

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