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