blob: 9388abbacf6bf282440e90f0dcc8f195d3545006 [file] [log] [blame]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001# Development tool - build-image plugin
2#
3# Copyright (C) 2015 Intel Corporation
4#
Brad Bishopc342db32019-05-15 21:57:59 -04005# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05006#
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05007
8"""Devtool plugin containing the build-image subcommand."""
9
10import os
Patrick Williamsc0f7c042017-02-23 20:41:17 -060011import errno
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050012import logging
13
14from bb.process import ExecutionError
15from devtool import exec_build_env_command, setup_tinfoil, parse_recipe, DevtoolError
16
17logger = logging.getLogger('devtool')
18
19class TargetNotImageError(Exception):
20 pass
21
22def _get_packages(tinfoil, workspace, config):
23 """Get list of packages from recipes in the workspace."""
24 result = []
25 for recipe in workspace:
26 data = parse_recipe(config, tinfoil, recipe, True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050027 if 'class-target' in data.getVar('OVERRIDES').split(':'):
28 if recipe in data.getVar('PACKAGES').split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050029 result.append(recipe)
30 else:
31 logger.warning("Skipping recipe %s as it doesn't produce a "
32 "package with the same name", recipe)
33 return result
34
35def build_image(args, config, basepath, workspace):
36 """Entry point for the devtool 'build-image' subcommand."""
37
38 image = args.imagename
39 auto_image = False
40 if not image:
41 sdk_targets = config.get('SDK', 'sdk_targets', '').split()
42 if sdk_targets:
43 image = sdk_targets[0]
44 auto_image = True
45 if not image:
46 raise DevtoolError('Unable to determine image to build, please specify one')
47
48 try:
49 if args.add_packages:
50 add_packages = args.add_packages.split(',')
51 else:
52 add_packages = None
53 result, outputdir = build_image_task(config, basepath, workspace, image, add_packages)
54 except TargetNotImageError:
55 if auto_image:
56 raise DevtoolError('Unable to determine image to build, please specify one')
57 else:
58 raise DevtoolError('Specified recipe %s is not an image recipe' % image)
59
60 if result == 0:
61 logger.info('Successfully built %s. You can find output files in %s'
62 % (image, outputdir))
63 return result
64
65def build_image_task(config, basepath, workspace, image, add_packages=None, task=None, extra_append=None):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050066 # remove <image>.bbappend to make sure setup_tinfoil doesn't
67 # break because of it
Patrick Williamsc0f7c042017-02-23 20:41:17 -060068 target_basename = config.get('SDK', 'target_basename', '')
69 if target_basename:
70 appendfile = os.path.join(config.workspace_path, 'appends',
71 '%s.bbappend' % target_basename)
72 try:
73 os.unlink(appendfile)
74 except OSError as exc:
75 if exc.errno != errno.ENOENT:
76 raise
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050077
78 tinfoil = setup_tinfoil(basepath=basepath)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050079 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060080 rd = parse_recipe(config, tinfoil, image, True)
81 if not rd:
82 # Error already shown
83 return (1, None)
84 if not bb.data.inherits_class('image', rd):
85 raise TargetNotImageError()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050086
Patrick Williamsc0f7c042017-02-23 20:41:17 -060087 # Get the actual filename used and strip the .bb and full path
Brad Bishop6e60e8b2018-02-01 10:27:11 -050088 target_basename = rd.getVar('FILE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -060089 target_basename = os.path.splitext(os.path.basename(target_basename))[0]
90 config.set('SDK', 'target_basename', target_basename)
91 config.write()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050092
Patrick Williamsc0f7c042017-02-23 20:41:17 -060093 appendfile = os.path.join(config.workspace_path, 'appends',
94 '%s.bbappend' % target_basename)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050095
Patrick Williamsc0f7c042017-02-23 20:41:17 -060096 outputdir = None
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050097 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060098 if workspace or add_packages:
99 if add_packages:
100 packages = add_packages
101 else:
102 packages = _get_packages(tinfoil, workspace, config)
103 else:
104 packages = None
105 if not task:
106 if not packages and not add_packages and workspace:
107 logger.warning('No recipes in workspace, building image %s unmodified', image)
108 elif not packages:
109 logger.warning('No packages to add, building image %s unmodified', image)
110
111 if packages or extra_append:
112 bb.utils.mkdirhier(os.path.dirname(appendfile))
113 with open(appendfile, 'w') as afile:
114 if packages:
115 # include packages from workspace recipes into the image
116 afile.write('IMAGE_INSTALL_append = " %s"\n' % ' '.join(packages))
117 if not task:
118 logger.info('Building image %s with the following '
119 'additional packages: %s', image, ' '.join(packages))
120 if extra_append:
121 for line in extra_append:
122 afile.write('%s\n' % line)
123
124 if task in ['populate_sdk', 'populate_sdk_ext']:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500125 outputdir = rd.getVar('SDK_DEPLOY')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600126 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500127 outputdir = rd.getVar('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600128
129 tmp_tinfoil = tinfoil
130 tinfoil = None
131 tmp_tinfoil.shutdown()
132
133 options = ''
134 if task:
135 options += '-c %s' % task
136
137 # run bitbake to build image (or specified task)
138 try:
139 exec_build_env_command(config.init_path, basepath,
140 'bitbake %s %s' % (options, image), watch=True)
141 except ExecutionError as err:
142 return (err.exitcode, None)
143 finally:
144 if os.path.isfile(appendfile):
145 os.unlink(appendfile)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500146 finally:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600147 if tinfoil:
148 tinfoil.shutdown()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500149 return (0, outputdir)
150
151
152def register_commands(subparsers, context):
153 """Register devtool subcommands from the build-image plugin"""
154 parser = subparsers.add_parser('build-image',
155 help='Build image including workspace recipe packages',
156 description='Builds an image, extending it to include '
157 'packages from recipes in the workspace',
158 group='testbuild', order=-10)
159 parser.add_argument('imagename', help='Image recipe to build', nargs='?')
160 parser.add_argument('-p', '--add-packages', help='Instead of adding packages for the '
161 'entire workspace, specify packages to be added to the image '
162 '(separate multiple packages by commas)',
163 metavar='PACKAGES')
164 parser.set_defaults(func=build_image)