init comm для настройки переменных для отладки
This commit is contained in:
732
.out/scanVars0.py
Normal file
732
.out/scanVars0.py
Normal file
@@ -0,0 +1,732 @@
|
||||
# pyinstaller --onefile --distpath . --workpath ./build --specpath ./build scanVars.py
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# === Словарь соответствия типов XML → DebugVarType_t ===
|
||||
type_map = dict([
|
||||
*[(k, 'pt_int8') for k in ('signed char', 'char')],
|
||||
*[(k, 'pt_int16') for k in ('int', 'int16', 'short')],
|
||||
*[(k, 'pt_int32') for k in ('long', 'int32', '_iqx')],
|
||||
*[(k, 'pt_int64') for k in ('long long', 'int64')],
|
||||
|
||||
*[(k, 'pt_uint8') for k in ('unsigned char',)],
|
||||
*[(k, 'pt_uint16') for k in ('unsigned int', 'unsigned short', 'Uint16')],
|
||||
*[(k, 'pt_uint32') for k in ('unsigned long', 'Uint32')],
|
||||
*[(k, 'pt_uint64') for k in ('unsigned long long', 'Uint64')],
|
||||
|
||||
*[(k, 'pt_ptr_int8') for k in ('signed char*',)],
|
||||
*[(k, 'pt_ptr_int16') for k in ('int*', 'short*')],
|
||||
*[(k, 'pt_ptr_int32') for k in ('long*',)],
|
||||
*[(k, 'pt_ptr_uint8') for k in ('unsigned char*',)],
|
||||
*[(k, 'pt_ptr_uint16') for k in ('unsigned int*', 'unsigned short*')],
|
||||
*[(k, 'pt_ptr_uint32') for k in ('unsigned long*',)],
|
||||
('unsigned long long*', 'pt_int64'),
|
||||
|
||||
*[(k, 'pt_arr_int8') for k in ('signed char[]',)],
|
||||
*[(k, 'pt_arr_int16') for k in ('int[]', 'short[]')],
|
||||
*[(k, 'pt_arr_int32') for k in ('long[]',)],
|
||||
*[(k, 'pt_arr_uint8') for k in ('unsigned char[]',)],
|
||||
*[(k, 'pt_arr_uint16') for k in ('unsigned int[]', 'unsigned short[]')],
|
||||
*[(k, 'pt_arr_uint32') for k in ('unsigned long[]',)],
|
||||
|
||||
*[(k, 'pt_float') for k in ('float', 'float32')],
|
||||
|
||||
('struct', 'pt_struct'),
|
||||
('union', 'pt_union'),
|
||||
])
|
||||
|
||||
def parse_makefile(makefile_path):
|
||||
makefile_dir = os.path.dirname(makefile_path)
|
||||
project_root = os.path.dirname(makefile_dir) # поднялись из Debug
|
||||
|
||||
with open(makefile_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
objs_lines = []
|
||||
collecting = False
|
||||
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
if stripped.startswith("ORDERED_OBJS") and "+=" in stripped:
|
||||
parts = stripped.split("\\")
|
||||
first_part = parts[0]
|
||||
idx = first_part.find("+=")
|
||||
tail = first_part[idx+2:].strip()
|
||||
if tail:
|
||||
objs_lines.append(tail)
|
||||
collecting = True
|
||||
if len(parts) > 1:
|
||||
for p in parts[1:]:
|
||||
p = p.strip()
|
||||
if p:
|
||||
objs_lines.append(p)
|
||||
continue
|
||||
|
||||
if collecting:
|
||||
if stripped.endswith("\\"):
|
||||
objs_lines.append(stripped[:-1].strip())
|
||||
else:
|
||||
objs_lines.append(stripped)
|
||||
collecting = False
|
||||
|
||||
objs_str = ' '.join(objs_lines)
|
||||
|
||||
objs_str = re.sub(r"\$\([^)]+\)", "", objs_str)
|
||||
|
||||
objs = []
|
||||
for part in objs_str.split():
|
||||
part = part.strip()
|
||||
if part.startswith('"') and part.endswith('"'):
|
||||
part = part[1:-1]
|
||||
if part:
|
||||
objs.append(part)
|
||||
|
||||
c_files = []
|
||||
include_dirs = set()
|
||||
|
||||
for obj_path in objs:
|
||||
if "DebugTools" in obj_path:
|
||||
continue
|
||||
if "v120" in obj_path:
|
||||
continue
|
||||
|
||||
if obj_path.startswith("Debug\\") or obj_path.startswith("Debug/"):
|
||||
rel_path = obj_path.replace("Debug\\", "Src\\").replace("Debug/", "Src/")
|
||||
else:
|
||||
rel_path = obj_path
|
||||
|
||||
abs_path = os.path.normpath(os.path.join(project_root, rel_path))
|
||||
|
||||
root, ext = os.path.splitext(abs_path)
|
||||
if ext.lower() == ".obj":
|
||||
c_path = root + ".c"
|
||||
else:
|
||||
c_path = abs_path
|
||||
|
||||
# Сохраняем только .c файлы
|
||||
if c_path.lower().endswith(".c"):
|
||||
c_files.append(c_path)
|
||||
dir_path = os.path.dirname(c_path)
|
||||
if dir_path and "DebugTools" not in dir_path:
|
||||
include_dirs.add(dir_path)
|
||||
|
||||
return c_files, sorted(include_dirs)
|
||||
|
||||
# Шаблон для поиска глобальных переменных
|
||||
# Пример: int varname;
|
||||
# Пропускаем строки с static и функции
|
||||
VAR_PATTERN = re.compile(
|
||||
r'^\s*(?!static)(\w[\w\s\*]+)\s+(\w+)\s*(=\s*[^;]+)?\s*;',
|
||||
re.MULTILINE)
|
||||
EXTERN_PATTERN = re.compile(
|
||||
r'^\s*extern\s+[\w\s\*]+\s+(\w+)\s*;',
|
||||
re.MULTILINE)
|
||||
|
||||
|
||||
TYPEDEF_PATTERN = re.compile(
|
||||
r'typedef\s+(struct|union)?\s*(\w+)?\s*{[^}]*}\s*(\w+)\s*;', re.DOTALL)
|
||||
|
||||
TYPEDEF_SIMPLE_PATTERN = re.compile(
|
||||
r'typedef\s+(.+?)\s+(\w+)\s*;', re.DOTALL)
|
||||
|
||||
def map_type_to_pt(typename, varname):
|
||||
typename = typename.strip()
|
||||
|
||||
# Убираем const и volatile, где бы они ни были (например, "const volatile int")
|
||||
for qualifier in ('const', 'volatile'):
|
||||
typename = typename.replace(qualifier, '')
|
||||
|
||||
typename = typename.strip() # снова убрать лишние пробелы после удаления
|
||||
|
||||
# Раскрутка через typedef
|
||||
resolved_type = typedef_aliases.get(typename, typename)
|
||||
|
||||
# Прямая проверка
|
||||
if resolved_type in type_map:
|
||||
return type_map[resolved_type]
|
||||
|
||||
if resolved_type.startswith('struct'):
|
||||
return type_map['struct']
|
||||
if resolved_type.startswith('union'):
|
||||
return type_map['union']
|
||||
|
||||
if '_iq' in resolved_type and '_iqx' in type_map:
|
||||
return type_map['_iqx']
|
||||
|
||||
return 'pt_unknown'
|
||||
|
||||
def get_iq_define(vtype):
|
||||
if '_iq' in vtype:
|
||||
# Преобразуем _iqXX в t_iqXX
|
||||
return 't' + vtype[vtype.index('_iq'):]
|
||||
else:
|
||||
return 't_iq_none'
|
||||
|
||||
def get_files_by_ext(roots, exts):
|
||||
files = []
|
||||
for root in roots:
|
||||
for dirpath, _, filenames in os.walk(root):
|
||||
for f in filenames:
|
||||
if any(f.endswith(e) for e in exts):
|
||||
files.append(os.path.join(dirpath, f))
|
||||
return files
|
||||
|
||||
def read_file_try_encodings(filepath):
|
||||
for enc in ['utf-8', 'cp1251']:
|
||||
try:
|
||||
with open(filepath, 'r', encoding=enc) as f:
|
||||
content = f.read()
|
||||
content = strip_single_line_comments(content) # <=== ВАЖНО
|
||||
return content, enc
|
||||
except UnicodeDecodeError:
|
||||
continue
|
||||
raise UnicodeDecodeError(f"Не удалось прочитать файл {filepath} с кодировками utf-8 и cp1251")
|
||||
|
||||
FUNC_PATTERN = re.compile(
|
||||
r'\w[\w\s\*\(\),]*\([^;{)]*\)\s*\{(?:[^{}]*|\{[^}]*\})*?\}', re.DOTALL)
|
||||
|
||||
def strip_single_line_comments(code):
|
||||
# Удалим // ... до конца строки
|
||||
return re.sub(r'//.*?$', '', code, flags=re.MULTILINE)
|
||||
|
||||
def remove_function_bodies(code):
|
||||
|
||||
result = []
|
||||
i = 0
|
||||
length = len(code)
|
||||
while i < length:
|
||||
match = re.search(r'\b[\w\s\*\(\),]*\([^;{}]*\)\s*\{', code[i:])
|
||||
if not match:
|
||||
result.append(code[i:])
|
||||
break
|
||||
|
||||
start = i + match.start()
|
||||
brace_start = i + match.end() - 1
|
||||
result.append(code[i:start]) # Добавляем всё до функции
|
||||
|
||||
# Ищем конец функции по уровню вложенности скобок
|
||||
brace_level = 1
|
||||
j = brace_start + 1
|
||||
in_string = False
|
||||
while j < length and brace_level > 0:
|
||||
char = code[j]
|
||||
|
||||
if char == '"' or char == "'":
|
||||
quote = char
|
||||
j += 1
|
||||
while j < length and code[j] != quote:
|
||||
if code[j] == '\\':
|
||||
j += 2
|
||||
else:
|
||||
j += 1
|
||||
elif code[j] == '{':
|
||||
brace_level += 1
|
||||
elif code[j] == '}':
|
||||
brace_level -= 1
|
||||
j += 1
|
||||
|
||||
# Заменяем тело функции пробелами той же длины
|
||||
result.append(' ' * (j - start))
|
||||
i = j
|
||||
|
||||
return ''.join(result)
|
||||
|
||||
def extract_struct_definitions_from_file(filepath):
|
||||
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
content = f.read()
|
||||
|
||||
# Удаляем комментарии
|
||||
content = re.sub(r'//.*', '', content)
|
||||
content = re.sub(r'/\*.*?\*/', '', content, flags=re.DOTALL)
|
||||
|
||||
type_definitions = {}
|
||||
anonymous_counter = [0]
|
||||
|
||||
def split_fields(body):
|
||||
"""
|
||||
Разбивает тело struct/union на поля с учётом вложенных { }.
|
||||
Возвращает список строк - полей (без ;).
|
||||
"""
|
||||
fields = []
|
||||
start = 0
|
||||
brace_level = 0
|
||||
for i, ch in enumerate(body):
|
||||
if ch == '{':
|
||||
brace_level += 1
|
||||
elif ch == '}':
|
||||
brace_level -= 1
|
||||
elif ch == ';' and brace_level == 0:
|
||||
fields.append(body[start:i].strip())
|
||||
start = i + 1
|
||||
# если что-то осталось после последнего ;
|
||||
tail = body[start:].strip()
|
||||
if tail:
|
||||
fields.append(tail)
|
||||
return fields
|
||||
|
||||
def parse_fields(body):
|
||||
fields = {}
|
||||
body = body.strip('{} \n\t')
|
||||
field_strings = split_fields(body)
|
||||
for field_str in field_strings:
|
||||
if field_str.startswith(('struct ', 'union ')):
|
||||
# Вложенный struct/union
|
||||
# Пытаемся найти имя и тело
|
||||
m = re.match(r'(struct|union)\s*(\w*)\s*({.*})\s*(\w+)?', field_str, re.DOTALL)
|
||||
if m:
|
||||
kind, tag, inner_body, varname = m.groups()
|
||||
if not varname:
|
||||
# Анонимная вложенная структура/объединение
|
||||
varname = f"__anon_{anonymous_counter[0]}"
|
||||
anonymous_counter[0] += 1
|
||||
type_definitions[varname] = parse_fields(inner_body)
|
||||
fields[varname] = varname
|
||||
else:
|
||||
# Если есть имя переменной
|
||||
anon_type_name = f"__anon_{anonymous_counter[0]}"
|
||||
anonymous_counter[0] += 1
|
||||
type_definitions[anon_type_name] = parse_fields(inner_body)
|
||||
fields[varname] = anon_type_name if tag == '' else f"{kind} {tag}"
|
||||
else:
|
||||
# Не смогли распарсить вложенную структуру - кладём как есть
|
||||
fields[field_str] = None
|
||||
else:
|
||||
# Обычное поле
|
||||
# Нужно выделить тип и имя поля с учётом указателей и массивов
|
||||
m = re.match(r'(.+?)\s+([\w\*\[\]]+)$', field_str)
|
||||
if m:
|
||||
typename, varname = m.groups()
|
||||
fields[varname.strip()] = typename.strip()
|
||||
else:
|
||||
# не распарсили поле — кладём "как есть"
|
||||
fields[field_str] = None
|
||||
return fields
|
||||
|
||||
# Парсим typedef struct/union {...} Alias;
|
||||
typedef_struct_pattern = re.compile(
|
||||
r'\btypedef\s+(struct|union)\s*({.*?})\s*(\w+)\s*;', re.DOTALL)
|
||||
for match in typedef_struct_pattern.finditer(content):
|
||||
alias = match.group(3)
|
||||
body = match.group(2)
|
||||
type_definitions[alias] = parse_fields(body)
|
||||
|
||||
# Парсим struct/union Name {...};
|
||||
named_struct_pattern = re.compile(
|
||||
r'\b(struct|union)\s+(\w+)\s*({.*?})\s*;', re.DOTALL)
|
||||
for match in named_struct_pattern.finditer(content):
|
||||
name = match.group(2)
|
||||
body = match.group(3)
|
||||
if name not in type_definitions:
|
||||
type_definitions[name] = parse_fields(body)
|
||||
|
||||
return type_definitions
|
||||
|
||||
def parse_vars_from_file(filepath):
|
||||
content, encoding = read_file_try_encodings(filepath)
|
||||
content_clean = remove_function_bodies(content)
|
||||
|
||||
vars_found = []
|
||||
for m in VAR_PATTERN.finditer(content_clean):
|
||||
typename = m.group(1).strip()
|
||||
varlist = m.group(2)
|
||||
for var in varlist.split(','):
|
||||
varname = var.strip()
|
||||
# Убираем указатели и массивы
|
||||
varname = varname.strip('*').split('[')[0]
|
||||
# Фильтрация мусора
|
||||
if not re.match(r'^[_a-zA-Z][_a-zA-Z0-9]*$', varname):
|
||||
continue
|
||||
vars_found.append((varname, typename))
|
||||
return vars_found, encoding
|
||||
|
||||
def parse_typedefs_from_file(filepath):
|
||||
"""
|
||||
Парсит typedef из файла C:
|
||||
- typedef struct/union { ... } Alias;
|
||||
- typedef simple_type Alias;
|
||||
|
||||
Возвращает словарь alias -> базовый тип (например, 'MyType' -> 'struct' или 'unsigned int').
|
||||
"""
|
||||
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
content = f.read()
|
||||
|
||||
# Убираем однострочные комментарии
|
||||
content = re.sub(r'//.*?$', '', content, flags=re.MULTILINE)
|
||||
# Убираем многострочные комментарии
|
||||
content = re.sub(r'/\*.*?\*/', '', content, flags=re.DOTALL)
|
||||
|
||||
aliases = {}
|
||||
|
||||
# --- Парсим typedef struct/union {...} Alias;
|
||||
# Используем стек для вложенных фигурных скобок
|
||||
typedef_struct_union_pattern = re.compile(r'\btypedef\s+(struct|union)\b', re.IGNORECASE)
|
||||
pos = 0
|
||||
while True:
|
||||
m = typedef_struct_union_pattern.search(content, pos)
|
||||
if not m:
|
||||
break
|
||||
kind = m.group(1)
|
||||
brace_open_pos = content.find('{', m.end())
|
||||
if brace_open_pos == -1:
|
||||
# Нет тела структуры, пропускаем
|
||||
pos = m.end()
|
||||
continue
|
||||
|
||||
# Ищем позицию закрывающей скобки с учётом вложенности
|
||||
brace_level = 1
|
||||
i = brace_open_pos + 1
|
||||
while i < len(content) and brace_level > 0:
|
||||
if content[i] == '{':
|
||||
brace_level += 1
|
||||
elif content[i] == '}':
|
||||
brace_level -= 1
|
||||
i += 1
|
||||
if brace_level != 0:
|
||||
# Некорректный синтаксис
|
||||
pos = m.end()
|
||||
continue
|
||||
|
||||
# Отрезок typedef структуры/объединения
|
||||
typedef_block = content[m.start():i]
|
||||
|
||||
# После закрывающей скобки ожидаем имя алиаса и точку с запятой
|
||||
rest = content[i:].lstrip()
|
||||
alias_match = re.match(r'(\w+)\s*;', rest)
|
||||
if alias_match:
|
||||
alias_name = alias_match.group(1)
|
||||
aliases[alias_name] = kind # например, "struct" или "union"
|
||||
pos = i + alias_match.end()
|
||||
else:
|
||||
# Анонимный typedef? Просто пропускаем
|
||||
pos = i
|
||||
|
||||
# --- Удаляем typedef struct/union {...} Alias; чтобы не мешали простым typedef
|
||||
# Для этого удалим весь блок typedef struct/union {...} Alias;
|
||||
def remove_typedef_struct_union_blocks(text):
|
||||
result = []
|
||||
last_pos = 0
|
||||
for m in typedef_struct_union_pattern.finditer(text):
|
||||
brace_open_pos = text.find('{', m.end())
|
||||
if brace_open_pos == -1:
|
||||
continue
|
||||
brace_level = 1
|
||||
i = brace_open_pos + 1
|
||||
while i < len(text) and brace_level > 0:
|
||||
if text[i] == '{':
|
||||
brace_level += 1
|
||||
elif text[i] == '}':
|
||||
brace_level -= 1
|
||||
i += 1
|
||||
if brace_level != 0:
|
||||
continue
|
||||
# Ищем имя алиаса и точку с запятой после i
|
||||
rest = text[i:].lstrip()
|
||||
alias_match = re.match(r'\w+\s*;', rest)
|
||||
if alias_match:
|
||||
end_pos = i + alias_match.end()
|
||||
result.append(text[last_pos:m.start()])
|
||||
last_pos = end_pos
|
||||
result.append(text[last_pos:])
|
||||
return ''.join(result)
|
||||
|
||||
content_simple = remove_typedef_struct_union_blocks(content)
|
||||
|
||||
# --- Парсим простые typedef: typedef base_type alias;
|
||||
simple_typedef_pattern = re.compile(
|
||||
r'\btypedef\s+([^{};]+?)\s+(\w+)\s*;', re.MULTILINE)
|
||||
for m in simple_typedef_pattern.finditer(content_simple):
|
||||
base_type = m.group(1).strip()
|
||||
alias = m.group(2).strip()
|
||||
if alias not in aliases:
|
||||
aliases[alias] = base_type
|
||||
|
||||
return aliases
|
||||
|
||||
def parse_externs_from_file(filepath):
|
||||
content, encoding = read_file_try_encodings(filepath)
|
||||
extern_vars = set(EXTERN_PATTERN.findall(content))
|
||||
return extern_vars, encoding
|
||||
|
||||
def get_relpath_to_srcdirs(filepath, src_dirs):
|
||||
# Ищем первый SRC_DIR, в котором лежит filepath, и возвращаем относительный путь
|
||||
for d in src_dirs:
|
||||
try:
|
||||
rel = os.path.relpath(filepath, d)
|
||||
# Проверим, что rel не уходит выше корня (например, не начинается с '..')
|
||||
if not rel.startswith('..'):
|
||||
return rel.replace('\\', '/') # Для единообразия в путях
|
||||
except ValueError:
|
||||
continue
|
||||
# Если ни один SRC_DIR не подходит, вернуть basename
|
||||
return os.path.basename(filepath)
|
||||
|
||||
def find_all_includes_recursive(c_files, include_dirs, processed_files=None):
|
||||
"""
|
||||
Рекурсивно ищет все include-файлы начиная с заданных c_files.
|
||||
include_dirs — список директорий, в которых ищем include-файлы.
|
||||
processed_files — множество уже обработанных файлов (для избежания циклов).
|
||||
"""
|
||||
if processed_files is None:
|
||||
processed_files = set()
|
||||
|
||||
include_files = set()
|
||||
include_pattern = re.compile(r'#include\s+"([^"]+)"')
|
||||
|
||||
for cfile in c_files:
|
||||
norm_path = os.path.normpath(cfile)
|
||||
if norm_path in processed_files:
|
||||
continue
|
||||
processed_files.add(norm_path)
|
||||
|
||||
content, _ = read_file_try_encodings(cfile)
|
||||
includes = include_pattern.findall(content)
|
||||
for inc in includes:
|
||||
include_files.add(inc)
|
||||
|
||||
# Ищем полный путь к include-файлу в include_dirs
|
||||
inc_full_path = None
|
||||
for dir_ in include_dirs:
|
||||
candidate = os.path.normpath(os.path.join(dir_, inc))
|
||||
if os.path.isfile(candidate):
|
||||
inc_full_path = candidate
|
||||
break
|
||||
|
||||
# Если нашли include-файл и ещё не обработали — рекурсивно ищем include внутри него
|
||||
if inc_full_path and inc_full_path not in processed_files:
|
||||
nested_includes = find_all_includes_recursive(
|
||||
[inc_full_path], include_dirs, processed_files
|
||||
)
|
||||
include_files.update(nested_includes)
|
||||
|
||||
return include_files
|
||||
|
||||
def file_uses_typedef_vars(filepath, missing_vars, typedefs):
|
||||
"""
|
||||
Проверяем, содержит ли файл typedef с одним из missing_vars.
|
||||
typedefs — словарь alias->базовый тип, полученный parse_typedefs_from_file.
|
||||
"""
|
||||
# Здесь проще проверить, есть ли в typedefs ключи из missing_vars,
|
||||
# но в условии — typedef переменных из missing_in_h,
|
||||
# значит, нужно проверить typedef переменных с этими именами
|
||||
|
||||
# Для упрощения — прочитаем содержимое и проверим наличие typedef с именами из missing_vars
|
||||
|
||||
content, _ = read_file_try_encodings(filepath)
|
||||
|
||||
for var in missing_vars:
|
||||
# Ищем в content что-то типа typedef ... var ...;
|
||||
# Для простоты регулярка: typedef ... var;
|
||||
pattern = re.compile(r'\btypedef\b[^;]*\b' + re.escape(var) + r'\b[^;]*;', re.DOTALL)
|
||||
if pattern.search(content):
|
||||
return True
|
||||
return False
|
||||
|
||||
def file_contains_extern_vars(filepath, extern_vars):
|
||||
content, _ = read_file_try_encodings(filepath)
|
||||
for var in extern_vars:
|
||||
pattern = re.compile(r'\bextern\b[^;]*\b' + re.escape(var) + r'\b\s*;')
|
||||
if pattern.search(content):
|
||||
return True
|
||||
return False
|
||||
|
||||
def add_struct_fields(new_debug_vars, var_prefix, struct_type, all_structs, existing_debug_vars):
|
||||
"""
|
||||
Рекурсивно добавляет поля структуры в new_debug_vars.
|
||||
|
||||
var_prefix: имя переменной или путь к полю (например "myVar" или "myVar.subfield")
|
||||
struct_type: имя типа структуры (например "MyStruct")
|
||||
all_structs: словарь всех структур
|
||||
existing_debug_vars: множество уже существующих имен переменных
|
||||
"""
|
||||
if struct_type not in all_structs:
|
||||
# Типа нет в структуре, значит не структура или неизвестный тип — выходим
|
||||
return
|
||||
|
||||
fields = all_structs[struct_type]
|
||||
for field_name, field_type in fields.items():
|
||||
full_var_name = f"{var_prefix}.{field_name}"
|
||||
|
||||
if full_var_name in existing_debug_vars or full_var_name in new_debug_vars:
|
||||
continue
|
||||
|
||||
if field_type is None:
|
||||
continue
|
||||
|
||||
iq_type = get_iq_define(field_type)
|
||||
pt_type = map_type_to_pt(field_type, full_var_name)
|
||||
formated_name = f'"{full_var_name}"'
|
||||
line = f'\t{{(char *)&{full_var_name:<40} , {pt_type:<20} , {iq_type:<20} , {formated_name:<40}}}, \\'
|
||||
new_debug_vars[full_var_name] = line
|
||||
|
||||
# Если поле — тоже структура, рекурсивно раскрываем
|
||||
# При этом убираем указатели и массивы из типа, если они есть
|
||||
base_field_type = field_type.split()[0] # например "struct" или "MyStruct*"
|
||||
# Удаляем указатели и массивы из имени типа для поиска в all_structs
|
||||
base_field_type = re.sub(r'[\*\[\]0-9]+', '', base_field_type)
|
||||
base_field_type = base_field_type.strip()
|
||||
|
||||
if base_field_type in all_structs:
|
||||
add_struct_fields(new_debug_vars, full_var_name, base_field_type, all_structs, existing_debug_vars)
|
||||
else:
|
||||
a=1
|
||||
|
||||
|
||||
|
||||
def main(make_path):
|
||||
|
||||
c_files, include_dirs = parse_makefile(make_path)
|
||||
all_dirs = c_files + include_dirs
|
||||
|
||||
h_files = get_files_by_ext(include_dirs, ['.h'])
|
||||
|
||||
vars_in_c = {}
|
||||
encodings_c = set()
|
||||
for cf in c_files:
|
||||
vars_found, enc = parse_vars_from_file(cf)
|
||||
encodings_c.add(enc)
|
||||
for vname, vtype in vars_found:
|
||||
vars_in_c[vname] = (vtype, cf)
|
||||
|
||||
externs_in_h = set()
|
||||
for hf in h_files:
|
||||
externs, _ = parse_externs_from_file(hf)
|
||||
externs_in_h |= externs
|
||||
|
||||
missing_in_h = {v: vars_in_c[v] for v in vars_in_c if v not in externs_in_h}
|
||||
|
||||
all_structs = {}
|
||||
for fl in c_files + h_files:
|
||||
structs = extract_struct_definitions_from_file(fl)
|
||||
all_structs.update(structs)
|
||||
|
||||
# Подготовка typedef-ов
|
||||
global typedef_aliases
|
||||
typedef_aliases = {}
|
||||
for f in h_files + c_files:
|
||||
aliases = parse_typedefs_from_file(f)
|
||||
typedef_aliases.update(aliases)
|
||||
|
||||
|
||||
included_headers = find_all_includes_recursive(c_files, include_dirs) # все подключенные .h
|
||||
include_files = []
|
||||
|
||||
for header in included_headers:
|
||||
# Полный путь к файлу нужно получить
|
||||
full_path = None
|
||||
for d in all_dirs:
|
||||
candidate = os.path.join(d, header)
|
||||
if os.path.isfile(candidate):
|
||||
full_path = candidate
|
||||
break
|
||||
if not full_path:
|
||||
continue # файл не найден в SRC_DIRS — игнорируем
|
||||
|
||||
# Проверяем, что это строго .h
|
||||
if not full_path.endswith('.h'):
|
||||
continue
|
||||
|
||||
# Парсим typedef из файла
|
||||
typedefs = parse_typedefs_from_file(full_path)
|
||||
|
||||
# Проверяем, использует ли typedef переменные из missing_in_h по их vtype
|
||||
uses_typedef = any(vtype in typedefs for (vtype, path) in missing_in_h.values())
|
||||
|
||||
# Проверяем наличие extern переменных
|
||||
has_extern = file_contains_extern_vars(full_path, externs_in_h)
|
||||
|
||||
if not has_extern and not uses_typedef:
|
||||
continue
|
||||
|
||||
# Если прошло оба условия — добавляем
|
||||
include_files.append(full_path)
|
||||
|
||||
# Путь к debug_vars.h
|
||||
common_prefix = os.path.commonpath(include_dirs)
|
||||
output_dir = os.path.join(common_prefix, 'DebugTools')
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
output_path = os.path.join(output_dir, 'debug_vars.c')
|
||||
|
||||
# Считываем существующие переменные
|
||||
existing_debug_vars = {}
|
||||
if os.path.isfile(output_path):
|
||||
with open(output_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
old_lines = f.readlines()
|
||||
for line in old_lines:
|
||||
m = re.match(r'\s*{.*?,\s+.*?,\s+.*?,\s+"([_a-zA-Z][_a-zA-Z0-9]*)"\s*},', line)
|
||||
if m:
|
||||
varname = m.group(1)
|
||||
existing_debug_vars[varname] = line.strip()
|
||||
|
||||
# Генерируем новые переменные
|
||||
new_debug_vars = {}
|
||||
for vname, (vtype, path) in vars_in_c.items():
|
||||
if vname in existing_debug_vars:
|
||||
continue
|
||||
iq_type = get_iq_define(vtype)
|
||||
pt_type = map_type_to_pt(vtype, vname)
|
||||
|
||||
if pt_type not in ('pt_struct', 'pt_union'):
|
||||
formated_name = f'"{vname}"'
|
||||
line = f'{{(char *)&{vname:<41} , {pt_type:<21} , {iq_type:<21} , {formated_name:<42}}}, \\'
|
||||
new_debug_vars[vname] = line
|
||||
else:
|
||||
continue
|
||||
# Если тип переменной — структура, добавляем поля
|
||||
base_type = vtype.split()[0]
|
||||
# Удаляем символы указателей '*' и всю квадратную скобку с содержимым (например [10])
|
||||
base_type = re.sub(r'\*|\[[^\]]*\]', '', base_type).strip()
|
||||
if base_type in all_structs:
|
||||
add_struct_fields(new_debug_vars, vname, base_type, all_structs, existing_debug_vars)
|
||||
|
||||
# Сортируем новые переменные по алфавиту по имени
|
||||
sorted_new_debug_vars = dict(sorted(new_debug_vars.items()))
|
||||
# Объединяем все переменные
|
||||
all_debug_lines = list(existing_debug_vars.values()) + list(sorted_new_debug_vars.values())
|
||||
|
||||
# DebugVar_Numb теперь по всем переменным
|
||||
out_lines = []
|
||||
out_lines.append("// Этот файл сгенерирован автоматически")
|
||||
out_lines.append(f'#include "debug_tools.h"')
|
||||
|
||||
out_lines.append('\n\n// Инклюды для доступа к переменным')
|
||||
out_lines.append(f'#include "IQmathLib.h"')
|
||||
for incf in include_files:
|
||||
out_lines.append(f'#include "{incf}"')
|
||||
|
||||
|
||||
out_lines.append('\n\n// Экстерны для доступа к переменным')
|
||||
for vname, (vtype, path) in missing_in_h.items():
|
||||
out_lines.append(f'extern {vtype} {vname};')
|
||||
|
||||
out_lines.append(f'\n\n// Определение массива с указателями на переменные для отладки')
|
||||
out_lines.append(f'int DebugVar_Numb = {len(all_debug_lines)};')
|
||||
out_lines.append('#pragma DATA_SECTION(dbg_vars,".dbgvar_info")')
|
||||
out_lines.append('DebugVar_t dbg_vars[] = {\\')
|
||||
out_lines.extend(all_debug_lines)
|
||||
out_lines.append('};')
|
||||
out_lines.append('')
|
||||
# Выберем кодировку для записи файла
|
||||
# Если встречается несколько, возьмем первую из set
|
||||
enc_to_write = 'cp1251'
|
||||
|
||||
#print("== GLOBAL VARS FOUND ==")
|
||||
#for vname, (vtype, path) in vars_in_c.items():
|
||||
#print(f"{vtype:<20} {vname:<40} // {path}")
|
||||
|
||||
|
||||
with open(output_path, 'w', encoding=enc_to_write) as f:
|
||||
f.write('\n'.join(out_lines))
|
||||
|
||||
print(f'Файл debug_vars.c сгенерирован в кодировке, переменных: {len(all_debug_lines)}')
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 2:
|
||||
main('F:/Work/Projects/TMS/TMS_new_bus/Debug/makefile')
|
||||
print("Usage: parse_makefile.py path/to/Makefile")
|
||||
sys.exit(1)
|
||||
else:
|
||||
main(sys.argv[1])
|
||||
Reference in New Issue
Block a user