xref: /openbmc/openbmc/poky/scripts/lib/devtool/import.py (revision c342db356d4f451821781eb24eb9f3d39d6c0c5e)
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