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