blob: 6f2b569d2a8a47f472edb0926f6103c860259508 [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright 2019 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.
"""Generate build API caller scripts.
Creates executable scripts of the form service__method for each Build API
endpoint. The scripts can be called without arguments. They instead make
assumptions about the input and output json file names and locations.
The system supports checking in example files which are automatically copied
in to the input file location. The example files should be along side this
script and of the form "service__method_example_input.json". When not found,
it will write out an empty json dict to the input file.
"""
from __future__ import print_function
import os
import re
from chromite.api import router as router_lib
from chromite.lib import commandline
from chromite.lib import cros_logging as logging
from chromite.lib import osutils
_SCRIPT_TEMPLATE_FILE = os.path.join(
os.path.dirname(__file__), 'call_templates', 'script_template')
SCRIPT_TEMPLATE = osutils.ReadFile(_SCRIPT_TEMPLATE_FILE)
EXAMPLES_PATH = os.path.join(os.path.dirname(__file__), 'call_templates')
OUTPUT_PATH = os.path.join(os.path.dirname(__file__), 'call_scripts')
def _camel_to_snake(string):
# Transform FooBARBaz into FooBAR_Baz. Avoids making Foo_B_A_R_Baz.
sub1 = re.sub(r'(.)([A-Z][a-z]+)', r'\1_\2', string)
# Transform FooBAR_Baz into Foo_BAR_Baz.
sub2 = re.sub('([a-z0-9])([A-Z])', r'\1_\2', sub1)
# Lower case to get foo_bar_baz.
return sub2.lower()
def _get_services():
"""Get all service: [method, ...] mappings.
Parses chromite.api.FooService/Method to foo: [method, ...].
Returns:
dict
"""
router = router_lib.GetRouter()
return router.ListMethods()
def get_services():
services = {}
for service_method in _get_services():
service, method = service_method.split('/')
service_parts = service.split('.')
full_service_name = service_parts[-1]
service_name = full_service_name.replace('Service', '')
final_service = _camel_to_snake(service_name)
final_method = _camel_to_snake(method)
if not final_service in services:
services[final_service] = {
'name': final_service,
'full_name': full_service_name,
'methods': []
}
services[final_service]['methods'].append({
'name': final_method,
'full_name': method,
})
return list(services.values())
def write_script(filename, service, method):
contents = SCRIPT_TEMPLATE % {'SERVICE': service, 'METHOD': method}
script_path = os.path.join(OUTPUT_PATH, filename)
osutils.WriteFile(script_path, contents, makedirs=True)
os.chmod(script_path, 0o755)
def write_scripts(build_target, force=False):
for service_data in get_services():
for method_data in service_data['methods']:
filename = '__'.join([service_data['name'], method_data['name']])
logging.info('Writing %s', filename)
write_script(filename, service_data['full_name'],
method_data['full_name'])
example_input = os.path.join(EXAMPLES_PATH,
'%s_example_input.json' % filename)
input_file = os.path.join(OUTPUT_PATH, '%s_input.json' % filename)
if not force and not _input_file_empty(input_file):
logging.info('%s exists, skipping.', input_file)
elif os.path.exists(example_input):
logging.info('Example %s exists, building input.', example_input)
content = osutils.ReadFile(example_input)
osutils.WriteFile(input_file, content % {'build_target': build_target})
elif not os.path.exists(input_file):
logging.info('No input could be found, writing empty input.')
osutils.WriteFile(input_file, '{}')
def _input_file_empty(input_file):
if not os.path.exists(input_file):
return True
contents = osutils.ReadFile(input_file).strip()
return not contents or contents == '{}'
def GetParser():
"""Build the argument parser."""
parser = commandline.ArgumentParser(description=__doc__)
parser.add_argument(
'--force',
action='store_true',
default=False,
help='Force replace all input files, even if not empty.')
parser.add_argument(
'-b',
'--board',
'--build-target',
dest='build_target',
help='Generate the configs with the given build target. Implies --force. '
'Defaults to "betty".')
return parser
def _ParseArgs(argv):
"""Parse and validate arguments."""
parser = GetParser()
opts = parser.parse_args(argv)
if opts.build_target:
opts.force = True
else:
opts.build_target = 'betty'
opts.Freeze()
return opts
def main(argv):
opts = _ParseArgs(argv)
write_scripts(opts.build_target, force=opts.force)