Improve depends.py structrue

Apply most improvements suggested by pylint.
Use config.py instead of config.pl.
Signed-off-by: Andrzej Kurek <andrzej.kurek@arm.com>
This commit is contained in:
Andrzej Kurek 2022-10-04 15:02:41 -04:00
parent 0e8b2d74f0
commit 3322c22087

View file

@ -28,7 +28,7 @@ import subprocess
import sys import sys
import traceback import traceback
class Colors: class Colors: # pylint: disable=too-few-public-methods
"""Minimalistic support for colored output. """Minimalistic support for colored output.
Each field of an object of this class is either None if 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 is not possible or not desired, or a pair of strings (start, stop) such
@ -39,6 +39,7 @@ stop switches the text color back to the default."""
bold_red = None bold_red = None
bold_green = None bold_green = None
def __init__(self, options=None): def __init__(self, options=None):
"""Initialize color profile according to passed options."""
if not options or options.color in ['no', 'never']: if not options or options.color in ['no', 'never']:
want_color = False want_color = False
elif options.color in ['yes', 'always']: elif options.color in ['yes', 'always']:
@ -56,7 +57,7 @@ NO_COLORS = Colors(None)
def log_line(text, prefix='depends.py:', suffix='', color=None): def log_line(text, prefix='depends.py:', suffix='', color=None):
"""Print a status message.""" """Print a status message."""
if color != None: if color is not None:
prefix = color[0] + prefix prefix = color[0] + prefix
suffix = suffix + color[1] suffix = suffix + color[1]
sys.stderr.write(prefix + ' ' + text + suffix + '\n') sys.stderr.write(prefix + ' ' + text + suffix + '\n')
@ -86,14 +87,35 @@ Remove the backup file if it was saved earlier."""
shutil.copy(options.config_backup, options.config) shutil.copy(options.config_backup, options.config)
def run_config_pl(options, args): def run_config_pl(options, args):
"""Run scripts/config.pl with the specified arguments.""" """Run scripts/config.py with the specified arguments."""
cmd = ['scripts/config.pl'] cmd = ['scripts/config.py']
if options.config != 'include/mbedtls/mbedtls_config.h': if options.config != 'include/mbedtls/mbedtls_config.h':
cmd += ['--file', options.config] cmd += ['--file', options.config]
cmd += args cmd += args
log_command(cmd) log_command(cmd)
subprocess.check_call(cmd) subprocess.check_call(cmd)
def set_reference_config(options):
"""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 memory management options that are not relevant to
# the tests and slow them down.
run_config_pl(options, ['full'])
run_config_pl(options, ['unset', 'MBEDTLS_MEMORY_BACKTRACE'])
run_config_pl(options, ['unset', 'MBEDTLS_MEMORY_BUFFER_ALLOC_C'])
run_config_pl(options, ['unset', 'MBEDTLS_MEMORY_DEBUG'])
def collect_config_symbols(options):
"""Read the list of settings from mbedtls_config.h.
Return them in a generator."""
with open(options.config, encoding="utf-8") as config_file:
rx = re.compile(r'\s*(?://\s*)?#define\s+(\w+)\s*(?:$|/[/*])')
for line in config_file:
m = re.match(rx, line)
if m:
yield m.group(1)
class Job: class Job:
"""A job builds the library in a specific configuration and runs some tests.""" """A job builds the library in a specific configuration and runs some tests."""
def __init__(self, name, config_settings, commands): def __init__(self, name, config_settings, commands):
@ -122,21 +144,10 @@ If what is False, announce that the job has failed.'''
else: else:
log_line('starting ' + self.name) log_line('starting ' + self.name)
def set_reference_config(self, options):
"""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 memory management options that are not relevant to
# the tests and slow them down.
run_config_pl(options, ['full'])
run_config_pl(options, ['unset', 'MBEDTLS_MEMORY_BACKTRACE'])
run_config_pl(options, ['unset', 'MBEDTLS_MEMORY_BUFFER_ALLOC_C'])
run_config_pl(options, ['unset', 'MBEDTLS_MEMORY_DEBUG'])
def configure(self, options): def configure(self, options):
'''Set library configuration options as required for the job. '''Set library configuration options as required for the job.
config_file_name indicates which file to modify.''' config_file_name indicates which file to modify.'''
self.set_reference_config(options) set_reference_config(options)
for key, value in sorted(self.config_settings.items()): for key, value in sorted(self.config_settings.items()):
if value is True: if value is True:
args = ['set', key] args = ['set', key]
@ -267,11 +278,7 @@ An option O is turned off if config_settings[O] is False."""
for dep in reverse_dependencies.get(key, []): for dep in reverse_dependencies.get(key, []):
config_settings[dep] = False config_settings[dep] = False
class Domain: class ExclusiveDomain: # pylint: disable=too-few-public-methods
"""A domain is a set of jobs that all relate to a particular configuration aspect."""
pass
class ExclusiveDomain(Domain):
"""A domain consisting of a set of conceptually-equivalent settings. """A domain consisting of a set of conceptually-equivalent settings.
Establish a list of configuration symbols. For each symbol, run a test job Establish a list of configuration symbols. For each symbol, run a test job
with this symbol set and the others unset, and a test job with this symbol with this symbol set and the others unset, and a test job with this symbol
@ -301,7 +308,7 @@ would match this regular expression."""
job = Job(description, config_settings, commands) job = Job(description, config_settings, commands)
self.jobs.append(job) self.jobs.append(job)
class ComplementaryDomain: class ComplementaryDomain: # pylint: disable=too-few-public-methods
"""A domain consisting of a set of loosely-related settings. """A domain consisting of a set of loosely-related settings.
Establish a list of configuration symbols. For each symbol, run a test job Establish a list of configuration symbols. For each symbol, run a test job
with this symbol unset.""" with this symbol unset."""
@ -317,28 +324,18 @@ Each job runs the specified commands."""
job = Job(description, config_settings, commands) job = Job(description, config_settings, commands)
self.jobs.append(job) self.jobs.append(job)
class CipherInfo: class CipherInfo: # pylint: disable=too-few-public-methods
"""Collect data about cipher.h.""" """Collect data about cipher.h."""
def __init__(self, options): def __init__(self):
self.base_symbols = set() self.base_symbols = set()
with open('include/mbedtls/cipher.h') as fh: with open('include/mbedtls/cipher.h', encoding="utf-8") as fh:
for line in fh: for line in fh:
m = re.match(r' *MBEDTLS_CIPHER_ID_(\w+),', line) m = re.match(r' *MBEDTLS_CIPHER_ID_(\w+),', line)
if m and m.group(1) not in ['NONE', 'NULL', '3DES']: if m and m.group(1) not in ['NONE', 'NULL', '3DES']:
self.base_symbols.add('MBEDTLS_' + m.group(1) + '_C') self.base_symbols.add('MBEDTLS_' + m.group(1) + '_C')
class DomainData: class DomainData:
"""Collect data about the library.""" """A container for domains and jobs, used to structurize testing."""
def collect_config_symbols(self, options):
"""Read the list of settings from mbedtls_config.h.
Return them in a generator."""
with open(options.config) as config_file:
rx = re.compile(r'\s*(?://\s*)?#define\s+(\w+)\s*(?:$|/[/*])')
for line in config_file:
m = re.match(rx, line)
if m:
yield m.group(1)
def config_symbols_matching(self, regexp): def config_symbols_matching(self, regexp):
"""List the mbedtls_config.h settings matching regexp.""" """List the mbedtls_config.h settings matching regexp."""
return [symbol for symbol in self.all_config_symbols return [symbol for symbol in self.all_config_symbols
@ -348,7 +345,7 @@ Return them in a generator."""
"""Gather data about the library and establish a list of domains to test.""" """Gather data about the library and establish a list of domains to test."""
build_command = [options.make_command, 'CFLAGS=-Werror'] build_command = [options.make_command, 'CFLAGS=-Werror']
build_and_test = [build_command, [options.make_command, 'test']] build_and_test = [build_command, [options.make_command, 'test']]
self.all_config_symbols = set(self.collect_config_symbols(options)) self.all_config_symbols = set(collect_config_symbols(options))
# Find hash modules by name. # Find hash modules by name.
hash_symbols = self.config_symbols_matching(r'MBEDTLS_(MD|RIPEMD|SHA)[0-9]+_C\Z') hash_symbols = self.config_symbols_matching(r'MBEDTLS_(MD|RIPEMD|SHA)[0-9]+_C\Z')
# Find elliptic curve enabling macros by name. # Find elliptic curve enabling macros by name.
@ -359,7 +356,7 @@ Return them in a generator."""
# and padding modes are exercised separately) information by parsing # and padding modes are exercised separately) information by parsing
# cipher.h, as the information is not readily available in mbedtls_config.h. # cipher.h, as the information is not readily available in mbedtls_config.h.
cipher_info = CipherInfo(options) cipher_info = CipherInfo()
# Find block cipher chaining and padding mode enabling macros by name. # 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_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') cipher_padding_symbols = self.config_symbols_matching(r'MBEDTLS_CIPHER_PADDING_\w+\Z')
@ -377,7 +374,8 @@ Return them in a generator."""
# hash which is obsolete. Run the test suites. Exclude # hash which is obsolete. Run the test suites. Exclude
# SHA512 and SHA256, as these are tested with SHA384 and SHA224. # SHA512 and SHA256, as these are tested with SHA384 and SHA224.
'hashes': ExclusiveDomain(hash_symbols, build_and_test, 'hashes': ExclusiveDomain(hash_symbols, build_and_test,
exclude=r'MBEDTLS_(MD|RIPEMD|SHA1_|SHA256_|SHA512_)|!MBEDTLS_(SHA256_|SHA512_)'), exclude=r'MBEDTLS_(MD|RIPEMD|SHA1_|SHA256_|SHA512_)\
|!MBEDTLS_(SHA256_|SHA512_)'),
# Key exchange types. Only build the library and the sample # Key exchange types. Only build the library and the sample
# programs. # programs.
'kex': ExclusiveDomain(key_exchange_symbols, 'kex': ExclusiveDomain(key_exchange_symbols,
@ -413,7 +411,7 @@ def run(options, job, colors=NO_COLORS):
job.announce(colors, success) job.announce(colors, success)
return success return success
def main(options, domain_data): def run_tests(options, domain_data):
"""Run the desired jobs. """Run the desired jobs.
domain_data should be a DomainData instance that describes the available domain_data should be a DomainData instance that describes the available
domains and jobs. domains and jobs.
@ -453,8 +451,7 @@ Run the jobs listed in options.domains."""
else: else:
return True return True
def main():
if __name__ == '__main__':
try: try:
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--color', metavar='WHEN', parser.add_argument('--color', metavar='WHEN',
@ -482,22 +479,24 @@ if __name__ == '__main__':
help='Command to run instead of make (e.g. gmake)', help='Command to run instead of make (e.g. gmake)',
action='store', default='make') action='store', default='make')
parser.add_argument('domains', metavar='DOMAIN', nargs='*', parser.add_argument('domains', metavar='DOMAIN', nargs='*',
help='The domain(s) to test (default: all). This can be also a list of jobs to run.', help='The domain(s) to test (default: all). This can \
be also a list of jobs to run.',
default=True) default=True)
options = parser.parse_args() options = parser.parse_args()
os.chdir(options.directory) os.chdir(options.directory)
domain_data = DomainData(options) domain_data = DomainData(options)
if options.domains == True: if options.domains is True:
options.domains = sorted(domain_data.domains.keys()) options.domains = sorted(domain_data.domains.keys())
if options.list: if options.list:
for what in options.list: for arg in options.list:
for key in sorted(getattr(domain_data, what).keys()): for domain_name in sorted(getattr(domain_data, arg).keys()):
print(key) print(domain_name)
exit(0) sys.exit(0)
else: else:
sys.exit(0 if main(options, domain_data) else 1) sys.exit(0 if run_tests(options, domain_data) else 1)
except SystemExit: except Exception: # pylint: disable=broad-except
raise
except:
traceback.print_exc() traceback.print_exc()
exit(3) sys.exit(3)
if __name__ == '__main__':
main()