• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 glob
22import importlib.util
23import os
24import pickle
25import shutil
26import sys
27from typing import List
28
29import yaml
30from mako import exceptions
31from mako.lookup import TemplateLookup
32from mako.runtime import Context
33from mako.template import Template
34
35PROJECT_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..",
36                            "..")
37# TODO(lidiz) find a better way for plugins to reference each other
38sys.path.append(os.path.join(PROJECT_ROOT, 'tools', 'buildgen', 'plugins'))
39
40
41def out(msg: str) -> None:
42    print(msg, file=sys.stderr)
43
44
45def showhelp() -> None:
46    out('mako-renderer.py [-o out] [-m cache] [-P preprocessed_input] [-d dict] [-d dict...]'
47        ' [-t template] [-w preprocessed_output]')
48
49
50def render_template(template: Template, context: Context) -> None:
51    """Render the mako template with given context.
52
53    Prints an error template to indicate where and what in the template caused
54    the render failure.
55    """
56    try:
57        template.render_context(context)
58    except:
59        out(exceptions.text_error_template().render())
60        raise
61
62
63def main(argv: List[str]) -> None:
64    got_input = False
65    module_directory = None
66    preprocessed_output = None
67    dictionary = {}
68    json_dict = {}
69    got_output = False
70    output_name = None
71    got_preprocessed_input = False
72    output_merged = None
73
74    try:
75        opts, args = getopt.getopt(argv, 'hM:m:o:t:P:')
76    except getopt.GetoptError:
77        out('Unknown option')
78        showhelp()
79        sys.exit(2)
80
81    for opt, arg in opts:
82        if opt == '-h':
83            out('Displaying showhelp')
84            showhelp()
85            sys.exit()
86        elif opt == '-o':
87            if got_output:
88                out('Got more than one output')
89                showhelp()
90                sys.exit(3)
91            got_output = True
92            output_name = arg
93        elif opt == '-m':
94            if module_directory is not None:
95                out('Got more than one cache directory')
96                showhelp()
97                sys.exit(4)
98            module_directory = arg
99        elif opt == '-M':
100            if output_merged is not None:
101                out('Got more than one output merged path')
102                showhelp()
103                sys.exit(5)
104            output_merged = arg
105        elif opt == '-P':
106            assert not got_preprocessed_input
107            assert json_dict == {}
108            with open(arg, 'rb') as dict_file:
109                dictionary = pickle.load(dict_file)
110            got_preprocessed_input = True
111
112    cleared_dir = False
113    for arg in args:
114        got_input = True
115        with open(arg) as f:
116            srcs = list(yaml.load_all(f.read(), Loader=yaml.FullLoader))
117        for src in srcs:
118            if isinstance(src, str):
119                assert len(srcs) == 1
120                template = Template(src,
121                                    filename=arg,
122                                    module_directory=module_directory,
123                                    lookup=TemplateLookup(directories=['.']))
124                with open(output_name, 'w') as output_file:
125                    render_template(template, Context(output_file,
126                                                      **dictionary))
127            else:
128                # we have optional control data: this template represents
129                # a directory
130                if not cleared_dir:
131                    if not os.path.exists(output_name):
132                        pass
133                    elif os.path.isfile(output_name):
134                        os.unlink(output_name)
135                    else:
136                        shutil.rmtree(output_name, ignore_errors=True)
137                    cleared_dir = True
138                items = []
139                if 'foreach' in src:
140                    for el in dictionary[src['foreach']]:
141                        if 'cond' in src:
142                            args = dict(dictionary)
143                            args['selected'] = el
144                            if not eval(src['cond'], {}, args):
145                                continue
146                        items.append(el)
147                    assert items
148                else:
149                    items = [None]
150                for item in items:
151                    args = dict(dictionary)
152                    args['selected'] = item
153                    item_output_name = os.path.join(
154                        output_name,
155                        Template(src['output_name']).render(**args))
156                    if not os.path.exists(os.path.dirname(item_output_name)):
157                        os.makedirs(os.path.dirname(item_output_name))
158                    template = Template(
159                        src['template'],
160                        filename=arg,
161                        module_directory=module_directory,
162                        lookup=TemplateLookup(directories=['.']))
163                    with open(item_output_name, 'w') as output_file:
164                        render_template(template, Context(output_file, **args))
165
166    if not got_input and not preprocessed_output:
167        out('Got nothing to do')
168        showhelp()
169
170
171if __name__ == '__main__':
172    main(sys.argv[1:])
173