| 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 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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): |
| 75 subp.add_argument('-b', '--builder', | 75 subp.add_argument('-b', '--builder', |
| 76 help='builder name to look up config from') | 76 help='builder name to look up config from') |
| 77 subp.add_argument('-m', '--master', | 77 subp.add_argument('-m', '--master', |
| 78 help='master name to look up config from') | 78 help='master name to look up config from') |
| 79 subp.add_argument('-c', '--config', | 79 subp.add_argument('-c', '--config', |
| 80 help='configuration to analyze') | 80 help='configuration to analyze') |
| 81 subp.add_argument('--phase', type=int, |
| 82 help=('build phase for a given build ' |
| 83 '(int in [1, 2, ...))')) |
| 81 subp.add_argument('-f', '--config-file', metavar='PATH', | 84 subp.add_argument('-f', '--config-file', metavar='PATH', |
| 82 default=self.default_config, | 85 default=self.default_config, |
| 83 help='path to config file ' | 86 help='path to config file ' |
| 84 '(default is //tools/mb/mb_config.pyl)') | 87 '(default is //tools/mb/mb_config.pyl)') |
| 85 subp.add_argument('-g', '--goma-dir', | 88 subp.add_argument('-g', '--goma-dir', |
| 86 help='path to goma directory') | 89 help='path to goma directory') |
| 87 subp.add_argument('--gyp-script', metavar='PATH', | 90 subp.add_argument('--gyp-script', metavar='PATH', |
| 88 default=self.PathJoin('build', 'gyp_chromium'), | 91 default=self.PathJoin('build', 'gyp_chromium'), |
| 89 help='path to gyp script relative to project root ' | 92 help='path to gyp script relative to project root ' |
| 90 '(default is %(default)s)') | 93 '(default is %(default)s)') |
| (...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 321 def CmdValidate(self, print_ok=True): | 324 def CmdValidate(self, print_ok=True): |
| 322 errs = [] | 325 errs = [] |
| 323 | 326 |
| 324 # Read the file to make sure it parses. | 327 # Read the file to make sure it parses. |
| 325 self.ReadConfigFile() | 328 self.ReadConfigFile() |
| 326 | 329 |
| 327 # Build a list of all of the configs referenced by builders. | 330 # Build a list of all of the configs referenced by builders. |
| 328 all_configs = {} | 331 all_configs = {} |
| 329 for master in self.masters: | 332 for master in self.masters: |
| 330 for config in self.masters[master].values(): | 333 for config in self.masters[master].values(): |
| 331 all_configs[config] = master | 334 if isinstance(config, list): |
| 335 for c in config: |
| 336 all_configs[c] = master |
| 337 else: |
| 338 all_configs[config] = master |
| 332 | 339 |
| 333 # Check that every referenced args file or config actually exists. | 340 # Check that every referenced args file or config actually exists. |
| 334 for config, loc in all_configs.items(): | 341 for config, loc in all_configs.items(): |
| 335 if config.startswith('//'): | 342 if config.startswith('//'): |
| 336 if not self.Exists(self.ToAbsPath(config)): | 343 if not self.Exists(self.ToAbsPath(config)): |
| 337 errs.append('Unknown args file "%s" referenced from "%s".' % | 344 errs.append('Unknown args file "%s" referenced from "%s".' % |
| 338 (config, loc)) | 345 (config, loc)) |
| 339 elif not config in self.configs: | 346 elif not config in self.configs: |
| 340 errs.append('Unknown config "%s" referenced from "%s".' % | 347 errs.append('Unknown config "%s" referenced from "%s".' % |
| 341 (config, loc)) | 348 (config, loc)) |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 468 config_only = config_builders - master_builders | 475 config_only = config_builders - master_builders |
| 469 tbd = set() | 476 tbd = set() |
| 470 gyp = set() | 477 gyp = set() |
| 471 done = set() | 478 done = set() |
| 472 notes = {builder: '' for builder in config_builders | master_builders} | 479 notes = {builder: '' for builder in config_builders | master_builders} |
| 473 | 480 |
| 474 for builder in both: | 481 for builder in both: |
| 475 config = self.masters[master][builder] | 482 config = self.masters[master][builder] |
| 476 if config == 'tbd': | 483 if config == 'tbd': |
| 477 tbd.add(builder) | 484 tbd.add(builder) |
| 485 elif isinstance(config, list): |
| 486 vals = self.FlattenConfig(config[0]) |
| 487 if vals['type'] == 'gyp': |
| 488 gyp.add(builder) |
| 489 else: |
| 490 done.add(builder) |
| 478 elif config.startswith('//'): | 491 elif config.startswith('//'): |
| 479 done.add(builder) | 492 done.add(builder) |
| 480 else: | 493 else: |
| 481 # TODO(dpranke): Check if MB is actually running? | |
| 482 vals = self.FlattenConfig(config) | 494 vals = self.FlattenConfig(config) |
| 483 if vals['type'] == 'gyp': | 495 if vals['type'] == 'gyp': |
| 484 gyp.add(builder) | 496 gyp.add(builder) |
| 485 else: | 497 else: |
| 486 done.add(builder) | 498 done.add(builder) |
| 487 | 499 |
| 488 if self.args.check_compile and (tbd or master_only): | 500 if self.args.check_compile and (tbd or master_only): |
| 489 either = tbd | master_only | 501 either = tbd | master_only |
| 490 for builder in either: | 502 for builder in either: |
| 491 notes[builder] = ' (' + self.CheckCompile(master, builder) +')' | 503 notes[builder] = ' (' + self.CheckCompile(master, builder) +')' |
| (...skipping 23 matching lines...) Expand all Loading... |
| 515 | 527 |
| 516 vals = {} | 528 vals = {} |
| 517 if self.args.builder or self.args.master or self.args.config: | 529 if self.args.builder or self.args.master or self.args.config: |
| 518 vals = self.Lookup() | 530 vals = self.Lookup() |
| 519 if vals['type'] == 'gn': | 531 if vals['type'] == 'gn': |
| 520 # Re-run gn gen in order to ensure the config is consistent with the | 532 # Re-run gn gen in order to ensure the config is consistent with the |
| 521 # build dir. | 533 # build dir. |
| 522 self.RunGNGen(vals) | 534 self.RunGNGen(vals) |
| 523 return vals | 535 return vals |
| 524 | 536 |
| 525 # TODO: We can only get the config for GN build dirs, not GYP build dirs. | |
| 526 # GN stores the args that were used in args.gn in the build dir, | |
| 527 # but GYP doesn't store them anywhere. We should consider modifying | |
| 528 # gyp_chromium to record the arguments it runs with in a similar | |
| 529 # manner. | |
| 530 | |
| 531 mb_type_path = self.PathJoin(self.ToAbsPath(build_dir), 'mb_type') | 537 mb_type_path = self.PathJoin(self.ToAbsPath(build_dir), 'mb_type') |
| 532 if not self.Exists(mb_type_path): | 538 if not self.Exists(mb_type_path): |
| 533 toolchain_path = self.PathJoin(self.ToAbsPath(build_dir), | 539 toolchain_path = self.PathJoin(self.ToAbsPath(build_dir), |
| 534 'toolchain.ninja') | 540 'toolchain.ninja') |
| 535 if not self.Exists(toolchain_path): | 541 if not self.Exists(toolchain_path): |
| 536 self.Print('Must either specify a path to an existing GN build dir ' | 542 self.Print('Must either specify a path to an existing GN build dir ' |
| 537 'or pass in a -m/-b pair or a -c flag to specify the ' | 543 'or pass in a -m/-b pair or a -c flag to specify the ' |
| 538 'configuration') | 544 'configuration') |
| 539 return {} | 545 return {} |
| 540 else: | 546 else: |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 649 '(-m/--master and -b/--builder)') | 655 '(-m/--master and -b/--builder)') |
| 650 | 656 |
| 651 if not self.args.master in self.masters: | 657 if not self.args.master in self.masters: |
| 652 raise MBErr('Master name "%s" not found in "%s"' % | 658 raise MBErr('Master name "%s" not found in "%s"' % |
| 653 (self.args.master, self.args.config_file)) | 659 (self.args.master, self.args.config_file)) |
| 654 | 660 |
| 655 if not self.args.builder in self.masters[self.args.master]: | 661 if not self.args.builder in self.masters[self.args.master]: |
| 656 raise MBErr('Builder name "%s" not found under masters[%s] in "%s"' % | 662 raise MBErr('Builder name "%s" not found under masters[%s] in "%s"' % |
| 657 (self.args.builder, self.args.master, self.args.config_file)) | 663 (self.args.builder, self.args.master, self.args.config_file)) |
| 658 | 664 |
| 659 return self.masters[self.args.master][self.args.builder] | 665 config = self.masters[self.args.master][self.args.builder] |
| 666 if isinstance(config, list): |
| 667 if self.args.phase is None: |
| 668 raise MBErr('Must specify a build --phase for %s on %s' % |
| 669 (self.args.builder, self.args.master)) |
| 670 phase = int(self.args.phase) |
| 671 if phase < 1 or phase > len(config): |
| 672 raise MBErr('Phase %d out of bounds for %s on %s' % |
| 673 (phase, self.args.builder, self.args.master)) |
| 674 return config[phase-1] |
| 675 |
| 676 if self.args.phase is not None: |
| 677 raise MBErr('Must not specify a build --phase for %s on %s' % |
| 678 (self.args.builder, self.args.master)) |
| 679 return config |
| 660 | 680 |
| 661 def FlattenConfig(self, config): | 681 def FlattenConfig(self, config): |
| 662 mixins = self.configs[config] | 682 mixins = self.configs[config] |
| 663 # TODO(dpranke): We really should provide a constructor for the | |
| 664 # default set of values. | |
| 665 vals = { | 683 vals = { |
| 666 'args_file': '', | 684 'args_file': '', |
| 667 'cros_passthrough': False, | 685 'cros_passthrough': False, |
| 668 'gn_args': [], | 686 'gn_args': [], |
| 669 'gyp_defines': '', | 687 'gyp_defines': '', |
| 670 'gyp_crosscompile': False, | 688 'gyp_crosscompile': False, |
| 671 'type': None, | 689 'type': None, |
| 672 } | 690 } |
| 673 | 691 |
| 674 visited = [] | 692 visited = [] |
| 675 self.FlattenMixins(mixins, vals, visited) | 693 self.FlattenMixins(mixins, vals, visited) |
| 676 return vals | 694 return vals |
| 677 | 695 |
| 678 def FlattenMixins(self, mixins, vals, visited): | 696 def FlattenMixins(self, mixins, vals, visited): |
| 679 for m in mixins: | 697 for m in mixins: |
| 680 if m not in self.mixins: | 698 if m not in self.mixins: |
| 681 raise MBErr('Unknown mixin "%s"' % m) | 699 raise MBErr('Unknown mixin "%s"' % m) |
| 682 | 700 |
| 683 # TODO: check for cycles in mixins. | |
| 684 | |
| 685 visited.append(m) | 701 visited.append(m) |
| 686 | 702 |
| 687 mixin_vals = self.mixins[m] | 703 mixin_vals = self.mixins[m] |
| 688 | 704 |
| 689 if 'cros_passthrough' in mixin_vals: | 705 if 'cros_passthrough' in mixin_vals: |
| 690 vals['cros_passthrough'] = mixin_vals['cros_passthrough'] | 706 vals['cros_passthrough'] = mixin_vals['cros_passthrough'] |
| 691 if 'gn_args' in mixin_vals: | 707 if 'gn_args' in mixin_vals: |
| 692 if vals['gn_args']: | 708 if vals['gn_args']: |
| 693 vals['gn_args'] += ' ' + mixin_vals['gn_args'] | 709 vals['gn_args'] += ' ' + mixin_vals['gn_args'] |
| 694 else: | 710 else: |
| (...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 969 self.PrintJSON(outp) | 985 self.PrintJSON(outp) |
| 970 self.Print() | 986 self.Print() |
| 971 | 987 |
| 972 return ret | 988 return ret |
| 973 | 989 |
| 974 def GetIsolateCommand(self, target, vals, gn_isolate_map): | 990 def GetIsolateCommand(self, target, vals, gn_isolate_map): |
| 975 android = 'target_os="android"' in vals['gn_args'] | 991 android = 'target_os="android"' in vals['gn_args'] |
| 976 | 992 |
| 977 # This needs to mirror the settings in //build/config/ui.gni: | 993 # This needs to mirror the settings in //build/config/ui.gni: |
| 978 # use_x11 = is_linux && !use_ozone. | 994 # use_x11 = is_linux && !use_ozone. |
| 979 # TODO(dpranke): Figure out how to keep this in sync better. | |
| 980 use_x11 = (self.platform == 'linux2' and | 995 use_x11 = (self.platform == 'linux2' and |
| 981 not android and | 996 not android and |
| 982 not 'use_ozone=true' in vals['gn_args']) | 997 not 'use_ozone=true' in vals['gn_args']) |
| 983 | 998 |
| 984 asan = 'is_asan=true' in vals['gn_args'] | 999 asan = 'is_asan=true' in vals['gn_args'] |
| 985 msan = 'is_msan=true' in vals['gn_args'] | 1000 msan = 'is_msan=true' in vals['gn_args'] |
| 986 tsan = 'is_tsan=true' in vals['gn_args'] | 1001 tsan = 'is_tsan=true' in vals['gn_args'] |
| 987 | 1002 |
| 988 target_name = self.GNTargetName(target) | 1003 target_name = self.GNTargetName(target) |
| 989 test_type = gn_isolate_map[target_name]['type'] | 1004 test_type = gn_isolate_map[target_name]['type'] |
| (...skipping 496 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1486 # Then check to see if the arg contains any metacharacters other than | 1501 # Then check to see if the arg contains any metacharacters other than |
| 1487 # double quotes; if it does, quote everything (including the double | 1502 # double quotes; if it does, quote everything (including the double |
| 1488 # quotes) for safety. | 1503 # quotes) for safety. |
| 1489 if any(a in UNSAFE_FOR_CMD for a in arg): | 1504 if any(a in UNSAFE_FOR_CMD for a in arg): |
| 1490 arg = ''.join('^' + a if a in ALL_META_CHARS else a for a in arg) | 1505 arg = ''.join('^' + a if a in ALL_META_CHARS else a for a in arg) |
| 1491 return arg | 1506 return arg |
| 1492 | 1507 |
| 1493 | 1508 |
| 1494 if __name__ == '__main__': | 1509 if __name__ == '__main__': |
| 1495 sys.exit(main(sys.argv[1:])) | 1510 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |