def codegen(request, response): """Generates wdl c code for devices from Jinja templates. Args: request: CodeGeneratorRequest, see google/protobuf/compiler/plugin.proto response: CodeGeneratorResponse, see google/protobuf/compiler/plugin.proto. Output files filled with generated files. Raises: Exception: Valid Jinja template directory required for wdl plugin. """ args = {} for argument in request.parameter.split(','): (key, value) = argument.split('=', 1) values = value.split(':') if len(values) == 1: args[key] = values[0] else: args[key] = values legacy_mode_enabled = ('legacy_mode_enabled' in args and args['legacy_mode_enabled'].lower() == 'true') gen_dependencies = ('gen_dependencies' in args and args['gen_dependencies'].lower() == 'true') codegen_reference_mode = ('codegen_reference_mode' in args and args['codegen_reference_mode'].lower() == 'true') if args['templates'] is None or not args['templates']: raise Exception('wdl_plugin: \'templates\' argument is empty') if isinstance(args['templates'], list): template_files = args['templates'] else: template_files = [ args['templates'], ] template_languages = { 'c': template_set.TemplateLanguage.C, 'cpp': template_set.TemplateLanguage.CPLUSPLUS, 'java': template_set.TemplateLanguage.JAVA, 'js': template_set.TemplateLanguage.JAVASCRIPT, 'md': template_set.TemplateLanguage.MARKDOWN, 'objc': template_set.TemplateLanguage.OBJECTIVEC, } if 'language' not in args or args['language'] not in template_languages: language = template_set.TemplateLanguage.BASE else: language = template_languages[args['language']] schema_obj = schema.Schema() schema_parser = nwv_parser.Parser(schema_obj) file_descs_to_gen = [ proto_file for proto_file in request.proto_file if (('semantic' not in proto_file.name) and ( 'retention' not in proto_file.name)) ] dependency_set = [] # This file needs to get added to the dependency list if we're in # codegen mode, since this file doesn't show up as a dependency by # default, but is still necessary for some code-generated targets. if (codegen_reference_mode): dependency_set.append('google/protobuf/field_mask.proto') for proto_file in file_descs_to_gen: dependency_set.append(proto_file.name) schema_parser.add_file_set(file_descs_to_gen) gwvc.check(schema_obj) # Add two spaces to each log messages to make it line up with our output. template_set.log = lambda *a: print(' ', *a) templates = template_set.TemplateSet( template_files, legacy_mode_enabled=legacy_mode_enabled, language=language) if gen_dependencies: templates.codegen(schema_obj, None, dependency_set) else: templates.codegen(schema_obj, None, request.file_to_generate) for filename, content in templates.output_files: out_file = response.file.add() out_file.name = filename # The newline was added in the legacy template_set file writer, # so it's included here to preserve compatibility. out_file.content = content.encode('utf-8') + '\n'
def codegen(schema_object, *args): """Generates code from Jinja templates. Templates should be named in the form: <object-type>.*.<extension> <object-type> must be one of (case insensitive): * "schema", "vendor", "interface", "trait", or "struct" <extension> can be anything. However, certain extensions will trigger specialized templating environments. See template_set.codegen_template_mapping for the exact mapping from extension to template subclass. In between <object-type> and <extension> can be any text. Args: schema_object: The WDL schema object to codegen. *args: The other command line arguments. """ parser = argparse.ArgumentParser(description='Code generator.') parser.add_argument( '--template_path', help='The directory to locate the template files. ' 'Defaults to the common prefix among all template_files', type=str) parser.add_argument('--out', required=True, help='The root directory to write generated files.', type=str) parser.add_argument('--define', help='Additional data to define in the template ' 'environment, specified as a name=value pair.', type=str, action='append') (parsed_args, _) = parser.parse_known_args(args) if parsed_args.template_path: template_path = os.path.abspath(parsed_args.template_path) else: logging.error('--template_path must be provided.') sys.exit(1) # Run check because codegen makes assumptions based on this passing check(schema_object) template_files = [ os.path.join(template_path, f) for f in os.listdir(template_path) ] env_data = {} if parsed_args.define: for pair in parsed_args.define: m = re.match(r'\s*([^=\s]+)\s*=\s*(.*)', pair) if not m: logging.error('Unable to parse user data: %s', pair) sys.exit(1) env_data[m.group(1)] = m.group(2) # Add two spaces to each log messages to make it line up with our output. template_set.log = lambda *a: logging.info(' ', *a) logging.info('Loading templates: %s', template_files) templates = template_set.TemplateSet(template_files) if env_data: logging.info('Additional environment data:') logging.info(json.dumps(env_data, indent=2, separators=(',', ': '))) else: logging.info('No additional environment data.') logging.info('Generating code: %s', parsed_args.out) templates.codegen(schema_object, env_data) for filename, contents in templates.output_files: dest_file = os.path.join(parsed_args.out, filename) file_utils.mkdir_p(os.path.dirname(dest_file)) file_utils.enable_writes([dest_file]) fh = open(dest_file, 'wb') fh.write(contents.encode('utf-8') + '\n') fh.close()