гитингор + чистка

+ часть работы xml перенесена в отдельный файл
+ фикс бага с записыванием в xml полных путей, вместо относительных
+ фикс бага при поиске
This commit is contained in:
2025-07-11 06:47:30 +03:00
parent 0d59f88444
commit a3850c2c8a
154 changed files with 1678 additions and 3042 deletions

View File

@@ -15,6 +15,7 @@ from VariableSelector import VariableSelectorDialog
from VariableTable import VariableTableWidget, rows
from scanVarGUI import ProcessOutputWindowDummy
import scanVars
import myXML
from PySide2.QtWidgets import (
QApplication, QWidget, QTableWidget, QTableWidgetItem,
@@ -31,7 +32,7 @@ xml_path_title = "Путь к XML:"
proj_path_title = "Путь к проекту:"
makefile_path_title = "Пусть к makefile (относительно проекта)"
output_path_title = "Папка для debug_vars.c:"
scan_title = "Обновить переменные"
scan_title = "Сканировать переменные"
build_title = "Обновить файл"
add_vars_title = "Добавить переменные"
open_output_title = "Открыть файл"
@@ -154,18 +155,21 @@ class VarEditor(QWidget):
def get_makefile_path(self):
proj_path = self.get_proj_path()
makefile_path = setupVars.make_absolute_path(self.makefile_edit.text().strip(), proj_path)
rel_makefile_path = self.makefile_edit.text().strip()
if not rel_makefile_path:
return None
makefile_path = myXML.make_absolute_path(rel_makefile_path, proj_path)
return makefile_path
def get_struct_path(self):
proj_path = self.get_proj_path()
xml_path = self.get_xml_path()
root, tree = scanVars.safe_parse_xml(xml_path)
root, tree = myXML.safe_parse_xml(xml_path)
if root is None:
return
# --- structs_path из атрибута ---
structs_path = root.attrib.get('structs_path', '').strip()
structs_path_full = setupVars.make_absolute_path(structs_path, proj_path)
structs_path_full = myXML.make_absolute_path(structs_path, proj_path)
if structs_path_full and os.path.isfile(structs_path_full):
structs_path = structs_path_full
else:
@@ -242,7 +246,7 @@ class VarEditor(QWidget):
self.update_all_paths()
if not self.proj_path or not self.xml_path or not self.output_path:
QMessageBox.warning(self, "Ошибка", "Заполните все пути: проект, XML и output.")
QMessageBox.warning(self, "Ошибка", f"Заполните: {xml_path_title[:-1]}, {proj_path_title[:-1]}, {output_path_title[:-1]}.")
return
try:
@@ -264,7 +268,7 @@ class VarEditor(QWidget):
return
try:
root, tree = scanVars.safe_parse_xml(self.xml_path)
root, tree = myXML.safe_parse_xml(self.xml_path)
if root is None:
return
@@ -278,8 +282,8 @@ class VarEditor(QWidget):
QMessageBox.warning(
self,
"Внимание",
"Путь к проекту (proj_path) не найден или не существует.\n"
"Пожалуйста, укажите его вручную в поле 'Project Path'."
"Путь к проекту не найден или не существует.\n"
f"Пожалуйста, укажите его вручную в поле '{proj_path_title[:-1]}'."
)
else:
if not os.path.isdir(self.proj_path):
@@ -287,25 +291,27 @@ class VarEditor(QWidget):
self,
"Внимание",
f"Указанный путь к проекту не существует:\n{self.proj_path}\n"
"Пожалуйста, исправьте путь в поле 'Project Path'."
"Пожалуйста, исправьте путь в поле '{proj_path_title[:-1]}'."
)
if not self.makefile_path:
# --- makefile_path из атрибута ---
if not self.makefile_path and self.proj_path and os.path.isdir(self.proj_path):
makefile_path = root.attrib.get('makefile_path', '').strip()
makefile_path_full = setupVars.make_absolute_path(makefile_path, self.proj_path)
if makefile_path_full and os.path.isfile(makefile_path_full):
makefile_path_full = myXML.make_absolute_path(makefile_path, self.proj_path)
if os.path.isfile(makefile_path_full):
# Обновляем edit-поле на относительный путь, абсолют сохраняем
self.makefile_path = makefile_path_full
self.makefile_edit.setText(makefile_path)
else:
self.makefile_path = None
self.makefile_edit.setText("")
# --- structs_path из атрибута ---
structs_path = root.attrib.get('structs_path', '').strip()
structs_path_full = setupVars.make_absolute_path(structs_path, self.proj_path)
structs_path_full = myXML.make_absolute_path(structs_path, self.proj_path)
if structs_path_full and os.path.isfile(structs_path_full):
self.structs_path = structs_path_full
self.structs, self.typedef_map = setupVars.setupVars.parse_structs(structs_path_full)
self.structs, self.typedef_map = setupVars.parse_structs(structs_path_full)
else:
self.structs_path = None
@@ -322,10 +328,22 @@ class VarEditor(QWidget):
dir_path = QFileDialog.getExistingDirectory(self, "Выберите папку проекта")
if dir_path:
self.proj_path_edit.setText(dir_path)
self.proj_path = dir_path
self.proj_path = dir_path
# Сброс makefile, если proj_path изменился
if not os.path.isdir(dir_path):
self.makefile_path = None
self.makefile_edit.setText("")
else:
if self.makefile_path and os.path.isfile(self.makefile_path):
rel_path = myXML.make_relative_path(self.makefile_path, dir_path)
self.makefile_edit.setText(rel_path)
self.update()
self.update()
if self.makefile_path and self.proj_path:
path = setupVars.make_relative_path(self.makefile_path, self.proj_path)
path = myXML.make_relative_path(self.makefile_path, self.proj_path)
self.makefile_edit.setText(path)
self.makefile_path = path
@@ -349,7 +367,7 @@ class VarEditor(QWidget):
self, "Выберите Makefile", filter="Makefile (makefile);;All Files (*)"
)
if file_path and self.proj_path:
path = setupVars.make_relative_path(file_path, self.proj_path)
path = myXML.make_relative_path(file_path, self.proj_path)
else:
path = file_path
self.makefile_edit.setText(path)
@@ -370,12 +388,23 @@ class VarEditor(QWidget):
def __on_proj_path_changed(self, _):
self.proj_path = self.get_proj_path()
if not os.path.isdir(self.proj_path):
self.makefile_path = None
self.makefile_edit.setText("")
return # Преждевременно выходим, если проект не существует
# Обновим путь к makefile, если он уже задан и абсолютен
if self.makefile_path and os.path.isfile(self.makefile_path):
rel_path = myXML.make_relative_path(self.makefile_path, self.proj_path)
self.makefile_edit.setText(rel_path)
self.update()
def __on_makefile_path_changed(self, _):
self.makefile_path = self.get_makefile_path()
if self.makefile_path and self.proj_path:
path = setupVars.make_relative_path(self.makefile_path, self.proj_path)
path = myXML.make_relative_path(self.makefile_path, self.proj_path)
self.makefile_edit.setText(path)
self.update()
@@ -417,7 +446,7 @@ class VarEditor(QWidget):
def __open_variable_selector(self):
if not self.vars_list:
QMessageBox.warning(self, "Нет переменных", "Сначала загрузите или обновите переменные.")
QMessageBox.warning(self, "Нет переменных", f"Сначала загрузите переменные ({scan_title}).")
return
dlg = VariableSelectorDialog(self.vars_list, self.structs, self.typedef_map, self.xml_path, self)
@@ -438,9 +467,12 @@ class VarEditor(QWidget):
if not self.makefile_path or not os.path.isfile(self.makefile_path):
print("makefile файл не найден или путь пустой")
return
if os.path.abspath(self.makefile_path) == os.path.abspath(self.proj_path):
print("makefile_path совпадает с proj_path — игнор")
return
try:
root, tree = scanVars.safe_parse_xml(self.xml_path)
root, tree = myXML.safe_parse_xml(self.xml_path)
if root is None:
return
@@ -448,12 +480,18 @@ class VarEditor(QWidget):
root.set("proj_path", self.proj_path.replace("\\", "/"))
if self.makefile_path and os.path.isfile(self.makefile_path):
rel_makefile = setupVars.make_relative_path(self.makefile_path, self.proj_path)
rel_makefile = myXML.make_relative_path(self.makefile_path, self.proj_path)
root.set("makefile_path", rel_makefile)
# Если результат — абсолютный путь, не записываем
if not os.path.isabs(rel_makefile):
root.set("makefile_path", rel_makefile)
if self.structs_path and os.path.isfile(self.structs_path):
rel_struct = setupVars.make_relative_path(self.structs_path, self.proj_path)
rel_struct = myXML.make_relative_path(self.structs_path, self.proj_path)
root.set("structs_path", rel_struct)
if not os.path.isabs(rel_struct):
root.set("structs_path", rel_struct)
vars_elem = root.find('variables')
if vars_elem is None:
@@ -524,8 +562,7 @@ class VarEditor(QWidget):
# Преобразуем дерево в строку
self.table.check()
scanVars.indent_xml(root)
ET.ElementTree(root).write(self.xml_path, encoding="utf-8", xml_declaration=True)
myXML.fwrite(root, self.xml_path)
except Exception as e:
print(f"Ошибка при сохранении XML: {e}")
@@ -533,7 +570,10 @@ class VarEditor(QWidget):
def __open_output_file_with_program(self):
output_path = self.get_output_path()
if not output_path:
QMessageBox.warning(self, "Ошибка", "Путь к output-файлу не задан.")
QMessageBox.warning(self,
"Ошибка",
"Путь к debug_var.c не задан."
f"Пожалуйста, укажите его в поле '{output_path_title[:-1]}'.")
return
output_file = os.path.join(output_path, "debug_vars.c")

