гитингор + чистка
+ часть работы xml перенесена в отдельный файл + фикс бага с записыванием в xml полных путей, вместо относительных + фикс бага при поиске
This commit is contained in:
parent
0d59f88444
commit
a3850c2c8a
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/.out
|
||||
/Src/__pycache__
|
||||
/build/__pycache__
|
||||
/build_temp
|
||||
/DebugVarEdit_GUI.build
|
||||
/DebugVarEdit_GUI.dist
|
||||
/DebugVarEdit_GUI.onefile-build
|
Binary file not shown.
Binary file not shown.
@ -1,732 +0,0 @@
|
||||
# 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])
|
@ -1,143 +0,0 @@
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
# === Словарь соответствия типов 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 is_anonymous(elem):
|
||||
typ = elem.attrib.get('type', '')
|
||||
return 'anonymous' in typ
|
||||
|
||||
|
||||
def get_array_sizes(elem):
|
||||
sizes = []
|
||||
i = 0
|
||||
while True:
|
||||
attr = 'size' if i == 0 else f'size{i}'
|
||||
if attr in elem.attrib:
|
||||
sizes.append(elem.attrib[attr])
|
||||
i += 1
|
||||
else:
|
||||
break
|
||||
return ''.join(f'[{size}]' for size in sizes)
|
||||
|
||||
|
||||
def collect_externs_and_vars(element, prefix, extern_vars, variables):
|
||||
kind = element.attrib.get('kind', '')
|
||||
name = element.attrib.get('name', prefix)
|
||||
vtype = element.attrib.get('type', 'unknown')
|
||||
|
||||
# Пропускаем анонимные типы полностью
|
||||
if is_anonymous(element):
|
||||
return
|
||||
|
||||
# Для массивов учитываем все размеры size, size1, size2...
|
||||
if kind == 'array':
|
||||
array_dims = get_array_sizes(element)
|
||||
extern_type = vtype # тип без размеров
|
||||
extern_vars[name] = (extern_type, array_dims)
|
||||
variables.append((name, vtype)) # В массив макросов кладём сам массив (без элементов)
|
||||
return
|
||||
|
||||
# Для обычных структур (не анонимных) и переменных
|
||||
if kind in ('struct', 'union'):
|
||||
extern_vars[name] = (vtype, '')
|
||||
variables.append((name, vtype))
|
||||
return
|
||||
|
||||
# Простые переменные
|
||||
extern_vars[name] = (vtype, '')
|
||||
variables.append((name, vtype))
|
||||
|
||||
|
||||
def parse_variables(xml_path):
|
||||
tree = ET.parse(xml_path)
|
||||
root = tree.getroot()
|
||||
|
||||
extern_vars = dict()
|
||||
variables = []
|
||||
|
||||
for var in root.findall('variable'):
|
||||
collect_externs_and_vars(var, var.attrib['name'], extern_vars, variables)
|
||||
|
||||
return variables, extern_vars
|
||||
|
||||
def format_extern_declaration(varname, vartype, dims):
|
||||
base_type = vartype.replace('[]', '') # убираем [] из типа
|
||||
return f"extern {base_type} {varname}{dims};"
|
||||
|
||||
|
||||
def to_macro_entry(varname, vartype):
|
||||
# pt_ и t_iq_none — пример, замените на вашу логику или словарь
|
||||
ptr_type = type_map.get(vartype)
|
||||
if ptr_type is None:
|
||||
ptr_type = 'char *'
|
||||
iq_type = "t_iq_none"
|
||||
return f"\t{{(char *)&{varname:<40}, {ptr_type:<20}, {iq_type:<20}, \"{varname}\"}},"
|
||||
|
||||
|
||||
def generate_code(variables, extern_vars):
|
||||
# extern-блок
|
||||
extern_lines = []
|
||||
for var, (vtype, dims) in sorted(extern_vars.items()):
|
||||
# фильтр анонимных структур по имени (пример)
|
||||
if "anonymous" in var:
|
||||
continue
|
||||
extern_lines.append(format_extern_declaration(var, vtype, dims))
|
||||
|
||||
# define DebugVar_Numb
|
||||
debugvar_numb = len(variables)
|
||||
defines = [f"#define DebugVar_Numb\t{debugvar_numb}"]
|
||||
|
||||
# define DebugVar_Init
|
||||
defines.append("#define DebugVar_Init\t\\")
|
||||
defines.append("{\\")
|
||||
for varname, vartype in variables:
|
||||
defines.append(to_macro_entry(varname, vartype))
|
||||
defines.append("}")
|
||||
|
||||
return "\n".join(extern_lines) + "\n\n" + "\n".join(defines)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
xml_file = "F:/Work/Projects/TMS/TMS_new_bus/bin/New_bus_allVars.xml"
|
||||
variables, extern_vars = parse_variables(xml_file)
|
||||
|
||||
code = generate_code(variables, extern_vars)
|
||||
|
||||
with open("debug_vars.h", "w") as f:
|
||||
f.write(code)
|
||||
|
||||
print("Сгенерировано в debug_vars.h")
|
@ -1,581 +0,0 @@
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import xml.etree.ElementTree as ET
|
||||
from generateVars import map_type_to_pt, get_iq_define, type_map
|
||||
from enum import IntEnum
|
||||
from scanVars import run_scan
|
||||
from generateVars import run_generate
|
||||
|
||||
from PySide2.QtWidgets import (
|
||||
QApplication, QWidget, QTableWidget, QTableWidgetItem,
|
||||
QCheckBox, QComboBox, QLineEdit, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||
QCompleter, QAbstractItemView, QLabel, QMessageBox, QFileDialog, QTextEdit
|
||||
)
|
||||
from PySide2.QtGui import QTextCursor
|
||||
from PySide2.QtCore import Qt, QProcess
|
||||
|
||||
class rows(IntEnum):
|
||||
include = 0
|
||||
name = 1
|
||||
type = 2
|
||||
pt_type = 3
|
||||
iq_type = 4
|
||||
ret_type = 5
|
||||
short_name = 6
|
||||
|
||||
# 1. Парсим vars.xml
|
||||
def make_absolute_path(path, base_path):
|
||||
if not os.path.isabs(path):
|
||||
return os.path.abspath(os.path.join(base_path, path))
|
||||
return os.path.abspath(path)
|
||||
|
||||
def parse_vars(filename):
|
||||
tree = ET.parse(filename)
|
||||
root = tree.getroot()
|
||||
|
||||
vars_list = []
|
||||
variables_elem = root.find('variables')
|
||||
if variables_elem is not None:
|
||||
for var in variables_elem.findall('var'):
|
||||
name = var.attrib.get('name', '')
|
||||
vars_list.append({
|
||||
'name': name,
|
||||
'enable': var.findtext('enable', 'false'),
|
||||
'shortname': var.findtext('shortname', name),
|
||||
'pt_type': var.findtext('pt_type', 'pt_unknown'),
|
||||
'iq_type': var.findtext('iq_type', 'iq_none'),
|
||||
'return_type': var.findtext('return_type', 'int'),
|
||||
'type': var.findtext('type', 'unknown'),
|
||||
'file': var.findtext('file', ''),
|
||||
'extern': var.findtext('extern', 'false') == 'true',
|
||||
'static': var.findtext('static', 'false') == 'true',
|
||||
})
|
||||
|
||||
return vars_list
|
||||
|
||||
# 2. Парсим structSup.xml
|
||||
def parse_structs(filename):
|
||||
tree = ET.parse(filename)
|
||||
root = tree.getroot()
|
||||
|
||||
structs = {}
|
||||
typedef_map = {}
|
||||
|
||||
# --- Считываем структуры ---
|
||||
structs_elem = root.find('structs')
|
||||
if structs_elem is not None:
|
||||
for struct in structs_elem.findall('struct'):
|
||||
name = struct.attrib['name']
|
||||
fields = {}
|
||||
for field in struct.findall('field'):
|
||||
fname = field.attrib.get('name')
|
||||
ftype = field.attrib.get('type')
|
||||
if fname and ftype:
|
||||
fields[fname] = ftype
|
||||
structs[name] = fields
|
||||
|
||||
# --- Считываем typedef-ы ---
|
||||
typedefs_elem = root.find('typedefs')
|
||||
if typedefs_elem is not None:
|
||||
for typedef in typedefs_elem.findall('typedef'):
|
||||
name = typedef.attrib.get('name')
|
||||
target_type = typedef.attrib.get('type')
|
||||
if name and target_type:
|
||||
typedef_map[name.strip()] = target_type.strip()
|
||||
|
||||
return structs, typedef_map
|
||||
|
||||
|
||||
|
||||
class ProcessOutputWindow(QWidget):
|
||||
def __init__(self, command, args):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Поиск переменных...")
|
||||
self.resize(600, 400)
|
||||
|
||||
self.layout = QVBoxLayout(self)
|
||||
self.output_edit = QTextEdit()
|
||||
self.output_edit.setReadOnly(True)
|
||||
self.layout.addWidget(self.output_edit)
|
||||
|
||||
self.btn_close = QPushButton("Закрыть")
|
||||
self.btn_close.setEnabled(False)
|
||||
self.btn_close.clicked.connect(self.close)
|
||||
self.layout.addWidget(self.btn_close)
|
||||
|
||||
self.process = QProcess(self)
|
||||
self.process.setProcessChannelMode(QProcess.MergedChannels)
|
||||
self.process.readyReadStandardOutput.connect(self.handle_stdout)
|
||||
self.process.finished.connect(self.process_finished)
|
||||
|
||||
self.process.start(command, args)
|
||||
|
||||
def handle_stdout(self):
|
||||
data = self.process.readAllStandardOutput()
|
||||
text = data.data().decode('utf-8')
|
||||
self.output_edit.append(text)
|
||||
|
||||
def process_finished(self):
|
||||
self.output_edit.append("\n--- Процесс завершён ---")
|
||||
self.btn_close.setEnabled(True)
|
||||
|
||||
# 3. UI: таблица с переменными
|
||||
class VarEditor(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.vars_list = []
|
||||
self.structs = {}
|
||||
self.typedef_map = {}
|
||||
self.structs_xml_path = None # сюда запишем путь из <structs_xml>
|
||||
self.initUI()
|
||||
|
||||
def initUI(self):
|
||||
self.setWindowTitle("Variable Editor")
|
||||
|
||||
# --- Поля ввода пути проекта и XML ---
|
||||
|
||||
# XML Output
|
||||
xml_layout = QHBoxLayout()
|
||||
xml_layout.addWidget(QLabel("XML Output:"))
|
||||
self.xml_output_edit = QLineEdit()
|
||||
xml_layout.addWidget(self.xml_output_edit)
|
||||
self.xml_output_edit.returnPressed.connect(self.read_xml_file)
|
||||
btn_xml_browse = QPushButton("...")
|
||||
btn_xml_browse.setFixedWidth(30)
|
||||
xml_layout.addWidget(btn_xml_browse)
|
||||
btn_xml_browse.clicked.connect(self.browse_xml_output)
|
||||
|
||||
# Project Path
|
||||
proj_layout = QHBoxLayout()
|
||||
proj_layout.addWidget(QLabel("Project Path:"))
|
||||
self.proj_path_edit = QLineEdit()
|
||||
proj_layout.addWidget(self.proj_path_edit)
|
||||
btn_proj_browse = QPushButton("...")
|
||||
btn_proj_browse.setFixedWidth(30)
|
||||
proj_layout.addWidget(btn_proj_browse)
|
||||
btn_proj_browse.clicked.connect(self.browse_proj_path)
|
||||
|
||||
# Makefile Path
|
||||
makefile_layout = QHBoxLayout()
|
||||
makefile_layout.addWidget(QLabel("Makefile Path (relative path):"))
|
||||
self.makefile_edit = QLineEdit()
|
||||
makefile_layout.addWidget(self.makefile_edit)
|
||||
btn_makefile_browse = QPushButton("...")
|
||||
btn_makefile_browse.setFixedWidth(30)
|
||||
makefile_layout.addWidget(btn_makefile_browse)
|
||||
btn_makefile_browse.clicked.connect(self.browse_makefile)
|
||||
|
||||
|
||||
|
||||
# Source Output File/Directory
|
||||
source_output_layout = QHBoxLayout()
|
||||
source_output_layout.addWidget(QLabel("Source Output File:"))
|
||||
self.source_output_edit = QLineEdit()
|
||||
source_output_layout.addWidget(self.source_output_edit)
|
||||
btn_source_output_browse = QPushButton("...")
|
||||
btn_source_output_browse.setFixedWidth(30)
|
||||
source_output_layout.addWidget(btn_source_output_browse)
|
||||
btn_source_output_browse.clicked.connect(self.browse_source_output)
|
||||
|
||||
|
||||
self.btn_update_vars = QPushButton("Обновить данные о переменных")
|
||||
self.btn_update_vars.clicked.connect(self.update_vars_data)
|
||||
|
||||
# Таблица переменных
|
||||
self.table = QTableWidget(len(self.vars_list), 7)
|
||||
self.table.setHorizontalHeaderLabels([
|
||||
'Include',
|
||||
'Name',
|
||||
'Origin Type',
|
||||
'Pointer Type',
|
||||
'IQ Type',
|
||||
'Return Type',
|
||||
'Short Name'
|
||||
])
|
||||
self.table.setEditTriggers(QAbstractItemView.AllEditTriggers)
|
||||
|
||||
# Кнопка сохранения
|
||||
btn_save = QPushButton("Build")
|
||||
btn_save.clicked.connect(self.save_build)
|
||||
|
||||
# Основной layout
|
||||
layout = QVBoxLayout()
|
||||
layout.addLayout(xml_layout)
|
||||
layout.addLayout(proj_layout)
|
||||
layout.addLayout(makefile_layout)
|
||||
layout.addWidget(self.btn_update_vars)
|
||||
layout.addWidget(self.table)
|
||||
layout.addWidget(btn_save)
|
||||
layout.addLayout(source_output_layout)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def update_vars_data(self):
|
||||
proj_path = os.path.abspath(self.proj_path_edit.text().strip())
|
||||
xml_path = os.path.abspath(self.xml_output_edit.text().strip())
|
||||
makefile_path = make_absolute_path(self.makefile_edit.text().strip(), proj_path)
|
||||
|
||||
if not proj_path or not xml_path:
|
||||
QMessageBox.warning(self, "Ошибка", "Пожалуйста, укажите пути проекта и XML.")
|
||||
return
|
||||
|
||||
if not os.path.isfile(makefile_path):
|
||||
QMessageBox.warning(self, "Ошибка", f"Makefile не найден по пути:\n{makefile_path}")
|
||||
return
|
||||
|
||||
scanvars_exe = "scanVars.exe"
|
||||
args = [proj_path, makefile_path, xml_path]
|
||||
|
||||
# Создаем и показываем окно с выводом процесса
|
||||
self.proc_win = ProcessOutputWindow("python", [])
|
||||
self.proc_win.show()
|
||||
self.proc_win.output_edit.append("Запуск анализа переменных...")
|
||||
|
||||
# Запускаем в отдельном процессе через QProcess, если нужен live-вывод:
|
||||
from threading import Thread
|
||||
|
||||
def run_scan_wrapper():
|
||||
try:
|
||||
run_scan(proj_path, makefile_path, xml_path)
|
||||
self.proc_win.output_edit.append("\n--- Анализ завершён ---")
|
||||
except Exception as e:
|
||||
self.proc_win.output_edit.append(f"\n[ОШИБКА] {e}")
|
||||
self.proc_win.btn_close.setEnabled(True)
|
||||
self.after_scanvars_finished(0, 0)
|
||||
|
||||
Thread(target=run_scan_wrapper).start()
|
||||
# Можно подписаться на сигнал завершения процесса, если хочешь обновить UI после
|
||||
#self.proc_win.process.finished.connect(lambda exitCode, exitStatus: self.after_scanvars_finished(exitCode, exitStatus))
|
||||
|
||||
|
||||
def save_build(self):
|
||||
vars_out = []
|
||||
for row in range(self.table.rowCount()):
|
||||
include_cb = self.table.cellWidget(row, rows.include)
|
||||
if not include_cb.isChecked():
|
||||
continue
|
||||
name_edit = self.table.cellWidget(row, rows.name)
|
||||
pt_type_combo = self.table.cellWidget(row, rows.pt_type)
|
||||
iq_combo = self.table.cellWidget(row, rows.iq_type)
|
||||
ret_combo = self.table.cellWidget(row, rows.ret_type)
|
||||
short_name_edit = self.table.cellWidget(row, rows.short_name)
|
||||
|
||||
var_data = {
|
||||
'name': name_edit.text(),
|
||||
'type': 'pt_' + pt_type_combo.currentText(),
|
||||
'iq_type': iq_combo.currentText(),
|
||||
'return_type': ret_combo.currentText() if ret_combo.currentText() else 'int',
|
||||
'short_name': short_name_edit.text(),
|
||||
}
|
||||
vars_out.append(var_data)
|
||||
|
||||
# Здесь нужно указать абсолютные пути к проекту, xml и output (замени на свои)
|
||||
proj_path = os.path.abspath(self.proj_path_edit.text().strip())
|
||||
xml_path = os.path.abspath(self.xml_output_edit.text().strip())
|
||||
output_dir_c_file = os.path.abspath(self.source_output_edit.text().strip())
|
||||
|
||||
if not proj_path or not xml_path or not output_dir_c_file:
|
||||
QMessageBox.warning(self, "Ошибка", "Заполните все пути: проект, XML и output.")
|
||||
return
|
||||
|
||||
try:
|
||||
run_generate(proj_path, xml_path, output_dir_c_file)
|
||||
QMessageBox.information(self, "Готово", "Файл debug_vars.c успешно сгенерирован.")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Ошибка при генерации", str(e))
|
||||
|
||||
def browse_proj_path(self):
|
||||
dir_path = QFileDialog.getExistingDirectory(self, "Выберите папку проекта")
|
||||
if dir_path:
|
||||
self.proj_path_edit.setText(dir_path)
|
||||
|
||||
def read_xml_file(self):
|
||||
file_path = self.xml_output_edit.text().strip()
|
||||
if file_path and not os.path.isfile(file_path):
|
||||
return
|
||||
|
||||
self.vars_list = parse_vars(file_path)
|
||||
try:
|
||||
tree = ET.parse(file_path)
|
||||
root = tree.getroot()
|
||||
|
||||
proj_path = self.proj_path_edit.text().strip()
|
||||
|
||||
if not proj_path:
|
||||
# Если в поле ничего нет, пробуем взять из XML
|
||||
proj_path_from_xml = root.attrib.get('proj_path', '').strip()
|
||||
if proj_path_from_xml and os.path.isdir(proj_path_from_xml):
|
||||
proj_path = proj_path_from_xml
|
||||
self.proj_path_edit.setText(proj_path_from_xml)
|
||||
else:
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
"Внимание",
|
||||
"Путь к проекту (proj_path) не найден или не существует.\n"
|
||||
"Пожалуйста, укажите его вручную в поле 'Project Path'."
|
||||
)
|
||||
else:
|
||||
if not os.path.isdir(proj_path):
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
"Внимание",
|
||||
f"Указанный путь к проекту не существует:\n{proj_path}\n"
|
||||
"Пожалуйста, исправьте путь в поле 'Project Path'."
|
||||
)
|
||||
|
||||
|
||||
# --- makefile_path из атрибута ---
|
||||
makefile_path = root.attrib.get('makefile_path', '').strip()
|
||||
makefile_path_full = make_absolute_path(makefile_path, proj_path)
|
||||
if makefile_path_full and os.path.isfile(makefile_path_full):
|
||||
self.makefile_edit.setText(makefile_path)
|
||||
|
||||
# --- structs_path из атрибута ---
|
||||
structs_path = root.attrib.get('structs_path', '').strip()
|
||||
structs_path_full = make_absolute_path(structs_path, proj_path)
|
||||
if structs_path_full and os.path.isfile(structs_path_full):
|
||||
self.structs_xml_path = structs_path_full
|
||||
self.structs, self.typedef_map = parse_structs(structs_path_full)
|
||||
else:
|
||||
self.structs_xml_path = None
|
||||
|
||||
self.update_table()
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "Ошибка", f"Ошибка при чтении XML:\n{e}")
|
||||
|
||||
def browse_xml_output(self):
|
||||
file_path, _ = QFileDialog.getSaveFileName(
|
||||
self,
|
||||
"Выберите XML файл",
|
||||
filter="XML files (*.xml);;All Files (*)"
|
||||
)
|
||||
self.xml_output_edit.setText(file_path)
|
||||
self.read_xml_file()
|
||||
|
||||
|
||||
def browse_xml_struct(self):
|
||||
file_path, _ = QFileDialog.getSaveFileName(self, "Выберите XML файл", filter="XML files (*.xml);;All Files (*)")
|
||||
if file_path:
|
||||
self.xml_output_edit.setText(file_path)
|
||||
if os.path.isfile(file_path):
|
||||
self.structs, self.typedef_map = parse_structs(file_path)
|
||||
|
||||
def browse_makefile(self):
|
||||
file_path, _ = QFileDialog.getOpenFileName(
|
||||
self, "Выберите Makefile", filter="Makefile (makefile);;All Files (*)"
|
||||
)
|
||||
if file_path:
|
||||
self.makefile_edit.setText(file_path)
|
||||
|
||||
def browse_source_output(self):
|
||||
dir_path = QFileDialog.getExistingDirectory(self, "Выберите папку для debug_vars.c")
|
||||
if dir_path:
|
||||
self.source_output_edit.setText(dir_path)
|
||||
|
||||
def after_scanvars_finished(self, exitCode, exitStatus):
|
||||
xml_path = self.xml_output_edit.text().strip()
|
||||
if not os.path.isfile(xml_path):
|
||||
QMessageBox.critical(self, "Ошибка", f"Файл не найден: {xml_path}")
|
||||
return
|
||||
|
||||
try:
|
||||
# Читаем структуры, если задан путь
|
||||
if self.structs_xml_path and os.path.isfile(self.structs_xml_path):
|
||||
try:
|
||||
self.structs, self.typedef_map = parse_structs(self.structs_xml_path)
|
||||
# При необходимости обновите UI или сделайте что-то с self.structs
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "Внимание", f"Не удалось загрузить структуры из {self.structs_xml_path}:\n{e}")
|
||||
|
||||
self.vars_list = parse_vars(xml_path)
|
||||
self.update_table()
|
||||
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Ошибка", f"Не удалось загрузить переменные:\n{e}")
|
||||
|
||||
def update_table(self):
|
||||
self.type_options = list(dict.fromkeys(type_map.values()))
|
||||
self.display_type_options = [t.replace('pt_', '') for t in self.type_options]
|
||||
iq_types = ['iq_none', 'iq'] + [f'iq{i}' for i in range(1, 31)]
|
||||
self.table.setRowCount(len(self.vars_list))
|
||||
|
||||
for row, var in enumerate(self.vars_list):
|
||||
cb = QCheckBox()
|
||||
enable_str = var.get('enable', 'false')
|
||||
cb.setChecked(enable_str.lower() == 'true')
|
||||
self.table.setCellWidget(row, rows.include, cb)
|
||||
|
||||
name_edit = QLineEdit(var['name'])
|
||||
if var['type'] in self.structs:
|
||||
completer = QCompleter(self.structs[var['type']].keys())
|
||||
completer.setCaseSensitivity(Qt.CaseInsensitive)
|
||||
name_edit.setCompleter(completer)
|
||||
self.table.setCellWidget(row, rows.name, name_edit)
|
||||
|
||||
# Type (origin)
|
||||
origin_type = var.get('type', '').strip()
|
||||
origin_item = QTableWidgetItem(origin_type)
|
||||
origin_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # read-only
|
||||
self.table.setItem(row, rows.type, origin_item)
|
||||
|
||||
pt_type_combo = QComboBox()
|
||||
pt_type_combo.addItems(self.display_type_options)
|
||||
internal_type = map_type_to_pt(var['type'], var['name'], self.typedef_map)
|
||||
display_type = internal_type.replace('pt_', '')
|
||||
if display_type in self.display_type_options:
|
||||
pt_type_combo.setCurrentText(display_type)
|
||||
else:
|
||||
pt_type_combo.addItem(display_type)
|
||||
pt_type_combo.setCurrentText(display_type)
|
||||
self.table.setCellWidget(row, rows.pt_type, pt_type_combo)
|
||||
|
||||
iq_combo = QComboBox()
|
||||
iq_combo.addItems(iq_types)
|
||||
iq_type = get_iq_define(var['type']) # Получаем IQ-тип, например 'iq24'
|
||||
display_type = iq_type.replace('t_', '')
|
||||
if iq_type in iq_types:
|
||||
iq_combo.setCurrentText(display_type)
|
||||
else:
|
||||
iq_combo.addItem(display_type)
|
||||
iq_combo.setCurrentText(display_type)
|
||||
self.table.setCellWidget(row, rows.iq_type, iq_combo)
|
||||
|
||||
ret_combo = QComboBox()
|
||||
ret_combo.addItems(iq_types)
|
||||
self.table.setCellWidget(row, rows.ret_type, ret_combo)
|
||||
|
||||
short_name_edit = QLineEdit(var['name'])
|
||||
self.table.setCellWidget(row, rows.short_name, short_name_edit)
|
||||
|
||||
cb.stateChanged.connect(self.save_table_to_xml)
|
||||
name_edit.textChanged.connect(self.save_table_to_xml)
|
||||
pt_type_combo.currentTextChanged.connect(self.save_table_to_xml)
|
||||
iq_combo.currentTextChanged.connect(self.save_table_to_xml)
|
||||
ret_combo.currentTextChanged.connect(self.save_table_to_xml)
|
||||
short_name_edit.textChanged.connect(self.save_table_to_xml)
|
||||
|
||||
|
||||
def save_table_to_xml(self):
|
||||
def make_relative_path(abs_path, base_path):
|
||||
try:
|
||||
return os.path.relpath(abs_path, base_path).replace("\\", "/")
|
||||
except ValueError:
|
||||
return abs_path.replace("\\", "/")
|
||||
|
||||
xml_path = self.xml_output_edit.text().strip()
|
||||
proj_path = self.proj_path_edit.text().strip()
|
||||
makefile_path = self.makefile_edit.text().strip()
|
||||
|
||||
if not xml_path or not os.path.isfile(xml_path):
|
||||
print("XML файл не найден или путь пустой")
|
||||
return
|
||||
|
||||
try:
|
||||
tree = ET.parse(xml_path)
|
||||
root = tree.getroot()
|
||||
|
||||
# Обновим атрибуты с относительными путями
|
||||
if os.path.isdir(proj_path):
|
||||
root.set("proj_path", proj_path.replace("\\", "/"))
|
||||
|
||||
if os.path.isfile(makefile_path):
|
||||
rel_makefile = make_relative_path(makefile_path, proj_path)
|
||||
root.set("makefile_path", rel_makefile)
|
||||
|
||||
if self.structs_xml_path and os.path.isfile(self.structs_xml_path):
|
||||
rel_struct_path = make_relative_path(self.structs_xml_path, proj_path)
|
||||
root.set("structs_path", rel_struct_path)
|
||||
|
||||
|
||||
vars_elem = root.find('variables')
|
||||
if vars_elem is None:
|
||||
# Если блока нет, создаём
|
||||
vars_elem = ET.SubElement(root, 'variables')
|
||||
|
||||
original_info = {}
|
||||
for var_elem in vars_elem.findall('var'):
|
||||
name = var_elem.attrib.get('name')
|
||||
if name:
|
||||
original_info[name] = {
|
||||
'type': var_elem.findtext('type', ''),
|
||||
'file': var_elem.findtext('file', ''),
|
||||
'extern': var_elem.findtext('extern', ''),
|
||||
'static': var_elem.findtext('static', '')
|
||||
}
|
||||
# Собираем данные из таблицы
|
||||
updated_vars = []
|
||||
for row in range(self.table.rowCount()):
|
||||
cb = self.table.cellWidget(row, 0)
|
||||
name_edit = self.table.cellWidget(row, 1)
|
||||
pt_type_combo = self.table.cellWidget(row, 3)
|
||||
iq_combo = self.table.cellWidget(row, 4)
|
||||
ret_combo = self.table.cellWidget(row, 5)
|
||||
short_name_edit = self.table.cellWidget(row, 6)
|
||||
|
||||
var_name = name_edit.text()
|
||||
|
||||
# Берём оригинальные type и file из словаря, если есть
|
||||
orig_type = original_info.get(var_name, {}).get('type', '')
|
||||
orig_file = original_info.get(var_name, {}).get('file', '')
|
||||
orig_extern = original_info.get(var_name, {}).get('extern', '')
|
||||
orig_static = original_info.get(var_name, {}).get('static', '')
|
||||
|
||||
updated_vars.append({
|
||||
'name': var_name,
|
||||
'enable': cb.isChecked(),
|
||||
'shortname': short_name_edit.text(),
|
||||
'pt_type': 'pt_' + pt_type_combo.currentText(),
|
||||
'iq_type': iq_combo.currentText(),
|
||||
'return_type': ret_combo.currentText() or 'int',
|
||||
'type': orig_type,
|
||||
'file': orig_file,
|
||||
'extern': orig_extern,
|
||||
'static': orig_static,
|
||||
})
|
||||
|
||||
# Обновляем или добавляем по одному var в XML
|
||||
for v in updated_vars:
|
||||
var_elem = None
|
||||
for ve in vars_elem.findall('var'):
|
||||
if ve.attrib.get('name') == v['name']:
|
||||
var_elem = ve
|
||||
break
|
||||
if var_elem is None:
|
||||
var_elem = ET.SubElement(vars_elem, 'var', {'name': v['name']})
|
||||
|
||||
def set_sub_elem_text(parent, tag, text):
|
||||
el = parent.find(tag)
|
||||
if el is None:
|
||||
el = ET.SubElement(parent, tag)
|
||||
el.text = str(text)
|
||||
|
||||
set_sub_elem_text(var_elem, 'enable', 'true' if v['enable'] else 'false')
|
||||
set_sub_elem_text(var_elem, 'shortname', v['shortname'])
|
||||
set_sub_elem_text(var_elem, 'pt_type', v['pt_type'])
|
||||
set_sub_elem_text(var_elem, 'iq_type', v['iq_type'])
|
||||
set_sub_elem_text(var_elem, 'return_type', v['return_type'])
|
||||
set_sub_elem_text(var_elem, 'type', v['type'])
|
||||
set_sub_elem_text(var_elem, 'file', v['file'])
|
||||
set_sub_elem_text(var_elem, 'extern', v['extern'])
|
||||
set_sub_elem_text(var_elem, 'static', v['static'])
|
||||
|
||||
# Сохраняем изменения
|
||||
tree.write(xml_path, encoding='utf-8', xml_declaration=True)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Ошибка при сохранении XML: {e}")
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
editor = VarEditor()
|
||||
editor.resize(900, 600)
|
||||
editor.show()
|
||||
|
||||
sys.exit(app.exec_())
|
||||
|
||||
|
BIN
DebugVarEdit.exe
BIN
DebugVarEdit.exe
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,2 +0,0 @@
|
||||
[Paths]
|
||||
Prefix = .
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +0,0 @@
|
||||
<クdハ<>箆!ソ`。スン
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user