1# Development tool - build-image plugin 2# 3# Copyright (C) 2015 Intel Corporation 4# 5# SPDX-License-Identifier: GPL-2.0-only 6# 7 8"""Devtool plugin containing the build-image subcommand.""" 9 10import os 11import errno 12import 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) 27 if 'class-target' in data.getVar('OVERRIDES').split(':'): 28 if recipe in data.getVar('PACKAGES').split(): 29 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): 66 # remove <image>.bbappend to make sure setup_tinfoil doesn't 67 # break because of it 68 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 77 78 tinfoil = setup_tinfoil(basepath=basepath) 79 try: 80 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() 86 87 # Get the actual filename used and strip the .bb and full path 88 target_basename = rd.getVar('FILE') 89 target_basename = os.path.splitext(os.path.basename(target_basename))[0] 90 config.set('SDK', 'target_basename', target_basename) 91 config.write() 92 93 appendfile = os.path.join(config.workspace_path, 'appends', 94 '%s.bbappend' % target_basename) 95 96 outputdir = None 97 try: 98 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']: 125 outputdir = rd.getVar('SDK_DEPLOY') 126 else: 127 outputdir = rd.getVar('DEPLOY_DIR_IMAGE') 128 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) 146 finally: 147 if tinfoil: 148 tinfoil.shutdown() 149 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) 165