View File

@@ -6,7 +6,7 @@ from PySide2.QtWidgets import (
from PySide2.QtGui import QKeySequence, QKeyEvent
from PySide2.QtCore import Qt, QStringListModel, QSettings
import setupVars
import scanVars
import myXML
array_re = re.compile(r'^(\w+)\[(\d+)\]$')
@@ -161,18 +161,23 @@ class VariableSelectorDialog(QDialog):
def show_matching_path(item, level=0):
name = item.text(0).lower()
# Проверяем соответствие до длины path_parts
# По умолчанию не совпадает
matched = False
# Проверяем путь
if not path_parts:
matched = True
else:
elif path_matches_search(name, path_parts[:level+1]):
matched = True
# Исключаем "плоские" элементы на верхнем уровне при глубоком поиске
if level == 0 and item.childCount() == 0 and len(path_parts) > 1:
matched = False
# Проверяем совпадение по пути
if path_matches_search(name, path_parts[:level+1]):
matched = True
item.setHidden(not matched)
# Раскрываем, если это не последний уровень поиска
# Раскрываем узел, если он соответствует и ещё есть путь вниз
if matched and level < len(path_parts) - 1:
item.setExpanded(True)
else:
@@ -186,6 +191,7 @@ class VariableSelectorDialog(QDialog):
return matched or matched_any_child
# Если в поиске нет точки — особая логика для первого уровня
if '.' not in text and '->' not in text and text != '':
for i in range(self.tree.topLevelItemCount()):
@@ -462,8 +468,7 @@ class VariableSelectorDialog(QDialog):
set_text('show_var', 'false')
set_text('enable', 'false')
scanVars.indent_xml(root)
ET.ElementTree(root).write(self.xml_path, encoding="utf-8", xml_declaration=True)
myXML.fwrite(root, self.xml_path)
self.populate_tree()
self.done(QDialog.Accepted)
@@ -513,8 +518,7 @@ class VariableSelectorDialog(QDialog):
self.all_vars[:] = [v for v in self.all_vars if v['name'] not in selected_names]
if removed_any:
scanVars.indent_xml(root)
ET.ElementTree(root).write(self.xml_path, encoding="utf-8", xml_declaration=True)
myXML.fwrite(root, self.xml_path)
self.populate_tree()
self.filter_tree()

View File

@@ -9,7 +9,7 @@ import re
import xml.etree.ElementTree as ET
from pathlib import Path
from xml.dom import minidom
from scanVars import indent_xml
import myXML
import argparse
@@ -216,8 +216,7 @@ def add_new_vars_to_xml(proj_path, xml_rel_path, output_path):
added_count += 1
if added_count > 0:
indent_xml(root)
ET.ElementTree(root).write(xml_full_path, encoding="utf-8", xml_declaration=True)
myXML.fwrite(root, xml_full_path)
print(f"[INFO] В XML добавлено новых переменных: {added_count}")
return True

78
Src/myXML.py Normal file
View File

@@ -0,0 +1,78 @@
import os
import xml.etree.ElementTree as ET
def make_absolute_path(path, base_path):
if not os.path.isabs(path) and os.path.isdir(base_path):
try:
return os.path.abspath(os.path.join(base_path, path))
except Exception:
pass # На случай сбоя в os.path.join или abspath
elif os.path.isabs(path):
return os.path.abspath(path)
else:
return path
def make_relative_path(abs_path, base_path):
abs_path = os.path.abspath(abs_path)
base_path = os.path.abspath(base_path)
# Разбиваем на списки директорий
abs_parts = abs_path.split(os.sep)
base_parts = base_path.split(os.sep)
# Проверяем, является ли base_path настоящим префиксом пути (по папкам)
if abs_parts[:len(base_parts)] == base_parts:
rel_parts = abs_parts[len(base_parts):]
return "/".join(rel_parts)
# Иначе пробуем relpath
try:
return os.path.relpath(abs_path, base_path).replace("\\", "/")
except Exception:
return abs_path.replace("\\", "/")
def indent_xml(elem, level=0):
indent = " "
i = "\n" + level * indent
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + indent
for child in elem:
indent_xml(child, level + 1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
def fwrite(root, xml_full_path):
indent_xml(root)
ET.ElementTree(root).write(xml_full_path, encoding="utf-8", xml_declaration=True)
def safe_parse_xml(xml_path):
"""
Безопасно парсит XML-файл.
Возвращает кортеж (root, tree) или (None, None) при ошибках.
"""
if not xml_path or not os.path.isfile(xml_path):
print(f"Файл '{xml_path}' не найден или путь пустой")
return None, None
try:
if os.path.getsize(xml_path) == 0:
return None, None
tree = ET.parse(xml_path)
root = tree.getroot()
return root, tree
except ET.ParseError as e:
print(f"Ошибка парсинга XML файла '{xml_path}': {e}")
return None, None
except Exception as e:
print(f"Неожиданная ошибка при чтении XML файла '{xml_path}': {e}")
return None, None

View File

@@ -14,22 +14,10 @@ from xml.dom import minidom
from parseMakefile import parse_makefile
from collections import deque
import argparse
from setupVars import make_relative_path
import myXML
BITFIELD_WIDTHS = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}
def indent_xml(elem, level=0):
indent = " "
i = "\n" + level * indent
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + indent
for child in elem:
indent_xml(child, level + 1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
def is_frozen():
# Для Nuitka --onefile
return getattr(sys, 'frozen', False)
@@ -529,31 +517,6 @@ def analyze_typedefs_and_structs_across_files(c_files, include_dirs):
return resolved_typedefs, resolved_structs
def safe_parse_xml(xml_path):
"""
Безопасно парсит XML-файл.
Возвращает кортеж (root, tree) или (None, None) при ошибках.
"""
if not xml_path or not os.path.isfile(xml_path):
print(f"Файл '{xml_path}' не найден или путь пустой")
return None, None
try:
if os.path.getsize(xml_path) == 0:
return None, None
tree = ET.parse(xml_path)
root = tree.getroot()
return root, tree
except ET.ParseError as e:
print(f"Ошибка парсинга XML файла '{xml_path}': {e}")
return None, None
except Exception as e:
print(f"Неожиданная ошибка при чтении XML файла '{xml_path}': {e}")
return None, None
def read_vars_from_xml(xml_path):
xml_full_path = os.path.normpath(xml_path)
vars_data = {}
@@ -561,7 +524,7 @@ def read_vars_from_xml(xml_path):
if not os.path.exists(xml_full_path):
return vars_data # пусто, если файла нет
root, tree = safe_parse_xml(xml_full_path)
root, tree = myXML.safe_parse_xml(xml_full_path)
if root is None:
return vars_data
@@ -612,12 +575,22 @@ def generate_xml_output(proj_path, xml_path, unique_vars, h_files_needed, vars_n
if os.path.isfile(xml_full_path):
existing_vars_data = read_vars_from_xml(xml_full_path)
# --- Новый блок: формируем атрибуты корневого тега ---
analysis_attrs = {"proj_path": proj_path}
# --- Новый блок: формируем атрибуты корневого тега с относительными путями ---
proj_path = os.path.abspath(proj_path)
analysis_attrs = {
"proj_path": proj_path.replace("\\", "/")
}
if makefile_path:
analysis_attrs["makefile_path"] = makefile_path
rel_makefile = make_relative_path(makefile_path, proj_path)
if not os.path.isabs(rel_makefile):
analysis_attrs["makefile_path"] = rel_makefile.replace("\\", "/")
if structs_xml_path:
analysis_attrs["structs_path"] = structs_xml_path
rel_struct = make_relative_path(structs_xml_path, proj_path)
if not os.path.isabs(rel_struct):
analysis_attrs["structs_path"] = rel_struct.replace("\\", "/")
root = ET.Element("analysis", attrib=analysis_attrs)
@@ -679,8 +652,7 @@ def generate_xml_output(proj_path, xml_path, unique_vars, h_files_needed, vars_n
ET.SubElement(var_elem, "file").text = rel_file.replace("\\", "/")
# Форматирование с отступами
indent_xml(root)
ET.ElementTree(root).write(xml_full_path, encoding="utf-8", xml_declaration=True)
myXML.fwrite(root, xml_full_path)
optional_printf(PRINT_STATUS, f"[XML] Variables saved to {xml_full_path}")
@@ -722,8 +694,7 @@ def write_typedefs_and_structs_to_xml(proj_path, xml_path, typedefs, structs):
ET.SubElement(typedefs_elem, "typedef", name=name, type=underlying)
# Преобразуем в красиво отформатированную XML-строку
indent_xml(root)
ET.ElementTree(root).write(xml_full_path, encoding="utf-8", xml_declaration=True)
myXML.fwrite(root, xml_full_path)
print(f"[XML] Typedefs and structs saved to: {xml_full_path}")
@@ -939,9 +910,15 @@ def run_scan(proj_path, makefile_path, output_xml, verbose=2):
typedefs = dict(sorted(typedefs.items()))
structs = dict(sorted(structs.items()))
print('Progress: "Writting XML..."')
print("Progress: 0/2")
print("[XML] Creating structs.xml...")
structs_xml = os.path.join(os.path.dirname(output_xml), "structs.xml")
write_typedefs_and_structs_to_xml(proj_path, structs_xml, typedefs, structs)
print("Progress: 1/2")
print("[XML] Creating vars.xml...")
generate_xml_output(proj_path, output_xml, vars, includes, externs, structs_xml, makefile_path)
print("Progress: 2/2")
print('Progress: "Done"')
print('Progress: 1/1')

View File

@@ -5,7 +5,7 @@ import xml.etree.ElementTree as ET
from generateVars import map_type_to_pt, get_iq_define, type_map
from enum import IntEnum
import scanVars
import setupVars
import myXML
def make_absolute_path(path, base_path):
@@ -41,7 +41,7 @@ def make_relative_path(abs_path, base_path):
def parse_vars(filename, typedef_map=None):
root, tree = safe_parse_xml(filename)
root, tree = myXML.safe_parse_xml(filename)
if root is None:
return []
@@ -93,15 +93,14 @@ def parse_vars(filename, typedef_map=None):
'static': var.findtext('static', 'false') == 'true',
})
scanVars.indent_xml(root)
ET.ElementTree(root).write(filename, encoding="utf-8", xml_declaration=True)
myXML.fwrite(root, filename)
return vars_list
# 2. Парсим structSup.xml
def parse_structs(filename):
root, tree = safe_parse_xml(filename)
root, tree = myXML.safe_parse_xml(filename)
if root is None:
return {}, {}
@@ -153,32 +152,6 @@ def parse_structs(filename):
return structs, typedef_map
def safe_parse_xml(xml_path):
"""
Безопасно парсит XML-файл.
Возвращает кортеж (root, tree) или (None, None) при ошибках.
"""
if not xml_path or not os.path.isfile(xml_path):
#print(f"Файл '{xml_path}' не найден или путь пустой")
return None, None
try:
if os.path.getsize(xml_path) == 0:
return None, None
tree = ET.parse(xml_path)
root = tree.getroot()
return root, tree
except ET.ParseError as e:
print(f"Ошибка парсинга XML файла '{xml_path}': {e}")
return None, None
except Exception as e:
print(f"Неожиданная ошибка при чтении XML файла '{xml_path}': {e}")
return None, None
def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, depth=0):
if depth > 10:

0
Src/setupVars_GUI.py Normal file
View File