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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
|
# 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 GetAllConfigsMaster(masters):
"""Build a list of all of the configs referenced by builders.
Deprecated in favor or GetAllConfigsBucket
"""
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 GetAllConfigsBucket(buckets):
"""Build a list of all of the configs referenced by builders."""
all_configs = {}
for bucket in buckets:
for config in buckets[bucket].values():
if isinstance(config, dict):
for c in config.values():
all_configs[c] = bucket
else:
all_configs[config] = bucket
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 EnsureNoProprietaryMixinsBucket(errs, default_config, config_file,
public_artifact_builders, buckets, configs,
mixins):
"""Check that the 'chromium' bots which build public artifacts
do not include the chrome_with_codecs mixin.
"""
if config_file != default_config:
return
if public_artifact_builders is None:
errs.append('Missing "public_artifact_builders" config entry. '
'Please update this proprietary codecs check with the '
'name of the builders responsible for public build artifacts.')
return
# crbug/1033585
for bucket, builders in public_artifact_builders.items():
for builder in builders:
config = buckets[bucket][builder]
def RecurseMixins(builder, 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(builder, mixin)
for mixin in configs[config]:
RecurseMixins(builder, mixin)
return errs
def EnsureNoProprietaryMixinsMaster(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.
Deprecated in favor of BlacklistMixinsBucket
"""
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 CheckMasterBucketConsistency(errs, masters_file, buckets_file):
"""Checks that mb_config_buckets.pyl is consistent with mb_config.pyl
mb_config_buckets.pyl is a subset of mb_config.pyl.
Make sure all configs that do exist are consistent.
Populates errs with any errors
Args:
errs: an accumulator for errors
masters_file: string form of mb_config.pyl
bucket_file: string form of mb_config_buckets.pyl
"""
master_contents = ast.literal_eval(masters_file)
bucket_contents = ast.literal_eval(buckets_file)
def check_missing(bucket_dict, master_dict):
return [name for name in bucket_dict.keys() if name not in master_dict]
# Cross check builders
configs_by_builder = _GetConfigsByBuilder(master_contents['masters'])
for bucketname, builders in bucket_contents['buckets'].items():
missing = check_missing(builders, configs_by_builder)
errs.extend('Builder "%s" in bucket "%s" from mb_config_buckets.pyl '
'not found in mb_config.pyl' % (builder, bucketname)
for builder in missing)
for buildername, config in builders.items():
if config not in configs_by_builder[buildername]:
errs.append('Builder "%s" in bucket "%s" from mb_config_buckets.pyl '
'doesn\'t match mb_config.pyl' % (buildername, bucketname))
def check_mismatch(bucket_dict, master_dict):
mismatched = []
for configname, config in bucket_dict.items():
if configname in master_dict and config != master_dict[configname]:
mismatched.append(configname)
return mismatched
# Cross check configs
missing = check_missing(bucket_contents['configs'],
master_contents['configs'])
errs.extend('Config "%s" from mb_config_buckets.pyl '
'not found in mb_config.pyl' % config for config in missing)
mismatched = check_mismatch(bucket_contents['configs'],
master_contents['configs'])
errs.extend(
'Config "%s" from mb_config_buckets.pyl doesn\'t match mb_config.pyl' %
config for config in mismatched)
# Cross check mixins
missing = check_missing(bucket_contents['mixins'], master_contents['mixins'])
errs.extend('Mixin "%s" from mb_config_buckets.pyl '
'not found in mb_config.pyl' % mixin for mixin in missing)
mismatched = check_mismatch(bucket_contents['mixins'],
master_contents['mixins'])
errs.extend(
'Mixin "%s" from mb_config_buckets.pyl doesn\'t match mb_config.pyl' %
mixin for mixin in mismatched)
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))))
|