diff options
author | Paul Eggleton <paul.eggleton@linux.intel.com> | 2015-05-18 16:15:08 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2015-05-20 21:41:04 +0100 |
commit | 7539c1f8892e5f7467e3fe8cf8cf7a5a23033e4d (patch) | |
tree | 02fa87af1b254a594c339871d54eaf665417e82f /scripts/lib | |
parent | fbfc06a969200e582a059c9943e6fd17aca70e30 (diff) | |
download | poky-7539c1f8892e5f7467e3fe8cf8cf7a5a23033e4d.tar.gz |
devtool: update-recipe: add option to write changes to bbappend
Quite often what you want to do having made customisations to a piece of
software is to apply those customisations in your own layer rather than
in the original recipe. Thus, add a -a/--append option to the
update-recipe subcommand which allows you to specify the layer to write
a bbappend into. The bbappend will be created at the appropriate path
within the specified layer directory (which may or may not be in your
bblayers.conf) or if one already exists it will be updated
appropriately.
(This re-uses code written for recipetool appendfile.)
Implements [YOCTO #7587].
(From OE-Core rev: 87d487ea4fdfb6cd30e3b3fad47732db12e86f23)
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts/lib')
-rw-r--r-- | scripts/lib/devtool/standard.py | 148 |
1 files changed, 107 insertions, 41 deletions
diff --git a/scripts/lib/devtool/standard.py b/scripts/lib/devtool/standard.py index 122121aedb..c5b32d81db 100644 --- a/scripts/lib/devtool/standard.py +++ b/scripts/lib/devtool/standard.py | |||
@@ -24,6 +24,7 @@ import tempfile | |||
24 | import logging | 24 | import logging |
25 | import argparse | 25 | import argparse |
26 | import scriptutils | 26 | import scriptutils |
27 | import errno | ||
27 | from devtool import exec_build_env_command, setup_tinfoil | 28 | from devtool import exec_build_env_command, setup_tinfoil |
28 | 29 | ||
29 | logger = logging.getLogger('devtool') | 30 | logger = logging.getLogger('devtool') |
@@ -510,6 +511,14 @@ def update_recipe(args, config, basepath, workspace): | |||
510 | logger.error("no recipe named %s in your workspace" % args.recipename) | 511 | logger.error("no recipe named %s in your workspace" % args.recipename) |
511 | return -1 | 512 | return -1 |
512 | 513 | ||
514 | if args.append: | ||
515 | if not os.path.exists(args.append): | ||
516 | logger.error('bbappend destination layer directory "%s" does not exist' % args.append) | ||
517 | return 2 | ||
518 | if not os.path.exists(os.path.join(args.append, 'conf', 'layer.conf')): | ||
519 | logger.error('conf/layer.conf not found in bbappend destination layer "%s"' % args.append) | ||
520 | return 2 | ||
521 | |||
513 | tinfoil = setup_tinfoil() | 522 | tinfoil = setup_tinfoil() |
514 | import bb | 523 | import bb |
515 | from oe.patch import GitApplyTree | 524 | from oe.patch import GitApplyTree |
@@ -535,22 +544,19 @@ def update_recipe(args, config, basepath, workspace): | |||
535 | else: | 544 | else: |
536 | mode = args.mode | 545 | mode = args.mode |
537 | 546 | ||
538 | def remove_patches(srcuri, patchlist): | 547 | def remove_patch_entries(srcuri, patchlist): |
539 | """Remove patches""" | 548 | """Remove patch entries from SRC_URI""" |
540 | updated = False | 549 | remaining = patchlist[:] |
550 | entries = [] | ||
541 | for patch in patchlist: | 551 | for patch in patchlist: |
542 | patchfile = os.path.basename(patch) | 552 | patchfile = os.path.basename(patch) |
543 | for i in xrange(len(srcuri)): | 553 | for i in xrange(len(srcuri)): |
544 | if srcuri[i].startswith('file://') and os.path.basename(srcuri[i]).split(';')[0] == patchfile: | 554 | if srcuri[i].startswith('file://') and os.path.basename(srcuri[i].split(';')[0]) == patchfile: |
545 | logger.info('Removing patch %s' % patchfile) | 555 | entries.append(srcuri[i]) |
556 | remaining.remove(patch) | ||
546 | srcuri.pop(i) | 557 | srcuri.pop(i) |
547 | # FIXME "git rm" here would be nice if the file in question is tracked | ||
548 | # 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 | ||
549 | if patch.startswith(os.path.dirname(recipefile)): | ||
550 | os.remove(patch) | ||
551 | updated = True | ||
552 | break | 558 | break |
553 | return updated | 559 | return entries, remaining |
554 | 560 | ||
555 | srctree = workspace[args.recipename] | 561 | srctree = workspace[args.recipename] |
556 | 562 | ||
@@ -565,8 +571,11 @@ def update_recipe(args, config, basepath, workspace): | |||
565 | logger.error('Invalid hash returned by git: %s' % stdout) | 571 | logger.error('Invalid hash returned by git: %s' % stdout) |
566 | return 1 | 572 | return 1 |
567 | 573 | ||
574 | removepatches = [] | ||
575 | destpath = None | ||
568 | if mode == 'srcrev': | 576 | if mode == 'srcrev': |
569 | logger.info('Updating SRCREV in recipe %s' % os.path.basename(recipefile)) | 577 | logger.info('Updating SRCREV in recipe %s' % os.path.basename(recipefile)) |
578 | removevalues = None | ||
570 | patchfields = {} | 579 | patchfields = {} |
571 | patchfields['SRCREV'] = srcrev | 580 | patchfields['SRCREV'] = srcrev |
572 | if not args.no_remove: | 581 | if not args.no_remove: |
@@ -575,7 +584,6 @@ def update_recipe(args, config, basepath, workspace): | |||
575 | 584 | ||
576 | old_srcrev = (rd.getVar('SRCREV', False) or '') | 585 | old_srcrev = (rd.getVar('SRCREV', False) or '') |
577 | tempdir = tempfile.mkdtemp(prefix='devtool') | 586 | tempdir = tempfile.mkdtemp(prefix='devtool') |
578 | removepatches = [] | ||
579 | try: | 587 | try: |
580 | GitApplyTree.extractPatches(srctree, old_srcrev, tempdir) | 588 | GitApplyTree.extractPatches(srctree, old_srcrev, tempdir) |
581 | newpatches = os.listdir(tempdir) | 589 | newpatches = os.listdir(tempdir) |
@@ -587,10 +595,14 @@ def update_recipe(args, config, basepath, workspace): | |||
587 | shutil.rmtree(tempdir) | 595 | shutil.rmtree(tempdir) |
588 | if removepatches: | 596 | if removepatches: |
589 | srcuri = (rd.getVar('SRC_URI', False) or '').split() | 597 | srcuri = (rd.getVar('SRC_URI', False) or '').split() |
590 | if remove_patches(srcuri, removepatches): | 598 | removedentries, _ = remove_patch_entries(srcuri, removepatches) |
599 | if removedentries: | ||
591 | patchfields['SRC_URI'] = ' '.join(srcuri) | 600 | patchfields['SRC_URI'] = ' '.join(srcuri) |
592 | 601 | ||
593 | oe.recipeutils.patch_recipe(tinfoil.config_data, recipefile, patchfields) | 602 | if args.append: |
603 | (appendfile, destpath) = oe.recipeutils.bbappend_recipe(rd, args.append, None, wildcardver=args.wildcard_version, extralines=patchfields) | ||
604 | else: | ||
605 | oe.recipeutils.patch_recipe(tinfoil.config_data, recipefile, patchfields) | ||
594 | 606 | ||
595 | if not 'git://' in orig_src_uri: | 607 | if not 'git://' in orig_src_uri: |
596 | logger.info('You will need to update SRC_URI within the recipe to point to a git repository where you have pushed your changes') | 608 | logger.info('You will need to update SRC_URI within the recipe to point to a git repository where you have pushed your changes') |
@@ -628,6 +640,7 @@ def update_recipe(args, config, basepath, workspace): | |||
628 | existing_patches = oe.recipeutils.get_recipe_patches(rd) | 640 | existing_patches = oe.recipeutils.get_recipe_patches(rd) |
629 | 641 | ||
630 | removepatches = [] | 642 | removepatches = [] |
643 | seqpatch_re = re.compile('^[0-9]{4}-') | ||
631 | if not args.no_remove: | 644 | if not args.no_remove: |
632 | # Get all patches from source tree and check if any should be removed | 645 | # Get all patches from source tree and check if any should be removed |
633 | tempdir = tempfile.mkdtemp(prefix='devtool') | 646 | tempdir = tempfile.mkdtemp(prefix='devtool') |
@@ -635,8 +648,18 @@ def update_recipe(args, config, basepath, workspace): | |||
635 | GitApplyTree.extractPatches(srctree, initial_rev, tempdir) | 648 | GitApplyTree.extractPatches(srctree, initial_rev, tempdir) |
636 | newpatches = os.listdir(tempdir) | 649 | newpatches = os.listdir(tempdir) |
637 | for patch in existing_patches: | 650 | for patch in existing_patches: |
651 | # If it's a git sequence named patch, the numbers might not match up | ||
652 | # since we are starting from a different revision | ||
653 | # This does assume that people are using unique shortlog values, but | ||
654 | # they ought to be anyway... | ||
638 | patchfile = os.path.basename(patch) | 655 | patchfile = os.path.basename(patch) |
639 | if patchfile not in newpatches: | 656 | if seqpatch_re.search(patchfile): |
657 | for newpatch in newpatches: | ||
658 | if seqpatch_re.search(newpatch) and patchfile[5:] == newpatch[5:]: | ||
659 | break | ||
660 | else: | ||
661 | removepatches.append(patch) | ||
662 | elif patchfile not in newpatches: | ||
640 | removepatches.append(patch) | 663 | removepatches.append(patch) |
641 | finally: | 664 | finally: |
642 | shutil.rmtree(tempdir) | 665 | shutil.rmtree(tempdir) |
@@ -650,33 +673,56 @@ def update_recipe(args, config, basepath, workspace): | |||
650 | updatepatches = False | 673 | updatepatches = False |
651 | updaterecipe = False | 674 | updaterecipe = False |
652 | newpatches = os.listdir(tempdir) | 675 | newpatches = os.listdir(tempdir) |
653 | for patch in existing_patches: | 676 | if args.append: |
654 | patchfile = os.path.basename(patch) | 677 | patchfiles = {} |
655 | if patchfile in newpatches: | 678 | for patch in existing_patches: |
656 | logger.info('Updating patch %s' % patchfile) | 679 | patchfile = os.path.basename(patch) |
657 | shutil.move(os.path.join(tempdir, patchfile), patch) | 680 | if patchfile in newpatches: |
658 | newpatches.remove(patchfile) | 681 | patchfiles[os.path.join(tempdir, patchfile)] = patchfile |
659 | updatepatches = True | 682 | newpatches.remove(patchfile) |
660 | srcuri = (rd.getVar('SRC_URI', False) or '').split() | ||
661 | if newpatches: | ||
662 | # Add any patches left over | ||
663 | patchdir = os.path.join(os.path.dirname(recipefile), rd.getVar('BPN', True)) | ||
664 | bb.utils.mkdirhier(patchdir) | ||
665 | for patchfile in newpatches: | 683 | for patchfile in newpatches: |
666 | logger.info('Adding new patch %s' % patchfile) | 684 | patchfiles[os.path.join(tempdir, patchfile)] = None |
667 | shutil.move(os.path.join(tempdir, patchfile), os.path.join(patchdir, patchfile)) | 685 | |
668 | srcuri.append('file://%s' % patchfile) | 686 | if patchfiles or removepatches: |
669 | updaterecipe = True | 687 | removevalues = None |
670 | if removepatches: | 688 | if removepatches: |
671 | if remove_patches(srcuri, removepatches): | 689 | srcuri = (rd.getVar('SRC_URI', False) or '').split() |
672 | updaterecipe = True | 690 | removedentries, remaining = remove_patch_entries(srcuri, removepatches) |
673 | if updaterecipe: | 691 | if removedentries or remaining: |
674 | logger.info('Updating recipe %s' % os.path.basename(recipefile)) | 692 | removevalues = {'SRC_URI': removedentries + ['file://' + os.path.basename(item) for item in remaining]} |
675 | oe.recipeutils.patch_recipe(tinfoil.config_data, | 693 | (appendfile, destpath) = oe.recipeutils.bbappend_recipe(rd, args.append, patchfiles, removevalues=removevalues) |
676 | recipefile, {'SRC_URI': ' '.join(srcuri)}) | 694 | else: |
677 | elif not updatepatches: | 695 | logger.info('No patches needed updating') |
678 | # Neither patches nor recipe were updated | 696 | else: |
679 | logger.info('No patches need updating') | 697 | for patch in existing_patches: |
698 | patchfile = os.path.basename(patch) | ||
699 | if patchfile in newpatches: | ||
700 | logger.info('Updating patch %s' % patchfile) | ||
701 | shutil.move(os.path.join(tempdir, patchfile), patch) | ||
702 | newpatches.remove(patchfile) | ||
703 | updatepatches = True | ||
704 | srcuri = (rd.getVar('SRC_URI', False) or '').split() | ||
705 | if newpatches: | ||
706 | # Add any patches left over | ||
707 | patchdir = os.path.join(os.path.dirname(recipefile), rd.getVar('BPN', True)) | ||
708 | bb.utils.mkdirhier(patchdir) | ||
709 | for patchfile in newpatches: | ||
710 | logger.info('Adding new patch %s' % patchfile) | ||
711 | shutil.move(os.path.join(tempdir, patchfile), os.path.join(patchdir, patchfile)) | ||
712 | srcuri.append('file://%s' % patchfile) | ||
713 | updaterecipe = True | ||
714 | if removepatches: | ||
715 | removedentries, _ = remove_patch_entries(srcuri, removepatches) | ||
716 | if removedentries: | ||
717 | updaterecipe = True | ||
718 | if updaterecipe: | ||
719 | logger.info('Updating recipe %s' % os.path.basename(recipefile)) | ||
720 | oe.recipeutils.patch_recipe(tinfoil.config_data, | ||
721 | recipefile, {'SRC_URI': ' '.join(srcuri)}) | ||
722 | elif not updatepatches: | ||
723 | # Neither patches nor recipe were updated | ||
724 | logger.info('No patches need updating') | ||
725 | |||
680 | finally: | 726 | finally: |
681 | shutil.rmtree(tempdir) | 727 | shutil.rmtree(tempdir) |
682 | 728 | ||
@@ -684,6 +730,24 @@ def update_recipe(args, config, basepath, workspace): | |||
684 | logger.error('update_recipe: invalid mode %s' % mode) | 730 | logger.error('update_recipe: invalid mode %s' % mode) |
685 | return 1 | 731 | return 1 |
686 | 732 | ||
733 | if removepatches: | ||
734 | for patchfile in removepatches: | ||
735 | if args.append: | ||
736 | if not destpath: | ||
737 | raise Exception('destpath should be set here') | ||
738 | patchfile = os.path.join(destpath, os.path.basename(patchfile)) | ||
739 | |||
740 | if os.path.exists(patchfile): | ||
741 | logger.info('Removing patch %s' % patchfile) | ||
742 | # FIXME "git rm" here would be nice if the file in question is tracked | ||
743 | # 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 | ||
744 | os.remove(patchfile) | ||
745 | # Remove directory if empty | ||
746 | try: | ||
747 | os.rmdir(os.path.dirname(patchfile)) | ||
748 | except OSError as ose: | ||
749 | if ose.errno != errno.ENOTEMPTY: | ||
750 | raise | ||
687 | return 0 | 751 | return 0 |
688 | 752 | ||
689 | 753 | ||
@@ -797,6 +861,8 @@ def register_commands(subparsers, context): | |||
797 | parser_update_recipe.add_argument('recipename', help='Name of recipe to update') | 861 | parser_update_recipe.add_argument('recipename', help='Name of recipe to update') |
798 | parser_update_recipe.add_argument('--mode', '-m', choices=['patch', 'srcrev', 'auto'], default='auto', help='Update mode (where %(metavar)s is %(choices)s; default is %(default)s)', metavar='MODE') | 862 | parser_update_recipe.add_argument('--mode', '-m', choices=['patch', 'srcrev', 'auto'], default='auto', help='Update mode (where %(metavar)s is %(choices)s; default is %(default)s)', metavar='MODE') |
799 | parser_update_recipe.add_argument('--initial-rev', help='Starting revision for patches') | 863 | parser_update_recipe.add_argument('--initial-rev', help='Starting revision for patches') |
864 | parser_update_recipe.add_argument('--append', '-a', help='Write changes to a bbappend in the specified layer instead of the recipe', metavar='LAYERDIR') | ||
865 | parser_update_recipe.add_argument('--wildcard-version', '-w', help='In conjunction with -a/--append, use a wildcard to make the bbappend apply to any recipe version', action='store_true') | ||
800 | parser_update_recipe.add_argument('--no-remove', '-n', action="store_true", help='Don\'t remove patches, only add or update') | 866 | parser_update_recipe.add_argument('--no-remove', '-n', action="store_true", help='Don\'t remove patches, only add or update') |
801 | parser_update_recipe.set_defaults(func=update_recipe) | 867 | parser_update_recipe.set_defaults(func=update_recipe) |
802 | 868 | ||