blob: 866e6fdb0632886fb1deb36f79066f3c0b93a310 [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
Craig Tiller3ab2fe02016-04-11 20:11:18 -070084 output_merged = None
Nicolas Nobleddef2462015-01-06 18:08:25 -080085
86 try:
Craig Tiller3ab2fe02016-04-11 20:11:18 -070087 opts, args = getopt.getopt(argv, 'hM:m:d:o:p:t:P:w:')
Nicolas Nobleddef2462015-01-06 18:08:25 -080088 except getopt.GetoptError:
89 out('Unknown option')
90 showhelp()
91 sys.exit(2)
92
93 for opt, arg in opts:
94 if opt == '-h':
95 out('Displaying showhelp')
96 showhelp()
97 sys.exit()
98 elif opt == '-o':
99 if got_output:
100 out('Got more than one output')
101 showhelp()
102 sys.exit(3)
103 got_output = True
Craig Tiller1ebb7c82015-08-31 15:53:36 -0700104 output_name = arg
Nicolas Nobleddef2462015-01-06 18:08:25 -0800105 elif opt == '-m':
106 if module_directory is not None:
107 out('Got more than one cache directory')
108 showhelp()
109 sys.exit(4)
110 module_directory = arg
Craig Tiller3ab2fe02016-04-11 20:11:18 -0700111 elif opt == '-M':
112 if output_merged is not None:
113 out('Got more than one output merged path')
114 showhelp()
115 sys.exit(5)
116 output_merged = arg
Craig Tiller560c9012016-02-24 16:34:38 -0800117 elif opt == '-P':
118 assert not got_preprocessed_input
119 assert json_dict == {}
120 sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), 'plugins')))
121 with open(arg, 'r') as dict_file:
122 dictionary = pickle.load(dict_file)
123 got_preprocessed_input = True
Nicolas Nobleddef2462015-01-06 18:08:25 -0800124 elif opt == '-d':
Craig Tiller560c9012016-02-24 16:34:38 -0800125 assert not got_preprocessed_input
126 with open(arg, 'r') as dict_file:
127 bunch.merge_json(json_dict, yaml.load(dict_file.read()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800128 elif opt == '-p':
129 plugins.append(import_plugin(arg))
Craig Tiller560c9012016-02-24 16:34:38 -0800130 elif opt == '-w':
131 preprocessed_output = arg
Nicolas Nobleddef2462015-01-06 18:08:25 -0800132
Craig Tiller560c9012016-02-24 16:34:38 -0800133 if not got_preprocessed_input:
134 for plugin in plugins:
135 plugin.mako_plugin(json_dict)
Craig Tiller3ab2fe02016-04-11 20:11:18 -0700136 if output_merged:
137 with open(output_merged, 'w') as yaml_file:
138 yaml_file.write(yaml.dump(json_dict))
Craig Tiller560c9012016-02-24 16:34:38 -0800139 for k, v in json_dict.items():
140 dictionary[k] = bunch.to_bunch(v)
Nicolas Nobleddef2462015-01-06 18:08:25 -0800141
Craig Tiller560c9012016-02-24 16:34:38 -0800142 if preprocessed_output:
143 with open(preprocessed_output, 'w') as dict_file:
144 pickle.dump(dictionary, dict_file)
Nicolas Nobleddef2462015-01-06 18:08:25 -0800145
Craig Tiller1ebb7c82015-08-31 15:53:36 -0700146 cleared_dir = False
Nicolas Nobleddef2462015-01-06 18:08:25 -0800147 for arg in args:
148 got_input = True
Craig Tiller1ebb7c82015-08-31 15:53:36 -0700149 with open(arg) as f:
150 srcs = list(yaml.load_all(f.read()))
151 for src in srcs:
152 if isinstance(src, basestring):
153 assert len(srcs) == 1
154 template = Template(src,
155 filename=arg,
156 module_directory=module_directory,
157 lookup=TemplateLookup(directories=['.']))
158 with open(output_name, 'w') as output_file:
159 template.render_context(Context(output_file, **dictionary))
160 else:
161 # we have optional control data: this template represents
162 # a directory
163 if not cleared_dir:
Craig Tiller259e6272015-08-31 16:58:18 -0700164 if not os.path.exists(output_name):
165 pass
166 elif os.path.isfile(output_name):
167 os.unlink(output_name)
168 else:
169 shutil.rmtree(output_name, ignore_errors=True)
Craig Tiller1ebb7c82015-08-31 15:53:36 -0700170 cleared_dir = True
171 items = []
172 if 'foreach' in src:
173 for el in dictionary[src['foreach']]:
174 if 'cond' in src:
175 args = dict(dictionary)
176 args['selected'] = el
177 if not eval(src['cond'], {}, args):
178 continue
179 items.append(el)
180 assert items
181 else:
182 items = [None]
183 for item in items:
184 args = dict(dictionary)
185 args['selected'] = item
186 item_output_name = os.path.join(
187 output_name, Template(src['output_name']).render(**args))
188 if not os.path.exists(os.path.dirname(item_output_name)):
189 os.makedirs(os.path.dirname(item_output_name))
190 template = Template(src['template'],
191 filename=arg,
192 module_directory=module_directory,
193 lookup=TemplateLookup(directories=['.']))
194 with open(item_output_name, 'w') as output_file:
195 template.render_context(Context(output_file, **args))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800196
Craig Tiller560c9012016-02-24 16:34:38 -0800197 if not got_input and not preprocessed_output:
Nicolas Nobleddef2462015-01-06 18:08:25 -0800198 out('Got nothing to do')
199 showhelp()
200
Nicolas Nobleddef2462015-01-06 18:08:25 -0800201if __name__ == '__main__':
202 main(sys.argv[1:])