1#!/usr/bin/env python3 2# Copyright 2015 gRPC authors. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15"""Simple Mako renderer. 16 17Just a wrapper around the mako rendering library. 18""" 19 20import getopt 21import importlib.util 22import os 23import pickle 24import shutil 25import sys 26 27import yaml 28from mako.lookup import TemplateLookup 29from mako.runtime import Context 30from mako.template import Template 31 32import bunch 33 34 35# Imports a plugin 36def import_plugin(path): 37 module_name = os.path.basename(path).replace('.py', '') 38 spec = importlib.util.spec_from_file_location(module_name, path) 39 module = importlib.util.module_from_spec(spec) 40 sys.modules[module_name] = module 41 spec.loader.exec_module(module) 42 return module 43 44 45def out(msg): 46 print(msg, file=sys.stderr) 47 48 49def showhelp(): 50 out('mako-renderer.py [-o out] [-m cache] [-P preprocessed_input] [-d dict] [-d dict...]' 51 ' [-t template] [-w preprocessed_output]') 52 53 54def main(argv): 55 got_input = False 56 module_directory = None 57 preprocessed_output = None 58 dictionary = {} 59 json_dict = {} 60 got_output = False 61 plugins = [] 62 output_name = None 63 got_preprocessed_input = False 64 output_merged = None 65 66 try: 67 opts, args = getopt.getopt(argv, 'hM:m:d:o:p:t:P:w:') 68 except getopt.GetoptError: 69 out('Unknown option') 70 showhelp() 71 sys.exit(2) 72 73 for opt, arg in opts: 74 if opt == '-h': 75 out('Displaying showhelp') 76 showhelp() 77 sys.exit() 78 elif opt == '-o': 79 if got_output: 80 out('Got more than one output') 81 showhelp() 82 sys.exit(3) 83 got_output = True 84 output_name = arg 85 elif opt == '-m': 86 if module_directory is not None: 87 out('Got more than one cache directory') 88 showhelp() 89 sys.exit(4) 90 module_directory = arg 91 elif opt == '-M': 92 if output_merged is not None: 93 out('Got more than one output merged path') 94 showhelp() 95 sys.exit(5) 96 output_merged = arg 97 elif opt == '-P': 98 assert not got_preprocessed_input 99 assert json_dict == {} 100 sys.path.insert( 101 0, 102 os.path.abspath( 103 os.path.join(os.path.dirname(sys.argv[0]), 'plugins'))) 104 with open(arg, 'rb') as dict_file: 105 dictionary = pickle.load(dict_file) 106 got_preprocessed_input = True 107 elif opt == '-d': 108 assert not got_preprocessed_input 109 with open(arg, 'r') as dict_file: 110 bunch.merge_json( 111 json_dict, 112 yaml.load(dict_file.read(), Loader=yaml.FullLoader)) 113 elif opt == '-p': 114 plugins.append(import_plugin(arg)) 115 elif opt == '-w': 116 preprocessed_output = arg 117 118 if not got_preprocessed_input: 119 for plugin in plugins: 120 plugin.mako_plugin(json_dict) 121 if output_merged: 122 with open(output_merged, 'w') as yaml_file: 123 yaml_file.write(yaml.dump(json_dict)) 124 for k, v in json_dict.items(): 125 dictionary[k] = bunch.to_bunch(v) 126 127 if preprocessed_output: 128 with open(preprocessed_output, 'wb') as dict_file: 129 pickle.dump(dictionary, dict_file) 130 131 cleared_dir = False 132 for arg in args: 133 got_input = True 134 with open(arg) as f: 135 srcs = list(yaml.load_all(f.read(), Loader=yaml.FullLoader)) 136 for src in srcs: 137 if isinstance(src, str): 138 assert len(srcs) == 1 139 template = Template(src, 140 filename=arg, 141 module_directory=module_directory, 142 lookup=TemplateLookup(directories=['.'])) 143 with open(output_name, 'w') as output_file: 144 template.render_context(Context(output_file, **dictionary)) 145 else: 146 # we have optional control data: this template represents 147 # a directory 148 if not cleared_dir: 149 if not os.path.exists(output_name): 150 pass 151 elif os.path.isfile(output_name): 152 os.unlink(output_name) 153 else: 154 shutil.rmtree(output_name, ignore_errors=True) 155 cleared_dir = True 156 items = [] 157 if 'foreach' in src: 158 for el in dictionary[src['foreach']]: 159 if 'cond' in src: 160 args = dict(dictionary) 161 args['selected'] = el 162 if not eval(src['cond'], {}, args): 163 continue 164 items.append(el) 165 assert items 166 else: 167 items = [None] 168 for item in items: 169 args = dict(dictionary) 170 args['selected'] = item 171 item_output_name = os.path.join( 172 output_name, 173 Template(src['output_name']).render(**args)) 174 if not os.path.exists(os.path.dirname(item_output_name)): 175 os.makedirs(os.path.dirname(item_output_name)) 176 template = Template( 177 src['template'], 178 filename=arg, 179 module_directory=module_directory, 180 lookup=TemplateLookup(directories=['.'])) 181 with open(item_output_name, 'w') as output_file: 182 template.render_context(Context(output_file, **args)) 183 184 if not got_input and not preprocessed_output: 185 out('Got nothing to do') 186 showhelp() 187 188 189if __name__ == '__main__': 190 main(sys.argv[1:]) 191