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