blob: 96a21dc9ff46527fee066fae51078b491e0b1591 [file] [log] [blame]
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Script for dumping and/or comparing build config contents."""
from __future__ import print_function
import json
import pprint
from chromite.cbuildbot import cbuildbot_config
from chromite.lib import commandline
from chromite.lib import cros_build_lib
class _JSONEncoder(json.JSONEncoder):
"""Json Encoder that encodes objects as their dictionaries."""
# pylint: disable=E0202
def default(self, obj):
return self.encode(obj.__dict__)
def _InjectDisplayPosition(config):
"""Add field to help buildbot masters order builders on the waterfall.
Args:
config: A dict of build config items.
Returns:
A similar config where each config item has a new 'display_position' value.
"""
def _GetSortKey(items):
my_config = items[1]
# Allow configs to override the display_position.
return (my_config.get('display_position', 1000000),
cbuildbot_config.GetDisplayPosition(my_config['name']),
my_config['internal'], my_config['vm_tests'])
source = sorted(config.iteritems(), key=_GetSortKey)
return dict((name, dict(value.items() + [('display_position', idx)]))
for idx, (name, value) in enumerate(source))
def _DumpConfigJson(cfg):
"""Dump |cfg| contents in JSON format.
Args:
cfg: A single build config.
"""
print(json.dumps(cfg, cls=_JSONEncoder))
def _HideDefaults(cfg):
"""Hide the defaults from a given config entry.
Args:
cfg: A config entry.
Returns:
The same config entry, but without any defaults.
"""
d = {}
for k, v in cfg.iteritems():
if cbuildbot_config.default.get(k) != v:
if k == 'child_configs':
d[k] = [_HideDefaults(x) for x in v]
else:
d[k] = v
return d
def _DumpConfigPrettyJson(cfg):
"""Dump |cfg| contents in pretty JSON format.
Args:
cfg: A single build config.
"""
print(json.dumps(cfg, cls=_JSONEncoder,
sort_keys=True, indent=4, separators=(',', ': ')))
def _DumpConfigPrettyPrint(cfg):
"""Dump |cfg| contents in pretty printer format.
Args:
cfg: A single build config.
"""
pretty_printer = pprint.PrettyPrinter(indent=2)
pretty_printer.pprint(cfg)
def _CompareConfig(old_cfg, new_cfg):
"""Compare two build configs targets, printing results.
Args:
old_cfg: The 'from' build config for comparison.
new_cfg: The 'to' build config for comparison.
"""
new_cfg = json.loads(json.dumps(new_cfg, cls=_JSONEncoder))
for key in sorted(set(new_cfg.keys() + old_cfg.keys())):
obj1, obj2 = old_cfg.get(key), new_cfg.get(key)
if obj1 == obj2:
continue
elif obj1 is None:
print('%s: added to config\n' % (key,))
continue
elif obj2 is None:
print('%s: removed from config\n' % (key,))
continue
print('%s:' % (key,))
for subkey in sorted(set(obj1.keys() + obj2.keys())):
sobj1, sobj2 = obj1.get(subkey), obj2.get(subkey)
if sobj1 != sobj2:
print(' %s: %r, %r' % (subkey, sobj1, sobj2))
print()
def GetParser():
"""Creates the argparse parser."""
parser = commandline.ArgumentParser(description=__doc__)
# Put options that control the mode of script into mutually exclusive group.
mode = parser.add_mutually_exclusive_group(required=True)
mode.add_argument('-c', '--compare', action='store',
type=commandline.argparse.FileType('rb'),
default=None, metavar='file_name',
help='Compare current config against a saved on disk '
'serialized (json) dump of a config.')
mode.add_argument('-d', '--dump', action='store_true', default=False,
help='Dump the configs in JSON format.')
parser.add_argument('--pretty', action='store_true', default=False,
help='If dumping, make json output human readable.')
parser.add_argument('--for-buildbot', action='store_true', default=False,
help='Include the display position in data.')
parser.add_argument('-s', '--separate-defaults', action='store_true',
default=False, help='Show the defaults separately.')
parser.add_argument('config_targets', metavar='config_target', nargs='*',
help='Name of a cbuildbot config target.')
return parser
def main(argv):
parser = GetParser()
options = parser.parse_args(argv)
if options.pretty and not options.dump:
parser.error('The --pretty option does not make sense without --dump')
# Possibly translate config contents first.
convert = lambda x: x
if options.for_buildbot:
convert = _InjectDisplayPosition
config = convert(cbuildbot_config.config)
# Separate the defaults and show them at the top. We prefix the name with
# an underscore so that it sorts to the top.
if options.separate_defaults:
for k, v in config.iteritems():
config[k] = _HideDefaults(v)
config['_default'] = cbuildbot_config.default
# If config_targets specified, only dump/load those.
if options.config_targets:
temp_config = dict()
for c in options.config_targets:
try:
temp_config[c] = config[c]
except KeyError:
cros_build_lib.Die('No such config id: %s', c)
config = temp_config
if config:
if options.dump:
if options.pretty:
_DumpConfigPrettyJson(config)
else:
_DumpConfigJson(config)
elif options.compare:
# Load the previously saved build config for comparison.
old_cfg = convert(json.load(options.compare))
_CompareConfig(old_cfg, config)
return 0