1 # Development tool - import command plugin 2 # 3 # Copyright (C) 2014-2017 Intel Corporation 4 # 5 # SPDX-License-Identifier: GPL-2.0-only 6 # 7 """Devtool import plugin""" 8 9 import os 10 import tarfile 11 import logging 12 import collections 13 import json 14 import fnmatch 15 16 from devtool import standard, setup_tinfoil, replace_from_file, DevtoolError 17 from devtool import export 18 19 logger = logging.getLogger('devtool') 20 21 def devimport(args, config, basepath, workspace): 22 """Entry point for the devtool 'import' subcommand""" 23 24 def get_pn(name): 25 """ Returns the filename of a workspace recipe/append""" 26 metadata = name.split('/')[-1] 27 fn, _ = os.path.splitext(metadata) 28 return fn 29 30 if not os.path.exists(args.file): 31 raise DevtoolError('Tar archive %s does not exist. Export your workspace using "devtool export"' % args.file) 32 33 with tarfile.open(args.file) as tar: 34 # Get exported metadata 35 export_workspace_path = export_workspace = None 36 try: 37 metadata = tar.getmember(export.metadata) 38 except KeyError as ke: 39 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".') 40 41 tar.extract(metadata) 42 with open(metadata.name) as fdm: 43 export_workspace_path, export_workspace = json.load(fdm) 44 os.unlink(metadata.name) 45 46 members = tar.getmembers() 47 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 58 # Setup tinfoil, get required data and shutdown 59 tinfoil = setup_tinfoil(config_only=False, basepath=basepath) 60 try: 61 current_fns = [os.path.basename(recipe[0]) for recipe in tinfoil.cooker.recipecaches[''].pkg_fn.items()] 62 finally: 63 tinfoil.shutdown() 64 65 # Find those appends that do not have recipes in current metadata 66 non_importables = [] 67 for fn in append_fns - recipe_fns: 68 # Check on current metadata (covering those layers indicated in bblayers.conf) 69 for current_fn in current_fns: 70 if fnmatch.fnmatch(current_fn, '*' + fn.replace('%', '') + '*'): 71 break 72 else: 73 non_importables.append(fn) 74 logger.warning('No recipe to append %s.bbapppend, skipping' % fn) 75 76 # Extract 77 imported = [] 78 for member in members: 79 if member.name == export.metadata: 80 continue 81 82 for nonimp in non_importables: 83 pn = nonimp.split('_')[0] 84 # do not extract data from non-importable recipes or metadata 85 if member.name.startswith('appends/%s' % nonimp) or \ 86 member.name.startswith('recipes/%s' % nonimp) or \ 87 member.name.startswith('sources/%s' % pn): 88 break 89 else: 90 path = os.path.join(config.workspace_path, member.name) 91 if os.path.exists(path): 92 # by default, no file overwrite is done unless -o is given by the user 93 if args.overwrite: 94 try: 95 tar.extract(member, path=config.workspace_path) 96 except PermissionError as pe: 97 logger.warning(pe) 98 else: 99 logger.warning('File already present. Use --overwrite/-o to overwrite it: %s' % member.name) 100 continue 101 else: 102 tar.extract(member, path=config.workspace_path) 103 104 # Update EXTERNALSRC and the devtool md5 file 105 if member.name.startswith('appends'): 106 if export_workspace_path: 107 # appends created by 'devtool modify' just need to update the workspace 108 replace_from_file(path, export_workspace_path, config.workspace_path) 109 110 # appends created by 'devtool add' need replacement of exported source tree 111 pn = get_pn(member.name).split('_')[0] 112 exported_srctree = export_workspace[pn]['srctree'] 113 if exported_srctree: 114 replace_from_file(path, exported_srctree, os.path.join(config.workspace_path, 'sources', pn)) 115 116 standard._add_md5(config, pn, path) 117 imported.append(pn) 118 119 if imported: 120 logger.info('Imported recipes into workspace %s: %s' % (config.workspace_path, ', '.join(imported))) 121 else: 122 logger.warning('No recipes imported into the workspace') 123 124 return 0 125 126 def register_commands(subparsers, context): 127 """Register devtool import subcommands""" 128 parser = subparsers.add_parser('import', 129 help='Import exported tar archive into workspace', 130 description='Import tar archive previously created by "devtool export" into workspace', 131 group='advanced') 132 parser.add_argument('file', metavar='FILE', help='Name of the tar archive to import') 133 parser.add_argument('--overwrite', '-o', action="store_true", help='Overwrite files when extracting') 134 parser.set_defaults(func=devimport) 135