#!/usr/bin/env python3 # encoding: utf-8 """ Pyolian template based generator. This is a really powerfull template-based, output-agnostic eolian generator. You just need a template file and then you can render it with the wanted eolian scope (class, namespace, enum, struct, etc...). For example (from this source folder): ./generator.py test_gen_class.template --cls Efl.Loop.Timer ./generator.py test_gen_namespace.template --ns Efl.Ui ...of course you can pass any other class or namespace to the example above. The generator is based on the great pyratemp engine (THANKS!), you can find the full template syntax at: www.simple-is-better.org/template/pyratemp.html Just keep in mind the syntax is a bit different in this implementation: sub_start = "${" Was "$!" in original pyratemp (and in docs) sub_end = "}$" Was "!$" in original pyratemp (and in docs) _block_start = "" comment_start = "#!" comment_end = "!#" You can also import this module and use the provided Template class if you are more confortable from within python. To import from outside this directory you need to hack sys.path in a way that this folder will be available on PYTHON_PATH, fe: pyolian_path = os.path.join(EFL_ROOT_PATH, 'src', 'scripts') sys.path.insert(0, pyolian_path) from pyolian.generator import Template """ import os import datetime import atexit try: from . import eolian from . import pyratemp except ImportError: import eolian import pyratemp # Use .eo files from the source tree (not the installed ones) script_path = os.path.dirname(os.path.realpath(__file__)) root_path = os.path.abspath(os.path.join(script_path, '..', '..', '..')) SCAN_FOLDER = os.path.join(root_path, 'src', 'lib') # load the whole eolian db eolian_db = eolian.Eolian_State() if not isinstance(eolian_db, eolian.Eolian_State): raise(RuntimeError('Eolian, failed to create Eolian state')) if not eolian_db.directory_add(SCAN_FOLDER): raise(RuntimeError('Eolian, failed to scan source directory')) if not eolian_db.all_eot_files_parse(): raise(RuntimeError('Eolian, failed to parse all EOT files')) if not eolian_db.all_eo_files_parse(): raise(RuntimeError('Eolian, failed to parse all EO files')) # cleanup the database on exit def cleanup_db(): global eolian_db del eolian_db atexit.register(cleanup_db) class Template(pyratemp.Template): """ Pyolian template based generator. You can directly use this class to generate custom outputs based on the eolian database and your provided templates. Usage is as simple as: t = Template() t.render(, cls=..., ns=..., ...) Args: filename: Template file to load. (REQUIRED) context: User provided context for the template (dict). """ def __init__(self, filename, encoding='utf-8', context=None, escape=None, loader_class=pyratemp.LoaderFile, parser_class=pyratemp.Parser, renderer_class=pyratemp.Renderer, eval_class=pyratemp.EvalPseudoSandbox): # Build the global context for the template global_ctx = {} # user provided context (low pri) if context: global_ctx.update(context) # standard names (not overwritables) global_ctx.update({ # Template info 'date': datetime.datetime.now(), 'template_file': os.path.basename(filename), # Eolian info # 'eolian_version': eolian.__version__, # 'eolian_version_info': eolian.__version_info__, # Eolian Classes 'Object': eolian.Object, 'Class': eolian.Class, 'Part': eolian.Part, 'Constructor': eolian.Constructor, 'Event': eolian.Event, 'Function': eolian.Function, 'Function_Parameter': eolian.Function_Parameter, 'Implement': eolian.Implement, 'Type': eolian.Type, 'Typedecl': eolian.Typedecl, 'Enum_Type_Field': eolian.Enum_Type_Field, 'Struct_Type_Field': eolian.Struct_Type_Field, 'Expression': eolian.Expression, 'Constant': eolian.Constant, 'Documentation': eolian.Documentation, 'Documentation_Token': eolian.Documentation_Token, 'Error': eolian.Error, # Eolian Enums 'Eolian_Object_Type': eolian.Eolian_Object_Type, 'Eolian_Function_Type': eolian.Eolian_Function_Type, 'Eolian_Parameter_Direction': eolian.Eolian_Parameter_Direction, 'Eolian_Class_Type': eolian.Eolian_Class_Type, 'Eolian_Object_Scope': eolian.Eolian_Object_Scope, 'Eolian_Typedecl_Type': eolian.Eolian_Typedecl_Type, 'Eolian_Type_Type': eolian.Eolian_Type_Type, 'Eolian_Type_Builtin_Type': eolian.Eolian_Type_Builtin_Type, # 'Eolian_C_Type_Type': eolian.Eolian_C_Type_Type, 'Eolian_Expression_Type': eolian.Eolian_Expression_Type, 'Eolian_Expression_Mask': eolian.Eolian_Expression_Mask, 'Eolian_Binary_Operator': eolian.Eolian_Binary_Operator, 'Eolian_Unary_Operator': eolian.Eolian_Unary_Operator, 'Eolian_Doc_Token_Type': eolian.Eolian_Doc_Token_Type, }) # Call the parent __init__ func self.template_filename = filename pyratemp.Template.__init__(self, filename=filename, encoding=encoding, data=global_ctx, escape=escape, loader_class=loader_class, parser_class=parser_class, renderer_class=renderer_class, eval_class=eval_class) def render(self, filename=None, verbose=True, cls=None, ns=None, struct=None, enum=None, alias=None, **kargs): # Build the context for the template ctx = {} if kargs: ctx.update(kargs) if cls: ctx['cls'] = eolian_db.class_by_name_get(cls) if ns: ctx['namespace'] = eolian_db.namespace_get_by_name(ns) if struct: ctx['struct'] = eolian_db.struct_by_name_get(struct) if enum: ctx['enum'] = eolian_db.enum_by_name_get(enum) if alias: ctx['alias'] = eolian_db.alias_by_name_get(alias) if verbose and filename: print("rendering: {} => {}".format( self.template_filename, filename)) # render with the augmented context output = self(**ctx) if filename is not None: # create directory tree if needed folder = os.path.dirname(filename) if folder and not os.path.isdir(folder): os.makedirs(folder) # write to file with open(filename, "w") as f: f.write(output) else: # or print to stdout print(output) if __name__ == '__main__': import argparse parser = argparse.ArgumentParser(description='Pyolian template based generator.') parser.add_argument('template', help='The template file to use. (REQUIRED)') parser.add_argument('--output', '-o', metavar='FILE', default=None, help='Where to write the rendered output. ' 'If not given will print to stdout.') parser.add_argument('--cls', metavar='CLASS_NAME', default=None, help='The full name of the class to render, ex: Efl.Loop.Timer') parser.add_argument('--ns', metavar='NAMESPACE', default=None, help='The namespace to render, ex: Efl.Loop') parser.add_argument('--struct', metavar='STRUCT_NAME', default=None, help='The name of the struct to render, ex: Efl.Loop.Arguments') parser.add_argument('--enum', metavar='ENUM_NAME', default=None, help='The name of the enum to render, ex: Efl.Loop.Handler.Flags') parser.add_argument('--alias', metavar='ALIAS_NAME', default=None, help='The name of the alias to render, ex: Efl.Font.Size') args = parser.parse_args() t = Template(args.template) t.render(args.output, cls=args.cls, ns=args.ns, struct=args.struct, enum=args.enum, alias=args.alias)