mirror of
https://github.com/espressif/mbedtls.git
synced 2025-07-13 18:20:30 +08:00

For easier maintenance the framework repository is flattened here and added to the forked branch in source format.
178 lines
5.9 KiB
Python
178 lines
5.9 KiB
Python
"""Generate and run C code.
|
|
"""
|
|
|
|
# Copyright The Mbed TLS Contributors
|
|
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
|
#
|
|
|
|
import os
|
|
import platform
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
class CompileError(Exception):
|
|
"""Exception to represent an error during the compilation."""
|
|
|
|
def __init__(self, message):
|
|
"""Save the error massage"""
|
|
|
|
super().__init__()
|
|
self.message = message
|
|
|
|
def remove_file_if_exists(filename):
|
|
"""Remove the specified file, ignoring errors."""
|
|
if not filename:
|
|
return
|
|
try:
|
|
os.remove(filename)
|
|
except OSError:
|
|
pass
|
|
|
|
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 <stdio.h>
|
|
''')
|
|
c_file.write(header)
|
|
c_file.write('''
|
|
int main(void)
|
|
{
|
|
''')
|
|
main_generator(c_file)
|
|
c_file.write(''' return 0;
|
|
}
|
|
''')
|
|
|
|
def compile_c_file(c_filename, exe_filename, include_dirs):
|
|
"""Compile a C source file with the host compiler.
|
|
|
|
* ``c_filename``: the name of the source file to compile.
|
|
* ``exe_filename``: the name for the executable to be created.
|
|
* ``include_dirs``: a list of paths to include directories to be passed
|
|
with the -I switch.
|
|
"""
|
|
# Respect $HOSTCC if it is set
|
|
cc = os.getenv('HOSTCC', None)
|
|
if cc is None:
|
|
cc = os.getenv('CC', 'cc')
|
|
cmd = [cc]
|
|
|
|
proc = subprocess.Popen(cmd,
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.PIPE,
|
|
universal_newlines=True)
|
|
cc_is_msvc = 'Microsoft (R) C/C++' in proc.communicate()[1]
|
|
|
|
cmd += ['-I' + dir for dir in include_dirs]
|
|
if cc_is_msvc:
|
|
# MSVC has deprecated using -o to specify the output file,
|
|
# and produces an object file in the working directory by default.
|
|
obj_filename = exe_filename[:-4] + '.obj'
|
|
cmd += ['-Fe' + exe_filename, '-Fo' + obj_filename]
|
|
else:
|
|
cmd += ['-o' + exe_filename]
|
|
|
|
try:
|
|
subprocess.check_output(cmd + [c_filename],
|
|
stderr=subprocess.PIPE,
|
|
universal_newlines=True)
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
raise CompileError(e.stderr) from e
|
|
|
|
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, too-many-locals
|
|
"""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
|
|
``cast_to``.
|
|
* ``include_path``: a list of directories containing header files.
|
|
* ``keep_c``: if true, keep the temporary C file (presumably for debugging
|
|
purposes).
|
|
|
|
Use the C compiler specified by the ``CC`` environment variable, defaulting
|
|
to ``cc``. If ``CC`` looks like MSVC, use its command line syntax,
|
|
otherwise assume the compiler supports Unix traditional ``-I`` and ``-o``.
|
|
|
|
Return the list of values of the ``expressions``.
|
|
"""
|
|
if include_path is None:
|
|
include_path = []
|
|
c_name = None
|
|
exe_name = None
|
|
obj_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()
|
|
|
|
compile_c_file(c_name, exe_name, include_path)
|
|
if keep_c:
|
|
sys.stderr.write('List of {} tests kept at {}\n'
|
|
.format(caller, c_name))
|
|
else:
|
|
os.remove(c_name)
|
|
output = subprocess.check_output([exe_name])
|
|
return output.decode('ascii').strip().split('\n')
|
|
finally:
|
|
remove_file_if_exists(exe_name)
|
|
remove_file_if_exists(obj_name)
|