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