blob: 347100dbe492fdaff1a4ea5aeeaa882df3d6d14a [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#
Bence Szépkúti1e148272020-08-07 13:07:28 +02004# Copyright The Mbed TLS Contributors
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.
Azim Khanf0e42fb2017-08-02 14:47:13 +010018
Mohammad Azim Khanfff49042017-03-28 01:48:31 +010019"""
Azim Khanaee05bb2018-07-02 16:01:04 +010020This script is a key part of Mbed TLS test suites framework. For
21understanding the script it is important to understand the
22framework. This doc string contains a summary of the framework
23and explains the function of this script.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +010024
Azim Khanaee05bb2018-07-02 16:01:04 +010025Mbed TLS test suites:
26=====================
27Scope:
28------
29The test suites focus on unit testing the crypto primitives and also
Azim Khanb31aa442018-07-03 11:57:54 +010030include x509 parser tests. Tests can be added to test any Mbed TLS
Azim Khanaee05bb2018-07-02 16:01:04 +010031module. However, the framework is not capable of testing SSL
32protocol, since that requires full stack execution and that is best
33tested as part of the system test.
34
35Test case definition:
36---------------------
37Tests are defined in a test_suite_<module>[.<optional sub module>].data
38file. A test definition contains:
39 test name
40 optional build macro dependencies
41 test function
42 test parameters
43
44Test dependencies are build macros that can be specified to indicate
45the build config in which the test is valid. For example if a test
46depends on a feature that is only enabled by defining a macro. Then
47that macro should be specified as a dependency of the test.
48
49Test function is the function that implements the test steps. This
50function is specified for different tests that perform same steps
51with different parameters.
52
53Test parameters are specified in string form separated by ':'.
54Parameters can be of type string, binary data specified as hex
55string and integer constants specified as integer, macro or
56as an expression. Following is an example test definition:
57
Mohammad Azim Khand2d01122018-07-18 17:48:37 +010058 AES 128 GCM Encrypt and decrypt 8 bytes
59 depends_on:MBEDTLS_AES_C:MBEDTLS_GCM_C
60 enc_dec_buf:MBEDTLS_CIPHER_AES_128_GCM:"AES-128-GCM":128:8:-1
Azim Khanaee05bb2018-07-02 16:01:04 +010061
62Test functions:
63---------------
64Test functions are coded in C in test_suite_<module>.function files.
65Functions file is itself not compilable and contains special
66format patterns to specify test suite dependencies, start and end
67of functions and function dependencies. Check any existing functions
68file for example.
69
70Execution:
71----------
72Tests are executed in 3 steps:
73- Generating test_suite_<module>[.<optional sub module>].c file
74 for each corresponding .data file.
75- Building each source file into executables.
76- Running each executable and printing report.
77
78Generating C test source requires more than just the test functions.
79Following extras are required:
80- Process main()
81- Reading .data file and dispatching test cases.
82- Platform specific test case execution
83- Dependency checking
84- Integer expression evaluation
85- Test function dispatch
86
87Build dependencies and integer expressions (in the test parameters)
88are specified as strings in the .data file. Their run time value is
89not known at the generation stage. Hence, they need to be translated
90into run time evaluations. This script generates the run time checks
91for dependencies and integer expressions.
92
93Similarly, function names have to be translated into function calls.
94This script also generates code for function dispatch.
95
96The extra code mentioned here is either generated by this script
97or it comes from the input files: helpers file, platform file and
98the template file.
99
100Helper file:
101------------
102Helpers file contains common helper/utility functions and data.
103
104Platform file:
105--------------
106Platform file contains platform specific setup code and test case
107dispatch code. For example, host_test.function reads test data
108file from host's file system and dispatches tests.
Azim Khanaee05bb2018-07-02 16:01:04 +0100109
110Template file:
111---------
112Template file for example main_test.function is a template C file in
113which generated code and code from input files is substituted to
114generate a compilable C file. It also contains skeleton functions for
115dependency checks, expression evaluation and function dispatch. These
116functions are populated with checks and return codes by this script.
117
118Template file contains "replacement" fields that are formatted
Mohammad Azim Khan5cb70172018-07-19 11:32:30 +0100119strings processed by Python string.Template.substitute() method.
Azim Khanaee05bb2018-07-02 16:01:04 +0100120
121This script:
122============
123Core function of this script is to fill the template file with
124code that is generated or read from helpers and platform files.
125
126This script replaces following fields in the template and generates
127the test source file:
128
David Horstmann360f8e42022-11-09 17:27:33 +0000129__MBEDTLS_TEST_TEMPLATE__TEST_COMMON_HELPERS
130 All common code from helpers.function
131 is substituted here.
132__MBEDTLS_TEST_TEMPLATE__FUNCTIONS_CODE
133 Test functions are substituted here
134 from the input test_suit_xyz.function
135 file. C preprocessor checks are generated
136 for the build dependencies specified
137 in the input file. This script also
138 generates wrappers for the test
139 functions with code to expand the
140 string parameters read from the data
141 file.
142__MBEDTLS_TEST_TEMPLATE__EXPRESSION_CODE
143 This script enumerates the
144 expressions in the .data file and
145 generates code to handle enumerated
146 expression Ids and return the values.
147__MBEDTLS_TEST_TEMPLATE__DEP_CHECK_CODE
148 This script enumerates all
149 build dependencies and generate
150 code to handle enumerated build
151 dependency Id and return status: if
152 the dependency is defined or not.
153__MBEDTLS_TEST_TEMPLATE__DISPATCH_CODE
154 This script enumerates the functions
155 specified in the input test data file
156 and generates the initializer for the
157 function table in the template
158 file.
159__MBEDTLS_TEST_TEMPLATE__PLATFORM_CODE
160 Platform specific setup and test
161 dispatch code.
Azim Khanaee05bb2018-07-02 16:01:04 +0100162
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100163"""
164
Azim Khanf0e42fb2017-08-02 14:47:13 +0100165
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100166import os
167import re
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +0100168import sys
Mohammad Azim Khan5cb70172018-07-19 11:32:30 +0100169import string
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100170import argparse
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100171
172
Azim Khanb31aa442018-07-03 11:57:54 +0100173BEGIN_HEADER_REGEX = r'/\*\s*BEGIN_HEADER\s*\*/'
174END_HEADER_REGEX = r'/\*\s*END_HEADER\s*\*/'
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100175
Azim Khanb31aa442018-07-03 11:57:54 +0100176BEGIN_SUITE_HELPERS_REGEX = r'/\*\s*BEGIN_SUITE_HELPERS\s*\*/'
177END_SUITE_HELPERS_REGEX = r'/\*\s*END_SUITE_HELPERS\s*\*/'
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000178
Azim Khanb31aa442018-07-03 11:57:54 +0100179BEGIN_DEP_REGEX = r'BEGIN_DEPENDENCIES'
180END_DEP_REGEX = r'END_DEPENDENCIES'
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100181
Azim Khan8d686bf2018-07-04 23:29:46 +0100182BEGIN_CASE_REGEX = r'/\*\s*BEGIN_CASE\s*(?P<depends_on>.*?)\s*\*/'
Azim Khanb31aa442018-07-03 11:57:54 +0100183END_CASE_REGEX = r'/\*\s*END_CASE\s*\*/'
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100184
Azim Khan8d686bf2018-07-04 23:29:46 +0100185DEPENDENCY_REGEX = r'depends_on:(?P<dependencies>.*)'
Ron Eldorb9b38132018-11-27 16:35:20 +0200186C_IDENTIFIER_REGEX = r'!?[a-z_][a-z0-9_]*'
187CONDITION_OPERATOR_REGEX = r'[!=]=|[<>]=?'
188# forbid 0ddd which might be accidentally octal or accidentally decimal
189CONDITION_VALUE_REGEX = r'[-+]?(0x[0-9a-f]+|0|[1-9][0-9]*)'
190CONDITION_REGEX = r'({})(?:\s*({})\s*({}))?$'.format(C_IDENTIFIER_REGEX,
191 CONDITION_OPERATOR_REGEX,
192 CONDITION_VALUE_REGEX)
Azim Khanfcdf6852018-07-05 17:31:46 +0100193TEST_FUNCTION_VALIDATION_REGEX = r'\s*void\s+(?P<func_name>\w+)\s*\('
Azim Khan8d686bf2018-07-04 23:29:46 +0100194INT_CHECK_REGEX = r'int\s+.*'
195CHAR_CHECK_REGEX = r'char\s*\*\s*.*'
196DATA_T_CHECK_REGEX = r'data_t\s*\*\s*.*'
Azim Khan8d686bf2018-07-04 23:29:46 +0100197FUNCTION_ARG_LIST_END_REGEX = r'.*\)'
198EXIT_LABEL_REGEX = r'^exit:'
199
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100200
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100201class GeneratorInputError(Exception):
202 """
Azim Khane3b26af2018-06-29 02:36:57 +0100203 Exception to indicate error in the input files to this script.
204 This includes missing patterns, test function names and other
205 parsing errors.
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100206 """
207 pass
208
209
Pengyu Lv7a344dd2023-04-19 15:03:20 +0800210class FileWrapper:
Azim Khan4b543232017-06-30 09:35:21 +0100211 """
Pengyu Lv7a344dd2023-04-19 15:03:20 +0800212 This class extends the file object with attribute line_no,
Azim Khane3b26af2018-06-29 02:36:57 +0100213 that indicates line number for the line that is read.
Azim Khan4b543232017-06-30 09:35:21 +0100214 """
215
Pengyu Lv7a344dd2023-04-19 15:03:20 +0800216 def __init__(self, file_name) -> None:
Azim Khan4b543232017-06-30 09:35:21 +0100217 """
Pengyu Lv7a344dd2023-04-19 15:03:20 +0800218 Instantiate the file object and initialize the line number to 0.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100219
Azim Khanf0e42fb2017-08-02 14:47:13 +0100220 :param file_name: File path to open.
Azim Khan4b543232017-06-30 09:35:21 +0100221 """
Pengyu Lv7a344dd2023-04-19 15:03:20 +0800222 # private mix-in file object
223 self._f = open(file_name, 'rb')
Azim Khanb31aa442018-07-03 11:57:54 +0100224 self._line_no = 0
Azim Khan4b543232017-06-30 09:35:21 +0100225
Pengyu Lv7a344dd2023-04-19 15:03:20 +0800226 def __iter__(self):
227 return self
228
Gilles Peskine0c3f5f12022-11-10 19:33:25 +0100229 def __next__(self):
Azim Khan4b543232017-06-30 09:35:21 +0100230 """
Pengyu Lv7a344dd2023-04-19 15:03:20 +0800231 This method makes FileWrapper iterable.
232 It counts the line numbers as each line is read.
Azim Khane3b26af2018-06-29 02:36:57 +0100233
Azim Khanf0e42fb2017-08-02 14:47:13 +0100234 :return: Line read from file.
Azim Khan4b543232017-06-30 09:35:21 +0100235 """
Pengyu Lv7a344dd2023-04-19 15:03:20 +0800236 line = self._f.__next__()
237 self._line_no += 1
238 # Convert byte array to string with correct encoding and
239 # strip any whitespaces added in the decoding process.
240 return line.decode(sys.getdefaultencoding()).rstrip()+ '\n'
Azim Khane3b26af2018-06-29 02:36:57 +0100241
Pengyu Lv7a344dd2023-04-19 15:03:20 +0800242 def __enter__(self):
243 return self
244
245 def __exit__(self, exc_type, exc_val, exc_tb):
246 self._f.__exit__(exc_type, exc_val, exc_tb)
247
248 @property
249 def line_no(self):
Azim Khanb31aa442018-07-03 11:57:54 +0100250 """
Pengyu Lv7a344dd2023-04-19 15:03:20 +0800251 Property that indicates line number for the line that is read.
Azim Khanb31aa442018-07-03 11:57:54 +0100252 """
253 return self._line_no
254
Pengyu Lv7a344dd2023-04-19 15:03:20 +0800255 @property
256 def name(self):
257 """
258 Property that indicates name of the file that is read.
259 """
260 return self._f.name
Azim Khan4b543232017-06-30 09:35:21 +0100261
262
263def split_dep(dep):
Azim Khanf0e42fb2017-08-02 14:47:13 +0100264 """
Azim Khanb31aa442018-07-03 11:57:54 +0100265 Split NOT character '!' from dependency. Used by gen_dependencies()
Azim Khanf0e42fb2017-08-02 14:47:13 +0100266
267 :param dep: Dependency list
Azim Khane3b26af2018-06-29 02:36:57 +0100268 :return: string tuple. Ex: ('!', MACRO) for !MACRO and ('', MACRO) for
269 MACRO.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100270 """
Azim Khan4b543232017-06-30 09:35:21 +0100271 return ('!', dep[1:]) if dep[0] == '!' else ('', dep)
272
273
Azim Khanb31aa442018-07-03 11:57:54 +0100274def gen_dependencies(dependencies):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100275 """
Azim Khane3b26af2018-06-29 02:36:57 +0100276 Test suite data and functions specifies compile time dependencies.
277 This function generates C preprocessor code from the input
278 dependency list. Caller uses the generated preprocessor code to
279 wrap dependent code.
280 A dependency in the input list can have a leading '!' character
281 to negate a condition. '!' is separated from the dependency using
282 function split_dep() and proper preprocessor check is generated
283 accordingly.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100284
Azim Khanb31aa442018-07-03 11:57:54 +0100285 :param dependencies: List of dependencies.
Azim Khan040b6a22018-06-28 16:49:13 +0100286 :return: if defined and endif code with macro annotations for
287 readability.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100288 """
Azim Khanb31aa442018-07-03 11:57:54 +0100289 dep_start = ''.join(['#if %sdefined(%s)\n' % (x, y) for x, y in
290 map(split_dep, dependencies)])
291 dep_end = ''.join(['#endif /* %s */\n' %
292 x for x in reversed(dependencies)])
Azim Khan4b543232017-06-30 09:35:21 +0100293
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100294 return dep_start, dep_end
295
296
Azim Khanb31aa442018-07-03 11:57:54 +0100297def gen_dependencies_one_line(dependencies):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100298 """
Azim Khanb31aa442018-07-03 11:57:54 +0100299 Similar to gen_dependencies() but generates dependency checks in one line.
Azim Khane3b26af2018-06-29 02:36:57 +0100300 Useful for generating code with #else block.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100301
Azim Khanb31aa442018-07-03 11:57:54 +0100302 :param dependencies: List of dependencies.
303 :return: Preprocessor check code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100304 """
Azim Khanb31aa442018-07-03 11:57:54 +0100305 defines = '#if ' if dependencies else ''
306 defines += ' && '.join(['%sdefined(%s)' % (x, y) for x, y in map(
307 split_dep, dependencies)])
Azim Khan4b543232017-06-30 09:35:21 +0100308 return defines
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100309
310
Azim Khanb31aa442018-07-03 11:57:54 +0100311def gen_function_wrapper(name, local_vars, args_dispatch):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100312 """
Azim Khan040b6a22018-06-28 16:49:13 +0100313 Creates test function wrapper code. A wrapper has the code to
314 unpack parameters from parameters[] array.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100315
Azim Khanf0e42fb2017-08-02 14:47:13 +0100316 :param name: Test function name
Azim Khanb31aa442018-07-03 11:57:54 +0100317 :param local_vars: Local variables declaration code
Azim Khan040b6a22018-06-28 16:49:13 +0100318 :param args_dispatch: List of dispatch arguments.
319 Ex: ['(char *)params[0]', '*((int *)params[1])']
Azim Khanf0e42fb2017-08-02 14:47:13 +0100320 :return: Test function wrapper.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100321 """
322 # Then create the wrapper
323 wrapper = '''
324void {name}_wrapper( void ** params )
325{{
Gilles Peskine77761412018-06-18 17:51:40 +0200326{unused_params}{locals}
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100327 {name}( {args} );
328}}
Gilles Peskine77761412018-06-18 17:51:40 +0200329'''.format(name=name,
Mohammad Azim Khanc3521df2018-06-26 14:06:52 +0100330 unused_params='' if args_dispatch else ' (void)params;\n',
Azim Khan4b543232017-06-30 09:35:21 +0100331 args=', '.join(args_dispatch),
Azim Khanb31aa442018-07-03 11:57:54 +0100332 locals=local_vars)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100333 return wrapper
334
335
Azim Khanb31aa442018-07-03 11:57:54 +0100336def gen_dispatch(name, dependencies):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100337 """
Azim Khane3b26af2018-06-29 02:36:57 +0100338 Test suite code template main_test.function defines a C function
339 array to contain test case functions. This function generates an
340 initializer entry for a function in that array. The entry is
341 composed of a compile time check for the test function
342 dependencies. At compile time the test function is assigned when
343 dependencies are met, else NULL is assigned.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100344
Azim Khanf0e42fb2017-08-02 14:47:13 +0100345 :param name: Test function name
Azim Khanb31aa442018-07-03 11:57:54 +0100346 :param dependencies: List of dependencies
Azim Khanf0e42fb2017-08-02 14:47:13 +0100347 :return: Dispatch code.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100348 """
Azim Khanb31aa442018-07-03 11:57:54 +0100349 if dependencies:
350 preprocessor_check = gen_dependencies_one_line(dependencies)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100351 dispatch_code = '''
Azim Khanb31aa442018-07-03 11:57:54 +0100352{preprocessor_check}
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100353 {name}_wrapper,
354#else
355 NULL,
356#endif
Azim Khanb31aa442018-07-03 11:57:54 +0100357'''.format(preprocessor_check=preprocessor_check, name=name)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100358 else:
359 dispatch_code = '''
360 {name}_wrapper,
361'''.format(name=name)
362
363 return dispatch_code
364
365
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000366def parse_until_pattern(funcs_f, end_regex):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100367 """
Azim Khane3b26af2018-06-29 02:36:57 +0100368 Matches pattern end_regex to the lines read from the file object.
369 Returns the lines read until end pattern is matched.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100370
Azim Khan8d686bf2018-07-04 23:29:46 +0100371 :param funcs_f: file object for .function file
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000372 :param end_regex: Pattern to stop parsing
Azim Khane3b26af2018-06-29 02:36:57 +0100373 :return: Lines read before the end pattern
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100374 """
Azim Khan4b543232017-06-30 09:35:21 +0100375 headers = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100376 for line in funcs_f:
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000377 if re.search(end_regex, line):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100378 break
379 headers += line
380 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100381 raise GeneratorInputError("file: %s - end pattern [%s] not found!" %
Azim Khanb31aa442018-07-03 11:57:54 +0100382 (funcs_f.name, end_regex))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100383
Azim Khan4b543232017-06-30 09:35:21 +0100384 return headers
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100385
386
Azim Khan8d686bf2018-07-04 23:29:46 +0100387def validate_dependency(dependency):
388 """
389 Validates a C macro and raises GeneratorInputError on invalid input.
390 :param dependency: Input macro dependency
391 :return: input dependency stripped of leading & trailing white spaces.
392 """
393 dependency = dependency.strip()
Ron Eldorb9b38132018-11-27 16:35:20 +0200394 if not re.match(CONDITION_REGEX, dependency, re.I):
Azim Khan8d686bf2018-07-04 23:29:46 +0100395 raise GeneratorInputError('Invalid dependency %s' % dependency)
396 return dependency
397
398
399def parse_dependencies(inp_str):
400 """
401 Parses dependencies out of inp_str, validates them and returns a
402 list of macros.
403
404 :param inp_str: Input string with macros delimited by ':'.
405 :return: list of dependencies
406 """
Gilles Peskine8b022352020-03-24 18:36:56 +0100407 dependencies = list(map(validate_dependency, inp_str.split(':')))
Azim Khan8d686bf2018-07-04 23:29:46 +0100408 return dependencies
409
410
Azim Khanb31aa442018-07-03 11:57:54 +0100411def parse_suite_dependencies(funcs_f):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100412 """
Azim Khane3b26af2018-06-29 02:36:57 +0100413 Parses test suite dependencies specified at the top of a
414 .function file, that starts with pattern BEGIN_DEPENDENCIES
415 and end with END_DEPENDENCIES. Dependencies are specified
416 after pattern 'depends_on:' and are delimited by ':'.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100417
Azim Khan8d686bf2018-07-04 23:29:46 +0100418 :param funcs_f: file object for .function file
Azim Khanf0e42fb2017-08-02 14:47:13 +0100419 :return: List of test suite dependencies.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100420 """
Azim Khanb31aa442018-07-03 11:57:54 +0100421 dependencies = []
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100422 for line in funcs_f:
Azim Khan8d686bf2018-07-04 23:29:46 +0100423 match = re.search(DEPENDENCY_REGEX, line.strip())
Azim Khanb31aa442018-07-03 11:57:54 +0100424 if match:
Azim Khan8d686bf2018-07-04 23:29:46 +0100425 try:
426 dependencies = parse_dependencies(match.group('dependencies'))
427 except GeneratorInputError as error:
428 raise GeneratorInputError(
429 str(error) + " - %s:%d" % (funcs_f.name, funcs_f.line_no))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100430 if re.search(END_DEP_REGEX, line):
431 break
432 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100433 raise GeneratorInputError("file: %s - end dependency pattern [%s]"
Azim Khanb31aa442018-07-03 11:57:54 +0100434 " not found!" % (funcs_f.name,
435 END_DEP_REGEX))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100436
Azim Khanb31aa442018-07-03 11:57:54 +0100437 return dependencies
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100438
439
Azim Khanb31aa442018-07-03 11:57:54 +0100440def parse_function_dependencies(line):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100441 """
Azim Khane3b26af2018-06-29 02:36:57 +0100442 Parses function dependencies, that are in the same line as
443 comment BEGIN_CASE. Dependencies are specified after pattern
444 'depends_on:' and are delimited by ':'.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100445
Azim Khan8d686bf2018-07-04 23:29:46 +0100446 :param line: Line from .function file that has dependencies.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100447 :return: List of dependencies.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100448 """
Azim Khanb31aa442018-07-03 11:57:54 +0100449 dependencies = []
450 match = re.search(BEGIN_CASE_REGEX, line)
Azim Khan8d686bf2018-07-04 23:29:46 +0100451 dep_str = match.group('depends_on')
Azim Khanb31aa442018-07-03 11:57:54 +0100452 if dep_str:
Azim Khan8d686bf2018-07-04 23:29:46 +0100453 match = re.search(DEPENDENCY_REGEX, dep_str)
Azim Khanb31aa442018-07-03 11:57:54 +0100454 if match:
Azim Khan8d686bf2018-07-04 23:29:46 +0100455 dependencies += parse_dependencies(match.group('dependencies'))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100456
Azim Khan8d686bf2018-07-04 23:29:46 +0100457 return dependencies
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100458
Azim Khan4084ec72018-07-05 14:20:08 +0100459
Azim Khanfcdf6852018-07-05 17:31:46 +0100460def parse_function_arguments(line):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100461 """
Azim Khane3b26af2018-06-29 02:36:57 +0100462 Parses test function signature for validation and generates
463 a dispatch wrapper function that translates input test vectors
464 read from the data file into test function arguments.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100465
Azim Khan8d686bf2018-07-04 23:29:46 +0100466 :param line: Line from .function file that has a function
Azim Khan040b6a22018-06-28 16:49:13 +0100467 signature.
Azim Khanfcdf6852018-07-05 17:31:46 +0100468 :return: argument list, local variables for
Azim Khan040b6a22018-06-28 16:49:13 +0100469 wrapper function and argument dispatch code.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100470 """
471 args = []
Azim Khanb31aa442018-07-03 11:57:54 +0100472 local_vars = ''
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100473 args_dispatch = []
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100474 arg_idx = 0
Azim Khanfcdf6852018-07-05 17:31:46 +0100475 # Remove characters before arguments
476 line = line[line.find('(') + 1:]
Azim Khan8d686bf2018-07-04 23:29:46 +0100477 # Process arguments, ex: <type> arg1, <type> arg2 )
478 # This script assumes that the argument list is terminated by ')'
479 # i.e. the test functions will not have a function pointer
480 # argument.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100481 for arg in line[:line.find(')')].split(','):
482 arg = arg.strip()
483 if arg == '':
484 continue
Azim Khan8d686bf2018-07-04 23:29:46 +0100485 if re.search(INT_CHECK_REGEX, arg.strip()):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100486 args.append('int')
487 args_dispatch.append('*( (int *) params[%d] )' % arg_idx)
Azim Khan8d686bf2018-07-04 23:29:46 +0100488 elif re.search(CHAR_CHECK_REGEX, arg.strip()):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100489 args.append('char*')
490 args_dispatch.append('(char *) params[%d]' % arg_idx)
Azim Khan8d686bf2018-07-04 23:29:46 +0100491 elif re.search(DATA_T_CHECK_REGEX, arg.strip()):
Azim Khana57a4202017-05-31 20:32:32 +0100492 args.append('hex')
Azim Khan2397bba2017-06-09 04:35:03 +0100493 # create a structure
Azim Khan040b6a22018-06-28 16:49:13 +0100494 pointer_initializer = '(uint8_t *) params[%d]' % arg_idx
495 len_initializer = '*( (uint32_t *) params[%d] )' % (arg_idx+1)
Azim Khanb31aa442018-07-03 11:57:54 +0100496 local_vars += """ data_t data%d = {%s, %s};
Azim Khan040b6a22018-06-28 16:49:13 +0100497""" % (arg_idx, pointer_initializer, len_initializer)
Azim Khan2397bba2017-06-09 04:35:03 +0100498
Azim Khan5fcca462018-06-29 11:05:32 +0100499 args_dispatch.append('&data%d' % arg_idx)
Azim Khan2397bba2017-06-09 04:35:03 +0100500 arg_idx += 1
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100501 else:
Azim Khan040b6a22018-06-28 16:49:13 +0100502 raise ValueError("Test function arguments can only be 'int', "
Azim Khan5fcca462018-06-29 11:05:32 +0100503 "'char *' or 'data_t'\n%s" % line)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100504 arg_idx += 1
505
Azim Khanfcdf6852018-07-05 17:31:46 +0100506 return args, local_vars, args_dispatch
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100507
508
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +0100509def generate_function_code(name, code, local_vars, args_dispatch,
510 dependencies):
511 """
512 Generate function code with preprocessor checks and parameter dispatch
513 wrapper.
514
515 :param name: Function name
516 :param code: Function code
517 :param local_vars: Local variables for function wrapper
518 :param args_dispatch: Argument dispatch code
519 :param dependencies: Preprocessor dependencies list
520 :return: Final function code
521 """
522 # Add exit label if not present
523 if code.find('exit:') == -1:
524 split_code = code.rsplit('}', 1)
525 if len(split_code) == 2:
526 code = """exit:
527 ;
528}""".join(split_code)
529
530 code += gen_function_wrapper(name, local_vars, args_dispatch)
531 preprocessor_check_start, preprocessor_check_end = \
532 gen_dependencies(dependencies)
533 return preprocessor_check_start + code + preprocessor_check_end
534
Gilles Peskine07510f52022-11-11 16:37:16 +0100535COMMENT_START_REGEX = re.compile(r'/[*/]')
536
537def skip_comments(line, stream):
538 """Remove comments in line.
539
540 If the line contains an unfinished comment, read more lines from stream
541 until the line that contains the comment.
542
543 :return: The original line with inner comments replaced by spaces.
544 Trailing comments and whitespace may be removed completely.
545 """
546 pos = 0
547 while True:
548 opening = COMMENT_START_REGEX.search(line, pos)
549 if not opening:
550 break
551 if line[opening.start(0) + 1] == '/': # //...
552 continuation = line
Gilles Peskine18f70282022-11-30 16:38:49 +0100553 # Count the number of line breaks, to keep line numbers aligned
554 # in the output.
555 line_count = 1
Gilles Peskine07510f52022-11-11 16:37:16 +0100556 while continuation.endswith('\\\n'):
557 # This errors out if the file ends with an unfinished line
Gilles Peskine268ea5a2022-11-18 22:26:03 +0100558 # comment. That's acceptable to not complicate the code further.
Gilles Peskine07510f52022-11-11 16:37:16 +0100559 continuation = next(stream)
Gilles Peskine18f70282022-11-30 16:38:49 +0100560 line_count += 1
561 return line[:opening.start(0)].rstrip() + '\n' * line_count
Gilles Peskine07510f52022-11-11 16:37:16 +0100562 # Parsing /*...*/, looking for the end
563 closing = line.find('*/', opening.end(0))
564 while closing == -1:
565 # This errors out if the file ends with an unfinished block
Gilles Peskine268ea5a2022-11-18 22:26:03 +0100566 # comment. That's acceptable to not complicate the code further.
Gilles Peskine07510f52022-11-11 16:37:16 +0100567 line += next(stream)
568 closing = line.find('*/', opening.end(0))
569 pos = closing + 2
Gilles Peskine9ac62c32022-11-18 22:27:37 +0100570 # Replace inner comment by spaces. There needs to be at least one space
571 # for things like 'int/*ihatespaces*/foo'. Go further and preserve the
Gilles Peskined8c08032022-11-29 22:03:32 +0100572 # width of the comment and line breaks, this way positions in error
573 # messages remain correct.
Gilles Peskine07510f52022-11-11 16:37:16 +0100574 line = (line[:opening.start(0)] +
Gilles Peskined8c08032022-11-29 22:03:32 +0100575 re.sub(r'.', r' ', line[opening.start(0):pos]) +
Gilles Peskine07510f52022-11-11 16:37:16 +0100576 line[pos:])
Gilles Peskined8c08032022-11-29 22:03:32 +0100577 # Strip whitespace at the end of lines (it's irrelevant to error messages).
Gilles Peskine07510f52022-11-11 16:37:16 +0100578 return re.sub(r' +(\n|\Z)', r'\1', line)
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +0100579
Azim Khanb31aa442018-07-03 11:57:54 +0100580def parse_function_code(funcs_f, dependencies, suite_dependencies):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100581 """
Azim Khan040b6a22018-06-28 16:49:13 +0100582 Parses out a function from function file object and generates
583 function and dispatch code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100584
Azim Khanf0e42fb2017-08-02 14:47:13 +0100585 :param funcs_f: file object of the functions file.
Azim Khanb31aa442018-07-03 11:57:54 +0100586 :param dependencies: List of dependencies
587 :param suite_dependencies: List of test suite dependencies
Azim Khanf0e42fb2017-08-02 14:47:13 +0100588 :return: Function name, arguments, function code and dispatch code.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100589 """
Azim Khanfcdf6852018-07-05 17:31:46 +0100590 line_directive = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name)
591 code = ''
Azim Khan8d686bf2018-07-04 23:29:46 +0100592 has_exit_label = False
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100593 for line in funcs_f:
Azim Khanfcdf6852018-07-05 17:31:46 +0100594 # Check function signature. Function signature may be split
595 # across multiple lines. Here we try to find the start of
596 # arguments list, then remove '\n's and apply the regex to
597 # detect function start.
Gilles Peskine07510f52022-11-11 16:37:16 +0100598 line = skip_comments(line, funcs_f)
Azim Khanfcdf6852018-07-05 17:31:46 +0100599 up_to_arg_list_start = code + line[:line.find('(') + 1]
600 match = re.match(TEST_FUNCTION_VALIDATION_REGEX,
601 up_to_arg_list_start.replace('\n', ' '), re.I)
Azim Khanb31aa442018-07-03 11:57:54 +0100602 if match:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100603 # check if we have full signature i.e. split in more lines
Azim Khanfcdf6852018-07-05 17:31:46 +0100604 name = match.group('func_name')
Azim Khan8d686bf2018-07-04 23:29:46 +0100605 if not re.match(FUNCTION_ARG_LIST_END_REGEX, line):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100606 for lin in funcs_f:
Gilles Peskine07510f52022-11-11 16:37:16 +0100607 line += skip_comments(lin, funcs_f)
Azim Khan8d686bf2018-07-04 23:29:46 +0100608 if re.search(FUNCTION_ARG_LIST_END_REGEX, line):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100609 break
Azim Khanfcdf6852018-07-05 17:31:46 +0100610 args, local_vars, args_dispatch = parse_function_arguments(
Azim Khanb31aa442018-07-03 11:57:54 +0100611 line)
Azim Khan8d686bf2018-07-04 23:29:46 +0100612 code += line
Azim Khanfcdf6852018-07-05 17:31:46 +0100613 break
614 code += line
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100615 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100616 raise GeneratorInputError("file: %s - Test functions not found!" %
Azim Khanb31aa442018-07-03 11:57:54 +0100617 funcs_f.name)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100618
Azim Khanfcdf6852018-07-05 17:31:46 +0100619 # Prefix test function name with 'test_'
620 code = code.replace(name, 'test_' + name, 1)
621 name = 'test_' + name
622
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100623 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100624 if re.search(END_CASE_REGEX, line):
625 break
Azim Khan8d686bf2018-07-04 23:29:46 +0100626 if not has_exit_label:
627 has_exit_label = \
628 re.search(EXIT_LABEL_REGEX, line.strip()) is not None
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100629 code += line
630 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100631 raise GeneratorInputError("file: %s - end case pattern [%s] not "
Azim Khanb31aa442018-07-03 11:57:54 +0100632 "found!" % (funcs_f.name, END_CASE_REGEX))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100633
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +0100634 code = line_directive + code
635 code = generate_function_code(name, code, local_vars, args_dispatch,
636 dependencies)
Azim Khanb31aa442018-07-03 11:57:54 +0100637 dispatch_code = gen_dispatch(name, suite_dependencies + dependencies)
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +0100638 return (name, args, code, dispatch_code)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100639
640
641def parse_functions(funcs_f):
642 """
Azim Khane3b26af2018-06-29 02:36:57 +0100643 Parses a test_suite_xxx.function file and returns information
644 for generating a C source file for the test suite.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100645
Azim Khanf0e42fb2017-08-02 14:47:13 +0100646 :param funcs_f: file object of the functions file.
Azim Khan040b6a22018-06-28 16:49:13 +0100647 :return: List of test suite dependencies, test function dispatch
648 code, function code and a dict with function identifiers
649 and arguments info.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100650 """
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000651 suite_helpers = ''
Azim Khanb31aa442018-07-03 11:57:54 +0100652 suite_dependencies = []
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100653 suite_functions = ''
654 func_info = {}
655 function_idx = 0
656 dispatch_code = ''
657 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100658 if re.search(BEGIN_HEADER_REGEX, line):
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +0100659 suite_helpers += parse_until_pattern(funcs_f, END_HEADER_REGEX)
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000660 elif re.search(BEGIN_SUITE_HELPERS_REGEX, line):
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +0100661 suite_helpers += parse_until_pattern(funcs_f,
662 END_SUITE_HELPERS_REGEX)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100663 elif re.search(BEGIN_DEP_REGEX, line):
Azim Khanb31aa442018-07-03 11:57:54 +0100664 suite_dependencies += parse_suite_dependencies(funcs_f)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100665 elif re.search(BEGIN_CASE_REGEX, line):
Azim Khan8d686bf2018-07-04 23:29:46 +0100666 try:
667 dependencies = parse_function_dependencies(line)
668 except GeneratorInputError as error:
669 raise GeneratorInputError(
670 "%s:%d: %s" % (funcs_f.name, funcs_f.line_no,
671 str(error)))
Azim Khan040b6a22018-06-28 16:49:13 +0100672 func_name, args, func_code, func_dispatch =\
Azim Khanb31aa442018-07-03 11:57:54 +0100673 parse_function_code(funcs_f, dependencies, suite_dependencies)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100674 suite_functions += func_code
675 # Generate dispatch code and enumeration info
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100676 if func_name in func_info:
677 raise GeneratorInputError(
Azim Khanb31aa442018-07-03 11:57:54 +0100678 "file: %s - function %s re-declared at line %d" %
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100679 (funcs_f.name, func_name, funcs_f.line_no))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100680 func_info[func_name] = (function_idx, args)
681 dispatch_code += '/* Function Id: %d */\n' % function_idx
682 dispatch_code += func_dispatch
683 function_idx += 1
684
Azim Khanb31aa442018-07-03 11:57:54 +0100685 func_code = (suite_helpers +
686 suite_functions).join(gen_dependencies(suite_dependencies))
687 return suite_dependencies, dispatch_code, func_code, func_info
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100688
689
Azim Khanb31aa442018-07-03 11:57:54 +0100690def escaped_split(inp_str, split_char):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100691 """
Azim Khanb31aa442018-07-03 11:57:54 +0100692 Split inp_str on character split_char but ignore if escaped.
Azim Khan040b6a22018-06-28 16:49:13 +0100693 Since, return value is used to write back to the intermediate
694 data file, any escape characters in the input are retained in the
695 output.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100696
Azim Khanb31aa442018-07-03 11:57:54 +0100697 :param inp_str: String to split
Azim Khan8d686bf2018-07-04 23:29:46 +0100698 :param split_char: Split character
Azim Khanf0e42fb2017-08-02 14:47:13 +0100699 :return: List of splits
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100700 """
Azim Khanb31aa442018-07-03 11:57:54 +0100701 if len(split_char) > 1:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100702 raise ValueError('Expected split character. Found string!')
Azim Khan63028132018-07-05 17:53:11 +0100703 out = re.sub(r'(\\.)|' + split_char,
704 lambda m: m.group(1) or '\n', inp_str,
705 len(inp_str)).split('\n')
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +0100706 out = [x for x in out if x]
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100707 return out
708
709
Azim Khanb31aa442018-07-03 11:57:54 +0100710def parse_test_data(data_f):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100711 """
Azim Khane3b26af2018-06-29 02:36:57 +0100712 Parses .data file for each test case name, test function name,
713 test dependencies and test arguments. This information is
714 correlated with the test functions file for generating an
715 intermediate data file replacing the strings for test function
716 names, dependencies and integer constant expressions with
717 identifiers. Mainly for optimising space for on-target
718 execution.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100719
Azim Khanf0e42fb2017-08-02 14:47:13 +0100720 :param data_f: file object of the data file.
Azim Khan040b6a22018-06-28 16:49:13 +0100721 :return: Generator that yields test name, function name,
722 dependency list and function argument list.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100723 """
Azim Khanb31aa442018-07-03 11:57:54 +0100724 __state_read_name = 0
725 __state_read_args = 1
726 state = __state_read_name
727 dependencies = []
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100728 name = ''
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100729 for line in data_f:
730 line = line.strip()
Azim Khan8d686bf2018-07-04 23:29:46 +0100731 # Skip comments
732 if line.startswith('#'):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100733 continue
734
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100735 # Blank line indicates end of test
Azim Khanb31aa442018-07-03 11:57:54 +0100736 if not line:
737 if state == __state_read_args:
Azim Khan040b6a22018-06-28 16:49:13 +0100738 raise GeneratorInputError("[%s:%d] Newline before arguments. "
739 "Test function and arguments "
740 "missing for %s" %
741 (data_f.name, data_f.line_no, name))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100742 continue
743
Azim Khanb31aa442018-07-03 11:57:54 +0100744 if state == __state_read_name:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100745 # Read test name
746 name = line
Azim Khanb31aa442018-07-03 11:57:54 +0100747 state = __state_read_args
748 elif state == __state_read_args:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100749 # Check dependencies
Azim Khan8d686bf2018-07-04 23:29:46 +0100750 match = re.search(DEPENDENCY_REGEX, line)
Azim Khanb31aa442018-07-03 11:57:54 +0100751 if match:
Azim Khan8d686bf2018-07-04 23:29:46 +0100752 try:
753 dependencies = parse_dependencies(
754 match.group('dependencies'))
755 except GeneratorInputError as error:
756 raise GeneratorInputError(
757 str(error) + " - %s:%d" %
758 (data_f.name, data_f.line_no))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100759 else:
760 # Read test vectors
761 parts = escaped_split(line, ':')
Azim Khanb31aa442018-07-03 11:57:54 +0100762 test_function = parts[0]
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100763 args = parts[1:]
Azim Khanb31aa442018-07-03 11:57:54 +0100764 yield name, test_function, dependencies, args
765 dependencies = []
766 state = __state_read_name
767 if state == __state_read_args:
Azim Khan040b6a22018-06-28 16:49:13 +0100768 raise GeneratorInputError("[%s:%d] Newline before arguments. "
769 "Test function and arguments missing for "
770 "%s" % (data_f.name, data_f.line_no, name))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100771
772
773def gen_dep_check(dep_id, dep):
774 """
Azim Khane3b26af2018-06-29 02:36:57 +0100775 Generate code for checking dependency with the associated
776 identifier.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100777
Azim Khanf0e42fb2017-08-02 14:47:13 +0100778 :param dep_id: Dependency identifier
779 :param dep: Dependency macro
780 :return: Dependency check code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100781 """
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100782 if dep_id < 0:
Azim Khan040b6a22018-06-28 16:49:13 +0100783 raise GeneratorInputError("Dependency Id should be a positive "
784 "integer.")
Azim Khanb31aa442018-07-03 11:57:54 +0100785 _not, dep = ('!', dep[1:]) if dep[0] == '!' else ('', dep)
786 if not dep:
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100787 raise GeneratorInputError("Dependency should not be an empty string.")
Ron Eldorb9b38132018-11-27 16:35:20 +0200788
789 dependency = re.match(CONDITION_REGEX, dep, re.I)
790 if not dependency:
791 raise GeneratorInputError('Invalid dependency %s' % dep)
792
793 _defined = '' if dependency.group(2) else 'defined'
794 _cond = dependency.group(2) if dependency.group(2) else ''
795 _value = dependency.group(3) if dependency.group(3) else ''
796
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100797 dep_check = '''
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100798 case {id}:
799 {{
Ron Eldorb9b38132018-11-27 16:35:20 +0200800#if {_not}{_defined}({macro}{_cond}{_value})
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100801 ret = DEPENDENCY_SUPPORTED;
Azim Khand61b8372017-07-10 11:54:01 +0100802#else
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100803 ret = DEPENDENCY_NOT_SUPPORTED;
Azim Khand61b8372017-07-10 11:54:01 +0100804#endif
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100805 }}
Ron Eldorb9b38132018-11-27 16:35:20 +0200806 break;'''.format(_not=_not, _defined=_defined,
807 macro=dependency.group(1), id=dep_id,
808 _cond=_cond, _value=_value)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100809 return dep_check
810
811
812def gen_expression_check(exp_id, exp):
813 """
Azim Khane3b26af2018-06-29 02:36:57 +0100814 Generates code for evaluating an integer expression using
815 associated expression Id.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100816
Azim Khanf0e42fb2017-08-02 14:47:13 +0100817 :param exp_id: Expression Identifier
818 :param exp: Expression/Macro
819 :return: Expression check code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100820 """
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100821 if exp_id < 0:
Azim Khan040b6a22018-06-28 16:49:13 +0100822 raise GeneratorInputError("Expression Id should be a positive "
823 "integer.")
Azim Khanb31aa442018-07-03 11:57:54 +0100824 if not exp:
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100825 raise GeneratorInputError("Expression should not be an empty string.")
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100826 exp_code = '''
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100827 case {exp_id}:
828 {{
829 *out_value = {expression};
830 }}
831 break;'''.format(exp_id=exp_id, expression=exp)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100832 return exp_code
833
834
Azim Khanb31aa442018-07-03 11:57:54 +0100835def write_dependencies(out_data_f, test_dependencies, unique_dependencies):
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100836 """
Azim Khane3b26af2018-06-29 02:36:57 +0100837 Write dependencies to intermediate test data file, replacing
838 the string form with identifiers. Also, generates dependency
839 check code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100840
Azim Khanf0e42fb2017-08-02 14:47:13 +0100841 :param out_data_f: Output intermediate data file
Azim Khanb31aa442018-07-03 11:57:54 +0100842 :param test_dependencies: Dependencies
843 :param unique_dependencies: Mutable list to track unique dependencies
Azim Khan040b6a22018-06-28 16:49:13 +0100844 that are global to this re-entrant function.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100845 :return: returns dependency check code.
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100846 """
Azim Khan599cd242017-07-06 17:34:27 +0100847 dep_check_code = ''
Azim Khanb31aa442018-07-03 11:57:54 +0100848 if test_dependencies:
Azim Khan599cd242017-07-06 17:34:27 +0100849 out_data_f.write('depends_on')
Azim Khanb31aa442018-07-03 11:57:54 +0100850 for dep in test_dependencies:
851 if dep not in unique_dependencies:
852 unique_dependencies.append(dep)
853 dep_id = unique_dependencies.index(dep)
Azim Khan599cd242017-07-06 17:34:27 +0100854 dep_check_code += gen_dep_check(dep_id, dep)
855 else:
Azim Khanb31aa442018-07-03 11:57:54 +0100856 dep_id = unique_dependencies.index(dep)
Azim Khan599cd242017-07-06 17:34:27 +0100857 out_data_f.write(':' + str(dep_id))
858 out_data_f.write('\n')
859 return dep_check_code
860
861
862def write_parameters(out_data_f, test_args, func_args, unique_expressions):
863 """
Azim Khane3b26af2018-06-29 02:36:57 +0100864 Writes test parameters to the intermediate data file, replacing
865 the string form with identifiers. Also, generates expression
866 check code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100867
Azim Khanf0e42fb2017-08-02 14:47:13 +0100868 :param out_data_f: Output intermediate data file
869 :param test_args: Test parameters
870 :param func_args: Function arguments
Azim Khan040b6a22018-06-28 16:49:13 +0100871 :param unique_expressions: Mutable list to track unique
872 expressions that are global to this re-entrant function.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100873 :return: Returns expression check code.
Azim Khan599cd242017-07-06 17:34:27 +0100874 """
875 expression_code = ''
Azim Khanb31aa442018-07-03 11:57:54 +0100876 for i, _ in enumerate(test_args):
Azim Khan599cd242017-07-06 17:34:27 +0100877 typ = func_args[i]
878 val = test_args[i]
879
Azim Khan040b6a22018-06-28 16:49:13 +0100880 # check if val is a non literal int val (i.e. an expression)
Azim Khan8d686bf2018-07-04 23:29:46 +0100881 if typ == 'int' and not re.match(r'(\d+|0x[0-9a-f]+)$',
882 val, re.I):
Azim Khan599cd242017-07-06 17:34:27 +0100883 typ = 'exp'
884 if val not in unique_expressions:
885 unique_expressions.append(val)
Azim Khan040b6a22018-06-28 16:49:13 +0100886 # exp_id can be derived from len(). But for
887 # readability and consistency with case of existing
888 # let's use index().
Azim Khan599cd242017-07-06 17:34:27 +0100889 exp_id = unique_expressions.index(val)
890 expression_code += gen_expression_check(exp_id, val)
891 val = exp_id
892 else:
893 val = unique_expressions.index(val)
894 out_data_f.write(':' + typ + ':' + str(val))
895 out_data_f.write('\n')
896 return expression_code
897
898
Azim Khanb31aa442018-07-03 11:57:54 +0100899def gen_suite_dep_checks(suite_dependencies, dep_check_code, expression_code):
Azim Khan599cd242017-07-06 17:34:27 +0100900 """
Azim Khane3b26af2018-06-29 02:36:57 +0100901 Generates preprocessor checks for test suite dependencies.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100902
Azim Khanb31aa442018-07-03 11:57:54 +0100903 :param suite_dependencies: Test suite dependencies read from the
Azim Khan8d686bf2018-07-04 23:29:46 +0100904 .function file.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100905 :param dep_check_code: Dependency check code
906 :param expression_code: Expression check code
Azim Khan040b6a22018-06-28 16:49:13 +0100907 :return: Dependency and expression code guarded by test suite
908 dependencies.
Azim Khan599cd242017-07-06 17:34:27 +0100909 """
Azim Khanb31aa442018-07-03 11:57:54 +0100910 if suite_dependencies:
911 preprocessor_check = gen_dependencies_one_line(suite_dependencies)
Azim Khan599cd242017-07-06 17:34:27 +0100912 dep_check_code = '''
Azim Khanb31aa442018-07-03 11:57:54 +0100913{preprocessor_check}
Azim Khan599cd242017-07-06 17:34:27 +0100914{code}
Azim Khan599cd242017-07-06 17:34:27 +0100915#endif
Azim Khanb31aa442018-07-03 11:57:54 +0100916'''.format(preprocessor_check=preprocessor_check, code=dep_check_code)
Azim Khan599cd242017-07-06 17:34:27 +0100917 expression_code = '''
Azim Khanb31aa442018-07-03 11:57:54 +0100918{preprocessor_check}
Azim Khan599cd242017-07-06 17:34:27 +0100919{code}
Azim Khan599cd242017-07-06 17:34:27 +0100920#endif
Azim Khanb31aa442018-07-03 11:57:54 +0100921'''.format(preprocessor_check=preprocessor_check, code=expression_code)
Azim Khan599cd242017-07-06 17:34:27 +0100922 return dep_check_code, expression_code
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100923
924
Azim Khanb31aa442018-07-03 11:57:54 +0100925def gen_from_test_data(data_f, out_data_f, func_info, suite_dependencies):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100926 """
Azim Khane3b26af2018-06-29 02:36:57 +0100927 This function reads test case name, dependencies and test vectors
928 from the .data file. This information is correlated with the test
929 functions file for generating an intermediate data file replacing
930 the strings for test function names, dependencies and integer
931 constant expressions with identifiers. Mainly for optimising
932 space for on-target execution.
933 It also generates test case dependency check code and expression
934 evaluation code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100935
Azim Khanf0e42fb2017-08-02 14:47:13 +0100936 :param data_f: Data file object
Azim Khan8d686bf2018-07-04 23:29:46 +0100937 :param out_data_f: Output intermediate data file
Azim Khan040b6a22018-06-28 16:49:13 +0100938 :param func_info: Dict keyed by function and with function id
939 and arguments info
Azim Khanb31aa442018-07-03 11:57:54 +0100940 :param suite_dependencies: Test suite dependencies
Azim Khanf0e42fb2017-08-02 14:47:13 +0100941 :return: Returns dependency and expression check code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100942 """
Azim Khanb31aa442018-07-03 11:57:54 +0100943 unique_dependencies = []
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100944 unique_expressions = []
945 dep_check_code = ''
946 expression_code = ''
Azim Khanb31aa442018-07-03 11:57:54 +0100947 for test_name, function_name, test_dependencies, test_args in \
948 parse_test_data(data_f):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100949 out_data_f.write(test_name + '\n')
950
Azim Khanb31aa442018-07-03 11:57:54 +0100951 # Write dependencies
952 dep_check_code += write_dependencies(out_data_f, test_dependencies,
953 unique_dependencies)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100954
Azim Khan599cd242017-07-06 17:34:27 +0100955 # Write test function name
956 test_function_name = 'test_' + function_name
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100957 if test_function_name not in func_info:
Azim Khan040b6a22018-06-28 16:49:13 +0100958 raise GeneratorInputError("Function %s not found!" %
959 test_function_name)
Azim Khan599cd242017-07-06 17:34:27 +0100960 func_id, func_args = func_info[test_function_name]
961 out_data_f.write(str(func_id))
962
963 # Write parameters
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100964 if len(test_args) != len(func_args):
Azim Khan040b6a22018-06-28 16:49:13 +0100965 raise GeneratorInputError("Invalid number of arguments in test "
Azim Khanb31aa442018-07-03 11:57:54 +0100966 "%s. See function %s signature." %
967 (test_name, function_name))
Azim Khan040b6a22018-06-28 16:49:13 +0100968 expression_code += write_parameters(out_data_f, test_args, func_args,
969 unique_expressions)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100970
Azim Khan599cd242017-07-06 17:34:27 +0100971 # Write a newline as test case separator
972 out_data_f.write('\n')
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100973
Azim Khanb31aa442018-07-03 11:57:54 +0100974 dep_check_code, expression_code = gen_suite_dep_checks(
975 suite_dependencies, dep_check_code, expression_code)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100976 return dep_check_code, expression_code
977
978
Azim Khanb31aa442018-07-03 11:57:54 +0100979def add_input_info(funcs_file, data_file, template_file,
980 c_file, snippets):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100981 """
Azim Khanb31aa442018-07-03 11:57:54 +0100982 Add generator input info in snippets.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100983
Azim Khanf0e42fb2017-08-02 14:47:13 +0100984 :param funcs_file: Functions file object
985 :param data_file: Data file object
986 :param template_file: Template file object
Azim Khanf0e42fb2017-08-02 14:47:13 +0100987 :param c_file: Output C file object
Azim Khanb31aa442018-07-03 11:57:54 +0100988 :param snippets: Dictionary to contain code pieces to be
989 substituted in the template.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100990 :return:
991 """
Azim Khanb31aa442018-07-03 11:57:54 +0100992 snippets['test_file'] = c_file
993 snippets['test_main_file'] = template_file
994 snippets['test_case_file'] = funcs_file
995 snippets['test_case_data_file'] = data_file
996
997
998def read_code_from_input_files(platform_file, helpers_file,
999 out_data_file, snippets):
1000 """
1001 Read code from input files and create substitutions for replacement
1002 strings in the template file.
1003
1004 :param platform_file: Platform file object
1005 :param helpers_file: Helper functions file object
1006 :param out_data_file: Output intermediate data file object
1007 :param snippets: Dictionary to contain code pieces to be
1008 substituted in the template.
1009 :return:
1010 """
1011 # Read helpers
1012 with open(helpers_file, 'r') as help_f, open(platform_file, 'r') as \
1013 platform_f:
1014 snippets['test_common_helper_file'] = helpers_file
1015 snippets['test_common_helpers'] = help_f.read()
1016 snippets['test_platform_file'] = platform_file
1017 snippets['platform_code'] = platform_f.read().replace(
1018 'DATA_FILE', out_data_file.replace('\\', '\\\\')) # escape '\'
1019
1020
1021def write_test_source_file(template_file, c_file, snippets):
1022 """
1023 Write output source file with generated source code.
1024
1025 :param template_file: Template file name
1026 :param c_file: Output source file
1027 :param snippets: Generated and code snippets
1028 :return:
1029 """
David Horstmannb85838f2022-11-03 17:49:29 +00001030
1031 # Create a placeholder pattern with the correct named capture groups
1032 # to override the default provided with Template.
1033 # Match nothing (no way of escaping placeholders).
1034 escaped = "(?P<escaped>(?!))"
1035 # Match the "__MBEDTLS_TEST_TEMPLATE__PLACEHOLDER_NAME" pattern.
1036 named = "__MBEDTLS_TEST_TEMPLATE__(?P<named>[A-Z][_A-Z0-9]*)"
1037 # Match nothing (no braced placeholder syntax).
1038 braced = "(?P<braced>(?!))"
1039 # If not already matched, a "__MBEDTLS_TEST_TEMPLATE__" prefix is invalid.
1040 invalid = "(?P<invalid>__MBEDTLS_TEST_TEMPLATE__)"
David Horstmann360f8e42022-11-09 17:27:33 +00001041 placeholder_pattern = re.compile("|".join([escaped, named, braced, invalid]))
David Horstmannb85838f2022-11-03 17:49:29 +00001042
Azim Khanb31aa442018-07-03 11:57:54 +01001043 with open(template_file, 'r') as template_f, open(c_file, 'w') as c_f:
Mohammad Azim Khand2d01122018-07-18 17:48:37 +01001044 for line_no, line in enumerate(template_f.readlines(), 1):
Azim Khanb31aa442018-07-03 11:57:54 +01001045 # Update line number. +1 as #line directive sets next line number
1046 snippets['line_no'] = line_no + 1
David Horstmannb85838f2022-11-03 17:49:29 +00001047 template = string.Template(line)
1048 template.pattern = placeholder_pattern
1049 snippets = {k.upper():v for (k, v) in snippets.items()}
1050 code = template.substitute(**snippets)
Azim Khanb31aa442018-07-03 11:57:54 +01001051 c_f.write(code)
Azim Khanb31aa442018-07-03 11:57:54 +01001052
1053
1054def parse_function_file(funcs_file, snippets):
1055 """
1056 Parse function file and generate function dispatch code.
1057
1058 :param funcs_file: Functions file name
1059 :param snippets: Dictionary to contain code pieces to be
1060 substituted in the template.
1061 :return:
1062 """
1063 with FileWrapper(funcs_file) as funcs_f:
1064 suite_dependencies, dispatch_code, func_code, func_info = \
1065 parse_functions(funcs_f)
1066 snippets['functions_code'] = func_code
1067 snippets['dispatch_code'] = dispatch_code
1068 return suite_dependencies, func_info
1069
1070
1071def generate_intermediate_data_file(data_file, out_data_file,
1072 suite_dependencies, func_info, snippets):
1073 """
1074 Generates intermediate data file from input data file and
1075 information read from functions file.
1076
1077 :param data_file: Data file name
1078 :param out_data_file: Output/Intermediate data file
1079 :param suite_dependencies: List of suite dependencies.
1080 :param func_info: Function info parsed from functions file.
1081 :param snippets: Dictionary to contain code pieces to be
1082 substituted in the template.
1083 :return:
1084 """
1085 with FileWrapper(data_file) as data_f, \
1086 open(out_data_file, 'w') as out_data_f:
1087 dep_check_code, expression_code = gen_from_test_data(
1088 data_f, out_data_f, func_info, suite_dependencies)
1089 snippets['dep_check_code'] = dep_check_code
1090 snippets['expression_code'] = expression_code
1091
1092
1093def generate_code(**input_info):
1094 """
1095 Generates C source code from test suite file, data file, common
1096 helpers file and platform file.
1097
1098 input_info expands to following parameters:
1099 funcs_file: Functions file object
1100 data_file: Data file object
1101 template_file: Template file object
1102 platform_file: Platform file object
1103 helpers_file: Helper functions file object
1104 suites_dir: Test suites dir
1105 c_file: Output C file object
1106 out_data_file: Output intermediate data file object
1107 :return:
1108 """
1109 funcs_file = input_info['funcs_file']
1110 data_file = input_info['data_file']
1111 template_file = input_info['template_file']
1112 platform_file = input_info['platform_file']
1113 helpers_file = input_info['helpers_file']
1114 suites_dir = input_info['suites_dir']
1115 c_file = input_info['c_file']
1116 out_data_file = input_info['out_data_file']
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001117 for name, path in [('Functions file', funcs_file),
1118 ('Data file', data_file),
1119 ('Template file', template_file),
1120 ('Platform file', platform_file),
Azim Khane3b26af2018-06-29 02:36:57 +01001121 ('Helpers code file', helpers_file),
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001122 ('Suites dir', suites_dir)]:
1123 if not os.path.exists(path):
1124 raise IOError("ERROR: %s [%s] not found!" % (name, path))
1125
Azim Khanb31aa442018-07-03 11:57:54 +01001126 snippets = {'generator_script': os.path.basename(__file__)}
1127 read_code_from_input_files(platform_file, helpers_file,
1128 out_data_file, snippets)
1129 add_input_info(funcs_file, data_file, template_file,
1130 c_file, snippets)
1131 suite_dependencies, func_info = parse_function_file(funcs_file, snippets)
1132 generate_intermediate_data_file(data_file, out_data_file,
1133 suite_dependencies, func_info, snippets)
1134 write_test_source_file(template_file, c_file, snippets)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001135
1136
Azim Khan8d686bf2018-07-04 23:29:46 +01001137def main():
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001138 """
1139 Command line parser.
1140
1141 :return:
1142 """
Azim Khan040b6a22018-06-28 16:49:13 +01001143 parser = argparse.ArgumentParser(
Azim Khane3b26af2018-06-29 02:36:57 +01001144 description='Dynamically generate test suite code.')
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001145
1146 parser.add_argument("-f", "--functions-file",
1147 dest="funcs_file",
1148 help="Functions file",
Azim Khane3b26af2018-06-29 02:36:57 +01001149 metavar="FUNCTIONS_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001150 required=True)
1151
1152 parser.add_argument("-d", "--data-file",
1153 dest="data_file",
1154 help="Data file",
Azim Khane3b26af2018-06-29 02:36:57 +01001155 metavar="DATA_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001156 required=True)
1157
1158 parser.add_argument("-t", "--template-file",
1159 dest="template_file",
1160 help="Template file",
Azim Khane3b26af2018-06-29 02:36:57 +01001161 metavar="TEMPLATE_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001162 required=True)
1163
1164 parser.add_argument("-s", "--suites-dir",
1165 dest="suites_dir",
1166 help="Suites dir",
Azim Khane3b26af2018-06-29 02:36:57 +01001167 metavar="SUITES_DIR",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001168 required=True)
1169
Azim Khane3b26af2018-06-29 02:36:57 +01001170 parser.add_argument("--helpers-file",
1171 dest="helpers_file",
1172 help="Helpers file",
1173 metavar="HELPERS_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001174 required=True)
1175
1176 parser.add_argument("-p", "--platform-file",
1177 dest="platform_file",
1178 help="Platform code file",
1179 metavar="PLATFORM_FILE",
1180 required=True)
1181
1182 parser.add_argument("-o", "--out-dir",
1183 dest="out_dir",
1184 help="Dir where generated code and scripts are copied",
1185 metavar="OUT_DIR",
1186 required=True)
1187
1188 args = parser.parse_args()
1189
1190 data_file_name = os.path.basename(args.data_file)
1191 data_name = os.path.splitext(data_file_name)[0]
1192
1193 out_c_file = os.path.join(args.out_dir, data_name + '.c')
Mohammad Azim Khan00c4b092018-06-28 13:10:19 +01001194 out_data_file = os.path.join(args.out_dir, data_name + '.datax')
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001195
1196 out_c_file_dir = os.path.dirname(out_c_file)
1197 out_data_file_dir = os.path.dirname(out_data_file)
Azim Khanb31aa442018-07-03 11:57:54 +01001198 for directory in [out_c_file_dir, out_data_file_dir]:
1199 if not os.path.exists(directory):
1200 os.makedirs(directory)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001201
Azim Khanb31aa442018-07-03 11:57:54 +01001202 generate_code(funcs_file=args.funcs_file, data_file=args.data_file,
1203 template_file=args.template_file,
1204 platform_file=args.platform_file,
1205 helpers_file=args.helpers_file, suites_dir=args.suites_dir,
1206 c_file=out_c_file, out_data_file=out_data_file)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001207
1208
1209if __name__ == "__main__":
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +01001210 try:
Azim Khan8d686bf2018-07-04 23:29:46 +01001211 main()
Azim Khanb31aa442018-07-03 11:57:54 +01001212 except GeneratorInputError as err:
Mohammad Azim Khan440d8732018-07-18 12:50:49 +01001213 sys.exit("%s: input error: %s" %
1214 (os.path.basename(sys.argv[0]), str(err)))