|
| 1 | +import json |
| 2 | +from os.path import join |
| 3 | +import pathlib |
| 4 | +import typing |
| 5 | + |
| 6 | +import jinja2 |
| 7 | + |
| 8 | +from .j2_context import HeaderContext |
| 9 | + |
| 10 | +templates_path = pathlib.Path(__file__).parent.absolute() |
| 11 | + |
| 12 | + |
| 13 | +class WrapperWriter: |
| 14 | + def __init__(self) -> None: |
| 15 | + # |
| 16 | + # Load all the templates first |
| 17 | + # |
| 18 | + |
| 19 | + self.env = jinja2.Environment( |
| 20 | + loader=jinja2.FileSystemLoader(searchpath=templates_path), |
| 21 | + undefined=jinja2.StrictUndefined, |
| 22 | + autoescape=False, |
| 23 | + auto_reload=False, |
| 24 | + # trim_blocks=True, |
| 25 | + # lstrip_blocks=True, |
| 26 | + ) |
| 27 | + |
| 28 | + # c++ file generated per-header |
| 29 | + self.header_cpp_j2 = self.env.get_template("header.cpp.j2") |
| 30 | + |
| 31 | + # class templates |
| 32 | + self.cls_tmpl_inst_cpp_j2 = self.env.get_template("cls_tmpl_inst.cpp.j2") |
| 33 | + self.cls_tmpl_inst_hpp_j2 = self.env.get_template("cls_tmpl_inst.hpp.j2") |
| 34 | + |
| 35 | + # rpy-include trampoline file |
| 36 | + self.rpy_include_hpp_j2 = self.env.get_template("cls_rpy_include.hpp.j2") |
| 37 | + |
| 38 | + def write_files( |
| 39 | + self, |
| 40 | + hctx: HeaderContext, |
| 41 | + name: str, |
| 42 | + cxx_gen_dir: str, |
| 43 | + hppoutdir: str, |
| 44 | + classdeps_json_fname: str, |
| 45 | + ) -> typing.List[str]: |
| 46 | + """Generates all files needed for a single processed header""" |
| 47 | + |
| 48 | + generated_sources: typing.List[str] = [] |
| 49 | + |
| 50 | + # Jinja requires input as a dictionary |
| 51 | + data = hctx.__dict__ |
| 52 | + |
| 53 | + # Write the cpp file first |
| 54 | + fname = join(cxx_gen_dir, f"{name}.cpp") |
| 55 | + generated_sources.append(fname) |
| 56 | + with open(fname, "w", encoding="utf-8") as fp: |
| 57 | + fp.write(self.header_cpp_j2.render(data)) |
| 58 | + |
| 59 | + # Then the json, no need for jinja here |
| 60 | + with open(classdeps_json_fname, "w", encoding="utf-8") as fp: |
| 61 | + json.dump(hctx.class_hierarchy, fp) |
| 62 | + |
| 63 | + # Generate an rpy-include file for each class that has either a trampoline |
| 64 | + # or a template class |
| 65 | + for cls in hctx.classes: |
| 66 | + if not cls.template and not cls.trampoline: |
| 67 | + continue |
| 68 | + |
| 69 | + data["cls"] = cls |
| 70 | + fname = join( |
| 71 | + hppoutdir, f"{cls.namespace.replace(':', '_')}__{cls.cpp_name}.hpp" |
| 72 | + ) |
| 73 | + with open(fname, "w", encoding="utf-8") as fp: |
| 74 | + fp.write(self.rpy_include_hpp_j2.render(data)) |
| 75 | + |
| 76 | + # Each class template is instantiated in a separate cpp file to lessen |
| 77 | + # compiler memory requirements when compiling obnoxious templates |
| 78 | + if hctx.template_instances: |
| 79 | + # Single header output that holds all the struct outlines |
| 80 | + fname = join(cxx_gen_dir, f"{name}_tmpl.hpp") |
| 81 | + with open(fname, "w", encoding="utf-8") as fp: |
| 82 | + fp.write(self.cls_tmpl_inst_hpp_j2.render(data)) |
| 83 | + |
| 84 | + # Each cpp file has a single class template instance |
| 85 | + for i, tmpl_data in enumerate(hctx.template_instances): |
| 86 | + data["tmpl_data"] = tmpl_data |
| 87 | + fname = join(hppoutdir, f"{name}_tmpl{i+1}.cpp") |
| 88 | + generated_sources.append(fname) |
| 89 | + with open(fname, "w", encoding="utf-8") as fp: |
| 90 | + fp.write(self.cls_tmpl_inst_cpp_j2.render(data)) |
| 91 | + |
| 92 | + return generated_sources |
| 93 | + |
| 94 | + def _render_template(self, tmpl_src: str, dst: str, data: dict): |
| 95 | + jtmpl = self.env.get_template(tmpl_src) |
| 96 | + content = jtmpl.render(data) |
| 97 | + with open(dst, "w", encoding="utf-8") as fp: |
| 98 | + fp.write(content) |
0 commit comments