mirror of
				https://github.com/Mbed-TLS/mbedtls.git
				synced 2025-10-30 10:45:34 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			163 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			5.4 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
 | |
| 
 | |
| 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]
 | |
| 
 | |
|     subprocess.check_call(cmd + [c_filename])
 | |
| 
 | |
| 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)
 |