1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3 4# 5# Copyright 2017, Data61 6# Commonwealth Scientific and Industrial Research Organisation (CSIRO) 7# ABN 41 687 119 230. 8# 9# This software may be distributed and modified according to the terms of 10# the BSD 2-Clause license. Note that NO WARRANTY is provided. 11# See "LICENSE_BSD2.txt" for details. 12# 13# @TAG(DATA61_BSD) 14# 15 16'''Brief wrapper around the jinja2 templating engine.''' 17 18from __future__ import absolute_import, division, print_function, \ 19 unicode_literals 20from camkes.internal.seven import cmp, filter, map, zip 21 22from .Context import new_context 23from camkes.templates import TemplateError 24 25import jinja2 26import os 27import platform 28import six 29import sys 30 31# Jinja is setup by default for HTML templating. We tweak the delimiters to 32# make it more suitable for C. 33START_BLOCK = '/*-' 34END_BLOCK = '-*/' 35START_VARIABLE = '/*?' 36END_VARIABLE = '?*/' 37START_COMMENT = '/*#' 38END_COMMENT = '#*/' 39 40 41def get_leaves(d): 42 '''Generator that yields the leaves of a hierarchical dictionary. See usage 43 below.''' 44 assert isinstance(d, dict) 45 for v in d.values(): 46 if isinstance(v, dict): 47 # We're at an intermediate node. Yield all leaves below it. 48 for x in get_leaves(v): 49 yield x 50 else: 51 # We're at a leaf node. 52 yield v 53 54 55class FileSystemLoaderWithLog(jinja2.FileSystemLoader): 56 57 def __init__(self, path): 58 super(FileSystemLoaderWithLog, self).__init__(path) 59 self.files = set() 60 61 def get_source(self, environment, template): 62 (source, filename, uptodate) = super( 63 FileSystemLoaderWithLog, self).get_source(environment, template) 64 self.files.add(filename) 65 return (source, filename, uptodate) 66 67 68class Renderer(object): 69 def __init__(self, templates): 70 # This function constructs a Jinja environment for our templates. 71 72 self.loaders = [] 73 74 # Source templates. 75 self.loaders.extend(FileSystemLoaderWithLog(os.path.abspath(x)) for x in 76 templates) 77 78 self.env = jinja2.Environment( 79 loader=jinja2.ChoiceLoader(self.loaders), 80 extensions=["jinja2.ext.do", "jinja2.ext.loopcontrols"], 81 block_start_string=START_BLOCK, 82 block_end_string=END_BLOCK, 83 variable_start_string=START_VARIABLE, 84 variable_end_string=END_VARIABLE, 85 comment_start_string=START_COMMENT, 86 comment_end_string=END_COMMENT, 87 auto_reload=False, 88 undefined=jinja2.StrictUndefined) 89 90 def render(self, me, assembly, template, render_state, state_key, outfile_name, 91 **kwargs): 92 context = new_context(me, assembly, render_state, state_key, outfile_name, 93 **kwargs) 94 95 t = self.env.get_template(template) 96 try: 97 return t.render(context) 98 except TemplateError: 99 raise 100 except Exception as e: 101 # Catch and re-cast any other exceptions to allow the runner to 102 # handle them as usual and prevent us barfing stack traces when 103 # exceptions aren't our fault. 104 six.reraise(TemplateError, TemplateError('unhandled exception in ' 105 'template %s: %s' % (template, e)), sys.exc_info()[2]) 106 107 def get_files_used(self): 108 files = set() 109 for x in self.loaders: 110 files |= x.files 111 return files 112