OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Compare the artifacts from two builds.""" | 6 """Compare the artifacts from two builds.""" |
7 | 7 |
8 import difflib | 8 import difflib |
9 import json | 9 import json |
10 import optparse | 10 import optparse |
11 import os | 11 import os |
| 12 import re |
| 13 import shutil |
12 import struct | 14 import struct |
| 15 import subprocess |
13 import sys | 16 import sys |
14 import time | 17 import time |
15 | 18 |
16 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | 19 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) |
17 | 20 |
18 | 21 |
19 # List of files that are known to be non deterministic. This is a "temporary" | 22 # List of files that are known to be non deterministic. This is a "temporary" |
20 # workaround to find regression on the deterministic builders. | 23 # workaround to find regression on the deterministic builders. |
21 # | 24 # |
22 # PNaCl general bug: https://crbug.com/429358 | 25 # PNaCl general bug: https://crbug.com/429358 |
(...skipping 465 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
488 # else, falls through binary comparison, it must be binary equal too. | 491 # else, falls through binary comparison, it must be binary equal too. |
489 | 492 |
490 file_len = os.stat(first_filepath).st_size | 493 file_len = os.stat(first_filepath).st_size |
491 if file_len != os.stat(second_filepath).st_size: | 494 if file_len != os.stat(second_filepath).st_size: |
492 return 'different size: %d != %d' % ( | 495 return 'different size: %d != %d' % ( |
493 file_len, os.stat(second_filepath).st_size) | 496 file_len, os.stat(second_filepath).st_size) |
494 | 497 |
495 return diff_binary(first_filepath, second_filepath, file_len) | 498 return diff_binary(first_filepath, second_filepath, file_len) |
496 | 499 |
497 | 500 |
| 501 def get_deps(build_dir, target): |
| 502 """Returns list of object files needed to build target.""" |
| 503 NODE_PATTERN = re.compile(r'label="([a-zA-Z0-9_\\/.-]+)"') |
| 504 CHECK_EXTS = ('.o', '.obj') |
| 505 |
| 506 # Rename to possibly original directory name if possible. |
| 507 fixed_build_dir = build_dir |
| 508 if build_dir.endswith('.1') or build_dir.endswith('.2'): |
| 509 fixed_build_dir = build_dir[:-2] |
| 510 if os.path.exists(fixed_build_dir): |
| 511 print >> sys.stderr, ('fixed_build_dir %s exists.' |
| 512 ' will try to use orig dir.' % fixed_build_dir) |
| 513 fixed_build_dir = build_dir |
| 514 else: |
| 515 shutil.move(build_dir, fixed_build_dir) |
| 516 |
| 517 try: |
| 518 out = subprocess.check_output(['ninja', '-C', fixed_build_dir, |
| 519 '-t', 'graph', target]) |
| 520 except subprocess.CalledProcessError as e: |
| 521 print >> sys.stderr, 'error to get graph for %s: %s' % (target, e) |
| 522 return [] |
| 523 |
| 524 finally: |
| 525 # Rename again if we renamed before. |
| 526 if fixed_build_dir != build_dir: |
| 527 shutil.move(fixed_build_dir, build_dir) |
| 528 |
| 529 files = [] |
| 530 for line in out.splitlines(): |
| 531 matched = NODE_PATTERN.search(line) |
| 532 if matched: |
| 533 path = matched.group(1) |
| 534 if not os.path.splitext(path)[1] in CHECK_EXTS: |
| 535 continue |
| 536 if os.path.isabs(path): |
| 537 print >> sys.stderr, ('not support abs path %s used for target %s' |
| 538 % (path, target)) |
| 539 continue |
| 540 files.append(path) |
| 541 return files |
| 542 |
| 543 |
| 544 def compare_deps(first_dir, second_dir, targets): |
| 545 """Print difference of dependent files.""" |
| 546 for target in targets: |
| 547 print 'Checking %s difference:' % target |
| 548 first_deps = get_deps(first_dir, target) |
| 549 second_deps =get_deps(second_dir, target) |
| 550 if set(first_deps) != set(second_deps): |
| 551 # Since we do not thiks this case occur, we do not do anything special |
| 552 # for this case. |
| 553 print 'deps on %s are different: %s' % ( |
| 554 target, set(first_deps).symmetric_difference(set(second_deps))) |
| 555 continue |
| 556 for d in first_deps: |
| 557 first_file = os.path.join(first_dir, d) |
| 558 second_file = os.path.join(second_dir, d) |
| 559 compare_files(first_file, second_file) |
| 560 |
| 561 |
498 def compare_build_artifacts(first_dir, second_dir, target_platform, | 562 def compare_build_artifacts(first_dir, second_dir, target_platform, |
499 recursive=False): | 563 recursive=False): |
500 """Compares the artifacts from two distinct builds.""" | 564 """Compares the artifacts from two distinct builds.""" |
501 if not os.path.isdir(first_dir): | 565 if not os.path.isdir(first_dir): |
502 print >> sys.stderr, '%s isn\'t a valid directory.' % first_dir | 566 print >> sys.stderr, '%s isn\'t a valid directory.' % first_dir |
503 return 1 | 567 return 1 |
504 if not os.path.isdir(second_dir): | 568 if not os.path.isdir(second_dir): |
505 print >> sys.stderr, '%s isn\'t a valid directory.' % second_dir | 569 print >> sys.stderr, '%s isn\'t a valid directory.' % second_dir |
506 return 1 | 570 return 1 |
507 | 571 |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
554 print('Unexpected diffs: %d' % len(unexpected_diffs)) | 618 print('Unexpected diffs: %d' % len(unexpected_diffs)) |
555 if unexpected_diffs: | 619 if unexpected_diffs: |
556 print('Unexpected files with diffs:\n') | 620 print('Unexpected files with diffs:\n') |
557 for u in unexpected_diffs: | 621 for u in unexpected_diffs: |
558 print(' %s' % u) | 622 print(' %s' % u) |
559 if unexpected_equals: | 623 if unexpected_equals: |
560 print('Unexpected files with no diffs:\n') | 624 print('Unexpected files with no diffs:\n') |
561 for u in unexpected_equals: | 625 for u in unexpected_equals: |
562 print(' %s' % u) | 626 print(' %s' % u) |
563 | 627 |
| 628 if unexpected_diffs: |
| 629 diffs_to_investigate = set(unexpected_diffs).difference( |
| 630 set(missing_files)) |
| 631 compare_deps(first_dir, second_dir, diffs_to_investigate) |
| 632 |
564 return int(bool(unexpected_diffs)) | 633 return int(bool(unexpected_diffs)) |
565 | 634 |
566 | 635 |
567 def main(): | 636 def main(): |
568 parser = optparse.OptionParser(usage='%prog [options]') | 637 parser = optparse.OptionParser(usage='%prog [options]') |
569 parser.add_option( | 638 parser.add_option( |
570 '-f', '--first-build-dir', help='The first build directory.') | 639 '-f', '--first-build-dir', help='The first build directory.') |
571 parser.add_option( | 640 parser.add_option( |
572 '-s', '--second-build-dir', help='The second build directory.') | 641 '-s', '--second-build-dir', help='The second build directory.') |
573 parser.add_option('-r', '--recursive', action='store_true', default=False, | 642 parser.add_option('-r', '--recursive', action='store_true', default=False, |
(...skipping 13 matching lines...) Expand all Loading... |
587 parser.error('--target-platform is required') | 656 parser.error('--target-platform is required') |
588 | 657 |
589 return compare_build_artifacts(os.path.abspath(options.first_build_dir), | 658 return compare_build_artifacts(os.path.abspath(options.first_build_dir), |
590 os.path.abspath(options.second_build_dir), | 659 os.path.abspath(options.second_build_dir), |
591 options.target_platform, | 660 options.target_platform, |
592 options.recursive) | 661 options.recursive) |
593 | 662 |
594 | 663 |
595 if __name__ == '__main__': | 664 if __name__ == '__main__': |
596 sys.exit(main()) | 665 sys.exit(main()) |
OLD | NEW |