| 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 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 54 self.mixins = {} | 54 self.mixins = {} |
| 55 | 55 |
| 56 def Main(self, args): | 56 def Main(self, args): |
| 57 self.ParseArgs(args) | 57 self.ParseArgs(args) |
| 58 try: | 58 try: |
| 59 ret = self.args.func() | 59 ret = self.args.func() |
| 60 if ret: | 60 if ret: |
| 61 self.DumpInputFiles() | 61 self.DumpInputFiles() |
| 62 return ret | 62 return ret |
| 63 except KeyboardInterrupt: | 63 except KeyboardInterrupt: |
| 64 self.Print('interrupted, exiting') | 64 self.Print('interrupted, exiting', stream=sys.stderr) |
| 65 return 130 | 65 return 130 |
| 66 except Exception: | 66 except Exception: |
| 67 self.DumpInputFiles() | 67 self.DumpInputFiles() |
| 68 s = traceback.format_exc() | 68 s = traceback.format_exc() |
| 69 for l in s.splitlines(): | 69 for l in s.splitlines(): |
| 70 self.Print(l) | 70 self.Print(l) |
| 71 return 1 | 71 return 1 |
| 72 | 72 |
| 73 def ParseArgs(self, argv): | 73 def ParseArgs(self, argv): |
| 74 def AddCommonOptions(subp): | 74 def AddCommonOptions(subp): |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 222 help='The command to get help for.') | 222 help='The command to get help for.') |
| 223 subp.set_defaults(func=self.CmdHelp) | 223 subp.set_defaults(func=self.CmdHelp) |
| 224 | 224 |
| 225 self.args = parser.parse_args(argv) | 225 self.args = parser.parse_args(argv) |
| 226 | 226 |
| 227 def DumpInputFiles(self): | 227 def DumpInputFiles(self): |
| 228 | 228 |
| 229 def DumpContentsOfFilePassedTo(arg_name, path): | 229 def DumpContentsOfFilePassedTo(arg_name, path): |
| 230 if path and self.Exists(path): | 230 if path and self.Exists(path): |
| 231 self.Print("\n# To recreate the file passed to %s:" % arg_name) | 231 self.Print("\n# To recreate the file passed to %s:" % arg_name) |
| 232 self.Print("%% cat > %s <<EOF" % path) | 232 self.Print("%% cat > %s <<EOF)" % path) |
| 233 contents = self.ReadFile(path) | 233 contents = self.ReadFile(path) |
| 234 self.Print(contents) | 234 self.Print(contents) |
| 235 self.Print("EOF\n%\n") | 235 self.Print("EOF\n%\n") |
| 236 | 236 |
| 237 if getattr(self.args, 'input_path', None): | 237 if getattr(self.args, 'input_path', None): |
| 238 DumpContentsOfFilePassedTo( | 238 DumpContentsOfFilePassedTo( |
| 239 'argv[0] (input_path)', self.args.input_path[0]) | 239 'argv[0] (input_path)', self.args.input_path[0]) |
| 240 if getattr(self.args, 'swarming_targets_file', None): | 240 if getattr(self.args, 'swarming_targets_file', None): |
| 241 DumpContentsOfFilePassedTo( | 241 DumpContentsOfFilePassedTo( |
| 242 '--swarming-targets-file', self.args.swarming_targets_file) | 242 '--swarming-targets-file', self.args.swarming_targets_file) |
| (...skipping 393 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 636 try: | 636 try: |
| 637 contents = ast.literal_eval(self.ReadFile(self.args.config_file)) | 637 contents = ast.literal_eval(self.ReadFile(self.args.config_file)) |
| 638 except SyntaxError as e: | 638 except SyntaxError as e: |
| 639 raise MBErr('Failed to parse config file "%s": %s' % | 639 raise MBErr('Failed to parse config file "%s": %s' % |
| 640 (self.args.config_file, e)) | 640 (self.args.config_file, e)) |
| 641 | 641 |
| 642 self.configs = contents['configs'] | 642 self.configs = contents['configs'] |
| 643 self.masters = contents['masters'] | 643 self.masters = contents['masters'] |
| 644 self.mixins = contents['mixins'] | 644 self.mixins = contents['mixins'] |
| 645 | 645 |
| 646 def ReadIsolateMap(self): | |
| 647 return ast.literal_eval(self.ReadFile(self.PathJoin( | |
| 648 self.chromium_src_dir, 'testing', 'buildbot', 'gn_isolate_map.pyl'))) | |
| 649 | |
| 650 def ConfigFromArgs(self): | 646 def ConfigFromArgs(self): |
| 651 if self.args.config: | 647 if self.args.config: |
| 652 if self.args.master or self.args.builder: | 648 if self.args.master or self.args.builder: |
| 653 raise MBErr('Can not specific both -c/--config and -m/--master or ' | 649 raise MBErr('Can not specific both -c/--config and -m/--master or ' |
| 654 '-b/--builder') | 650 '-b/--builder') |
| 655 | 651 |
| 656 return self.args.config | 652 return self.args.config |
| 657 | 653 |
| 658 if not self.args.master or not self.args.builder: | 654 if not self.args.master or not self.args.builder: |
| 659 raise MBErr('Must specify either -c/--config or ' | 655 raise MBErr('Must specify either -c/--config or ' |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 767 # Since GN hasn't run yet, the build directory may not even exist. | 763 # Since GN hasn't run yet, the build directory may not even exist. |
| 768 self.MaybeMakeDirectory(self.ToAbsPath(build_dir)) | 764 self.MaybeMakeDirectory(self.ToAbsPath(build_dir)) |
| 769 | 765 |
| 770 gn_args_path = self.ToAbsPath(build_dir, 'args.gn') | 766 gn_args_path = self.ToAbsPath(build_dir, 'args.gn') |
| 771 self.WriteFile(gn_args_path, gn_args, force_verbose=True) | 767 self.WriteFile(gn_args_path, gn_args, force_verbose=True) |
| 772 | 768 |
| 773 swarming_targets = [] | 769 swarming_targets = [] |
| 774 if getattr(self.args, 'swarming_targets_file', None): | 770 if getattr(self.args, 'swarming_targets_file', None): |
| 775 # We need GN to generate the list of runtime dependencies for | 771 # We need GN to generate the list of runtime dependencies for |
| 776 # the compile targets listed (one per line) in the file so | 772 # the compile targets listed (one per line) in the file so |
| 777 # we can run them via swarming. We use gn_isolate_map.pyl to convert | 773 # we can run them via swarming. We use ninja_to_gn.pyl to convert |
| 778 # the compile targets to the matching GN labels. | 774 # the compile targets to the matching GN labels. |
| 779 path = self.args.swarming_targets_file | 775 path = self.args.swarming_targets_file |
| 780 if not self.Exists(path): | 776 if not self.Exists(path): |
| 781 self.WriteFailureAndRaise('"%s" does not exist' % path, | 777 self.WriteFailureAndRaise('"%s" does not exist' % path, |
| 782 output_path=None) | 778 output_path=None) |
| 783 contents = self.ReadFile(path) | 779 contents = self.ReadFile(path) |
| 784 swarming_targets = set(contents.splitlines()) | 780 swarming_targets = set(contents.splitlines()) |
| 781 gn_isolate_map = ast.literal_eval(self.ReadFile(self.PathJoin( |
| 782 self.chromium_src_dir, 'testing', 'buildbot', 'gn_isolate_map.pyl'))) |
| 783 gn_labels = [] |
| 784 err = '' |
| 785 for target in swarming_targets: |
| 786 target_name = self.GNTargetName(target) |
| 787 if not target_name in gn_isolate_map: |
| 788 err += ('test target "%s" not found\n' % target_name) |
| 789 elif gn_isolate_map[target_name]['type'] == 'unknown': |
| 790 err += ('test target "%s" type is unknown\n' % target_name) |
| 791 else: |
| 792 gn_labels.append(gn_isolate_map[target_name]['label']) |
| 785 | 793 |
| 786 isolate_map = self.ReadIsolateMap() | |
| 787 err, labels = self.MapTargetsToLabels(isolate_map, swarming_targets) | |
| 788 if err: | 794 if err: |
| 789 raise MBErr(err) | 795 raise MBErr('Error: Failed to match swarming targets to %s:\n%s' % |
| 796 ('//testing/buildbot/gn_isolate_map.pyl', err)) |
| 790 | 797 |
| 791 gn_runtime_deps_path = self.ToAbsPath(build_dir, 'runtime_deps') | 798 gn_runtime_deps_path = self.ToAbsPath(build_dir, 'runtime_deps') |
| 792 self.WriteFile(gn_runtime_deps_path, '\n'.join(labels) + '\n') | 799 self.WriteFile(gn_runtime_deps_path, '\n'.join(gn_labels) + '\n') |
| 793 cmd.append('--runtime-deps-list-file=%s' % gn_runtime_deps_path) | 800 cmd.append('--runtime-deps-list-file=%s' % gn_runtime_deps_path) |
| 794 | 801 |
| 795 ret, _, _ = self.Run(cmd) | 802 ret, _, _ = self.Run(cmd) |
| 796 if ret: | 803 if ret: |
| 797 # If `gn gen` failed, we should exit early rather than trying to | 804 # If `gn gen` failed, we should exit early rather than trying to |
| 798 # generate isolates. Run() will have already logged any error output. | 805 # generate isolates. Run() will have already logged any error output. |
| 799 self.Print('GN gen failed: %d' % ret) | 806 self.Print('GN gen failed: %d' % ret) |
| 800 return ret | 807 return ret |
| 801 | 808 |
| 802 android = 'target_os="android"' in vals['gn_args'] | 809 android = 'target_os="android"' in vals['gn_args'] |
| 803 for target in swarming_targets: | 810 for target in swarming_targets: |
| 804 if android: | 811 if android: |
| 805 # Android targets may be either android_apk or executable. The former | 812 # Android targets may be either android_apk or executable. The former |
| 806 # will result in runtime_deps associated with the stamp file, while the | 813 # will result in runtime_deps associated with the stamp file, while the |
| 807 # latter will result in runtime_deps associated with the executable. | 814 # latter will result in runtime_deps associated with the executable. |
| 808 label = isolate_map[target]['label'] | 815 target_name = self.GNTargetName(target) |
| 816 label = gn_isolate_map[target_name]['label'] |
| 809 runtime_deps_targets = [ | 817 runtime_deps_targets = [ |
| 810 target + '.runtime_deps', | 818 target_name + '.runtime_deps', |
| 811 'obj/%s.stamp.runtime_deps' % label.replace(':', '/')] | 819 'obj/%s.stamp.runtime_deps' % label.replace(':', '/')] |
| 812 elif isolate_map[target]['type'] == 'gpu_browser_test': | 820 elif gn_isolate_map[target]['type'] == 'gpu_browser_test': |
| 813 if self.platform == 'win32': | 821 if self.platform == 'win32': |
| 814 runtime_deps_targets = ['browser_tests.exe.runtime_deps'] | 822 runtime_deps_targets = ['browser_tests.exe.runtime_deps'] |
| 815 else: | 823 else: |
| 816 runtime_deps_targets = ['browser_tests.runtime_deps'] | 824 runtime_deps_targets = ['browser_tests.runtime_deps'] |
| 817 elif (isolate_map[target]['type'] == 'script' or | 825 elif (gn_isolate_map[target]['type'] == 'script' or |
| 818 isolate_map[target].get('label_type') == 'group'): | 826 gn_isolate_map[target].get('label_type') == 'group'): |
| 819 # For script targets, the build target is usually a group, | 827 # For script targets, the build target is usually a group, |
| 820 # for which gn generates the runtime_deps next to the stamp file | 828 # for which gn generates the runtime_deps next to the stamp file |
| 821 # for the label, which lives under the obj/ directory. | 829 # for the label, which lives under the obj/ directory. |
| 822 label = isolate_map[target]['label'] | 830 label = gn_isolate_map[target]['label'] |
| 823 runtime_deps_targets = [ | 831 runtime_deps_targets = [ |
| 824 'obj/%s.stamp.runtime_deps' % label.replace(':', '/')] | 832 'obj/%s.stamp.runtime_deps' % label.replace(':', '/')] |
| 825 elif self.platform == 'win32': | 833 elif self.platform == 'win32': |
| 826 runtime_deps_targets = [target + '.exe.runtime_deps'] | 834 runtime_deps_targets = [target + '.exe.runtime_deps'] |
| 827 else: | 835 else: |
| 828 runtime_deps_targets = [target + '.runtime_deps'] | 836 runtime_deps_targets = [target + '.runtime_deps'] |
| 829 | 837 |
| 830 for r in runtime_deps_targets: | 838 for r in runtime_deps_targets: |
| 831 runtime_deps_path = self.ToAbsPath(build_dir, r) | 839 runtime_deps_path = self.ToAbsPath(build_dir, r) |
| 832 if self.Exists(runtime_deps_path): | 840 if self.Exists(runtime_deps_path): |
| 833 break | 841 break |
| 834 else: | 842 else: |
| 835 raise MBErr('did not generate any of %s' % | 843 raise MBErr('did not generate any of %s' % |
| 836 ', '.join(runtime_deps_targets)) | 844 ', '.join(runtime_deps_targets)) |
| 837 | 845 |
| 838 command, extra_files = self.GetIsolateCommand(target, vals) | 846 command, extra_files = self.GetIsolateCommand(target, vals, |
| 847 gn_isolate_map) |
| 839 | 848 |
| 840 runtime_deps = self.ReadFile(runtime_deps_path).splitlines() | 849 runtime_deps = self.ReadFile(runtime_deps_path).splitlines() |
| 841 | 850 |
| 842 self.WriteIsolateFiles(build_dir, command, target, runtime_deps, | 851 self.WriteIsolateFiles(build_dir, command, target, runtime_deps, |
| 843 extra_files) | 852 extra_files) |
| 844 | 853 |
| 845 return 0 | 854 return 0 |
| 846 | 855 |
| 847 def RunGNIsolate(self, vals): | 856 def RunGNIsolate(self, vals): |
| 848 target = self.args.target[0] | 857 gn_isolate_map = ast.literal_eval(self.ReadFile(self.PathJoin( |
| 849 isolate_map = self.ReadIsolateMap() | 858 self.chromium_src_dir, 'testing', 'buildbot', 'gn_isolate_map.pyl'))) |
| 850 err, labels = self.MapTargetsToLabels(isolate_map, [target]) | |
| 851 if err: | |
| 852 raise MBErr(err) | |
| 853 label = labels[0] | |
| 854 | 859 |
| 855 build_dir = self.args.path[0] | 860 build_dir = self.args.path[0] |
| 856 command, extra_files = self.GetIsolateCommand(target, vals) | 861 target = self.args.target[0] |
| 862 target_name = self.GNTargetName(target) |
| 863 command, extra_files = self.GetIsolateCommand(target, vals, gn_isolate_map) |
| 857 | 864 |
| 865 label = gn_isolate_map[target_name]['label'] |
| 858 cmd = self.GNCmd('desc', build_dir, label, 'runtime_deps') | 866 cmd = self.GNCmd('desc', build_dir, label, 'runtime_deps') |
| 859 ret, out, _ = self.Call(cmd) | 867 ret, out, _ = self.Call(cmd) |
| 860 if ret: | 868 if ret: |
| 861 if out: | 869 if out: |
| 862 self.Print(out) | 870 self.Print(out) |
| 863 return ret | 871 return ret |
| 864 | 872 |
| 865 runtime_deps = out.splitlines() | 873 runtime_deps = out.splitlines() |
| 866 | 874 |
| 867 self.WriteIsolateFiles(build_dir, command, target, runtime_deps, | 875 self.WriteIsolateFiles(build_dir, command, target, runtime_deps, |
| (...skipping 29 matching lines...) Expand all Loading... |
| 897 self.ToSrcRelPath('%s/%s.isolated' % (build_dir, target)), | 905 self.ToSrcRelPath('%s/%s.isolated' % (build_dir, target)), |
| 898 '--isolate', | 906 '--isolate', |
| 899 self.ToSrcRelPath('%s/%s.isolate' % (build_dir, target)), | 907 self.ToSrcRelPath('%s/%s.isolate' % (build_dir, target)), |
| 900 ], | 908 ], |
| 901 'dir': self.chromium_src_dir, | 909 'dir': self.chromium_src_dir, |
| 902 'version': 1, | 910 'version': 1, |
| 903 }, | 911 }, |
| 904 isolate_path + 'd.gen.json', | 912 isolate_path + 'd.gen.json', |
| 905 ) | 913 ) |
| 906 | 914 |
| 907 def MapTargetsToLabels(self, isolate_map, targets): | |
| 908 labels = [] | |
| 909 err = '' | |
| 910 for target in targets: | |
| 911 if target == 'all': | |
| 912 labels.append(target) | |
| 913 elif not target in isolate_map: | |
| 914 non_run_target = target[:-4] | |
| 915 if target.endswith('_run') and non_run_target in isolate_map: | |
| 916 labels.append(isolate_map[non_run_target]['label'] + '_run') | |
| 917 elif target.startswith('//'): | |
| 918 labels.append(target) | |
| 919 else: | |
| 920 err += ('target "%s" not found in ' | |
| 921 '//testing/buildbot/gn_isolate_map.pyl\n' % target) | |
| 922 elif isolate_map[target]['type'] == 'unknown': | |
| 923 err += ('test target "%s" type is unknown\n' % target) | |
| 924 else: | |
| 925 labels.append(isolate_map[target]['label']) | |
| 926 return err, labels | |
| 927 | |
| 928 def GNCmd(self, subcommand, path, *args): | 915 def GNCmd(self, subcommand, path, *args): |
| 929 if self.platform == 'linux2': | 916 if self.platform == 'linux2': |
| 930 subdir, exe = 'linux64', 'gn' | 917 subdir, exe = 'linux64', 'gn' |
| 931 elif self.platform == 'darwin': | 918 elif self.platform == 'darwin': |
| 932 subdir, exe = 'mac', 'gn' | 919 subdir, exe = 'mac', 'gn' |
| 933 else: | 920 else: |
| 934 subdir, exe = 'win', 'gn.exe' | 921 subdir, exe = 'win', 'gn.exe' |
| 935 | 922 |
| 936 gn_path = self.PathJoin(self.chromium_src_dir, 'buildtools', subdir, exe) | 923 gn_path = self.PathJoin(self.chromium_src_dir, 'buildtools', subdir, exe) |
| 924 |
| 937 return [gn_path, subcommand, path] + list(args) | 925 return [gn_path, subcommand, path] + list(args) |
| 938 | 926 |
| 939 | |
| 940 def GNArgs(self, vals): | 927 def GNArgs(self, vals): |
| 941 if vals['cros_passthrough']: | 928 if vals['cros_passthrough']: |
| 942 if not 'GN_ARGS' in os.environ: | 929 if not 'GN_ARGS' in os.environ: |
| 943 raise MBErr('MB is expecting GN_ARGS to be in the environment') | 930 raise MBErr('MB is expecting GN_ARGS to be in the environment') |
| 944 gn_args = os.environ['GN_ARGS'] | 931 gn_args = os.environ['GN_ARGS'] |
| 945 if not re.search('target_os.*=.*"chromeos"', gn_args): | 932 if not re.search('target_os.*=.*"chromeos"', gn_args): |
| 946 raise MBErr('GN_ARGS is missing target_os = "chromeos": (GN_ARGS=%s)' % | 933 raise MBErr('GN_ARGS is missing target_os = "chromeos": (GN_ARGS=%s)' % |
| 947 gn_args) | 934 gn_args) |
| 948 else: | 935 else: |
| 949 gn_args = vals['gn_args'] | 936 gn_args = vals['gn_args'] |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 994 ret, _, _ = self.Run(cmd, env=env) | 981 ret, _, _ = self.Run(cmd, env=env) |
| 995 if not ret and self.args.verbose: | 982 if not ret and self.args.verbose: |
| 996 outp = json.loads(self.ReadFile(self.args.output_path[0])) | 983 outp = json.loads(self.ReadFile(self.args.output_path[0])) |
| 997 self.Print() | 984 self.Print() |
| 998 self.Print('analyze output:') | 985 self.Print('analyze output:') |
| 999 self.PrintJSON(outp) | 986 self.PrintJSON(outp) |
| 1000 self.Print() | 987 self.Print() |
| 1001 | 988 |
| 1002 return ret | 989 return ret |
| 1003 | 990 |
| 1004 def GetIsolateCommand(self, target, vals): | 991 def GetIsolateCommand(self, target, vals, gn_isolate_map): |
| 1005 android = 'target_os="android"' in vals['gn_args'] | 992 android = 'target_os="android"' in vals['gn_args'] |
| 1006 | 993 |
| 1007 # This needs to mirror the settings in //build/config/ui.gni: | 994 # This needs to mirror the settings in //build/config/ui.gni: |
| 1008 # use_x11 = is_linux && !use_ozone. | 995 # use_x11 = is_linux && !use_ozone. |
| 1009 use_x11 = (self.platform == 'linux2' and | 996 use_x11 = (self.platform == 'linux2' and |
| 1010 not android and | 997 not android and |
| 1011 not 'use_ozone=true' in vals['gn_args']) | 998 not 'use_ozone=true' in vals['gn_args']) |
| 1012 | 999 |
| 1013 asan = 'is_asan=true' in vals['gn_args'] | 1000 asan = 'is_asan=true' in vals['gn_args'] |
| 1014 msan = 'is_msan=true' in vals['gn_args'] | 1001 msan = 'is_msan=true' in vals['gn_args'] |
| 1015 tsan = 'is_tsan=true' in vals['gn_args'] | 1002 tsan = 'is_tsan=true' in vals['gn_args'] |
| 1016 | 1003 |
| 1017 isolate_map = self.ReadIsolateMap() | 1004 target_name = self.GNTargetName(target) |
| 1018 test_type = isolate_map[target]['type'] | 1005 test_type = gn_isolate_map[target_name]['type'] |
| 1019 | 1006 |
| 1020 executable = isolate_map[target].get('executable', target) | 1007 executable = gn_isolate_map[target_name].get('executable', target_name) |
| 1021 executable_suffix = '.exe' if self.platform == 'win32' else '' | 1008 executable_suffix = '.exe' if self.platform == 'win32' else '' |
| 1022 | 1009 |
| 1023 cmdline = [] | 1010 cmdline = [] |
| 1024 extra_files = [] | 1011 extra_files = [] |
| 1025 | 1012 |
| 1026 if test_type == 'nontest': | |
| 1027 self.WriteFailureAndRaise('We should not be isolating %s.' % target, | |
| 1028 output_path=None) | |
| 1029 | |
| 1030 if android and test_type != "script": | 1013 if android and test_type != "script": |
| 1031 logdog_command = [ | 1014 logdog_command = [ |
| 1032 '--logdog-bin-cmd', './../../bin/logdog_butler', | 1015 '--logdog-bin-cmd', './../../bin/logdog_butler', |
| 1033 '--project', 'chromium', | 1016 '--project', 'chromium', |
| 1034 '--service-account-json', | 1017 '--service-account-json', |
| 1035 '/creds/service_accounts/service-account-luci-logdog-publisher.json', | 1018 '/creds/service_accounts/service-account-luci-logdog-publisher.json', |
| 1036 '--prefix', 'android/swarming/logcats/${SWARMING_TASK_ID}', | 1019 '--prefix', 'android/swarming/logcats/${SWARMING_TASK_ID}', |
| 1037 '--source', '${ISOLATED_OUTDIR}/logcats', | 1020 '--source', '${ISOLATED_OUTDIR}/logcats', |
| 1038 '--name', 'unified_logcats', | 1021 '--name', 'unified_logcats', |
| 1039 ] | 1022 ] |
| 1040 test_cmdline = [ | 1023 test_cmdline = [ |
| 1041 self.PathJoin('bin', 'run_%s' % target), | 1024 self.PathJoin('bin', 'run_%s' % target_name), |
| 1042 '--logcat-output-file', '${ISOLATED_OUTDIR}/logcats', | 1025 '--logcat-output-file', '${ISOLATED_OUTDIR}/logcats', |
| 1043 '--target-devices-file', '${SWARMING_BOT_FILE}', | 1026 '--target-devices-file', '${SWARMING_BOT_FILE}', |
| 1044 '-v' | 1027 '-v' |
| 1045 ] | 1028 ] |
| 1046 cmdline = (['./../../build/android/test_wrapper/logdog_wrapper.py'] | 1029 cmdline = (['./../../build/android/test_wrapper/logdog_wrapper.py'] |
| 1047 + logdog_command + test_cmdline) | 1030 + logdog_command + test_cmdline) |
| 1048 elif use_x11 and test_type == 'windowed_test_launcher': | 1031 elif use_x11 and test_type == 'windowed_test_launcher': |
| 1049 extra_files = [ | 1032 extra_files = [ |
| 1050 'xdisplaycheck', | 1033 'xdisplaycheck', |
| 1051 '../../testing/test_env.py', | 1034 '../../testing/test_env.py', |
| (...skipping 19 matching lines...) Expand all Loading... |
| 1071 '--brave-new-test-launcher', | 1054 '--brave-new-test-launcher', |
| 1072 '--test-launcher-bot-mode', | 1055 '--test-launcher-bot-mode', |
| 1073 '--asan=%d' % asan, | 1056 '--asan=%d' % asan, |
| 1074 '--msan=%d' % msan, | 1057 '--msan=%d' % msan, |
| 1075 '--tsan=%d' % tsan, | 1058 '--tsan=%d' % tsan, |
| 1076 ] | 1059 ] |
| 1077 elif test_type == 'gpu_browser_test': | 1060 elif test_type == 'gpu_browser_test': |
| 1078 extra_files = [ | 1061 extra_files = [ |
| 1079 '../../testing/test_env.py' | 1062 '../../testing/test_env.py' |
| 1080 ] | 1063 ] |
| 1081 gtest_filter = isolate_map[target]['gtest_filter'] | 1064 gtest_filter = gn_isolate_map[target]['gtest_filter'] |
| 1082 cmdline = [ | 1065 cmdline = [ |
| 1083 '../../testing/test_env.py', | 1066 '../../testing/test_env.py', |
| 1084 './browser_tests' + executable_suffix, | 1067 './browser_tests' + executable_suffix, |
| 1085 '--test-launcher-bot-mode', | 1068 '--test-launcher-bot-mode', |
| 1086 '--enable-gpu', | 1069 '--enable-gpu', |
| 1087 '--test-launcher-jobs=1', | 1070 '--test-launcher-jobs=1', |
| 1088 '--gtest_filter=%s' % gtest_filter, | 1071 '--gtest_filter=%s' % gtest_filter, |
| 1089 ] | 1072 ] |
| 1090 elif test_type == 'script': | 1073 elif test_type == 'script': |
| 1091 extra_files = [ | 1074 extra_files = [ |
| 1092 '../../testing/test_env.py' | 1075 '../../testing/test_env.py' |
| 1093 ] | 1076 ] |
| 1094 cmdline = [ | 1077 cmdline = [ |
| 1095 '../../testing/test_env.py', | 1078 '../../testing/test_env.py', |
| 1096 '../../' + self.ToSrcRelPath(isolate_map[target]['script']) | 1079 '../../' + self.ToSrcRelPath(gn_isolate_map[target]['script']) |
| 1097 ] | 1080 ] |
| 1098 elif test_type in ('raw'): | 1081 elif test_type in ('raw'): |
| 1099 extra_files = [] | 1082 extra_files = [] |
| 1100 cmdline = [ | 1083 cmdline = [ |
| 1101 './' + str(target) + executable_suffix, | 1084 './' + str(target) + executable_suffix, |
| 1102 ] | 1085 ] |
| 1103 | 1086 |
| 1104 else: | 1087 else: |
| 1105 self.WriteFailureAndRaise('No command line for %s found (test type %s).' | 1088 self.WriteFailureAndRaise('No command line for %s found (test type %s).' |
| 1106 % (target, test_type), output_path=None) | 1089 % (target, test_type), output_path=None) |
| 1107 | 1090 |
| 1108 cmdline += isolate_map[target].get('args', []) | 1091 cmdline += gn_isolate_map[target_name].get('args', []) |
| 1109 | 1092 |
| 1110 return cmdline, extra_files | 1093 return cmdline, extra_files |
| 1111 | 1094 |
| 1112 def ToAbsPath(self, build_path, *comps): | 1095 def ToAbsPath(self, build_path, *comps): |
| 1113 return self.PathJoin(self.chromium_src_dir, | 1096 return self.PathJoin(self.chromium_src_dir, |
| 1114 self.ToSrcRelPath(build_path), | 1097 self.ToSrcRelPath(build_path), |
| 1115 *comps) | 1098 *comps) |
| 1116 | 1099 |
| 1117 def ToSrcRelPath(self, path): | 1100 def ToSrcRelPath(self, path): |
| 1118 """Returns a relative path from the top of the repo.""" | 1101 """Returns a relative path from the top of the repo.""" |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1194 if 'GYP_CHROMIUM_NO_ACTION' in env: | 1177 if 'GYP_CHROMIUM_NO_ACTION' in env: |
| 1195 del env['GYP_CHROMIUM_NO_ACTION'] | 1178 del env['GYP_CHROMIUM_NO_ACTION'] |
| 1196 if 'GYP_CROSSCOMPILE' in env: | 1179 if 'GYP_CROSSCOMPILE' in env: |
| 1197 del env['GYP_CROSSCOMPILE'] | 1180 del env['GYP_CROSSCOMPILE'] |
| 1198 env['GYP_DEFINES'] = gyp_defines | 1181 env['GYP_DEFINES'] = gyp_defines |
| 1199 if vals['gyp_crosscompile']: | 1182 if vals['gyp_crosscompile']: |
| 1200 env['GYP_CROSSCOMPILE'] = '1' | 1183 env['GYP_CROSSCOMPILE'] = '1' |
| 1201 return cmd, env | 1184 return cmd, env |
| 1202 | 1185 |
| 1203 def RunGNAnalyze(self, vals): | 1186 def RunGNAnalyze(self, vals): |
| 1204 # Analyze runs before 'gn gen' now, so we need to run gn gen | 1187 # analyze runs before 'gn gen' now, so we need to run gn gen |
| 1205 # in order to ensure that we have a build directory. | 1188 # in order to ensure that we have a build directory. |
| 1206 ret = self.RunGNGen(vals) | 1189 ret = self.RunGNGen(vals) |
| 1207 if ret: | 1190 if ret: |
| 1208 return ret | 1191 return ret |
| 1209 | 1192 |
| 1210 build_path = self.args.path[0] | |
| 1211 input_path = self.args.input_path[0] | |
| 1212 gn_input_path = input_path + '.gn' | |
| 1213 output_path = self.args.output_path[0] | |
| 1214 gn_output_path = output_path + '.gn' | |
| 1215 | |
| 1216 inp = self.ReadInputJSON(['files', 'test_targets', | 1193 inp = self.ReadInputJSON(['files', 'test_targets', |
| 1217 'additional_compile_targets']) | 1194 'additional_compile_targets']) |
| 1218 if self.args.verbose: | 1195 if self.args.verbose: |
| 1219 self.Print() | 1196 self.Print() |
| 1220 self.Print('analyze input:') | 1197 self.Print('analyze input:') |
| 1221 self.PrintJSON(inp) | 1198 self.PrintJSON(inp) |
| 1222 self.Print() | 1199 self.Print() |
| 1223 | 1200 |
| 1201 # TODO(crbug.com/555273) - currently GN treats targets and |
| 1202 # additional_compile_targets identically since we can't tell the |
| 1203 # difference between a target that is a group in GN and one that isn't. |
| 1204 # We should eventually fix this and treat the two types differently. |
| 1205 targets = (set(inp['test_targets']) | |
| 1206 set(inp['additional_compile_targets'])) |
| 1207 |
| 1208 output_path = self.args.output_path[0] |
| 1209 |
| 1210 # Bail out early if a GN file was modified, since 'gn refs' won't know |
| 1211 # what to do about it. Also, bail out early if 'all' was asked for, |
| 1212 # since we can't deal with it yet. |
| 1213 if (any(f.endswith('.gn') or f.endswith('.gni') for f in inp['files']) or |
| 1214 'all' in targets): |
| 1215 self.WriteJSON({ |
| 1216 'status': 'Found dependency (all)', |
| 1217 'compile_targets': sorted(targets), |
| 1218 'test_targets': sorted(targets & set(inp['test_targets'])), |
| 1219 }, output_path) |
| 1220 return 0 |
| 1224 | 1221 |
| 1225 # This shouldn't normally happen, but could due to unusual race conditions, | 1222 # This shouldn't normally happen, but could due to unusual race conditions, |
| 1226 # like a try job that gets scheduled before a patch lands but runs after | 1223 # like a try job that gets scheduled before a patch lands but runs after |
| 1227 # the patch has landed. | 1224 # the patch has landed. |
| 1228 if not inp['files']: | 1225 if not inp['files']: |
| 1229 self.Print('Warning: No files modified in patch, bailing out early.') | 1226 self.Print('Warning: No files modified in patch, bailing out early.') |
| 1230 self.WriteJSON({ | 1227 self.WriteJSON({ |
| 1231 'status': 'No dependency', | 1228 'status': 'No dependency', |
| 1232 'compile_targets': [], | 1229 'compile_targets': [], |
| 1233 'test_targets': [], | 1230 'test_targets': [], |
| 1234 }, output_path) | 1231 }, output_path) |
| 1235 return 0 | 1232 return 0 |
| 1236 | 1233 |
| 1237 gn_inp = {} | 1234 ret = 0 |
| 1238 gn_inp['files'] = ['//' + f for f in inp['files'] if not f.startswith('//')] | 1235 response_file = self.TempFile() |
| 1236 response_file.write('\n'.join(inp['files']) + '\n') |
| 1237 response_file.close() |
| 1239 | 1238 |
| 1240 isolate_map = self.ReadIsolateMap() | 1239 matching_targets = set() |
| 1241 err, gn_inp['additional_compile_targets'] = self.MapTargetsToLabels( | 1240 try: |
| 1242 isolate_map, inp['additional_compile_targets']) | 1241 cmd = self.GNCmd('refs', |
| 1243 if err: | 1242 self.args.path[0], |
| 1244 raise MBErr(err) | 1243 '@%s' % response_file.name, |
| 1244 '--all', |
| 1245 '--as=output') |
| 1246 ret, out, _ = self.Run(cmd, force_verbose=False) |
| 1247 if ret and not 'The input matches no targets' in out: |
| 1248 self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), |
| 1249 output_path) |
| 1250 build_dir = self.ToSrcRelPath(self.args.path[0]) + self.sep |
| 1251 for output in out.splitlines(): |
| 1252 build_output = output.replace(build_dir, '') |
| 1253 if build_output in targets: |
| 1254 matching_targets.add(build_output) |
| 1245 | 1255 |
| 1246 err, gn_inp['test_targets'] = self.MapTargetsToLabels( | 1256 cmd = self.GNCmd('refs', |
| 1247 isolate_map, inp['test_targets']) | 1257 self.args.path[0], |
| 1248 if err: | 1258 '@%s' % response_file.name, |
| 1249 raise MBErr(err) | 1259 '--all') |
| 1250 labels_to_targets = {} | 1260 ret, out, _ = self.Run(cmd, force_verbose=False) |
| 1251 for i, label in enumerate(gn_inp['test_targets']): | 1261 if ret and not 'The input matches no targets' in out: |
| 1252 labels_to_targets[label] = inp['test_targets'][i] | 1262 self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), |
| 1263 output_path) |
| 1264 for label in out.splitlines(): |
| 1265 build_target = label[2:] |
| 1266 # We want to accept 'chrome/android:chrome_public_apk' and |
| 1267 # just 'chrome_public_apk'. This may result in too many targets |
| 1268 # getting built, but we can adjust that later if need be. |
| 1269 for input_target in targets: |
| 1270 if (input_target == build_target or |
| 1271 build_target.endswith(':' + input_target)): |
| 1272 matching_targets.add(input_target) |
| 1273 finally: |
| 1274 self.RemoveFile(response_file.name) |
| 1253 | 1275 |
| 1254 try: | 1276 if matching_targets: |
| 1255 self.WriteJSON(gn_inp, gn_input_path) | 1277 self.WriteJSON({ |
| 1256 cmd = self.GNCmd('analyze', build_path, gn_input_path, gn_output_path) | 1278 'status': 'Found dependency', |
| 1257 ret, _, _ = self.Run(cmd, force_verbose=True) | 1279 'compile_targets': sorted(matching_targets), |
| 1258 if ret: | 1280 'test_targets': sorted(matching_targets & |
| 1259 return ret | 1281 set(inp['test_targets'])), |
| 1282 }, output_path) |
| 1283 else: |
| 1284 self.WriteJSON({ |
| 1285 'status': 'No dependency', |
| 1286 'compile_targets': [], |
| 1287 'test_targets': [], |
| 1288 }, output_path) |
| 1260 | 1289 |
| 1261 gn_outp_str = self.ReadFile(gn_output_path) | 1290 if self.args.verbose: |
| 1262 try: | 1291 outp = json.loads(self.ReadFile(output_path)) |
| 1263 gn_outp = json.loads(gn_outp_str) | 1292 self.Print() |
| 1264 except Exception as e: | 1293 self.Print('analyze output:') |
| 1265 self.Print("Failed to parse the JSON string GN returned: %s\n%s" | 1294 self.PrintJSON(outp) |
| 1266 % (repr(gn_outp_str), str(e))) | 1295 self.Print() |
| 1267 raise | |
| 1268 | |
| 1269 outp = {} | |
| 1270 if 'status' in gn_outp: | |
| 1271 outp['status'] = gn_outp['status'] | |
| 1272 if 'error' in gn_outp: | |
| 1273 outp['error'] = gn_outp['error'] | |
| 1274 if 'invalid_targets' in gn_outp: | |
| 1275 outp['invalid_targets'] = gn_outp['invalid_targets'] | |
| 1276 if 'compile_targets' in gn_outp: | |
| 1277 outp['compile_targets'] = [ | |
| 1278 label.replace('//', '') for label in gn_outp['compile_targets']] | |
| 1279 if 'test_targets' in gn_outp: | |
| 1280 outp['test_targets'] = [ | |
| 1281 labels_to_targets[label] for label in gn_outp['test_targets']] | |
| 1282 | |
| 1283 if self.args.verbose: | |
| 1284 self.Print() | |
| 1285 self.Print('analyze output:') | |
| 1286 self.PrintJSON(outp) | |
| 1287 self.Print() | |
| 1288 | |
| 1289 self.WriteJSON(outp, output_path) | |
| 1290 | |
| 1291 finally: | |
| 1292 if self.Exists(gn_input_path): | |
| 1293 self.RemoveFile(gn_input_path) | |
| 1294 if self.Exists(gn_output_path): | |
| 1295 self.RemoveFile(gn_output_path) | |
| 1296 | 1296 |
| 1297 return 0 | 1297 return 0 |
| 1298 | 1298 |
| 1299 def ReadInputJSON(self, required_keys): | 1299 def ReadInputJSON(self, required_keys): |
| 1300 path = self.args.input_path[0] | 1300 path = self.args.input_path[0] |
| 1301 output_path = self.args.output_path[0] | 1301 output_path = self.args.output_path[0] |
| 1302 if not self.Exists(path): | 1302 if not self.Exists(path): |
| 1303 self.WriteFailureAndRaise('"%s" does not exist' % path, output_path) | 1303 self.WriteFailureAndRaise('"%s" does not exist' % path, output_path) |
| 1304 | 1304 |
| 1305 try: | 1305 try: |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1368 print_env('GYP_LINK_CONCURRENCY') | 1368 print_env('GYP_LINK_CONCURRENCY') |
| 1369 print_env('LLVM_FORCE_HEAD_REVISION') | 1369 print_env('LLVM_FORCE_HEAD_REVISION') |
| 1370 | 1370 |
| 1371 if cmd[0] == self.executable: | 1371 if cmd[0] == self.executable: |
| 1372 cmd = ['python'] + cmd[1:] | 1372 cmd = ['python'] + cmd[1:] |
| 1373 self.Print(*[shell_quoter(arg) for arg in cmd]) | 1373 self.Print(*[shell_quoter(arg) for arg in cmd]) |
| 1374 | 1374 |
| 1375 def PrintJSON(self, obj): | 1375 def PrintJSON(self, obj): |
| 1376 self.Print(json.dumps(obj, indent=2, sort_keys=True)) | 1376 self.Print(json.dumps(obj, indent=2, sort_keys=True)) |
| 1377 | 1377 |
| 1378 def GNTargetName(self, target): |
| 1379 return target |
| 1380 |
| 1378 def Build(self, target): | 1381 def Build(self, target): |
| 1379 build_dir = self.ToSrcRelPath(self.args.path[0]) | 1382 build_dir = self.ToSrcRelPath(self.args.path[0]) |
| 1380 ninja_cmd = ['ninja', '-C', build_dir] | 1383 ninja_cmd = ['ninja', '-C', build_dir] |
| 1381 if self.args.jobs: | 1384 if self.args.jobs: |
| 1382 ninja_cmd.extend(['-j', '%d' % self.args.jobs]) | 1385 ninja_cmd.extend(['-j', '%d' % self.args.jobs]) |
| 1383 ninja_cmd.append(target) | 1386 ninja_cmd.append(target) |
| 1384 ret, _, _ = self.Run(ninja_cmd, force_verbose=False, buffer_output=False) | 1387 ret, _, _ = self.Run(ninja_cmd, force_verbose=False, buffer_output=False) |
| 1385 return ret | 1388 return ret |
| 1386 | 1389 |
| 1387 def Run(self, cmd, env=None, force_verbose=True, buffer_output=True): | 1390 def Run(self, cmd, env=None, force_verbose=True, buffer_output=True): |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1510 # Then check to see if the arg contains any metacharacters other than | 1513 # Then check to see if the arg contains any metacharacters other than |
| 1511 # double quotes; if it does, quote everything (including the double | 1514 # double quotes; if it does, quote everything (including the double |
| 1512 # quotes) for safety. | 1515 # quotes) for safety. |
| 1513 if any(a in UNSAFE_FOR_CMD for a in arg): | 1516 if any(a in UNSAFE_FOR_CMD for a in arg): |
| 1514 arg = ''.join('^' + a if a in ALL_META_CHARS else a for a in arg) | 1517 arg = ''.join('^' + a if a in ALL_META_CHARS else a for a in arg) |
| 1515 return arg | 1518 return arg |
| 1516 | 1519 |
| 1517 | 1520 |
| 1518 if __name__ == '__main__': | 1521 if __name__ == '__main__': |
| 1519 sys.exit(main(sys.argv[1:])) | 1522 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |