| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2012 The LUCI Authors. All rights reserved. | 2 # Copyright 2012 The LUCI Authors. All rights reserved. |
| 3 # Use of this source code is governed under the Apache License, Version 2.0 | 3 # Use of this source code is governed under the Apache License, Version 2.0 |
| 4 # that can be found in the LICENSE file. | 4 # that can be found in the LICENSE file. |
| 5 | 5 |
| 6 """Front end tool to operate on .isolate files. | 6 """Front end tool to operate on .isolate files. |
| 7 | 7 |
| 8 This includes creating, merging or compiling them to generate a .isolated file. | 8 This includes creating, merging or compiling them to generate a .isolated file. |
| 9 | 9 |
| 10 See more information at | 10 See more information at |
| (...skipping 461 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 472 """Loads state from disk.""" | 472 """Loads state from disk.""" |
| 473 assert os.path.isabs(isolated_filepath), isolated_filepath | 473 assert os.path.isabs(isolated_filepath), isolated_filepath |
| 474 isolated_basedir = os.path.dirname(isolated_filepath) | 474 isolated_basedir = os.path.dirname(isolated_filepath) |
| 475 return cls( | 475 return cls( |
| 476 isolated_filepath, | 476 isolated_filepath, |
| 477 SavedState.load_file( | 477 SavedState.load_file( |
| 478 isolatedfile_to_state(isolated_filepath), isolated_basedir)) | 478 isolatedfile_to_state(isolated_filepath), isolated_basedir)) |
| 479 | 479 |
| 480 def load_isolate( | 480 def load_isolate( |
| 481 self, cwd, isolate_file, path_variables, config_variables, | 481 self, cwd, isolate_file, path_variables, config_variables, |
| 482 extra_variables, blacklist, ignore_broken_items): | 482 extra_variables, blacklist, ignore_broken_items, collapse_symlinks): |
| 483 """Updates self.isolated and self.saved_state with information loaded from a | 483 """Updates self.isolated and self.saved_state with information loaded from a |
| 484 .isolate file. | 484 .isolate file. |
| 485 | 485 |
| 486 Processes the loaded data, deduce root_dir, relative_cwd. | 486 Processes the loaded data, deduce root_dir, relative_cwd. |
| 487 """ | 487 """ |
| 488 # Make sure to not depend on os.getcwd(). | 488 # Make sure to not depend on os.getcwd(). |
| 489 assert os.path.isabs(isolate_file), isolate_file | 489 assert os.path.isabs(isolate_file), isolate_file |
| 490 isolate_file = file_path.get_native_path_case(isolate_file) | 490 isolate_file = file_path.get_native_path_case(isolate_file) |
| 491 logging.info( | 491 logging.info( |
| 492 'CompleteState.load_isolate(%s, %s, %s, %s, %s, %s)', | 492 'CompleteState.load_isolate(%s, %s, %s, %s, %s, %s, %s)', |
| 493 cwd, isolate_file, path_variables, config_variables, extra_variables, | 493 cwd, isolate_file, path_variables, config_variables, extra_variables, |
| 494 ignore_broken_items) | 494 ignore_broken_items, collapse_symlinks) |
| 495 | 495 |
| 496 # Config variables are not affected by the paths and must be used to | 496 # Config variables are not affected by the paths and must be used to |
| 497 # retrieve the paths, so update them first. | 497 # retrieve the paths, so update them first. |
| 498 self.saved_state.update_config(config_variables) | 498 self.saved_state.update_config(config_variables) |
| 499 | 499 |
| 500 with fs.open(isolate_file, 'r') as f: | 500 with fs.open(isolate_file, 'r') as f: |
| 501 # At that point, variables are not replaced yet in command and infiles. | 501 # At that point, variables are not replaced yet in command and infiles. |
| 502 # infiles may contain directory entries and is in posix style. | 502 # infiles may contain directory entries and is in posix style. |
| 503 command, infiles, read_only, isolate_cmd_dir = ( | 503 command, infiles, read_only, isolate_cmd_dir = ( |
| 504 isolate_format.load_isolate_for_config( | 504 isolate_format.load_isolate_for_config( |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 543 '%s; %s' | 543 '%s; %s' |
| 544 % (k, v, self.saved_state.root_dir, dest)) | 544 % (k, v, self.saved_state.root_dir, dest)) |
| 545 # Normalize the files based to self.saved_state.root_dir. It is important to | 545 # Normalize the files based to self.saved_state.root_dir. It is important to |
| 546 # keep the trailing os.path.sep at that step. | 546 # keep the trailing os.path.sep at that step. |
| 547 infiles = [ | 547 infiles = [ |
| 548 file_path.relpath( | 548 file_path.relpath( |
| 549 file_path.normpath(os.path.join(isolate_cmd_dir, f)), | 549 file_path.normpath(os.path.join(isolate_cmd_dir, f)), |
| 550 self.saved_state.root_dir) | 550 self.saved_state.root_dir) |
| 551 for f in infiles | 551 for f in infiles |
| 552 ] | 552 ] |
| 553 follow_symlinks = sys.platform != 'win32' | 553 follow_symlinks = False |
| 554 if not collapse_symlinks: |
| 555 follow_symlinks = sys.platform != 'win32' |
| 554 # Expand the directories by listing each file inside. Up to now, trailing | 556 # Expand the directories by listing each file inside. Up to now, trailing |
| 555 # os.path.sep must be kept. | 557 # os.path.sep must be kept. |
| 556 infiles = isolated_format.expand_directories_and_symlinks( | 558 infiles = isolated_format.expand_directories_and_symlinks( |
| 557 self.saved_state.root_dir, | 559 self.saved_state.root_dir, |
| 558 infiles, | 560 infiles, |
| 559 tools.gen_blacklist(blacklist), | 561 tools.gen_blacklist(blacklist), |
| 560 follow_symlinks, | 562 follow_symlinks, |
| 561 ignore_broken_items) | 563 ignore_broken_items) |
| 562 | 564 |
| 563 # Finally, update the new data to be able to generate the foo.isolated file, | 565 # Finally, update the new data to be able to generate the foo.isolated file, |
| 564 # the file that is used by run_isolated.py. | 566 # the file that is used by run_isolated.py. |
| 565 self.saved_state.update_isolated(command, infiles, read_only, relative_cwd) | 567 self.saved_state.update_isolated(command, infiles, read_only, relative_cwd) |
| 566 logging.debug(self) | 568 logging.debug(self) |
| 567 | 569 |
| 568 def files_to_metadata(self, subdir): | 570 def files_to_metadata(self, subdir, collapse_symlinks): |
| 569 """Updates self.saved_state.files with the files' mode and hash. | 571 """Updates self.saved_state.files with the files' mode and hash. |
| 570 | 572 |
| 571 If |subdir| is specified, filters to a subdirectory. The resulting .isolated | 573 If |subdir| is specified, filters to a subdirectory. The resulting .isolated |
| 572 file is tainted. | 574 file is tainted. |
| 573 | 575 |
| 574 See isolated_format.file_to_metadata() for more information. | 576 See isolated_format.file_to_metadata() for more information. |
| 575 """ | 577 """ |
| 576 for infile in sorted(self.saved_state.files): | 578 for infile in sorted(self.saved_state.files): |
| 577 if subdir and not infile.startswith(subdir): | 579 if subdir and not infile.startswith(subdir): |
| 578 self.saved_state.files.pop(infile) | 580 self.saved_state.files.pop(infile) |
| 579 else: | 581 else: |
| 580 filepath = os.path.join(self.root_dir, infile) | 582 filepath = os.path.join(self.root_dir, infile) |
| 581 self.saved_state.files[infile] = isolated_format.file_to_metadata( | 583 self.saved_state.files[infile] = isolated_format.file_to_metadata( |
| 582 filepath, | 584 filepath, |
| 583 self.saved_state.files[infile], | 585 self.saved_state.files[infile], |
| 584 self.saved_state.read_only, | 586 self.saved_state.read_only, |
| 585 self.saved_state.algo) | 587 self.saved_state.algo, |
| 588 collapse_symlinks) |
| 586 | 589 |
| 587 def save_files(self): | 590 def save_files(self): |
| 588 """Saves self.saved_state and creates a .isolated file.""" | 591 """Saves self.saved_state and creates a .isolated file.""" |
| 589 logging.debug('Dumping to %s' % self.isolated_filepath) | 592 logging.debug('Dumping to %s' % self.isolated_filepath) |
| 590 self.saved_state.child_isolated_files = chromium_save_isolated( | 593 self.saved_state.child_isolated_files = chromium_save_isolated( |
| 591 self.isolated_filepath, | 594 self.isolated_filepath, |
| 592 self.saved_state.to_isolated(), | 595 self.saved_state.to_isolated(), |
| 593 self.saved_state.path_variables, | 596 self.saved_state.path_variables, |
| 594 self.saved_state.algo) | 597 self.saved_state.algo) |
| 595 total_bytes = sum( | 598 total_bytes = sum( |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 667 complete_state.saved_state.isolate_file, | 670 complete_state.saved_state.isolate_file, |
| 668 isolatedfile_to_state(options.isolated)) | 671 isolatedfile_to_state(options.isolated)) |
| 669 complete_state = CompleteState( | 672 complete_state = CompleteState( |
| 670 options.isolated, | 673 options.isolated, |
| 671 SavedState(complete_state.saved_state.isolated_basedir)) | 674 SavedState(complete_state.saved_state.isolated_basedir)) |
| 672 | 675 |
| 673 if not skip_update: | 676 if not skip_update: |
| 674 # Then load the .isolate and expands directories. | 677 # Then load the .isolate and expands directories. |
| 675 complete_state.load_isolate( | 678 complete_state.load_isolate( |
| 676 cwd, isolate, options.path_variables, options.config_variables, | 679 cwd, isolate, options.path_variables, options.config_variables, |
| 677 options.extra_variables, options.blacklist, options.ignore_broken_items) | 680 options.extra_variables, options.blacklist, options.ignore_broken_items, |
| 681 options.collapse_symlinks) |
| 678 | 682 |
| 679 # Regenerate complete_state.saved_state.files. | 683 # Regenerate complete_state.saved_state.files. |
| 680 if subdir: | 684 if subdir: |
| 681 subdir = unicode(subdir) | 685 subdir = unicode(subdir) |
| 682 # This is tricky here. If it is a path, take it from the root_dir. If | 686 # This is tricky here. If it is a path, take it from the root_dir. If |
| 683 # it is a variable, it must be keyed from the directory containing the | 687 # it is a variable, it must be keyed from the directory containing the |
| 684 # .isolate file. So translate all variables first. | 688 # .isolate file. So translate all variables first. |
| 685 translated_path_variables = dict( | 689 translated_path_variables = dict( |
| 686 (k, | 690 (k, |
| 687 os.path.normpath(os.path.join(complete_state.saved_state.relative_cwd, | 691 os.path.normpath(os.path.join(complete_state.saved_state.relative_cwd, |
| 688 v))) | 692 v))) |
| 689 for k, v in complete_state.saved_state.path_variables.iteritems()) | 693 for k, v in complete_state.saved_state.path_variables.iteritems()) |
| 690 subdir = isolate_format.eval_variables(subdir, translated_path_variables) | 694 subdir = isolate_format.eval_variables(subdir, translated_path_variables) |
| 691 subdir = subdir.replace('/', os.path.sep) | 695 subdir = subdir.replace('/', os.path.sep) |
| 692 | 696 |
| 693 if not skip_update: | 697 if not skip_update: |
| 694 complete_state.files_to_metadata(subdir) | 698 complete_state.files_to_metadata(subdir, options.collapse_symlinks) |
| 695 return complete_state | 699 return complete_state |
| 696 | 700 |
| 697 | 701 |
| 698 def create_isolate_tree(outdir, root_dir, files, relative_cwd, read_only): | 702 def create_isolate_tree(outdir, root_dir, files, relative_cwd, read_only): |
| 699 """Creates a isolated tree usable for test execution. | 703 """Creates a isolated tree usable for test execution. |
| 700 | 704 |
| 701 Returns the current working directory where the isolated command should be | 705 Returns the current working directory where the isolated command should be |
| 702 started in. | 706 started in. |
| 703 """ | 707 """ |
| 704 # Forcibly copy when the tree has to be read only. Otherwise the inode is | 708 # Forcibly copy when the tree has to be read only. Otherwise the inode is |
| (...skipping 410 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1115 '-i', '--isolate', | 1119 '-i', '--isolate', |
| 1116 metavar='FILE', | 1120 metavar='FILE', |
| 1117 help='.isolate file to load the dependency data from') | 1121 help='.isolate file to load the dependency data from') |
| 1118 add_variable_option(group) | 1122 add_variable_option(group) |
| 1119 group.add_option( | 1123 group.add_option( |
| 1120 '--ignore_broken_items', action='store_true', | 1124 '--ignore_broken_items', action='store_true', |
| 1121 default=bool(os.environ.get('ISOLATE_IGNORE_BROKEN_ITEMS')), | 1125 default=bool(os.environ.get('ISOLATE_IGNORE_BROKEN_ITEMS')), |
| 1122 help='Indicates that invalid entries in the isolated file to be ' | 1126 help='Indicates that invalid entries in the isolated file to be ' |
| 1123 'only be logged and not stop processing. Defaults to True if ' | 1127 'only be logged and not stop processing. Defaults to True if ' |
| 1124 'env var ISOLATE_IGNORE_BROKEN_ITEMS is set') | 1128 'env var ISOLATE_IGNORE_BROKEN_ITEMS is set') |
| 1129 group.add_option( |
| 1130 '-L', '--collapse_symlinks', action='store_true', |
| 1131 help='Treat any symlinks as if they were the normal underlying file') |
| 1125 parser.add_option_group(group) | 1132 parser.add_option_group(group) |
| 1126 | 1133 |
| 1127 | 1134 |
| 1128 def add_subdir_option(parser): | 1135 def add_subdir_option(parser): |
| 1129 parser.add_option( | 1136 parser.add_option( |
| 1130 '--subdir', | 1137 '--subdir', |
| 1131 help='Filters to a subdirectory. Its behavior changes depending if it ' | 1138 help='Filters to a subdirectory. Its behavior changes depending if it ' |
| 1132 'is a relative path as a string or as a path variable. Path ' | 1139 'is a relative path as a string or as a path variable. Path ' |
| 1133 'variables are always keyed from the directory containing the ' | 1140 'variables are always keyed from the directory containing the ' |
| 1134 '.isolate file. Anything else is keyed on the root directory.') | 1141 '.isolate file. Anything else is keyed on the root directory.') |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1217 print >> sys.stderr, 'Execution failure: %s' % e | 1224 print >> sys.stderr, 'Execution failure: %s' % e |
| 1218 return 1 | 1225 return 1 |
| 1219 | 1226 |
| 1220 | 1227 |
| 1221 if __name__ == '__main__': | 1228 if __name__ == '__main__': |
| 1222 subprocess42.inhibit_os_error_reporting() | 1229 subprocess42.inhibit_os_error_reporting() |
| 1223 fix_encoding.fix_encoding() | 1230 fix_encoding.fix_encoding() |
| 1224 tools.disable_buffering() | 1231 tools.disable_buffering() |
| 1225 colorama.init() | 1232 colorama.init() |
| 1226 sys.exit(main(sys.argv[1:])) | 1233 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |