blob: 5066a57b7fdac0d898eb187f27fbeeda6b9203a3 [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 Horstmann8eff06f2022-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 Khan1ec7e6f2018-04-11 23:46:37 +0100166import io
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100167import os
168import re
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +0100169import sys
Mohammad Azim Khan5cb70172018-07-19 11:32:30 +0100170import string
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100171import argparse
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100172
173
Azim Khanb31aa442018-07-03 11:57:54 +0100174BEGIN_HEADER_REGEX = r'/\*\s*BEGIN_HEADER\s*\*/'
175END_HEADER_REGEX = r'/\*\s*END_HEADER\s*\*/'
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100176
Azim Khanb31aa442018-07-03 11:57:54 +0100177BEGIN_SUITE_HELPERS_REGEX = r'/\*\s*BEGIN_SUITE_HELPERS\s*\*/'
178END_SUITE_HELPERS_REGEX = r'/\*\s*END_SUITE_HELPERS\s*\*/'
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000179
Azim Khanb31aa442018-07-03 11:57:54 +0100180BEGIN_DEP_REGEX = r'BEGIN_DEPENDENCIES'
181END_DEP_REGEX = r'END_DEPENDENCIES'
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100182
Azim Khan8d686bf2018-07-04 23:29:46 +0100183BEGIN_CASE_REGEX = r'/\*\s*BEGIN_CASE\s*(?P<depends_on>.*?)\s*\*/'
Azim Khanb31aa442018-07-03 11:57:54 +0100184END_CASE_REGEX = r'/\*\s*END_CASE\s*\*/'
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100185
Azim Khan8d686bf2018-07-04 23:29:46 +0100186DEPENDENCY_REGEX = r'depends_on:(?P<dependencies>.*)'
Ron Eldorb9b38132018-11-27 16:35:20 +0200187C_IDENTIFIER_REGEX = r'!?[a-z_][a-z0-9_]*'
188CONDITION_OPERATOR_REGEX = r'[!=]=|[<>]=?'
189# forbid 0ddd which might be accidentally octal or accidentally decimal
190CONDITION_VALUE_REGEX = r'[-+]?(0x[0-9a-f]+|0|[1-9][0-9]*)'
191CONDITION_REGEX = r'({})(?:\s*({})\s*({}))?$'.format(C_IDENTIFIER_REGEX,
192 CONDITION_OPERATOR_REGEX,
193 CONDITION_VALUE_REGEX)
Azim Khanfcdf6852018-07-05 17:31:46 +0100194TEST_FUNCTION_VALIDATION_REGEX = r'\s*void\s+(?P<func_name>\w+)\s*\('
Azim Khan8d686bf2018-07-04 23:29:46 +0100195INT_CHECK_REGEX = r'int\s+.*'
196CHAR_CHECK_REGEX = r'char\s*\*\s*.*'
197DATA_T_CHECK_REGEX = r'data_t\s*\*\s*.*'
Azim Khan8d686bf2018-07-04 23:29:46 +0100198FUNCTION_ARG_LIST_END_REGEX = r'.*\)'
199EXIT_LABEL_REGEX = r'^exit:'
200
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100201
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100202class GeneratorInputError(Exception):
203 """
Azim Khane3b26af2018-06-29 02:36:57 +0100204 Exception to indicate error in the input files to this script.
205 This includes missing patterns, test function names and other
206 parsing errors.
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100207 """
208 pass
209
210
Gilles Peskine184c0962020-03-24 18:25:17 +0100211class FileWrapper(io.FileIO):
Azim Khan4b543232017-06-30 09:35:21 +0100212 """
Azim Khane3b26af2018-06-29 02:36:57 +0100213 This class extends built-in io.FileIO class with attribute line_no,
214 that indicates line number for the line that is read.
Azim Khan4b543232017-06-30 09:35:21 +0100215 """
216
217 def __init__(self, file_name):
218 """
Azim Khane3b26af2018-06-29 02:36:57 +0100219 Instantiate the base class and initialize the line number to 0.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100220
Azim Khanf0e42fb2017-08-02 14:47:13 +0100221 :param file_name: File path to open.
Azim Khan4b543232017-06-30 09:35:21 +0100222 """
Gilles Peskine5f0057d2022-11-10 19:33:25 +0100223 super().__init__(file_name, 'r')
Azim Khanb31aa442018-07-03 11:57:54 +0100224 self._line_no = 0
Azim Khan4b543232017-06-30 09:35:21 +0100225
Gilles Peskine5f0057d2022-11-10 19:33:25 +0100226 def __next__(self):
Azim Khan4b543232017-06-30 09:35:21 +0100227 """
Gilles Peskine5f0057d2022-11-10 19:33:25 +0100228 This method overrides base class's __next__ method and extends it
229 method to count the line numbers as each line is read.
Azim Khane3b26af2018-06-29 02:36:57 +0100230
Azim Khanf0e42fb2017-08-02 14:47:13 +0100231 :return: Line read from file.
Azim Khan4b543232017-06-30 09:35:21 +0100232 """
Gilles Peskine5f0057d2022-11-10 19:33:25 +0100233 line = super().__next__()
Azim Khanb31aa442018-07-03 11:57:54 +0100234 if line is not None:
235 self._line_no += 1
Azim Khan936ea932018-06-28 16:47:12 +0100236 # Convert byte array to string with correct encoding and
237 # strip any whitespaces added in the decoding process.
Azim Khan8d686bf2018-07-04 23:29:46 +0100238 return line.decode(sys.getdefaultencoding()).rstrip() + '\n'
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +0100239 return None
Azim Khane3b26af2018-06-29 02:36:57 +0100240
Azim Khanb31aa442018-07-03 11:57:54 +0100241 def get_line_no(self):
242 """
243 Gives current line number.
244 """
245 return self._line_no
246
247 line_no = property(get_line_no)
Azim Khan4b543232017-06-30 09:35:21 +0100248
249
250def split_dep(dep):
Azim Khanf0e42fb2017-08-02 14:47:13 +0100251 """
Azim Khanb31aa442018-07-03 11:57:54 +0100252 Split NOT character '!' from dependency. Used by gen_dependencies()
Azim Khanf0e42fb2017-08-02 14:47:13 +0100253
254 :param dep: Dependency list
Azim Khane3b26af2018-06-29 02:36:57 +0100255 :return: string tuple. Ex: ('!', MACRO) for !MACRO and ('', MACRO) for
256 MACRO.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100257 """
Azim Khan4b543232017-06-30 09:35:21 +0100258 return ('!', dep[1:]) if dep[0] == '!' else ('', dep)
259
260
Azim Khanb31aa442018-07-03 11:57:54 +0100261def gen_dependencies(dependencies):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100262 """
Azim Khane3b26af2018-06-29 02:36:57 +0100263 Test suite data and functions specifies compile time dependencies.
264 This function generates C preprocessor code from the input
265 dependency list. Caller uses the generated preprocessor code to
266 wrap dependent code.
267 A dependency in the input list can have a leading '!' character
268 to negate a condition. '!' is separated from the dependency using
269 function split_dep() and proper preprocessor check is generated
270 accordingly.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100271
Azim Khanb31aa442018-07-03 11:57:54 +0100272 :param dependencies: List of dependencies.
Azim Khan040b6a22018-06-28 16:49:13 +0100273 :return: if defined and endif code with macro annotations for
274 readability.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100275 """
Azim Khanb31aa442018-07-03 11:57:54 +0100276 dep_start = ''.join(['#if %sdefined(%s)\n' % (x, y) for x, y in
277 map(split_dep, dependencies)])
278 dep_end = ''.join(['#endif /* %s */\n' %
279 x for x in reversed(dependencies)])
Azim Khan4b543232017-06-30 09:35:21 +0100280
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100281 return dep_start, dep_end
282
283
Azim Khanb31aa442018-07-03 11:57:54 +0100284def gen_dependencies_one_line(dependencies):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100285 """
Azim Khanb31aa442018-07-03 11:57:54 +0100286 Similar to gen_dependencies() but generates dependency checks in one line.
Azim Khane3b26af2018-06-29 02:36:57 +0100287 Useful for generating code with #else block.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100288
Azim Khanb31aa442018-07-03 11:57:54 +0100289 :param dependencies: List of dependencies.
290 :return: Preprocessor check code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100291 """
Azim Khanb31aa442018-07-03 11:57:54 +0100292 defines = '#if ' if dependencies else ''
293 defines += ' && '.join(['%sdefined(%s)' % (x, y) for x, y in map(
294 split_dep, dependencies)])
Azim Khan4b543232017-06-30 09:35:21 +0100295 return defines
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100296
297
Azim Khanb31aa442018-07-03 11:57:54 +0100298def gen_function_wrapper(name, local_vars, args_dispatch):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100299 """
Azim Khan040b6a22018-06-28 16:49:13 +0100300 Creates test function wrapper code. A wrapper has the code to
301 unpack parameters from parameters[] array.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100302
Azim Khanf0e42fb2017-08-02 14:47:13 +0100303 :param name: Test function name
Azim Khanb31aa442018-07-03 11:57:54 +0100304 :param local_vars: Local variables declaration code
Azim Khan040b6a22018-06-28 16:49:13 +0100305 :param args_dispatch: List of dispatch arguments.
306 Ex: ['(char *)params[0]', '*((int *)params[1])']
Azim Khanf0e42fb2017-08-02 14:47:13 +0100307 :return: Test function wrapper.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100308 """
309 # Then create the wrapper
310 wrapper = '''
311void {name}_wrapper( void ** params )
312{{
Gilles Peskine77761412018-06-18 17:51:40 +0200313{unused_params}{locals}
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100314 {name}( {args} );
315}}
Gilles Peskine77761412018-06-18 17:51:40 +0200316'''.format(name=name,
Mohammad Azim Khanc3521df2018-06-26 14:06:52 +0100317 unused_params='' if args_dispatch else ' (void)params;\n',
Azim Khan4b543232017-06-30 09:35:21 +0100318 args=', '.join(args_dispatch),
Azim Khanb31aa442018-07-03 11:57:54 +0100319 locals=local_vars)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100320 return wrapper
321
322
Azim Khanb31aa442018-07-03 11:57:54 +0100323def gen_dispatch(name, dependencies):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100324 """
Azim Khane3b26af2018-06-29 02:36:57 +0100325 Test suite code template main_test.function defines a C function
326 array to contain test case functions. This function generates an
327 initializer entry for a function in that array. The entry is
328 composed of a compile time check for the test function
329 dependencies. At compile time the test function is assigned when
330 dependencies are met, else NULL is assigned.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100331
Azim Khanf0e42fb2017-08-02 14:47:13 +0100332 :param name: Test function name
Azim Khanb31aa442018-07-03 11:57:54 +0100333 :param dependencies: List of dependencies
Azim Khanf0e42fb2017-08-02 14:47:13 +0100334 :return: Dispatch code.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100335 """
Azim Khanb31aa442018-07-03 11:57:54 +0100336 if dependencies:
337 preprocessor_check = gen_dependencies_one_line(dependencies)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100338 dispatch_code = '''
Azim Khanb31aa442018-07-03 11:57:54 +0100339{preprocessor_check}
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100340 {name}_wrapper,
341#else
342 NULL,
343#endif
Azim Khanb31aa442018-07-03 11:57:54 +0100344'''.format(preprocessor_check=preprocessor_check, name=name)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100345 else:
346 dispatch_code = '''
347 {name}_wrapper,
348'''.format(name=name)
349
350 return dispatch_code
351
352
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000353def parse_until_pattern(funcs_f, end_regex):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100354 """
Azim Khane3b26af2018-06-29 02:36:57 +0100355 Matches pattern end_regex to the lines read from the file object.
356 Returns the lines read until end pattern is matched.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100357
Azim Khan8d686bf2018-07-04 23:29:46 +0100358 :param funcs_f: file object for .function file
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000359 :param end_regex: Pattern to stop parsing
Azim Khane3b26af2018-06-29 02:36:57 +0100360 :return: Lines read before the end pattern
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100361 """
Azim Khan4b543232017-06-30 09:35:21 +0100362 headers = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100363 for line in funcs_f:
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000364 if re.search(end_regex, line):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100365 break
366 headers += line
367 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100368 raise GeneratorInputError("file: %s - end pattern [%s] not found!" %
Azim Khanb31aa442018-07-03 11:57:54 +0100369 (funcs_f.name, end_regex))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100370
Azim Khan4b543232017-06-30 09:35:21 +0100371 return headers
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100372
373
Azim Khan8d686bf2018-07-04 23:29:46 +0100374def validate_dependency(dependency):
375 """
376 Validates a C macro and raises GeneratorInputError on invalid input.
377 :param dependency: Input macro dependency
378 :return: input dependency stripped of leading & trailing white spaces.
379 """
380 dependency = dependency.strip()
Ron Eldorb9b38132018-11-27 16:35:20 +0200381 if not re.match(CONDITION_REGEX, dependency, re.I):
Azim Khan8d686bf2018-07-04 23:29:46 +0100382 raise GeneratorInputError('Invalid dependency %s' % dependency)
383 return dependency
384
385
386def parse_dependencies(inp_str):
387 """
388 Parses dependencies out of inp_str, validates them and returns a
389 list of macros.
390
391 :param inp_str: Input string with macros delimited by ':'.
392 :return: list of dependencies
393 """
Gilles Peskine8b022352020-03-24 18:36:56 +0100394 dependencies = list(map(validate_dependency, inp_str.split(':')))
Azim Khan8d686bf2018-07-04 23:29:46 +0100395 return dependencies
396
397
Azim Khanb31aa442018-07-03 11:57:54 +0100398def parse_suite_dependencies(funcs_f):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100399 """
Azim Khane3b26af2018-06-29 02:36:57 +0100400 Parses test suite dependencies specified at the top of a
401 .function file, that starts with pattern BEGIN_DEPENDENCIES
402 and end with END_DEPENDENCIES. Dependencies are specified
403 after pattern 'depends_on:' and are delimited by ':'.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100404
Azim Khan8d686bf2018-07-04 23:29:46 +0100405 :param funcs_f: file object for .function file
Azim Khanf0e42fb2017-08-02 14:47:13 +0100406 :return: List of test suite dependencies.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100407 """
Azim Khanb31aa442018-07-03 11:57:54 +0100408 dependencies = []
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100409 for line in funcs_f:
Azim Khan8d686bf2018-07-04 23:29:46 +0100410 match = re.search(DEPENDENCY_REGEX, line.strip())
Azim Khanb31aa442018-07-03 11:57:54 +0100411 if match:
Azim Khan8d686bf2018-07-04 23:29:46 +0100412 try:
413 dependencies = parse_dependencies(match.group('dependencies'))
414 except GeneratorInputError as error:
415 raise GeneratorInputError(
416 str(error) + " - %s:%d" % (funcs_f.name, funcs_f.line_no))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100417 if re.search(END_DEP_REGEX, line):
418 break
419 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100420 raise GeneratorInputError("file: %s - end dependency pattern [%s]"
Azim Khanb31aa442018-07-03 11:57:54 +0100421 " not found!" % (funcs_f.name,
422 END_DEP_REGEX))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100423
Azim Khanb31aa442018-07-03 11:57:54 +0100424 return dependencies
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100425
426
Azim Khanb31aa442018-07-03 11:57:54 +0100427def parse_function_dependencies(line):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100428 """
Azim Khane3b26af2018-06-29 02:36:57 +0100429 Parses function dependencies, that are in the same line as
430 comment BEGIN_CASE. Dependencies are specified after pattern
431 'depends_on:' and are delimited by ':'.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100432
Azim Khan8d686bf2018-07-04 23:29:46 +0100433 :param line: Line from .function file that has dependencies.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100434 :return: List of dependencies.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100435 """
Azim Khanb31aa442018-07-03 11:57:54 +0100436 dependencies = []
437 match = re.search(BEGIN_CASE_REGEX, line)
Azim Khan8d686bf2018-07-04 23:29:46 +0100438 dep_str = match.group('depends_on')
Azim Khanb31aa442018-07-03 11:57:54 +0100439 if dep_str:
Azim Khan8d686bf2018-07-04 23:29:46 +0100440 match = re.search(DEPENDENCY_REGEX, dep_str)
Azim Khanb31aa442018-07-03 11:57:54 +0100441 if match:
Azim Khan8d686bf2018-07-04 23:29:46 +0100442 dependencies += parse_dependencies(match.group('dependencies'))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100443
Azim Khan8d686bf2018-07-04 23:29:46 +0100444 return dependencies
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100445
Azim Khan4084ec72018-07-05 14:20:08 +0100446
Azim Khanfcdf6852018-07-05 17:31:46 +0100447def parse_function_arguments(line):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100448 """
Azim Khane3b26af2018-06-29 02:36:57 +0100449 Parses test function signature for validation and generates
450 a dispatch wrapper function that translates input test vectors
451 read from the data file into test function arguments.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100452
Azim Khan8d686bf2018-07-04 23:29:46 +0100453 :param line: Line from .function file that has a function
Azim Khan040b6a22018-06-28 16:49:13 +0100454 signature.
Azim Khanfcdf6852018-07-05 17:31:46 +0100455 :return: argument list, local variables for
Azim Khan040b6a22018-06-28 16:49:13 +0100456 wrapper function and argument dispatch code.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100457 """
458 args = []
Gilles Peskine3a37f192022-12-04 14:00:32 +0100459 local_vars = []
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100460 args_dispatch = []
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100461 arg_idx = 0
Azim Khanfcdf6852018-07-05 17:31:46 +0100462 # Remove characters before arguments
463 line = line[line.find('(') + 1:]
Azim Khan8d686bf2018-07-04 23:29:46 +0100464 # Process arguments, ex: <type> arg1, <type> arg2 )
465 # This script assumes that the argument list is terminated by ')'
466 # i.e. the test functions will not have a function pointer
467 # argument.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100468 for arg in line[:line.find(')')].split(','):
469 arg = arg.strip()
470 if arg == '':
471 continue
Azim Khan8d686bf2018-07-04 23:29:46 +0100472 if re.search(INT_CHECK_REGEX, arg.strip()):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100473 args.append('int')
Gilles Peskine9ad7bd32022-12-04 13:10:55 +0100474 args_dispatch.append('((mbedtls_test_argument_t*)params[%d])->s32' % arg_idx)
Azim Khan8d686bf2018-07-04 23:29:46 +0100475 elif re.search(CHAR_CHECK_REGEX, arg.strip()):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100476 args.append('char*')
477 args_dispatch.append('(char *) params[%d]' % arg_idx)
Azim Khan8d686bf2018-07-04 23:29:46 +0100478 elif re.search(DATA_T_CHECK_REGEX, arg.strip()):
Azim Khana57a4202017-05-31 20:32:32 +0100479 args.append('hex')
Azim Khan2397bba2017-06-09 04:35:03 +0100480 # create a structure
Azim Khan040b6a22018-06-28 16:49:13 +0100481 pointer_initializer = '(uint8_t *) params[%d]' % arg_idx
Gilles Peskine9ad7bd32022-12-04 13:10:55 +0100482 len_initializer = '((mbedtls_test_argument_t*)params[%d])->len' % (arg_idx+1)
Gilles Peskine3a37f192022-12-04 14:00:32 +0100483 local_vars.append(' data_t data%d = {%s, %s};\n' %
484 (arg_idx, pointer_initializer, len_initializer))
Azim Khan5fcca462018-06-29 11:05:32 +0100485 args_dispatch.append('&data%d' % arg_idx)
Azim Khan2397bba2017-06-09 04:35:03 +0100486 arg_idx += 1
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100487 else:
Azim Khan040b6a22018-06-28 16:49:13 +0100488 raise ValueError("Test function arguments can only be 'int', "
Azim Khan5fcca462018-06-29 11:05:32 +0100489 "'char *' or 'data_t'\n%s" % line)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100490 arg_idx += 1
491
Gilles Peskine3a37f192022-12-04 14:00:32 +0100492 return args, ''.join(local_vars), args_dispatch
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100493
494
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +0100495def generate_function_code(name, code, local_vars, args_dispatch,
496 dependencies):
497 """
498 Generate function code with preprocessor checks and parameter dispatch
499 wrapper.
500
501 :param name: Function name
502 :param code: Function code
503 :param local_vars: Local variables for function wrapper
504 :param args_dispatch: Argument dispatch code
505 :param dependencies: Preprocessor dependencies list
506 :return: Final function code
507 """
508 # Add exit label if not present
509 if code.find('exit:') == -1:
510 split_code = code.rsplit('}', 1)
511 if len(split_code) == 2:
512 code = """exit:
513 ;
514}""".join(split_code)
515
516 code += gen_function_wrapper(name, local_vars, args_dispatch)
517 preprocessor_check_start, preprocessor_check_end = \
518 gen_dependencies(dependencies)
519 return preprocessor_check_start + code + preprocessor_check_end
520
Gilles Peskined3ad55e2022-11-11 16:37:16 +0100521COMMENT_START_REGEX = re.compile(r'/[*/]')
522
523def skip_comments(line, stream):
524 """Remove comments in line.
525
526 If the line contains an unfinished comment, read more lines from stream
527 until the line that contains the comment.
528
529 :return: The original line with inner comments replaced by spaces.
530 Trailing comments and whitespace may be removed completely.
531 """
532 pos = 0
533 while True:
534 opening = COMMENT_START_REGEX.search(line, pos)
535 if not opening:
536 break
537 if line[opening.start(0) + 1] == '/': # //...
538 continuation = line
Gilles Peskineaec4bec2022-11-30 16:38:49 +0100539 # Count the number of line breaks, to keep line numbers aligned
540 # in the output.
541 line_count = 1
Gilles Peskined3ad55e2022-11-11 16:37:16 +0100542 while continuation.endswith('\\\n'):
543 # This errors out if the file ends with an unfinished line
Gilles Peskine43febf22022-11-18 22:26:03 +0100544 # comment. That's acceptable to not complicate the code further.
Gilles Peskined3ad55e2022-11-11 16:37:16 +0100545 continuation = next(stream)
Gilles Peskineaec4bec2022-11-30 16:38:49 +0100546 line_count += 1
547 return line[:opening.start(0)].rstrip() + '\n' * line_count
Gilles Peskined3ad55e2022-11-11 16:37:16 +0100548 # Parsing /*...*/, looking for the end
549 closing = line.find('*/', opening.end(0))
550 while closing == -1:
551 # This errors out if the file ends with an unfinished block
Gilles Peskine43febf22022-11-18 22:26:03 +0100552 # comment. That's acceptable to not complicate the code further.
Gilles Peskined3ad55e2022-11-11 16:37:16 +0100553 line += next(stream)
554 closing = line.find('*/', opening.end(0))
555 pos = closing + 2
Gilles Peskine7e8d4b62022-11-18 22:27:37 +0100556 # Replace inner comment by spaces. There needs to be at least one space
557 # for things like 'int/*ihatespaces*/foo'. Go further and preserve the
Gilles Peskine07995fd2022-11-29 22:03:32 +0100558 # width of the comment and line breaks, this way positions in error
559 # messages remain correct.
Gilles Peskined3ad55e2022-11-11 16:37:16 +0100560 line = (line[:opening.start(0)] +
Gilles Peskine07995fd2022-11-29 22:03:32 +0100561 re.sub(r'.', r' ', line[opening.start(0):pos]) +
Gilles Peskined3ad55e2022-11-11 16:37:16 +0100562 line[pos:])
Gilles Peskine07995fd2022-11-29 22:03:32 +0100563 # Strip whitespace at the end of lines (it's irrelevant to error messages).
Gilles Peskined3ad55e2022-11-11 16:37:16 +0100564 return re.sub(r' +(\n|\Z)', r'\1', line)
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +0100565
Azim Khanb31aa442018-07-03 11:57:54 +0100566def parse_function_code(funcs_f, dependencies, suite_dependencies):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100567 """
Azim Khan040b6a22018-06-28 16:49:13 +0100568 Parses out a function from function file object and generates
569 function and dispatch code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100570
Azim Khanf0e42fb2017-08-02 14:47:13 +0100571 :param funcs_f: file object of the functions file.
Azim Khanb31aa442018-07-03 11:57:54 +0100572 :param dependencies: List of dependencies
573 :param suite_dependencies: List of test suite dependencies
Azim Khanf0e42fb2017-08-02 14:47:13 +0100574 :return: Function name, arguments, function code and dispatch code.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100575 """
Azim Khanfcdf6852018-07-05 17:31:46 +0100576 line_directive = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name)
577 code = ''
Azim Khan8d686bf2018-07-04 23:29:46 +0100578 has_exit_label = False
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100579 for line in funcs_f:
Azim Khanfcdf6852018-07-05 17:31:46 +0100580 # Check function signature. Function signature may be split
581 # across multiple lines. Here we try to find the start of
582 # arguments list, then remove '\n's and apply the regex to
583 # detect function start.
Gilles Peskined3ad55e2022-11-11 16:37:16 +0100584 line = skip_comments(line, funcs_f)
Azim Khanfcdf6852018-07-05 17:31:46 +0100585 up_to_arg_list_start = code + line[:line.find('(') + 1]
586 match = re.match(TEST_FUNCTION_VALIDATION_REGEX,
587 up_to_arg_list_start.replace('\n', ' '), re.I)
Azim Khanb31aa442018-07-03 11:57:54 +0100588 if match:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100589 # check if we have full signature i.e. split in more lines
Azim Khanfcdf6852018-07-05 17:31:46 +0100590 name = match.group('func_name')
Azim Khan8d686bf2018-07-04 23:29:46 +0100591 if not re.match(FUNCTION_ARG_LIST_END_REGEX, line):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100592 for lin in funcs_f:
Gilles Peskined3ad55e2022-11-11 16:37:16 +0100593 line += skip_comments(lin, funcs_f)
Azim Khan8d686bf2018-07-04 23:29:46 +0100594 if re.search(FUNCTION_ARG_LIST_END_REGEX, line):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100595 break
Azim Khanfcdf6852018-07-05 17:31:46 +0100596 args, local_vars, args_dispatch = parse_function_arguments(
Azim Khanb31aa442018-07-03 11:57:54 +0100597 line)
Azim Khan8d686bf2018-07-04 23:29:46 +0100598 code += line
Azim Khanfcdf6852018-07-05 17:31:46 +0100599 break
600 code += line
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100601 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100602 raise GeneratorInputError("file: %s - Test functions not found!" %
Azim Khanb31aa442018-07-03 11:57:54 +0100603 funcs_f.name)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100604
Azim Khanfcdf6852018-07-05 17:31:46 +0100605 # Prefix test function name with 'test_'
606 code = code.replace(name, 'test_' + name, 1)
607 name = 'test_' + name
608
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100609 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100610 if re.search(END_CASE_REGEX, line):
611 break
Azim Khan8d686bf2018-07-04 23:29:46 +0100612 if not has_exit_label:
613 has_exit_label = \
614 re.search(EXIT_LABEL_REGEX, line.strip()) is not None
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100615 code += line
616 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100617 raise GeneratorInputError("file: %s - end case pattern [%s] not "
Azim Khanb31aa442018-07-03 11:57:54 +0100618 "found!" % (funcs_f.name, END_CASE_REGEX))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100619
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +0100620 code = line_directive + code
621 code = generate_function_code(name, code, local_vars, args_dispatch,
622 dependencies)
Azim Khanb31aa442018-07-03 11:57:54 +0100623 dispatch_code = gen_dispatch(name, suite_dependencies + dependencies)
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +0100624 return (name, args, code, dispatch_code)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100625
626
627def parse_functions(funcs_f):
628 """
Azim Khane3b26af2018-06-29 02:36:57 +0100629 Parses a test_suite_xxx.function file and returns information
630 for generating a C source file for the test suite.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100631
Azim Khanf0e42fb2017-08-02 14:47:13 +0100632 :param funcs_f: file object of the functions file.
Azim Khan040b6a22018-06-28 16:49:13 +0100633 :return: List of test suite dependencies, test function dispatch
634 code, function code and a dict with function identifiers
635 and arguments info.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100636 """
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000637 suite_helpers = ''
Azim Khanb31aa442018-07-03 11:57:54 +0100638 suite_dependencies = []
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100639 suite_functions = ''
640 func_info = {}
641 function_idx = 0
642 dispatch_code = ''
643 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100644 if re.search(BEGIN_HEADER_REGEX, line):
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +0100645 suite_helpers += parse_until_pattern(funcs_f, END_HEADER_REGEX)
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000646 elif re.search(BEGIN_SUITE_HELPERS_REGEX, line):
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +0100647 suite_helpers += parse_until_pattern(funcs_f,
648 END_SUITE_HELPERS_REGEX)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100649 elif re.search(BEGIN_DEP_REGEX, line):
Azim Khanb31aa442018-07-03 11:57:54 +0100650 suite_dependencies += parse_suite_dependencies(funcs_f)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100651 elif re.search(BEGIN_CASE_REGEX, line):
Azim Khan8d686bf2018-07-04 23:29:46 +0100652 try:
653 dependencies = parse_function_dependencies(line)
654 except GeneratorInputError as error:
655 raise GeneratorInputError(
656 "%s:%d: %s" % (funcs_f.name, funcs_f.line_no,
657 str(error)))
Azim Khan040b6a22018-06-28 16:49:13 +0100658 func_name, args, func_code, func_dispatch =\
Azim Khanb31aa442018-07-03 11:57:54 +0100659 parse_function_code(funcs_f, dependencies, suite_dependencies)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100660 suite_functions += func_code
661 # Generate dispatch code and enumeration info
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100662 if func_name in func_info:
663 raise GeneratorInputError(
Azim Khanb31aa442018-07-03 11:57:54 +0100664 "file: %s - function %s re-declared at line %d" %
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100665 (funcs_f.name, func_name, funcs_f.line_no))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100666 func_info[func_name] = (function_idx, args)
667 dispatch_code += '/* Function Id: %d */\n' % function_idx
668 dispatch_code += func_dispatch
669 function_idx += 1
670
Azim Khanb31aa442018-07-03 11:57:54 +0100671 func_code = (suite_helpers +
672 suite_functions).join(gen_dependencies(suite_dependencies))
673 return suite_dependencies, dispatch_code, func_code, func_info
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100674
675
Azim Khanb31aa442018-07-03 11:57:54 +0100676def escaped_split(inp_str, split_char):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100677 """
Azim Khanb31aa442018-07-03 11:57:54 +0100678 Split inp_str on character split_char but ignore if escaped.
Azim Khan040b6a22018-06-28 16:49:13 +0100679 Since, return value is used to write back to the intermediate
680 data file, any escape characters in the input are retained in the
681 output.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100682
Azim Khanb31aa442018-07-03 11:57:54 +0100683 :param inp_str: String to split
Azim Khan8d686bf2018-07-04 23:29:46 +0100684 :param split_char: Split character
Azim Khanf0e42fb2017-08-02 14:47:13 +0100685 :return: List of splits
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100686 """
Azim Khanb31aa442018-07-03 11:57:54 +0100687 if len(split_char) > 1:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100688 raise ValueError('Expected split character. Found string!')
Azim Khan63028132018-07-05 17:53:11 +0100689 out = re.sub(r'(\\.)|' + split_char,
690 lambda m: m.group(1) or '\n', inp_str,
691 len(inp_str)).split('\n')
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +0100692 out = [x for x in out if x]
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100693 return out
694
695
Azim Khanb31aa442018-07-03 11:57:54 +0100696def parse_test_data(data_f):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100697 """
Azim Khane3b26af2018-06-29 02:36:57 +0100698 Parses .data file for each test case name, test function name,
699 test dependencies and test arguments. This information is
700 correlated with the test functions file for generating an
701 intermediate data file replacing the strings for test function
702 names, dependencies and integer constant expressions with
703 identifiers. Mainly for optimising space for on-target
704 execution.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100705
Azim Khanf0e42fb2017-08-02 14:47:13 +0100706 :param data_f: file object of the data file.
Gilles Peskinef122aed2022-12-03 22:58:52 +0100707 :return: Generator that yields line number, test name, function name,
Azim Khan040b6a22018-06-28 16:49:13 +0100708 dependency list and function argument list.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100709 """
Azim Khanb31aa442018-07-03 11:57:54 +0100710 __state_read_name = 0
711 __state_read_args = 1
712 state = __state_read_name
713 dependencies = []
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100714 name = ''
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100715 for line in data_f:
716 line = line.strip()
Azim Khan8d686bf2018-07-04 23:29:46 +0100717 # Skip comments
718 if line.startswith('#'):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100719 continue
720
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100721 # Blank line indicates end of test
Azim Khanb31aa442018-07-03 11:57:54 +0100722 if not line:
723 if state == __state_read_args:
Azim Khan040b6a22018-06-28 16:49:13 +0100724 raise GeneratorInputError("[%s:%d] Newline before arguments. "
725 "Test function and arguments "
726 "missing for %s" %
727 (data_f.name, data_f.line_no, name))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100728 continue
729
Azim Khanb31aa442018-07-03 11:57:54 +0100730 if state == __state_read_name:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100731 # Read test name
732 name = line
Azim Khanb31aa442018-07-03 11:57:54 +0100733 state = __state_read_args
734 elif state == __state_read_args:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100735 # Check dependencies
Azim Khan8d686bf2018-07-04 23:29:46 +0100736 match = re.search(DEPENDENCY_REGEX, line)
Azim Khanb31aa442018-07-03 11:57:54 +0100737 if match:
Azim Khan8d686bf2018-07-04 23:29:46 +0100738 try:
739 dependencies = parse_dependencies(
740 match.group('dependencies'))
741 except GeneratorInputError as error:
742 raise GeneratorInputError(
743 str(error) + " - %s:%d" %
744 (data_f.name, data_f.line_no))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100745 else:
746 # Read test vectors
747 parts = escaped_split(line, ':')
Azim Khanb31aa442018-07-03 11:57:54 +0100748 test_function = parts[0]
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100749 args = parts[1:]
Gilles Peskinef122aed2022-12-03 22:58:52 +0100750 yield data_f.line_no, name, test_function, dependencies, args
Azim Khanb31aa442018-07-03 11:57:54 +0100751 dependencies = []
752 state = __state_read_name
753 if state == __state_read_args:
Azim Khan040b6a22018-06-28 16:49:13 +0100754 raise GeneratorInputError("[%s:%d] Newline before arguments. "
755 "Test function and arguments missing for "
756 "%s" % (data_f.name, data_f.line_no, name))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100757
758
759def gen_dep_check(dep_id, dep):
760 """
Azim Khane3b26af2018-06-29 02:36:57 +0100761 Generate code for checking dependency with the associated
762 identifier.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100763
Azim Khanf0e42fb2017-08-02 14:47:13 +0100764 :param dep_id: Dependency identifier
765 :param dep: Dependency macro
766 :return: Dependency check code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100767 """
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100768 if dep_id < 0:
Azim Khan040b6a22018-06-28 16:49:13 +0100769 raise GeneratorInputError("Dependency Id should be a positive "
770 "integer.")
Azim Khanb31aa442018-07-03 11:57:54 +0100771 _not, dep = ('!', dep[1:]) if dep[0] == '!' else ('', dep)
772 if not dep:
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100773 raise GeneratorInputError("Dependency should not be an empty string.")
Ron Eldorb9b38132018-11-27 16:35:20 +0200774
775 dependency = re.match(CONDITION_REGEX, dep, re.I)
776 if not dependency:
777 raise GeneratorInputError('Invalid dependency %s' % dep)
778
779 _defined = '' if dependency.group(2) else 'defined'
780 _cond = dependency.group(2) if dependency.group(2) else ''
781 _value = dependency.group(3) if dependency.group(3) else ''
782
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100783 dep_check = '''
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100784 case {id}:
785 {{
Ron Eldorb9b38132018-11-27 16:35:20 +0200786#if {_not}{_defined}({macro}{_cond}{_value})
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100787 ret = DEPENDENCY_SUPPORTED;
Azim Khand61b8372017-07-10 11:54:01 +0100788#else
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100789 ret = DEPENDENCY_NOT_SUPPORTED;
Azim Khand61b8372017-07-10 11:54:01 +0100790#endif
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100791 }}
Ron Eldorb9b38132018-11-27 16:35:20 +0200792 break;'''.format(_not=_not, _defined=_defined,
793 macro=dependency.group(1), id=dep_id,
794 _cond=_cond, _value=_value)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100795 return dep_check
796
797
798def gen_expression_check(exp_id, exp):
799 """
Azim Khane3b26af2018-06-29 02:36:57 +0100800 Generates code for evaluating an integer expression using
801 associated expression Id.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100802
Azim Khanf0e42fb2017-08-02 14:47:13 +0100803 :param exp_id: Expression Identifier
804 :param exp: Expression/Macro
805 :return: Expression check code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100806 """
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100807 if exp_id < 0:
Azim Khan040b6a22018-06-28 16:49:13 +0100808 raise GeneratorInputError("Expression Id should be a positive "
809 "integer.")
Azim Khanb31aa442018-07-03 11:57:54 +0100810 if not exp:
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100811 raise GeneratorInputError("Expression should not be an empty string.")
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100812 exp_code = '''
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100813 case {exp_id}:
814 {{
815 *out_value = {expression};
816 }}
817 break;'''.format(exp_id=exp_id, expression=exp)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100818 return exp_code
819
820
Azim Khanb31aa442018-07-03 11:57:54 +0100821def write_dependencies(out_data_f, test_dependencies, unique_dependencies):
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100822 """
Azim Khane3b26af2018-06-29 02:36:57 +0100823 Write dependencies to intermediate test data file, replacing
824 the string form with identifiers. Also, generates dependency
825 check code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100826
Azim Khanf0e42fb2017-08-02 14:47:13 +0100827 :param out_data_f: Output intermediate data file
Azim Khanb31aa442018-07-03 11:57:54 +0100828 :param test_dependencies: Dependencies
829 :param unique_dependencies: Mutable list to track unique dependencies
Azim Khan040b6a22018-06-28 16:49:13 +0100830 that are global to this re-entrant function.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100831 :return: returns dependency check code.
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100832 """
Azim Khan599cd242017-07-06 17:34:27 +0100833 dep_check_code = ''
Azim Khanb31aa442018-07-03 11:57:54 +0100834 if test_dependencies:
Azim Khan599cd242017-07-06 17:34:27 +0100835 out_data_f.write('depends_on')
Azim Khanb31aa442018-07-03 11:57:54 +0100836 for dep in test_dependencies:
837 if dep not in unique_dependencies:
838 unique_dependencies.append(dep)
839 dep_id = unique_dependencies.index(dep)
Azim Khan599cd242017-07-06 17:34:27 +0100840 dep_check_code += gen_dep_check(dep_id, dep)
841 else:
Azim Khanb31aa442018-07-03 11:57:54 +0100842 dep_id = unique_dependencies.index(dep)
Azim Khan599cd242017-07-06 17:34:27 +0100843 out_data_f.write(':' + str(dep_id))
844 out_data_f.write('\n')
845 return dep_check_code
846
847
Gilles Peskinea2990432022-12-04 00:28:56 +0100848INT_VAL_REGEX = re.compile(r'-?(\d+|0x[0-9a-f]+)$', re.I)
849def val_is_int(val: str) -> bool:
850 """Whether val is suitable as an 'int' parameter in the .datax file."""
851 if not INT_VAL_REGEX.match(val):
852 return False
853 # Limit the range to what is guaranteed to get through strtol()
854 return abs(int(val, 0)) <= 0x7fffffff
855
Azim Khan599cd242017-07-06 17:34:27 +0100856def write_parameters(out_data_f, test_args, func_args, unique_expressions):
857 """
Azim Khane3b26af2018-06-29 02:36:57 +0100858 Writes test parameters to the intermediate data file, replacing
859 the string form with identifiers. Also, generates expression
860 check code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100861
Azim Khanf0e42fb2017-08-02 14:47:13 +0100862 :param out_data_f: Output intermediate data file
863 :param test_args: Test parameters
864 :param func_args: Function arguments
Azim Khan040b6a22018-06-28 16:49:13 +0100865 :param unique_expressions: Mutable list to track unique
866 expressions that are global to this re-entrant function.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100867 :return: Returns expression check code.
Azim Khan599cd242017-07-06 17:34:27 +0100868 """
869 expression_code = ''
Azim Khanb31aa442018-07-03 11:57:54 +0100870 for i, _ in enumerate(test_args):
Azim Khan599cd242017-07-06 17:34:27 +0100871 typ = func_args[i]
872 val = test_args[i]
873
Gilles Peskinea2990432022-12-04 00:28:56 +0100874 # Pass small integer constants literally. This reduces the size of
875 # the C code. Register anything else as an expression.
876 if typ == 'int' and not val_is_int(val):
Azim Khan599cd242017-07-06 17:34:27 +0100877 typ = 'exp'
878 if val not in unique_expressions:
879 unique_expressions.append(val)
Azim Khan040b6a22018-06-28 16:49:13 +0100880 # exp_id can be derived from len(). But for
881 # readability and consistency with case of existing
882 # let's use index().
Azim Khan599cd242017-07-06 17:34:27 +0100883 exp_id = unique_expressions.index(val)
884 expression_code += gen_expression_check(exp_id, val)
885 val = exp_id
886 else:
887 val = unique_expressions.index(val)
888 out_data_f.write(':' + typ + ':' + str(val))
889 out_data_f.write('\n')
890 return expression_code
891
892
Azim Khanb31aa442018-07-03 11:57:54 +0100893def gen_suite_dep_checks(suite_dependencies, dep_check_code, expression_code):
Azim Khan599cd242017-07-06 17:34:27 +0100894 """
Azim Khane3b26af2018-06-29 02:36:57 +0100895 Generates preprocessor checks for test suite dependencies.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100896
Azim Khanb31aa442018-07-03 11:57:54 +0100897 :param suite_dependencies: Test suite dependencies read from the
Azim Khan8d686bf2018-07-04 23:29:46 +0100898 .function file.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100899 :param dep_check_code: Dependency check code
900 :param expression_code: Expression check code
Azim Khan040b6a22018-06-28 16:49:13 +0100901 :return: Dependency and expression code guarded by test suite
902 dependencies.
Azim Khan599cd242017-07-06 17:34:27 +0100903 """
Azim Khanb31aa442018-07-03 11:57:54 +0100904 if suite_dependencies:
905 preprocessor_check = gen_dependencies_one_line(suite_dependencies)
Azim Khan599cd242017-07-06 17:34:27 +0100906 dep_check_code = '''
Azim Khanb31aa442018-07-03 11:57:54 +0100907{preprocessor_check}
Azim Khan599cd242017-07-06 17:34:27 +0100908{code}
Azim Khan599cd242017-07-06 17:34:27 +0100909#endif
Azim Khanb31aa442018-07-03 11:57:54 +0100910'''.format(preprocessor_check=preprocessor_check, code=dep_check_code)
Azim Khan599cd242017-07-06 17:34:27 +0100911 expression_code = '''
Azim Khanb31aa442018-07-03 11:57:54 +0100912{preprocessor_check}
Azim Khan599cd242017-07-06 17:34:27 +0100913{code}
Azim Khan599cd242017-07-06 17:34:27 +0100914#endif
Azim Khanb31aa442018-07-03 11:57:54 +0100915'''.format(preprocessor_check=preprocessor_check, code=expression_code)
Azim Khan599cd242017-07-06 17:34:27 +0100916 return dep_check_code, expression_code
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100917
918
Gilles Peskineab56a692022-12-04 17:27:25 +0100919def get_function_info(func_info, function_name, line_no):
920 """Look up information about a test function by name.
921
922 Raise an informative expression if function_name is not found.
923
924 :param func_info: dictionary mapping function names to their information.
925 :param function_name: the function name as written in the .function and
926 .data files.
927 :param line_no: line number for error messages.
928 :return Function information (id, args).
929 """
930 test_function_name = 'test_' + function_name
931 if test_function_name not in func_info:
932 raise GeneratorInputError("%d: Function %s not found!" %
933 (line_no, test_function_name))
934 return func_info[test_function_name]
935
936
Azim Khanb31aa442018-07-03 11:57:54 +0100937def gen_from_test_data(data_f, out_data_f, func_info, suite_dependencies):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100938 """
Azim Khane3b26af2018-06-29 02:36:57 +0100939 This function reads test case name, dependencies and test vectors
940 from the .data file. This information is correlated with the test
941 functions file for generating an intermediate data file replacing
942 the strings for test function names, dependencies and integer
943 constant expressions with identifiers. Mainly for optimising
944 space for on-target execution.
945 It also generates test case dependency check code and expression
946 evaluation code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100947
Azim Khanf0e42fb2017-08-02 14:47:13 +0100948 :param data_f: Data file object
Azim Khan8d686bf2018-07-04 23:29:46 +0100949 :param out_data_f: Output intermediate data file
Azim Khan040b6a22018-06-28 16:49:13 +0100950 :param func_info: Dict keyed by function and with function id
951 and arguments info
Azim Khanb31aa442018-07-03 11:57:54 +0100952 :param suite_dependencies: Test suite dependencies
Azim Khanf0e42fb2017-08-02 14:47:13 +0100953 :return: Returns dependency and expression check code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100954 """
Azim Khanb31aa442018-07-03 11:57:54 +0100955 unique_dependencies = []
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100956 unique_expressions = []
957 dep_check_code = ''
958 expression_code = ''
Gilles Peskinef122aed2022-12-03 22:58:52 +0100959 for line_no, test_name, function_name, test_dependencies, test_args in \
Azim Khanb31aa442018-07-03 11:57:54 +0100960 parse_test_data(data_f):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100961 out_data_f.write(test_name + '\n')
962
Azim Khanb31aa442018-07-03 11:57:54 +0100963 # Write dependencies
964 dep_check_code += write_dependencies(out_data_f, test_dependencies,
965 unique_dependencies)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100966
Azim Khan599cd242017-07-06 17:34:27 +0100967 # Write test function name
Gilles Peskineab56a692022-12-04 17:27:25 +0100968 func_id, func_args = \
969 get_function_info(func_info, function_name, line_no)
Azim Khan599cd242017-07-06 17:34:27 +0100970 out_data_f.write(str(func_id))
971
972 # Write parameters
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100973 if len(test_args) != len(func_args):
Gilles Peskinef122aed2022-12-03 22:58:52 +0100974 raise GeneratorInputError("%d: Invalid number of arguments in test "
Azim Khanb31aa442018-07-03 11:57:54 +0100975 "%s. See function %s signature." %
Gilles Peskinef122aed2022-12-03 22:58:52 +0100976 (line_no, test_name, function_name))
Azim Khan040b6a22018-06-28 16:49:13 +0100977 expression_code += write_parameters(out_data_f, test_args, func_args,
978 unique_expressions)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100979
Azim Khan599cd242017-07-06 17:34:27 +0100980 # Write a newline as test case separator
981 out_data_f.write('\n')
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100982
Azim Khanb31aa442018-07-03 11:57:54 +0100983 dep_check_code, expression_code = gen_suite_dep_checks(
984 suite_dependencies, dep_check_code, expression_code)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100985 return dep_check_code, expression_code
986
987
Azim Khanb31aa442018-07-03 11:57:54 +0100988def add_input_info(funcs_file, data_file, template_file,
989 c_file, snippets):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100990 """
Azim Khanb31aa442018-07-03 11:57:54 +0100991 Add generator input info in snippets.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100992
Azim Khanf0e42fb2017-08-02 14:47:13 +0100993 :param funcs_file: Functions file object
994 :param data_file: Data file object
995 :param template_file: Template file object
Azim Khanf0e42fb2017-08-02 14:47:13 +0100996 :param c_file: Output C file object
Azim Khanb31aa442018-07-03 11:57:54 +0100997 :param snippets: Dictionary to contain code pieces to be
998 substituted in the template.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100999 :return:
1000 """
Azim Khanb31aa442018-07-03 11:57:54 +01001001 snippets['test_file'] = c_file
1002 snippets['test_main_file'] = template_file
1003 snippets['test_case_file'] = funcs_file
1004 snippets['test_case_data_file'] = data_file
1005
1006
1007def read_code_from_input_files(platform_file, helpers_file,
1008 out_data_file, snippets):
1009 """
1010 Read code from input files and create substitutions for replacement
1011 strings in the template file.
1012
1013 :param platform_file: Platform file object
1014 :param helpers_file: Helper functions file object
1015 :param out_data_file: Output intermediate data file object
1016 :param snippets: Dictionary to contain code pieces to be
1017 substituted in the template.
1018 :return:
1019 """
1020 # Read helpers
1021 with open(helpers_file, 'r') as help_f, open(platform_file, 'r') as \
1022 platform_f:
1023 snippets['test_common_helper_file'] = helpers_file
1024 snippets['test_common_helpers'] = help_f.read()
1025 snippets['test_platform_file'] = platform_file
1026 snippets['platform_code'] = platform_f.read().replace(
1027 'DATA_FILE', out_data_file.replace('\\', '\\\\')) # escape '\'
1028
1029
1030def write_test_source_file(template_file, c_file, snippets):
1031 """
1032 Write output source file with generated source code.
1033
1034 :param template_file: Template file name
1035 :param c_file: Output source file
1036 :param snippets: Generated and code snippets
1037 :return:
1038 """
David Horstmann14bae832022-11-03 17:49:29 +00001039
1040 # Create a placeholder pattern with the correct named capture groups
1041 # to override the default provided with Template.
1042 # Match nothing (no way of escaping placeholders).
1043 escaped = "(?P<escaped>(?!))"
1044 # Match the "__MBEDTLS_TEST_TEMPLATE__PLACEHOLDER_NAME" pattern.
1045 named = "__MBEDTLS_TEST_TEMPLATE__(?P<named>[A-Z][_A-Z0-9]*)"
1046 # Match nothing (no braced placeholder syntax).
1047 braced = "(?P<braced>(?!))"
1048 # If not already matched, a "__MBEDTLS_TEST_TEMPLATE__" prefix is invalid.
1049 invalid = "(?P<invalid>__MBEDTLS_TEST_TEMPLATE__)"
David Horstmann8eff06f2022-11-09 17:27:33 +00001050 placeholder_pattern = re.compile("|".join([escaped, named, braced, invalid]))
David Horstmann14bae832022-11-03 17:49:29 +00001051
Azim Khanb31aa442018-07-03 11:57:54 +01001052 with open(template_file, 'r') as template_f, open(c_file, 'w') as c_f:
Mohammad Azim Khand2d01122018-07-18 17:48:37 +01001053 for line_no, line in enumerate(template_f.readlines(), 1):
Azim Khanb31aa442018-07-03 11:57:54 +01001054 # Update line number. +1 as #line directive sets next line number
1055 snippets['line_no'] = line_no + 1
David Horstmann14bae832022-11-03 17:49:29 +00001056 template = string.Template(line)
1057 template.pattern = placeholder_pattern
1058 snippets = {k.upper():v for (k, v) in snippets.items()}
1059 code = template.substitute(**snippets)
Azim Khanb31aa442018-07-03 11:57:54 +01001060 c_f.write(code)
Azim Khanb31aa442018-07-03 11:57:54 +01001061
1062
1063def parse_function_file(funcs_file, snippets):
1064 """
1065 Parse function file and generate function dispatch code.
1066
1067 :param funcs_file: Functions file name
1068 :param snippets: Dictionary to contain code pieces to be
1069 substituted in the template.
1070 :return:
1071 """
1072 with FileWrapper(funcs_file) as funcs_f:
1073 suite_dependencies, dispatch_code, func_code, func_info = \
1074 parse_functions(funcs_f)
1075 snippets['functions_code'] = func_code
1076 snippets['dispatch_code'] = dispatch_code
1077 return suite_dependencies, func_info
1078
1079
1080def generate_intermediate_data_file(data_file, out_data_file,
1081 suite_dependencies, func_info, snippets):
1082 """
1083 Generates intermediate data file from input data file and
1084 information read from functions file.
1085
1086 :param data_file: Data file name
1087 :param out_data_file: Output/Intermediate data file
1088 :param suite_dependencies: List of suite dependencies.
1089 :param func_info: Function info parsed from functions file.
1090 :param snippets: Dictionary to contain code pieces to be
1091 substituted in the template.
1092 :return:
1093 """
1094 with FileWrapper(data_file) as data_f, \
1095 open(out_data_file, 'w') as out_data_f:
1096 dep_check_code, expression_code = gen_from_test_data(
1097 data_f, out_data_f, func_info, suite_dependencies)
1098 snippets['dep_check_code'] = dep_check_code
1099 snippets['expression_code'] = expression_code
1100
1101
1102def generate_code(**input_info):
1103 """
1104 Generates C source code from test suite file, data file, common
1105 helpers file and platform file.
1106
1107 input_info expands to following parameters:
1108 funcs_file: Functions file object
1109 data_file: Data file object
1110 template_file: Template file object
1111 platform_file: Platform file object
1112 helpers_file: Helper functions file object
1113 suites_dir: Test suites dir
1114 c_file: Output C file object
1115 out_data_file: Output intermediate data file object
1116 :return:
1117 """
1118 funcs_file = input_info['funcs_file']
1119 data_file = input_info['data_file']
1120 template_file = input_info['template_file']
1121 platform_file = input_info['platform_file']
1122 helpers_file = input_info['helpers_file']
1123 suites_dir = input_info['suites_dir']
1124 c_file = input_info['c_file']
1125 out_data_file = input_info['out_data_file']
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001126 for name, path in [('Functions file', funcs_file),
1127 ('Data file', data_file),
1128 ('Template file', template_file),
1129 ('Platform file', platform_file),
Azim Khane3b26af2018-06-29 02:36:57 +01001130 ('Helpers code file', helpers_file),
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001131 ('Suites dir', suites_dir)]:
1132 if not os.path.exists(path):
1133 raise IOError("ERROR: %s [%s] not found!" % (name, path))
1134
Azim Khanb31aa442018-07-03 11:57:54 +01001135 snippets = {'generator_script': os.path.basename(__file__)}
1136 read_code_from_input_files(platform_file, helpers_file,
1137 out_data_file, snippets)
1138 add_input_info(funcs_file, data_file, template_file,
1139 c_file, snippets)
1140 suite_dependencies, func_info = parse_function_file(funcs_file, snippets)
1141 generate_intermediate_data_file(data_file, out_data_file,
1142 suite_dependencies, func_info, snippets)
1143 write_test_source_file(template_file, c_file, snippets)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001144
1145
Azim Khan8d686bf2018-07-04 23:29:46 +01001146def main():
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001147 """
1148 Command line parser.
1149
1150 :return:
1151 """
Azim Khan040b6a22018-06-28 16:49:13 +01001152 parser = argparse.ArgumentParser(
Azim Khane3b26af2018-06-29 02:36:57 +01001153 description='Dynamically generate test suite code.')
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001154
1155 parser.add_argument("-f", "--functions-file",
1156 dest="funcs_file",
1157 help="Functions file",
Azim Khane3b26af2018-06-29 02:36:57 +01001158 metavar="FUNCTIONS_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001159 required=True)
1160
1161 parser.add_argument("-d", "--data-file",
1162 dest="data_file",
1163 help="Data file",
Azim Khane3b26af2018-06-29 02:36:57 +01001164 metavar="DATA_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001165 required=True)
1166
1167 parser.add_argument("-t", "--template-file",
1168 dest="template_file",
1169 help="Template file",
Azim Khane3b26af2018-06-29 02:36:57 +01001170 metavar="TEMPLATE_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001171 required=True)
1172
1173 parser.add_argument("-s", "--suites-dir",
1174 dest="suites_dir",
1175 help="Suites dir",
Azim Khane3b26af2018-06-29 02:36:57 +01001176 metavar="SUITES_DIR",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001177 required=True)
1178
Azim Khane3b26af2018-06-29 02:36:57 +01001179 parser.add_argument("--helpers-file",
1180 dest="helpers_file",
1181 help="Helpers file",
1182 metavar="HELPERS_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001183 required=True)
1184
1185 parser.add_argument("-p", "--platform-file",
1186 dest="platform_file",
1187 help="Platform code file",
1188 metavar="PLATFORM_FILE",
1189 required=True)
1190
1191 parser.add_argument("-o", "--out-dir",
1192 dest="out_dir",
1193 help="Dir where generated code and scripts are copied",
1194 metavar="OUT_DIR",
1195 required=True)
1196
1197 args = parser.parse_args()
1198
1199 data_file_name = os.path.basename(args.data_file)
1200 data_name = os.path.splitext(data_file_name)[0]
1201
1202 out_c_file = os.path.join(args.out_dir, data_name + '.c')
Mohammad Azim Khan00c4b092018-06-28 13:10:19 +01001203 out_data_file = os.path.join(args.out_dir, data_name + '.datax')
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001204
1205 out_c_file_dir = os.path.dirname(out_c_file)
1206 out_data_file_dir = os.path.dirname(out_data_file)
Azim Khanb31aa442018-07-03 11:57:54 +01001207 for directory in [out_c_file_dir, out_data_file_dir]:
1208 if not os.path.exists(directory):
1209 os.makedirs(directory)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001210
Azim Khanb31aa442018-07-03 11:57:54 +01001211 generate_code(funcs_file=args.funcs_file, data_file=args.data_file,
1212 template_file=args.template_file,
1213 platform_file=args.platform_file,
1214 helpers_file=args.helpers_file, suites_dir=args.suites_dir,
1215 c_file=out_c_file, out_data_file=out_data_file)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001216
1217
1218if __name__ == "__main__":
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +01001219 try:
Azim Khan8d686bf2018-07-04 23:29:46 +01001220 main()
Azim Khanb31aa442018-07-03 11:57:54 +01001221 except GeneratorInputError as err:
Mohammad Azim Khan440d8732018-07-18 12:50:49 +01001222 sys.exit("%s: input error: %s" %
1223 (os.path.basename(sys.argv[0]), str(err)))