c_build_helper.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. """Generate and run C code.
  2. """
  3. # Copyright The Mbed TLS Contributors
  4. # SPDX-License-Identifier: Apache-2.0
  5. #
  6. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  7. # not use this file except in compliance with the License.
  8. # You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  14. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. import os
  18. import platform
  19. import subprocess
  20. import sys
  21. import tempfile
  22. def remove_file_if_exists(filename):
  23. """Remove the specified file, ignoring errors."""
  24. if not filename:
  25. return
  26. try:
  27. os.remove(filename)
  28. except OSError:
  29. pass
  30. def create_c_file(file_label):
  31. """Create a temporary C file.
  32. * ``file_label``: a string that will be included in the file name.
  33. Return ```(c_file, c_name, exe_name)``` where ``c_file`` is a Python
  34. stream open for writing to the file, ``c_name`` is the name of the file
  35. and ``exe_name`` is the name of the executable that will be produced
  36. by compiling the file.
  37. """
  38. c_fd, c_name = tempfile.mkstemp(prefix='tmp-{}-'.format(file_label),
  39. suffix='.c')
  40. exe_suffix = '.exe' if platform.system() == 'Windows' else ''
  41. exe_name = c_name[:-2] + exe_suffix
  42. remove_file_if_exists(exe_name)
  43. c_file = os.fdopen(c_fd, 'w', encoding='ascii')
  44. return c_file, c_name, exe_name
  45. def generate_c_printf_expressions(c_file, cast_to, printf_format, expressions):
  46. """Generate C instructions to print the value of ``expressions``.
  47. Write the code with ``c_file``'s ``write`` method.
  48. Each expression is cast to the type ``cast_to`` and printed with the
  49. printf format ``printf_format``.
  50. """
  51. for expr in expressions:
  52. c_file.write(' printf("{}\\n", ({}) {});\n'
  53. .format(printf_format, cast_to, expr))
  54. def generate_c_file(c_file,
  55. caller, header,
  56. main_generator):
  57. """Generate a temporary C source file.
  58. * ``c_file`` is an open stream on the C source file.
  59. * ``caller``: an informational string written in a comment at the top
  60. of the file.
  61. * ``header``: extra code to insert before any function in the generated
  62. C file.
  63. * ``main_generator``: a function called with ``c_file`` as its sole argument
  64. to generate the body of the ``main()`` function.
  65. """
  66. c_file.write('/* Generated by {} */'
  67. .format(caller))
  68. c_file.write('''
  69. #include <stdio.h>
  70. ''')
  71. c_file.write(header)
  72. c_file.write('''
  73. int main(void)
  74. {
  75. ''')
  76. main_generator(c_file)
  77. c_file.write(''' return 0;
  78. }
  79. ''')
  80. def get_c_expression_values(
  81. cast_to, printf_format,
  82. expressions,
  83. caller=__name__, file_label='',
  84. header='', include_path=None,
  85. keep_c=False,
  86. ): # pylint: disable=too-many-arguments
  87. """Generate and run a program to print out numerical values for expressions.
  88. * ``cast_to``: a C type.
  89. * ``printf_format``: a printf format suitable for the type ``cast_to``.
  90. * ``header``: extra code to insert before any function in the generated
  91. C file.
  92. * ``expressions``: a list of C language expressions that have the type
  93. ``cast_to``.
  94. * ``include_path``: a list of directories containing header files.
  95. * ``keep_c``: if true, keep the temporary C file (presumably for debugging
  96. purposes).
  97. Return the list of values of the ``expressions``.
  98. """
  99. if include_path is None:
  100. include_path = []
  101. c_name = None
  102. exe_name = None
  103. try:
  104. c_file, c_name, exe_name = create_c_file(file_label)
  105. generate_c_file(
  106. c_file, caller, header,
  107. lambda c_file: generate_c_printf_expressions(c_file,
  108. cast_to, printf_format,
  109. expressions)
  110. )
  111. c_file.close()
  112. cc = os.getenv('CC', 'cc')
  113. subprocess.check_call([cc] +
  114. ['-I' + dir for dir in include_path] +
  115. ['-o', exe_name, c_name])
  116. if keep_c:
  117. sys.stderr.write('List of {} tests kept at {}\n'
  118. .format(caller, c_name))
  119. else:
  120. os.remove(c_name)
  121. output = subprocess.check_output([exe_name])
  122. return output.decode('ascii').strip().split('\n')
  123. finally:
  124. remove_file_if_exists(exe_name)