# Development tool - build-image plugin # # Copyright (C) 2015 Intel Corporation # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Devtool plugin containing the build-image subcommand.""" import os import errno import logging from bb.process import ExecutionError from devtool import exec_build_env_command, setup_tinfoil, parse_recipe, DevtoolError logger = logging.getLogger('devtool') class TargetNotImageError(Exception): pass def _get_packages(tinfoil, workspace, config): """Get list of packages from recipes in the workspace.""" result = [] for recipe in workspace: data = parse_recipe(config, tinfoil, recipe, True) if 'class-target' in data.getVar('OVERRIDES').split(':'): if recipe in data.getVar('PACKAGES').split(): result.append(recipe) else: logger.warning("Skipping recipe %s as it doesn't produce a " "package with the same name", recipe) return result def build_image(args, config, basepath, workspace): """Entry point for the devtool 'build-image' subcommand.""" image = args.imagename auto_image = False if not image: sdk_targets = config.get('SDK', 'sdk_targets', '').split() if sdk_targets: image = sdk_targets[0] auto_image = True if not image: raise DevtoolError('Unable to determine image to build, please specify one') try: if args.add_packages: add_packages = args.add_packages.split(',') else: add_packages = None result, outputdir = build_image_task(config, basepath, workspace, image, add_packages) except TargetNotImageError: if auto_image: raise DevtoolError('Unable to determine image to build, please specify one') else: raise DevtoolError('Specified recipe %s is not an image recipe' % image) if result == 0: logger.info('Successfully built %s. You can find output files in %s' % (image, outputdir)) return result def build_image_task(config, basepath, workspace, image, add_packages=None, task=None, extra_append=None): # remove .bbappend to make sure setup_tinfoil doesn't # break because of it target_basename = config.get('SDK', 'target_basename', '') if target_basename: appendfile = os.path.join(config.workspace_path, 'appends', '%s.bbappend' % target_basename) try: os.unlink(appendfile) except OSError as exc: if exc.errno != errno.ENOENT: raise tinfoil = setup_tinfoil(basepath=basepath) try: rd = parse_recipe(config, tinfoil, image, True) if not rd: # Error already shown return (1, None) if not bb.data.inherits_class('image', rd): raise TargetNotImageError() # Get the actual filename used and strip the .bb and full path target_basename = rd.getVar('FILE') target_basename = os.path.splitext(os.path.basename(target_basename))[0] config.set('SDK', 'target_basename', target_basename) config.write() appendfile = os.path.join(config.workspace_path, 'appends', '%s.bbappend' % target_basename) outputdir = None try: if workspace or add_packages: if add_packages: packages = add_packages else: packages = _get_packages(tinfoil, workspace, config) else: packages = None if not task: if not packages and not add_packages and workspace: logger.warning('No recipes in workspace, building image %s unmodified', image) elif not packages: logger.warning('No packages to add, building image %s unmodified', image) if packages or extra_append: bb.utils.mkdirhier(os.path.dirname(appendfile)) with open(appendfile, 'w') as afile: if packages: # include packages from workspace recipes into the image afile.write('IMAGE_INSTALL_append = " %s"\n' % ' '.join(packages)) if not task: logger.info('Building image %s with the following ' 'additional packages: %s', image, ' '.join(packages)) if extra_append: for line in extra_append: afile.write('%s\n' % line) if task in ['populate_sdk', 'populate_sdk_ext']: outputdir = rd.getVar('SDK_DEPLOY') else: outputdir = rd.getVar('DEPLOY_DIR_IMAGE') tmp_tinfoil = tinfoil tinfoil = None tmp_tinfoil.shutdown() options = '' if task: options += '-c %s' % task # run bitbake to build image (or specified task) try: exec_build_env_command(config.init_path, basepath, 'bitbake %s %s' % (options, image), watch=True) except ExecutionError as err: return (err.exitcode, None) finally: if os.path.isfile(appendfile): os.unlink(appendfile) finally: if tinfoil: tinfoil.shutdown() return (0, outputdir) def register_commands(subparsers, context): """Register devtool subcommands from the build-image plugin""" parser = subparsers.add_parser('build-image', help='Build image including workspace recipe packages', description='Builds an image, extending it to include ' 'packages from recipes in the workspace', group='testbuild', order=-10) parser.add_argument('imagename', help='Image recipe to build', nargs='?') parser.add_argument('-p', '--add-packages', help='Instead of adding packages for the ' 'entire workspace, specify packages to be added to the image ' '(separate multiple packages by commas)', metavar='PACKAGES') parser.set_defaults(func=build_image)