xref: /OK3568_Linux_fs/yocto/poky/scripts/lib/devtool/build_image.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun# Development tool - build-image plugin
2*4882a593Smuzhiyun#
3*4882a593Smuzhiyun# Copyright (C) 2015 Intel Corporation
4*4882a593Smuzhiyun#
5*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only
6*4882a593Smuzhiyun#
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun"""Devtool plugin containing the build-image subcommand."""
9*4882a593Smuzhiyun
10*4882a593Smuzhiyunimport os
11*4882a593Smuzhiyunimport errno
12*4882a593Smuzhiyunimport logging
13*4882a593Smuzhiyun
14*4882a593Smuzhiyunfrom bb.process import ExecutionError
15*4882a593Smuzhiyunfrom devtool import exec_build_env_command, setup_tinfoil, parse_recipe, DevtoolError
16*4882a593Smuzhiyun
17*4882a593Smuzhiyunlogger = logging.getLogger('devtool')
18*4882a593Smuzhiyun
19*4882a593Smuzhiyunclass TargetNotImageError(Exception):
20*4882a593Smuzhiyun    pass
21*4882a593Smuzhiyun
22*4882a593Smuzhiyundef _get_packages(tinfoil, workspace, config):
23*4882a593Smuzhiyun    """Get list of packages from recipes in the workspace."""
24*4882a593Smuzhiyun    result = []
25*4882a593Smuzhiyun    for recipe in workspace:
26*4882a593Smuzhiyun        data = parse_recipe(config, tinfoil, recipe, True)
27*4882a593Smuzhiyun        if 'class-target' in data.getVar('OVERRIDES').split(':'):
28*4882a593Smuzhiyun            if recipe in data.getVar('PACKAGES').split():
29*4882a593Smuzhiyun                result.append(recipe)
30*4882a593Smuzhiyun            else:
31*4882a593Smuzhiyun                logger.warning("Skipping recipe %s as it doesn't produce a "
32*4882a593Smuzhiyun                               "package with the same name", recipe)
33*4882a593Smuzhiyun    return result
34*4882a593Smuzhiyun
35*4882a593Smuzhiyundef build_image(args, config, basepath, workspace):
36*4882a593Smuzhiyun    """Entry point for the devtool 'build-image' subcommand."""
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun    image = args.imagename
39*4882a593Smuzhiyun    auto_image = False
40*4882a593Smuzhiyun    if not image:
41*4882a593Smuzhiyun        sdk_targets = config.get('SDK', 'sdk_targets', '').split()
42*4882a593Smuzhiyun        if sdk_targets:
43*4882a593Smuzhiyun            image = sdk_targets[0]
44*4882a593Smuzhiyun            auto_image = True
45*4882a593Smuzhiyun    if not image:
46*4882a593Smuzhiyun        raise DevtoolError('Unable to determine image to build, please specify one')
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun    try:
49*4882a593Smuzhiyun        if args.add_packages:
50*4882a593Smuzhiyun            add_packages = args.add_packages.split(',')
51*4882a593Smuzhiyun        else:
52*4882a593Smuzhiyun            add_packages = None
53*4882a593Smuzhiyun        result, outputdir = build_image_task(config, basepath, workspace, image, add_packages)
54*4882a593Smuzhiyun    except TargetNotImageError:
55*4882a593Smuzhiyun        if auto_image:
56*4882a593Smuzhiyun            raise DevtoolError('Unable to determine image to build, please specify one')
57*4882a593Smuzhiyun        else:
58*4882a593Smuzhiyun            raise DevtoolError('Specified recipe %s is not an image recipe' % image)
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun    if result == 0:
61*4882a593Smuzhiyun        logger.info('Successfully built %s. You can find output files in %s'
62*4882a593Smuzhiyun                    % (image, outputdir))
63*4882a593Smuzhiyun    return result
64*4882a593Smuzhiyun
65*4882a593Smuzhiyundef build_image_task(config, basepath, workspace, image, add_packages=None, task=None, extra_append=None):
66*4882a593Smuzhiyun    # remove <image>.bbappend to make sure setup_tinfoil doesn't
67*4882a593Smuzhiyun    # break because of it
68*4882a593Smuzhiyun    target_basename = config.get('SDK', 'target_basename', '')
69*4882a593Smuzhiyun    if target_basename:
70*4882a593Smuzhiyun        appendfile = os.path.join(config.workspace_path, 'appends',
71*4882a593Smuzhiyun                                  '%s.bbappend' % target_basename)
72*4882a593Smuzhiyun        try:
73*4882a593Smuzhiyun            os.unlink(appendfile)
74*4882a593Smuzhiyun        except OSError as exc:
75*4882a593Smuzhiyun            if exc.errno != errno.ENOENT:
76*4882a593Smuzhiyun                raise
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun    tinfoil = setup_tinfoil(basepath=basepath)
79*4882a593Smuzhiyun    try:
80*4882a593Smuzhiyun        rd = parse_recipe(config, tinfoil, image, True)
81*4882a593Smuzhiyun        if not rd:
82*4882a593Smuzhiyun            # Error already shown
83*4882a593Smuzhiyun            return (1, None)
84*4882a593Smuzhiyun        if not bb.data.inherits_class('image', rd):
85*4882a593Smuzhiyun            raise TargetNotImageError()
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun        # Get the actual filename used and strip the .bb and full path
88*4882a593Smuzhiyun        target_basename = rd.getVar('FILE')
89*4882a593Smuzhiyun        target_basename = os.path.splitext(os.path.basename(target_basename))[0]
90*4882a593Smuzhiyun        config.set('SDK', 'target_basename', target_basename)
91*4882a593Smuzhiyun        config.write()
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun        appendfile = os.path.join(config.workspace_path, 'appends',
94*4882a593Smuzhiyun                                '%s.bbappend' % target_basename)
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun        outputdir = None
97*4882a593Smuzhiyun        try:
98*4882a593Smuzhiyun            if workspace or add_packages:
99*4882a593Smuzhiyun                if add_packages:
100*4882a593Smuzhiyun                    packages = add_packages
101*4882a593Smuzhiyun                else:
102*4882a593Smuzhiyun                    packages = _get_packages(tinfoil, workspace, config)
103*4882a593Smuzhiyun            else:
104*4882a593Smuzhiyun                packages = None
105*4882a593Smuzhiyun            if not task:
106*4882a593Smuzhiyun                if not packages and not add_packages and workspace:
107*4882a593Smuzhiyun                    logger.warning('No recipes in workspace, building image %s unmodified', image)
108*4882a593Smuzhiyun                elif not packages:
109*4882a593Smuzhiyun                    logger.warning('No packages to add, building image %s unmodified', image)
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun            if packages or extra_append:
112*4882a593Smuzhiyun                bb.utils.mkdirhier(os.path.dirname(appendfile))
113*4882a593Smuzhiyun                with open(appendfile, 'w') as afile:
114*4882a593Smuzhiyun                    if packages:
115*4882a593Smuzhiyun                        # include packages from workspace recipes into the image
116*4882a593Smuzhiyun                        afile.write('IMAGE_INSTALL:append = " %s"\n' % ' '.join(packages))
117*4882a593Smuzhiyun                        if not task:
118*4882a593Smuzhiyun                            logger.info('Building image %s with the following '
119*4882a593Smuzhiyun                                        'additional packages: %s', image, ' '.join(packages))
120*4882a593Smuzhiyun                    if extra_append:
121*4882a593Smuzhiyun                        for line in extra_append:
122*4882a593Smuzhiyun                            afile.write('%s\n' % line)
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun            if task in ['populate_sdk', 'populate_sdk_ext']:
125*4882a593Smuzhiyun                outputdir = rd.getVar('SDK_DEPLOY')
126*4882a593Smuzhiyun            else:
127*4882a593Smuzhiyun                outputdir = rd.getVar('DEPLOY_DIR_IMAGE')
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun            tmp_tinfoil = tinfoil
130*4882a593Smuzhiyun            tinfoil = None
131*4882a593Smuzhiyun            tmp_tinfoil.shutdown()
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun            options = ''
134*4882a593Smuzhiyun            if task:
135*4882a593Smuzhiyun                options += '-c %s' % task
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun            # run bitbake to build image (or specified task)
138*4882a593Smuzhiyun            try:
139*4882a593Smuzhiyun                exec_build_env_command(config.init_path, basepath,
140*4882a593Smuzhiyun                                    'bitbake %s %s' % (options, image), watch=True)
141*4882a593Smuzhiyun            except ExecutionError as err:
142*4882a593Smuzhiyun                return (err.exitcode, None)
143*4882a593Smuzhiyun        finally:
144*4882a593Smuzhiyun            if os.path.isfile(appendfile):
145*4882a593Smuzhiyun                os.unlink(appendfile)
146*4882a593Smuzhiyun    finally:
147*4882a593Smuzhiyun        if tinfoil:
148*4882a593Smuzhiyun            tinfoil.shutdown()
149*4882a593Smuzhiyun    return (0, outputdir)
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun
152*4882a593Smuzhiyundef register_commands(subparsers, context):
153*4882a593Smuzhiyun    """Register devtool subcommands from the build-image plugin"""
154*4882a593Smuzhiyun    parser = subparsers.add_parser('build-image',
155*4882a593Smuzhiyun                                   help='Build image including workspace recipe packages',
156*4882a593Smuzhiyun                                   description='Builds an image, extending it to include '
157*4882a593Smuzhiyun                                   'packages from recipes in the workspace',
158*4882a593Smuzhiyun                                   group='testbuild', order=-10)
159*4882a593Smuzhiyun    parser.add_argument('imagename', help='Image recipe to build', nargs='?')
160*4882a593Smuzhiyun    parser.add_argument('-p', '--add-packages', help='Instead of adding packages for the '
161*4882a593Smuzhiyun                        'entire workspace, specify packages to be added to the image '
162*4882a593Smuzhiyun                        '(separate multiple packages by commas)',
163*4882a593Smuzhiyun                        metavar='PACKAGES')
164*4882a593Smuzhiyun    parser.set_defaults(func=build_image)
165