summaryrefslogtreecommitdiff
path: root/src/scripts/pyolian/generator.py
diff options
context:
space:
mode:
authorDave Andreoli <dave@gurumeditation.it>2017-12-28 16:10:04 +0100
committerDave Andreoli <dave@gurumeditation.it>2017-12-28 16:10:04 +0100
commit15c7bbf5fab8d9d7478b906323fde2c6c147a6d7 (patch)
tree173c60b1c379a04ec666b2ee677d32df3c2e5fff /src/scripts/pyolian/generator.py
parent455f2414635237ed8cebbd1d8ab324d8ec7c167f (diff)
Pyolian template-based generator.
This is a really powerfull tool that can be used to generate anything eolian releted just providing a template file. You can then render the template with the wanted scope (class, namespace, enum, etc) For example give a try at this (from the src/srcipts/pyolian folder): ./generator.py test_gen_class.template --cls Efl.Loop.Timer or ./generator.py -h for the full help Next step: maybe generate the new efl API doc using this tool? @andy I think this will make your life much easier :)
Diffstat (limited to 'src/scripts/pyolian/generator.py')
-rwxr-xr-xsrc/scripts/pyolian/generator.py206
1 files changed, 206 insertions, 0 deletions
diff --git a/src/scripts/pyolian/generator.py b/src/scripts/pyolian/generator.py
new file mode 100755
index 0000000000..b1e9bca786
--- /dev/null
+++ b/src/scripts/pyolian/generator.py
@@ -0,0 +1,206 @@
1#!/usr/bin/env python3
2# encoding: utf-8
3"""
4
5Pyolian template based generator.
6
7This is a really powerfull template-based, output-agnostic eolian generator.
8You just need a template file and then you can render it with the
9wanted eolian scope (class, namespace, enum, struct, etc...).
10
11For example (from this source folder):
12
13./generator.py test_gen_class.template --cls Efl.Loop.Timer
14./generator.py test_gen_namespace.template --ns Efl.Ui
15
16...of course you can pass any other class or namespace to the example above.
17
18You can also import this module and use the provided Template class if you
19are more confortable from within python.
20
21The generator is based on the great pyratemp engine (THANKS!), you can find
22the full template syntax at: www.simple-is-better.org/template/pyratemp.html
23
24Just keep in mind the syntax is a bit different in this implementation:
25
26 sub_start = "${" Was "$!" in original pyratemp (and in docs)
27 sub_end = "}$" Was "!$" in original pyratemp (and in docs)
28 _block_start = "<!--("
29 _block_end = ")-->"
30 comment_start = "#!"
31 comment_end = "!#"
32
33"""
34import os
35import datetime
36
37import eolian
38import pyratemp
39
40
41# logging utils
42be_verbose = True
43def ERR(*args): print(*(('PYOLIANGEN ERROR:', ) + args))
44def WRN(*args): print(*(('PYOLIANGEN WARNING:', ) + args))
45def INF(*args): print(*(('PYOLIANGEN ', ) + args))
46
47
48# Use .eo files from the source tree (not the installed ones)
49script_path = os.path.dirname(os.path.realpath(__file__))
50root_path = os.path.abspath(os.path.join(script_path, '..', '..', '..'))
51SCAN_FOLDER = os.path.join(root_path, 'src', 'lib')
52
53
54# load the whole eolian db
55eolian_db = eolian.Eolian()
56if not isinstance(eolian_db, eolian.Eolian):
57 raise(RuntimeError('Eolian, failed to create Eolian state'))
58
59if not eolian_db.directory_scan(SCAN_FOLDER):
60 raise(RuntimeError('Eolian, failed to scan source directory'))
61
62if not eolian_db.all_eot_files_parse():
63 raise(RuntimeError('Eolian, failed to parse all EOT files'))
64
65if not eolian_db.all_eo_files_parse():
66 raise(RuntimeError('Eolian, failed to parse all EO files'))
67
68# cleanup the database on exit
69import atexit
70def cleanup_db():
71 global eolian_db
72 del eolian_db
73atexit.register(cleanup_db)
74
75
76class Template(pyratemp.Template):
77 """ Pyolian template based generator.
78
79 You can directly use this class to generate custom outputs based
80 on the eolian database and your provided templates.
81
82 Usage is as simple as:
83 t = Template(<template_file>)
84 t.render(<output_file>, cls=..., ns=..., ...)
85
86 Args:
87 filename: Template file to load. (REQUIRED)
88 data: User provided context for the template.
89 """
90 def __init__(self, filename, encoding='utf-8', data=None, escape=None,
91 loader_class=pyratemp.LoaderFile,
92 parser_class=pyratemp.Parser,
93 renderer_class=pyratemp.Renderer,
94 eval_class=pyratemp.EvalPseudoSandbox):
95
96 # Build the global context for the template
97 global_ctx = {}
98 # user provided context (low pri)
99 if data:
100 global_ctx.update(data)
101 # standard names (not overwritables)
102 global_ctx.update({
103 # Template info
104 'date': datetime.datetime.now(),
105 'template_file': os.path.basename(filename),
106 # Eolian info
107 # 'eolian_version': eolian.__version__,
108 # 'eolian_version_info': eolian.__version_info__,
109 # Eolian Enums
110 'Eolian_Function_Type': eolian.Eolian_Function_Type,
111 'Eolian_Parameter_Dir': eolian.Eolian_Parameter_Dir,
112 'Eolian_Class_Type': eolian.Eolian_Class_Type,
113 'Eolian_Object_Scope': eolian.Eolian_Object_Scope,
114 'Eolian_Typedecl_Type': eolian.Eolian_Typedecl_Type,
115 'Eolian_Type_Type': eolian.Eolian_Type_Type,
116 'Eolian_Type_Builtin_Type': eolian.Eolian_Type_Builtin_Type,
117 'Eolian_C_Type_Type': eolian.Eolian_C_Type_Type,
118 'Eolian_Expression_Type': eolian.Eolian_Expression_Type,
119 'Eolian_Expression_Mask': eolian.Eolian_Expression_Mask,
120 'Eolian_Variable_Type': eolian.Eolian_Variable_Type,
121 'Eolian_Binary_Operator': eolian.Eolian_Binary_Operator,
122 'Eolian_Unary_Operator': eolian.Eolian_Unary_Operator,
123 'Eolian_Declaration_Type': eolian.Eolian_Declaration_Type,
124 'Eolian_Doc_Token_Type': eolian.Eolian_Doc_Token_Type,
125 'Eolian_Doc_Ref_Type': eolian.Eolian_Doc_Ref_Type,
126 })
127
128 # Call the parent __init__ func
129 self.template_filename = filename
130 pyratemp.Template.__init__(self, filename=filename, encoding=encoding,
131 data=global_ctx, escape=escape,
132 loader_class=loader_class,
133 parser_class=parser_class,
134 renderer_class=renderer_class,
135 eval_class=eval_class)
136
137 def render(self, filename=None, cls=None, ns=None,
138 struct=None, enum=None, alias=None, **kargs):
139 # Build the context for the template
140 ctx = {}
141 if kargs:
142 ctx.update(kargs)
143 if cls:
144 ctx['cls'] = eolian_db.class_get_by_name(cls)
145 if struct:
146 ctx['struct'] = eolian_db.typedecl_struct_get_by_name(struct)
147 if enum:
148 ctx['enum'] = eolian_db.typedecl_enum_get_by_name(enum)
149 if alias:
150 ctx['alias'] = eolian_db.typedecl_alias_get_by_name(alias)
151 if ns:
152 ctx['namespace'] = ns
153 ctx['namespaces'] = ns.split('.')
154 ctx['classes'] = [ c for c in eolian_db.all_classes
155 if c.full_name.startswith(ns + '.') ]
156 ctx['aliases'] = [ a for a in eolian_db.typedecl_all_aliases
157 if a.full_name.startswith(ns + '.') ]
158 ctx['structs'] = [ s for s in eolian_db.typedecl_all_structs
159 if s.full_name.startswith(ns + '.') ]
160 ctx['enums'] = [ e for e in eolian_db.typedecl_all_enums
161 if e.full_name.startswith(ns + '.') ]
162
163 if filename is not None:
164 INF('generating "%s" from template "%s"' % (
165 filename, self.template_filename))
166
167 # render with the augmented context
168 output = self(**ctx)
169
170 if filename is not None:
171 # create directory tree if needed
172 folder = os.path.dirname(filename)
173 if folder and not os.path.isdir(folder):
174 os.makedirs(folder)
175 # write to file
176 with open(filename, "w") as f:
177 f.write(output)
178 else:
179 # or print to stdout
180 print(output)
181
182
183if __name__ == '__main__':
184 import argparse
185
186 parser = argparse.ArgumentParser(description='Pyolian generator.')
187 parser.add_argument('template',
188 help='The template file to use. (REQUIRED)')
189 parser.add_argument('--output', '-o', metavar='FILE', default=None,
190 help='Where to write the rendered output. '
191 'If not given will print to stdout.')
192 parser.add_argument('--cls', metavar='CLASS_NAME', default=None,
193 help='The full name of the class to render, ex: Efl.Loop.Timer')
194 parser.add_argument('--ns', metavar='NAMESPACE', default=None,
195 help='The namespace to render, ex: Efl.Loop')
196 parser.add_argument('--struct', metavar='STRUCT_NAME', default=None,
197 help='The name of the struct to render, ex: Efl.Loop.Arguments')
198 parser.add_argument('--enum', metavar='ENUM_NAME', default=None,
199 help='The name of the enum to render, ex: Efl.Loop.Handler.Flags')
200 parser.add_argument('--alias', metavar='ALIAS_NAME', default=None,
201 help='The name of the alias to render, ex: Efl.Font.Size')
202 args = parser.parse_args()
203
204 t = Template(args.template)
205 t.render(args.output, cls=args.cls, ns=args.ns,
206 struct=args.struct, enum=args.enum, alias=args.alias)