diff options
Diffstat (limited to 'scripts/lib')
| -rw-r--r-- | scripts/lib/devtool/standard.py | 230 |
1 files changed, 144 insertions, 86 deletions
diff --git a/scripts/lib/devtool/standard.py b/scripts/lib/devtool/standard.py index 9b5a0855b2..3a8c66c131 100644 --- a/scripts/lib/devtool/standard.py +++ b/scripts/lib/devtool/standard.py | |||
| @@ -359,104 +359,162 @@ def update_recipe(args, config, basepath, workspace): | |||
| 359 | from oe.patch import GitApplyTree | 359 | from oe.patch import GitApplyTree |
| 360 | import oe.recipeutils | 360 | import oe.recipeutils |
| 361 | 361 | ||
| 362 | srctree = workspace[args.recipename] | ||
| 363 | commits = [] | ||
| 364 | update_rev = None | ||
| 365 | if args.initial_rev: | ||
| 366 | initial_rev = args.initial_rev | ||
| 367 | else: | ||
| 368 | initial_rev = None | ||
| 369 | with open(appends[0], 'r') as f: | ||
| 370 | for line in f: | ||
| 371 | if line.startswith('# initial_rev:'): | ||
| 372 | initial_rev = line.split(':')[-1].strip() | ||
| 373 | elif line.startswith('# commit:'): | ||
| 374 | commits.append(line.split(':')[-1].strip()) | ||
| 375 | |||
| 376 | if initial_rev: | ||
| 377 | # Find first actually changed revision | ||
| 378 | (stdout, _) = bb.process.run('git rev-list --reverse %s..HEAD' % initial_rev, cwd=srctree) | ||
| 379 | newcommits = stdout.split() | ||
| 380 | for i in xrange(min(len(commits), len(newcommits))): | ||
| 381 | if newcommits[i] == commits[i]: | ||
| 382 | update_rev = commits[i] | ||
| 383 | |||
| 384 | if not initial_rev: | ||
| 385 | logger.error('Unable to find initial revision - please specify it with --initial-rev') | ||
| 386 | return -1 | ||
| 387 | |||
| 388 | if not update_rev: | ||
| 389 | update_rev = initial_rev | ||
| 390 | |||
| 391 | # Find list of existing patches in recipe file | ||
| 392 | recipefile = _get_recipe_file(tinfoil.cooker, args.recipename) | 362 | recipefile = _get_recipe_file(tinfoil.cooker, args.recipename) |
| 393 | if not recipefile: | 363 | if not recipefile: |
| 394 | # Error already logged | 364 | # Error already logged |
| 395 | return -1 | 365 | return -1 |
| 396 | rd = oe.recipeutils.parse_recipe(recipefile, tinfoil.config_data) | 366 | rd = oe.recipeutils.parse_recipe(recipefile, tinfoil.config_data) |
| 397 | existing_patches = oe.recipeutils.get_recipe_patches(rd) | ||
| 398 | 367 | ||
| 399 | removepatches = [] | 368 | orig_src_uri = rd.getVar('SRC_URI', False) or '' |
| 400 | if not args.no_remove: | 369 | if args.mode == 'auto': |
| 401 | # Get all patches from source tree and check if any should be removed | 370 | if 'git://' in orig_src_uri: |
| 371 | mode = 'srcrev' | ||
| 372 | else: | ||
| 373 | mode = 'patch' | ||
| 374 | else: | ||
| 375 | mode = args.mode | ||
| 376 | |||
| 377 | def remove_patches(srcuri, patchlist): | ||
| 378 | # Remove any patches that we don't need | ||
| 379 | updated = False | ||
| 380 | for patch in patchlist: | ||
| 381 | patchfile = os.path.basename(patch) | ||
| 382 | for i in xrange(len(srcuri)): | ||
| 383 | if srcuri[i].startswith('file://') and os.path.basename(srcuri[i]).split(';')[0] == patchfile: | ||
| 384 | logger.info('Removing patch %s' % patchfile) | ||
| 385 | srcuri.pop(i) | ||
| 386 | # FIXME "git rm" here would be nice if the file in question is tracked | ||
| 387 | # 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 | ||
| 388 | if patch.startswith(os.path.dirname(recipefile)): | ||
| 389 | os.remove(patch) | ||
| 390 | updated = True | ||
| 391 | break | ||
| 392 | return updated | ||
| 393 | |||
| 394 | srctree = workspace[args.recipename] | ||
| 395 | if mode == 'srcrev': | ||
| 396 | (stdout, _) = bb.process.run('git rev-parse HEAD', cwd=srctree) | ||
| 397 | srcrev = stdout.strip() | ||
| 398 | if len(srcrev) != 40: | ||
| 399 | logger.error('Invalid hash returned by git: %s' % stdout) | ||
| 400 | return 1 | ||
| 401 | |||
| 402 | logger.info('Updating SRCREV in recipe %s' % os.path.basename(recipefile)) | ||
| 403 | patchfields = {} | ||
| 404 | patchfields['SRCREV'] = srcrev | ||
| 405 | if not args.no_remove: | ||
| 406 | # Find list of existing patches in recipe file | ||
| 407 | existing_patches = oe.recipeutils.get_recipe_patches(rd) | ||
| 408 | |||
| 409 | old_srcrev = (rd.getVar('SRCREV', False) or '') | ||
| 410 | tempdir = tempfile.mkdtemp(prefix='devtool') | ||
| 411 | removepatches = [] | ||
| 412 | try: | ||
| 413 | GitApplyTree.extractPatches(srctree, old_srcrev, tempdir) | ||
| 414 | newpatches = os.listdir(tempdir) | ||
| 415 | for patch in existing_patches: | ||
| 416 | patchfile = os.path.basename(patch) | ||
| 417 | if patchfile in newpatches: | ||
| 418 | removepatches.append(patch) | ||
| 419 | finally: | ||
| 420 | shutil.rmtree(tempdir) | ||
| 421 | if removepatches: | ||
| 422 | srcuri = (rd.getVar('SRC_URI', False) or '').split() | ||
| 423 | if remove_patches(srcuri, removepatches): | ||
| 424 | patchfields['SRC_URI'] = ' '.join(srcuri) | ||
| 425 | |||
| 426 | oe.recipeutils.patch_recipe(rd, recipefile, patchfields) | ||
| 427 | |||
| 428 | if not 'git://' in orig_src_uri: | ||
| 429 | logger.info('You will need to update SRC_URI within the recipe to point to a git repository where you have pushed your changes') | ||
| 430 | |||
| 431 | elif mode == 'patch': | ||
| 432 | commits = [] | ||
| 433 | update_rev = None | ||
| 434 | if args.initial_rev: | ||
| 435 | initial_rev = args.initial_rev | ||
| 436 | else: | ||
| 437 | initial_rev = None | ||
| 438 | with open(appends[0], 'r') as f: | ||
| 439 | for line in f: | ||
| 440 | if line.startswith('# initial_rev:'): | ||
| 441 | initial_rev = line.split(':')[-1].strip() | ||
| 442 | elif line.startswith('# commit:'): | ||
| 443 | commits.append(line.split(':')[-1].strip()) | ||
| 444 | |||
| 445 | if initial_rev: | ||
| 446 | # Find first actually changed revision | ||
| 447 | (stdout, _) = bb.process.run('git rev-list --reverse %s..HEAD' % initial_rev, cwd=srctree) | ||
| 448 | newcommits = stdout.split() | ||
| 449 | for i in xrange(min(len(commits), len(newcommits))): | ||
| 450 | if newcommits[i] == commits[i]: | ||
| 451 | update_rev = commits[i] | ||
| 452 | |||
| 453 | if not initial_rev: | ||
| 454 | logger.error('Unable to find initial revision - please specify it with --initial-rev') | ||
| 455 | return -1 | ||
| 456 | |||
| 457 | if not update_rev: | ||
| 458 | update_rev = initial_rev | ||
| 459 | |||
| 460 | # Find list of existing patches in recipe file | ||
| 461 | existing_patches = oe.recipeutils.get_recipe_patches(rd) | ||
| 462 | |||
| 463 | removepatches = [] | ||
| 464 | if not args.no_remove: | ||
| 465 | # Get all patches from source tree and check if any should be removed | ||
| 466 | tempdir = tempfile.mkdtemp(prefix='devtool') | ||
| 467 | try: | ||
| 468 | GitApplyTree.extractPatches(srctree, initial_rev, tempdir) | ||
| 469 | newpatches = os.listdir(tempdir) | ||
| 470 | for patch in existing_patches: | ||
| 471 | patchfile = os.path.basename(patch) | ||
| 472 | if patchfile not in newpatches: | ||
| 473 | removepatches.append(patch) | ||
| 474 | finally: | ||
| 475 | shutil.rmtree(tempdir) | ||
| 476 | |||
| 477 | # Get updated patches from source tree | ||
| 402 | tempdir = tempfile.mkdtemp(prefix='devtool') | 478 | tempdir = tempfile.mkdtemp(prefix='devtool') |
| 403 | try: | 479 | try: |
| 404 | GitApplyTree.extractPatches(srctree, initial_rev, tempdir) | 480 | GitApplyTree.extractPatches(srctree, update_rev, tempdir) |
| 481 | |||
| 482 | # Match up and replace existing patches with corresponding new patches | ||
| 483 | updatepatches = False | ||
| 484 | updaterecipe = False | ||
| 405 | newpatches = os.listdir(tempdir) | 485 | newpatches = os.listdir(tempdir) |
| 406 | for patch in existing_patches: | 486 | for patch in existing_patches: |
| 407 | patchfile = os.path.basename(patch) | 487 | patchfile = os.path.basename(patch) |
| 408 | if patchfile not in newpatches: | 488 | if patchfile in newpatches: |
| 409 | removepatches.append(patch) | 489 | logger.info('Updating patch %s' % patchfile) |
| 490 | shutil.move(os.path.join(tempdir, patchfile), patch) | ||
| 491 | newpatches.remove(patchfile) | ||
| 492 | updatepatches = True | ||
| 493 | srcuri = (rd.getVar('SRC_URI', False) or '').split() | ||
| 494 | if newpatches: | ||
| 495 | # Add any patches left over | ||
| 496 | patchdir = os.path.join(os.path.dirname(recipefile), rd.getVar('BPN', True)) | ||
| 497 | bb.utils.mkdirhier(patchdir) | ||
| 498 | for patchfile in newpatches: | ||
| 499 | logger.info('Adding new patch %s' % patchfile) | ||
| 500 | shutil.move(os.path.join(tempdir, patchfile), os.path.join(patchdir, patchfile)) | ||
| 501 | srcuri.append('file://%s' % patchfile) | ||
| 502 | updaterecipe = True | ||
| 503 | if removepatches: | ||
| 504 | if remove_patches(srcuri, removepatches): | ||
| 505 | updaterecipe = True | ||
| 506 | if updaterecipe: | ||
| 507 | logger.info('Updating recipe %s' % os.path.basename(recipefile)) | ||
| 508 | oe.recipeutils.patch_recipe(rd, recipefile, {'SRC_URI': ' '.join(srcuri)}) | ||
| 509 | elif not updatepatches: | ||
| 510 | # Neither patches nor recipe were updated | ||
| 511 | logger.info('No patches need updating') | ||
| 410 | finally: | 512 | finally: |
| 411 | shutil.rmtree(tempdir) | 513 | shutil.rmtree(tempdir) |
| 412 | 514 | ||
| 413 | # Get updated patches from source tree | 515 | else: |
| 414 | tempdir = tempfile.mkdtemp(prefix='devtool') | 516 | logger.error('update_recipe: invalid mode %s' % mode) |
| 415 | try: | 517 | return 1 |
| 416 | GitApplyTree.extractPatches(srctree, update_rev, tempdir) | ||
| 417 | |||
| 418 | # Match up and replace existing patches with corresponding new patches | ||
| 419 | updatepatches = False | ||
| 420 | updaterecipe = False | ||
| 421 | newpatches = os.listdir(tempdir) | ||
| 422 | for patch in existing_patches: | ||
| 423 | patchfile = os.path.basename(patch) | ||
| 424 | if patchfile in newpatches: | ||
| 425 | logger.info('Updating patch %s' % patchfile) | ||
| 426 | shutil.move(os.path.join(tempdir, patchfile), patch) | ||
| 427 | newpatches.remove(patchfile) | ||
| 428 | updatepatches = True | ||
| 429 | srcuri = (rd.getVar('SRC_URI', False) or '').split() | ||
| 430 | if newpatches: | ||
| 431 | # Add any patches left over | ||
| 432 | patchdir = os.path.join(os.path.dirname(recipefile), rd.getVar('BPN', True)) | ||
| 433 | bb.utils.mkdirhier(patchdir) | ||
| 434 | for patchfile in newpatches: | ||
| 435 | logger.info('Adding new patch %s' % patchfile) | ||
| 436 | shutil.move(os.path.join(tempdir, patchfile), os.path.join(patchdir, patchfile)) | ||
| 437 | srcuri.append('file://%s' % patchfile) | ||
| 438 | updaterecipe = True | ||
| 439 | if removepatches: | ||
| 440 | # Remove any patches that we don't need | ||
| 441 | for patch in removepatches: | ||
| 442 | patchfile = os.path.basename(patch) | ||
| 443 | for i in xrange(len(srcuri)): | ||
| 444 | if srcuri[i].startswith('file://') and os.path.basename(srcuri[i]).split(';')[0] == patchfile: | ||
| 445 | logger.info('Removing patch %s' % patchfile) | ||
| 446 | srcuri.pop(i) | ||
| 447 | # FIXME "git rm" here would be nice if the file in question is tracked | ||
| 448 | # 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 | ||
| 449 | os.remove(patch) | ||
| 450 | updaterecipe = True | ||
| 451 | break | ||
| 452 | if updaterecipe: | ||
| 453 | logger.info('Updating recipe %s' % os.path.basename(recipefile)) | ||
| 454 | oe.recipeutils.patch_recipe(rd, recipefile, {'SRC_URI': ' '.join(srcuri)}) | ||
| 455 | elif not updatepatches: | ||
| 456 | # Neither patches nor recipe were updated | ||
| 457 | logger.info('No patches need updating') | ||
| 458 | finally: | ||
| 459 | shutil.rmtree(tempdir) | ||
| 460 | 518 | ||
| 461 | return 0 | 519 | return 0 |
| 462 | 520 | ||
| @@ -539,9 +597,9 @@ def register_commands(subparsers, context): | |||
| 539 | parser_add.set_defaults(func=extract) | 597 | parser_add.set_defaults(func=extract) |
| 540 | 598 | ||
| 541 | parser_add = subparsers.add_parser('update-recipe', help='Apply changes from external source tree to recipe', | 599 | parser_add = subparsers.add_parser('update-recipe', help='Apply changes from external source tree to recipe', |
| 542 | description='Applies changes from external source tree to a recipe (updating/adding/removing patches as necessary)', | 600 | description='Applies changes from external source tree to a recipe (updating/adding/removing patches as necessary, or by updating SRCREV)') |
| 543 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) | ||
| 544 | parser_add.add_argument('recipename', help='Name of recipe to update') | 601 | parser_add.add_argument('recipename', help='Name of recipe to update') |
| 602 | parser_add.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') | ||
| 545 | parser_add.add_argument('--initial-rev', help='Starting revision for patches') | 603 | parser_add.add_argument('--initial-rev', help='Starting revision for patches') |
| 546 | parser_add.add_argument('--no-remove', '-n', action="store_true", help='Don\'t remove patches, only add or update') | 604 | parser_add.add_argument('--no-remove', '-n', action="store_true", help='Don\'t remove patches, only add or update') |
| 547 | parser_add.set_defaults(func=update_recipe) | 605 | parser_add.set_defaults(func=update_recipe) |
