blob: 4264b7d8209962aac8969ee9ebc4936194fc6a6a [file] [log] [blame]
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001# 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
19import os
20import tarfile
21import logging
22import collections
23import json
24import fnmatch
25
26from devtool import standard, setup_tinfoil, replace_from_file, DevtoolError
27from devtool import export
28
29logger = logging.getLogger('devtool')
30
31def devimport(args, config, basepath, workspace):
32 """Entry point for the devtool 'import' subcommand"""
33
34 def get_pn(name):
35 """ Returns the filename of a workspace recipe/append"""
36 metadata = name.split('/')[-1]
37 fn, _ = os.path.splitext(metadata)
38 return fn
39
40 if not os.path.exists(args.file):
41 raise DevtoolError('Tar archive %s does not exist. Export your workspace using "devtool export"' % args.file)
42
43 with tarfile.open(args.file) as tar:
44 # Get exported metadata
45 export_workspace_path = export_workspace = None
46 try:
47 metadata = tar.getmember(export.metadata)
48 except KeyError as ke:
49 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".')
50
51 tar.extract(metadata)
52 with open(metadata.name) as fdm:
53 export_workspace_path, export_workspace = json.load(fdm)
54 os.unlink(metadata.name)
55
56 members = tar.getmembers()
57
58 # Get appends and recipes from the exported archive, these
59 # will be needed to find out those appends without corresponding
60 # recipe pair
61 append_fns, recipe_fns = set(), set()
62 for member in members:
63 if member.name.startswith('appends'):
64 append_fns.add(get_pn(member.name))
65 elif member.name.startswith('recipes'):
66 recipe_fns.add(get_pn(member.name))
67
68 # Setup tinfoil, get required data and shutdown
69 tinfoil = setup_tinfoil(config_only=False, basepath=basepath)
70 try:
71 current_fns = [os.path.basename(recipe[0]) for recipe in tinfoil.cooker.recipecaches[''].pkg_fn.items()]
72 finally:
73 tinfoil.shutdown()
74
75 # Find those appends that do not have recipes in current metadata
76 non_importables = []
77 for fn in append_fns - recipe_fns:
78 # Check on current metadata (covering those layers indicated in bblayers.conf)
79 for current_fn in current_fns:
80 if fnmatch.fnmatch(current_fn, '*' + fn.replace('%', '') + '*'):
81 break
82 else:
83 non_importables.append(fn)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080084 logger.warning('No recipe to append %s.bbapppend, skipping' % fn)
Brad Bishopd7bf8c12018-02-25 22:55:05 -050085
86 # Extract
87 imported = []
88 for member in members:
89 if member.name == export.metadata:
90 continue
91
92 for nonimp in non_importables:
93 pn = nonimp.split('_')[0]
94 # do not extract data from non-importable recipes or metadata
95 if member.name.startswith('appends/%s' % nonimp) or \
96 member.name.startswith('recipes/%s' % nonimp) or \
97 member.name.startswith('sources/%s' % pn):
98 break
99 else:
100 path = os.path.join(config.workspace_path, member.name)
101 if os.path.exists(path):
102 # by default, no file overwrite is done unless -o is given by the user
103 if args.overwrite:
104 try:
105 tar.extract(member, path=config.workspace_path)
106 except PermissionError as pe:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800107 logger.warning(pe)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500108 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800109 logger.warning('File already present. Use --overwrite/-o to overwrite it: %s' % member.name)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500110 continue
111 else:
112 tar.extract(member, path=config.workspace_path)
113
114 # Update EXTERNALSRC and the devtool md5 file
115 if member.name.startswith('appends'):
116 if export_workspace_path:
117 # appends created by 'devtool modify' just need to update the workspace
118 replace_from_file(path, export_workspace_path, config.workspace_path)
119
120 # appends created by 'devtool add' need replacement of exported source tree
121 pn = get_pn(member.name).split('_')[0]
122 exported_srctree = export_workspace[pn]['srctree']
123 if exported_srctree:
124 replace_from_file(path, exported_srctree, os.path.join(config.workspace_path, 'sources', pn))
125
126 standard._add_md5(config, pn, path)
127 imported.append(pn)
128
129 if imported:
130 logger.info('Imported recipes into workspace %s: %s' % (config.workspace_path, ', '.join(imported)))
131 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800132 logger.warning('No recipes imported into the workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500133
134 return 0
135
136def register_commands(subparsers, context):
137 """Register devtool import subcommands"""
138 parser = subparsers.add_parser('import',
139 help='Import exported tar archive into workspace',
140 description='Import tar archive previously created by "devtool export" into workspace',
141 group='advanced')
142 parser.add_argument('file', metavar='FILE', help='Name of the tar archive to import')
143 parser.add_argument('--overwrite', '-o', action="store_true", help='Overwrite files when extracting')
144 parser.set_defaults(func=devimport)