def runqemu(args, config, basepath, workspace): """Entry point for the devtool 'runqemu' subcommand""" tinfoil = setup_tinfoil(config_only=True, basepath=basepath) machine = tinfoil.config_data.getVar('MACHINE', True) bindir_native = tinfoil.config_data.getVar('STAGING_BINDIR_NATIVE', True) tinfoil.shutdown() if not glob.glob(os.path.join(bindir_native, 'qemu-system-*')): raise DevtoolError('QEMU is not available within this SDK') imagename = args.imagename if not imagename: sdk_targets = config.get('SDK', 'sdk_targets', '').split() if sdk_targets: imagename = sdk_targets[0] if not imagename: raise DevtoolError('Unable to determine image name to run, please specify one') try: exec_build_env_command(config.init_path, basepath, 'runqemu %s %s %s' % (machine, imagename, " ".join(args.args)), watch=True) except bb.process.ExecutionError as e: # We've already seen the output since watch=True, so just ensure we return something to the user return e.exitcode return 0
def edit_recipe(args, config, basepath, workspace): """Entry point for the devtool 'edit-recipe' subcommand""" if args.any_recipe: tinfoil = setup_tinfoil(config_only=False, basepath=basepath) try: rd = parse_recipe(config, tinfoil, args.recipename, True) if not rd: return 1 recipefile = rd.getVar('FILE', True) finally: tinfoil.shutdown() else: check_workspace_recipe(workspace, args.recipename) recipefile = workspace[args.recipename]['recipefile'] if not recipefile: raise DevtoolError( "Recipe file for %s is not under the workspace" % args.recipename) editor = os.environ.get('EDITOR', None) if not editor: raise DevtoolError("EDITOR environment variable not set") import subprocess try: subprocess.check_call('%s "%s"' % (editor, recipefile), shell=True) except subprocess.CalledProcessError as e: return e.returncode return 0
def runqemu(args, config, basepath, workspace): """Entry point for the devtool 'runqemu' subcommand""" tinfoil = setup_tinfoil(config_only=True, basepath=basepath) try: machine = tinfoil.config_data.getVar('MACHINE') bindir_native = os.path.join(tinfoil.config_data.getVar('STAGING_DIR'), tinfoil.config_data.getVar('BUILD_ARCH'), tinfoil.config_data.getVar('bindir_native').lstrip(os.path.sep)) finally: tinfoil.shutdown() if not glob.glob(os.path.join(bindir_native, 'qemu-system-*')): raise DevtoolError('QEMU is not available within this SDK') imagename = args.imagename if not imagename: sdk_targets = config.get('SDK', 'sdk_targets', '').split() if sdk_targets: imagename = sdk_targets[0] if not imagename: raise DevtoolError('Unable to determine image name to run, please specify one') try: # FIXME runqemu assumes that if OECORE_NATIVE_SYSROOT is set then it shouldn't # run bitbake to find out the values of various environment variables, which # isn't the case for the extensible SDK. Work around it for now. newenv = dict(os.environ) newenv.pop('OECORE_NATIVE_SYSROOT', '') exec_build_env_command(config.init_path, basepath, 'runqemu %s %s %s' % (machine, imagename, " ".join(args.args)), watch=True, env=newenv) except bb.process.ExecutionError as e: # We've already seen the output since watch=True, so just ensure we return something to the user return e.exitcode return 0
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 configure(args, config, basepath, workspace): """Entry point for the devtool 'configure' subcommand""" if args.component not in workspace: raise DevtoolError( "recipe %s is not in your workspace, run devtool modify command first" % args.component) rd = "" tinfoil = setup_tinfoil(basepath=basepath) try: rd = parse_recipe(config, tinfoil, args.component, appends=True, filter_workspace=False) if not rd: return 1 pn = rd.getVar('PN', True) if pn not in workspace: raise DevtoolError( "Run devtool modify before calling configure for %s" % pn) finally: tinfoil.shutdown() exec_build_env_command(config.init_path, basepath, 'bitbake -c configure %s' % pn, watch=True) return 0
def sdk_install(args, config, basepath, workspace): """Entry point for the devtool sdk-install command""" import oe.recipeutils import bb.process for recipe in args.recipename: if recipe in workspace: raise DevtoolError('recipe %s is a recipe in your workspace' % recipe) tasks = ['do_populate_sysroot', 'do_packagedata'] stampprefixes = {} def checkstamp(recipe): stampprefix = stampprefixes[recipe] stamps = glob.glob(stampprefix + '*') for stamp in stamps: if '.sigdata.' not in stamp and stamp.startswith((stampprefix + '.', stampprefix + '_setscene.')): return True else: return False install_recipes = [] tinfoil = setup_tinfoil(config_only=False, basepath=basepath) try: for recipe in args.recipename: rd = parse_recipe(config, tinfoil, recipe, True) if not rd: return 1 stampprefixes[recipe] = '%s.%s' % (rd.getVar('STAMP', True), tasks[0]) if checkstamp(recipe): logger.info('%s is already installed' % recipe) else: install_recipes.append(recipe) finally: tinfoil.shutdown() if install_recipes: logger.info('Installing %s...' % ', '.join(install_recipes)) install_tasks = [] for recipe in install_recipes: for task in tasks: if recipe.endswith('-native') and 'package' in task: continue install_tasks.append('%s:%s' % (recipe, task)) options = '' if not args.allow_build: options += ' --setscene-only' try: exec_build_env_command(config.init_path, basepath, 'bitbake %s %s' % (options, ' '.join(install_tasks)), watch=True) except bb.process.ExecutionError as e: raise DevtoolError('Failed to install %s:\n%s' % (recipe, str(e))) failed = False for recipe in install_recipes: if checkstamp(recipe): logger.info('Successfully installed %s' % recipe) else: raise DevtoolError('Failed to install %s - unavailable' % recipe) failed = True if failed: return 2
def build_sdk(args, config, basepath, workspace): """Entry point for the devtool build-sdk command""" sdk_targets = config.get('SDK', 'sdk_targets', '').split() if sdk_targets: image = sdk_targets[0] else: raise DevtoolError('Unable to determine image to build SDK for') extra_append = ['SDK_DERIVATIVE = "1"'] try: result, outputdir = build_image.build_image_task( config, basepath, workspace, image, task='populate_sdk_ext', extra_append=extra_append) except build_image.TargetNotImageError: raise DevtoolError('Unable to determine image to build SDK for') if result == 0: logger.info('Successfully built SDK. You can find output files in %s' % outputdir) return result
def undeploy(args, config, basepath, workspace): """Entry point for the devtool 'undeploy' subcommand""" if args.all and args.recipename: raise argparse_oe.ArgumentUsageError( 'Cannot specify -a/--all with a recipe name', 'undeploy-target') elif not args.recipename and not args.all: raise argparse_oe.ArgumentUsageError( 'If you don\'t specify a recipe, you must specify -a/--all', 'undeploy-target') extraoptions = '' if args.no_host_check: extraoptions += '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' if not args.show_status: extraoptions += ' -q' scp_port = '' ssh_port = '' if args.port: scp_port = "-P %s" % args.port ssh_port = "-p %s" % args.port args.target = args.target.split(':')[0] tmpdir = tempfile.mkdtemp(prefix='devtool') try: tmpscript = '/tmp/devtool_undeploy.sh' shellscript = _prepare_remote_script(deploy=False, dryrun=args.dry_run, undeployall=args.all) # Write out the script to a file with open(os.path.join(tmpdir, os.path.basename(tmpscript)), 'w') as f: f.write(shellscript) # Copy it to the target ret = subprocess.call("scp %s %s %s/* %s:%s" % (scp_port, extraoptions, tmpdir, args.target, os.path.dirname(tmpscript)), shell=True) if ret != 0: raise DevtoolError( 'Failed to copy script to %s - rerun with -s to ' 'get a complete error message' % args.target) finally: shutil.rmtree(tmpdir) # Now run the script ret = subprocess.call( 'ssh %s %s %s \'sh %s %s\'' % (ssh_port, extraoptions, args.target, tmpscript, args.recipename), shell=True) if ret != 0: raise DevtoolError('Undeploy failed - rerun with -s to get a complete ' 'error message') if not args.all and not args.dry_run: logger.info('Successfully undeployed %s' % args.recipename) return 0
def upgrade(args, config, basepath, workspace): """Entry point for the devtool 'upgrade' subcommand""" if args.recipename in workspace: raise DevtoolError("recipe %s is already in your workspace" % args.recipename) if not args.version and not args.srcrev: raise DevtoolError( "You must provide a version using the --version/-V option, or for recipes that fetch from an SCM such as git, the --srcrev/-S option" ) reason = oe.recipeutils.validate_pn(args.recipename) if reason: raise DevtoolError(reason) tinfoil = setup_tinfoil(basepath=basepath) rd = parse_recipe(config, tinfoil, args.recipename, True) if not rd: return 1 pn = rd.getVar('PN', True) if pn != args.recipename: logger.info('Mapping %s to %s' % (args.recipename, pn)) if pn in workspace: raise DevtoolError("recipe %s is already in your workspace" % pn) standard._check_compatible_recipe(pn, rd) if rd.getVar('PV', True) == args.version and rd.getVar( 'SRCREV', True) == args.srcrev: raise DevtoolError( "Current and upgrade versions are the same version" % version) rf = None try: rev1 = standard._extract_source(args.srctree, False, 'devtool-orig', rd) rev2, md5, sha256 = _extract_new_source(args.version, args.srctree, args.no_patch, args.srcrev, args.branch, args.keep_temp, tinfoil, rd) rf = _create_new_recipe(args.version, md5, sha256, config.workspace_path, rd) except bb.process.CmdError as e: _upgrade_error(e, rf, args.srctree) except DevtoolError as e: _upgrade_error(e, rf, args.srctree) standard._add_md5(config, pn, os.path.dirname(rf)) af = _write_append(rf, args.srctree, args.same_dir, args.no_same_dir, rev2, config.workspace_path, rd) standard._add_md5(config, pn, af) logger.info('Upgraded source extracted to %s' % args.srctree) return 0
def reset(args, config, basepath, workspace): """Entry point for the devtool 'reset' subcommand""" import bb if args.recipename: if args.all: raise DevtoolError( "Recipe cannot be specified if -a/--all is used") elif not args.recipename in workspace: raise DevtoolError("no recipe named %s in your workspace" % args.recipename) elif not args.all: raise DevtoolError("Recipe must be specified, or specify -a/--all to " "reset all recipes") if args.all: recipes = workspace else: recipes = [args.recipename] for pn in recipes: if not args.no_clean: logger.info('Cleaning sysroot for recipe %s...' % pn) try: exec_build_env_command(config.init_path, basepath, 'bitbake -c clean %s' % pn) except bb.process.ExecutionError as e: raise DevtoolError( 'Command \'%s\' failed, output:\n%s\nIf you ' 'wish, you may specify -n/--no-clean to ' 'skip running this command when resetting' % (e.command, e.stdout)) _check_preserve(config, pn) preservepath = os.path.join(config.workspace_path, 'attic', pn) def preservedir(origdir): if os.path.exists(origdir): for root, dirs, files in os.walk(origdir): for fn in files: logger.warn('Preserving %s in %s' % (fn, preservepath)) bb.utils.mkdirhier(preservepath) shutil.move(os.path.join(origdir, fn), os.path.join(preservepath, fn)) for dn in dirs: os.rmdir(os.path.join(root, dn)) os.rmdir(origdir) preservedir(os.path.join(config.workspace_path, 'recipes', pn)) # We don't automatically create this dir next to appends, but the user can preservedir(os.path.join(config.workspace_path, 'appends', pn)) return 0
def build(args, config, basepath, workspace): """Entry point for the devtool 'build' subcommand""" if not args.recipename in workspace: raise DevtoolError("no recipe named %s in your workspace" % args.recipename) build_task = config.get('Build', 'build_task', 'populate_sysroot') postfile_param = "" postfile = "" if args.disable_parallel_make: logger.info("Disabling 'make' parallelism") postfile = os.path.join(basepath, 'conf', 'disable_parallelism.conf') _create_conf_file({'PARALLEL_MAKE':''}, postfile) postfile_param = "-R %s" % postfile try: exec_build_env_command(config.init_path, basepath, 'bitbake -c %s %s %s' % (build_task, postfile_param, args.recipename), watch=True) except bb.process.ExecutionError as e: # We've already seen the output since watch=True, so just ensure we return something to the user return e.exitcode finally: if postfile: logger.debug('Removing postfile') os.remove(postfile) return 0
def _write_append(rc, srctree, same_dir, no_same_dir, rev, copied, workspace, d): """Writes an append file""" if not os.path.exists(rc): raise DevtoolError("bbappend not created because %s does not exist" % rc) appendpath = os.path.join(workspace, 'appends') if not os.path.exists(appendpath): bb.utils.mkdirhier(appendpath) brf = os.path.basename(os.path.splitext(rc)[0]) # rc basename srctree = os.path.abspath(srctree) pn = d.getVar('PN') af = os.path.join(appendpath, '%s.bbappend' % brf) with open(af, 'w') as f: f.write('FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n\n') f.write('inherit externalsrc\n') f.write(( '# NOTE: We use pn- overrides here to avoid affecting' 'multiple variants in the case where the recipe uses BBCLASSEXTEND\n' )) f.write('EXTERNALSRC_pn-%s = "%s"\n' % (pn, srctree)) b_is_s = use_external_build(same_dir, no_same_dir, d) if b_is_s: f.write('EXTERNALSRC_BUILD_pn-%s = "%s"\n' % (pn, srctree)) f.write('\n') if rev: f.write('# initial_rev: %s\n' % rev) if copied: f.write('# original_path: %s\n' % os.path.dirname(d.getVar('FILE'))) f.write('# original_files: %s\n' % ' '.join(copied)) return af
def _check_compatible_recipe(pn, d): """Check if the recipe is supported by devtool""" if pn == 'perf': raise DevtoolError("The perf recipe does not actually check out " "source and thus cannot be supported by this tool") if pn in ['kernel-devsrc', 'package-index'] or pn.startswith('gcc-source'): raise DevtoolError("The %s recipe is not supported by this tool" % pn) if bb.data.inherits_class('image', d): raise DevtoolError("The %s recipe is an image, and therefore is not " "supported by this tool" % pn) if bb.data.inherits_class('populate_sdk', d): raise DevtoolError("The %s recipe is an SDK, and therefore is not " "supported by this tool" % pn) if bb.data.inherits_class('packagegroup', d): raise DevtoolError("The %s recipe is a packagegroup, and therefore is " "not supported by this tool" % pn) if bb.data.inherits_class('meta', d): raise DevtoolError("The %s recipe is a meta-recipe, and therefore is " "not supported by this tool" % pn) if bb.data.inherits_class('externalsrc', d) and d.getVar( 'EXTERNALSRC', True): raise DevtoolError("externalsrc is currently enabled for the %s " "recipe. This prevents the normal do_patch task " "from working. You will need to disable this " "first." % pn)
def _find_recipe_path(args, config, basepath, workspace): if args.any_recipe: tinfoil = setup_tinfoil(config_only=False, basepath=basepath) try: rd = parse_recipe(config, tinfoil, args.recipename, True) if not rd: raise DevtoolError("Failed to find specified recipe") recipefile = rd.getVar('FILE') finally: tinfoil.shutdown() else: check_workspace_recipe(workspace, args.recipename) recipefile = workspace[args.recipename]['recipefile'] if not recipefile: raise DevtoolError( "Recipe file for %s is not under the workspace" % args.recipename) return recipefile
def undeploy(args, config, basepath, workspace): """Entry point for the devtool 'undeploy' subcommand""" deploy_file = os.path.join(basepath, 'target_deploy', args.target, args.recipename + '.list') if not os.path.exists(deploy_file): raise DevtoolError('%s has not been deployed' % args.recipename) if args.dry_run: print( 'Previously deployed files to be un-deployed for %s on target %s:' % (args.recipename, args.target)) with open(deploy_file, 'r') as f: for line in f: print(' %s' % line.rstrip()) return 0 extraoptions = '' if args.no_host_check: extraoptions += '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' if not args.show_status: extraoptions += ' -q' ret = subprocess.call("scp %s %s %s:/tmp" % (extraoptions, deploy_file, args.target), shell=True) if ret != 0: raise DevtoolError('Failed to copy file list to %s - rerun with -s to ' 'get a complete error message' % args.target) ret = subprocess.call( "ssh %s %s 'xargs -n1 rm -f </tmp/%s'" % (extraoptions, args.target, os.path.basename(deploy_file)), shell=True) if ret == 0: logger.info('Successfully undeployed %s' % args.recipename) os.remove(deploy_file) else: raise DevtoolError('Undeploy failed - rerun with -s to get a complete ' 'error message') return ret
def menuconfig(args, config, basepath, workspace): """Entry point for the devtool 'menuconfig' subcommand""" rd = "" kconfigpath = "" pn_src = "" localfilesdir = "" workspace_dir = "" tinfoil = setup_tinfoil(basepath=basepath) try: rd = parse_recipe(config, tinfoil, args.component, appends=True, filter_workspace=False) if not rd: return 1 check_workspace_recipe(workspace, args.component) pn = rd.getVar('PN', True) if not rd.getVarFlag('do_menuconfig', 'task'): raise DevtoolError( "This recipe does not support menuconfig option") workspace_dir = os.path.join(config.workspace_path, 'sources') kconfigpath = rd.getVar('B') pn_src = os.path.join(workspace_dir, pn) # add check to see if oe_local_files exists or not localfilesdir = os.path.join(pn_src, 'oe-local-files') if not os.path.exists(localfilesdir): bb.utils.mkdirhier(localfilesdir) # Add gitignore to ensure source tree is clean gitignorefile = os.path.join(localfilesdir, '.gitignore') with open(gitignorefile, 'w') as f: f.write( '# Ignore local files, by default. Remove this file if you want to commit the directory to Git\n' ) f.write('*\n') finally: tinfoil.shutdown() logger.info('Launching menuconfig') exec_build_env_command(config.init_path, basepath, 'bitbake -c menuconfig %s' % pn, watch=True) fragment = os.path.join(localfilesdir, 'devtool-fragment.cfg') res = standard._create_kconfig_diff(pn_src, rd, fragment) return 0
def _rename_recipe_file(bpn, oldpv, newpv, path): oldrecipe = "%s_%s.bb" % (bpn, oldpv) newrecipe = "%s_%s.bb" % (bpn, newpv) if os.path.isfile(os.path.join(path, oldrecipe)): if oldrecipe != newrecipe: _run('mv %s %s' % (oldrecipe, newrecipe), cwd=path) else: recipe = "%s_git.bb" % bpn if os.path.isfile(os.path.join(path, recipe)): newrecipe = recipe raise DevtoolError("Original recipe not found on workspace") return os.path.join(path, newrecipe)
def update_recipe(args, config, basepath, workspace): """Entry point for the devtool 'update-recipe' subcommand""" if not args.recipename in workspace: raise DevtoolError("no recipe named %s in your workspace" % args.recipename) if args.append: if not os.path.exists(args.append): raise DevtoolError('bbappend destination layer directory "%s" ' 'does not exist' % args.append) if not os.path.exists(os.path.join(args.append, 'conf', 'layer.conf')): raise DevtoolError('conf/layer.conf not found in bbappend ' 'destination layer "%s"' % args.append) tinfoil = setup_tinfoil() rd = parse_recipe(config, tinfoil, args.recipename, True) if not rd: return 1 srctree = workspace[args.recipename]['srctree'] if args.mode == 'auto': mode = _guess_recipe_update_mode(srctree, rd) else: mode = args.mode if mode == 'srcrev': _update_recipe_srcrev(args, srctree, rd, tinfoil.config_data) elif mode == 'patch': _update_recipe_patch(args, config, srctree, rd, tinfoil.config_data) else: raise DevtoolError('update_recipe: invalid mode %s' % mode) rf = rd.getVar('FILE', True) if rf.startswith(config.workspace_path): logger.warn( 'Recipe file %s has been updated but is inside the workspace - you will need to move it (and any associated files next to it) out to the desired layer before using "devtool reset" in order to keep any changes' % rf) return 0
def update_recipe(args, config, basepath, workspace): """Entry point for the devtool 'update-recipe' subcommand""" if not args.recipename in workspace: raise DevtoolError("no recipe named %s in your workspace" % args.recipename) if args.append: if not os.path.exists(args.append): raise DevtoolError('bbappend destination layer directory "%s" ' 'does not exist' % args.append) if not os.path.exists(os.path.join(args.append, 'conf', 'layer.conf')): raise DevtoolError('conf/layer.conf not found in bbappend ' 'destination layer "%s"' % args.append) tinfoil = setup_tinfoil() rd = _parse_recipe(config, tinfoil, args.recipename, True) if not rd: return 1 orig_src_uri = rd.getVar('SRC_URI', False) or '' if args.mode == 'auto': if 'git://' in orig_src_uri: mode = 'srcrev' else: mode = 'patch' else: mode = args.mode srctree = workspace[args.recipename]['srctree'] if mode == 'srcrev': _update_recipe_srcrev(args, srctree, rd, tinfoil.config_data) elif mode == 'patch': _update_recipe_patch(args, config, srctree, rd, tinfoil.config_data) else: raise DevtoolError('update_recipe: invalid mode %s' % mode) return 0
def _get_uri(rd): srcuris = rd.getVar('SRC_URI').split() if not len(srcuris): raise DevtoolError('SRC_URI not found on recipe') # Get first non-local entry in SRC_URI - usually by convention it's # the first entry, but not always! srcuri = None for entry in srcuris: if not entry.startswith('file://'): srcuri = entry break if not srcuri: raise DevtoolError('Unable to find non-local entry in SRC_URI') srcrev = '${AUTOREV}' if '://' in srcuri: # Fetch a URL rev_re = re.compile(';rev=([^;]+)') res = rev_re.search(srcuri) if res: srcrev = res.group(1) srcuri = rev_re.sub('', srcuri) return srcuri, srcrev
def _get_uri(rd): srcuris = rd.getVar('SRC_URI', True).split() if not len(srcuris): raise DevtoolError('SRC_URI not found on recipe') srcuri = srcuris[0] # it is assumed, URI is at first position srcrev = '${AUTOREV}' if '://' in srcuri: # Fetch a URL rev_re = re.compile(';rev=([^;]+)') res = rev_re.search(srcuri) if res: srcrev = res.group(1) srcuri = rev_re.sub('', srcuri) return srcuri, srcrev
def upgrade(args, config, basepath, workspace): """Entry point for the devtool 'upgrade' subcommand""" if args.recipename in workspace: raise DevtoolError("recipe %s is already in your workspace" % args.recipename) if not args.version and not args.srcrev: raise DevtoolError("You must provide a version using the --version/-V option, or for recipes that fetch from an SCM such as git, the --srcrev/-S option") if args.srcbranch and not args.srcrev: raise DevtoolError("If you specify --srcbranch/-B then you must use --srcrev/-S to specify the revision" % args.recipename) tinfoil = setup_tinfoil(basepath=basepath, tracking=True) try: rd = parse_recipe(config, tinfoil, args.recipename, True) if not rd: return 1 pn = rd.getVar('PN') if pn != args.recipename: logger.info('Mapping %s to %s' % (args.recipename, pn)) if pn in workspace: raise DevtoolError("recipe %s is already in your workspace" % pn) if args.srctree: srctree = os.path.abspath(args.srctree) else: srctree = standard.get_default_srctree(config, pn) standard._check_compatible_recipe(pn, rd) old_srcrev = rd.getVar('SRCREV') if old_srcrev == 'INVALID': old_srcrev = None if old_srcrev and not args.srcrev: raise DevtoolError("Recipe specifies a SRCREV value; you must specify a new one when upgrading") if rd.getVar('PV') == args.version and old_srcrev == args.srcrev: raise DevtoolError("Current and upgrade versions are the same version") rf = None try: rev1 = standard._extract_source(srctree, False, 'devtool-orig', False, rd, tinfoil) rev2, md5, sha256 = _extract_new_source(args.version, srctree, args.no_patch, args.srcrev, args.branch, args.keep_temp, tinfoil, rd) rf, copied = _create_new_recipe(args.version, md5, sha256, args.srcrev, args.srcbranch, config.workspace_path, tinfoil, rd) except bb.process.CmdError as e: _upgrade_error(e, rf, srctree) except DevtoolError as e: _upgrade_error(e, rf, srctree) standard._add_md5(config, pn, os.path.dirname(rf)) af = _write_append(rf, srctree, args.same_dir, args.no_same_dir, rev2, copied, config.workspace_path, rd) standard._add_md5(config, pn, af) logger.info('Upgraded source extracted to %s' % srctree) logger.info('New recipe is %s' % rf) finally: tinfoil.shutdown() return 0
def _prep_extract_operation(config, basepath, recipename): """HACK: Ugly workaround for making sure that requirements are met when trying to extract a package. Returns the tinfoil instance to be used.""" tinfoil = setup_tinfoil() rd = parse_recipe(config, tinfoil, recipename, True) if bb.data.inherits_class('kernel-yocto', rd): tinfoil.shutdown() try: stdout, _ = exec_build_env_command(config.init_path, basepath, 'bitbake kern-tools-native') tinfoil = setup_tinfoil() except bb.process.ExecutionError as err: raise DevtoolError("Failed to build kern-tools-native:\n%s" % err.stdout) return tinfoil
def find_recipe(args, config, basepath, workspace): """Entry point for the devtool 'find-recipe' subcommand""" if args.any_recipe: tinfoil = setup_tinfoil(config_only=False, basepath=basepath) try: rd = parse_recipe(config, tinfoil, args.recipename, True) if not rd: return 1 recipefile = rd.getVar('FILE') finally: tinfoil.shutdown() else: check_workspace_recipe(workspace, args.recipename) recipefile = workspace[args.recipename]['recipefile'] if not recipefile: raise DevtoolError("Recipe file for %s is not under the workspace" % args.recipename)
def build(args, config, basepath, workspace): """Entry point for the devtool 'build' subcommand""" import bb if not args.recipename in workspace: raise DevtoolError("no recipe named %s in your workspace" % args.recipename) build_task = config.get('Build', 'build_task', 'populate_sysroot') try: exec_build_env_command(config.init_path, basepath, 'bitbake -c %s %s' % (build_task, args.recipename), watch=True) except bb.process.ExecutionError as e: # We've already seen the output since watch=True, so just ensure we return something to the user return e.exitcode return 0
def _find_recipe_path(args, config, basepath, workspace): if args.any_recipe: logger.warning( '-a/--any-recipe option is now always active, and thus the option will be removed in a future release' ) if args.recipename in workspace: recipefile = workspace[args.recipename]['recipefile'] else: recipefile = None if not recipefile: tinfoil = setup_tinfoil(config_only=False, basepath=basepath) try: rd = parse_recipe(config, tinfoil, args.recipename, True) if not rd: raise DevtoolError("Failed to find specified recipe") recipefile = rd.getVar('FILE') finally: tinfoil.shutdown() return recipefile
def _check_git_config(): def getconfig(name): try: value = bb.process.run('git config --global %s' % name)[0].strip() except bb.process.ExecutionError as e: if e.exitcode == 1: value = None else: raise return value username = getconfig('user.name') useremail = getconfig('user.email') configerr = [] if not username: configerr.append('Please set your name using:\n git config --global user.name') if not useremail: configerr.append('Please set your email using:\n git config --global user.email') if configerr: raise DevtoolError('Your git configuration is incomplete which will prevent rebases from working:\n' + '\n'.join(configerr))
def package(args, config, basepath, workspace): """Entry point for the devtool 'package' subcommand""" if not args.recipename in workspace: raise DevtoolError("no recipe named %s in your workspace" % args.recipename) image_pkgtype = config.get('Package', 'image_pkgtype', '') if not image_pkgtype: tinfoil = setup_tinfoil() try: tinfoil.prepare(config_only=True) image_pkgtype = tinfoil.config_data.getVar('IMAGE_PKGTYPE', True) finally: tinfoil.shutdown() package_task = config.get('Package', 'package_task', 'package_write_%s' % image_pkgtype) try: exec_build_env_command(config.init_path, basepath, 'bitbake -c %s %s' % (package_task, args.recipename), watch=True) except bb.process.ExecutionError as e: # We've already seen the output since watch=True, so just ensure we return something to the user return e.exitcode logger.info('Your packages are in %s/tmp/deploy/%s' % (basepath, image_pkgtype)) return 0
def upgrade(args, config, basepath, workspace): """Entry point for the devtool 'upgrade' subcommand""" if args.recipename in workspace: raise DevtoolError("recipe %s is already in your workspace" % args.recipename) if args.srcbranch and not args.srcrev: raise DevtoolError( "If you specify --srcbranch/-B then you must use --srcrev/-S to specify the revision" % args.recipename) _check_git_config() tinfoil = setup_tinfoil(basepath=basepath, tracking=True) try: rd = parse_recipe(config, tinfoil, args.recipename, True) if not rd: return 1 pn = rd.getVar('PN') if pn != args.recipename: logger.info('Mapping %s to %s' % (args.recipename, pn)) if pn in workspace: raise DevtoolError("recipe %s is already in your workspace" % pn) if args.srctree: srctree = os.path.abspath(args.srctree) else: srctree = standard.get_default_srctree(config, pn) # try to automatically discover latest version and revision if not provided on command line if not args.version and not args.srcrev: version_info = oe.recipeutils.get_recipe_upstream_version(rd) if version_info['version'] and not version_info[ 'version'].endswith("new-commits-available"): args.version = version_info['version'] if version_info['revision']: args.srcrev = version_info['revision'] if not args.version and not args.srcrev: raise DevtoolError( "Automatic discovery of latest version/revision failed - you must provide a version using the --version/-V option, or for recipes that fetch from an SCM such as git, the --srcrev/-S option." ) standard._check_compatible_recipe(pn, rd) old_srcrev = rd.getVar('SRCREV') if old_srcrev == 'INVALID': old_srcrev = None if old_srcrev and not args.srcrev: raise DevtoolError( "Recipe specifies a SRCREV value; you must specify a new one when upgrading" ) old_ver = rd.getVar('PV') if old_ver == args.version and old_srcrev == args.srcrev: raise DevtoolError( "Current and upgrade versions are the same version") if args.version: if bb.utils.vercmp_string(args.version, old_ver) < 0: logger.warning( 'Upgrade version %s compares as less than the current version %s. If you are using a package feed for on-target upgrades or providing this recipe for general consumption, then you should increment PE in the recipe (or if there is no current PE value set, set it to "1")' % (args.version, old_ver)) check_prerelease_version(args.version, 'devtool upgrade') rf = None license_diff = None try: logger.info('Extracting current version source...') rev1, srcsubdir1 = standard._extract_source( srctree, False, 'devtool-orig', False, config, basepath, workspace, args.fixed_setup, rd, tinfoil, no_overrides=args.no_overrides) old_licenses = _extract_licenses(srctree, rd.getVar('LIC_FILES_CHKSUM')) logger.info('Extracting upgraded version source...') rev2, md5, sha256, srcbranch, srcsubdir2 = _extract_new_source( args.version, srctree, args.no_patch, args.srcrev, args.srcbranch, args.branch, args.keep_temp, tinfoil, rd) new_licenses = _extract_licenses(srctree, rd.getVar('LIC_FILES_CHKSUM')) license_diff = _generate_license_diff(old_licenses, new_licenses) rf, copied = _create_new_recipe(args.version, md5, sha256, args.srcrev, srcbranch, srcsubdir1, srcsubdir2, config.workspace_path, tinfoil, rd, license_diff, new_licenses) except bb.process.CmdError as e: _upgrade_error(e, rf, srctree) except DevtoolError as e: _upgrade_error(e, rf, srctree) standard._add_md5(config, pn, os.path.dirname(rf)) af = _write_append(rf, srctree, args.same_dir, args.no_same_dir, rev2, copied, config.workspace_path, rd) standard._add_md5(config, pn, af) update_unlockedsigs(basepath, workspace, args.fixed_setup, [pn]) logger.info('Upgraded source extracted to %s' % srctree) logger.info('New recipe is %s' % rf) if license_diff: logger.info( 'License checksums have been updated in the new recipe; please refer to it for the difference between the old and the new license texts.' ) finally: tinfoil.shutdown() return 0
def _create_new_recipe(newpv, md5, sha256, srcrev, srcbranch, srcsubdir_old, srcsubdir_new, workspace, tinfoil, rd, license_diff, new_licenses): """Creates the new recipe under workspace""" bpn = rd.getVar('BPN') path = os.path.join(workspace, 'recipes', bpn) bb.utils.mkdirhier(path) copied, _ = oe.recipeutils.copy_recipe_files(rd, path, all_variants=True) if not copied: raise DevtoolError( 'Internal error - no files were copied for recipe %s' % bpn) logger.debug('Copied %s to %s' % (copied, path)) oldpv = rd.getVar('PV') if not newpv: newpv = oldpv origpath = rd.getVar('FILE') fullpath = _rename_recipe_files(origpath, bpn, oldpv, newpv, path) logger.debug('Upgraded %s => %s' % (origpath, fullpath)) newvalues = {} if _recipe_contains(rd, 'PV') and newpv != oldpv: newvalues['PV'] = newpv if srcrev: newvalues['SRCREV'] = srcrev if srcbranch: src_uri = oe.recipeutils.split_var_value( rd.getVar('SRC_URI', False) or '') changed = False replacing = True new_src_uri = [] for entry in src_uri: scheme, network, path, user, passwd, params = bb.fetch2.decodeurl( entry) if replacing and scheme in ['git', 'gitsm']: branch = params.get('branch', 'master') if rd.expand(branch) != srcbranch: # Handle case where branch is set through a variable res = re.match(r'\$\{([^}@]+)\}', branch) if res: newvalues[res.group(1)] = srcbranch # We know we won't change SRC_URI now, so break out break else: params['branch'] = srcbranch entry = bb.fetch2.encodeurl( (scheme, network, path, user, passwd, params)) changed = True replacing = False new_src_uri.append(entry) if changed: newvalues['SRC_URI'] = ' '.join(new_src_uri) newvalues['PR'] = None # Work out which SRC_URI entries have changed in case the entry uses a name crd = rd.createCopy() crd.setVar('PV', newpv) for var, value in newvalues.items(): crd.setVar(var, value) old_src_uri = (rd.getVar('SRC_URI') or '').split() new_src_uri = (crd.getVar('SRC_URI') or '').split() newnames = [] addnames = [] for newentry in new_src_uri: _, _, _, _, _, params = bb.fetch2.decodeurl(newentry) if 'name' in params: newnames.append(params['name']) if newentry not in old_src_uri: addnames.append(params['name']) # Find what's been set in the original recipe oldnames = [] noname = False for varflag in rd.getVarFlags('SRC_URI'): if varflag.endswith(('.md5sum', '.sha256sum')): name = varflag.rsplit('.', 1)[0] if name not in oldnames: oldnames.append(name) elif varflag in ['md5sum', 'sha256sum']: noname = True # Even if SRC_URI has named entries it doesn't have to actually use the name if noname and addnames and addnames[0] not in oldnames: addnames = [] # Drop any old names (the name actually might include ${PV}) for name in oldnames: if name not in newnames: newvalues['SRC_URI[%s.md5sum]' % name] = None newvalues['SRC_URI[%s.sha256sum]' % name] = None if md5 and sha256: if addnames: nameprefix = '%s.' % addnames[0] else: nameprefix = '' newvalues['SRC_URI[%smd5sum]' % nameprefix] = md5 newvalues['SRC_URI[%ssha256sum]' % nameprefix] = sha256 if srcsubdir_new != srcsubdir_old: s_subdir_old = os.path.relpath(os.path.abspath(rd.getVar('S')), rd.getVar('WORKDIR')) s_subdir_new = os.path.relpath(os.path.abspath(crd.getVar('S')), crd.getVar('WORKDIR')) if srcsubdir_old == s_subdir_old and srcsubdir_new != s_subdir_new: # Subdir for old extracted source matches what S points to (it should!) # but subdir for new extracted source doesn't match what S will be newvalues['S'] = '${WORKDIR}/%s' % srcsubdir_new.replace( newpv, '${PV}') if crd.expand(newvalues['S']) == crd.expand('${WORKDIR}/${BP}'): # It's the default, drop it # FIXME what if S is being set in a .inc? newvalues['S'] = None logger.info( 'Source subdirectory has changed, dropping S value since it now matches the default ("${WORKDIR}/${BP}")' ) else: logger.info( 'Source subdirectory has changed, updating S value') if license_diff: newlicchksum = " ".join([ "file://{}".format(l['path']) + (";beginline={}".format(l['beginline']) if l['beginline'] else "") + (";endline={}".format(l['endline']) if l['endline'] else "") + (";md5={}".format(l['actual_md5'])) for l in new_licenses ]) newvalues["LIC_FILES_CHKSUM"] = newlicchksum _add_license_diff_to_recipe(fullpath, license_diff) rd = tinfoil.parse_recipe_file(fullpath, False) oe.recipeutils.patch_recipe(rd, fullpath, newvalues) return fullpath, copied