-
Notifications
You must be signed in to change notification settings - Fork 146
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2000a51
commit 091d30a
Showing
3 changed files
with
189 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD | ||
# SPDX-License-Identifier: Apache-2.0 | ||
import json | ||
import os | ||
import re | ||
import subprocess | ||
from collections import namedtuple | ||
|
||
from pycparser import c_ast, c_parser, preprocess_file | ||
|
||
Param = namedtuple('Param', ['ptr', 'array', 'qual', 'type', 'name']) | ||
|
||
class FunctionVisitor(c_ast.NodeVisitor): | ||
def __init__(self, header): | ||
self.function_prototypes = {} | ||
self.ptr = 0 | ||
self.array = 0 | ||
self.content = open(header, 'r').read() | ||
|
||
def get_type(self, node, suffix='param'): | ||
if suffix == 'param': | ||
self.ptr = 0 | ||
self.array = 0 | ||
|
||
if isinstance(node.type, c_ast.TypeDecl): | ||
typename = node.type.declname | ||
quals = '' | ||
if node.type.quals: | ||
quals = ' '.join(node.type.quals) | ||
if node.type.type.names: | ||
type = node.type.type.names[0] | ||
return quals, type, typename | ||
if isinstance(node.type, c_ast.PtrDecl): | ||
quals, type, name = self.get_type(node.type, 'ptr') | ||
self.ptr += 1 | ||
return quals, type, name | ||
|
||
if isinstance(node.type, c_ast.ArrayDecl): | ||
quals, type, name = self.get_type(node.type, 'array') | ||
self.array = int(node.type.dim.value) | ||
return quals, type, name | ||
|
||
def visit_FuncDecl(self, node): | ||
if isinstance(node.type, c_ast.TypeDecl): | ||
func_name = node.type.declname | ||
if func_name.startswith('esp_wifi') and func_name in self.content: | ||
ret = node.type.type.names[0] | ||
args = [] | ||
for param in node.args.params: | ||
quals, type, name = self.get_type(param) | ||
param = Param(ptr=self.ptr, array=self.array, qual=quals, type=type, name=name) | ||
args.append(param) | ||
self.function_prototypes[func_name] = (ret, args) | ||
|
||
# Parse the header file and extract function prototypes | ||
def extract_function_prototypes(header_code, header): | ||
parser = c_parser.CParser() # Set debug parameter to False | ||
ast = parser.parse(header_code) | ||
visitor = FunctionVisitor(header) | ||
visitor.visit(ast) | ||
return visitor.function_prototypes | ||
|
||
def preprocess_header(header_file): | ||
with open(header_file, 'r') as file: | ||
preprocessed_code = preprocess_file(header_file) | ||
return preprocessed_code | ||
|
||
# # Parse the preprocessed header file and extract function prototypes | ||
# def extract_function_prototypes(header_code): | ||
|
||
# Generate test cases for each function | ||
def generate_test_cases(): | ||
test_cases = {} | ||
for func_name, args in function_prototypes.items(): | ||
test_args = [] | ||
for arg_info in args: | ||
if isinstance(arg_info, c_ast.TypeDecl): | ||
print(arg_info.type.names[0]) | ||
arg_name = arg_info.declname | ||
arg_type = 'int' # Default to int type if type is missing | ||
else: | ||
arg_name, arg_type = arg_info | ||
test_args.append((arg_name, generate_test_argument(arg_type))) | ||
test_cases[func_name] = test_args | ||
return test_cases | ||
|
||
# Generate test argument based on type | ||
def generate_test_argument(arg_type): | ||
if isinstance(arg_type, c_ast.TypeDecl): | ||
return generate_test_argument(arg_type.type) | ||
elif isinstance(arg_type, c_ast.PtrDecl): | ||
return None # Placeholder for pointer argument handling | ||
elif isinstance(arg_type, c_ast.IdentifierType): | ||
if 'int' in arg_type.names: | ||
return 1 # Example integer argument | ||
elif 'float' in arg_type.names: | ||
return 1.0 # Example float argument | ||
# Add more cases as needed for other data types | ||
return None # Unknown type | ||
|
||
def generate_forwarding_c_file(prefix): | ||
with open('forwarding.c', 'w') as file: | ||
# Write includes and any other necessary declarations | ||
file.write('#include "example.h"\n\n') | ||
|
||
# Write forwarding function definitions | ||
for func_name, args in function_prototypes.items(): | ||
# Write the forwarding function declaration | ||
file.write(f'{function_prototypes[func_name][0]} {prefix}_{func_name}(') | ||
file.write(') {\n') | ||
|
||
# Write the forwarding call | ||
file.write(f' return {func_name}(') | ||
file.write(');\n}\n\n') | ||
|
||
def exec_cmd(what, out_file): | ||
p = subprocess.Popen(what, stdin=subprocess.PIPE, stdout=out_file, stderr=subprocess.PIPE) | ||
output_b, err_b = p.communicate() | ||
rc = p.returncode | ||
output: str = output_b.decode('utf-8') if output_b is not None else '' | ||
err: str = err_b.decode('utf-8') if err_b is not None else '' | ||
return rc, output, err, ' '.join(what) | ||
|
||
|
||
def preprocess(idf_path, header): | ||
project_dir = os.path.join(idf_path, 'examples', 'get-started', 'blink') | ||
build_dir = os.path.join(project_dir, 'build') | ||
build_commands_json = os.path.join(build_dir, 'compile_commands.json') | ||
with open(build_commands_json, 'r', encoding='utf-8') as f: | ||
build_command = json.load(f)[0]['command'].split() | ||
include_dir_flags = [] | ||
include_dirs = [] | ||
# process compilation flags (includes and defines) | ||
for item in build_command: | ||
if item.startswith('-I'): | ||
include_dir_flags.append(item) | ||
if 'components' in item: | ||
include_dirs.append(item[2:]) # Removing the leading "-I" | ||
if item.startswith('-D'): | ||
include_dir_flags.append(item.replace('\\','')) # removes escaped quotes, eg: -DMBEDTLS_CONFIG_FILE=\\\"mbedtls/esp_config.h\\\" | ||
include_dir_flags.append('-I' + os.path.join(build_dir, 'config')) | ||
temp_file = 'esp_wifi_preprocessed.h' | ||
with open(temp_file, 'w') as f: | ||
f.write('#define asm\n') | ||
f.write('#define volatile\n') | ||
f.write('#define __asm__\n') | ||
f.write('#define __volatile__\n') | ||
with open(temp_file, 'a') as f: | ||
rc, out, err, cmd = exec_cmd(['xtensa-esp32-elf-gcc', '-w', '-P', '-include', 'ignore_extensions.h', '-E', header] + include_dir_flags, f) | ||
print(err) | ||
preprocessed_code = preprocess_file(temp_file) | ||
return preprocessed_code | ||
|
||
# with open('example.h', 'w') as f: | ||
# riscv32-esp-elf-gcc | ||
|
||
if __name__ == '__main__': | ||
idf_path = os.getenv('IDF_PATH') | ||
if idf_path is None: | ||
raise RuntimeError("Environment variable 'IDF_PATH' wasn't set.") | ||
header = os.path.join(idf_path, 'components', 'esp_wifi', 'include', 'esp_wifi.h') | ||
function_prototypes = extract_function_prototypes(preprocess(idf_path, header), header) | ||
|
||
for func_name, args in function_prototypes.items(): | ||
params = [] | ||
for param in args[1]: | ||
typename=param.type | ||
if typename == 'void': | ||
params.append(f'{typename}') | ||
continue | ||
if param.qual != '': | ||
typename = f'{param.qual} '+ typename | ||
declname = param.name | ||
if param.ptr > 0: | ||
declname = '*'*param.ptr + declname | ||
if param.array > 0: | ||
declname += f'[{param.array}]' | ||
params.append(f'{typename} {declname}') | ||
arguments = ', '.join(params) | ||
print(f'{args[0]} {func_name}({arguments});') | ||
|
||
|
||
# for func_name, args in test_cases.items(): | ||
|
||
# for func_name, args in test_cases.items(): |