| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2015 The Chromium Authors. All rights reserved. | 2 # Copyright 2015 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 """MB - the Meta-Build wrapper around GYP and GN | 6 """MB - the Meta-Build wrapper around GYP and GN |
| 7 | 7 |
| 8 MB is a wrapper script for GYP and GN that can be used to generate build files | 8 MB is a wrapper script for GYP and GN that can be used to generate build files |
| 9 for sets of canned configurations and analyze them. | 9 for sets of canned configurations and analyze them. |
| 10 """ | 10 """ |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 56 help='configuration to analyze') | 56 help='configuration to analyze') |
| 57 subp.add_argument('-f', '--config-file', metavar='PATH', | 57 subp.add_argument('-f', '--config-file', metavar='PATH', |
| 58 default=self.default_config, | 58 default=self.default_config, |
| 59 help='path to config file ' | 59 help='path to config file ' |
| 60 '(default is //tools/mb/mb_config.pyl)') | 60 '(default is //tools/mb/mb_config.pyl)') |
| 61 subp.add_argument('-g', '--goma-dir', default=self.ExpandUser('~/goma'), | 61 subp.add_argument('-g', '--goma-dir', default=self.ExpandUser('~/goma'), |
| 62 help='path to goma directory (default is %(default)s).') | 62 help='path to goma directory (default is %(default)s).') |
| 63 subp.add_argument('-n', '--dryrun', action='store_true', | 63 subp.add_argument('-n', '--dryrun', action='store_true', |
| 64 help='Do a dry run (i.e., do nothing, just print ' | 64 help='Do a dry run (i.e., do nothing, just print ' |
| 65 'the commands that will run)') | 65 'the commands that will run)') |
| 66 subp.add_argument('-q', '--quiet', action='store_true', | 66 subp.add_argument('-v', '--verbose', action='store_true', |
| 67 help='Do not print anything on success, ' | 67 help='verbose logging') |
| 68 'just return an exit code.') | |
| 69 subp.add_argument('-v', '--verbose', action='count', | |
| 70 help='verbose logging (may specify multiple times).') | |
| 71 | 68 |
| 72 parser = argparse.ArgumentParser(prog='mb') | 69 parser = argparse.ArgumentParser(prog='mb') |
| 73 subps = parser.add_subparsers() | 70 subps = parser.add_subparsers() |
| 74 | 71 |
| 75 subp = subps.add_parser('analyze', | 72 subp = subps.add_parser('analyze', |
| 76 help='analyze whether changes to a set of files ' | 73 help='analyze whether changes to a set of files ' |
| 77 'will cause a set of binaries to be rebuilt.') | 74 'will cause a set of binaries to be rebuilt.') |
| 78 AddCommonOptions(subp) | 75 AddCommonOptions(subp) |
| 79 subp.add_argument('--swarming-targets-file', | 76 subp.add_argument('--swarming-targets-file', |
| 80 help='save runtime dependencies for targets listed ' | 77 help='save runtime dependencies for targets listed ' |
| (...skipping 23 matching lines...) Expand all Loading... |
| 104 'builder') | 101 'builder') |
| 105 AddCommonOptions(subp) | 102 AddCommonOptions(subp) |
| 106 subp.set_defaults(func=self.CmdLookup) | 103 subp.set_defaults(func=self.CmdLookup) |
| 107 | 104 |
| 108 subp = subps.add_parser('validate', | 105 subp = subps.add_parser('validate', |
| 109 help='validate the config file') | 106 help='validate the config file') |
| 110 subp.add_argument('-f', '--config-file', metavar='PATH', | 107 subp.add_argument('-f', '--config-file', metavar='PATH', |
| 111 default=self.default_config, | 108 default=self.default_config, |
| 112 help='path to config file ' | 109 help='path to config file ' |
| 113 '(default is //tools/mb/mb_config.pyl)') | 110 '(default is //tools/mb/mb_config.pyl)') |
| 114 subp.add_argument('-q', '--quiet', action='store_true', | |
| 115 help='Do not print anything on success, ' | |
| 116 'just return an exit code.') | |
| 117 subp.set_defaults(func=self.CmdValidate) | 111 subp.set_defaults(func=self.CmdValidate) |
| 118 | 112 |
| 119 subp = subps.add_parser('help', | 113 subp = subps.add_parser('help', |
| 120 help='Get help on a subcommand.') | 114 help='Get help on a subcommand.') |
| 121 subp.add_argument(nargs='?', action='store', dest='subcommand', | 115 subp.add_argument(nargs='?', action='store', dest='subcommand', |
| 122 help='The command to get help for.') | 116 help='The command to get help for.') |
| 123 subp.set_defaults(func=self.CmdHelp) | 117 subp.set_defaults(func=self.CmdHelp) |
| 124 | 118 |
| 125 self.args = parser.parse_args(argv) | 119 self.args = parser.parse_args(argv) |
| 126 | 120 |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 225 | 219 |
| 226 # Check that every mixin defined is actually referenced somewhere. | 220 # Check that every mixin defined is actually referenced somewhere. |
| 227 for mixin in self.mixins: | 221 for mixin in self.mixins: |
| 228 if not mixin in referenced_mixins: | 222 if not mixin in referenced_mixins: |
| 229 errs.append('Unreferenced mixin "%s".' % mixin) | 223 errs.append('Unreferenced mixin "%s".' % mixin) |
| 230 | 224 |
| 231 if errs: | 225 if errs: |
| 232 raise MBErr(('mb config file %s has problems:' % self.args.config_file) + | 226 raise MBErr(('mb config file %s has problems:' % self.args.config_file) + |
| 233 '\n ' + '\n '.join(errs)) | 227 '\n ' + '\n '.join(errs)) |
| 234 | 228 |
| 235 if not self.args.quiet: | 229 self.Print('mb config file %s looks ok.' % self.args.config_file) |
| 236 self.Print('mb config file %s looks ok.' % self.args.config_file) | |
| 237 return 0 | 230 return 0 |
| 238 | 231 |
| 239 def GetConfig(self): | 232 def GetConfig(self): |
| 240 self.ReadConfigFile() | 233 self.ReadConfigFile() |
| 241 config = self.ConfigFromArgs() | 234 config = self.ConfigFromArgs() |
| 242 if not config in self.configs: | 235 if not config in self.configs: |
| 243 raise MBErr('Config "%s" not found in %s' % | 236 raise MBErr('Config "%s" not found in %s' % |
| 244 (config, self.args.config_file)) | 237 (config, self.args.config_file)) |
| 245 | 238 |
| 246 return self.FlattenConfig(config) | 239 return self.FlattenConfig(config) |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 352 | 345 |
| 353 gn_runtime_deps_path = self.ToAbsPath(path, 'runtime_deps') | 346 gn_runtime_deps_path = self.ToAbsPath(path, 'runtime_deps') |
| 354 | 347 |
| 355 # Since GN hasn't run yet, the build directory may not even exist. | 348 # Since GN hasn't run yet, the build directory may not even exist. |
| 356 self.MaybeMakeDirectory(self.ToAbsPath(path)) | 349 self.MaybeMakeDirectory(self.ToAbsPath(path)) |
| 357 | 350 |
| 358 self.WriteFile(gn_runtime_deps_path, '\n'.join(gn_labels) + '\n') | 351 self.WriteFile(gn_runtime_deps_path, '\n'.join(gn_labels) + '\n') |
| 359 cmd.append('--runtime-deps-list-file=%s' % gn_runtime_deps_path) | 352 cmd.append('--runtime-deps-list-file=%s' % gn_runtime_deps_path) |
| 360 | 353 |
| 361 ret, _, _ = self.Run(cmd) | 354 ret, _, _ = self.Run(cmd) |
| 355 if ret: |
| 356 # If `gn gen` failed, we should exit early rather than trying to |
| 357 # generate isolates. Run() will have already logged any error output. |
| 358 self.Print('GN gen failed: %d' % ret) |
| 359 return ret |
| 362 | 360 |
| 363 for target in swarming_targets: | 361 for target in swarming_targets: |
| 364 if gn_isolate_map[target]['type'] == 'gpu_browser_test': | 362 if gn_isolate_map[target]['type'] == 'gpu_browser_test': |
| 365 runtime_deps_target = 'browser_tests' | 363 runtime_deps_target = 'browser_tests' |
| 366 elif gn_isolate_map[target]['type'] == 'script': | 364 elif gn_isolate_map[target]['type'] == 'script': |
| 367 # For script targets, the build target is usually a group, | 365 # For script targets, the build target is usually a group, |
| 368 # for which gn generates the runtime_deps next to the stamp file | 366 # for which gn generates the runtime_deps next to the stamp file |
| 369 # for the label, which lives under the obj/ directory. | 367 # for the label, which lives under the obj/ directory. |
| 370 label = gn_isolate_map[target]['label'] | 368 label = gn_isolate_map[target]['label'] |
| 371 runtime_deps_target = 'obj/%s.stamp' % label.replace(':', '/') | 369 runtime_deps_target = 'obj/%s.stamp' % label.replace(':', '/') |
| (...skipping 29 matching lines...) Expand all Loading... |
| 401 self.ToSrcRelPath('%s%s%s.isolated' % (path, os.sep, target)), | 399 self.ToSrcRelPath('%s%s%s.isolated' % (path, os.sep, target)), |
| 402 '--isolate', | 400 '--isolate', |
| 403 self.ToSrcRelPath('%s%s%s.isolate' % (path, os.sep, target)), | 401 self.ToSrcRelPath('%s%s%s.isolate' % (path, os.sep, target)), |
| 404 ], | 402 ], |
| 405 'dir': self.chromium_src_dir, | 403 'dir': self.chromium_src_dir, |
| 406 'version': 1, | 404 'version': 1, |
| 407 }, | 405 }, |
| 408 isolate_path + 'd.gen.json', | 406 isolate_path + 'd.gen.json', |
| 409 ) | 407 ) |
| 410 | 408 |
| 411 | |
| 412 return ret | 409 return ret |
| 413 | 410 |
| 414 def GNCmd(self, subcommand, path, gn_args=''): | 411 def GNCmd(self, subcommand, path, gn_args=''): |
| 415 if self.platform == 'linux2': | 412 if self.platform == 'linux2': |
| 416 gn_path = os.path.join(self.chromium_src_dir, 'buildtools', 'linux64', | 413 gn_path = os.path.join(self.chromium_src_dir, 'buildtools', 'linux64', |
| 417 'gn') | 414 'gn') |
| 418 elif self.platform == 'darwin': | 415 elif self.platform == 'darwin': |
| 419 gn_path = os.path.join(self.chromium_src_dir, 'buildtools', 'mac', | 416 gn_path = os.path.join(self.chromium_src_dir, 'buildtools', 'mac', |
| 420 'gn') | 417 'gn') |
| 421 else: | 418 else: |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 466 ret, _, _ = self.Run(cmd) | 463 ret, _, _ = self.Run(cmd) |
| 467 if not ret and self.args.verbose: | 464 if not ret and self.args.verbose: |
| 468 outp = json.loads(self.ReadFile(self.args.output_path[0])) | 465 outp = json.loads(self.ReadFile(self.args.output_path[0])) |
| 469 self.Print() | 466 self.Print() |
| 470 self.Print('analyze output:') | 467 self.Print('analyze output:') |
| 471 self.PrintJSON(outp) | 468 self.PrintJSON(outp) |
| 472 self.Print() | 469 self.Print() |
| 473 | 470 |
| 474 return ret | 471 return ret |
| 475 | 472 |
| 476 def RunGNIsolate(self, vals): | |
| 477 build_path = self.args.path[0] | |
| 478 inp = self.ReadInputJSON(['targets']) | |
| 479 if self.args.verbose: | |
| 480 self.Print() | |
| 481 self.Print('isolate input:') | |
| 482 self.PrintJSON(inp) | |
| 483 self.Print() | |
| 484 output_path = self.args.output_path[0] | |
| 485 | |
| 486 for target in inp['targets']: | |
| 487 runtime_deps_path = self.ToAbsPath(build_path, target + '.runtime_deps') | |
| 488 | |
| 489 if not self.Exists(runtime_deps_path): | |
| 490 self.WriteFailureAndRaise('"%s" does not exist' % runtime_deps_path, | |
| 491 output_path) | |
| 492 | |
| 493 command, extra_files = self.GetIsolateCommand(target, vals, None) | |
| 494 | |
| 495 runtime_deps = self.ReadFile(runtime_deps_path).splitlines() | |
| 496 | |
| 497 | |
| 498 isolate_path = self.ToAbsPath(build_path, target + '.isolate') | |
| 499 self.WriteFile(isolate_path, | |
| 500 pprint.pformat({ | |
| 501 'variables': { | |
| 502 'command': command, | |
| 503 'files': sorted(runtime_deps + extra_files), | |
| 504 } | |
| 505 }) + '\n') | |
| 506 | |
| 507 self.WriteJSON( | |
| 508 { | |
| 509 'args': [ | |
| 510 '--isolated', | |
| 511 self.ToSrcRelPath('%s/%s.isolated' % (build_path, target)), | |
| 512 '--isolate', | |
| 513 self.ToSrcRelPath('%s/%s.isolate' % (build_path, target)), | |
| 514 ], | |
| 515 'dir': self.chromium_src_dir, | |
| 516 'version': 1, | |
| 517 }, | |
| 518 isolate_path + 'd.gen.json', | |
| 519 ) | |
| 520 | |
| 521 return 0 | |
| 522 | |
| 523 def GetIsolateCommand(self, target, vals, gn_isolate_map): | 473 def GetIsolateCommand(self, target, vals, gn_isolate_map): |
| 524 # This needs to mirror the settings in //build/config/ui.gni: | 474 # This needs to mirror the settings in //build/config/ui.gni: |
| 525 # use_x11 = is_linux && !use_ozone. | 475 # use_x11 = is_linux && !use_ozone. |
| 526 # TODO(dpranke): Figure out how to keep this in sync better. | 476 # TODO(dpranke): Figure out how to keep this in sync better. |
| 527 use_x11 = (sys.platform == 'linux2' and | 477 use_x11 = (sys.platform == 'linux2' and |
| 528 not 'target_os="android"' in vals['gn_args'] and | 478 not 'target_os="android"' in vals['gn_args'] and |
| 529 not 'use_ozone=true' in vals['gn_args']) | 479 not 'use_ozone=true' in vals['gn_args']) |
| 530 | 480 |
| 531 asan = 'is_asan=true' in vals['gn_args'] | 481 asan = 'is_asan=true' in vals['gn_args'] |
| 532 msan = 'is_msan=true' in vals['gn_args'] | 482 msan = 'is_msan=true' in vals['gn_args'] |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 676 | 626 |
| 677 ret = 0 | 627 ret = 0 |
| 678 response_file = self.TempFile() | 628 response_file = self.TempFile() |
| 679 response_file.write('\n'.join(inp['files']) + '\n') | 629 response_file.write('\n'.join(inp['files']) + '\n') |
| 680 response_file.close() | 630 response_file.close() |
| 681 | 631 |
| 682 matching_targets = [] | 632 matching_targets = [] |
| 683 try: | 633 try: |
| 684 cmd = self.GNCmd('refs', self.args.path[0]) + [ | 634 cmd = self.GNCmd('refs', self.args.path[0]) + [ |
| 685 '@%s' % response_file.name, '--all', '--as=output'] | 635 '@%s' % response_file.name, '--all', '--as=output'] |
| 686 ret, out, _ = self.Run(cmd) | 636 ret, out, _ = self.Run(cmd, force_verbose=False) |
| 687 if ret and not 'The input matches no targets' in out: | 637 if ret and not 'The input matches no targets' in out: |
| 688 self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), | 638 self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), |
| 689 output_path) | 639 output_path) |
| 690 build_dir = self.ToSrcRelPath(self.args.path[0]) + os.sep | 640 build_dir = self.ToSrcRelPath(self.args.path[0]) + os.sep |
| 691 for output in out.splitlines(): | 641 for output in out.splitlines(): |
| 692 build_output = output.replace(build_dir, '') | 642 build_output = output.replace(build_dir, '') |
| 693 if build_output in inp['targets']: | 643 if build_output in inp['targets']: |
| 694 matching_targets.append(build_output) | 644 matching_targets.append(build_output) |
| 695 | 645 |
| 696 cmd = self.GNCmd('refs', self.args.path[0]) + [ | 646 cmd = self.GNCmd('refs', self.args.path[0]) + [ |
| 697 '@%s' % response_file.name, '--all'] | 647 '@%s' % response_file.name, '--all'] |
| 698 ret, out, _ = self.Run(cmd) | 648 ret, out, _ = self.Run(cmd, force_verbose=False) |
| 699 if ret and not 'The input matches no targets' in out: | 649 if ret and not 'The input matches no targets' in out: |
| 700 self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), | 650 self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), |
| 701 output_path) | 651 output_path) |
| 702 for label in out.splitlines(): | 652 for label in out.splitlines(): |
| 703 build_target = label[2:] | 653 build_target = label[2:] |
| 704 # We want to accept 'chrome/android:chrome_public_apk' and | 654 # We want to accept 'chrome/android:chrome_public_apk' and |
| 705 # just 'chrome_public_apk'. This may result in too many targets | 655 # just 'chrome_public_apk'. This may result in too many targets |
| 706 # getting built, but we can adjust that later if need be. | 656 # getting built, but we can adjust that later if need be. |
| 707 for input_target in inp['targets']: | 657 for input_target in inp['targets']: |
| 708 if (input_target == build_target or | 658 if (input_target == build_target or |
| 709 build_target.endswith(':' + input_target)): | 659 build_target.endswith(':' + input_target)): |
| 710 matching_targets.append(input_target) | 660 matching_targets.append(input_target) |
| 711 finally: | 661 finally: |
| 712 self.RemoveFile(response_file.name) | 662 self.RemoveFile(response_file.name) |
| 713 | 663 |
| 714 if matching_targets: | 664 if matching_targets: |
| 715 # TODO: it could be that a target X might depend on a target Y | 665 # TODO: it could be that a target X might depend on a target Y |
| 716 # and both would be listed in the input, but we would only need | 666 # and both would be listed in the input, but we would only need |
| 717 # to specify target X as a build_target (whereas both X and Y are | 667 # to specify target X as a build_target (whereas both X and Y are |
| 718 # targets). I'm not sure if that optimization is generally worth it. | 668 # targets). I'm not sure if that optimization is generally worth it. |
| 719 self.WriteJSON({'targets': sorted(matching_targets), | 669 self.WriteJSON({'targets': sorted(set(matching_targets)), |
| 720 'build_targets': sorted(matching_targets), | 670 'build_targets': sorted(set(matching_targets)), |
| 721 'status': 'Found dependency'}, output_path) | 671 'status': 'Found dependency'}, output_path) |
| 722 else: | 672 else: |
| 723 self.WriteJSON({'targets': [], | 673 self.WriteJSON({'targets': [], |
| 724 'build_targets': [], | 674 'build_targets': [], |
| 725 'status': 'No dependency'}, output_path) | 675 'status': 'No dependency'}, output_path) |
| 726 | 676 |
| 727 if not ret and self.args.verbose: | 677 if self.args.verbose: |
| 728 outp = json.loads(self.ReadFile(output_path)) | 678 outp = json.loads(self.ReadFile(output_path)) |
| 729 self.Print() | 679 self.Print() |
| 730 self.Print('analyze output:') | 680 self.Print('analyze output:') |
| 731 self.PrintJSON(outp) | 681 self.PrintJSON(outp) |
| 732 self.Print() | 682 self.Print() |
| 733 | 683 |
| 734 return 0 | 684 return 0 |
| 735 | 685 |
| 736 def ReadInputJSON(self, required_keys): | 686 def ReadInputJSON(self, required_keys): |
| 737 path = self.args.input_path[0] | 687 path = self.args.input_path[0] |
| 738 output_path = self.args.output_path[0] | 688 output_path = self.args.output_path[0] |
| 739 if not self.Exists(path): | 689 if not self.Exists(path): |
| 740 self.WriteFailureAndRaise('"%s" does not exist' % path, output_path) | 690 self.WriteFailureAndRaise('"%s" does not exist' % path, output_path) |
| 741 | 691 |
| 742 try: | 692 try: |
| 743 inp = json.loads(self.ReadFile(path)) | 693 inp = json.loads(self.ReadFile(path)) |
| 744 except Exception as e: | 694 except Exception as e: |
| 745 self.WriteFailureAndRaise('Failed to read JSON input from "%s": %s' % | 695 self.WriteFailureAndRaise('Failed to read JSON input from "%s": %s' % |
| 746 (path, e), output_path) | 696 (path, e), output_path) |
| 747 | 697 |
| 748 for k in required_keys: | 698 for k in required_keys: |
| 749 if not k in inp: | 699 if not k in inp: |
| 750 self.WriteFailureAndRaise('input file is missing a "%s" key' % k, | 700 self.WriteFailureAndRaise('input file is missing a "%s" key' % k, |
| 751 output_path) | 701 output_path) |
| 752 | 702 |
| 753 return inp | 703 return inp |
| 754 | 704 |
| 755 def WriteFailureAndRaise(self, msg, output_path): | 705 def WriteFailureAndRaise(self, msg, output_path): |
| 756 if output_path: | 706 if output_path: |
| 757 self.WriteJSON({'error': msg}, output_path) | 707 self.WriteJSON({'error': msg}, output_path, force_verbose=True) |
| 758 raise MBErr(msg) | 708 raise MBErr(msg) |
| 759 | 709 |
| 760 def WriteJSON(self, obj, path): | 710 def WriteJSON(self, obj, path, force_verbose=False): |
| 761 try: | 711 try: |
| 762 self.WriteFile(path, json.dumps(obj, indent=2, sort_keys=True) + '\n') | 712 self.WriteFile(path, json.dumps(obj, indent=2, sort_keys=True) + '\n', |
| 713 force_verbose=force_verbose) |
| 763 except Exception as e: | 714 except Exception as e: |
| 764 raise MBErr('Error %s writing to the output path "%s"' % | 715 raise MBErr('Error %s writing to the output path "%s"' % |
| 765 (e, path)) | 716 (e, path)) |
| 766 | 717 |
| 767 def PrintCmd(self, cmd): | 718 def PrintCmd(self, cmd): |
| 768 if cmd[0] == sys.executable: | 719 if cmd[0] == sys.executable: |
| 769 cmd = ['python'] + cmd[1:] | 720 cmd = ['python'] + cmd[1:] |
| 770 self.Print(*[pipes.quote(c) for c in cmd]) | 721 self.Print(*[pipes.quote(c) for c in cmd]) |
| 771 | 722 |
| 772 def PrintJSON(self, obj): | 723 def PrintJSON(self, obj): |
| 773 self.Print(json.dumps(obj, indent=2, sort_keys=True)) | 724 self.Print(json.dumps(obj, indent=2, sort_keys=True)) |
| 774 | 725 |
| 775 def Print(self, *args, **kwargs): | 726 def Print(self, *args, **kwargs): |
| 776 # This function largely exists so it can be overridden for testing. | 727 # This function largely exists so it can be overridden for testing. |
| 777 print(*args, **kwargs) | 728 print(*args, **kwargs) |
| 778 | 729 |
| 779 def Run(self, cmd, env=None): | 730 def Run(self, cmd, env=None, force_verbose=True): |
| 780 # This function largely exists so it can be overridden for testing. | 731 # This function largely exists so it can be overridden for testing. |
| 781 if self.args.dryrun or self.args.verbose: | 732 if self.args.dryrun or self.args.verbose or force_verbose: |
| 782 self.PrintCmd(cmd) | 733 self.PrintCmd(cmd) |
| 783 if self.args.dryrun: | 734 if self.args.dryrun: |
| 784 return 0, '', '' | 735 return 0, '', '' |
| 736 |
| 785 ret, out, err = self.Call(cmd, env=env) | 737 ret, out, err = self.Call(cmd, env=env) |
| 786 if self.args.verbose: | 738 if self.args.verbose or force_verbose: |
| 787 if out: | 739 if out: |
| 788 self.Print(out, end='') | 740 self.Print(out, end='') |
| 789 if err: | 741 if err: |
| 790 self.Print(err, end='', file=sys.stderr) | 742 self.Print(err, end='', file=sys.stderr) |
| 791 return ret, out, err | 743 return ret, out, err |
| 792 | 744 |
| 793 def Call(self, cmd, env=None): | 745 def Call(self, cmd, env=None): |
| 794 p = subprocess.Popen(cmd, shell=False, cwd=self.chromium_src_dir, | 746 p = subprocess.Popen(cmd, shell=False, cwd=self.chromium_src_dir, |
| 795 stdout=subprocess.PIPE, stderr=subprocess.PIPE, | 747 stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| 796 env=env) | 748 env=env) |
| (...skipping 21 matching lines...) Expand all Loading... |
| 818 return fp.read() | 770 return fp.read() |
| 819 | 771 |
| 820 def RemoveFile(self, path): | 772 def RemoveFile(self, path): |
| 821 # This function largely exists so it can be overriden for testing. | 773 # This function largely exists so it can be overriden for testing. |
| 822 os.remove(path) | 774 os.remove(path) |
| 823 | 775 |
| 824 def TempFile(self, mode='w'): | 776 def TempFile(self, mode='w'): |
| 825 # This function largely exists so it can be overriden for testing. | 777 # This function largely exists so it can be overriden for testing. |
| 826 return tempfile.NamedTemporaryFile(mode=mode, delete=False) | 778 return tempfile.NamedTemporaryFile(mode=mode, delete=False) |
| 827 | 779 |
| 828 def WriteFile(self, path, contents): | 780 def WriteFile(self, path, contents, force_verbose=False): |
| 829 # This function largely exists so it can be overriden for testing. | 781 # This function largely exists so it can be overriden for testing. |
| 830 if self.args.dryrun or self.args.verbose: | 782 if self.args.dryrun or self.args.verbose or force_verbose: |
| 831 self.Print('\nWriting """\\\n%s""" to %s.\n' % (contents, path)) | 783 self.Print('\nWriting """\\\n%s""" to %s.\n' % (contents, path)) |
| 832 with open(path, 'w') as fp: | 784 with open(path, 'w') as fp: |
| 833 return fp.write(contents) | 785 return fp.write(contents) |
| 834 | 786 |
| 835 | 787 |
| 836 class MBErr(Exception): | 788 class MBErr(Exception): |
| 837 pass | 789 pass |
| 838 | 790 |
| 839 | 791 |
| 840 if __name__ == '__main__': | 792 if __name__ == '__main__': |
| 841 try: | 793 try: |
| 842 sys.exit(main(sys.argv[1:])) | 794 sys.exit(main(sys.argv[1:])) |
| 843 except MBErr as e: | 795 except MBErr as e: |
| 844 print(e) | 796 print(e) |
| 845 sys.exit(1) | 797 sys.exit(1) |
| 846 except KeyboardInterrupt: | 798 except KeyboardInterrupt: |
| 847 print("interrupted, exiting", stream=sys.stderr) | 799 print("interrupted, exiting", stream=sys.stderr) |
| 848 sys.exit(130) | 800 sys.exit(130) |
| OLD | NEW |