| Index: scripts/tools/buildbot_tool.py
|
| diff --git a/scripts/tools/buildbot_tool.py b/scripts/tools/buildbot_tool.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..7f5aa54aab365de89886640ed0e5055b2f10a40e
|
| --- /dev/null
|
| +++ b/scripts/tools/buildbot_tool.py
|
| @@ -0,0 +1,169 @@
|
| +#!/usr/bin/python
|
| +# Copyright 2014 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.
|
| +
|
| +from __future__ import print_function
|
| +
|
| +import argparse
|
| +import ast
|
| +import os
|
| +import re
|
| +import sys
|
| +
|
| +
|
| +TOOLS_DIR = os.path.abspath(os.path.dirname(__file__))
|
| +SCRIPTS_DIR = os.path.dirname(TOOLS_DIR)
|
| +BASE_DIR = os.path.dirname(SCRIPTS_DIR)
|
| +
|
| +# This adjusts sys.path, so must be done before we import other modules.
|
| +if not SCRIPTS_DIR in sys.path:
|
| + sys.path.append(SCRIPTS_DIR)
|
| +
|
| +from common import filesystem
|
| +
|
| +
|
| +TEMPLATE_SUBPATH = os.path.join('scripts', 'tools', 'buildbot_tool_templates')
|
| +TEMPLATE_DIR = os.path.join(BASE_DIR, TEMPLATE_SUBPATH)
|
| +
|
| +BUILDER_TEMPLATE = """\
|
| +c['builders'].append({
|
| + 'name': '%(builder_name)s',
|
| + 'factory': m_annotator.BaseFactory('%(recipe)s'),
|
| + 'slavebuilddir': '%(slavebuilddir)s'})
|
| +"""
|
| +
|
| +
|
| +SLAVE_TEMPLATE = """\
|
| + {
|
| + 'master': '%(master_classname)s',
|
| + 'hostname': '%(hostname)s',
|
| + 'builder': '%(builder_name)s',
|
| + 'os': '%(os)s',
|
| + 'version': '%(version)s',
|
| + 'bits': '%(bits)s',
|
| + },
|
| +"""
|
| +
|
| +
|
| +def main(argv, fs):
|
| + args = parse_args(argv)
|
| + return args.func(args, fs)
|
| +
|
| +
|
| +def parse_args(argv):
|
| + parser = argparse.ArgumentParser()
|
| + subps = parser.add_subparsers()
|
| +
|
| + subp = subps.add_parser('gen', help=run_gen.__doc__)
|
| + subp.add_argument('master_dirname', nargs=1,
|
| + help='Path to master config directory (must contain '
|
| + 'a builders.py file).')
|
| + subp.set_defaults(func=run_gen)
|
| +
|
| + subp = subps.add_parser('help', help=run_help.__doc__)
|
| + subp.add_argument(nargs='?', action='store', dest='subcommand',
|
| + help='The command to get help for.')
|
| + subp.set_defaults(func=run_help)
|
| +
|
| + return parser.parse_args(argv)
|
| +
|
| +
|
| +def run_gen(args, fs):
|
| + """Generate a new master config."""
|
| +
|
| + master_dirname = args.master_dirname[0]
|
| + master_subpath = fs.relpath(master_dirname, BASE_DIR)
|
| + builders_path = fs.join(BASE_DIR, master_subpath, 'builders.py')
|
| +
|
| + if not fs.exists(builders_path):
|
| + print("%s not found" % master_dirname, file=sys.stderr)
|
| + return 1
|
| +
|
| + values = _values_from_file(fs, builders_path)
|
| +
|
| + for filename in fs.listfiles(TEMPLATE_DIR):
|
| + template = fs.read_text_file(fs.join(TEMPLATE_DIR, filename))
|
| + contents = _expand(template, values,
|
| + '%s/%s' % (TEMPLATE_SUBPATH, filename),
|
| + master_subpath)
|
| + fs.write_text_file(fs.join(BASE_DIR, master_subpath, filename), contents)
|
| + print("Wrote %s." % filename)
|
| +
|
| + return 0
|
| +
|
| +
|
| +def run_help(args, fs):
|
| + """Get help on a subcommand."""
|
| +
|
| + if args.subcommand:
|
| + return main([args.subcommand, '--help'], fs)
|
| + return main(['--help'], fs)
|
| +
|
| +
|
| +def _values_from_file(fs, builders_path):
|
| + builders = ast.literal_eval(fs.read_text_file(builders_path))
|
| + master_dirname = fs.basename(fs.dirname(builders_path))
|
| + master_name_comps = master_dirname.split('.')[1:]
|
| + master_classname = ''.join(c[0].upper() + c[1:] for c in master_name_comps)
|
| +
|
| + builders_block = ""
|
| + slaves_block = "slaves = [\n"
|
| +
|
| + for builder_name, builder_vals in builders['builders'].items():
|
| + builders_block += BUILDER_TEMPLATE % {
|
| + 'builder_name': builder_name,
|
| + 'recipe': builder_vals['recipe'],
|
| + 'slavebuilddir': builder_vals['slavebuilddir']
|
| + }
|
| +
|
| + for pool_name in builder_vals['slave_pools']:
|
| + pool = builders['slave_pools'][pool_name]
|
| + slave_data = pool['slave_data']
|
| + slaves = pool['slaves']
|
| + for slave in slaves:
|
| + slaves_block += SLAVE_TEMPLATE % {
|
| + 'master_classname': master_classname,
|
| + 'hostname': slave,
|
| + 'builder_name': builder_name,
|
| + 'os': slave_data['os'],
|
| + 'version': slave_data['version'],
|
| + 'bits': slave_data['bits'],
|
| + }
|
| +
|
| + slaves_block += "]"
|
| +
|
| + v = {}
|
| + v['builders_block'] = builders_block
|
| + v['git_repo_url'] = builders['git_repo_url']
|
| + v['master_dirname'] = master_dirname
|
| + v['master_classname'] = master_classname
|
| + v['master_base_class'] = builders['master_base_class']
|
| + v['master_port'] = builders['master_port']
|
| + v['master_port_alt'] = builders['master_port_alt']
|
| + v['slave_port'] = builders['slave_port']
|
| + v['slaves_block'] = slaves_block
|
| + return v
|
| +
|
| +
|
| +def _expand(template, values, source, master_subpath):
|
| + try:
|
| + contents = template % values
|
| + except:
|
| + print("Error populating template %s" % source, file=sys.stderr)
|
| + raise
|
| + return _update_generated_file_disclaimer(contents, source, master_subpath)
|
| +
|
| +
|
| +def _update_generated_file_disclaimer(contents, source, master_subpath):
|
| + pattern = '# This file is used by scripts/tools/buildbot-tool.*'
|
| + replacement = ('# This file was generated from\n'
|
| + '# %s\n'
|
| + '# by "scripts/tools/buildbot-tool gen %s".\n'
|
| + '# DO NOT EDIT BY HAND!\n' %
|
| + (source, master_subpath))
|
| + return re.sub(pattern, replacement, contents)
|
| +
|
| +
|
| +if __name__ == '__main__': # pragma: no cover
|
| + sys.exit(main(sys.argv[1:], filesystem.Filesystem()))
|
|
|