Run cc instead of cpp

Run a C compiler, not just the preprocessor. This allows capturing static
assertions (`MBEDTLS_STATIC_ASSERT`).

Signed-off-by: Gilles Peskine <[email protected]>
This commit is contained in:
Gilles Peskine
2025-09-19 21:12:22 +02:00
parent 103cdbc45c
commit 172e29bd7c
@@ -2,7 +2,7 @@
This tests the output of ``generate_config_checks.py``. This tests the output of ``generate_config_checks.py``.
This can also let us verify what we enforce in the manually written This can also let us verify what we enforce in the manually written
checks in ``<PROJECT>_check_config.h``. checks in ``<PROJECT>_check_config.h`` and ``<PROJECT>_config.c``.
""" """
## Copyright The Mbed TLS Contributors ## Copyright The Mbed TLS Contributors
@@ -29,27 +29,27 @@ class TestConfigChecks(unittest.TestCase):
PROJECT_SPECIFIC_INCLUDE_DIRECTORIES = [] #type: List[str] PROJECT_SPECIFIC_INCLUDE_DIRECTORIES = [] #type: List[str]
# Increase the length of strings that assertion failures are willing to # Increase the length of strings that assertion failures are willing to
# print. This is useful for failures where the preprocessor has a lot # print. This is useful for failures where the compiler has a lot
# to say. # to say.
maxDiff = 9999 maxDiff = 9999
def setUp(self) -> None: def setUp(self) -> None:
self.cpp_output = None #type: Optional[str] self.cc_output = None #type: Optional[str]
def tearDown(self) -> None: def tearDown(self) -> None:
"""Log the preprocessor output to a file, if available and desired. """Log the compiler output to a file, if available and desired.
This is intended for debugging. It only happens if the environment This is intended for debugging. It only happens if the environment
variable UNITTEST_CONFIG_CHECKS_DEBUG is non-empty. variable UNITTEST_CONFIG_CHECKS_DEBUG is non-empty.
""" """
if os.getenv('UNITTEST_CONFIG_CHECKS_DEBUG'): if os.getenv('UNITTEST_CONFIG_CHECKS_DEBUG'):
# We set self.cpp_output to the preprocessor output before # We set self.cc_output to the compiler output before
# asserting, and set it to None if all the assertions pass. # asserting, and set it to None if all the assertions pass.
if self.cpp_output is not None: if self.cc_output is not None:
basename = os.path.splitext(os.path.basename(sys.argv[0]))[0] basename = os.path.splitext(os.path.basename(sys.argv[0]))[0]
filename = f'{basename}.{self._testMethodName}.out.txt' filename = f'{basename}.{self._testMethodName}.out.txt'
with open(filename, 'w') as out: with open(filename, 'w') as out:
out.write(self.cpp_output) out.write(self.cc_output)
def user_config_file_name(self, variant: str) -> str: def user_config_file_name(self, variant: str) -> str:
"""Construct a unique temporary file name for a user config header.""" """Construct a unique temporary file name for a user config header."""
@@ -81,12 +81,12 @@ class TestConfigChecks(unittest.TestCase):
mbedtls_user_config_file: Optional[str], mbedtls_user_config_file: Optional[str],
extra_options: List[str], extra_options: List[str],
) -> subprocess.CompletedProcess: ) -> subprocess.CompletedProcess:
"""Run cpp with the given user configuration files. """Run cc with the given user configuration files.
Return the CompletedProcess object capturing the return code, Return the CompletedProcess object capturing the return code,
stdout and stderr. stdout and stderr.
""" """
cmd = ['cpp'] cmd = [os.getenv('CC', 'cc')]
if os.getenv('UNITTEST_CONFIG_CHECKS_DEBUG'): if os.getenv('UNITTEST_CONFIG_CHECKS_DEBUG'):
cmd += ['-dD'] cmd += ['-dD']
if crypto_user_config_file is not None: if crypto_user_config_file is not None:
@@ -99,7 +99,7 @@ class TestConfigChecks(unittest.TestCase):
cmd += ['-Iinclude', cmd += ['-Iinclude',
'-I.', '-I.',
'-I' + os.path.dirname(self.PROJECT_CONFIG_C)] '-I' + os.path.dirname(self.PROJECT_CONFIG_C)]
cmd.append(self.PROJECT_CONFIG_C) cmd += ['-o', os.devnull, '-c', self.PROJECT_CONFIG_C]
return subprocess.run(cmd, return subprocess.run(cmd,
check=False, check=False,
encoding='utf-8', encoding='utf-8',
@@ -111,7 +111,7 @@ class TestConfigChecks(unittest.TestCase):
mbedtls_user_config: Optional[str] = None, mbedtls_user_config: Optional[str] = None,
extra_options: Optional[List[str]] = None, extra_options: Optional[List[str]] = None,
) -> subprocess.CompletedProcess: ) -> subprocess.CompletedProcess:
"""Run cpp with the given content for user configuration files. """Run cc with the given content for user configuration files.
Return the CompletedProcess object capturing the return code, Return the CompletedProcess object capturing the return code,
stdout and stderr. stdout and stderr.
@@ -149,18 +149,18 @@ class TestConfigChecks(unittest.TestCase):
mbedtls_user_config: Optional[str] = None, mbedtls_user_config: Optional[str] = None,
extra_options: Optional[List[str]] = None, extra_options: Optional[List[str]] = None,
) -> None: ) -> None:
"""Run cpp with the given user config(s). Expect no error. """Run cc with the given user config(s). Expect no error.
Pass extra_options on the command line of cpp. Pass extra_options on the command line of cc.
""" """
cp = self.run_with_config(crypto_user_config, mbedtls_user_config, cp = self.run_with_config(crypto_user_config, mbedtls_user_config,
extra_options=extra_options) extra_options=extra_options)
# Assert the error text before the status. That way, if it fails, # Assert the error text before the status. That way, if it fails,
# we see the unexpected error messages in the test log. # we see the unexpected error messages in the test log.
self.cpp_output = cp.stdout self.cc_output = cp.stdout
self.assertEqual(cp.stderr, '') self.assertEqual(cp.stderr, '')
self.assertEqual(cp.returncode, 0) self.assertEqual(cp.returncode, 0)
self.cpp_output = None self.cc_output = None
def bad_case(self, def bad_case(self,
crypto_user_config: Optional[str], crypto_user_config: Optional[str],
@@ -168,22 +168,22 @@ class TestConfigChecks(unittest.TestCase):
error: Optional[Union[str, Pattern]] = None, error: Optional[Union[str, Pattern]] = None,
extra_options: Optional[List[str]] = None, extra_options: Optional[List[str]] = None,
) -> None: ) -> None:
"""Run cpp with the given user config(s). Expect errors. """Run cc with the given user config(s). Expect errors.
Pass extra_options on the command line of cpp. Pass extra_options on the command line of cc.
If error is given, the standard error from cpp must match this regex. If error is given, the standard error from cc must match this regex.
""" """
cp = self.run_with_config(crypto_user_config, mbedtls_user_config, cp = self.run_with_config(crypto_user_config, mbedtls_user_config,
extra_options=extra_options) extra_options=extra_options)
self.cpp_output = cp.stdout self.cc_output = cp.stdout
if error is not None: if error is not None:
# Assert the error text before the status. That way, if it fails, # Assert the error text before the status. That way, if it fails,
# we see the unexpected error messages in the test log. # we see the unexpected error messages in the test log.
self.assertRegex(cp.stderr, error) self.assertRegex(cp.stderr, error)
self.assertGreater(cp.returncode, 0) self.assertGreater(cp.returncode, 0)
self.assertLess(cp.returncode, 126) self.assertLess(cp.returncode, 126)
self.cpp_output = None self.cc_output = None
# Nominal case, run first # Nominal case, run first
def test_01_nominal(self) -> None: def test_01_nominal(self) -> None: