Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
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

    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
Ejemplo n.º 4
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
Ejemplo n.º 5
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
Ejemplo n.º 6
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
Ejemplo n.º 7
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)
Ejemplo n.º 8
0
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)
Ejemplo n.º 9
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)
Ejemplo n.º 10
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

    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
Ejemplo n.º 11
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