diff options
author | Juergen Bocklage-Ryannel <juergen@ryannel.org> | 2018-12-24 13:28:16 +0100 |
---|---|---|
committer | Juergen Bocklage-Ryannel <juergen@ryannel.org> | 2018-12-24 13:28:16 +0100 |
commit | f354532d7cc9e3663b005f5413baf03833c60398 (patch) | |
tree | e2bcb28bb3d8f3d3efc86bb1c064380cd5579e60 | |
parent | 22840dd0a67644afd20c1b9c4dfffa2c23cb4c6d (diff) |
- introduce qface script mode
- add ability to load dynamic filters for qface script mode
- extract generic filters
- fixed some issues with cpp macros
-rw-r--r-- | docs/index.rst | 1 | ||||
-rw-r--r-- | docs/script.rst | 40 | ||||
-rw-r--r-- | qface/__about__.py | 4 | ||||
-rw-r--r-- | qface/app.py | 42 | ||||
-rw-r--r-- | qface/filters.py | 68 | ||||
-rw-r--r-- | qface/generator.py | 7 | ||||
-rw-r--r-- | qface/helper/generic.py | 52 | ||||
-rw-r--r-- | qface/helper/qtcpp.py | 6 | ||||
-rw-r--r-- | qface/templates/qface/qtcpp.j2 | 12 | ||||
-rw-r--r-- | qface/utils.py | 13 |
10 files changed, 173 insertions, 72 deletions
diff --git a/docs/index.rst b/docs/index.rst index 0212095..12a05b7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -18,6 +18,7 @@ Several code generators for common use cases have already been implemented. Thes json domain extending + script api diff --git a/docs/script.rst b/docs/script.rst new file mode 100644 index 0000000..fb3def2 --- /dev/null +++ b/docs/script.rst @@ -0,0 +1,40 @@ +*********** +Script Mode +*********** + +In the script mode, qface is used as the qface exectuable. In this mode the code generator consits of a rule document and template files and optionally a filter module. + +Whereas normally the generator writer create an own python package in this module only some documents are needed and the qface script is used. + +Setup +===== + +To get started create a `qface-rules.yml` document and a templates folder:: + + qface-rules.yml + templates/ + + +In the rules file you provide the code generation rules accoring to the rule generator documentation. The templates folder will contain the required templates. + +Filters +======= + +To provide extra filder you need to create a `filters.py` document with the declaration of your filters:: + + def echo(s): + print(s) + return "World" + + filters['echo'] = echo + +The filters module will be loaded by qface and all entries to the filters dictionary are added to the global lists of Jinja filters. + +Running +======= + +To run now the generator you can simply call:: + + qface --rules qface-rules.yml --target out counter.qface + +This will take your rules and generate the files inside the out folder based on the `counter.qface` interface file. diff --git a/qface/__about__.py b/qface/__about__.py index e63ff15..0ed253d 100644 --- a/qface/__about__.py +++ b/qface/__about__.py @@ -9,7 +9,7 @@ except NameError: __title__ = "qface" __summary__ = "A generator framework based on a common modern IDL" __url__ = "https://pelagicore.github.io/qface/" -__version__ = "1.9.1" +__version__ = "2.0.0" __author__ = "JRyannel" -__author_email__ = "qface-generator@googlegroups.com" +__author_email__ = "" __copyright__ = "2019 Pelagicore" diff --git a/qface/app.py b/qface/app.py index 800744a..38a26d6 100644 --- a/qface/app.py +++ b/qface/app.py @@ -7,39 +7,61 @@ import logging from path import Path from qface.generator import FileSystem, RuleGenerator from qface.watch import monitor +from qface.utils import load_filters here = Path(__file__).dirname() logging.basicConfig() -def run(spec, src, dst): +def run(spec, src, dst, features, force): spec = Path(spec) project = Path(dst).name system = FileSystem.parse(src) - context = { + extra_filters_path = spec.dirname() / 'filters.py' + extra_filters = load_filters(extra_filters_path) + + ctx = { 'dst': dst, 'system': system, 'project': project, } - generator = RuleGenerator(search_path=spec.dirname() / 'templates', destination=dst, context=context) + generator = RuleGenerator( + search_path=spec.dirname() / 'templates', + destination=dst, + context=ctx, + features=features, + force=force + ) + generator.filters = extra_filters generator.process_rules(spec, system) @click.command() -@click.option('--spec', type=click.Path(exists=True, file_okay=True)) -@click.option('--dst', type=click.Path(exists=False, file_okay=False)) +@click.option('--rules', type=click.Path(exists=True, file_okay=True)) +@click.option('--target', type=click.Path(exists=False, file_okay=False)) @click.option('--reload/--no-reload', default=False, help="Auto reload script on changes") -@click.argument('src', nargs=-1, type=click.Path(exists=True)) -def main(spec, dst, reload, src): - spec = Path(spec) +@click.option('--scaffold/--no-scaffold', default=False, help="Add extrac scaffolding code") +@click.option('--watch', type=click.Path(exists=False, file_okay=False)) +@click.option('--feature', multiple=True) +@click.option('--force/--no-force', default=False, help="forces overwriting of files") +@click.argument('source', nargs=-1, type=click.Path(exists=True)) +def main(rules, target, reload, source, watch, scaffold, feature, force): + rules = Path(rules) if reload: argv = sys.argv.copy() argv.remove('--reload') - monitor(args=argv, watch=src + (spec.dirname(),)) + watch_list = list(source) + watch_list.append(rules.dirname()) + if watch: + watch_list.append(watch) + monitor(args=argv, watch=watch_list) else: - run(spec, src, dst) + features = set(feature) + if scaffold: + features.add('scaffold') + run(rules, source, target, features=features, force=force) if __name__ == '__main__': diff --git a/qface/filters.py b/qface/filters.py index 5969a64..236c7b3 100644 --- a/qface/filters.py +++ b/qface/filters.py @@ -1,58 +1,28 @@ -import json -import hashlib +from .helper import generic from .helper import qtqml from .helper import qtcpp from .helper import doc +import importlib.util -def jsonify(symbol): - """ returns json format for symbol """ - try: - # all symbols have a toJson method, try it - return json.dumps(symbol.toJson(), indent=' ') - except AttributeError: - pass - return json.dumps(symbol, indent=' ') +def get_filters(): + filters = {} + filters.update(generic.get_filters()) + filters.update(qtqml.Filters.get_filters()) + filters.update(qtcpp.Filters.get_filters()) + filters.update(doc.get_filters()) + return filters -def upper_first(s): - """ uppercase first letter """ - s = str(s) - return s[0].upper() + s[1:] +def load_filters(path): + if not path.exists(): + print('filter module does not exist') + return {} + extra_filters = {} + spec = importlib.util.spec_from_file_location('filters', path.abspath()) + filters_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(filters_module) + filters_module.get_filters(extra_filters) + return extra_filters -def lower_first(s): - s = str(s) - return s[0].lower() + s[1:] - - -def hash(symbol, hash_type='sha1'): - """ create a hash code from symbol """ - code = hashlib.new(hash_type) - code.update(str(symbol).encode('utf-8')) - return code.hexdigest() - - -def path(symbol): - """ replaces '.' with '/' """ - return str(symbol).replace('.', '/') - - -def identifier(s): - return str(s).lower().replace('.', '_') - - -filters = { - 'jsonify': jsonify, - 'upper_first': upper_first, - 'lower_first': lower_first, - 'upperfirst': upper_first, - 'lowerfirst': lower_first, - 'hash': hash, - 'path': path, - 'identifier': identifier, -} - -filters.update(qtqml.Filters.get_filters()) -filters.update(qtcpp.Filters.get_filters()) -filters.update(doc.get_filters()) diff --git a/qface/generator.py b/qface/generator.py index eb3172d..b4f93df 100644 --- a/qface/generator.py +++ b/qface/generator.py @@ -21,7 +21,7 @@ from .idl.parser.TListener import TListener from .idl.profile import EProfile from .idl.domain import System from .idl.listener import DomainListener -from .filters import filters +from .filters import get_filters from jinja2.debug import make_traceback as _make_traceback @@ -94,7 +94,7 @@ class Generator(object): lstrip_blocks=True, ) self.env.exception_handler = template_error_handler - self.env.filters.update(filters) + self.env.filters.update(get_filters()) self._destination = Path() self._path = Path() self._source = '' @@ -170,6 +170,9 @@ class Generator(object): """Using a template file name it renders a template into a file given a context """ + if not file_path or not template: + click.secho('source or target missing for document') + return if not context: context = self.context error = False diff --git a/qface/helper/generic.py b/qface/helper/generic.py new file mode 100644 index 0000000..713952d --- /dev/null +++ b/qface/helper/generic.py @@ -0,0 +1,52 @@ +import json +import hashlib + + +def jsonify(symbol): + """ returns json format for symbol """ + try: + # all symbols have a toJson method, try it + return json.dumps(symbol.toJson(), indent=' ') + except AttributeError: + pass + return json.dumps(symbol, indent=' ') + + +def upper_first(s): + """ uppercase first letter """ + s = str(s) + return s[0].upper() + s[1:] + + +def lower_first(s): + s = str(s) + return s[0].lower() + s[1:] + + +def hash(symbol, hash_type='sha1'): + """ create a hash code from symbol """ + code = hashlib.new(hash_type) + code.update(str(symbol).encode('utf-8')) + return code.hexdigest() + + +def path(symbol): + """ replaces '.' with '/' """ + return str(symbol).replace('.', '/') + + +def identifier(s): + return str(s).lower().replace('.', '_') + + +def get_filters(): + return { + 'jsonify': jsonify, + 'upper_first': upper_first, + 'lower_first': lower_first, + 'upperfirst': upper_first, + 'lowerfirst': lower_first, + 'hash': hash, + 'path': path, + 'identifier': identifier, + } diff --git a/qface/helper/qtcpp.py b/qface/helper/qtcpp.py index aeae6f0..c9e6835 100644 --- a/qface/helper/qtcpp.py +++ b/qface/helper/qtcpp.py @@ -36,7 +36,7 @@ class Filters(object): return 'QVariant()' elif t.is_void: return '' - elif t.is_enum: + elif t.is_enumeration: value = next(iter(t.reference.members)) return '{0}::{0}Enum::{1}'.format(symbol.type, value) elif symbol.kind == 'enum': @@ -58,7 +58,7 @@ class Filters(object): @staticmethod def parameterType(symbol): prefix = Filters.classPrefix - if symbol.type.is_enum: + if symbol.type.is_enumeration: return '{0}::{0}Enum {1}'.format(symbol.type, symbol) if symbol.type.is_void or symbol.type.is_primitive: if symbol.type.is_string: @@ -88,7 +88,7 @@ class Filters(object): def returnType(symbol): prefix = Filters.classPrefix t = symbol.type - if t.is_enum: + if t.is_enumeration: return '{0}::{0}Enum'.format(symbol.type) if symbol.type.is_void or symbol.type.is_primitive: if t.is_string: diff --git a/qface/templates/qface/qtcpp.j2 b/qface/templates/qface/qtcpp.j2 index 729bfc9..b2e3bff 100644 --- a/qface/templates/qface/qtcpp.j2 +++ b/qface/templates/qface/qtcpp.j2 @@ -14,15 +14,15 @@ {%- endmacro %} {% macro property_decl(property, notifiable=True) -%} -Q_PROPERTY({{property|qt.returnType}} {{property}} READ {{property}} {% if not property.readonly %}WRITE set{{property|upperfirst}}{% endif %}{% if not property.const and notifiable %} NOTIFY {{property}}Changed{% endif %}) +Q_PROPERTY({{property|qt.returnType}} {{property}} READ {{property}} {% if not property.readonly %}WRITE push{{property|upperfirst}}{% endif %}{% if not property.const and notifiable %} NOTIFY {{property}}Changed{% endif %}) {%- endmacro %} -{% macro property_setter_decl(property, ending=";") -%} -void set{{property|upperfirst}}({{ property|qt.parameterType }}){{ending}} +{% macro property_setter_decl(property, ending=";", prefix='virtual') -%} +{{prefix}} void push{{property|upperfirst}}({{ property|qt.parameterType }}){{ending}} {%- endmacro %} -{% macro property_getter_decl(property, ending=";") -%} -{{property|qt.returnType}} {{property}}() const{{ending}} +{% macro property_getter_decl(property, ending=";", prefix='virtual') -%} +{{prefix}} {{property|qt.returnType}} {{property}}() const{{ending}} {%- endmacro %} {% macro signal_decl(symbol, postfix="") -%} @@ -41,7 +41,7 @@ void {{symbol}}{{postfix}}(); {{doc.description|join("\n ")}} {% endwith %} */ -void {{class}}::set{{property|upperfirst}}({{ property|qt.parameterType }}) +void {{class}}::push{{property|upperfirst}}({{ property|qt.parameterType }}) { {% if notifiable %} if (m_{{property}} != {{property}}) { diff --git a/qface/utils.py b/qface/utils.py index f816b58..889d862 100644 --- a/qface/utils.py +++ b/qface/utils.py @@ -9,3 +9,16 @@ def module_info(text): 'title': module.name, 'brief': " ".join(doc.parse_doc(module.comment).brief) } + + +def load_filters(path): + if not path.exists(): + print('filter module does not exist') + return {} + + ctx = { + 'filters': {} + } + exec(path.text(), ctx) + return ctx['filters'] + |