#! /usr/bin/env python3 # import json5 import argparse from pathlib import Path from jinja2 import Environment, FileSystemLoader def load_idl(path): with open(path) as f: return json5.load(f) def format_method_qualifiers(method): """ Build qualifier string: const noexcept """ quals = [] if method.get('const', False): quals.append(' const') if method.get('noexcept', False): quals.append(' noexcept') return ' '.join(quals) def format_method_staticqual(method): """ Build qualifier string for a static method: noexcept """ quals = [] if method.get('noexcept', False): quals.append(' noexcept') return ' '.join(quals) def format_args(args, include_names=True): """ Format argument list for a method """ if not args: return '' if include_names: return ', '.join(f"{p['type']} {p['name']}" for p in args) else: return ', '.join(p['type'] for p in args) def format_args_nonames(args): return format_args(args, False) def format_arg_names(args): """ Format argument names, for forwarding """ names = [p['name'] for p in args] return ', '.join([f"_dcast({names[0]})"] + names[1:]) def format_arg_names_nodata(args): """ Format argument names for forwarding, omit data ('self') arg """ names = [p['name'] for p in args[1:]] return ', '.join(names) def format_args_nodata(args): """ Format arguments, but exclude data arg """ return format_args(args[1:]) def format_args_routing(args): """ Format argument names, for routing """ names = [p['name'] for p in args] return ', '.join([f"O::data()"] + names[1:]) def format_args_impl_const(args, drepr): """ Format argument names, for implementation (IFoo_DRepr) """ names = [f"{p['type']} {p['name']}" for p in args] return ', '.join([f"const {drepr} & self"] + names[1:]) def format_args_impl_nonconst(args, drepr): """ Format argument names, for implementation (IFoo_DRepr) """ names = [f"{p['type']} {p['name']}" for p in args] return ', '.join([f"{drepr} & self"] + names[1:]) def gen_facet(env, idl_fname, idl, output_hpp_dir, #output_impl_hpp_subdir, output_cpp_dir): # true to insert doxygen markup in generated .hpp/.cpp files using_dox = idl['using_doxygen'] # extra include files (or perhaps other definitions) facet_includes = idl['includes'] # extra (post) includes for user .hpp e.g. Sequence.hpp user_hpp_includes = idl['user_hpp_includes'] # arbitrary text after includes, before opening namespaces facet_pretext = idl['pretext'] # detail facet_detail_subdir = idl['detail_subdir'] facet_ns1 = idl['namespace1'] facet_ns2 = idl['namespace2'] facet_name = idl['facet'] # e.g. Sequence facet_name_lc = facet_name.lower() facet_brief = idl['brief'] facet_doc = '\n'.join(idl['doc']) output_impl_hpp_dir = output_hpp_dir / facet_detail_subdir types = idl['types'] for ty in types: ty['doc'] = '\n'.join(ty['doc']) const_methods = idl['const_methods'] for md in const_methods: md['args'] = [{'type': "Copaque", 'name': "data"}] + md['args'] md['doc'] = '\n'.join(md['doc']) nonconst_methods = idl['nonconst_methods'] for md in nonconst_methods: md['args'] = [{'type': "Opaque", 'name': "data"}] + md['args'] md['doc'] = '\n'.join(md['doc']) router_facet_explicit_content = idl.get('router_facet_explicit_content', []) # Foo.hpp facet_hpp_fname = f'{facet_name}.hpp' # AFoo abstract_facet = f'A{facet_name}' # AFoo.hpp abstract_facet_fname = f'{abstract_facet}.hpp' # IFoo iface_facet = f'I{facet_name}' # IFoo_ImplType iface_facet_impltype = f'{iface_facet}_ImplType' # # IFoo_Any iface_facet_any = f'{iface_facet}_Any' # IFoo_Any.hpp iface_facet_any_hpp_fname = f'{iface_facet_any}.hpp' # IFoo_Any.cpp iface_facet_any_cpp_fname = f'{iface_facet_any}.cpp' # # IFoo_Xfer iface_facet_xfer = f'{iface_facet}_Xfer' # IFoo_Xfer.hpp iface_facet_xfer_hpp_fname = f'{iface_facet_xfer}.hpp' # IFoo_Xfer.cpp iface_facet_xfer_cpp_fname = f'{iface_facet_xfer}.cpp' # # RFoo router_facet = f'R{facet_name}' # RFoo.hpp router_facet_hpp_fname = f'{router_facet}.hpp' # REMINDER: this context for FACET definition, e.g. AGCObject context = { 'genfacet': 'xo-facet/codegen/genfacet', 'genfacet_input': idl_fname, 'using_dox': using_dox, 'impl_hpp_subdir': facet_detail_subdir, # legacy name 'facet_detail_subdir': facet_detail_subdir, # 'facet_hpp_j2': 'facet.hpp.j2', 'facet_includes': facet_includes, 'facet_pretext': facet_pretext, 'facet_ns1': facet_ns1, 'facet_ns2': facet_ns2, 'facet_name_lc': facet_name_lc, 'facet_hpp_fname': facet_hpp_fname, # 'user_hpp_includes': user_hpp_includes, #'name': facet_name, 'idl_fname': idl_fname, # 'abstract_facet_hpp_j2': 'abstract_facet.hpp.j2', 'abstract_facet': abstract_facet, 'abstract_facet_fname': abstract_facet_fname, 'abstract_facet_doc': facet_doc, # 'iface_facet': iface_facet, 'iface_facet_impltype': iface_facet_impltype, # 'iface_facet_any': iface_facet_any, 'iface_facet_any_hpp_j2': 'iface_facet_any.hpp.j2', 'iface_facet_any_cpp_j2': 'iface_facet_any.cpp.j2', 'iface_facet_any_hpp_fname': iface_facet_any_hpp_fname, 'iface_facet_any_cpp_fname': iface_facet_any_cpp_fname, # 'iface_facet_xfer': iface_facet_xfer, 'iface_facet_xfer_hpp_j2': 'iface_facet_xfer.hpp.j2', 'iface_facet_xfer_cpp_j2': 'iface_facet_xfer.cpp.j2', 'iface_facet_xfer_hpp_fname': iface_facet_xfer_hpp_fname, 'iface_facet_xfer_cpp_fname': iface_facet_xfer_cpp_fname, # 'router_facet': router_facet, 'router_facet_hpp_j2': 'router_facet.hpp.j2', 'router_facet_hpp_fname': router_facet_hpp_fname, 'router_facet_explicit_content': router_facet_explicit_content, # 'types': types, # 'const_methods': const_methods, # 'nonconst_methods': nonconst_methods, } # generate .hpp files templates = {} templates[facet_hpp_fname] = [output_hpp_dir, context['facet_hpp_j2']] templates[abstract_facet_fname] = [output_impl_hpp_dir, context['abstract_facet_hpp_j2']] templates[iface_facet_any_hpp_fname] = [output_impl_hpp_dir, context['iface_facet_any_hpp_j2']] templates[iface_facet_any_cpp_fname] = [output_cpp_dir, context['iface_facet_any_cpp_j2']] templates[iface_facet_xfer_hpp_fname] = [output_impl_hpp_dir, context['iface_facet_xfer_hpp_j2']] templates[router_facet_hpp_fname] = [output_impl_hpp_dir, context['router_facet_hpp_j2']] for out_file, record in templates.items(): out_dir = record[0] template_name = record[1] print(f'out_dir: [{out_dir}]') print(f'out_file: [{out_file}]') print(f'template_name: [{template_name}]') template = env.get_template(template_name) content = template.render(**context) (out_dir / out_file).write_text(content) print(f"Generated {out_dir}/{out_file}") def gen_facet_impl(env, idl_fname, idl, facet_idl, output_hpp_dir, output_impl_hpp_subdir, output_cpp_dir): # true to insert doxygen markup in generated .hpp/.cpp files using_dox = idl['using_doxygen'] # extra include files (or perhaps other definitions) # facet_includes: include section for AFoo.hpp: # #facet_includes = facet_idl['includes'] facet_includes = idl['includes'] # # sequence facet_detail_subdir = facet_idl['detail_subdir'] # xo - facet_ns1: outer namespace for facet [e.g. xo] facet_ns1 = facet_idl['namespace1'] # facet_ns2: nested namespace for facet [print] facet_ns2 = facet_idl['namespace2'] # Sequence - facet_name: facet name facet_name = facet_idl['facet'] # sequence - facet_name_lc: lower case [e.g. sequence] facet_name_lc = facet_name.lower() # brief doc for facet facet_brief = idl['brief'] # doc section for facet facet_doc = '\n'.join(idl['doc']) facet_types = facet_idl['types'] for ty in facet_types: ty['doc'] = '\n'.join(ty['doc']) const_methods = facet_idl['const_methods'] for md in const_methods: md['args'] = [{'type': "Copaque", 'name': "data"}] + md['args'] md['doc'] = '\n'.join(md['doc']) nonconst_methods = facet_idl['nonconst_methods'] for md in nonconst_methods: md['args'] = [{'type': "Opaque", 'name': "data"}] + md['args'] md['doc'] = '\n'.join(md['doc']) # Foo.hpp facet_hpp_fname = f'{facet_name}.hpp' # AFoo abstract_facet = f'A{facet_name}' # AFoo.hpp abstract_facet_fname = f'{abstract_facet}.hpp' # IFoo iface_facet = f'I{facet_name}' # IFoo_ImplType iface_facet_impltype = f'{iface_facet}_ImplType' # # IFoo_Any iface_facet_any = f'{iface_facet}_Any' # IFoo_Any.hpp iface_facet_any_hpp_fname = f'{iface_facet_any}.hpp' # IFoo_Any.cpp iface_facet_any_cpp_fname = f'{iface_facet_any}.cpp' # # IFoo_Xfer iface_facet_xfer = f'{iface_facet}_Xfer' # IFoo_Xfer.hpp iface_facet_xfer_hpp_fname = f'{iface_facet_xfer}.hpp' # IFoo_Xfer.cpp iface_facet_xfer_cpp_fname = f'{iface_facet_xfer}.cpp' # # RFoo router_facet = f'R{facet_name}' # RFoo.hpp router_facet_hpp_fname = f'{router_facet}.hpp' # user defined content -- whatever you want router_facet_explicit_content = facet_idl['router_facet_explicit_content'] # ================================================================ # vars for IFacet_DRepr # ---------------------------------------------------------------- # DList data_repr = idl['repr'] # dlist data_repr_lc = data_repr.lower() # DList.hpp data_repr_hpp_fname = f'{data_repr}.hpp' # repr_ns1: outer namespace for repr [e.g. xo]. # (need not match facet) repr_ns1 = idl['namespace1'] # repr_ns2: nested namespace for repr [e.g. scm]. repr_ns2 = idl['namespace2'] # local_types: addition type defs (e.g. repr_ns2 != facet_ns2) local_types = idl['local_types'] # iface_facet_repr: IFoo_DRepr iface_facet_repr = f'{iface_facet}_{data_repr}' # iface_facet_repr_hpp_fname: IFoo_DRepr.hpp iface_facet_repr_hpp_fname = f'{iface_facet_repr}.hpp' # iface_facet_repr_cpp_fname: IFoo_DRepr.cpp iface_facet_repr_cpp_fname = f'{iface_facet_repr}.cpp' # ================================================================ context = { 'genfacet': 'xo-facet/codegen/genfacet', 'genfacet_input': idl_fname, 'using_dox': using_dox, 'impl_hpp_subdir': output_impl_hpp_subdir, # 'facet_hpp_j2': 'facet.hpp.j2', 'facet_includes': facet_includes, 'facet_detail_subdir': facet_detail_subdir, 'facet_ns1': facet_ns1, 'facet_ns2': facet_ns2, 'facet_name': facet_name, 'facet_name_lc': facet_name_lc, 'facet_hpp_fname': facet_hpp_fname, #'name': facet_name, 'idl_fname': idl_fname, # 'abstract_facet_hpp_j2': 'abstract_facet.hpp.j2', 'abstract_facet': abstract_facet, 'abstract_facet_fname': abstract_facet_fname, 'abstract_facet_doc': facet_doc, # 'iface_facet': iface_facet, 'iface_facet_impltype': iface_facet_impltype, # 'iface_facet_any': iface_facet_any, 'iface_facet_any_hpp_j2': 'iface_facet_any.hpp.j2', 'iface_facet_any_cpp_j2': 'iface_facet_any.cpp.j2', 'iface_facet_any_hpp_fname': iface_facet_any_hpp_fname, 'iface_facet_any_cpp_fname': iface_facet_any_cpp_fname, # 'iface_facet_xfer': iface_facet_xfer, 'iface_facet_xfer_hpp_j2': 'iface_facet_xfer.hpp.j2', 'iface_facet_xfer_cpp_j2': 'iface_facet_xfer.cpp.j2', 'iface_facet_xfer_hpp_fname': iface_facet_xfer_hpp_fname, 'iface_facet_xfer_cpp_fname': iface_facet_xfer_cpp_fname, # 'router_facet': router_facet, 'router_facet_hpp_j2': 'router_facet.hpp.j2', 'router_facet_hpp_fname': router_facet_hpp_fname, 'router_facet_explicit_content': router_facet_explicit_content, # 'types': facet_types, 'local_types': local_types, # 'const_methods': const_methods, # 'nonconst_methods': nonconst_methods, # ---------------------------------------------------------------- # vars for IFacet_DRepr # ---------------------------------------------------------------- 'repr_ns1': repr_ns1, 'repr_ns2': repr_ns2, # 'data_repr': data_repr, 'data_repr_lc': data_repr_lc, 'data_repr_hpp_fname': data_repr_hpp_fname, # 'iface_facet_repr': iface_facet_repr, 'iface_facet_repr_hpp_j2': 'iface_facet_repr.hpp.j2', 'iface_facet_repr_hpp_fname': iface_facet_repr_hpp_fname, 'iface_facet_repr_cpp_j2': 'iface_facet_repr.cpp.j2', 'iface_facet_repr_cpp_fname': iface_facet_repr_cpp_fname, # } # generate .hpp files templates = {} templates[iface_facet_repr_hpp_fname] = [output_hpp_dir, context['iface_facet_repr_hpp_j2']] templates[iface_facet_repr_cpp_fname] = [output_cpp_dir, context['iface_facet_repr_cpp_j2']] # templates[facet_hpp_fname] = [output_hpp_dir, # context['facet_hpp_j2']] # templates[abstract_facet_fname] = [output_impl_hpp_dir, # context['abstract_facet_hpp_j2']] # templates[iface_facet_any_hpp_fname] = [output_impl_hpp_dir, # context['iface_facet_any_hpp_j2']] # templates[iface_facet_any_cpp_fname] = [output_cpp_dir, # context['iface_facet_any_cpp_j2']] # templates[iface_facet_xfer_hpp_fname] = [output_impl_hpp_dir, # context['iface_facet_xfer_hpp_j2']] # templates[router_facet_hpp_fname] = [output_impl_hpp_dir, # context['router_facet_hpp_j2']] for out_file, record in templates.items(): out_dir = record[0] template_name = record[1] print(f'out_dir: [{out_dir}]') print(f'out_file: [{out_file}]') print(f'template_name: [{template_name}]') template = env.get_template(template_name) content = template.render(**context) (out_dir / out_file).write_text(content) print(f"Generated {out_dir}/{out_file}") def main(): parser = argparse.ArgumentParser() parser.add_argument('--input', required=True, help='input IDL JSON5 file') # --facet-dir: only with mode=implementation parser.add_argument('--facet-dir', required=False, help='base dir for facet json') args = parser.parse_args() idl_fname = args.input idl = load_idl(idl_fname) output_hpp_dir = Path(idl['output_hpp_dir']) output_hpp_dir.mkdir(parents=False, exist_ok=True) # output_impl_hpp_subdir: prefer IDL, fall back to CLI output_impl_hpp_subdir = Path(idl['output_impl_subdir']) output_impl_hpp_dir = output_hpp_dir / output_impl_hpp_subdir output_impl_hpp_dir.mkdir(parents=False, exist_ok=True) output_cpp_dir = Path(idl['output_cpp_dir']) output_cpp_dir.mkdir(parents=False, exist_ok=True) # setup jinja2 #template_dir = Path(args.templates) template_dir = Path(__file__).parent #template_dir = Path(__file__).parent / 'codegen' print(f'template_dir: [{template_dir}]') env = Environment(loader = FileSystemLoader(template_dir), trim_blocks = True, lstrip_blocks = True, keep_trailing_newline = True) # custom filters. # A filter 'foo' provides ability to write '{{var | foo}}' to expand # j2 variable 'var' to 'filters[foo](var)' # env.filters['qualifiers'] = format_method_qualifiers env.filters['staticqual'] = format_method_staticqual env.filters['args'] = format_args env.filters['argtypes'] = format_args_nonames env.filters['argnames'] = format_arg_names env.filters['argnamesnodata'] = format_arg_names_nodata env.filters['argsnodata'] = format_args_nodata env.filters['argrouting'] = format_args_routing env.filters['argimplconst'] = format_args_impl_const env.filters['argimplnonconst'] = format_args_impl_nonconst if idl['mode'] == 'facet': gen_facet(env=env, idl_fname=idl_fname, idl=idl, output_hpp_dir=output_hpp_dir, output_cpp_dir=output_cpp_dir) elif idl['mode'] == 'implementation': # idl: json5 for (iface, data) combination # facet: json5 for abstract iface facet_idl_fname = args.facet_dir + '/' + idl['facet_idl'] facet_idl = load_idl(facet_idl_fname) gen_facet_impl(env=env, idl_fname=idl_fname, idl=idl, facet_idl=facet_idl, output_hpp_dir=output_impl_hpp_dir, output_impl_hpp_subdir=output_impl_hpp_subdir, output_cpp_dir=output_cpp_dir) if __name__ == '__main__': main()