blob: 68b5979425ae720ee0768341de5f2485b3c5e407 [file] [log] [blame]
Miklos Balint470919c2018-05-22 17:51:29 +02001#-------------------------------------------------------------------------------
Kevin Peng578a8492020-12-31 10:22:59 +08002# Copyright (c) 2018-2021, Arm Limited. All rights reserved.
Miklos Balint470919c2018-05-22 17:51:29 +02003#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6#-------------------------------------------------------------------------------
7
8import os
Mate Toth-Pal36f21842018-11-08 16:12:51 +01009import io
Shawn Shana9ad1e02019-08-07 15:49:48 +080010import sys
11import argparse
Ken Liu1f345b02020-05-30 21:11:05 +080012from jinja2 import Environment, BaseLoader, select_autoescape, TemplateNotFound
Miklos Balint470919c2018-05-22 17:51:29 +020013
14try:
15 import yaml
16except ImportError as e:
Mate Toth-Pala99ec6b2019-05-07 11:00:56 +020017 print (str(e) + " To install it, type:")
Mate Toth-Pal36f21842018-11-08 16:12:51 +010018 print ("pip install PyYAML")
Miklos Balint470919c2018-05-22 17:51:29 +020019 exit(1)
20
Edison Ai48b2d9e2019-06-24 14:39:45 +080021donotedit_warning = \
22 "/*********** " + \
23 "WARNING: This is an auto-generated file. Do not edit!" + \
24 " ***********/"
Kevin Peng655f2392019-11-27 16:33:02 +080025
Kevin Peng655f2392019-11-27 16:33:02 +080026OUT_DIR = None # The root directory that files are generated to
Edison Ai48b2d9e2019-06-24 14:39:45 +080027
Mate Toth-Pal36f21842018-11-08 16:12:51 +010028class TemplateLoader(BaseLoader):
29 """
30 Template loader class.
Miklos Balint470919c2018-05-22 17:51:29 +020031
Mate Toth-Pal36f21842018-11-08 16:12:51 +010032 An instance of this class is passed to the template engine. It is
33 responsible for reading the template file
34 """
35 def __init__(self):
36 pass
Miklos Balint470919c2018-05-22 17:51:29 +020037
Mate Toth-Pal36f21842018-11-08 16:12:51 +010038 def get_source(self, environment, template):
39 """
40 This function reads the template files.
41 For detailed documentation see:
42 http://jinja.pocoo.org/docs/2.10/api/#jinja2.BaseLoader.get_source
43
44 Please note that this function always return 'false' as 'uptodate'
45 value, so the output file will always be generated.
46 """
47 if not os.path.isfile(template):
48 raise TemplateNotFound(template)
49 with open(template) as f:
50 source = f.read()
51 return source, template, False
52
Raef Colesf42f0882020-07-10 10:01:58 +010053def process_manifest(manifest_list_files):
Mate Toth-Pal36f21842018-11-08 16:12:51 +010054 """
Kevin Peng655f2392019-11-27 16:33:02 +080055 Parse the input manifest, generate the data base for genereated files
56 and generate manifest header files.
Mate Toth-Pal36f21842018-11-08 16:12:51 +010057
58 Parameters
59 ----------
Raef Colesf42f0882020-07-10 10:01:58 +010060 manifest_list_files:
61 The manifest lists to parse.
Mate Toth-Pal36f21842018-11-08 16:12:51 +010062
63 Returns
64 -------
Kevin Peng578a8492020-12-31 10:22:59 +080065 The partition data base.
Edison Ai48b2d9e2019-06-24 14:39:45 +080066 """
Kevin Peng655f2392019-11-27 16:33:02 +080067
Kevin Peng578a8492020-12-31 10:22:59 +080068 partition_db = []
Kevin Peng655f2392019-11-27 16:33:02 +080069 manifest_list = []
70
Raef Colesf42f0882020-07-10 10:01:58 +010071 for f in manifest_list_files:
72 with open(f) as manifest_list_yaml_file:
73 manifest_dic = yaml.safe_load(manifest_list_yaml_file)
Kevin Peng655f2392019-11-27 16:33:02 +080074 manifest_list.extend(manifest_dic["manifest_list"])
75
Mingyang Sund20999f2020-10-15 14:53:12 +080076 manifesttemplate = ENV.get_template('secure_fw/partitions/manifestfilename.template')
77 memorytemplate = ENV.get_template('secure_fw/partitions/partition_intermedia.template')
Mingyang Sunf6a78572021-04-02 16:51:05 +080078 infotemplate = ENV.get_template('secure_fw/partitions/partition_static_info.template')
Kevin Peng655f2392019-11-27 16:33:02 +080079
Xinyu Zhang19504a52021-03-31 16:26:20 +080080 pid_list = []
Xinyu Zhangc46ee1f2021-04-01 10:10:43 +080081 no_pid_manifest_idx = []
82 for i, manifest_item in enumerate(manifest_list):
83 # Check if partition ID is manually set
84 if 'pid' not in manifest_item.keys():
85 no_pid_manifest_idx.append(i)
86 continue
Xinyu Zhang19504a52021-03-31 16:26:20 +080087 # Check if partition ID is duplicated
88 if manifest_item['pid'] in pid_list:
89 raise Exception("PID No. {pid} has already been used!".format(pid=manifest_item['pid']))
90 pid_list.append(manifest_item['pid'])
Xinyu Zhangc46ee1f2021-04-01 10:10:43 +080091 # Automatically generate PIDs for partitions without PID
92 pid = 256
93 for idx in no_pid_manifest_idx:
94 while pid in pid_list:
95 pid += 1
96 manifest_list[idx]['pid'] = pid
97 pid_list.append(pid)
Xinyu Zhang19504a52021-03-31 16:26:20 +080098
Xinyu Zhangc46ee1f2021-04-01 10:10:43 +080099 print("Start to generate PSA manifests:")
100 for manifest_item in manifest_list:
Raef Coles558487a2020-10-29 13:09:44 +0000101 # Replace environment variables in the manifest path
Kevin Peng655f2392019-11-27 16:33:02 +0800102 manifest_path = os.path.expandvars(manifest_item['manifest'])
103 file = open(manifest_path)
104 manifest = yaml.safe_load(file)
105
Kevin Peng655f2392019-11-27 16:33:02 +0800106 utilities = {}
107 utilities['donotedit_warning']=donotedit_warning
108
109 context = {}
110 context['manifest'] = manifest
111 context['attr'] = manifest_item
112 context['utilities'] = utilities
113
114 manifest_dir, manifest_name = os.path.split(manifest_path)
115 outfile_name = manifest_name.replace('yaml', 'h').replace('json', 'h')
116 context['file_name'] = outfile_name.replace('.h', '')
TTornblom7f92a732020-03-05 13:12:20 +0100117 outfile_name = os.path.join(manifest_dir, "psa_manifest", outfile_name).replace('\\', '/')
Mingyang Sund20999f2020-10-15 14:53:12 +0800118 intermediafile_name = os.path.join(manifest_dir, "auto_generated", 'intermedia_' + context['file_name'] + '.c').replace('\\', '/')
Mingyang Sunf6a78572021-04-02 16:51:05 +0800119 infofile_name = os.path.join(manifest_dir, "auto_generated", 'static_info_' + context['file_name'] + '.c').replace('\\', '/')
Kevin Peng655f2392019-11-27 16:33:02 +0800120
Mingyang Sun4f012692020-10-16 14:04:49 +0800121 """
122 Remove the `source_path` portion of the filepaths, so that it can be
123 interpreted as a relative path from the OUT_DIR.
124 """
125 if 'source_path' in manifest_item:
Raef Coles558487a2020-10-29 13:09:44 +0000126 # Replace environment variables in the source path
127 source_path = os.path.expandvars(manifest_item['source_path'])
128 outfile_name = os.path.relpath(outfile_name, start = source_path)
Mingyang Sund20999f2020-10-15 14:53:12 +0800129 intermediafile_name = os.path.relpath(intermediafile_name, start = source_path)
Mingyang Sunf6a78572021-04-02 16:51:05 +0800130 infofile_name = os.path.relpath(infofile_name, start = source_path)
Mingyang Sun4f012692020-10-16 14:04:49 +0800131
Kevin Peng578a8492020-12-31 10:22:59 +0800132 partition_db.append({"manifest": manifest, "attr": manifest_item, "header_file": outfile_name})
Kevin Peng655f2392019-11-27 16:33:02 +0800133
134 if OUT_DIR is not None:
135 outfile_name = os.path.join(OUT_DIR, outfile_name)
Mingyang Sund20999f2020-10-15 14:53:12 +0800136 intermediafile_name = os.path.join(OUT_DIR, intermediafile_name)
Mingyang Sunf6a78572021-04-02 16:51:05 +0800137 infofile_name = os.path.join(OUT_DIR, infofile_name)
Kevin Peng655f2392019-11-27 16:33:02 +0800138
139 outfile_path = os.path.dirname(outfile_name)
140 if not os.path.exists(outfile_path):
141 os.makedirs(outfile_path)
142
143 print ("Generating " + outfile_name)
144
TTornblom441a0702020-04-28 12:40:42 +0200145 outfile = io.open(outfile_name, "w", newline=None)
Mingyang Sund20999f2020-10-15 14:53:12 +0800146 outfile.write(manifesttemplate.render(context))
Kevin Peng655f2392019-11-27 16:33:02 +0800147 outfile.close()
148
Mingyang Sund20999f2020-10-15 14:53:12 +0800149 intermediafile_path = os.path.dirname(intermediafile_name)
150 if not os.path.exists(intermediafile_path):
151 os.makedirs(intermediafile_path)
152
153 print ("Generating " + intermediafile_name)
154
155 memoutfile = io.open(intermediafile_name, "w", newline=None)
156 memoutfile.write(memorytemplate.render(context))
157 memoutfile.close()
158
Mingyang Sunf6a78572021-04-02 16:51:05 +0800159 infofile_path = os.path.dirname(infofile_name)
160 if not os.path.exists(infofile_path):
161 os.makedirs(infofile_path)
162
163 print ("Generating " + infofile_name)
164
165 info_outfile = io.open(infofile_name, "w", newline=None)
166 info_outfile.write(infotemplate.render(context))
167 info_outfile.close()
168
Kevin Peng578a8492020-12-31 10:22:59 +0800169 return partition_db
Kevin Peng655f2392019-11-27 16:33:02 +0800170
Raef Colesf42f0882020-07-10 10:01:58 +0100171def gen_files(context, gen_file_lists):
Kevin Peng655f2392019-11-27 16:33:02 +0800172 """
173 Generate files according to the gen_file_list
Edison Ai48b2d9e2019-06-24 14:39:45 +0800174
175 Parameters
176 ----------
Raef Colesf42f0882020-07-10 10:01:58 +0100177 gen_file_lists:
178 The lists of files to generate
Edison Ai48b2d9e2019-06-24 14:39:45 +0800179 """
Kevin Peng655f2392019-11-27 16:33:02 +0800180 file_list = []
Shawn Shana9ad1e02019-08-07 15:49:48 +0800181
Raef Colesf42f0882020-07-10 10:01:58 +0100182 for f in gen_file_lists:
183 with open(f) as file_list_yaml_file:
Kevin Peng655f2392019-11-27 16:33:02 +0800184 file_list_yaml = yaml.safe_load(file_list_yaml_file)
185 file_list.extend(file_list_yaml["file_list"])
Edison Ai48b2d9e2019-06-24 14:39:45 +0800186
edison.ai7b299f52020-07-16 15:44:18 +0800187 print("Start to generate file from the generated list:")
Kevin Peng655f2392019-11-27 16:33:02 +0800188 for file in file_list:
Raef Coles558487a2020-10-29 13:09:44 +0000189 # Replace environment variables in the output filepath
Kevin Peng655f2392019-11-27 16:33:02 +0800190 outfile_name = os.path.expandvars(file["output"])
Raef Coles558487a2020-10-29 13:09:44 +0000191 # Replace environment variables in the template filepath
Kevin Peng1ec5e7c2019-11-29 10:52:00 +0800192 templatefile_name = os.path.expandvars(file["template"])
Edison Ai48b2d9e2019-06-24 14:39:45 +0800193
Kevin Peng655f2392019-11-27 16:33:02 +0800194 if OUT_DIR is not None:
195 outfile_name = os.path.join(OUT_DIR, outfile_name)
Edison Ai48b2d9e2019-06-24 14:39:45 +0800196
edison.ai7b299f52020-07-16 15:44:18 +0800197 print ("Generating " + outfile_name)
198
Kevin Peng655f2392019-11-27 16:33:02 +0800199 outfile_path = os.path.dirname(outfile_name)
200 if not os.path.exists(outfile_path):
201 os.makedirs(outfile_path)
Edison Ai48b2d9e2019-06-24 14:39:45 +0800202
Kevin Peng655f2392019-11-27 16:33:02 +0800203 template = ENV.get_template(templatefile_name)
Edison Ai6e3f2a32019-06-11 15:29:05 +0800204
TTornblom441a0702020-04-28 12:40:42 +0200205 outfile = io.open(outfile_name, "w", newline=None)
Kevin Peng655f2392019-11-27 16:33:02 +0800206 outfile.write(template.render(context))
207 outfile.close()
Edison Ai48b2d9e2019-06-24 14:39:45 +0800208
Kevin Peng655f2392019-11-27 16:33:02 +0800209 print ("Generation of files done")
Edison Ai48b2d9e2019-06-24 14:39:45 +0800210
Mingyang Suna1ca6112021-01-11 11:34:59 +0800211def process_stateless_services(partitions, static_handle_max_num):
212 """
213 This function collects all stateless services together, and allocates
Mingyang Sun4ecea992021-03-30 17:56:26 +0800214 stateless handles for them.
Kevin Pengc05319d2021-04-22 22:59:35 +0800215 Valid stateless handle in service will be converted to an index. If the
216 stateless handle is set as "auto", or not set, framework will allocate a
217 valid index for the service.
218 Framework puts each service into a reordered stateless service list at
219 position of "index". Other unused positions are left None.
Mingyang Suna1ca6112021-01-11 11:34:59 +0800220 """
Kevin Pengc05319d2021-04-22 22:59:35 +0800221 collected_stateless_services = []
Mingyang Suna1ca6112021-01-11 11:34:59 +0800222
223 # Collect all stateless services first.
224 for partition in partitions:
225 # Skip the FF-M 1.0 partitions
226 if partition['manifest']['psa_framework_version'] < 1.1:
227 continue
228 # Skip the Non-IPC partitions
229 if partition['attr']['tfm_partition_ipc'] is not True:
230 continue
231 for service in partition['manifest']['services']:
232 if 'connection_based' not in service:
233 raise Exception("'connection_based' is mandatory in FF-M 1.1 service!")
234 if service['connection_based'] is False:
Kevin Pengc05319d2021-04-22 22:59:35 +0800235 collected_stateless_services.append(service)
Mingyang Suna1ca6112021-01-11 11:34:59 +0800236
Kevin Pengc05319d2021-04-22 22:59:35 +0800237 if len(collected_stateless_services) == 0:
Mingyang Suna1ca6112021-01-11 11:34:59 +0800238 return []
239
Kevin Pengc05319d2021-04-22 22:59:35 +0800240 if len(collected_stateless_services) > static_handle_max_num:
241 raise Exception("Stateless service numbers range exceed {number}.".format(number=static_handle_max_num))
Mingyang Suna1ca6112021-01-11 11:34:59 +0800242
243 """
Kevin Pengc05319d2021-04-22 22:59:35 +0800244 Allocate an empty stateless service list to store services.
245 Use "handle - 1" as the index for service, since handle value starts from
246 1 and list index starts from 0.
Mingyang Suna1ca6112021-01-11 11:34:59 +0800247 """
Mingyang Sun4ecea992021-03-30 17:56:26 +0800248 reordered_stateless_services = [None] * static_handle_max_num
Kevin Pengc05319d2021-04-22 22:59:35 +0800249 auto_alloc_services = []
Mingyang Suna1ca6112021-01-11 11:34:59 +0800250
Kevin Pengc05319d2021-04-22 22:59:35 +0800251 for service in collected_stateless_services:
252 # If not set, it is "auto" by default
253 if 'stateless_handle' not in service:
254 auto_alloc_services.append(service)
255 continue
256
Mingyang Sun4ecea992021-03-30 17:56:26 +0800257 service_handle = service['stateless_handle']
Mingyang Suna1ca6112021-01-11 11:34:59 +0800258
Mingyang Sun4ecea992021-03-30 17:56:26 +0800259 # Fill in service list with specified stateless handle, otherwise skip
260 if isinstance(service_handle, int):
261 if service_handle < 1 or service_handle > static_handle_max_num:
Kevin Pengc05319d2021-04-22 22:59:35 +0800262 raise Exception("Invalid stateless_handle setting: {handle}.".format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800263 # Convert handle index to reordered service list index
264 service_handle = service_handle - 1
265
266 if reordered_stateless_services[service_handle] is not None:
Kevin Pengc05319d2021-04-22 22:59:35 +0800267 raise Exception("Duplicated stateless_handle setting: {handle}.".format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800268 reordered_stateless_services[service_handle] = service
Kevin Pengc05319d2021-04-22 22:59:35 +0800269 elif service_handle == 'auto':
270 auto_alloc_services.append(service)
271 else:
272 raise Exception("Invalid stateless_handle setting: {handle}.".format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800273
274 # Auto-allocate stateless handle and encode the stateless handle
Mingyang Suna1ca6112021-01-11 11:34:59 +0800275 for i in range(0, static_handle_max_num):
Mingyang Sun4ecea992021-03-30 17:56:26 +0800276 service = reordered_stateless_services[i]
277
Kevin Pengc05319d2021-04-22 22:59:35 +0800278 if service == None and len(auto_alloc_services) > 0:
279 service = auto_alloc_services.pop(0)
Mingyang Sun4ecea992021-03-30 17:56:26 +0800280
Mingyang Sun453ad402021-03-17 17:58:33 +0800281 """
282 Encode stateless flag and version into stateless handle
283 bit 30: stateless handle indicator
284 bit 15-8: stateless service version
285 bit 7-0: stateless handle index
286 """
Mingyang Sun4ecea992021-03-30 17:56:26 +0800287 stateless_handle_value = 0
288 if service != None:
289 stateless_index = (i & 0xFF)
290 stateless_handle_value |= stateless_index
Mingyang Sun453ad402021-03-17 17:58:33 +0800291 stateless_flag = 1 << 30
292 stateless_handle_value |= stateless_flag
Mingyang Sun4ecea992021-03-30 17:56:26 +0800293 stateless_version = (service['version'] & 0xFF) << 8
Mingyang Sun453ad402021-03-17 17:58:33 +0800294 stateless_handle_value |= stateless_version
Mingyang Sun4ecea992021-03-30 17:56:26 +0800295 service['stateless_handle_value'] = '0x{0:08x}'.format(stateless_handle_value)
Mingyang Suna1ca6112021-01-11 11:34:59 +0800296
Mingyang Sun4ecea992021-03-30 17:56:26 +0800297 reordered_stateless_services[i] = service
298
299 return reordered_stateless_services
Mingyang Suna1ca6112021-01-11 11:34:59 +0800300
Kevin Peng655f2392019-11-27 16:33:02 +0800301def parse_args():
Raef Coles558487a2020-10-29 13:09:44 +0000302 parser = argparse.ArgumentParser(description='Parse secure partition manifest list and generate files listed by the file list',
303 epilog='Note that environment variables in template files will be replaced with their values')
304
Kevin Peng655f2392019-11-27 16:33:02 +0800305 parser.add_argument('-o', '--outdir'
306 , dest='outdir'
307 , required=False
308 , default=None
309 , metavar='out_dir'
310 , help='The root directory for generated files, the default is TF-M root folder.')
Shawn Shana9ad1e02019-08-07 15:49:48 +0800311
Kevin Peng655f2392019-11-27 16:33:02 +0800312 parser.add_argument('-m', '--manifest'
Raef Colesf42f0882020-07-10 10:01:58 +0100313 , nargs='+'
Kevin Peng655f2392019-11-27 16:33:02 +0800314 , dest='manifest_args'
Raef Colesf42f0882020-07-10 10:01:58 +0100315 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800316 , metavar='manifest'
Raef Colesf42f0882020-07-10 10:01:58 +0100317 , help='A set of secure partition manifest lists to parse')
Kevin Peng655f2392019-11-27 16:33:02 +0800318
319 parser.add_argument('-f', '--file-list'
Raef Colesf42f0882020-07-10 10:01:58 +0100320 , nargs='+'
Kevin Peng655f2392019-11-27 16:33:02 +0800321 , dest='gen_file_args'
Raef Colesf42f0882020-07-10 10:01:58 +0100322 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800323 , metavar='file-list'
Raef Colesf42f0882020-07-10 10:01:58 +0100324 , help='These files descripe the file list to generate')
Kevin Peng655f2392019-11-27 16:33:02 +0800325
326 args = parser.parse_args()
327 manifest_args = args.manifest_args
328 gen_file_args = args.gen_file_args
329
Kevin Peng655f2392019-11-27 16:33:02 +0800330 return args
331
332ENV = Environment(
333 loader = TemplateLoader(),
334 autoescape = select_autoescape(['html', 'xml']),
335 lstrip_blocks = True,
336 trim_blocks = True,
337 keep_trailing_newline = True
338 )
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100339
Miklos Balint470919c2018-05-22 17:51:29 +0200340def main():
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100341 """
342 The entry point of the script.
343
344 Generates the output files based on the templates and the manifests.
345 """
Shawn Shana9ad1e02019-08-07 15:49:48 +0800346
Kevin Peng655f2392019-11-27 16:33:02 +0800347 global OUT_DIR
Shawn Shana9ad1e02019-08-07 15:49:48 +0800348
Kevin Peng655f2392019-11-27 16:33:02 +0800349 args = parse_args()
Shawn Shana9ad1e02019-08-07 15:49:48 +0800350
Kevin Peng655f2392019-11-27 16:33:02 +0800351 manifest_args = args.manifest_args
352 gen_file_args = args.gen_file_args
353 OUT_DIR = args.outdir
Kevin Peng655f2392019-11-27 16:33:02 +0800354
Raef Coles558487a2020-10-29 13:09:44 +0000355 manifest_list = [os.path.abspath(x) for x in args.manifest_args]
356 gen_file_list = [os.path.abspath(x) for x in args.gen_file_args]
Shawn Shana9ad1e02019-08-07 15:49:48 +0800357
358 # Arguments could be relative path.
Kevin Peng655f2392019-11-27 16:33:02 +0800359 # Convert to absolute path as we are going to change diretory later
360 if OUT_DIR is not None:
361 OUT_DIR = os.path.abspath(OUT_DIR)
362
Shawn Shana9ad1e02019-08-07 15:49:48 +0800363 """
Kevin Peng655f2392019-11-27 16:33:02 +0800364 Relative path to TF-M root folder is supported in the manifests
365 and default value of manifest list and generated file list are relative to TF-M root folder as well,
366 so first change directory to TF-M root folder.
Shawn Shana9ad1e02019-08-07 15:49:48 +0800367 By doing this, the script can be executed anywhere
Kevin Peng655f2392019-11-27 16:33:02 +0800368 The script is located in <TF-M root folder>/tools, so sys.path[0]<location of the script>/.. is TF-M root folder.
Shawn Shana9ad1e02019-08-07 15:49:48 +0800369 """
370 os.chdir(os.path.join(sys.path[0], ".."))
371
Kevin Peng578a8492020-12-31 10:22:59 +0800372 partition_db = process_manifest(manifest_list)
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100373
Edison Ai6e3f2a32019-06-11 15:29:05 +0800374 utilities = {}
375 context = {}
376
Mingyang Suna1ca6112021-01-11 11:34:59 +0800377 utilities['donotedit_warning'] = donotedit_warning
Miklos Balint470919c2018-05-22 17:51:29 +0200378
Kevin Peng578a8492020-12-31 10:22:59 +0800379 context['partitions'] = partition_db
Kevin Peng655f2392019-11-27 16:33:02 +0800380 context['utilities'] = utilities
Mingyang Suna1ca6112021-01-11 11:34:59 +0800381 context['stateless_services'] = process_stateless_services(partition_db, 32)
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100382
Raef Colesf42f0882020-07-10 10:01:58 +0100383 gen_files(context, gen_file_list)
Miklos Balint470919c2018-05-22 17:51:29 +0200384
385if __name__ == "__main__":
386 main()