blob: f629e68eb928c8d22c156e2d3a4306bb97f2c143 [file] [log] [blame]
Nathaniel Manistaae4fbcd2015-09-23 16:29:44 +00001#!/usr/bin/env python2.7
Craig Tiller6169d5f2016-03-31 07:46:18 -07002# Copyright 2015, Google Inc.
Craig Tillerc2c79212015-02-16 12:00:01 -08003# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15# * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
Nicolas Nobleddef2462015-01-06 18:08:25 -080031
32"""Simple Mako renderer.
33
34Just a wrapper around the mako rendering library.
35
36"""
37
38import getopt
39import imp
40import os
Craig Tiller560c9012016-02-24 16:34:38 -080041import cPickle as pickle
Craig Tiller1ebb7c82015-08-31 15:53:36 -070042import shutil
Nicolas Nobleddef2462015-01-06 18:08:25 -080043import sys
44
45
46from mako.lookup import TemplateLookup
47from mako.runtime import Context
48from mako.template import Template
Nicolas Nobleddef2462015-01-06 18:08:25 -080049import bunch
Craig Tiller1ebb7c82015-08-31 15:53:36 -070050import yaml
Nicolas Nobleddef2462015-01-06 18:08:25 -080051
52
53# Imports a plugin
54def import_plugin(name):
55 _, base_ex = os.path.split(name)
56 base, _ = os.path.splitext(base_ex)
57
58 with open(name, 'r') as plugin_file:
59 plugin_code = plugin_file.read()
60 plugin_module = imp.new_module(base)
61 exec plugin_code in plugin_module.__dict__
62 return plugin_module
63
64
65def out(msg):
66 print >> sys.stderr, msg
67
68
69def showhelp():
Craig Tiller560c9012016-02-24 16:34:38 -080070 out('mako-renderer.py [-o out] [-m cache] [-P preprocessed_input] [-d dict] [-d dict...]'
71 ' [-t template] [-w preprocessed_output]')
Nicolas Nobleddef2462015-01-06 18:08:25 -080072
73
74def main(argv):
75 got_input = False
76 module_directory = None
Craig Tiller560c9012016-02-24 16:34:38 -080077 preprocessed_output = None
Nicolas Nobleddef2462015-01-06 18:08:25 -080078 dictionary = {}
79 json_dict = {}
80 got_output = False
Nicolas Nobleddef2462015-01-06 18:08:25 -080081 plugins = []
Craig Tiller1ebb7c82015-08-31 15:53:36 -070082 output_name = None
Craig Tiller560c9012016-02-24 16:34:38 -080083 got_preprocessed_input = False
Nicolas Nobleddef2462015-01-06 18:08:25 -080084
85 try:
Craig Tiller560c9012016-02-24 16:34:38 -080086 opts, args = getopt.getopt(argv, 'hm:d:o:p:t:P:w:')
Nicolas Nobleddef2462015-01-06 18:08:25 -080087 except getopt.GetoptError:
88 out('Unknown option')
89 showhelp()
90 sys.exit(2)
91
92 for opt, arg in opts:
93 if opt == '-h':
94 out('Displaying showhelp')
95 showhelp()
96 sys.exit()
97 elif opt == '-o':
98 if got_output:
99 out('Got more than one output')
100 showhelp()
101 sys.exit(3)
102 got_output = True
Craig Tiller1ebb7c82015-08-31 15:53:36 -0700103 output_name = arg
Nicolas Nobleddef2462015-01-06 18:08:25 -0800104 elif opt == '-m':
105 if module_directory is not None:
106 out('Got more than one cache directory')
107 showhelp()
108 sys.exit(4)
109 module_directory = arg
Craig Tiller560c9012016-02-24 16:34:38 -0800110 elif opt == '-P':
111 assert not got_preprocessed_input
112 assert json_dict == {}
113 sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), 'plugins')))
114 with open(arg, 'r') as dict_file:
115 dictionary = pickle.load(dict_file)
116 got_preprocessed_input = True
Nicolas Nobleddef2462015-01-06 18:08:25 -0800117 elif opt == '-d':
Craig Tiller560c9012016-02-24 16:34:38 -0800118 assert not got_preprocessed_input
119 with open(arg, 'r') as dict_file:
120 bunch.merge_json(json_dict, yaml.load(dict_file.read()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800121 elif opt == '-p':
122 plugins.append(import_plugin(arg))
Craig Tiller560c9012016-02-24 16:34:38 -0800123 elif opt == '-w':
124 preprocessed_output = arg
Nicolas Nobleddef2462015-01-06 18:08:25 -0800125
Craig Tiller560c9012016-02-24 16:34:38 -0800126 if not got_preprocessed_input:
127 for plugin in plugins:
128 plugin.mako_plugin(json_dict)
129 for k, v in json_dict.items():
130 dictionary[k] = bunch.to_bunch(v)
Nicolas Nobleddef2462015-01-06 18:08:25 -0800131
Craig Tiller560c9012016-02-24 16:34:38 -0800132 if preprocessed_output:
133 with open(preprocessed_output, 'w') as dict_file:
134 pickle.dump(dictionary, dict_file)
Nicolas Nobleddef2462015-01-06 18:08:25 -0800135
Craig Tiller1ebb7c82015-08-31 15:53:36 -0700136 cleared_dir = False
Nicolas Nobleddef2462015-01-06 18:08:25 -0800137 for arg in args:
138 got_input = True
Craig Tiller1ebb7c82015-08-31 15:53:36 -0700139 with open(arg) as f:
140 srcs = list(yaml.load_all(f.read()))
141 for src in srcs:
142 if isinstance(src, basestring):
143 assert len(srcs) == 1
144 template = Template(src,
145 filename=arg,
146 module_directory=module_directory,
147 lookup=TemplateLookup(directories=['.']))
148 with open(output_name, 'w') as output_file:
149 template.render_context(Context(output_file, **dictionary))
150 else:
151 # we have optional control data: this template represents
152 # a directory
153 if not cleared_dir:
Craig Tiller259e6272015-08-31 16:58:18 -0700154 if not os.path.exists(output_name):
155 pass
156 elif os.path.isfile(output_name):
157 os.unlink(output_name)
158 else:
159 shutil.rmtree(output_name, ignore_errors=True)
Craig Tiller1ebb7c82015-08-31 15:53:36 -0700160 cleared_dir = True
161 items = []
162 if 'foreach' in src:
163 for el in dictionary[src['foreach']]:
164 if 'cond' in src:
165 args = dict(dictionary)
166 args['selected'] = el
167 if not eval(src['cond'], {}, args):
168 continue
169 items.append(el)
170 assert items
171 else:
172 items = [None]
173 for item in items:
174 args = dict(dictionary)
175 args['selected'] = item
176 item_output_name = os.path.join(
177 output_name, Template(src['output_name']).render(**args))
178 if not os.path.exists(os.path.dirname(item_output_name)):
179 os.makedirs(os.path.dirname(item_output_name))
180 template = Template(src['template'],
181 filename=arg,
182 module_directory=module_directory,
183 lookup=TemplateLookup(directories=['.']))
184 with open(item_output_name, 'w') as output_file:
185 template.render_context(Context(output_file, **args))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800186
Craig Tiller560c9012016-02-24 16:34:38 -0800187 if not got_input and not preprocessed_output:
Nicolas Nobleddef2462015-01-06 18:08:25 -0800188 out('Got nothing to do')
189 showhelp()
190
Nicolas Nobleddef2462015-01-06 18:08:25 -0800191if __name__ == '__main__':
192 main(sys.argv[1:])