blob: c4c11fc391e329e2fd93e20e0abca1498d586fce [file] [log] [blame]
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +01001#!/usr/bin/env python3
Azim Khanf0e42fb2017-08-02 14:47:13 +01002# Test suites code generator.
3#
Mohammad Azim Khan78befd92018-03-06 11:49:41 +00004# Copyright (C) 2018, ARM Limited, All Rights Reserved
Azim Khanf0e42fb2017-08-02 14:47:13 +01005# SPDX-License-Identifier: Apache-2.0
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
19# This file is part of mbed TLS (https://tls.mbed.org)
20
Mohammad Azim Khanfff49042017-03-28 01:48:31 +010021"""
Azim Khane3b26af2018-06-29 02:36:57 +010022This script dynamically generates test suite code for Mbed TLS, by
23taking following input files.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +010024
Azim Khane3b26af2018-06-29 02:36:57 +010025test_suite_xyz.function - Test suite functions file contains test
26 functions.
27test_suite_xyz.data - Contains test case vectors.
Azim Khan040b6a22018-06-28 16:49:13 +010028main_test.function - Template to substitute generated test
Azim Khane3b26af2018-06-29 02:36:57 +010029 functions, dispatch code, dependency
30 checking code etc.
31platform .function - Platform specific initialization and
32 platform code.
33helpers.function - Common/reusable data and functions.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +010034"""
35
Azim Khanf0e42fb2017-08-02 14:47:13 +010036
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +010037import io
Mohammad Azim Khanfff49042017-03-28 01:48:31 +010038import os
39import re
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +010040import sys
Mohammad Azim Khanfff49042017-03-28 01:48:31 +010041import argparse
Mohammad Azim Khanfff49042017-03-28 01:48:31 +010042
43
Mohammad Azim Khanfff49042017-03-28 01:48:31 +010044BEGIN_HEADER_REGEX = '/\*\s*BEGIN_HEADER\s*\*/'
45END_HEADER_REGEX = '/\*\s*END_HEADER\s*\*/'
46
Mohammad Azim Khanb5229292018-02-06 13:08:01 +000047BEGIN_SUITE_HELPERS_REGEX = '/\*\s*BEGIN_SUITE_HELPERS\s*\*/'
48END_SUITE_HELPERS_REGEX = '/\*\s*END_SUITE_HELPERS\s*\*/'
49
Mohammad Azim Khanfff49042017-03-28 01:48:31 +010050BEGIN_DEP_REGEX = 'BEGIN_DEPENDENCIES'
51END_DEP_REGEX = 'END_DEPENDENCIES'
52
53BEGIN_CASE_REGEX = '/\*\s*BEGIN_CASE\s*(.*?)\s*\*/'
54END_CASE_REGEX = '/\*\s*END_CASE\s*\*/'
55
56
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +010057class GeneratorInputError(Exception):
58 """
Azim Khane3b26af2018-06-29 02:36:57 +010059 Exception to indicate error in the input files to this script.
60 This includes missing patterns, test function names and other
61 parsing errors.
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +010062 """
63 pass
64
65
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +010066class FileWrapper(io.FileIO):
Azim Khan4b543232017-06-30 09:35:21 +010067 """
Azim Khane3b26af2018-06-29 02:36:57 +010068 This class extends built-in io.FileIO class with attribute line_no,
69 that indicates line number for the line that is read.
Azim Khan4b543232017-06-30 09:35:21 +010070 """
71
72 def __init__(self, file_name):
73 """
Azim Khane3b26af2018-06-29 02:36:57 +010074 Instantiate the base class and initialize the line number to 0.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +010075
Azim Khanf0e42fb2017-08-02 14:47:13 +010076 :param file_name: File path to open.
Azim Khan4b543232017-06-30 09:35:21 +010077 """
78 super(FileWrapper, self).__init__(file_name, 'r')
79 self.line_no = 0
80
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +010081 def __next__(self):
Azim Khan4b543232017-06-30 09:35:21 +010082 """
Azim Khane3b26af2018-06-29 02:36:57 +010083 Python 2 iterator method. This method overrides base class's
84 next method and extends the next method to count the line
85 numbers as each line is read.
86
87 It works for both Python 2 and Python 3 by checking iterator
88 method name in the base iterator object.
89
Azim Khanf0e42fb2017-08-02 14:47:13 +010090 :return: Line read from file.
Azim Khan4b543232017-06-30 09:35:21 +010091 """
Gilles Peskine667f7f82018-06-18 17:51:56 +020092 parent = super(FileWrapper, self)
93 if hasattr(parent, '__next__'):
94 line = parent.__next__() # Python 3
95 else:
96 line = parent.next() # Python 2
Azim Khan4b543232017-06-30 09:35:21 +010097 if line:
98 self.line_no += 1
Azim Khan936ea932018-06-28 16:47:12 +010099 # Convert byte array to string with correct encoding and
100 # strip any whitespaces added in the decoding process.
101 return line.decode(sys.getdefaultencoding()).strip() + "\n"
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +0100102 return None
Azim Khane3b26af2018-06-29 02:36:57 +0100103
104 # Python 3 iterator method
Gilles Peskine667f7f82018-06-18 17:51:56 +0200105 next = __next__
Azim Khan4b543232017-06-30 09:35:21 +0100106
107
108def split_dep(dep):
Azim Khanf0e42fb2017-08-02 14:47:13 +0100109 """
110 Split NOT character '!' from dependency. Used by gen_deps()
111
112 :param dep: Dependency list
Azim Khane3b26af2018-06-29 02:36:57 +0100113 :return: string tuple. Ex: ('!', MACRO) for !MACRO and ('', MACRO) for
114 MACRO.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100115 """
Azim Khan4b543232017-06-30 09:35:21 +0100116 return ('!', dep[1:]) if dep[0] == '!' else ('', dep)
117
118
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100119def gen_deps(deps):
120 """
Azim Khane3b26af2018-06-29 02:36:57 +0100121 Test suite data and functions specifies compile time dependencies.
122 This function generates C preprocessor code from the input
123 dependency list. Caller uses the generated preprocessor code to
124 wrap dependent code.
125 A dependency in the input list can have a leading '!' character
126 to negate a condition. '!' is separated from the dependency using
127 function split_dep() and proper preprocessor check is generated
128 accordingly.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100129
Azim Khanf0e42fb2017-08-02 14:47:13 +0100130 :param deps: List of dependencies.
Azim Khan040b6a22018-06-28 16:49:13 +0100131 :return: if defined and endif code with macro annotations for
132 readability.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100133 """
Azim Khan4b543232017-06-30 09:35:21 +0100134 dep_start = ''.join(['#if %sdefined(%s)\n' % split_dep(x) for x in deps])
135 dep_end = ''.join(['#endif /* %s */\n' % x for x in reversed(deps)])
136
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100137 return dep_start, dep_end
138
139
140def gen_deps_one_line(deps):
141 """
Azim Khane3b26af2018-06-29 02:36:57 +0100142 Similar to gen_deps() but generates dependency checks in one line.
143 Useful for generating code with #else block.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100144
Azim Khanf0e42fb2017-08-02 14:47:13 +0100145 :param deps: List of dependencies.
146 :return: ifdef code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100147 """
Azim Khan040b6a22018-06-28 16:49:13 +0100148 defines = '#if ' if len(deps) else ''
149 defines += ' && '.join(['%sdefined(%s)' % split_dep(x) for x in deps])
Azim Khan4b543232017-06-30 09:35:21 +0100150 return defines
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100151
152
Azim Khan4b543232017-06-30 09:35:21 +0100153def gen_function_wrapper(name, locals, args_dispatch):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100154 """
Azim Khan040b6a22018-06-28 16:49:13 +0100155 Creates test function wrapper code. A wrapper has the code to
156 unpack parameters from parameters[] array.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100157
Azim Khanf0e42fb2017-08-02 14:47:13 +0100158 :param name: Test function name
159 :param locals: Local variables declaration code
Azim Khan040b6a22018-06-28 16:49:13 +0100160 :param args_dispatch: List of dispatch arguments.
161 Ex: ['(char *)params[0]', '*((int *)params[1])']
Azim Khanf0e42fb2017-08-02 14:47:13 +0100162 :return: Test function wrapper.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100163 """
164 # Then create the wrapper
165 wrapper = '''
166void {name}_wrapper( void ** params )
167{{
Gilles Peskine77761412018-06-18 17:51:40 +0200168{unused_params}{locals}
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100169 {name}( {args} );
170}}
Gilles Peskine77761412018-06-18 17:51:40 +0200171'''.format(name=name,
Mohammad Azim Khanc3521df2018-06-26 14:06:52 +0100172 unused_params='' if args_dispatch else ' (void)params;\n',
Azim Khan4b543232017-06-30 09:35:21 +0100173 args=', '.join(args_dispatch),
174 locals=locals)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100175 return wrapper
176
177
178def gen_dispatch(name, deps):
179 """
Azim Khane3b26af2018-06-29 02:36:57 +0100180 Test suite code template main_test.function defines a C function
181 array to contain test case functions. This function generates an
182 initializer entry for a function in that array. The entry is
183 composed of a compile time check for the test function
184 dependencies. At compile time the test function is assigned when
185 dependencies are met, else NULL is assigned.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100186
Azim Khanf0e42fb2017-08-02 14:47:13 +0100187 :param name: Test function name
188 :param deps: List of dependencies
189 :return: Dispatch code.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100190 """
191 if len(deps):
192 ifdef = gen_deps_one_line(deps)
193 dispatch_code = '''
194{ifdef}
195 {name}_wrapper,
196#else
197 NULL,
198#endif
199'''.format(ifdef=ifdef, name=name)
200 else:
201 dispatch_code = '''
202 {name}_wrapper,
203'''.format(name=name)
204
205 return dispatch_code
206
207
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000208def parse_until_pattern(funcs_f, end_regex):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100209 """
Azim Khane3b26af2018-06-29 02:36:57 +0100210 Matches pattern end_regex to the lines read from the file object.
211 Returns the lines read until end pattern is matched.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100212
Azim Khanf0e42fb2017-08-02 14:47:13 +0100213 :param funcs_f: file object for .functions file
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000214 :param end_regex: Pattern to stop parsing
Azim Khane3b26af2018-06-29 02:36:57 +0100215 :return: Lines read before the end pattern
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100216 """
Azim Khan4b543232017-06-30 09:35:21 +0100217 headers = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100218 for line in funcs_f:
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000219 if re.search(end_regex, line):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100220 break
221 headers += line
222 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100223 raise GeneratorInputError("file: %s - end pattern [%s] not found!" %
Azim Khan040b6a22018-06-28 16:49:13 +0100224 (funcs_f.name, end_regex))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100225
Azim Khan4b543232017-06-30 09:35:21 +0100226 return headers
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100227
228
Azim Khan4b543232017-06-30 09:35:21 +0100229def parse_suite_deps(funcs_f):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100230 """
Azim Khane3b26af2018-06-29 02:36:57 +0100231 Parses test suite dependencies specified at the top of a
232 .function file, that starts with pattern BEGIN_DEPENDENCIES
233 and end with END_DEPENDENCIES. Dependencies are specified
234 after pattern 'depends_on:' and are delimited by ':'.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100235
Azim Khanf0e42fb2017-08-02 14:47:13 +0100236 :param funcs_f: file object for .functions file
237 :return: List of test suite dependencies.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100238 """
239 deps = []
240 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100241 m = re.search('depends_on\:(.*)', line.strip())
242 if m:
243 deps += [x.strip() for x in m.group(1).split(':')]
244 if re.search(END_DEP_REGEX, line):
245 break
246 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100247 raise GeneratorInputError("file: %s - end dependency pattern [%s]"
Azim Khan040b6a22018-06-28 16:49:13 +0100248 " not found!" % (funcs_f.name, END_DEP_REGEX))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100249
Azim Khan4b543232017-06-30 09:35:21 +0100250 return deps
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100251
252
253def parse_function_deps(line):
254 """
Azim Khane3b26af2018-06-29 02:36:57 +0100255 Parses function dependencies, that are in the same line as
256 comment BEGIN_CASE. Dependencies are specified after pattern
257 'depends_on:' and are delimited by ':'.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100258
Azim Khanf0e42fb2017-08-02 14:47:13 +0100259 :param line: Line from .functions file that has dependencies.
260 :return: List of dependencies.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100261 """
262 deps = []
263 m = re.search(BEGIN_CASE_REGEX, line)
264 dep_str = m.group(1)
265 if len(dep_str):
266 m = re.search('depends_on:(.*)', dep_str)
267 if m:
Azim Khan4b543232017-06-30 09:35:21 +0100268 deps = [x.strip() for x in m.group(1).strip().split(':')]
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100269 return deps
270
271
272def parse_function_signature(line):
273 """
Azim Khane3b26af2018-06-29 02:36:57 +0100274 Parses test function signature for validation and generates
275 a dispatch wrapper function that translates input test vectors
276 read from the data file into test function arguments.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100277
Azim Khan040b6a22018-06-28 16:49:13 +0100278 :param line: Line from .functions file that has a function
279 signature.
280 :return: function name, argument list, local variables for
281 wrapper function and argument dispatch code.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100282 """
283 args = []
Azim Khan2397bba2017-06-09 04:35:03 +0100284 locals = ''
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100285 args_dispatch = []
Azim Khane3b26af2018-06-29 02:36:57 +0100286 # Check if the test function returns void.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100287 m = re.search('\s*void\s+(\w+)\s*\(', line, re.I)
288 if not m:
289 raise ValueError("Test function should return 'void'\n%s" % line)
290 name = m.group(1)
291 line = line[len(m.group(0)):]
292 arg_idx = 0
293 for arg in line[:line.find(')')].split(','):
294 arg = arg.strip()
295 if arg == '':
296 continue
297 if re.search('int\s+.*', arg.strip()):
298 args.append('int')
299 args_dispatch.append('*( (int *) params[%d] )' % arg_idx)
300 elif re.search('char\s*\*\s*.*', arg.strip()):
301 args.append('char*')
302 args_dispatch.append('(char *) params[%d]' % arg_idx)
Azim Khan5fcca462018-06-29 11:05:32 +0100303 elif re.search('data_t\s*\*\s*.*', arg.strip()):
Azim Khana57a4202017-05-31 20:32:32 +0100304 args.append('hex')
Azim Khan2397bba2017-06-09 04:35:03 +0100305 # create a structure
Azim Khan040b6a22018-06-28 16:49:13 +0100306 pointer_initializer = '(uint8_t *) params[%d]' % arg_idx
307 len_initializer = '*( (uint32_t *) params[%d] )' % (arg_idx+1)
Azim Khan5fcca462018-06-29 11:05:32 +0100308 locals += """ data_t data%d = {%s, %s};
Azim Khan040b6a22018-06-28 16:49:13 +0100309""" % (arg_idx, pointer_initializer, len_initializer)
Azim Khan2397bba2017-06-09 04:35:03 +0100310
Azim Khan5fcca462018-06-29 11:05:32 +0100311 args_dispatch.append('&data%d' % arg_idx)
Azim Khan2397bba2017-06-09 04:35:03 +0100312 arg_idx += 1
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100313 else:
Azim Khan040b6a22018-06-28 16:49:13 +0100314 raise ValueError("Test function arguments can only be 'int', "
Azim Khan5fcca462018-06-29 11:05:32 +0100315 "'char *' or 'data_t'\n%s" % line)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100316 arg_idx += 1
317
Azim Khan4b543232017-06-30 09:35:21 +0100318 return name, args, locals, args_dispatch
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100319
320
Azim Khan4b543232017-06-30 09:35:21 +0100321def parse_function_code(funcs_f, deps, suite_deps):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100322 """
Azim Khan040b6a22018-06-28 16:49:13 +0100323 Parses out a function from function file object and generates
324 function and dispatch code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100325
Azim Khanf0e42fb2017-08-02 14:47:13 +0100326 :param funcs_f: file object of the functions file.
327 :param deps: List of dependencies
328 :param suite_deps: List of test suite dependencies
329 :return: Function name, arguments, function code and dispatch code.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100330 """
Azim Khan4b543232017-06-30 09:35:21 +0100331 code = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100332 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100333 # Check function signature
334 m = re.match('.*?\s+(\w+)\s*\(', line, re.I)
335 if m:
336 # check if we have full signature i.e. split in more lines
337 if not re.match('.*\)', line):
338 for lin in funcs_f:
339 line += lin
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100340 if re.search('.*?\)', line):
341 break
Azim Khan4b543232017-06-30 09:35:21 +0100342 name, args, locals, args_dispatch = parse_function_signature(line)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100343 code += line.replace(name, 'test_' + name)
344 name = 'test_' + name
345 break
346 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100347 raise GeneratorInputError("file: %s - Test functions not found!" %
Azim Khan040b6a22018-06-28 16:49:13 +0100348 funcs_f.name)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100349
350 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100351 if re.search(END_CASE_REGEX, line):
352 break
353 code += line
354 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100355 raise GeneratorInputError("file: %s - end case pattern [%s] not "
Azim Khan040b6a22018-06-28 16:49:13 +0100356 "found!" % (funcs_f.name, END_CASE_REGEX))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100357
358 # Add exit label if not present
359 if code.find('exit:') == -1:
360 s = code.rsplit('}', 1)
361 if len(s) == 2:
Azim Khan4b543232017-06-30 09:35:21 +0100362 code = """exit:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100363 ;;
Azim Khan4b543232017-06-30 09:35:21 +0100364}""".join(s)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100365
Azim Khan4b543232017-06-30 09:35:21 +0100366 code += gen_function_wrapper(name, locals, args_dispatch)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100367 ifdef, endif = gen_deps(deps)
368 dispatch_code = gen_dispatch(name, suite_deps + deps)
Azim Khan4b543232017-06-30 09:35:21 +0100369 return name, args, ifdef + code + endif, dispatch_code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100370
371
372def parse_functions(funcs_f):
373 """
Azim Khane3b26af2018-06-29 02:36:57 +0100374 Parses a test_suite_xxx.function file and returns information
375 for generating a C source file for the test suite.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100376
Azim Khanf0e42fb2017-08-02 14:47:13 +0100377 :param funcs_f: file object of the functions file.
Azim Khan040b6a22018-06-28 16:49:13 +0100378 :return: List of test suite dependencies, test function dispatch
379 code, function code and a dict with function identifiers
380 and arguments info.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100381 """
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100382 suite_headers = ''
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000383 suite_helpers = ''
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100384 suite_deps = []
385 suite_functions = ''
386 func_info = {}
387 function_idx = 0
388 dispatch_code = ''
389 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100390 if re.search(BEGIN_HEADER_REGEX, line):
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000391 headers = parse_until_pattern(funcs_f, END_HEADER_REGEX)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100392 suite_headers += headers
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000393 elif re.search(BEGIN_SUITE_HELPERS_REGEX, line):
394 helpers = parse_until_pattern(funcs_f, END_SUITE_HELPERS_REGEX)
395 suite_helpers += helpers
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100396 elif re.search(BEGIN_DEP_REGEX, line):
Azim Khan4b543232017-06-30 09:35:21 +0100397 deps = parse_suite_deps(funcs_f)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100398 suite_deps += deps
399 elif re.search(BEGIN_CASE_REGEX, line):
400 deps = parse_function_deps(line)
Azim Khan040b6a22018-06-28 16:49:13 +0100401 func_name, args, func_code, func_dispatch =\
402 parse_function_code(funcs_f, deps, suite_deps)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100403 suite_functions += func_code
404 # Generate dispatch code and enumeration info
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100405 if func_name in func_info:
406 raise GeneratorInputError(
407 "file: %s - function %s re-declared at line %d" % \
408 (funcs_f.name, func_name, funcs_f.line_no))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100409 func_info[func_name] = (function_idx, args)
410 dispatch_code += '/* Function Id: %d */\n' % function_idx
411 dispatch_code += func_dispatch
412 function_idx += 1
413
414 ifdef, endif = gen_deps(suite_deps)
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000415 func_code = ifdef + suite_headers + suite_helpers + suite_functions + endif
Azim Khan13c6bfb2017-06-15 14:45:56 +0100416 return suite_deps, dispatch_code, func_code, func_info
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100417
418
419def escaped_split(str, ch):
420 """
421 Split str on character ch but ignore escaped \{ch}
Azim Khan040b6a22018-06-28 16:49:13 +0100422 Since, return value is used to write back to the intermediate
423 data file, any escape characters in the input are retained in the
424 output.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100425
Azim Khanf0e42fb2017-08-02 14:47:13 +0100426 :param str: String to split
427 :param ch: split character
428 :return: List of splits
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100429 """
430 if len(ch) > 1:
431 raise ValueError('Expected split character. Found string!')
432 out = []
433 part = ''
434 escape = False
435 for i in range(len(str)):
436 if not escape and str[i] == ch:
437 out.append(part)
438 part = ''
439 else:
Azim Khanacc54732017-07-03 14:06:45 +0100440 part += str[i]
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100441 escape = not escape and str[i] == '\\'
442 if len(part):
443 out.append(part)
444 return out
445
446
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100447def parse_test_data(data_f, debug=False):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100448 """
Azim Khane3b26af2018-06-29 02:36:57 +0100449 Parses .data file for each test case name, test function name,
450 test dependencies and test arguments. This information is
451 correlated with the test functions file for generating an
452 intermediate data file replacing the strings for test function
453 names, dependencies and integer constant expressions with
454 identifiers. Mainly for optimising space for on-target
455 execution.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100456
Azim Khanf0e42fb2017-08-02 14:47:13 +0100457 :param data_f: file object of the data file.
Azim Khan040b6a22018-06-28 16:49:13 +0100458 :return: Generator that yields test name, function name,
459 dependency list and function argument list.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100460 """
461 STATE_READ_NAME = 0
462 STATE_READ_ARGS = 1
463 state = STATE_READ_NAME
464 deps = []
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100465 name = ''
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100466 for line in data_f:
467 line = line.strip()
468 if len(line) and line[0] == '#': # Skip comments
469 continue
470
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100471 # Blank line indicates end of test
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100472 if len(line) == 0:
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100473 if state == STATE_READ_ARGS:
Azim Khan040b6a22018-06-28 16:49:13 +0100474 raise GeneratorInputError("[%s:%d] Newline before arguments. "
475 "Test function and arguments "
476 "missing for %s" %
477 (data_f.name, data_f.line_no, name))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100478 continue
479
480 if state == STATE_READ_NAME:
481 # Read test name
482 name = line
483 state = STATE_READ_ARGS
484 elif state == STATE_READ_ARGS:
485 # Check dependencies
486 m = re.search('depends_on\:(.*)', line)
487 if m:
Azim Khan040b6a22018-06-28 16:49:13 +0100488 deps = [x.strip() for x in m.group(1).split(':') if len(
489 x.strip())]
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100490 else:
491 # Read test vectors
492 parts = escaped_split(line, ':')
493 function = parts[0]
494 args = parts[1:]
495 yield name, function, deps, args
496 deps = []
497 state = STATE_READ_NAME
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100498 if state == STATE_READ_ARGS:
Azim Khan040b6a22018-06-28 16:49:13 +0100499 raise GeneratorInputError("[%s:%d] Newline before arguments. "
500 "Test function and arguments missing for "
501 "%s" % (data_f.name, data_f.line_no, name))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100502
503
504def gen_dep_check(dep_id, dep):
505 """
Azim Khane3b26af2018-06-29 02:36:57 +0100506 Generate code for checking dependency with the associated
507 identifier.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100508
Azim Khanf0e42fb2017-08-02 14:47:13 +0100509 :param dep_id: Dependency identifier
510 :param dep: Dependency macro
511 :return: Dependency check code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100512 """
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100513 if dep_id < 0:
Azim Khan040b6a22018-06-28 16:49:13 +0100514 raise GeneratorInputError("Dependency Id should be a positive "
515 "integer.")
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100516 noT, dep = ('!', dep[1:]) if dep[0] == '!' else ('', dep)
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100517 if len(dep) == 0:
518 raise GeneratorInputError("Dependency should not be an empty string.")
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100519 dep_check = '''
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100520 case {id}:
521 {{
Azim Khand61b8372017-07-10 11:54:01 +0100522#if {noT}defined({macro})
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100523 ret = DEPENDENCY_SUPPORTED;
Azim Khand61b8372017-07-10 11:54:01 +0100524#else
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100525 ret = DEPENDENCY_NOT_SUPPORTED;
Azim Khand61b8372017-07-10 11:54:01 +0100526#endif
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100527 }}
528 break;'''.format(noT=noT, macro=dep, id=dep_id)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100529 return dep_check
530
531
532def gen_expression_check(exp_id, exp):
533 """
Azim Khane3b26af2018-06-29 02:36:57 +0100534 Generates code for evaluating an integer expression using
535 associated expression Id.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100536
Azim Khanf0e42fb2017-08-02 14:47:13 +0100537 :param exp_id: Expression Identifier
538 :param exp: Expression/Macro
539 :return: Expression check code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100540 """
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100541 if exp_id < 0:
Azim Khan040b6a22018-06-28 16:49:13 +0100542 raise GeneratorInputError("Expression Id should be a positive "
543 "integer.")
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100544 if len(exp) == 0:
545 raise GeneratorInputError("Expression should not be an empty string.")
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100546 exp_code = '''
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100547 case {exp_id}:
548 {{
549 *out_value = {expression};
550 }}
551 break;'''.format(exp_id=exp_id, expression=exp)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100552 return exp_code
553
554
Azim Khan599cd242017-07-06 17:34:27 +0100555def write_deps(out_data_f, test_deps, unique_deps):
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100556 """
Azim Khane3b26af2018-06-29 02:36:57 +0100557 Write dependencies to intermediate test data file, replacing
558 the string form with identifiers. Also, generates dependency
559 check code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100560
Azim Khanf0e42fb2017-08-02 14:47:13 +0100561 :param out_data_f: Output intermediate data file
562 :param test_deps: Dependencies
Azim Khan040b6a22018-06-28 16:49:13 +0100563 :param unique_deps: Mutable list to track unique dependencies
564 that are global to this re-entrant function.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100565 :return: returns dependency check code.
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100566 """
Azim Khan599cd242017-07-06 17:34:27 +0100567 dep_check_code = ''
568 if len(test_deps):
569 out_data_f.write('depends_on')
570 for dep in test_deps:
571 if dep not in unique_deps:
572 unique_deps.append(dep)
573 dep_id = unique_deps.index(dep)
574 dep_check_code += gen_dep_check(dep_id, dep)
575 else:
576 dep_id = unique_deps.index(dep)
577 out_data_f.write(':' + str(dep_id))
578 out_data_f.write('\n')
579 return dep_check_code
580
581
582def write_parameters(out_data_f, test_args, func_args, unique_expressions):
583 """
Azim Khane3b26af2018-06-29 02:36:57 +0100584 Writes test parameters to the intermediate data file, replacing
585 the string form with identifiers. Also, generates expression
586 check code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100587
Azim Khanf0e42fb2017-08-02 14:47:13 +0100588 :param out_data_f: Output intermediate data file
589 :param test_args: Test parameters
590 :param func_args: Function arguments
Azim Khan040b6a22018-06-28 16:49:13 +0100591 :param unique_expressions: Mutable list to track unique
592 expressions that are global to this re-entrant function.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100593 :return: Returns expression check code.
Azim Khan599cd242017-07-06 17:34:27 +0100594 """
595 expression_code = ''
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +0100596 for i in range(len(test_args)):
Azim Khan599cd242017-07-06 17:34:27 +0100597 typ = func_args[i]
598 val = test_args[i]
599
Azim Khan040b6a22018-06-28 16:49:13 +0100600 # check if val is a non literal int val (i.e. an expression)
601 if typ == 'int' and not re.match('(\d+$)|((0x)?[0-9a-fA-F]+$)', val):
Azim Khan599cd242017-07-06 17:34:27 +0100602 typ = 'exp'
603 if val not in unique_expressions:
604 unique_expressions.append(val)
Azim Khan040b6a22018-06-28 16:49:13 +0100605 # exp_id can be derived from len(). But for
606 # readability and consistency with case of existing
607 # let's use index().
Azim Khan599cd242017-07-06 17:34:27 +0100608 exp_id = unique_expressions.index(val)
609 expression_code += gen_expression_check(exp_id, val)
610 val = exp_id
611 else:
612 val = unique_expressions.index(val)
613 out_data_f.write(':' + typ + ':' + str(val))
614 out_data_f.write('\n')
615 return expression_code
616
617
618def gen_suite_deps_checks(suite_deps, dep_check_code, expression_code):
619 """
Azim Khane3b26af2018-06-29 02:36:57 +0100620 Generates preprocessor checks for test suite dependencies.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100621
Azim Khan040b6a22018-06-28 16:49:13 +0100622 :param suite_deps: Test suite dependencies read from the
623 .functions file.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100624 :param dep_check_code: Dependency check code
625 :param expression_code: Expression check code
Azim Khan040b6a22018-06-28 16:49:13 +0100626 :return: Dependency and expression code guarded by test suite
627 dependencies.
Azim Khan599cd242017-07-06 17:34:27 +0100628 """
Azim Khan599cd242017-07-06 17:34:27 +0100629 if len(suite_deps):
630 ifdef = gen_deps_one_line(suite_deps)
631 dep_check_code = '''
632{ifdef}
633{code}
Azim Khan599cd242017-07-06 17:34:27 +0100634#endif
635'''.format(ifdef=ifdef, code=dep_check_code)
636 expression_code = '''
637{ifdef}
638{code}
Azim Khan599cd242017-07-06 17:34:27 +0100639#endif
640'''.format(ifdef=ifdef, code=expression_code)
641 return dep_check_code, expression_code
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100642
643
Azim Khan13c6bfb2017-06-15 14:45:56 +0100644def gen_from_test_data(data_f, out_data_f, func_info, suite_deps):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100645 """
Azim Khane3b26af2018-06-29 02:36:57 +0100646 This function reads test case name, dependencies and test vectors
647 from the .data file. This information is correlated with the test
648 functions file for generating an intermediate data file replacing
649 the strings for test function names, dependencies and integer
650 constant expressions with identifiers. Mainly for optimising
651 space for on-target execution.
652 It also generates test case dependency check code and expression
653 evaluation code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100654
Azim Khanf0e42fb2017-08-02 14:47:13 +0100655 :param data_f: Data file object
656 :param out_data_f:Output intermediate data file
Azim Khan040b6a22018-06-28 16:49:13 +0100657 :param func_info: Dict keyed by function and with function id
658 and arguments info
Azim Khanf0e42fb2017-08-02 14:47:13 +0100659 :param suite_deps: Test suite deps
660 :return: Returns dependency and expression check code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100661 """
662 unique_deps = []
663 unique_expressions = []
664 dep_check_code = ''
665 expression_code = ''
Azim Khan040b6a22018-06-28 16:49:13 +0100666 for test_name, function_name, test_deps, test_args in parse_test_data(
667 data_f):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100668 out_data_f.write(test_name + '\n')
669
Azim Khan599cd242017-07-06 17:34:27 +0100670 # Write deps
671 dep_check_code += write_deps(out_data_f, test_deps, unique_deps)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100672
Azim Khan599cd242017-07-06 17:34:27 +0100673 # Write test function name
674 test_function_name = 'test_' + function_name
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100675 if test_function_name not in func_info:
Azim Khan040b6a22018-06-28 16:49:13 +0100676 raise GeneratorInputError("Function %s not found!" %
677 test_function_name)
Azim Khan599cd242017-07-06 17:34:27 +0100678 func_id, func_args = func_info[test_function_name]
679 out_data_f.write(str(func_id))
680
681 # Write parameters
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100682 if len(test_args) != len(func_args):
Azim Khan040b6a22018-06-28 16:49:13 +0100683 raise GeneratorInputError("Invalid number of arguments in test "
684 "%s. See function %s signature." % (
685 test_name, function_name))
686 expression_code += write_parameters(out_data_f, test_args, func_args,
687 unique_expressions)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100688
Azim Khan599cd242017-07-06 17:34:27 +0100689 # Write a newline as test case separator
690 out_data_f.write('\n')
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100691
Azim Khan040b6a22018-06-28 16:49:13 +0100692 dep_check_code, expression_code = gen_suite_deps_checks(
693 suite_deps, dep_check_code, expression_code)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100694 return dep_check_code, expression_code
695
696
Azim Khan040b6a22018-06-28 16:49:13 +0100697def generate_code(funcs_file, data_file, template_file, platform_file,
Azim Khane3b26af2018-06-29 02:36:57 +0100698 helpers_file, suites_dir, c_file, out_data_file):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100699 """
Azim Khane3b26af2018-06-29 02:36:57 +0100700 Generates C source code from test suite file, data file, common
701 helpers file and platform file.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100702
Azim Khanf0e42fb2017-08-02 14:47:13 +0100703 :param funcs_file: Functions file object
704 :param data_file: Data file object
705 :param template_file: Template file object
706 :param platform_file: Platform file object
Azim Khane3b26af2018-06-29 02:36:57 +0100707 :param helpers_file: Helper functions file object
Azim Khanf0e42fb2017-08-02 14:47:13 +0100708 :param suites_dir: Test suites dir
709 :param c_file: Output C file object
710 :param out_data_file: Output intermediate data file object
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100711 :return:
712 """
713 for name, path in [('Functions file', funcs_file),
714 ('Data file', data_file),
715 ('Template file', template_file),
716 ('Platform file', platform_file),
Azim Khane3b26af2018-06-29 02:36:57 +0100717 ('Helpers code file', helpers_file),
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100718 ('Suites dir', suites_dir)]:
719 if not os.path.exists(path):
720 raise IOError("ERROR: %s [%s] not found!" % (name, path))
721
722 snippets = {'generator_script' : os.path.basename(__file__)}
723
724 # Read helpers
Azim Khane3b26af2018-06-29 02:36:57 +0100725 with open(helpers_file, 'r') as help_f, open(platform_file, 'r') as \
Azim Khan040b6a22018-06-28 16:49:13 +0100726 platform_f:
Azim Khane3b26af2018-06-29 02:36:57 +0100727 snippets['test_common_helper_file'] = helpers_file
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100728 snippets['test_common_helpers'] = help_f.read()
729 snippets['test_platform_file'] = platform_file
Azim Khan040b6a22018-06-28 16:49:13 +0100730 snippets['platform_code'] = platform_f.read().replace(
731 'DATA_FILE', out_data_file.replace('\\', '\\\\')) # escape '\'
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100732
733 # Function code
Azim Khan040b6a22018-06-28 16:49:13 +0100734 with FileWrapper(funcs_file) as funcs_f, FileWrapper(data_file) as \
735 data_f, open(out_data_file, 'w') as out_data_f:
736 suite_deps, dispatch_code, func_code, func_info = parse_functions(
737 funcs_f)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100738 snippets['functions_code'] = func_code
739 snippets['dispatch_code'] = dispatch_code
Azim Khan040b6a22018-06-28 16:49:13 +0100740 dep_check_code, expression_code = gen_from_test_data(
741 data_f, out_data_f, func_info, suite_deps)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100742 snippets['dep_check_code'] = dep_check_code
743 snippets['expression_code'] = expression_code
744
745 snippets['test_file'] = c_file
746 snippets['test_main_file'] = template_file
747 snippets['test_case_file'] = funcs_file
748 snippets['test_case_data_file'] = data_file
749 # Read Template
750 # Add functions
751 #
752 with open(template_file, 'r') as template_f, open(c_file, 'w') as c_f:
753 line_no = 1
754 for line in template_f.readlines():
Azim Khan040b6a22018-06-28 16:49:13 +0100755 # Update line number. +1 as #line directive sets next line number
756 snippets['line_no'] = line_no + 1
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100757 code = line.format(**snippets)
758 c_f.write(code)
759 line_no += 1
760
761
762def check_cmd():
763 """
764 Command line parser.
765
766 :return:
767 """
Azim Khan040b6a22018-06-28 16:49:13 +0100768 parser = argparse.ArgumentParser(
Azim Khane3b26af2018-06-29 02:36:57 +0100769 description='Dynamically generate test suite code.')
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100770
771 parser.add_argument("-f", "--functions-file",
772 dest="funcs_file",
773 help="Functions file",
Azim Khane3b26af2018-06-29 02:36:57 +0100774 metavar="FUNCTIONS_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100775 required=True)
776
777 parser.add_argument("-d", "--data-file",
778 dest="data_file",
779 help="Data file",
Azim Khane3b26af2018-06-29 02:36:57 +0100780 metavar="DATA_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100781 required=True)
782
783 parser.add_argument("-t", "--template-file",
784 dest="template_file",
785 help="Template file",
Azim Khane3b26af2018-06-29 02:36:57 +0100786 metavar="TEMPLATE_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100787 required=True)
788
789 parser.add_argument("-s", "--suites-dir",
790 dest="suites_dir",
791 help="Suites dir",
Azim Khane3b26af2018-06-29 02:36:57 +0100792 metavar="SUITES_DIR",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100793 required=True)
794
Azim Khane3b26af2018-06-29 02:36:57 +0100795 parser.add_argument("--helpers-file",
796 dest="helpers_file",
797 help="Helpers file",
798 metavar="HELPERS_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100799 required=True)
800
801 parser.add_argument("-p", "--platform-file",
802 dest="platform_file",
803 help="Platform code file",
804 metavar="PLATFORM_FILE",
805 required=True)
806
807 parser.add_argument("-o", "--out-dir",
808 dest="out_dir",
809 help="Dir where generated code and scripts are copied",
810 metavar="OUT_DIR",
811 required=True)
812
813 args = parser.parse_args()
814
815 data_file_name = os.path.basename(args.data_file)
816 data_name = os.path.splitext(data_file_name)[0]
817
818 out_c_file = os.path.join(args.out_dir, data_name + '.c')
Mohammad Azim Khan00c4b092018-06-28 13:10:19 +0100819 out_data_file = os.path.join(args.out_dir, data_name + '.datax')
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100820
821 out_c_file_dir = os.path.dirname(out_c_file)
822 out_data_file_dir = os.path.dirname(out_data_file)
823 for d in [out_c_file_dir, out_data_file_dir]:
824 if not os.path.exists(d):
825 os.makedirs(d)
826
Azim Khan040b6a22018-06-28 16:49:13 +0100827 generate_code(args.funcs_file, args.data_file, args.template_file,
Azim Khane3b26af2018-06-29 02:36:57 +0100828 args.platform_file, args.helpers_file, args.suites_dir,
Azim Khan040b6a22018-06-28 16:49:13 +0100829 out_c_file, out_data_file)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100830
831
832if __name__ == "__main__":
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100833 try:
834 check_cmd()
835 except GeneratorInputError as e:
836 script_name = os.path.basename(sys.argv[0])
837 print("%s: input error: %s" % (script_name, str(e)))