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