| 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 551 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 562 | 562 |
| 563 def Lookup(self): | 563 def Lookup(self): |
| 564 vals = self.ReadBotConfig() | 564 vals = self.ReadBotConfig() |
| 565 if not vals: | 565 if not vals: |
| 566 self.ReadConfigFile() | 566 self.ReadConfigFile() |
| 567 config = self.ConfigFromArgs() | 567 config = self.ConfigFromArgs() |
| 568 if config.startswith('//'): | 568 if config.startswith('//'): |
| 569 if not self.Exists(self.ToAbsPath(config)): | 569 if not self.Exists(self.ToAbsPath(config)): |
| 570 raise MBErr('args file "%s" not found' % config) | 570 raise MBErr('args file "%s" not found' % config) |
| 571 vals = { | 571 vals = { |
| 572 'args_file': config, |
| 573 'cros_passthrough': False, |
| 574 'gn_args': '', |
| 575 'gyp_crosscompile': False, |
| 576 'gyp_defines': '', |
| 572 'type': 'gn', | 577 'type': 'gn', |
| 573 'args_file': config, | |
| 574 'gn_args': '', | |
| 575 } | 578 } |
| 576 else: | 579 else: |
| 577 if not config in self.configs: | 580 if not config in self.configs: |
| 578 raise MBErr('Config "%s" not found in %s' % | 581 raise MBErr('Config "%s" not found in %s' % |
| 579 (config, self.args.config_file)) | 582 (config, self.args.config_file)) |
| 580 vals = self.FlattenConfig(config) | 583 vals = self.FlattenConfig(config) |
| 581 | 584 |
| 582 # Do some basic sanity checking on the config so that we | 585 # Do some basic sanity checking on the config so that we |
| 583 # don't have to do this in every caller. | 586 # don't have to do this in every caller. |
| 584 assert 'type' in vals, 'No meta-build type specified in the config' | 587 assert 'type' in vals, 'No meta-build type specified in the config' |
| (...skipping 12 matching lines...) Expand all Loading... |
| 597 | 600 |
| 598 contents = json.loads(self.ReadFile(path)) | 601 contents = json.loads(self.ReadFile(path)) |
| 599 gyp_vals = contents.get('GYP_DEFINES', {}) | 602 gyp_vals = contents.get('GYP_DEFINES', {}) |
| 600 if isinstance(gyp_vals, dict): | 603 if isinstance(gyp_vals, dict): |
| 601 gyp_defines = ' '.join('%s=%s' % (k, v) for k, v in gyp_vals.items()) | 604 gyp_defines = ' '.join('%s=%s' % (k, v) for k, v in gyp_vals.items()) |
| 602 else: | 605 else: |
| 603 gyp_defines = ' '.join(gyp_vals) | 606 gyp_defines = ' '.join(gyp_vals) |
| 604 gn_args = ' '.join(contents.get('gn_args', [])) | 607 gn_args = ' '.join(contents.get('gn_args', [])) |
| 605 | 608 |
| 606 return { | 609 return { |
| 610 'args_file': '', |
| 611 'cros_passthrough': False, |
| 612 'gn_args': gn_args, |
| 613 'gyp_crosscompile': False, |
| 614 'gyp_defines': gyp_defines, |
| 607 'type': contents.get('mb_type', ''), | 615 'type': contents.get('mb_type', ''), |
| 608 'gn_args': gn_args, | |
| 609 'gyp_defines': gyp_defines, | |
| 610 'gyp_crosscompile': False, | |
| 611 } | 616 } |
| 612 | 617 |
| 613 def ReadConfigFile(self): | 618 def ReadConfigFile(self): |
| 614 if not self.Exists(self.args.config_file): | 619 if not self.Exists(self.args.config_file): |
| 615 raise MBErr('config file not found at %s' % self.args.config_file) | 620 raise MBErr('config file not found at %s' % self.args.config_file) |
| 616 | 621 |
| 617 try: | 622 try: |
| 618 contents = ast.literal_eval(self.ReadFile(self.args.config_file)) | 623 contents = ast.literal_eval(self.ReadFile(self.args.config_file)) |
| 619 except SyntaxError as e: | 624 except SyntaxError as e: |
| 620 raise MBErr('Failed to parse config file "%s": %s' % | 625 raise MBErr('Failed to parse config file "%s": %s' % |
| (...skipping 20 matching lines...) Expand all Loading... |
| 641 (self.args.master, self.args.config_file)) | 646 (self.args.master, self.args.config_file)) |
| 642 | 647 |
| 643 if not self.args.builder in self.masters[self.args.master]: | 648 if not self.args.builder in self.masters[self.args.master]: |
| 644 raise MBErr('Builder name "%s" not found under masters[%s] in "%s"' % | 649 raise MBErr('Builder name "%s" not found under masters[%s] in "%s"' % |
| 645 (self.args.builder, self.args.master, self.args.config_file)) | 650 (self.args.builder, self.args.master, self.args.config_file)) |
| 646 | 651 |
| 647 return self.masters[self.args.master][self.args.builder] | 652 return self.masters[self.args.master][self.args.builder] |
| 648 | 653 |
| 649 def FlattenConfig(self, config): | 654 def FlattenConfig(self, config): |
| 650 mixins = self.configs[config] | 655 mixins = self.configs[config] |
| 656 # TODO(dpranke): We really should provide a constructor for the |
| 657 # default set of values. |
| 651 vals = { | 658 vals = { |
| 652 'type': None, | 659 'args_file': '', |
| 660 'cros_passthrough': False, |
| 653 'gn_args': [], | 661 'gn_args': [], |
| 654 'gyp_defines': '', | 662 'gyp_defines': '', |
| 655 'gyp_crosscompile': False, | 663 'gyp_crosscompile': False, |
| 664 'type': None, |
| 656 } | 665 } |
| 657 | 666 |
| 658 visited = [] | 667 visited = [] |
| 659 self.FlattenMixins(mixins, vals, visited) | 668 self.FlattenMixins(mixins, vals, visited) |
| 660 return vals | 669 return vals |
| 661 | 670 |
| 662 def FlattenMixins(self, mixins, vals, visited): | 671 def FlattenMixins(self, mixins, vals, visited): |
| 663 for m in mixins: | 672 for m in mixins: |
| 664 if m not in self.mixins: | 673 if m not in self.mixins: |
| 665 raise MBErr('Unknown mixin "%s"' % m) | 674 raise MBErr('Unknown mixin "%s"' % m) |
| 666 | 675 |
| 667 # TODO: check for cycles in mixins. | 676 # TODO: check for cycles in mixins. |
| 668 | 677 |
| 669 visited.append(m) | 678 visited.append(m) |
| 670 | 679 |
| 671 mixin_vals = self.mixins[m] | 680 mixin_vals = self.mixins[m] |
| 672 if 'type' in mixin_vals: | 681 |
| 673 vals['type'] = mixin_vals['type'] | 682 if 'cros_passthrough' in mixin_vals: |
| 683 vals['cros_passthrough'] = mixin_vals['cros_passthrough'] |
| 674 if 'gn_args' in mixin_vals: | 684 if 'gn_args' in mixin_vals: |
| 675 if vals['gn_args']: | 685 if vals['gn_args']: |
| 676 vals['gn_args'] += ' ' + mixin_vals['gn_args'] | 686 vals['gn_args'] += ' ' + mixin_vals['gn_args'] |
| 677 else: | 687 else: |
| 678 vals['gn_args'] = mixin_vals['gn_args'] | 688 vals['gn_args'] = mixin_vals['gn_args'] |
| 679 if 'gyp_crosscompile' in mixin_vals: | 689 if 'gyp_crosscompile' in mixin_vals: |
| 680 vals['gyp_crosscompile'] = mixin_vals['gyp_crosscompile'] | 690 vals['gyp_crosscompile'] = mixin_vals['gyp_crosscompile'] |
| 681 if 'gyp_defines' in mixin_vals: | 691 if 'gyp_defines' in mixin_vals: |
| 682 if vals['gyp_defines']: | 692 if vals['gyp_defines']: |
| 683 vals['gyp_defines'] += ' ' + mixin_vals['gyp_defines'] | 693 vals['gyp_defines'] += ' ' + mixin_vals['gyp_defines'] |
| 684 else: | 694 else: |
| 685 vals['gyp_defines'] = mixin_vals['gyp_defines'] | 695 vals['gyp_defines'] = mixin_vals['gyp_defines'] |
| 696 if 'type' in mixin_vals: |
| 697 vals['type'] = mixin_vals['type'] |
| 698 |
| 686 if 'mixins' in mixin_vals: | 699 if 'mixins' in mixin_vals: |
| 687 self.FlattenMixins(mixin_vals['mixins'], vals, visited) | 700 self.FlattenMixins(mixin_vals['mixins'], vals, visited) |
| 688 return vals | 701 return vals |
| 689 | 702 |
| 690 def ClobberIfNeeded(self, vals): | 703 def ClobberIfNeeded(self, vals): |
| 691 path = self.args.path[0] | 704 path = self.args.path[0] |
| 692 build_dir = self.ToAbsPath(path) | 705 build_dir = self.ToAbsPath(path) |
| 693 mb_type_path = self.PathJoin(build_dir, 'mb_type') | 706 mb_type_path = self.PathJoin(build_dir, 'mb_type') |
| 694 needs_clobber = False | 707 needs_clobber = False |
| 695 new_mb_type = vals['type'] | 708 new_mb_type = vals['type'] |
| (...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 880 elif self.platform == 'darwin': | 893 elif self.platform == 'darwin': |
| 881 subdir, exe = 'mac', 'gn' | 894 subdir, exe = 'mac', 'gn' |
| 882 else: | 895 else: |
| 883 subdir, exe = 'win', 'gn.exe' | 896 subdir, exe = 'win', 'gn.exe' |
| 884 | 897 |
| 885 gn_path = self.PathJoin(self.chromium_src_dir, 'buildtools', subdir, exe) | 898 gn_path = self.PathJoin(self.chromium_src_dir, 'buildtools', subdir, exe) |
| 886 | 899 |
| 887 return [gn_path, subcommand, path] + list(args) | 900 return [gn_path, subcommand, path] + list(args) |
| 888 | 901 |
| 889 def GNArgs(self, vals): | 902 def GNArgs(self, vals): |
| 890 gn_args = vals['gn_args'] | 903 if vals['cros_passthrough']: |
| 904 if not 'GN_ARGS' in os.environ: |
| 905 raise MBErr('MB is expecting GN_ARGS to be in the environment') |
| 906 gn_args = os.environ['GN_ARGS'] |
| 907 if not 'target_os="chromeos"' in gn_args: |
| 908 raise MBErr('GN_ARGS is missing target_os="chromeos": (GN_ARGS=%s)' % |
| 909 gn_args) |
| 910 else: |
| 911 gn_args = vals['gn_args'] |
| 912 |
| 891 if self.args.goma_dir: | 913 if self.args.goma_dir: |
| 892 gn_args += ' goma_dir="%s"' % self.args.goma_dir | 914 gn_args += ' goma_dir="%s"' % self.args.goma_dir |
| 893 | 915 |
| 894 android_version_code = self.args.android_version_code | 916 android_version_code = self.args.android_version_code |
| 895 if android_version_code: | 917 if android_version_code: |
| 896 gn_args += ' android_default_version_code="%s"' % android_version_code | 918 gn_args += ' android_default_version_code="%s"' % android_version_code |
| 897 | 919 |
| 898 android_version_name = self.args.android_version_name | 920 android_version_name = self.args.android_version_name |
| 899 if android_version_name: | 921 if android_version_name: |
| 900 gn_args += ' android_default_version_name="%s"' % android_version_name | 922 gn_args += ' android_default_version_name="%s"' % android_version_name |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1042 if path.startswith('//'): | 1064 if path.startswith('//'): |
| 1043 return path[2:].replace('/', self.sep) | 1065 return path[2:].replace('/', self.sep) |
| 1044 return self.RelPath(path, self.chromium_src_dir) | 1066 return self.RelPath(path, self.chromium_src_dir) |
| 1045 | 1067 |
| 1046 def ParseGYPConfigPath(self, path): | 1068 def ParseGYPConfigPath(self, path): |
| 1047 rpath = self.ToSrcRelPath(path) | 1069 rpath = self.ToSrcRelPath(path) |
| 1048 output_dir, _, _ = rpath.rpartition(self.sep) | 1070 output_dir, _, _ = rpath.rpartition(self.sep) |
| 1049 return output_dir | 1071 return output_dir |
| 1050 | 1072 |
| 1051 def GYPCmd(self, output_dir, vals): | 1073 def GYPCmd(self, output_dir, vals): |
| 1052 gyp_defines = vals['gyp_defines'] | 1074 if vals['cros_passthrough']: |
| 1075 if not 'GYP_DEFINES' in os.environ: |
| 1076 raise MBErr('MB is expecting GYP_DEFINES to be in the environment') |
| 1077 gyp_defines = os.environ['GYP_DEFINES'] |
| 1078 if not 'chromeos=1' in gyp_defines: |
| 1079 raise MBErr('GYP_DEFINES is missing chromeos=1: (GYP_DEFINES=%s)' % |
| 1080 gyp_defines) |
| 1081 else: |
| 1082 gyp_defines = vals['gyp_defines'] |
| 1083 |
| 1053 goma_dir = self.args.goma_dir | 1084 goma_dir = self.args.goma_dir |
| 1054 | 1085 |
| 1055 # GYP uses shlex.split() to split the gyp defines into separate arguments, | 1086 # GYP uses shlex.split() to split the gyp defines into separate arguments, |
| 1056 # so we can support backslashes and and spaces in arguments by quoting | 1087 # so we can support backslashes and and spaces in arguments by quoting |
| 1057 # them, even on Windows, where this normally wouldn't work. | 1088 # them, even on Windows, where this normally wouldn't work. |
| 1058 if goma_dir and ('\\' in goma_dir or ' ' in goma_dir): | 1089 if goma_dir and ('\\' in goma_dir or ' ' in goma_dir): |
| 1059 goma_dir = "'%s'" % goma_dir | 1090 goma_dir = "'%s'" % goma_dir |
| 1060 | 1091 |
| 1061 if goma_dir: | 1092 if goma_dir: |
| 1062 gyp_defines += ' gomadir=%s' % goma_dir | 1093 gyp_defines += ' gomadir=%s' % goma_dir |
| (...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1417 # Then check to see if the arg contains any metacharacters other than | 1448 # Then check to see if the arg contains any metacharacters other than |
| 1418 # double quotes; if it does, quote everything (including the double | 1449 # double quotes; if it does, quote everything (including the double |
| 1419 # quotes) for safety. | 1450 # quotes) for safety. |
| 1420 if any(a in UNSAFE_FOR_CMD for a in arg): | 1451 if any(a in UNSAFE_FOR_CMD for a in arg): |
| 1421 arg = ''.join('^' + a if a in ALL_META_CHARS else a for a in arg) | 1452 arg = ''.join('^' + a if a in ALL_META_CHARS else a for a in arg) |
| 1422 return arg | 1453 return arg |
| 1423 | 1454 |
| 1424 | 1455 |
| 1425 if __name__ == '__main__': | 1456 if __name__ == '__main__': |
| 1426 sys.exit(main(sys.argv[1:])) | 1457 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |