summaryrefslogtreecommitdiff
path: root/chromium/tools/mb/lib/validation.py
blob: cda90534c03dc593b94138527c3631c4a957ec63 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Validation functions for the Meta-Build config file"""

import ast
import collections
import json
import re


def GetAllConfigs(masters):
  """Build a list of all of the configs referenced by builders.
  """
  all_configs = {}
  for master in masters:
    for config in masters[master].values():
      if isinstance(config, dict):
        for c in config.values():
          all_configs[c] = master
      else:
        all_configs[config] = master
  return all_configs


def CheckAllConfigsAndMixinsReferenced(errs, all_configs, configs, mixins):
  """Check that every actual config is actually referenced."""
  for config in configs:
    if not config in all_configs:
      errs.append('Unused config "%s".' % config)

  # Figure out the whole list of mixins, and check that every mixin
  # listed by a config or another mixin actually exists.
  referenced_mixins = set()
  for config, mixin_names in configs.items():
    for mixin in mixin_names:
      if not mixin in mixins:
        errs.append(
            'Unknown mixin "%s" referenced by config "%s".' % (mixin, config))
      referenced_mixins.add(mixin)

  for mixin in mixins:
    for sub_mixin in mixins[mixin].get('mixins', []):
      if not sub_mixin in mixins:
        errs.append(
            'Unknown mixin "%s" referenced by mixin "%s".' % (sub_mixin, mixin))
      referenced_mixins.add(sub_mixin)

  # Check that every mixin defined is actually referenced somewhere.
  for mixin in mixins:
    if not mixin in referenced_mixins:
      errs.append('Unreferenced mixin "%s".' % mixin)

  return errs


def EnsureNoProprietaryMixins(errs, default_config, config_file, masters,
                              configs, mixins):
  """If we're checking the Chromium config, check that the 'chromium' bots
  which build public artifacts do not include the chrome_with_codecs mixin.
  """
  if config_file == default_config:
    if 'chromium' in masters:
      for builder in masters['chromium']:
        config = masters['chromium'][builder]

        def RecurseMixins(current_mixin):
          if current_mixin == 'chrome_with_codecs':
            errs.append('Public artifact builder "%s" can not contain the '
                        '"chrome_with_codecs" mixin.' % builder)
            return
          if not 'mixins' in mixins[current_mixin]:
            return
          for mixin in mixins[current_mixin]['mixins']:
            RecurseMixins(mixin)

        for mixin in configs[config]:
          RecurseMixins(mixin)
    else:
      errs.append('Missing "chromium" master. Please update this '
                  'proprietary codecs check with the name of the master '
                  'responsible for public build artifacts.')


def _GetConfigsByBuilder(masters):
  """Builds a mapping from buildername -> [config]

    Args
      masters: the master's dict from mb_config.pyl
    """

  result = collections.defaultdict(list)
  for master in masters.values():
    for buildername, builder in master.items():
      result[buildername].append(builder)

  return result


def CheckDuplicateConfigs(errs, config_pool, mixin_pool, grouping,
                          flatten_config):
  """Check for duplicate configs.

  Evaluate all configs, and see if, when
  evaluated, differently named configs are the same.
  """
  evaled_to_source = collections.defaultdict(set)
  for group, builders in grouping.items():
    for builder in builders:
      config = grouping[group][builder]
      if not config:
        continue

      if isinstance(config, dict):
        # Ignore for now
        continue
      elif config.startswith('//'):
        args = config
      else:
        flattened_config = flatten_config(config_pool, mixin_pool, config)
        args = flattened_config['gn_args']
        if 'error' in args:
          continue
        # Force the args_file into consideration when testing for duplicate
        # configs.
        args_file = flattened_config['args_file']
        if args_file:
          args += ' args_file=%s' % args_file

      evaled_to_source[args].add(config)

  for v in evaled_to_source.values():
    if len(v) != 1:
      errs.append(
          'Duplicate configs detected. When evaluated fully, the '
          'following configs are all equivalent: %s. Please '
          'consolidate these configs into only one unique name per '
          'configuration value.' % (', '.join(sorted('%r' % val for val in v))))