init comm для настройки переменных для отладки

This commit is contained in:
2025-07-08 06:36:15 +03:00
commit 2e9592ffbb
48 changed files with 61295 additions and 0 deletions

732
.out/scanVars0.py Normal file
View 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])