add enum value to string helpers
Only add helpers for enum in `ssl.h`. Signed-off-by: Jerry Yu <jerry.h.yu@arm.com>
This commit is contained in:
parent
1dc3c4553d
commit
e78ee99624
2
library/.gitignore
vendored
2
library/.gitignore
vendored
@ -6,3 +6,5 @@ libmbed*
|
||||
# Automatically generated files
|
||||
/error.c
|
||||
/version_features.c
|
||||
/ssl_debug_helpers_generated.c
|
||||
/ssl_debug_helpers_generated.h
|
||||
|
@ -77,6 +77,7 @@ set(src_crypto
|
||||
sha1.c
|
||||
sha256.c
|
||||
sha512.c
|
||||
ssl_debug_helpers_generated.c
|
||||
threading.c
|
||||
timing.c
|
||||
version.c
|
||||
|
@ -24,6 +24,12 @@ endif
|
||||
|
||||
PERL ?= perl
|
||||
|
||||
ifdef WINDOWS
|
||||
PYTHON ?= python
|
||||
else
|
||||
PYTHON ?= $(shell if type python3 >/dev/null 2>/dev/null; then echo python3; else echo python; fi)
|
||||
endif
|
||||
|
||||
# if were running on Windows build for Windows
|
||||
ifdef WINDOWS
|
||||
WINDOWS_BUILD=1
|
||||
@ -136,6 +142,7 @@ OBJS_CRYPTO= \
|
||||
sha1.o \
|
||||
sha256.o \
|
||||
sha512.o \
|
||||
ssl_debug_helpers_generated.o \
|
||||
threading.o \
|
||||
timing.o \
|
||||
version.o \
|
||||
@ -281,7 +288,7 @@ libmbedcrypto.dll: $(OBJS_CRYPTO)
|
||||
$(CC) $(LOCAL_CFLAGS) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
.PHONY: generated_files
|
||||
GENERATED_FILES = error.c version_features.c
|
||||
GENERATED_FILES = error.c version_features.c ssl_debug_helpers_generated.c
|
||||
generated_files: $(GENERATED_FILES)
|
||||
|
||||
error.c: ../scripts/generate_errors.pl
|
||||
@ -291,6 +298,12 @@ error.c:
|
||||
echo " Gen $@"
|
||||
$(PERL) ../scripts/generate_errors.pl
|
||||
|
||||
ssl_debug_helpers_generated.c: ../scripts/generate_ssl_debug_helpers.py
|
||||
ssl_debug_helpers_generated.c: $(filter-out %config%,$(wildcard ../include/mbedtls/*.h))
|
||||
ssl_debug_helpers_generated.c:
|
||||
echo " Gen $@"
|
||||
$(PYTHON) ../scripts/generate_ssl_debug_helpers.py
|
||||
|
||||
version_features.c: ../scripts/generate_features.pl
|
||||
version_features.c: ../scripts/data_files/version_features.fmt
|
||||
## The generated file only depends on the options that are present in mbedtls_config.h,
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "ssl_misc.h"
|
||||
#include "ecdh_misc.h"
|
||||
#include "ssl_tls13_keys.h"
|
||||
#include "ssl_debug_helpers_generated.h"
|
||||
|
||||
/* Write extensions */
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "mbedtls/debug.h"
|
||||
|
||||
#include "ssl_misc.h"
|
||||
#include "ssl_debug_helpers_generated.h"
|
||||
|
||||
int mbedtls_ssl_tls13_handshake_server_step( mbedtls_ssl_context *ssl )
|
||||
{
|
||||
|
310
scripts/generate_ssl_debug_helpers.py
Executable file
310
scripts/generate_ssl_debug_helpers.py
Executable file
@ -0,0 +1,310 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""Generate library/ssl_debug_helps_generated.c
|
||||
|
||||
The code generated by this module includes debug helper functions that can not be
|
||||
implemented by fixed codes.
|
||||
|
||||
"""
|
||||
|
||||
# Copyright The Mbed TLS Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
import textwrap
|
||||
from mbedtls_dev import build_tree
|
||||
|
||||
def remove_c_comments(string):
|
||||
"""
|
||||
Remove C style comments from input string
|
||||
"""
|
||||
string_pattern = r"(?P<string>\".*?\"|\'.*?\')"
|
||||
comment_pattern = r"(?P<comment>/\*.*?\*/|//[^\r\n]*$)"
|
||||
pattern = re.compile(string_pattern + r'|' + comment_pattern,
|
||||
re.MULTILINE|re.DOTALL)
|
||||
def replacer(match):
|
||||
if match.lastgroup == 'comment':
|
||||
return ""
|
||||
return match.group()
|
||||
return pattern.sub(replacer, string)
|
||||
|
||||
class CondDirectiveNotMatch(Exception):
|
||||
pass
|
||||
|
||||
def preprocesse_c_source_code(source, *classes):
|
||||
"""
|
||||
Simple preprocessor for C source code.
|
||||
|
||||
Only processs condition directives without expanding them.
|
||||
Yield object accodring to the classes input. Most match firstly
|
||||
|
||||
If there are directive pair does not match, raise CondDirectiveNotMatch.
|
||||
|
||||
Assume source code does not include comments and compile pass.
|
||||
|
||||
"""
|
||||
|
||||
pattern = re.compile(r"^[ \t]*#[ \t]*" +
|
||||
r"(?P<directive>(if[ \t]|ifndef[ \t]|ifdef[ \t]|else|endif))" +
|
||||
r"[ \t]*(?P<param>(.*\\\n)*.*$)",
|
||||
re.MULTILINE)
|
||||
stack = []
|
||||
|
||||
def _yield_objects(s, d, p, st, end):
|
||||
"""
|
||||
Output matched source piece
|
||||
"""
|
||||
nonlocal stack
|
||||
start_line, end_line = '', ''
|
||||
if stack:
|
||||
start_line = '#{} {}'.format(d, p)
|
||||
if d == 'if':
|
||||
end_line = '#endif /* {} */'.format(p)
|
||||
elif d == 'ifdef':
|
||||
end_line = '#endif /* defined({}) */'.format(p)
|
||||
else:
|
||||
end_line = '#endif /* !defined({}) */'.format(p)
|
||||
has_instance = False
|
||||
for cls in classes:
|
||||
for instance in cls.extract(s, st, end):
|
||||
if has_instance is False:
|
||||
has_instance = True
|
||||
yield pair_start, start_line
|
||||
yield instance.span()[0], instance
|
||||
if has_instance:
|
||||
yield start, end_line
|
||||
|
||||
for match in pattern.finditer(source):
|
||||
|
||||
directive = match.groupdict()['directive'].strip()
|
||||
param = match.groupdict()['param']
|
||||
start, end = match.span()
|
||||
|
||||
if directive in ('if', 'ifndef', 'ifdef'):
|
||||
stack.append((directive, param, start, end))
|
||||
continue
|
||||
|
||||
if not stack:
|
||||
raise CondDirectiveNotMatch()
|
||||
|
||||
pair_directive, pair_param, pair_start, pair_end = stack.pop()
|
||||
yield from _yield_objects(source,
|
||||
pair_directive,
|
||||
pair_param,
|
||||
pair_end,
|
||||
start)
|
||||
|
||||
if directive == 'endif':
|
||||
continue
|
||||
|
||||
if pair_directive == 'if':
|
||||
directive = 'if'
|
||||
param = "!( {} )".format(pair_param)
|
||||
elif pair_directive == 'ifdef':
|
||||
directive = 'ifndef'
|
||||
param = pair_param
|
||||
else:
|
||||
directive = 'ifdef'
|
||||
param = pair_param
|
||||
|
||||
stack.append((directive, param, start, end))
|
||||
assert not stack, len(stack)
|
||||
|
||||
|
||||
|
||||
class EnumDefinition:
|
||||
"""
|
||||
Generate helper functions around enumeration.
|
||||
|
||||
Currently, it generate tranlation function from enum value to string.
|
||||
Enum definition looks like:
|
||||
[typedef] enum [prefix name] { [body] } [suffix name];
|
||||
|
||||
Known limitation:
|
||||
- the '}' and ';' SHOULD NOT exist in different macro blocks. Like
|
||||
```
|
||||
enum test {
|
||||
....
|
||||
#if defined(A)
|
||||
....
|
||||
};
|
||||
#else
|
||||
....
|
||||
};
|
||||
#endif
|
||||
```
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def extract(cls, source_code, start=0, end=-1):
|
||||
enum_pattern = re.compile(r'enum\s*(?P<prefix_name>\w*)\s*' +
|
||||
r'{\s*(?P<body>[^}]*)}' +
|
||||
r'\s*(?P<suffix_name>\w*)\s*;',
|
||||
re.MULTILINE|re.DOTALL)
|
||||
|
||||
for match in enum_pattern.finditer(source_code, start, end):
|
||||
yield EnumDefinition(source_code,
|
||||
span=match.span(),
|
||||
group=match.groupdict())
|
||||
|
||||
def __init__(self, source_code, span=None, group=None):
|
||||
assert isinstance(group, dict)
|
||||
prefix_name = group.get('prefix_name', None)
|
||||
suffix_name = group.get('suffix_name', None)
|
||||
body = group.get('body', None)
|
||||
assert prefix_name or suffix_name
|
||||
assert body
|
||||
assert span
|
||||
# If suffix_name exists, it is a typedef
|
||||
self._prototype = suffix_name if suffix_name else 'enum ' + prefix_name
|
||||
self._name = suffix_name if suffix_name else prefix_name
|
||||
self._body = body
|
||||
self._source = source_code
|
||||
self._span = span
|
||||
|
||||
def __repr__(self):
|
||||
return 'Enum({},{})'.format(self._name, self._span)
|
||||
|
||||
def __str__(self):
|
||||
return repr(self)
|
||||
|
||||
def span(self):
|
||||
return self._span
|
||||
|
||||
def generate_tranlation_function(self):
|
||||
"""
|
||||
Generate function for translating value to string
|
||||
"""
|
||||
translation_table = []
|
||||
|
||||
for line in self._body.splitlines():
|
||||
|
||||
if line.strip().startswith('#'):
|
||||
# Preprocess directive, keep it in table
|
||||
translation_table.append(line.strip())
|
||||
continue
|
||||
|
||||
if not line.strip():
|
||||
continue
|
||||
|
||||
for field in line.strip().split(','):
|
||||
if not field.strip():
|
||||
continue
|
||||
member = field.strip().split()[0]
|
||||
translation_table.append(
|
||||
'{space}[{member}] = "{member}",'.format(member=member,
|
||||
space=' '*8)
|
||||
)
|
||||
|
||||
body = textwrap.dedent('''\
|
||||
const char *{name}_str( {prototype} in )
|
||||
{{
|
||||
const char * in_to_str[]=
|
||||
{{
|
||||
{translation_table}
|
||||
}};
|
||||
|
||||
if( in > ( sizeof( in_to_str )/sizeof( in_to_str[0]) - 1 ) ||
|
||||
in_to_str[ in ] == NULL )
|
||||
{{
|
||||
return "UNKOWN_VAULE";
|
||||
}}
|
||||
return in_to_str[ in ];
|
||||
}}
|
||||
''')
|
||||
body = body.format(translation_table='\n'.join(translation_table),
|
||||
name=self._name,
|
||||
prototype=self._prototype)
|
||||
prototype = 'const char *{name}_str( {prototype} in );\n'
|
||||
prototype = prototype.format(name=self._name,
|
||||
prototype=self._prototype)
|
||||
return body, prototype
|
||||
|
||||
OUTPUT_C_TEMPLATE = '''\
|
||||
/* Automatically generated by generate_ssl_debug_helpers.py. DO NOT EDIT. */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if defined(MBEDTLS_DEBUG_C)
|
||||
|
||||
#include "ssl_debug_helpers_generated.h"
|
||||
|
||||
{functions}
|
||||
|
||||
#endif /* MBEDTLS_DEBUG_C */
|
||||
/* End of automatically generated file. */
|
||||
|
||||
'''
|
||||
|
||||
OUTPUT_H_TEMPLATE = '''\
|
||||
/* Automatically generated by generate_ssl_debug_helpers.py. DO NOT EDIT. */
|
||||
#ifndef MBEDTLS_SSL_DEBUG_HELPERS_H
|
||||
#define MBEDTLS_SSL_DEBUG_HELPERS_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if defined(MBEDTLS_DEBUG_C)
|
||||
|
||||
#include "mbedtls/ssl.h"
|
||||
#include "ssl_misc.h"
|
||||
|
||||
{functions}
|
||||
|
||||
#endif /* MBEDTLS_DEBUG_C */
|
||||
|
||||
#endif /* SSL_DEBUG_HELPERS_H */
|
||||
|
||||
/* End of automatically generated file. */
|
||||
|
||||
'''
|
||||
|
||||
|
||||
def generate_ssl_debug_helpers(target_dir):
|
||||
"""
|
||||
Generate functions of debug helps
|
||||
"""
|
||||
with open('include/mbedtls/ssl.h') as f:
|
||||
source_code = remove_c_comments(f.read())
|
||||
|
||||
definitions = dict()
|
||||
prototypes = dict()
|
||||
for start, instance in preprocesse_c_source_code(source_code, EnumDefinition):
|
||||
if start in definitions:
|
||||
continue
|
||||
if isinstance(instance, EnumDefinition):
|
||||
definition, prototype = instance.generate_tranlation_function()
|
||||
else:
|
||||
definition = instance
|
||||
prototype = instance
|
||||
definitions[start] = definition
|
||||
prototypes[start] = prototype
|
||||
|
||||
functions = [str(v) for _, v in sorted(definitions.items())]
|
||||
with open(os.path.join(target_dir, 'ssl_debug_helpers_generated.c'), 'w') as f:
|
||||
f.write(OUTPUT_C_TEMPLATE.format(functions='\n'.join(functions)))
|
||||
|
||||
functions = [str(v) for _, v in sorted(prototypes.items())]
|
||||
with open(os.path.join(target_dir, 'ssl_debug_helpers_generated.h'), 'w') as f:
|
||||
f.write(OUTPUT_H_TEMPLATE.format(functions='\n'.join(functions)))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
build_tree.chdir_to_root()
|
||||
OUTPUT_FILE_DIR = sys.argv[1] if len(sys.argv) == 2 else "library"
|
||||
generate_ssl_debug_helpers(OUTPUT_FILE_DIR)
|
@ -4,6 +4,7 @@
|
||||
perl scripts\generate_errors.pl || exit /b 1
|
||||
perl scripts\generate_query_config.pl || exit /b 1
|
||||
perl scripts\generate_features.pl || exit /b 1
|
||||
python scripts\generate_ssl_debug_helpers.py || exit /b 1
|
||||
perl scripts\generate_visualc_files.pl || exit /b 1
|
||||
python scripts\generate_psa_constants.py || exit /b 1
|
||||
python tests\scripts\generate_psa_tests.py || exit /b 1
|
||||
|
@ -118,6 +118,7 @@ check()
|
||||
check scripts/generate_errors.pl library/error.c
|
||||
check scripts/generate_query_config.pl programs/test/query_config.c
|
||||
check scripts/generate_features.pl library/version_features.c
|
||||
check scripts/generate_ssl_debug_helpers.py library/ssl_debug_helpers_generated.c
|
||||
# generate_visualc_files enumerates source files (library/*.c). It doesn't
|
||||
# care about their content, but the files must exist. So it must run after
|
||||
# the step that creates or updates these files.
|
||||
|
Loading…
Reference in New Issue
Block a user