import.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. # Development tool - import command plugin
  2. #
  3. # Copyright (C) 2014-2017 Intel Corporation
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License version 2 as
  7. # published by the Free Software Foundation.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License along
  15. # with this program; if not, write to the Free Software Foundation, Inc.,
  16. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. """Devtool import plugin"""
  18. import os
  19. import tarfile
  20. import logging
  21. import collections
  22. import json
  23. import fnmatch
  24. from devtool import standard, setup_tinfoil, replace_from_file, DevtoolError
  25. from devtool import export
  26. logger = logging.getLogger('devtool')
  27. def devimport(args, config, basepath, workspace):
  28. """Entry point for the devtool 'import' subcommand"""
  29. def get_pn(name):
  30. """ Returns the filename of a workspace recipe/append"""
  31. metadata = name.split('/')[-1]
  32. fn, _ = os.path.splitext(metadata)
  33. return fn
  34. if not os.path.exists(args.file):
  35. raise DevtoolError('Tar archive %s does not exist. Export your workspace using "devtool export"' % args.file)
  36. with tarfile.open(args.file) as tar:
  37. # Get exported metadata
  38. export_workspace_path = export_workspace = None
  39. try:
  40. metadata = tar.getmember(export.metadata)
  41. except KeyError as ke:
  42. raise DevtoolError('The export metadata file created by "devtool export" was not found. "devtool import" can only be used to import tar archives created by "devtool export".')
  43. tar.extract(metadata)
  44. with open(metadata.name) as fdm:
  45. export_workspace_path, export_workspace = json.load(fdm)
  46. os.unlink(metadata.name)
  47. members = tar.getmembers()
  48. # Get appends and recipes from the exported archive, these
  49. # will be needed to find out those appends without corresponding
  50. # recipe pair
  51. append_fns, recipe_fns = set(), set()
  52. for member in members:
  53. if member.name.startswith('appends'):
  54. append_fns.add(get_pn(member.name))
  55. elif member.name.startswith('recipes'):
  56. recipe_fns.add(get_pn(member.name))
  57. # Setup tinfoil, get required data and shutdown
  58. tinfoil = setup_tinfoil(config_only=False, basepath=basepath)
  59. try:
  60. current_fns = [os.path.basename(recipe[0]) for recipe in tinfoil.cooker.recipecaches[''].pkg_fn.items()]
  61. finally:
  62. tinfoil.shutdown()
  63. # Find those appends that do not have recipes in current metadata
  64. non_importables = []
  65. for fn in append_fns - recipe_fns:
  66. # Check on current metadata (covering those layers indicated in bblayers.conf)
  67. for current_fn in current_fns:
  68. if fnmatch.fnmatch(current_fn, '*' + fn.replace('%', '') + '*'):
  69. break
  70. else:
  71. non_importables.append(fn)
  72. logger.warning('No recipe to append %s.bbapppend, skipping' % fn)
  73. # Extract
  74. imported = []
  75. for member in members:
  76. if member.name == export.metadata:
  77. continue
  78. for nonimp in non_importables:
  79. pn = nonimp.split('_')[0]
  80. # do not extract data from non-importable recipes or metadata
  81. if member.name.startswith('appends/%s' % nonimp) or \
  82. member.name.startswith('recipes/%s' % nonimp) or \
  83. member.name.startswith('sources/%s' % pn):
  84. break
  85. else:
  86. path = os.path.join(config.workspace_path, member.name)
  87. if os.path.exists(path):
  88. # by default, no file overwrite is done unless -o is given by the user
  89. if args.overwrite:
  90. try:
  91. tar.extract(member, path=config.workspace_path)
  92. except PermissionError as pe:
  93. logger.warning(pe)
  94. else:
  95. logger.warning('File already present. Use --overwrite/-o to overwrite it: %s' % member.name)
  96. continue
  97. else:
  98. tar.extract(member, path=config.workspace_path)
  99. # Update EXTERNALSRC and the devtool md5 file
  100. if member.name.startswith('appends'):
  101. if export_workspace_path:
  102. # appends created by 'devtool modify' just need to update the workspace
  103. replace_from_file(path, export_workspace_path, config.workspace_path)
  104. # appends created by 'devtool add' need replacement of exported source tree
  105. pn = get_pn(member.name).split('_')[0]
  106. exported_srctree = export_workspace[pn]['srctree']
  107. if exported_srctree:
  108. replace_from_file(path, exported_srctree, os.path.join(config.workspace_path, 'sources', pn))
  109. standard._add_md5(config, pn, path)
  110. imported.append(pn)
  111. if imported:
  112. logger.info('Imported recipes into workspace %s: %s' % (config.workspace_path, ', '.join(imported)))
  113. else:
  114. logger.warning('No recipes imported into the workspace')
  115. return 0
  116. def register_commands(subparsers, context):
  117. """Register devtool import subcommands"""
  118. parser = subparsers.add_parser('import',
  119. help='Import exported tar archive into workspace',
  120. description='Import tar archive previously created by "devtool export" into workspace',
  121. group='advanced')
  122. parser.add_argument('file', metavar='FILE', help='Name of the tar archive to import')
  123. parser.add_argument('--overwrite', '-o', action="store_true", help='Overwrite files when extracting')
  124. parser.set_defaults(func=devimport)