From fc62211e3b7ab294d767cb796b14991280f374ab Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 11 Dec 2020 00:27:14 +0100 Subject: [PATCH] Refactor and generalize run_c Generalize the very ad hoc run_c function into a function to generate a C program to print the value of a list of expressions. Refactor the code into several functions to make it more manageable. No intended behavior change. Signed-off-by: Gilles Peskine --- tests/scripts/test_psa_constant_names.py | 133 ++++++++++++++++++----- 1 file changed, 104 insertions(+), 29 deletions(-) diff --git a/tests/scripts/test_psa_constant_names.py b/tests/scripts/test_psa_constant_names.py index c7011a7845..68bfe77be9 100755 --- a/tests/scripts/test_psa_constant_names.py +++ b/tests/scripts/test_psa_constant_names.py @@ -313,40 +313,97 @@ def remove_file_if_exists(filename): except OSError: pass -def run_c(type_word, expressions, include_path=None, keep_c=False): - """Generate and run a program to print out numerical values for expressions.""" - if include_path is None: - include_path = [] - if type_word == 'status': - cast_to = 'long' - printf_format = '%ld' - else: - cast_to = 'unsigned long' - printf_format = '0x%08lx' - c_name = None - exe_name = None - try: - c_fd, c_name = tempfile.mkstemp(prefix='tmp-{}-'.format(type_word), - suffix='.c', - dir='programs/psa') - exe_suffix = '.exe' if platform.system() == 'Windows' else '' - exe_name = c_name[:-2] + exe_suffix - remove_file_if_exists(exe_name) - c_file = os.fdopen(c_fd, 'w', encoding='ascii') - c_file.write('/* Generated by test_psa_constant_names.py for {} values */' - .format(type_word)) - c_file.write(''' +def create_c_file(file_label): + """Create a temporary C file. + + * ``file_label``: a string that will be included in the file name. + + Return ```(c_file, c_name, exe_name)``` where ``c_file`` is a Python + stream open for writing to the file, ``c_name`` is the name of the file + and ``exe_name`` is the name of the executable that will be produced + by compiling the file. + """ + c_fd, c_name = tempfile.mkstemp(prefix='tmp-{}-'.format(file_label), + suffix='.c') + exe_suffix = '.exe' if platform.system() == 'Windows' else '' + exe_name = c_name[:-2] + exe_suffix + remove_file_if_exists(exe_name) + c_file = os.fdopen(c_fd, 'w', encoding='ascii') + return c_file, c_name, exe_name + +def generate_c_printf_expressions(c_file, cast_to, printf_format, expressions): + """Generate C instructions to print the value of ``expressions``. + + Write the code with ``c_file``'s ``write`` method. + + Each expression is cast to the type ``cast_to`` and printed with the + printf format ``printf_format``. + """ + for expr in expressions: + c_file.write(' printf("{}\\n", ({}) {});\n' + .format(printf_format, cast_to, expr)) + +def generate_c_file(c_file, + caller, header, + main_generator): + """Generate a temporary C source file. + + * ``c_file`` is an open stream on the C source file. + * ``caller``: an informational string written in a comment at the top + of the file. + * ``header``: extra code to insert before any function in the generated + C file. + * ``main_generator``: a function called with ``c_file`` as its sole argument + to generate the body of the ``main()`` function. + """ + c_file.write('/* Generated by {} */' + .format(caller)) + c_file.write(''' #include -#include +''') + c_file.write(header) + c_file.write(''' int main(void) { ''') - for expr in expressions: - c_file.write(' printf("{}\\n", ({}) {});\n' - .format(printf_format, cast_to, expr)) - c_file.write(''' return 0; + main_generator(c_file) + c_file.write(''' return 0; } ''') + +def get_c_expression_values( + cast_to, printf_format, + expressions, + caller=__name__, file_label='', + header='', include_path=None, + keep_c=False, +): # pylint: disable=too-many-arguments + """Generate and run a program to print out numerical values for expressions. + + * ``cast_to``: a C type. + * ``printf_format``: a printf format suitable for the type ``cast_to``. + * ``header``: extra code to insert before any function in the generated + C file. + * ``expressions``: a list of C language expressions that have the type + ``type``. + * ``include_path``: a list of directories containing header files. + * ``keep_c``: if true, keep the temporary C file (presumably for debugging + purposes). + + Return the list of values of the ``expressions``. + """ + if include_path is None: + include_path = [] + c_name = None + exe_name = None + try: + c_file, c_name, exe_name = create_c_file(file_label) + generate_c_file( + c_file, caller, header, + lambda c_file: generate_c_printf_expressions(c_file, + cast_to, printf_format, + expressions) + ) c_file.close() cc = os.getenv('CC', 'cc') subprocess.check_call([cc] + @@ -354,7 +411,7 @@ int main(void) ['-o', exe_name, c_name]) if keep_c: sys.stderr.write('List of {} tests kept at {}\n' - .format(type_word, c_name)) + .format(caller, c_name)) else: os.remove(c_name) output = subprocess.check_output([exe_name]) @@ -362,6 +419,24 @@ int main(void) finally: remove_file_if_exists(exe_name) +def run_c(type_word, expressions, include_path=None, keep_c=False): + """Generate and run a program to print out numerical values for expressions.""" + if type_word == 'status': + cast_to = 'long' + printf_format = '%ld' + else: + cast_to = 'unsigned long' + printf_format = '0x%08lx' + return get_c_expression_values( + cast_to, printf_format, + expressions, + caller='test_psa_constant_names.py for {} values'.format(type_word), + file_label=type_word, + header='#include ', + include_path=include_path, + keep_c=keep_c + ) + NORMALIZE_STRIP_RE = re.compile(r'\s+') def normalize(expr): """Normalize the C expression so as not to care about trivial differences.