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 """ |
11 | 11 |
12 from __future__ import print_function | 12 from __future__ import print_function |
13 | 13 |
14 import argparse | 14 import argparse |
15 import ast | 15 import ast |
16 import errno | 16 import errno |
17 import json | 17 import json |
18 import os | 18 import os |
19 import pipes | 19 import pipes |
20 import pprint | 20 import pprint |
21 import re | 21 import re |
22 import shutil | 22 import shutil |
23 import sys | 23 import sys |
24 import subprocess | 24 import subprocess |
25 import tempfile | 25 import tempfile |
26 import urllib2 | 26 import urllib2 |
27 | 27 |
28 from collections import OrderedDict | 28 from collections import OrderedDict |
29 | 29 |
| 30 CHROMIUM_SRC_DIR = os.path.dirname(os.path.dirname(os.path.dirname( |
| 31 os.path.abspath(__file__)))) |
| 32 sys.path = [os.path.join(CHROMIUM_SRC_DIR, 'build')] + sys.path |
| 33 |
| 34 import gn_helpers |
| 35 |
| 36 |
30 def main(args): | 37 def main(args): |
31 mbw = MetaBuildWrapper() | 38 mbw = MetaBuildWrapper() |
32 return mbw.Main(args) | 39 return mbw.Main(args) |
33 | 40 |
34 | 41 |
35 class MetaBuildWrapper(object): | 42 class MetaBuildWrapper(object): |
36 def __init__(self): | 43 def __init__(self): |
37 p = os.path | 44 self.chromium_src_dir = CHROMIUM_SRC_DIR |
38 d = os.path.dirname | 45 self.default_config = os.path.join(self.chromium_src_dir, 'tools', 'mb', |
39 self.chromium_src_dir = p.normpath(d(d(d(p.abspath(__file__))))) | 46 'mb_config.pyl') |
40 self.default_config = p.join(self.chromium_src_dir, 'tools', 'mb', | |
41 'mb_config.pyl') | |
42 self.executable = sys.executable | 47 self.executable = sys.executable |
43 self.platform = sys.platform | 48 self.platform = sys.platform |
44 self.sep = os.sep | 49 self.sep = os.sep |
45 self.args = argparse.Namespace() | 50 self.args = argparse.Namespace() |
46 self.configs = {} | 51 self.configs = {} |
47 self.masters = {} | 52 self.masters = {} |
48 self.mixins = {} | 53 self.mixins = {} |
49 | 54 |
50 def Main(self, args): | 55 def Main(self, args): |
51 self.ParseArgs(args) | 56 self.ParseArgs(args) |
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
248 return 1 | 253 return 1 |
249 | 254 |
250 if vals['type'] == 'gn': | 255 if vals['type'] == 'gn': |
251 return self.RunGNIsolate(vals) | 256 return self.RunGNIsolate(vals) |
252 else: | 257 else: |
253 return self.Build('%s_run' % self.args.target[0]) | 258 return self.Build('%s_run' % self.args.target[0]) |
254 | 259 |
255 def CmdLookup(self): | 260 def CmdLookup(self): |
256 vals = self.Lookup() | 261 vals = self.Lookup() |
257 if vals['type'] == 'gn': | 262 if vals['type'] == 'gn': |
258 cmd = self.GNCmd('gen', '_path_', vals['gn_args']) | 263 cmd = self.GNCmd('gen', '_path_') |
| 264 gn_args = self.GNArgs(vals) |
| 265 self.Print('\nWriting """\\\n%s""" to _path_/args.gn.\n' % gn_args) |
259 env = None | 266 env = None |
260 else: | 267 else: |
261 cmd, env = self.GYPCmd('_path_', vals) | 268 cmd, env = self.GYPCmd('_path_', vals) |
262 | 269 |
263 self.PrintCmd(cmd, env) | 270 self.PrintCmd(cmd, env) |
264 return 0 | 271 return 0 |
265 | 272 |
266 def CmdRun(self): | 273 def CmdRun(self): |
267 vals = self.GetConfig() | 274 vals = self.GetConfig() |
268 if not vals: | 275 if not vals: |
(...skipping 414 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
683 | 690 |
684 if needs_clobber: | 691 if needs_clobber: |
685 self.RemoveDirectory(build_dir) | 692 self.RemoveDirectory(build_dir) |
686 | 693 |
687 self.MaybeMakeDirectory(build_dir) | 694 self.MaybeMakeDirectory(build_dir) |
688 self.WriteFile(mb_type_path, new_mb_type) | 695 self.WriteFile(mb_type_path, new_mb_type) |
689 | 696 |
690 def RunGNGen(self, vals): | 697 def RunGNGen(self, vals): |
691 build_dir = self.args.path[0] | 698 build_dir = self.args.path[0] |
692 | 699 |
693 cmd = self.GNCmd('gen', build_dir, vals['gn_args'], extra_args=['--check']) | 700 cmd = self.GNCmd('gen', build_dir, '--check') |
| 701 gn_args = self.GNArgs(vals) |
| 702 |
| 703 # Since GN hasn't run yet, the build directory may not even exist. |
| 704 self.MaybeMakeDirectory(self.ToAbsPath(build_dir)) |
| 705 |
| 706 gn_args_path = self.ToAbsPath(build_dir, 'args.gn') |
| 707 self.WriteFile(gn_args_path, gn_args) |
694 | 708 |
695 swarming_targets = [] | 709 swarming_targets = [] |
696 if getattr(self.args, 'swarming_targets_file', None): | 710 if getattr(self.args, 'swarming_targets_file', None): |
697 # We need GN to generate the list of runtime dependencies for | 711 # We need GN to generate the list of runtime dependencies for |
698 # the compile targets listed (one per line) in the file so | 712 # the compile targets listed (one per line) in the file so |
699 # we can run them via swarming. We use ninja_to_gn.pyl to convert | 713 # we can run them via swarming. We use ninja_to_gn.pyl to convert |
700 # the compile targets to the matching GN labels. | 714 # the compile targets to the matching GN labels. |
701 path = self.args.swarming_targets_file | 715 path = self.args.swarming_targets_file |
702 if not self.Exists(path): | 716 if not self.Exists(path): |
703 self.WriteFailureAndRaise('"%s" does not exist' % path, | 717 self.WriteFailureAndRaise('"%s" does not exist' % path, |
(...skipping 11 matching lines...) Expand all Loading... |
715 elif gn_isolate_map[target_name]['type'] == 'unknown': | 729 elif gn_isolate_map[target_name]['type'] == 'unknown': |
716 err += ('test target "%s" type is unknown\n' % target_name) | 730 err += ('test target "%s" type is unknown\n' % target_name) |
717 else: | 731 else: |
718 gn_labels.append(gn_isolate_map[target_name]['label']) | 732 gn_labels.append(gn_isolate_map[target_name]['label']) |
719 | 733 |
720 if err: | 734 if err: |
721 raise MBErr('Error: Failed to match swarming targets to %s:\n%s' % | 735 raise MBErr('Error: Failed to match swarming targets to %s:\n%s' % |
722 ('//testing/buildbot/gn_isolate_map.pyl', err)) | 736 ('//testing/buildbot/gn_isolate_map.pyl', err)) |
723 | 737 |
724 gn_runtime_deps_path = self.ToAbsPath(build_dir, 'runtime_deps') | 738 gn_runtime_deps_path = self.ToAbsPath(build_dir, 'runtime_deps') |
725 | |
726 # Since GN hasn't run yet, the build directory may not even exist. | |
727 self.MaybeMakeDirectory(self.ToAbsPath(build_dir)) | |
728 | |
729 self.WriteFile(gn_runtime_deps_path, '\n'.join(gn_labels) + '\n') | 739 self.WriteFile(gn_runtime_deps_path, '\n'.join(gn_labels) + '\n') |
730 cmd.append('--runtime-deps-list-file=%s' % gn_runtime_deps_path) | 740 cmd.append('--runtime-deps-list-file=%s' % gn_runtime_deps_path) |
731 | 741 |
732 ret, _, _ = self.Run(cmd) | 742 ret, _, _ = self.Run(cmd) |
733 if ret: | 743 if ret: |
734 # If `gn gen` failed, we should exit early rather than trying to | 744 # If `gn gen` failed, we should exit early rather than trying to |
735 # generate isolates. Run() will have already logged any error output. | 745 # generate isolates. Run() will have already logged any error output. |
736 self.Print('GN gen failed: %d' % ret) | 746 self.Print('GN gen failed: %d' % ret) |
737 return ret | 747 return ret |
738 | 748 |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
785 def RunGNIsolate(self, vals): | 795 def RunGNIsolate(self, vals): |
786 gn_isolate_map = ast.literal_eval(self.ReadFile(self.PathJoin( | 796 gn_isolate_map = ast.literal_eval(self.ReadFile(self.PathJoin( |
787 self.chromium_src_dir, 'testing', 'buildbot', 'gn_isolate_map.pyl'))) | 797 self.chromium_src_dir, 'testing', 'buildbot', 'gn_isolate_map.pyl'))) |
788 | 798 |
789 build_dir = self.args.path[0] | 799 build_dir = self.args.path[0] |
790 target = self.args.target[0] | 800 target = self.args.target[0] |
791 target_name = self.GNTargetName(target) | 801 target_name = self.GNTargetName(target) |
792 command, extra_files = self.GetIsolateCommand(target, vals, gn_isolate_map) | 802 command, extra_files = self.GetIsolateCommand(target, vals, gn_isolate_map) |
793 | 803 |
794 label = gn_isolate_map[target_name]['label'] | 804 label = gn_isolate_map[target_name]['label'] |
795 cmd = self.GNCmd('desc', build_dir, extra_args=[label, 'runtime_deps']) | 805 cmd = self.GNCmd('desc', build_dir, label, 'runtime_deps') |
796 ret, out, _ = self.Call(cmd) | 806 ret, out, _ = self.Call(cmd) |
797 if ret: | 807 if ret: |
798 if out: | 808 if out: |
799 self.Print(out) | 809 self.Print(out) |
800 return ret | 810 return ret |
801 | 811 |
802 runtime_deps = out.splitlines() | 812 runtime_deps = out.splitlines() |
803 | 813 |
804 self.WriteIsolateFiles(build_dir, command, target, runtime_deps, | 814 self.WriteIsolateFiles(build_dir, command, target, runtime_deps, |
805 extra_files) | 815 extra_files) |
(...skipping 28 matching lines...) Expand all Loading... |
834 self.ToSrcRelPath('%s/%s.isolated' % (build_dir, target)), | 844 self.ToSrcRelPath('%s/%s.isolated' % (build_dir, target)), |
835 '--isolate', | 845 '--isolate', |
836 self.ToSrcRelPath('%s/%s.isolate' % (build_dir, target)), | 846 self.ToSrcRelPath('%s/%s.isolate' % (build_dir, target)), |
837 ], | 847 ], |
838 'dir': self.chromium_src_dir, | 848 'dir': self.chromium_src_dir, |
839 'version': 1, | 849 'version': 1, |
840 }, | 850 }, |
841 isolate_path + 'd.gen.json', | 851 isolate_path + 'd.gen.json', |
842 ) | 852 ) |
843 | 853 |
844 def GNCmd(self, subcommand, path, gn_args='', extra_args=None): | 854 def GNCmd(self, subcommand, path, *args): |
845 if self.platform == 'linux2': | 855 if self.platform == 'linux2': |
846 subdir, exe = 'linux64', 'gn' | 856 subdir, exe = 'linux64', 'gn' |
847 elif self.platform == 'darwin': | 857 elif self.platform == 'darwin': |
848 subdir, exe = 'mac', 'gn' | 858 subdir, exe = 'mac', 'gn' |
849 else: | 859 else: |
850 subdir, exe = 'win', 'gn.exe' | 860 subdir, exe = 'win', 'gn.exe' |
| 861 |
851 gn_path = self.PathJoin(self.chromium_src_dir, 'buildtools', subdir, exe) | 862 gn_path = self.PathJoin(self.chromium_src_dir, 'buildtools', subdir, exe) |
852 | 863 |
853 cmd = [gn_path, subcommand, path] | 864 return [gn_path, subcommand, path] + list(args) |
| 865 |
| 866 def GNArgs(self, vals): |
| 867 gn_args = vals['gn_args'] |
854 if self.args.goma_dir: | 868 if self.args.goma_dir: |
855 gn_args += ' goma_dir="%s"' % self.args.goma_dir | 869 gn_args += ' goma_dir="%s"' % self.args.goma_dir |
856 if gn_args: | 870 |
857 cmd.append('--args=%s' % gn_args) | 871 # Canonicalize the arg string into a sorted, newline-separated list |
858 if extra_args: | 872 # of key-value pairs, and de-dup the keys if need be so that only |
859 cmd.extend(extra_args) | 873 # the last instance of each arg is listed. |
860 return cmd | 874 gn_args = gn_helpers.ToGNString(gn_helpers.FromGNArgs(gn_args)) |
| 875 |
| 876 return gn_args |
861 | 877 |
862 def RunGYPGen(self, vals): | 878 def RunGYPGen(self, vals): |
863 path = self.args.path[0] | 879 path = self.args.path[0] |
864 | 880 |
865 output_dir = self.ParseGYPConfigPath(path) | 881 output_dir = self.ParseGYPConfigPath(path) |
866 cmd, env = self.GYPCmd(output_dir, vals) | 882 cmd, env = self.GYPCmd(output_dir, vals) |
867 ret, _, _ = self.Run(cmd, env=env) | 883 ret, _, _ = self.Run(cmd, env=env) |
868 return ret | 884 return ret |
869 | 885 |
870 def RunGYPAnalyze(self, vals): | 886 def RunGYPAnalyze(self, vals): |
(...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1079 }, output_path) | 1095 }, output_path) |
1080 return 0 | 1096 return 0 |
1081 | 1097 |
1082 ret = 0 | 1098 ret = 0 |
1083 response_file = self.TempFile() | 1099 response_file = self.TempFile() |
1084 response_file.write('\n'.join(inp['files']) + '\n') | 1100 response_file.write('\n'.join(inp['files']) + '\n') |
1085 response_file.close() | 1101 response_file.close() |
1086 | 1102 |
1087 matching_targets = set() | 1103 matching_targets = set() |
1088 try: | 1104 try: |
1089 cmd = self.GNCmd('refs', self.args.path[0]) + [ | 1105 cmd = self.GNCmd('refs', |
1090 '@%s' % response_file.name, '--all', '--as=output'] | 1106 self.args.path[0], |
| 1107 '@%s' % response_file.name, |
| 1108 '--all', |
| 1109 '--as=output') |
1091 ret, out, _ = self.Run(cmd, force_verbose=False) | 1110 ret, out, _ = self.Run(cmd, force_verbose=False) |
1092 if ret and not 'The input matches no targets' in out: | 1111 if ret and not 'The input matches no targets' in out: |
1093 self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), | 1112 self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), |
1094 output_path) | 1113 output_path) |
1095 build_dir = self.ToSrcRelPath(self.args.path[0]) + self.sep | 1114 build_dir = self.ToSrcRelPath(self.args.path[0]) + self.sep |
1096 for output in out.splitlines(): | 1115 for output in out.splitlines(): |
1097 build_output = output.replace(build_dir, '') | 1116 build_output = output.replace(build_dir, '') |
1098 if build_output in targets: | 1117 if build_output in targets: |
1099 matching_targets.add(build_output) | 1118 matching_targets.add(build_output) |
1100 | 1119 |
1101 cmd = self.GNCmd('refs', self.args.path[0]) + [ | 1120 cmd = self.GNCmd('refs', |
1102 '@%s' % response_file.name, '--all'] | 1121 self.args.path[0], |
| 1122 '@%s' % response_file.name, |
| 1123 '--all') |
1103 ret, out, _ = self.Run(cmd, force_verbose=False) | 1124 ret, out, _ = self.Run(cmd, force_verbose=False) |
1104 if ret and not 'The input matches no targets' in out: | 1125 if ret and not 'The input matches no targets' in out: |
1105 self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), | 1126 self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), |
1106 output_path) | 1127 output_path) |
1107 for label in out.splitlines(): | 1128 for label in out.splitlines(): |
1108 build_target = label[2:] | 1129 build_target = label[2:] |
1109 # We want to accept 'chrome/android:chrome_public_apk' and | 1130 # We want to accept 'chrome/android:chrome_public_apk' and |
1110 # just 'chrome_public_apk'. This may result in too many targets | 1131 # just 'chrome_public_apk'. This may result in too many targets |
1111 # getting built, but we can adjust that later if need be. | 1132 # getting built, but we can adjust that later if need be. |
1112 for input_target in targets: | 1133 for input_target in targets: |
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1354 # Then check to see if the arg contains any metacharacters other than | 1375 # Then check to see if the arg contains any metacharacters other than |
1355 # double quotes; if it does, quote everything (including the double | 1376 # double quotes; if it does, quote everything (including the double |
1356 # quotes) for safety. | 1377 # quotes) for safety. |
1357 if any(a in UNSAFE_FOR_CMD for a in arg): | 1378 if any(a in UNSAFE_FOR_CMD for a in arg): |
1358 arg = ''.join('^' + a if a in ALL_META_CHARS else a for a in arg) | 1379 arg = ''.join('^' + a if a in ALL_META_CHARS else a for a in arg) |
1359 return arg | 1380 return arg |
1360 | 1381 |
1361 | 1382 |
1362 if __name__ == '__main__': | 1383 if __name__ == '__main__': |
1363 sys.exit(main(sys.argv[1:])) | 1384 sys.exit(main(sys.argv[1:])) |
OLD | NEW |