1a8e1175bSopenharmony_ci"""Generate and run C code. 2a8e1175bSopenharmony_ci""" 3a8e1175bSopenharmony_ci 4a8e1175bSopenharmony_ci# Copyright The Mbed TLS Contributors 5a8e1175bSopenharmony_ci# SPDX-License-Identifier: Apache-2.0 6a8e1175bSopenharmony_ci# 7a8e1175bSopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); you may 8a8e1175bSopenharmony_ci# not use this file except in compliance with the License. 9a8e1175bSopenharmony_ci# You may obtain a copy of the License at 10a8e1175bSopenharmony_ci# 11a8e1175bSopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 12a8e1175bSopenharmony_ci# 13a8e1175bSopenharmony_ci# Unless required by applicable law or agreed to in writing, software 14a8e1175bSopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15a8e1175bSopenharmony_ci# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16a8e1175bSopenharmony_ci# See the License for the specific language governing permissions and 17a8e1175bSopenharmony_ci# limitations under the License. 18a8e1175bSopenharmony_ci 19a8e1175bSopenharmony_ciimport os 20a8e1175bSopenharmony_ciimport platform 21a8e1175bSopenharmony_ciimport subprocess 22a8e1175bSopenharmony_ciimport sys 23a8e1175bSopenharmony_ciimport tempfile 24a8e1175bSopenharmony_ci 25a8e1175bSopenharmony_cidef remove_file_if_exists(filename): 26a8e1175bSopenharmony_ci """Remove the specified file, ignoring errors.""" 27a8e1175bSopenharmony_ci if not filename: 28a8e1175bSopenharmony_ci return 29a8e1175bSopenharmony_ci try: 30a8e1175bSopenharmony_ci os.remove(filename) 31a8e1175bSopenharmony_ci except OSError: 32a8e1175bSopenharmony_ci pass 33a8e1175bSopenharmony_ci 34a8e1175bSopenharmony_cidef create_c_file(file_label): 35a8e1175bSopenharmony_ci """Create a temporary C file. 36a8e1175bSopenharmony_ci 37a8e1175bSopenharmony_ci * ``file_label``: a string that will be included in the file name. 38a8e1175bSopenharmony_ci 39a8e1175bSopenharmony_ci Return ```(c_file, c_name, exe_name)``` where ``c_file`` is a Python 40a8e1175bSopenharmony_ci stream open for writing to the file, ``c_name`` is the name of the file 41a8e1175bSopenharmony_ci and ``exe_name`` is the name of the executable that will be produced 42a8e1175bSopenharmony_ci by compiling the file. 43a8e1175bSopenharmony_ci """ 44a8e1175bSopenharmony_ci c_fd, c_name = tempfile.mkstemp(prefix='tmp-{}-'.format(file_label), 45a8e1175bSopenharmony_ci suffix='.c') 46a8e1175bSopenharmony_ci exe_suffix = '.exe' if platform.system() == 'Windows' else '' 47a8e1175bSopenharmony_ci exe_name = c_name[:-2] + exe_suffix 48a8e1175bSopenharmony_ci remove_file_if_exists(exe_name) 49a8e1175bSopenharmony_ci c_file = os.fdopen(c_fd, 'w', encoding='ascii') 50a8e1175bSopenharmony_ci return c_file, c_name, exe_name 51a8e1175bSopenharmony_ci 52a8e1175bSopenharmony_cidef generate_c_printf_expressions(c_file, cast_to, printf_format, expressions): 53a8e1175bSopenharmony_ci """Generate C instructions to print the value of ``expressions``. 54a8e1175bSopenharmony_ci 55a8e1175bSopenharmony_ci Write the code with ``c_file``'s ``write`` method. 56a8e1175bSopenharmony_ci 57a8e1175bSopenharmony_ci Each expression is cast to the type ``cast_to`` and printed with the 58a8e1175bSopenharmony_ci printf format ``printf_format``. 59a8e1175bSopenharmony_ci """ 60a8e1175bSopenharmony_ci for expr in expressions: 61a8e1175bSopenharmony_ci c_file.write(' printf("{}\\n", ({}) {});\n' 62a8e1175bSopenharmony_ci .format(printf_format, cast_to, expr)) 63a8e1175bSopenharmony_ci 64a8e1175bSopenharmony_cidef generate_c_file(c_file, 65a8e1175bSopenharmony_ci caller, header, 66a8e1175bSopenharmony_ci main_generator): 67a8e1175bSopenharmony_ci """Generate a temporary C source file. 68a8e1175bSopenharmony_ci 69a8e1175bSopenharmony_ci * ``c_file`` is an open stream on the C source file. 70a8e1175bSopenharmony_ci * ``caller``: an informational string written in a comment at the top 71a8e1175bSopenharmony_ci of the file. 72a8e1175bSopenharmony_ci * ``header``: extra code to insert before any function in the generated 73a8e1175bSopenharmony_ci C file. 74a8e1175bSopenharmony_ci * ``main_generator``: a function called with ``c_file`` as its sole argument 75a8e1175bSopenharmony_ci to generate the body of the ``main()`` function. 76a8e1175bSopenharmony_ci """ 77a8e1175bSopenharmony_ci c_file.write('/* Generated by {} */' 78a8e1175bSopenharmony_ci .format(caller)) 79a8e1175bSopenharmony_ci c_file.write(''' 80a8e1175bSopenharmony_ci#include <stdio.h> 81a8e1175bSopenharmony_ci''') 82a8e1175bSopenharmony_ci c_file.write(header) 83a8e1175bSopenharmony_ci c_file.write(''' 84a8e1175bSopenharmony_ciint main(void) 85a8e1175bSopenharmony_ci{ 86a8e1175bSopenharmony_ci''') 87a8e1175bSopenharmony_ci main_generator(c_file) 88a8e1175bSopenharmony_ci c_file.write(''' return 0; 89a8e1175bSopenharmony_ci} 90a8e1175bSopenharmony_ci''') 91a8e1175bSopenharmony_ci 92a8e1175bSopenharmony_cidef compile_c_file(c_filename, exe_filename, include_dirs): 93a8e1175bSopenharmony_ci """Compile a C source file with the host compiler. 94a8e1175bSopenharmony_ci 95a8e1175bSopenharmony_ci * ``c_filename``: the name of the source file to compile. 96a8e1175bSopenharmony_ci * ``exe_filename``: the name for the executable to be created. 97a8e1175bSopenharmony_ci * ``include_dirs``: a list of paths to include directories to be passed 98a8e1175bSopenharmony_ci with the -I switch. 99a8e1175bSopenharmony_ci """ 100a8e1175bSopenharmony_ci # Respect $HOSTCC if it is set 101a8e1175bSopenharmony_ci cc = os.getenv('HOSTCC', None) 102a8e1175bSopenharmony_ci if cc is None: 103a8e1175bSopenharmony_ci cc = os.getenv('CC', 'cc') 104a8e1175bSopenharmony_ci cmd = [cc] 105a8e1175bSopenharmony_ci 106a8e1175bSopenharmony_ci proc = subprocess.Popen(cmd, 107a8e1175bSopenharmony_ci stdout=subprocess.DEVNULL, 108a8e1175bSopenharmony_ci stderr=subprocess.PIPE, 109a8e1175bSopenharmony_ci universal_newlines=True) 110a8e1175bSopenharmony_ci cc_is_msvc = 'Microsoft (R) C/C++' in proc.communicate()[1] 111a8e1175bSopenharmony_ci 112a8e1175bSopenharmony_ci cmd += ['-I' + dir for dir in include_dirs] 113a8e1175bSopenharmony_ci if cc_is_msvc: 114a8e1175bSopenharmony_ci # MSVC has deprecated using -o to specify the output file, 115a8e1175bSopenharmony_ci # and produces an object file in the working directory by default. 116a8e1175bSopenharmony_ci obj_filename = exe_filename[:-4] + '.obj' 117a8e1175bSopenharmony_ci cmd += ['-Fe' + exe_filename, '-Fo' + obj_filename] 118a8e1175bSopenharmony_ci else: 119a8e1175bSopenharmony_ci cmd += ['-o' + exe_filename] 120a8e1175bSopenharmony_ci 121a8e1175bSopenharmony_ci subprocess.check_call(cmd + [c_filename]) 122a8e1175bSopenharmony_ci 123a8e1175bSopenharmony_cidef get_c_expression_values( 124a8e1175bSopenharmony_ci cast_to, printf_format, 125a8e1175bSopenharmony_ci expressions, 126a8e1175bSopenharmony_ci caller=__name__, file_label='', 127a8e1175bSopenharmony_ci header='', include_path=None, 128a8e1175bSopenharmony_ci keep_c=False, 129a8e1175bSopenharmony_ci): # pylint: disable=too-many-arguments, too-many-locals 130a8e1175bSopenharmony_ci """Generate and run a program to print out numerical values for expressions. 131a8e1175bSopenharmony_ci 132a8e1175bSopenharmony_ci * ``cast_to``: a C type. 133a8e1175bSopenharmony_ci * ``printf_format``: a printf format suitable for the type ``cast_to``. 134a8e1175bSopenharmony_ci * ``header``: extra code to insert before any function in the generated 135a8e1175bSopenharmony_ci C file. 136a8e1175bSopenharmony_ci * ``expressions``: a list of C language expressions that have the type 137a8e1175bSopenharmony_ci ``cast_to``. 138a8e1175bSopenharmony_ci * ``include_path``: a list of directories containing header files. 139a8e1175bSopenharmony_ci * ``keep_c``: if true, keep the temporary C file (presumably for debugging 140a8e1175bSopenharmony_ci purposes). 141a8e1175bSopenharmony_ci 142a8e1175bSopenharmony_ci Use the C compiler specified by the ``CC`` environment variable, defaulting 143a8e1175bSopenharmony_ci to ``cc``. If ``CC`` looks like MSVC, use its command line syntax, 144a8e1175bSopenharmony_ci otherwise assume the compiler supports Unix traditional ``-I`` and ``-o``. 145a8e1175bSopenharmony_ci 146a8e1175bSopenharmony_ci Return the list of values of the ``expressions``. 147a8e1175bSopenharmony_ci """ 148a8e1175bSopenharmony_ci if include_path is None: 149a8e1175bSopenharmony_ci include_path = [] 150a8e1175bSopenharmony_ci c_name = None 151a8e1175bSopenharmony_ci exe_name = None 152a8e1175bSopenharmony_ci obj_name = None 153a8e1175bSopenharmony_ci try: 154a8e1175bSopenharmony_ci c_file, c_name, exe_name = create_c_file(file_label) 155a8e1175bSopenharmony_ci generate_c_file( 156a8e1175bSopenharmony_ci c_file, caller, header, 157a8e1175bSopenharmony_ci lambda c_file: generate_c_printf_expressions(c_file, 158a8e1175bSopenharmony_ci cast_to, printf_format, 159a8e1175bSopenharmony_ci expressions) 160a8e1175bSopenharmony_ci ) 161a8e1175bSopenharmony_ci c_file.close() 162a8e1175bSopenharmony_ci 163a8e1175bSopenharmony_ci compile_c_file(c_name, exe_name, include_path) 164a8e1175bSopenharmony_ci if keep_c: 165a8e1175bSopenharmony_ci sys.stderr.write('List of {} tests kept at {}\n' 166a8e1175bSopenharmony_ci .format(caller, c_name)) 167a8e1175bSopenharmony_ci else: 168a8e1175bSopenharmony_ci os.remove(c_name) 169a8e1175bSopenharmony_ci output = subprocess.check_output([exe_name]) 170a8e1175bSopenharmony_ci return output.decode('ascii').strip().split('\n') 171a8e1175bSopenharmony_ci finally: 172a8e1175bSopenharmony_ci remove_file_if_exists(exe_name) 173a8e1175bSopenharmony_ci remove_file_if_exists(obj_name) 174