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'
Exemple #2
0
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()