| #!/usr/bin/env python3 |
| |
| # Copyright The Mbed TLS Contributors |
| # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later |
| |
| """ |
| Test Mbed TLS with a subset of algorithms. |
| |
| This script can be divided into several steps: |
| |
| First, include/mbedtls/mbedtls_config.h or a different config file passed |
| in the arguments is parsed to extract any configuration options (using config.py). |
| |
| Then, test domains (groups of jobs, tests) are built based on predefined data |
| collected in the DomainData class. Here, each domain has five major traits: |
| - domain name, can be used to run only specific tests via command-line; |
| - configuration building method, described in detail below; |
| - list of symbols passed to the configuration building method; |
| - commands to be run on each job (only build, build and test, or any other custom); |
| - optional list of symbols to be excluded from testing. |
| |
| The configuration building method can be one of the three following: |
| |
| - ComplementaryDomain - build a job for each passed symbol by disabling a single |
| symbol and its reverse dependencies (defined in REVERSE_DEPENDENCIES); |
| |
| - ExclusiveDomain - build a job where, for each passed symbol, only this particular |
| one is defined and other symbols from the list are unset. For each job look for |
| any non-standard symbols to set/unset in EXCLUSIVE_GROUPS. These are usually not |
| direct dependencies, but rather non-trivial results of other configs missing. Then |
| look for any unset symbols and handle their reverse dependencies. |
| Examples of EXCLUSIVE_GROUPS usage: |
| - MBEDTLS_SHA512_C job turns off all hashes except SHA512. MBEDTLS_SSL_COOKIE_C |
| requires either SHA256 or SHA384 to work, so it also has to be disabled. |
| This is not a dependency on SHA512_C, but a result of an exclusive domain |
| config building method. Relevant field: |
| 'MBEDTLS_SHA512_C': ['-MBEDTLS_SSL_COOKIE_C'], |
| |
| - DualDomain - combination of the two above - both complementary and exclusive domain |
| job generation code will be run. Currently only used for hashes. |
| |
| Lastly, the collected jobs are executed and (optionally) tested, with |
| error reporting and coloring as configured in options. Each test starts with |
| a full config without a couple of slowing down or unnecessary options |
| (see set_reference_config), then the specific job config is derived. |
| """ |
| import argparse |
| import os |
| import re |
| import shutil |
| import subprocess |
| import sys |
| import traceback |
| from typing import Union |
| |
| # Add the Mbed TLS Python library directory to the module search path |
| import scripts_path # pylint: disable=unused-import |
| import config |
| |
| class Colors: # pylint: disable=too-few-public-methods |
| """Minimalistic support for colored output. |
| Each field of an object of this class is either None if colored output |
| is not possible or not desired, or a pair of strings (start, stop) such |
| that outputting start switches the text color to the desired color and |
| stop switches the text color back to the default.""" |
| red = None |
| green = None |
| cyan = None |
| bold_red = None |
| bold_green = None |
| def __init__(self, options=None): |
| """Initialize color profile according to passed options.""" |
| if not options or options.color in ['no', 'never']: |
| want_color = False |
| elif options.color in ['yes', 'always']: |
| want_color = True |
| else: |
| want_color = sys.stderr.isatty() |
| if want_color: |
| # Assume ANSI compatible terminal |
| normal = '\033[0m' |
| self.red = ('\033[31m', normal) |
| self.green = ('\033[32m', normal) |
| self.cyan = ('\033[36m', normal) |
| self.bold_red = ('\033[1;31m', normal) |
| self.bold_green = ('\033[1;32m', normal) |
| NO_COLORS = Colors(None) |
| |
| def log_line(text, prefix='depends.py:', suffix='', color=None): |
| """Print a status message.""" |
| if color is not None: |
| prefix = color[0] + prefix |
| suffix = suffix + color[1] |
| sys.stderr.write(prefix + ' ' + text + suffix + '\n') |
| sys.stderr.flush() |
| |
| def log_command(cmd): |
| """Print a trace of the specified command. |
| cmd is a list of strings: a command name and its arguments.""" |
| log_line(' '.join(cmd), prefix='+') |
| |
| def backup_config(options): |
| """Back up the library configuration file (mbedtls_config.h). |
| If the backup file already exists, it is presumed to be the desired backup, |
| so don't make another backup.""" |
| if os.path.exists(options.config_backup): |
| options.own_backup = False |
| else: |
| options.own_backup = True |
| shutil.copy(options.config, options.config_backup) |
| |
| def restore_config(options): |
| """Restore the library configuration file (mbedtls_config.h). |
| Remove the backup file if it was saved earlier.""" |
| if options.own_backup: |
| shutil.move(options.config_backup, options.config) |
| else: |
| shutil.copy(options.config_backup, options.config) |
| |
| def option_exists(conf, option): |
| return option in conf.settings |
| |
| def set_config_option_value(conf, option, colors, value: Union[bool, str]): |
| """Set/unset a configuration option, optionally specifying a value. |
| value can be either True/False (set/unset config option), or a string, |
| which will make a symbol defined with a certain value.""" |
| if not option_exists(conf, option): |
| log_line('Symbol {} was not found in {}'.format(option, conf.filename), color=colors.red) |
| return False |
| |
| if value is False: |
| log_command(['config.py', 'unset', option]) |
| conf.unset(option) |
| elif value is True: |
| log_command(['config.py', 'set', option]) |
| conf.set(option) |
| else: |
| log_command(['config.py', 'set', option, value]) |
| conf.set(option, value) |
| return True |
| |
| def set_reference_config(conf, options, colors): |
| """Change the library configuration file (mbedtls_config.h) to the reference state. |
| The reference state is the one from which the tested configurations are |
| derived.""" |
| # Turn off options that are not relevant to the tests and slow them down. |
| log_command(['config.py', 'full']) |
| conf.adapt(config.full_adapter) |
| set_config_option_value(conf, 'MBEDTLS_TEST_HOOKS', colors, False) |
| set_config_option_value(conf, 'MBEDTLS_PSA_CRYPTO_CONFIG', colors, False) |
| if options.unset_use_psa: |
| set_config_option_value(conf, 'MBEDTLS_USE_PSA_CRYPTO', colors, False) |
| |
| class Job: |
| """A job builds the library in a specific configuration and runs some tests.""" |
| def __init__(self, name, config_settings, commands): |
| """Build a job object. |
| The job uses the configuration described by config_settings. This is a |
| dictionary where the keys are preprocessor symbols and the values are |
| booleans or strings. A boolean indicates whether or not to #define the |
| symbol. With a string, the symbol is #define'd to that value. |
| After setting the configuration, the job runs the programs specified by |
| commands. This is a list of lists of strings; each list of string is a |
| command name and its arguments and is passed to subprocess.call with |
| shell=False.""" |
| self.name = name |
| self.config_settings = config_settings |
| self.commands = commands |
| |
| def announce(self, colors, what): |
| '''Announce the start or completion of a job. |
| If what is None, announce the start of the job. |
| If what is True, announce that the job has passed. |
| If what is False, announce that the job has failed.''' |
| if what is True: |
| log_line(self.name + ' PASSED', color=colors.green) |
| elif what is False: |
| log_line(self.name + ' FAILED', color=colors.red) |
| else: |
| log_line('starting ' + self.name, color=colors.cyan) |
| |
| def configure(self, conf, options, colors): |
| '''Set library configuration options as required for the job.''' |
| set_reference_config(conf, options, colors) |
| for key, value in sorted(self.config_settings.items()): |
| ret = set_config_option_value(conf, key, colors, value) |
| if ret is False: |
| return False |
| return True |
| |
| def test(self, options): |
| '''Run the job's build and test commands. |
| Return True if all the commands succeed and False otherwise. |
| If options.keep_going is false, stop as soon as one command fails. Otherwise |
| run all the commands, except that if the first command fails, none of the |
| other commands are run (typically, the first command is a build command |
| and subsequent commands are tests that cannot run if the build failed).''' |
| built = False |
| success = True |
| for command in self.commands: |
| log_command(command) |
| ret = subprocess.call(command) |
| if ret != 0: |
| if command[0] not in ['make', options.make_command]: |
| log_line('*** [{}] Error {}'.format(' '.join(command), ret)) |
| if not options.keep_going or not built: |
| return False |
| success = False |
| built = True |
| return success |
| |
| # If the configuration option A requires B, make sure that |
| # B in REVERSE_DEPENDENCIES[A]. |
| # All the information here should be contained in check_config.h. This |
| # file includes a copy because it changes rarely and it would be a pain |
| # to extract automatically. |
| REVERSE_DEPENDENCIES = { |
| 'MBEDTLS_AES_C': ['MBEDTLS_CTR_DRBG_C', |
| 'MBEDTLS_NIST_KW_C'], |
| 'MBEDTLS_CHACHA20_C': ['MBEDTLS_CHACHAPOLY_C'], |
| 'MBEDTLS_ECDSA_C': ['MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED', |
| 'MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED'], |
| 'MBEDTLS_ECP_C': ['MBEDTLS_ECDSA_C', |
| 'MBEDTLS_ECDH_C', |
| 'MBEDTLS_ECJPAKE_C', |
| 'MBEDTLS_ECP_RESTARTABLE', |
| 'MBEDTLS_PK_PARSE_EC_EXTENDED', |
| 'MBEDTLS_PK_PARSE_EC_COMPRESSED', |
| 'MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED', |
| 'MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED', |
| 'MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED', |
| 'MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED', |
| 'MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED', |
| 'MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED', |
| 'MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED', |
| 'MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED'], |
| 'MBEDTLS_ECP_DP_SECP256R1_ENABLED': ['MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED'], |
| 'MBEDTLS_PKCS1_V21': ['MBEDTLS_X509_RSASSA_PSS_SUPPORT'], |
| 'MBEDTLS_PKCS1_V15': ['MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED', |
| 'MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED', |
| 'MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED', |
| 'MBEDTLS_KEY_EXCHANGE_RSA_ENABLED'], |
| 'MBEDTLS_RSA_C': ['MBEDTLS_X509_RSASSA_PSS_SUPPORT', |
| 'MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED', |
| 'MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED', |
| 'MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED', |
| 'MBEDTLS_KEY_EXCHANGE_RSA_ENABLED', |
| 'MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED'], |
| 'MBEDTLS_SHA256_C': ['MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED', |
| 'MBEDTLS_ENTROPY_FORCE_SHA256', |
| 'MBEDTLS_SHA256_USE_ARMV8_A_CRYPTO_IF_PRESENT', |
| 'MBEDTLS_SHA256_USE_ARMV8_A_CRYPTO_ONLY', |
| 'MBEDTLS_LMS_C', |
| 'MBEDTLS_LMS_PRIVATE'], |
| 'MBEDTLS_SHA512_C': ['MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT', |
| 'MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY'], |
| 'MBEDTLS_SHA224_C': ['MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED', |
| 'MBEDTLS_ENTROPY_FORCE_SHA256', |
| 'MBEDTLS_SHA256_USE_ARMV8_A_CRYPTO_IF_PRESENT', |
| 'MBEDTLS_SHA256_USE_ARMV8_A_CRYPTO_ONLY'], |
| 'MBEDTLS_X509_RSASSA_PSS_SUPPORT': [] |
| } |
| |
| # If an option is tested in an exclusive test, alter the following defines. |
| # These are not necessarily dependencies, but just minimal required changes |
| # if a given define is the only one enabled from an exclusive group. |
| EXCLUSIVE_GROUPS = { |
| 'MBEDTLS_SHA512_C': ['-MBEDTLS_SSL_COOKIE_C', |
| '-MBEDTLS_SSL_TLS_C'], |
| 'MBEDTLS_ECP_DP_CURVE448_ENABLED': ['-MBEDTLS_ECDSA_C', |
| '-MBEDTLS_ECDSA_DETERMINISTIC', |
| '-MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED', |
| '-MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED', |
| '-MBEDTLS_ECJPAKE_C', |
| '-MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED'], |
| 'MBEDTLS_ECP_DP_CURVE25519_ENABLED': ['-MBEDTLS_ECDSA_C', |
| '-MBEDTLS_ECDSA_DETERMINISTIC', |
| '-MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED', |
| '-MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED', |
| '-MBEDTLS_ECJPAKE_C', |
| '-MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED'], |
| 'MBEDTLS_ARIA_C': ['-MBEDTLS_CMAC_C'], |
| 'MBEDTLS_CAMELLIA_C': ['-MBEDTLS_CMAC_C'], |
| 'MBEDTLS_CHACHA20_C': ['-MBEDTLS_CMAC_C', '-MBEDTLS_CCM_C', '-MBEDTLS_GCM_C'], |
| 'MBEDTLS_DES_C': ['-MBEDTLS_CCM_C', |
| '-MBEDTLS_GCM_C', |
| '-MBEDTLS_SSL_TICKET_C', |
| '-MBEDTLS_SSL_CONTEXT_SERIALIZATION'], |
| } |
| def handle_exclusive_groups(config_settings, symbol): |
| """For every symbol tested in an exclusive group check if there are other |
| defines to be altered. """ |
| for dep in EXCLUSIVE_GROUPS.get(symbol, []): |
| unset = dep.startswith('-') |
| dep = dep[1:] |
| config_settings[dep] = not unset |
| |
| def turn_off_dependencies(config_settings): |
| """For every option turned off config_settings, also turn off what depends on it. |
| An option O is turned off if config_settings[O] is False.""" |
| for key, value in sorted(config_settings.items()): |
| if value is not False: |
| continue |
| for dep in REVERSE_DEPENDENCIES.get(key, []): |
| config_settings[dep] = False |
| |
| class BaseDomain: # pylint: disable=too-few-public-methods, unused-argument |
| """A base class for all domains.""" |
| def __init__(self, symbols, commands, exclude): |
| """Initialize the jobs container""" |
| self.jobs = [] |
| |
| class ExclusiveDomain(BaseDomain): # pylint: disable=too-few-public-methods |
| """A domain consisting of a set of conceptually-equivalent settings. |
| Establish a list of configuration symbols. For each symbol, run a test job |
| with this symbol set and the others unset.""" |
| def __init__(self, symbols, commands, exclude=None): |
| """Build a domain for the specified list of configuration symbols. |
| The domain contains a set of jobs that enable one of the elements |
| of symbols and disable the others. |
| Each job runs the specified commands. |
| If exclude is a regular expression, skip generated jobs whose description |
| would match this regular expression.""" |
| super().__init__(symbols, commands, exclude) |
| base_config_settings = {} |
| for symbol in symbols: |
| base_config_settings[symbol] = False |
| for symbol in symbols: |
| description = symbol |
| if exclude and re.match(exclude, description): |
| continue |
| config_settings = base_config_settings.copy() |
| config_settings[symbol] = True |
| handle_exclusive_groups(config_settings, symbol) |
| turn_off_dependencies(config_settings) |
| job = Job(description, config_settings, commands) |
| self.jobs.append(job) |
| |
| class ComplementaryDomain(BaseDomain): # pylint: disable=too-few-public-methods |
| """A domain consisting of a set of loosely-related settings. |
| Establish a list of configuration symbols. For each symbol, run a test job |
| with this symbol unset. |
| If exclude is a regular expression, skip generated jobs whose description |
| would match this regular expression.""" |
| def __init__(self, symbols, commands, exclude=None): |
| """Build a domain for the specified list of configuration symbols. |
| Each job in the domain disables one of the specified symbols. |
| Each job runs the specified commands.""" |
| super().__init__(symbols, commands, exclude) |
| for symbol in symbols: |
| description = '!' + symbol |
| if exclude and re.match(exclude, description): |
| continue |
| config_settings = {symbol: False} |
| turn_off_dependencies(config_settings) |
| job = Job(description, config_settings, commands) |
| self.jobs.append(job) |
| |
| class DualDomain(ExclusiveDomain, ComplementaryDomain): # pylint: disable=too-few-public-methods |
| """A domain that contains both the ExclusiveDomain and BaseDomain tests. |
| Both parent class __init__ calls are performed in any order and |
| each call adds respective jobs. The job array initialization is done once in |
| BaseDomain, before the parent __init__ calls.""" |
| |
| class CipherInfo: # pylint: disable=too-few-public-methods |
| """Collect data about cipher.h.""" |
| def __init__(self): |
| self.base_symbols = set() |
| with open('include/mbedtls/cipher.h', encoding="utf-8") as fh: |
| for line in fh: |
| m = re.match(r' *MBEDTLS_CIPHER_ID_(\w+),', line) |
| if m and m.group(1) not in ['NONE', 'NULL', '3DES']: |
| self.base_symbols.add('MBEDTLS_' + m.group(1) + '_C') |
| |
| class DomainData: |
| """A container for domains and jobs, used to structurize testing.""" |
| def config_symbols_matching(self, regexp): |
| """List the mbedtls_config.h settings matching regexp.""" |
| return [symbol for symbol in self.all_config_symbols |
| if re.match(regexp, symbol)] |
| |
| def __init__(self, options, conf): |
| """Gather data about the library and establish a list of domains to test.""" |
| build_command = [options.make_command, 'CFLAGS=-Werror -O2'] |
| build_and_test = [build_command, [options.make_command, 'test']] |
| self.all_config_symbols = set(conf.settings.keys()) |
| # Find hash modules by name. |
| hash_symbols = self.config_symbols_matching(r'MBEDTLS_(MD|RIPEMD|SHA)[0-9]+_C\Z') |
| # Find elliptic curve enabling macros by name. |
| curve_symbols = self.config_symbols_matching(r'MBEDTLS_ECP_DP_\w+_ENABLED\Z') |
| # Find key exchange enabling macros by name. |
| key_exchange_symbols = self.config_symbols_matching(r'MBEDTLS_KEY_EXCHANGE_\w+_ENABLED\Z') |
| # Find cipher IDs (block permutations and stream ciphers --- chaining |
| # and padding modes are exercised separately) information by parsing |
| # cipher.h, as the information is not readily available in mbedtls_config.h. |
| cipher_info = CipherInfo() |
| # Find block cipher chaining and padding mode enabling macros by name. |
| cipher_chaining_symbols = self.config_symbols_matching(r'MBEDTLS_CIPHER_MODE_\w+\Z') |
| cipher_padding_symbols = self.config_symbols_matching(r'MBEDTLS_CIPHER_PADDING_\w+\Z') |
| self.domains = { |
| # Cipher IDs, chaining modes and padding modes. Run the test suites. |
| 'cipher_id': ExclusiveDomain(cipher_info.base_symbols, |
| build_and_test), |
| 'cipher_chaining': ExclusiveDomain(cipher_chaining_symbols, |
| build_and_test), |
| 'cipher_padding': ExclusiveDomain(cipher_padding_symbols, |
| build_and_test), |
| # Elliptic curves. Run the test suites. |
| 'curves': ExclusiveDomain(curve_symbols, build_and_test), |
| # Hash algorithms. Excluding exclusive domains of MD, RIPEMD, SHA1, |
| # SHA224 and SHA384 because MBEDTLS_ENTROPY_C is extensively used |
| # across various modules, but it depends on either SHA256 or SHA512. |
| # As a consequence an "exclusive" test of anything other than SHA256 |
| # or SHA512 with MBEDTLS_ENTROPY_C enabled is not possible. |
| 'hashes': DualDomain(hash_symbols, build_and_test, |
| exclude=r'MBEDTLS_(MD|RIPEMD|SHA1_)' \ |
| '|MBEDTLS_SHA224_' \ |
| '|MBEDTLS_SHA384_' \ |
| '|MBEDTLS_SHA3_'), |
| # Key exchange types. |
| 'kex': ExclusiveDomain(key_exchange_symbols, build_and_test), |
| 'pkalgs': ComplementaryDomain(['MBEDTLS_ECDSA_C', |
| 'MBEDTLS_ECP_C', |
| 'MBEDTLS_PKCS1_V21', |
| 'MBEDTLS_PKCS1_V15', |
| 'MBEDTLS_RSA_C', |
| 'MBEDTLS_X509_RSASSA_PSS_SUPPORT'], |
| build_and_test), |
| } |
| self.jobs = {} |
| for domain in self.domains.values(): |
| for job in domain.jobs: |
| self.jobs[job.name] = job |
| |
| def get_jobs(self, name): |
| """Return the list of jobs identified by the given name. |
| A name can either be the name of a domain or the name of one specific job.""" |
| if name in self.domains: |
| return sorted(self.domains[name].jobs, key=lambda job: job.name) |
| else: |
| return [self.jobs[name]] |
| |
| def run(options, job, conf, colors=NO_COLORS): |
| """Run the specified job (a Job instance).""" |
| subprocess.check_call([options.make_command, 'clean']) |
| job.announce(colors, None) |
| if not job.configure(conf, options, colors): |
| job.announce(colors, False) |
| return False |
| conf.write() |
| success = job.test(options) |
| job.announce(colors, success) |
| return success |
| |
| def run_tests(options, domain_data, conf): |
| """Run the desired jobs. |
| domain_data should be a DomainData instance that describes the available |
| domains and jobs. |
| Run the jobs listed in options.tasks.""" |
| if not hasattr(options, 'config_backup'): |
| options.config_backup = options.config + '.bak' |
| colors = Colors(options) |
| jobs = [] |
| failures = [] |
| successes = [] |
| for name in options.tasks: |
| jobs += domain_data.get_jobs(name) |
| backup_config(options) |
| try: |
| for job in jobs: |
| success = run(options, job, conf, colors=colors) |
| if not success: |
| if options.keep_going: |
| failures.append(job.name) |
| else: |
| return False |
| else: |
| successes.append(job.name) |
| restore_config(options) |
| except: |
| # Restore the configuration, except in stop-on-error mode if there |
| # was an error, where we leave the failing configuration up for |
| # developer convenience. |
| if options.keep_going: |
| restore_config(options) |
| raise |
| if successes: |
| log_line('{} passed'.format(' '.join(successes)), color=colors.bold_green) |
| if failures: |
| log_line('{} FAILED'.format(' '.join(failures)), color=colors.bold_red) |
| return False |
| else: |
| return True |
| |
| def main(): |
| try: |
| parser = argparse.ArgumentParser( |
| formatter_class=argparse.RawDescriptionHelpFormatter, |
| description= |
| "Test Mbed TLS with a subset of algorithms.\n\n" |
| "Example usage:\n" |
| r"./tests/scripts/depends.py \!MBEDTLS_SHA1_C MBEDTLS_SHA256_C""\n" |
| "./tests/scripts/depends.py MBEDTLS_AES_C hashes\n" |
| "./tests/scripts/depends.py cipher_id cipher_chaining\n") |
| parser.add_argument('--color', metavar='WHEN', |
| help='Colorize the output (always/auto/never)', |
| choices=['always', 'auto', 'never'], default='auto') |
| parser.add_argument('-c', '--config', metavar='FILE', |
| help='Configuration file to modify', |
| default='include/mbedtls/mbedtls_config.h') |
| parser.add_argument('-C', '--directory', metavar='DIR', |
| help='Change to this directory before anything else', |
| default='.') |
| parser.add_argument('-k', '--keep-going', |
| help='Try all configurations even if some fail (default)', |
| action='store_true', dest='keep_going', default=True) |
| parser.add_argument('-e', '--no-keep-going', |
| help='Stop as soon as a configuration fails', |
| action='store_false', dest='keep_going') |
| parser.add_argument('--list-jobs', |
| help='List supported jobs and exit', |
| action='append_const', dest='list', const='jobs') |
| parser.add_argument('--list-domains', |
| help='List supported domains and exit', |
| action='append_const', dest='list', const='domains') |
| parser.add_argument('--make-command', metavar='CMD', |
| help='Command to run instead of make (e.g. gmake)', |
| action='store', default='make') |
| parser.add_argument('--unset-use-psa', |
| help='Unset MBEDTLS_USE_PSA_CRYPTO before any test', |
| action='store_true', dest='unset_use_psa') |
| parser.add_argument('tasks', metavar='TASKS', nargs='*', |
| help='The domain(s) or job(s) to test (default: all).', |
| default=True) |
| options = parser.parse_args() |
| os.chdir(options.directory) |
| conf = config.ConfigFile(options.config) |
| domain_data = DomainData(options, conf) |
| |
| if options.tasks is True: |
| options.tasks = sorted(domain_data.domains.keys()) |
| if options.list: |
| for arg in options.list: |
| for domain_name in sorted(getattr(domain_data, arg).keys()): |
| print(domain_name) |
| sys.exit(0) |
| else: |
| sys.exit(0 if run_tests(options, domain_data, conf) else 1) |
| except Exception: # pylint: disable=broad-except |
| traceback.print_exc() |
| sys.exit(3) |
| |
| if __name__ == '__main__': |
| main() |