def _update_recipe_srcrev(args, srctree, rd, config_data): """Implement the 'srcrev' mode of update-recipe""" import bb import oe.recipeutils from oe.patch import GitApplyTree recipefile = rd.getVar('FILE', True) logger.info('Updating SRCREV in recipe %s' % os.path.basename(recipefile)) # Get HEAD revision try: stdout, _ = bb.process.run('git rev-parse HEAD', cwd=srctree) except bb.process.ExecutionError as err: raise DevtoolError('Failed to get HEAD revision in %s: %s' % (srctree, err)) srcrev = stdout.strip() if len(srcrev) != 40: raise DevtoolError('Invalid hash returned by git: %s' % stdout) destpath = None removepatches = [] patchfields = {} patchfields['SRCREV'] = srcrev orig_src_uri = rd.getVar('SRC_URI', False) or '' if not args.no_remove: # Find list of existing patches in recipe file existing_patches = oe.recipeutils.get_recipe_patches(rd) old_srcrev = (rd.getVar('SRCREV', False) or '') tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, old_srcrev, tempdir) newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: removepatches.append(patch) finally: shutil.rmtree(tempdir) if removepatches: srcuri = orig_src_uri.split() removedentries, _ = _remove_patch_entries(srcuri, removepatches) if removedentries: patchfields['SRC_URI'] = ' '.join(srcuri) if args.append: _, destpath = oe.recipeutils.bbappend_recipe( rd, args.append, None, wildcardver=args.wildcard_version, extralines=patchfields) else: oe.recipeutils.patch_recipe(rd, recipefile, patchfields) if not 'git://' in orig_src_uri: logger.info('You will need to update SRC_URI within the recipe to ' 'point to a git repository where you have pushed your ' 'changes') _remove_patch_files(args, removepatches, destpath)
def _export_patches(srctree, rd, start_rev, destdir): """Export patches from srctree to given location. Returns three-tuple of dicts: 1. updated - patches that already exist in SRCURI 2. added - new patches that don't exist in SRCURI 3 removed - patches that exist in SRCURI but not in exported patches In each dict the key is the 'basepath' of the URI and value is the absolute path to the existing file in recipe space (if any). """ import oe.recipeutils from oe.patch import GitApplyTree updated = OrderedDict() added = OrderedDict() seqpatch_re = re.compile('^([0-9]{4}-)?(.+)') existing_patches = dict((os.path.basename(path), path) for path in oe.recipeutils.get_recipe_patches(rd)) # Generate patches from Git, exclude local files directory patch_pathspec = _git_exclude_path(srctree, 'oe-local-files') GitApplyTree.extractPatches(srctree, start_rev, destdir, patch_pathspec) new_patches = sorted(os.listdir(destdir)) for new_patch in new_patches: # Strip numbering from patch names. If it's a git sequence named patch, # the numbers might not match up since we are starting from a different # revision This does assume that people are using unique shortlog # values, but they ought to be anyway... new_basename = seqpatch_re.match(new_patch).group(2) found = False for old_patch in existing_patches: old_basename = seqpatch_re.match(old_patch).group(2) if new_basename == old_basename: updated[new_patch] = existing_patches.pop(old_patch) found = True # Rename patch files if new_patch != old_patch: os.rename(os.path.join(destdir, new_patch), os.path.join(destdir, old_patch)) break if not found: added[new_patch] = None return (updated, added, existing_patches)
def update_recipe(args, config, basepath, workspace): if not args.recipename in workspace: logger.error("no recipe named %s in your workspace" % args.recipename) return -1 tinfoil = setup_tinfoil() import bb from oe.patch import GitApplyTree import oe.recipeutils rd = _parse_recipe(config, tinfoil, args.recipename, True) if not rd: return -1 recipefile = rd.getVar('FILE', True) # Get initial revision from bbappend append = os.path.join( config.workspace_path, 'appends', '%s.bbappend' % os.path.splitext(os.path.basename(recipefile))[0]) if not os.path.exists(append): logger.error('unable to find workspace bbappend for recipe %s' % args.recipename) 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 def remove_patches(srcuri, patchlist): # Remove any patches that we don't need updated = False for patch in patchlist: patchfile = os.path.basename(patch) for i in xrange(len(srcuri)): if srcuri[i].startswith('file://') and os.path.basename( srcuri[i]).split(';')[0] == patchfile: logger.info('Removing patch %s' % patchfile) srcuri.pop(i) # FIXME "git rm" here would be nice if the file in question is tracked # FIXME there's a chance that this file is referred to by another recipe, in which case deleting wouldn't be the right thing to do if patch.startswith(os.path.dirname(recipefile)): os.remove(patch) updated = True break return updated srctree = workspace[args.recipename] # Get HEAD revision try: (stdout, _) = bb.process.run('git rev-parse HEAD', cwd=srctree) except bb.process.ExecutionError as err: print('Failed to get HEAD revision in %s: %s' % (srctree, err)) return 1 srcrev = stdout.strip() if len(srcrev) != 40: logger.error('Invalid hash returned by git: %s' % stdout) return 1 if mode == 'srcrev': logger.info('Updating SRCREV in recipe %s' % os.path.basename(recipefile)) patchfields = {} patchfields['SRCREV'] = srcrev if not args.no_remove: # Find list of existing patches in recipe file existing_patches = oe.recipeutils.get_recipe_patches(rd) old_srcrev = (rd.getVar('SRCREV', False) or '') tempdir = tempfile.mkdtemp(prefix='devtool') removepatches = [] try: GitApplyTree.extractPatches(srctree, old_srcrev, tempdir) newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: removepatches.append(patch) finally: shutil.rmtree(tempdir) if removepatches: srcuri = (rd.getVar('SRC_URI', False) or '').split() if remove_patches(srcuri, removepatches): patchfields['SRC_URI'] = ' '.join(srcuri) oe.recipeutils.patch_recipe(tinfoil.config_data, recipefile, patchfields) if not 'git://' in orig_src_uri: logger.info( 'You will need to update SRC_URI within the recipe to point to a git repository where you have pushed your changes' ) elif mode == 'patch': commits = [] update_rev = None if args.initial_rev: initial_rev = args.initial_rev else: initial_rev = None with open(append, 'r') as f: for line in f: if line.startswith('# initial_rev:'): initial_rev = line.split(':')[-1].strip() elif line.startswith('# commit:'): commits.append(line.split(':')[-1].strip()) if initial_rev: # Find first actually changed revision (stdout, _) = bb.process.run( 'git rev-list --reverse %s..HEAD' % initial_rev, cwd=srctree) newcommits = stdout.split() for i in xrange(min(len(commits), len(newcommits))): if newcommits[i] == commits[i]: update_rev = commits[i] if not initial_rev: logger.error( 'Unable to find initial revision - please specify it with --initial-rev' ) return -1 if not update_rev: update_rev = initial_rev # Find list of existing patches in recipe file existing_patches = oe.recipeutils.get_recipe_patches(rd) removepatches = [] if not args.no_remove: # Get all patches from source tree and check if any should be removed tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, initial_rev, tempdir) newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile not in newpatches: removepatches.append(patch) finally: shutil.rmtree(tempdir) # Get updated patches from source tree tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, update_rev, tempdir) # Match up and replace existing patches with corresponding new patches updatepatches = False updaterecipe = False newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: logger.info('Updating patch %s' % patchfile) shutil.move(os.path.join(tempdir, patchfile), patch) newpatches.remove(patchfile) updatepatches = True srcuri = (rd.getVar('SRC_URI', False) or '').split() if newpatches: # Add any patches left over patchdir = os.path.join(os.path.dirname(recipefile), rd.getVar('BPN', True)) bb.utils.mkdirhier(patchdir) for patchfile in newpatches: logger.info('Adding new patch %s' % patchfile) shutil.move(os.path.join(tempdir, patchfile), os.path.join(patchdir, patchfile)) srcuri.append('file://%s' % patchfile) updaterecipe = True if removepatches: if remove_patches(srcuri, removepatches): updaterecipe = True if updaterecipe: logger.info('Updating recipe %s' % os.path.basename(recipefile)) oe.recipeutils.patch_recipe(tinfoil.config_data, recipefile, {'SRC_URI': ' '.join(srcuri)}) elif not updatepatches: # Neither patches nor recipe were updated logger.info('No patches need updating') finally: shutil.rmtree(tempdir) else: logger.error('update_recipe: invalid mode %s' % mode) return 1 return 0
def update_recipe(args, config, basepath, workspace): if not args.recipename in workspace: logger.error("no recipe named %s in your workspace" % args.recipename) return -1 # Get initial revision from bbappend appends = glob.glob(os.path.join(config.workspace_path, 'appends', '%s_*.bbappend' % args.recipename)) if not appends: logger.error('unable to find workspace bbappend for recipe %s' % args.recipename) return -1 tinfoil = setup_tinfoil() import bb from oe.patch import GitApplyTree import oe.recipeutils srctree = workspace[args.recipename] commits = [] update_rev = None if args.initial_rev: initial_rev = args.initial_rev else: initial_rev = None with open(appends[0], 'r') as f: for line in f: if line.startswith('# initial_rev:'): initial_rev = line.split(':')[-1].strip() elif line.startswith('# commit:'): commits.append(line.split(':')[-1].strip()) if initial_rev: # Find first actually changed revision (stdout, _) = bb.process.run('git rev-list --reverse %s..HEAD' % initial_rev, cwd=srctree) newcommits = stdout.split() for i in xrange(min(len(commits), len(newcommits))): if newcommits[i] == commits[i]: update_rev = commits[i] if not initial_rev: logger.error('Unable to find initial revision - please specify it with --initial-rev') return -1 if not update_rev: update_rev = initial_rev # Find list of existing patches in recipe file recipefile = _get_recipe_file(tinfoil.cooker, args.recipename) if not recipefile: # Error already logged return -1 rd = oe.recipeutils.parse_recipe(recipefile, tinfoil.config_data) existing_patches = oe.recipeutils.get_recipe_patches(rd) removepatches = [] if not args.no_remove: # Get all patches from source tree and check if any should be removed tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, initial_rev, tempdir) newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile not in newpatches: removepatches.append(patch) finally: shutil.rmtree(tempdir) # Get updated patches from source tree tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, update_rev, tempdir) # Match up and replace existing patches with corresponding new patches updatepatches = False updaterecipe = False newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: logger.info('Updating patch %s' % patchfile) shutil.move(os.path.join(tempdir, patchfile), patch) newpatches.remove(patchfile) updatepatches = True srcuri = (rd.getVar('SRC_URI', False) or '').split() if newpatches: # Add any patches left over patchdir = os.path.join(os.path.dirname(recipefile), rd.getVar('BPN', True)) bb.utils.mkdirhier(patchdir) for patchfile in newpatches: logger.info('Adding new patch %s' % patchfile) shutil.move(os.path.join(tempdir, patchfile), os.path.join(patchdir, patchfile)) srcuri.append('file://%s' % patchfile) updaterecipe = True if removepatches: # Remove any patches that we don't need for patch in removepatches: patchfile = os.path.basename(patch) for i in xrange(len(srcuri)): if srcuri[i].startswith('file://') and os.path.basename(srcuri[i]).split(';')[0] == patchfile: logger.info('Removing patch %s' % patchfile) srcuri.pop(i) # FIXME "git rm" here would be nice if the file in question is tracked # FIXME there's a chance that this file is referred to by another recipe, in which case deleting wouldn't be the right thing to do os.remove(patch) updaterecipe = True break if updaterecipe: logger.info('Updating recipe %s' % os.path.basename(recipefile)) oe.recipeutils.patch_recipe(rd, recipefile, {'SRC_URI': ' '.join(srcuri)}) elif not updatepatches: # Neither patches nor recipe were updated logger.info('No patches need updating') finally: shutil.rmtree(tempdir) return 0
def update_recipe(args, config, basepath, workspace): if not args.recipename in workspace: logger.error("no recipe named %s in your workspace" % args.recipename) return -1 # Get initial revision from bbappend appends = glob.glob( os.path.join(config.workspace_path, 'appends', '%s_*.bbappend' % args.recipename)) if not appends: logger.error('unable to find workspace bbappend for recipe %s' % args.recipename) return -1 tinfoil = setup_tinfoil() import bb from oe.patch import GitApplyTree import oe.recipeutils srctree = workspace[args.recipename] commits = [] update_rev = None if args.initial_rev: initial_rev = args.initial_rev else: initial_rev = None with open(appends[0], 'r') as f: for line in f: if line.startswith('# initial_rev:'): initial_rev = line.split(':')[-1].strip() elif line.startswith('# commit:'): commits.append(line.split(':')[-1].strip()) if initial_rev: # Find first actually changed revision (stdout, _) = bb.process.run('git rev-list --reverse %s..HEAD' % initial_rev, cwd=srctree) newcommits = stdout.split() for i in xrange(min(len(commits), len(newcommits))): if newcommits[i] == commits[i]: update_rev = commits[i] if not initial_rev: logger.error( 'Unable to find initial revision - please specify it with --initial-rev' ) return -1 if not update_rev: update_rev = initial_rev # Find list of existing patches in recipe file recipefile = _get_recipe_file(tinfoil.cooker, args.recipename) if not recipefile: # Error already logged return -1 rd = oe.recipeutils.parse_recipe(recipefile, tinfoil.config_data) existing_patches = oe.recipeutils.get_recipe_patches(rd) removepatches = [] if not args.no_remove: # Get all patches from source tree and check if any should be removed tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, initial_rev, tempdir) newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile not in newpatches: removepatches.append(patch) finally: shutil.rmtree(tempdir) # Get updated patches from source tree tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, update_rev, tempdir) # Match up and replace existing patches with corresponding new patches updatepatches = False updaterecipe = False newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: logger.info('Updating patch %s' % patchfile) shutil.move(os.path.join(tempdir, patchfile), patch) newpatches.remove(patchfile) updatepatches = True srcuri = (rd.getVar('SRC_URI', False) or '').split() if newpatches: # Add any patches left over patchdir = os.path.join(os.path.dirname(recipefile), rd.getVar('BPN', True)) bb.utils.mkdirhier(patchdir) for patchfile in newpatches: logger.info('Adding new patch %s' % patchfile) shutil.move(os.path.join(tempdir, patchfile), os.path.join(patchdir, patchfile)) srcuri.append('file://%s' % patchfile) updaterecipe = True if removepatches: # Remove any patches that we don't need for patch in removepatches: patchfile = os.path.basename(patch) for i in xrange(len(srcuri)): if srcuri[i].startswith('file://') and os.path.basename( srcuri[i]).split(';')[0] == patchfile: logger.info('Removing patch %s' % patchfile) srcuri.pop(i) # FIXME "git rm" here would be nice if the file in question is tracked # FIXME there's a chance that this file is referred to by another recipe, in which case deleting wouldn't be the right thing to do os.remove(patch) updaterecipe = True break if updaterecipe: logger.info('Updating recipe %s' % os.path.basename(recipefile)) oe.recipeutils.patch_recipe(rd, recipefile, {'SRC_URI': ' '.join(srcuri)}) elif not updatepatches: # Neither patches nor recipe were updated logger.info('No patches need updating') finally: shutil.rmtree(tempdir) return 0
def update_recipe(args, config, basepath, workspace): """Entry point for the devtool 'update-recipe' subcommand""" if not args.recipename in workspace: logger.error("no recipe named %s in your workspace" % args.recipename) return -1 if args.append: if not os.path.exists(args.append): logger.error('bbappend destination layer directory "%s" does not exist' % args.append) return 2 if not os.path.exists(os.path.join(args.append, 'conf', 'layer.conf')): logger.error('conf/layer.conf not found in bbappend destination layer "%s"' % args.append) return 2 tinfoil = setup_tinfoil() import bb from oe.patch import GitApplyTree import oe.recipeutils rd = _parse_recipe(config, tinfoil, args.recipename, True) if not rd: return -1 recipefile = rd.getVar('FILE', True) # Get initial revision from bbappend append = os.path.join(config.workspace_path, 'appends', '%s.bbappend' % os.path.splitext(os.path.basename(recipefile))[0]) if not os.path.exists(append): logger.error('unable to find workspace bbappend for recipe %s' % args.recipename) 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 def remove_patch_entries(srcuri, patchlist): """Remove patch entries from SRC_URI""" remaining = patchlist[:] entries = [] for patch in patchlist: patchfile = os.path.basename(patch) for i in xrange(len(srcuri)): if srcuri[i].startswith('file://') and os.path.basename(srcuri[i].split(';')[0]) == patchfile: entries.append(srcuri[i]) remaining.remove(patch) srcuri.pop(i) break return entries, remaining srctree = workspace[args.recipename] # Get HEAD revision try: (stdout, _) = bb.process.run('git rev-parse HEAD', cwd=srctree) except bb.process.ExecutionError as err: print('Failed to get HEAD revision in %s: %s' % (srctree, err)) return 1 srcrev = stdout.strip() if len(srcrev) != 40: logger.error('Invalid hash returned by git: %s' % stdout) return 1 removepatches = [] destpath = None if mode == 'srcrev': logger.info('Updating SRCREV in recipe %s' % os.path.basename(recipefile)) removevalues = None patchfields = {} patchfields['SRCREV'] = srcrev if not args.no_remove: # Find list of existing patches in recipe file existing_patches = oe.recipeutils.get_recipe_patches(rd) old_srcrev = (rd.getVar('SRCREV', False) or '') tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, old_srcrev, tempdir) newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: removepatches.append(patch) finally: shutil.rmtree(tempdir) if removepatches: srcuri = (rd.getVar('SRC_URI', False) or '').split() removedentries, _ = remove_patch_entries(srcuri, removepatches) if removedentries: patchfields['SRC_URI'] = ' '.join(srcuri) if args.append: (appendfile, destpath) = oe.recipeutils.bbappend_recipe(rd, args.append, None, wildcardver=args.wildcard_version, extralines=patchfields) else: oe.recipeutils.patch_recipe(tinfoil.config_data, recipefile, patchfields) if not 'git://' in orig_src_uri: logger.info('You will need to update SRC_URI within the recipe to point to a git repository where you have pushed your changes') elif mode == 'patch': commits = [] update_rev = None if args.initial_rev: initial_rev = args.initial_rev else: initial_rev = None with open(append, 'r') as f: for line in f: if line.startswith('# initial_rev:'): initial_rev = line.split(':')[-1].strip() elif line.startswith('# commit:'): commits.append(line.split(':')[-1].strip()) if initial_rev: # Find first actually changed revision (stdout, _) = bb.process.run('git rev-list --reverse %s..HEAD' % initial_rev, cwd=srctree) newcommits = stdout.split() for i in xrange(min(len(commits), len(newcommits))): if newcommits[i] == commits[i]: update_rev = commits[i] if not initial_rev: logger.error('Unable to find initial revision - please specify it with --initial-rev') return -1 if not update_rev: update_rev = initial_rev # Find list of existing patches in recipe file existing_patches = oe.recipeutils.get_recipe_patches(rd) removepatches = [] seqpatch_re = re.compile('^[0-9]{4}-') if not args.no_remove: # Get all patches from source tree and check if any should be removed tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, initial_rev, tempdir) newpatches = os.listdir(tempdir) for patch in existing_patches: # If it's a git sequence named patch, the numbers might not match up # since we are starting from a different revision # This does assume that people are using unique shortlog values, but # they ought to be anyway... patchfile = os.path.basename(patch) if seqpatch_re.search(patchfile): for newpatch in newpatches: if seqpatch_re.search(newpatch) and patchfile[5:] == newpatch[5:]: break else: removepatches.append(patch) elif patchfile not in newpatches: removepatches.append(patch) finally: shutil.rmtree(tempdir) # Get updated patches from source tree tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, update_rev, tempdir) # Match up and replace existing patches with corresponding new patches updatepatches = False updaterecipe = False newpatches = os.listdir(tempdir) if args.append: patchfiles = {} for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: patchfiles[os.path.join(tempdir, patchfile)] = patchfile newpatches.remove(patchfile) for patchfile in newpatches: patchfiles[os.path.join(tempdir, patchfile)] = None if patchfiles or removepatches: removevalues = None if removepatches: srcuri = (rd.getVar('SRC_URI', False) or '').split() removedentries, remaining = remove_patch_entries(srcuri, removepatches) if removedentries or remaining: removevalues = {'SRC_URI': removedentries + ['file://' + os.path.basename(item) for item in remaining]} (appendfile, destpath) = oe.recipeutils.bbappend_recipe(rd, args.append, patchfiles, removevalues=removevalues) else: logger.info('No patches needed updating') else: for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: logger.info('Updating patch %s' % patchfile) shutil.move(os.path.join(tempdir, patchfile), patch) newpatches.remove(patchfile) updatepatches = True srcuri = (rd.getVar('SRC_URI', False) or '').split() if newpatches: # Add any patches left over patchdir = os.path.join(os.path.dirname(recipefile), rd.getVar('BPN', True)) bb.utils.mkdirhier(patchdir) for patchfile in newpatches: logger.info('Adding new patch %s' % patchfile) shutil.move(os.path.join(tempdir, patchfile), os.path.join(patchdir, patchfile)) srcuri.append('file://%s' % patchfile) updaterecipe = True if removepatches: removedentries, _ = remove_patch_entries(srcuri, removepatches) if removedentries: updaterecipe = True if updaterecipe: logger.info('Updating recipe %s' % os.path.basename(recipefile)) oe.recipeutils.patch_recipe(tinfoil.config_data, recipefile, {'SRC_URI': ' '.join(srcuri)}) elif not updatepatches: # Neither patches nor recipe were updated logger.info('No patches need updating') finally: shutil.rmtree(tempdir) else: logger.error('update_recipe: invalid mode %s' % mode) return 1 if removepatches: for patchfile in removepatches: if args.append: if not destpath: raise Exception('destpath should be set here') patchfile = os.path.join(destpath, os.path.basename(patchfile)) if os.path.exists(patchfile): logger.info('Removing patch %s' % patchfile) # FIXME "git rm" here would be nice if the file in question is tracked # FIXME there's a chance that this file is referred to by another recipe, in which case deleting wouldn't be the right thing to do os.remove(patchfile) # Remove directory if empty try: os.rmdir(os.path.dirname(patchfile)) except OSError as ose: if ose.errno != errno.ENOTEMPTY: raise return 0
def _update_recipe_patch(args, config, srctree, rd, config_data): """Implement the 'patch' mode of update-recipe""" import bb import oe.recipeutils from oe.patch import GitApplyTree recipefile = rd.getVar('FILE', True) append = os.path.join( config.workspace_path, 'appends', '%s.bbappend' % os.path.splitext(os.path.basename(recipefile))[0]) if not os.path.exists(append): raise DevtoolError('unable to find workspace bbappend for recipe %s' % args.recipename) initial_rev, update_rev = _get_patchset_revs(args, srctree, append) if not initial_rev: raise DevtoolError('Unable to find initial revision - please specify ' 'it with --initial-rev') # Find list of existing patches in recipe file existing_patches = oe.recipeutils.get_recipe_patches(rd) removepatches = [] seqpatch_re = re.compile('^([0-9]{4}-)?(.+)') if not args.no_remove: # Get all patches from source tree and check if any should be removed tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, initial_rev, tempdir) # Strip numbering from patch names. If it's a git sequence named # patch, the numbers might not match up since we are starting from # a different revision This does assume that people are using # unique shortlog values, but they ought to be anyway... newpatches = [ seqpatch_re.match(fname).group(2) for fname in os.listdir(tempdir) ] for patch in existing_patches: basename = seqpatch_re.match(os.path.basename(patch)).group(2) if basename not in newpatches: removepatches.append(patch) finally: shutil.rmtree(tempdir) # Get updated patches from source tree tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, update_rev, tempdir) # Match up and replace existing patches with corresponding new patches updatepatches = False updaterecipe = False destpath = None newpatches = os.listdir(tempdir) if args.append: patchfiles = {} for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: patchfiles[os.path.join(tempdir, patchfile)] = patchfile newpatches.remove(patchfile) for patchfile in newpatches: patchfiles[os.path.join(tempdir, patchfile)] = None if patchfiles or removepatches: removevalues = None if removepatches: srcuri = (rd.getVar('SRC_URI', False) or '').split() removedentries, remaining = _remove_patch_entries( srcuri, removepatches) if removedentries or remaining: remaining = [ 'file://' + os.path.basename(item) for item in remaining ] removevalues = {'SRC_URI': removedentries + remaining} _, destpath = oe.recipeutils.bbappend_recipe( rd, args.append, patchfiles, removevalues=removevalues) else: logger.info('No patches needed updating') else: for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: logger.info('Updating patch %s' % patchfile) shutil.move(os.path.join(tempdir, patchfile), patch) newpatches.remove(patchfile) updatepatches = True srcuri = (rd.getVar('SRC_URI', False) or '').split() if newpatches: # Add any patches left over patchdir = os.path.join(os.path.dirname(recipefile), rd.getVar('BPN', True)) bb.utils.mkdirhier(patchdir) for patchfile in newpatches: logger.info('Adding new patch %s' % patchfile) shutil.move(os.path.join(tempdir, patchfile), os.path.join(patchdir, patchfile)) srcuri.append('file://%s' % patchfile) updaterecipe = True if removepatches: removedentries, _ = _remove_patch_entries( srcuri, removepatches) if removedentries: updaterecipe = True if updaterecipe: logger.info('Updating recipe %s' % os.path.basename(recipefile)) oe.recipeutils.patch_recipe(rd, recipefile, {'SRC_URI': ' '.join(srcuri)}) elif not updatepatches: # Neither patches nor recipe were updated logger.info('No patches need updating') finally: shutil.rmtree(tempdir) _remove_patch_files(args, removepatches, destpath)
def _update_recipe_patch(args, config, srctree, rd, config_data): """Implement the 'patch' mode of update-recipe""" import bb import oe.recipeutils from oe.patch import GitApplyTree recipefile = rd.getVar('FILE', True) append = os.path.join(config.workspace_path, 'appends', '%s.bbappend' % os.path.splitext(os.path.basename(recipefile))[0]) if not os.path.exists(append): raise DevtoolError('unable to find workspace bbappend for recipe %s' % args.recipename) initial_rev, update_rev = _get_patchset_revs(args, srctree, append) if not initial_rev: raise DevtoolError('Unable to find initial revision - please specify ' 'it with --initial-rev') # Find list of existing patches in recipe file existing_patches = oe.recipeutils.get_recipe_patches(rd) removepatches = [] seqpatch_re = re.compile('^([0-9]{4}-)?(.+)') if not args.no_remove: # Get all patches from source tree and check if any should be removed tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, initial_rev, tempdir) # Strip numbering from patch names. If it's a git sequence named # patch, the numbers might not match up since we are starting from # a different revision This does assume that people are using # unique shortlog values, but they ought to be anyway... newpatches = [seqpatch_re.match(fname).group(2) for fname in os.listdir(tempdir)] for patch in existing_patches: basename = seqpatch_re.match( os.path.basename(patch)).group(2) if basename not in newpatches: removepatches.append(patch) finally: shutil.rmtree(tempdir) # Get updated patches from source tree tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, update_rev, tempdir) # Match up and replace existing patches with corresponding new patches updatepatches = False updaterecipe = False destpath = None newpatches = os.listdir(tempdir) if args.append: patchfiles = {} for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: patchfiles[os.path.join(tempdir, patchfile)] = patchfile newpatches.remove(patchfile) for patchfile in newpatches: patchfiles[os.path.join(tempdir, patchfile)] = None if patchfiles or removepatches: removevalues = None if removepatches: srcuri = (rd.getVar('SRC_URI', False) or '').split() removedentries, remaining = _remove_patch_entries( srcuri, removepatches) if removedentries or remaining: remaining = ['file://' + os.path.basename(item) for item in remaining] removevalues = {'SRC_URI': removedentries + remaining} _, destpath = oe.recipeutils.bbappend_recipe( rd, args.append, patchfiles, removevalues=removevalues) else: logger.info('No patches needed updating') else: for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: logger.info('Updating patch %s' % patchfile) shutil.move(os.path.join(tempdir, patchfile), patch) newpatches.remove(patchfile) updatepatches = True srcuri = (rd.getVar('SRC_URI', False) or '').split() if newpatches: # Add any patches left over patchdir = os.path.join(os.path.dirname(recipefile), rd.getVar('BPN', True)) bb.utils.mkdirhier(patchdir) for patchfile in newpatches: logger.info('Adding new patch %s' % patchfile) shutil.move(os.path.join(tempdir, patchfile), os.path.join(patchdir, patchfile)) srcuri.append('file://%s' % patchfile) updaterecipe = True if removepatches: removedentries, _ = _remove_patch_entries(srcuri, removepatches) if removedentries: updaterecipe = True if updaterecipe: logger.info('Updating recipe %s' % os.path.basename(recipefile)) oe.recipeutils.patch_recipe(rd, recipefile, {'SRC_URI': ' '.join(srcuri)}) elif not updatepatches: # Neither patches nor recipe were updated logger.info('No patches need updating') finally: shutil.rmtree(tempdir) _remove_patch_files(args, removepatches, destpath)
def update_recipe(args, config, basepath, workspace): if not args.recipename in workspace: logger.error("no recipe named %s in your workspace" % args.recipename) return -1 tinfoil = setup_tinfoil() import bb from oe.patch import GitApplyTree import oe.recipeutils rd = _parse_recipe(config, tinfoil, args.recipename, True) if not rd: return -1 recipefile = rd.getVar('FILE', True) # Get initial revision from bbappend append = os.path.join(config.workspace_path, 'appends', '%s.bbappend' % os.path.splitext(os.path.basename(recipefile))[0]) if not os.path.exists(append): logger.error('unable to find workspace bbappend for recipe %s' % args.recipename) 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 def remove_patches(srcuri, patchlist): # Remove any patches that we don't need updated = False for patch in patchlist: patchfile = os.path.basename(patch) for i in xrange(len(srcuri)): if srcuri[i].startswith('file://') and os.path.basename(srcuri[i]).split(';')[0] == patchfile: logger.info('Removing patch %s' % patchfile) srcuri.pop(i) # FIXME "git rm" here would be nice if the file in question is tracked # FIXME there's a chance that this file is referred to by another recipe, in which case deleting wouldn't be the right thing to do if patch.startswith(os.path.dirname(recipefile)): os.remove(patch) updated = True break return updated srctree = workspace[args.recipename] # Get HEAD revision try: (stdout, _) = bb.process.run('git rev-parse HEAD', cwd=srctree) except bb.process.ExecutionError as err: print('Failed to get HEAD revision in %s: %s' % (srctree, err)) return 1 srcrev = stdout.strip() if len(srcrev) != 40: logger.error('Invalid hash returned by git: %s' % stdout) return 1 if mode == 'srcrev': logger.info('Updating SRCREV in recipe %s' % os.path.basename(recipefile)) patchfields = {} patchfields['SRCREV'] = srcrev if not args.no_remove: # Find list of existing patches in recipe file existing_patches = oe.recipeutils.get_recipe_patches(rd) old_srcrev = (rd.getVar('SRCREV', False) or '') tempdir = tempfile.mkdtemp(prefix='devtool') removepatches = [] try: GitApplyTree.extractPatches(srctree, old_srcrev, tempdir) newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: removepatches.append(patch) finally: shutil.rmtree(tempdir) if removepatches: srcuri = (rd.getVar('SRC_URI', False) or '').split() if remove_patches(srcuri, removepatches): patchfields['SRC_URI'] = ' '.join(srcuri) oe.recipeutils.patch_recipe(tinfoil.config_data, recipefile, patchfields) if not 'git://' in orig_src_uri: logger.info('You will need to update SRC_URI within the recipe to point to a git repository where you have pushed your changes') elif mode == 'patch': commits = [] update_rev = None if args.initial_rev: initial_rev = args.initial_rev else: initial_rev = None with open(append, 'r') as f: for line in f: if line.startswith('# initial_rev:'): initial_rev = line.split(':')[-1].strip() elif line.startswith('# commit:'): commits.append(line.split(':')[-1].strip()) if initial_rev: # Find first actually changed revision (stdout, _) = bb.process.run('git rev-list --reverse %s..HEAD' % initial_rev, cwd=srctree) newcommits = stdout.split() for i in xrange(min(len(commits), len(newcommits))): if newcommits[i] == commits[i]: update_rev = commits[i] if not initial_rev: logger.error('Unable to find initial revision - please specify it with --initial-rev') return -1 if not update_rev: update_rev = initial_rev # Find list of existing patches in recipe file existing_patches = oe.recipeutils.get_recipe_patches(rd) removepatches = [] if not args.no_remove: # Get all patches from source tree and check if any should be removed tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, initial_rev, tempdir) newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile not in newpatches: removepatches.append(patch) finally: shutil.rmtree(tempdir) # Get updated patches from source tree tempdir = tempfile.mkdtemp(prefix='devtool') try: GitApplyTree.extractPatches(srctree, update_rev, tempdir) # Match up and replace existing patches with corresponding new patches updatepatches = False updaterecipe = False newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: logger.info('Updating patch %s' % patchfile) shutil.move(os.path.join(tempdir, patchfile), patch) newpatches.remove(patchfile) updatepatches = True srcuri = (rd.getVar('SRC_URI', False) or '').split() if newpatches: # Add any patches left over patchdir = os.path.join(os.path.dirname(recipefile), rd.getVar('BPN', True)) bb.utils.mkdirhier(patchdir) for patchfile in newpatches: logger.info('Adding new patch %s' % patchfile) shutil.move(os.path.join(tempdir, patchfile), os.path.join(patchdir, patchfile)) srcuri.append('file://%s' % patchfile) updaterecipe = True if removepatches: if remove_patches(srcuri, removepatches): updaterecipe = True if updaterecipe: logger.info('Updating recipe %s' % os.path.basename(recipefile)) oe.recipeutils.patch_recipe(tinfoil.config_data, recipefile, {'SRC_URI': ' '.join(srcuri)}) elif not updatepatches: # Neither patches nor recipe were updated logger.info('No patches need updating') finally: shutil.rmtree(tempdir) else: logger.error('update_recipe: invalid mode %s' % mode) return 1 return 0
def update_recipe(args, config, basepath, workspace): if not args.recipename in workspace: logger.error("no recipe named %s in your workspace" % args.recipename) return -1 # Get initial revision from bbappend appends = glob.glob(os.path.join(config.workspace_path, "appends", "%s_*.bbappend" % args.recipename)) if not appends: logger.error("unable to find workspace bbappend for recipe %s" % args.recipename) return -1 tinfoil = setup_tinfoil() import bb from oe.patch import GitApplyTree import oe.recipeutils recipefile = _get_recipe_file(tinfoil.cooker, args.recipename) if not recipefile: # Error already logged return -1 rd = oe.recipeutils.parse_recipe(recipefile, tinfoil.config_data) 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 def remove_patches(srcuri, patchlist): # Remove any patches that we don't need updated = False for patch in patchlist: patchfile = os.path.basename(patch) for i in xrange(len(srcuri)): if srcuri[i].startswith("file://") and os.path.basename(srcuri[i]).split(";")[0] == patchfile: logger.info("Removing patch %s" % patchfile) srcuri.pop(i) # FIXME "git rm" here would be nice if the file in question is tracked # FIXME there's a chance that this file is referred to by another recipe, in which case deleting wouldn't be the right thing to do if patch.startswith(os.path.dirname(recipefile)): os.remove(patch) updated = True break return updated srctree = workspace[args.recipename] if mode == "srcrev": (stdout, _) = bb.process.run("git rev-parse HEAD", cwd=srctree) srcrev = stdout.strip() if len(srcrev) != 40: logger.error("Invalid hash returned by git: %s" % stdout) return 1 logger.info("Updating SRCREV in recipe %s" % os.path.basename(recipefile)) patchfields = {} patchfields["SRCREV"] = srcrev if not args.no_remove: # Find list of existing patches in recipe file existing_patches = oe.recipeutils.get_recipe_patches(rd) old_srcrev = rd.getVar("SRCREV", False) or "" tempdir = tempfile.mkdtemp(prefix="devtool") removepatches = [] try: GitApplyTree.extractPatches(srctree, old_srcrev, tempdir) newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: removepatches.append(patch) finally: shutil.rmtree(tempdir) if removepatches: srcuri = (rd.getVar("SRC_URI", False) or "").split() if remove_patches(srcuri, removepatches): patchfields["SRC_URI"] = " ".join(srcuri) oe.recipeutils.patch_recipe(rd, recipefile, patchfields) if not "git://" in orig_src_uri: logger.info( "You will need to update SRC_URI within the recipe to point to a git repository where you have pushed your changes" ) elif mode == "patch": commits = [] update_rev = None if args.initial_rev: initial_rev = args.initial_rev else: initial_rev = None with open(appends[0], "r") as f: for line in f: if line.startswith("# initial_rev:"): initial_rev = line.split(":")[-1].strip() elif line.startswith("# commit:"): commits.append(line.split(":")[-1].strip()) if initial_rev: # Find first actually changed revision (stdout, _) = bb.process.run("git rev-list --reverse %s..HEAD" % initial_rev, cwd=srctree) newcommits = stdout.split() for i in xrange(min(len(commits), len(newcommits))): if newcommits[i] == commits[i]: update_rev = commits[i] if not initial_rev: logger.error("Unable to find initial revision - please specify it with --initial-rev") return -1 if not update_rev: update_rev = initial_rev # Find list of existing patches in recipe file existing_patches = oe.recipeutils.get_recipe_patches(rd) removepatches = [] if not args.no_remove: # Get all patches from source tree and check if any should be removed tempdir = tempfile.mkdtemp(prefix="devtool") try: GitApplyTree.extractPatches(srctree, initial_rev, tempdir) newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile not in newpatches: removepatches.append(patch) finally: shutil.rmtree(tempdir) # Get updated patches from source tree tempdir = tempfile.mkdtemp(prefix="devtool") try: GitApplyTree.extractPatches(srctree, update_rev, tempdir) # Match up and replace existing patches with corresponding new patches updatepatches = False updaterecipe = False newpatches = os.listdir(tempdir) for patch in existing_patches: patchfile = os.path.basename(patch) if patchfile in newpatches: logger.info("Updating patch %s" % patchfile) shutil.move(os.path.join(tempdir, patchfile), patch) newpatches.remove(patchfile) updatepatches = True srcuri = (rd.getVar("SRC_URI", False) or "").split() if newpatches: # Add any patches left over patchdir = os.path.join(os.path.dirname(recipefile), rd.getVar("BPN", True)) bb.utils.mkdirhier(patchdir) for patchfile in newpatches: logger.info("Adding new patch %s" % patchfile) shutil.move(os.path.join(tempdir, patchfile), os.path.join(patchdir, patchfile)) srcuri.append("file://%s" % patchfile) updaterecipe = True if removepatches: if remove_patches(srcuri, removepatches): updaterecipe = True if updaterecipe: logger.info("Updating recipe %s" % os.path.basename(recipefile)) oe.recipeutils.patch_recipe(rd, recipefile, {"SRC_URI": " ".join(srcuri)}) elif not updatepatches: # Neither patches nor recipe were updated logger.info("No patches need updating") finally: shutil.rmtree(tempdir) else: logger.error("update_recipe: invalid mode %s" % mode) return 1 return 0