на win7 работает и Nuitka и PyInstaller
добавлен прогрессбар на сканирование переменных исправлены мелкие баги
This commit is contained in:
@@ -13,6 +13,7 @@ from generateVars import run_generate
|
||||
from setupVars import *
|
||||
from VariableSelector import *
|
||||
from VariableTable import VariableTableWidget, rows
|
||||
from scanVarGUI import *
|
||||
|
||||
from PySide2.QtWidgets import (
|
||||
QApplication, QWidget, QTableWidget, QTableWidgetItem,
|
||||
@@ -24,61 +25,6 @@ from PySide2.QtGui import QTextCursor, QKeyEvent
|
||||
from PySide2.QtCore import Qt, QProcess, QObject, Signal, QSettings
|
||||
|
||||
|
||||
|
||||
class EmittingStream(QObject):
|
||||
text_written = Signal(str)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._buffer = ""
|
||||
|
||||
def write(self, text):
|
||||
self._buffer += text
|
||||
while '\n' in self._buffer:
|
||||
line, self._buffer = self._buffer.split('\n', 1)
|
||||
# Отправляем строку без '\n'
|
||||
self.text_written.emit(line)
|
||||
|
||||
def flush(self):
|
||||
if self._buffer:
|
||||
self.text_written.emit(self._buffer)
|
||||
self._buffer = ""
|
||||
|
||||
|
||||
class ProcessOutputWindowDummy(QDialog):
|
||||
def __init__(self, on_done_callback):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Поиск переменных...")
|
||||
self.resize(600, 400)
|
||||
self.setModal(True) # сделаем окно модальным
|
||||
|
||||
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.layout.addWidget(self.btn_close)
|
||||
|
||||
self.btn_close.clicked.connect(self.__handle_done)
|
||||
self._on_done_callback = on_done_callback
|
||||
|
||||
def __handle_done(self):
|
||||
if self._on_done_callback:
|
||||
self._on_done_callback()
|
||||
self.accept() # закрыть диалог
|
||||
|
||||
def append_text(self, text):
|
||||
cursor = self.output_edit.textCursor()
|
||||
cursor.movePosition(QTextCursor.End)
|
||||
for line in text.splitlines():
|
||||
self.output_edit.append(line)
|
||||
self.output_edit.setTextCursor(cursor)
|
||||
self.output_edit.ensureCursorVisible()
|
||||
|
||||
|
||||
|
||||
# 3. UI: таблица с переменными
|
||||
class VarEditor(QWidget):
|
||||
def __init__(self):
|
||||
@@ -236,11 +182,9 @@ class VarEditor(QWidget):
|
||||
|
||||
# Создаём окно с кнопкой "Готово"
|
||||
self.proc_win = ProcessOutputWindowDummy(self.__after_scanvars_finished)
|
||||
self.emitting_stream = self.proc_win.emitting_stream # ключевая строка!
|
||||
self.proc_win.show()
|
||||
|
||||
self.emitting_stream = EmittingStream()
|
||||
self.emitting_stream.text_written.connect(self.proc_win.append_text)
|
||||
|
||||
def run_scan_wrapper():
|
||||
try:
|
||||
old_stdout = sys.stdout
|
||||
@@ -562,15 +506,8 @@ class VarEditor(QWidget):
|
||||
set_sub_elem_text(var_elem, 'static', static_val)
|
||||
|
||||
# Преобразуем дерево в строку
|
||||
rough_string = ET.tostring(root, encoding="utf-8")
|
||||
|
||||
# Парсим и форматируем с отступами
|
||||
reparsed = minidom.parseString(rough_string)
|
||||
pretty_xml = reparsed.toprettyxml(indent=" ")
|
||||
|
||||
# Записываем в файл
|
||||
with open(self.xml_path, "w", encoding="utf-8") as f:
|
||||
f.write(pretty_xml)
|
||||
indent_xml(root)
|
||||
ET.ElementTree(root).write(self.xml_path, encoding="utf-8", xml_declaration=True)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Ошибка при сохранении XML: {e}")
|
||||
|
||||
@@ -458,16 +458,8 @@ class VariableSelectorDialog(QDialog):
|
||||
set_text('show_var', 'false')
|
||||
set_text('enable', 'false')
|
||||
|
||||
# Преобразуем дерево в строку
|
||||
rough_string = ET.tostring(root, encoding="utf-8")
|
||||
|
||||
# Парсим и форматируем с отступами
|
||||
reparsed = minidom.parseString(rough_string)
|
||||
pretty_xml = reparsed.toprettyxml(indent=" ")
|
||||
|
||||
# Записываем в файл
|
||||
with open(self.xml_path, "w", encoding="utf-8") as f:
|
||||
f.write(pretty_xml)
|
||||
indent_xml(root)
|
||||
ET.ElementTree(root).write(self.xml_path, encoding="utf-8", xml_declaration=True)
|
||||
|
||||
self.populate_tree()
|
||||
self.accept()
|
||||
@@ -517,16 +509,8 @@ class VariableSelectorDialog(QDialog):
|
||||
self.all_vars[:] = [v for v in self.all_vars if v['name'] not in selected_names]
|
||||
|
||||
if removed_any:
|
||||
# Преобразуем дерево в строку
|
||||
rough_string = ET.tostring(root, encoding="utf-8")
|
||||
|
||||
# Парсим и форматируем с отступами
|
||||
reparsed = minidom.parseString(rough_string)
|
||||
pretty_xml = reparsed.toprettyxml(indent=" ")
|
||||
|
||||
# Записываем в файл
|
||||
with open(self.xml_path, "w", encoding="utf-8") as f:
|
||||
f.write(pretty_xml)
|
||||
indent_xml(root)
|
||||
ET.ElementTree(root).write(self.xml_path, encoding="utf-8", xml_declaration=True)
|
||||
|
||||
self.populate_tree()
|
||||
self.filter_tree()
|
||||
|
||||
Binary file not shown.
Binary file not shown.
BIN
Src/__pycache__/scanVarGUI.cpython-37.pyc
Normal file
BIN
Src/__pycache__/scanVarGUI.cpython-37.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -9,6 +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 argparse
|
||||
|
||||
|
||||
@@ -215,16 +216,9 @@ def add_new_vars_to_xml(proj_path, xml_rel_path, output_path):
|
||||
added_count += 1
|
||||
|
||||
if added_count > 0:
|
||||
# Преобразуем дерево в строку
|
||||
rough_string = ET.tostring(root, encoding="utf-8")
|
||||
indent_xml(root)
|
||||
ET.ElementTree(root).write(xml_full_path, encoding="utf-8", xml_declaration=True)
|
||||
|
||||
# Парсим и форматируем с отступами
|
||||
reparsed = minidom.parseString(rough_string)
|
||||
pretty_xml = reparsed.toprettyxml(indent=" ")
|
||||
|
||||
# Записываем в файл
|
||||
with open(xml_full_path, "w", encoding="utf-8") as f:
|
||||
f.write(pretty_xml)
|
||||
print(f"[INFO] В XML добавлено новых переменных: {added_count}")
|
||||
return True
|
||||
else:
|
||||
|
||||
BIN
Src/libclang.dll
Normal file
BIN
Src/libclang.dll
Normal file
Binary file not shown.
135
Src/scanVarGUI.py
Normal file
135
Src/scanVarGUI.py
Normal file
@@ -0,0 +1,135 @@
|
||||
|
||||
import sys
|
||||
import re
|
||||
import subprocess
|
||||
import xml.etree.ElementTree as ET
|
||||
from generateVars import type_map
|
||||
from enum import IntEnum
|
||||
import threading
|
||||
from scanVars import run_scan
|
||||
from generateVars import run_generate
|
||||
from setupVars import *
|
||||
from VariableSelector import *
|
||||
from VariableTable import VariableTableWidget, rows
|
||||
from scanVarGUI import *
|
||||
|
||||
from PySide2.QtWidgets import (
|
||||
QApplication, QWidget, QTableWidget, QTableWidgetItem,
|
||||
QCheckBox, QComboBox, QLineEdit, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||
QCompleter, QAbstractItemView, QLabel, QMessageBox, QFileDialog, QTextEdit,
|
||||
QDialog, QTreeWidget, QTreeWidgetItem, QSizePolicy, QHeaderView, QProgressBar
|
||||
)
|
||||
from PySide2.QtGui import QTextCursor, QKeyEvent
|
||||
from PySide2.QtCore import Qt, QProcess, QObject, Signal, QSettings
|
||||
|
||||
|
||||
class EmittingStream(QObject):
|
||||
text_written = Signal(str)
|
||||
progress_updated = Signal(str, int, int) # bar_name, current, total
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._buffer = ""
|
||||
self._current_bar_name = None
|
||||
|
||||
def write(self, text):
|
||||
self._buffer += text
|
||||
while '\n' in self._buffer:
|
||||
line, self._buffer = self._buffer.split('\n', 1)
|
||||
|
||||
if line.startswith('Progress: "') and line.endswith('"'):
|
||||
bar_name = self._extract_bar_name(line)
|
||||
if bar_name:
|
||||
self._current_bar_name = bar_name
|
||||
continue # не выводим строку
|
||||
elif self._is_progress_line(line):
|
||||
current, total = self._extract_progress(line)
|
||||
if current is not None and total is not None:
|
||||
name = self._current_bar_name if self._current_bar_name else "Progress"
|
||||
self.progress_updated.emit(name, current, total)
|
||||
continue # не выводим строку
|
||||
|
||||
# если не прогресс и не имя — выводим
|
||||
self.text_written.emit(line)
|
||||
|
||||
def _extract_bar_name(self, line):
|
||||
# точно обрезаем вручную
|
||||
prefix = 'Progress: "'
|
||||
suffix = '"'
|
||||
if line.startswith(prefix) and line.endswith(suffix):
|
||||
return line[len(prefix):-1]
|
||||
return None
|
||||
|
||||
def _is_progress_line(self, line):
|
||||
return re.match(r'^Progress:\s*\d+\s*/\s*\d+$', line) is not None
|
||||
|
||||
def _extract_progress(self, line):
|
||||
match = re.match(r'^Progress:\s*(\d+)\s*/\s*(\d+)$', line)
|
||||
if match:
|
||||
return int(match.group(1)), int(match.group(2))
|
||||
return None, None
|
||||
|
||||
def flush(self):
|
||||
if self._buffer:
|
||||
line = self._buffer.strip()
|
||||
if not self._is_progress_line(line) and not self._extract_bar_name(line):
|
||||
self.text_written.emit(line)
|
||||
self._buffer = ""
|
||||
|
||||
|
||||
class ProcessOutputWindowDummy(QDialog):
|
||||
def __init__(self, on_done_callback):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Поиск переменных...")
|
||||
self.resize(600, 480)
|
||||
self.setModal(True)
|
||||
|
||||
self.layout = QVBoxLayout(self)
|
||||
|
||||
self.output_edit = QTextEdit()
|
||||
self.output_edit.setReadOnly(True)
|
||||
self.layout.addWidget(self.output_edit)
|
||||
|
||||
# Метка с именем прогрессбара
|
||||
self.progress_label = QLabel("Progress:")
|
||||
self.layout.addWidget(self.progress_label)
|
||||
|
||||
# Прогрессбар
|
||||
self.progress_bar = QProgressBar()
|
||||
self.progress_bar.setMinimum(0)
|
||||
self.progress_bar.setValue(0)
|
||||
self.layout.addWidget(self.progress_bar)
|
||||
|
||||
self.btn_close = QPushButton("Закрыть")
|
||||
self.btn_close.setEnabled(False)
|
||||
self.layout.addWidget(self.btn_close)
|
||||
|
||||
self.btn_close.clicked.connect(self.__handle_done)
|
||||
self._on_done_callback = on_done_callback
|
||||
|
||||
self.emitting_stream = EmittingStream()
|
||||
self.emitting_stream.text_written.connect(self.append_text)
|
||||
self.emitting_stream.progress_updated.connect(self.update_progress)
|
||||
|
||||
sys.stdout = self.emitting_stream
|
||||
|
||||
def __handle_done(self):
|
||||
sys.stdout = sys.__stdout__ # восстановить stdout
|
||||
if self._on_done_callback:
|
||||
self._on_done_callback()
|
||||
self.accept()
|
||||
|
||||
def append_text(self, text):
|
||||
cursor = self.output_edit.textCursor()
|
||||
cursor.movePosition(QTextCursor.End)
|
||||
if not text.endswith('\n'):
|
||||
text += '\n'
|
||||
for line in text.splitlines(True):
|
||||
cursor.insertText(line)
|
||||
self.output_edit.setTextCursor(cursor)
|
||||
self.output_edit.ensureCursorVisible()
|
||||
|
||||
def update_progress(self, bar_name, current, total):
|
||||
self.progress_label.setText(f"{bar_name}")
|
||||
self.progress_bar.setMaximum(total)
|
||||
self.progress_bar.setValue(current)
|
||||
137
Src/scanVars.py
137
Src/scanVars.py
@@ -15,17 +15,68 @@ from parseMakefile import parse_makefile
|
||||
from collections import deque
|
||||
import argparse
|
||||
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)
|
||||
|
||||
# Укажи полный путь к libclang.dll — поменяй на свой путь или оставь относительный
|
||||
dll_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "build/libclang.dll")
|
||||
def get_base_path():
|
||||
if is_frozen():
|
||||
# В Nuitka onefile распаковывается в папку с самим exe во временной директории
|
||||
return os.path.dirname(sys.executable)
|
||||
else:
|
||||
# Режим разработки
|
||||
return os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
if getattr(sys, 'frozen', False): # PyInstaller/Nuitka
|
||||
base_path = sys._MEIPASS if hasattr(sys, '_MEIPASS') else os.path.dirname(sys.executable)
|
||||
Config.set_library_file(os.path.join(base_path, "libclang.dll"))
|
||||
def print_embedded_dlls():
|
||||
# Папка временной распаковки для onefile приложений Nuitka/PyInstaller
|
||||
base_path = get_base_path()
|
||||
print(f"Scanning DLLs in temporary folder: {base_path}")
|
||||
|
||||
dlls = []
|
||||
for root, dirs, files in os.walk(base_path):
|
||||
for file in files:
|
||||
if file.lower().endswith('.dll'):
|
||||
full_path = os.path.join(root, file)
|
||||
rel_path = os.path.relpath(full_path, base_path)
|
||||
dlls.append(rel_path)
|
||||
|
||||
if dlls:
|
||||
print("DLL files found:")
|
||||
for d in dlls:
|
||||
print(" -", d)
|
||||
else:
|
||||
print("No DLL files found in temporary folder.")
|
||||
|
||||
# Вызов при старте
|
||||
print_embedded_dlls()
|
||||
|
||||
base_path = get_base_path()
|
||||
print("Base path:", base_path)
|
||||
|
||||
# Указываем полный путь к libclang.dll внутри распакованного каталога
|
||||
dll_path = os.path.join(base_path, "libclang.dll")
|
||||
|
||||
if os.path.exists(dll_path):
|
||||
print("Loading libclang.dll from:", dll_path)
|
||||
Config.set_library_file(dll_path)
|
||||
else:
|
||||
# путь при разработке
|
||||
Config.set_library_file("F:/Work/Projects/TMS/TMS_new_bus/Src/DebugTools/build/libclang.dll")
|
||||
print("ERROR: libclang.dll not found at", dll_path)
|
||||
|
||||
|
||||
index = cindex.Index.create()
|
||||
PRINT_LEVEL = 2
|
||||
@@ -165,7 +216,9 @@ def analyze_variables_across_files(c_files, h_files, include_dirs):
|
||||
return vars_in_file
|
||||
|
||||
optional_printf(PRINT_STATUS, "Parsing header files (.h)...")
|
||||
for h in h_files:
|
||||
optional_printf(PRINT_STATUS, 'Progress: "Parsing variables from headers"')
|
||||
total_h = len(h_files)
|
||||
for i, h in enumerate(h_files, 1):
|
||||
vars_in_h = parse_file(h)
|
||||
for v in vars_in_h:
|
||||
name = v["name"]
|
||||
@@ -176,9 +229,12 @@ def analyze_variables_across_files(c_files, h_files, include_dirs):
|
||||
"static": v["static"],
|
||||
"file": v["file"]
|
||||
}
|
||||
optional_printf(PRINT_STATUS, f"Progress: {i}/{total_h}")
|
||||
|
||||
optional_printf(PRINT_STATUS, "Parsing source files (.c)...")
|
||||
for c in c_files:
|
||||
optional_printf(PRINT_STATUS, 'Progress: "Parsing variables from sources files"')
|
||||
total_c = len(c_files)
|
||||
for i, c in enumerate(c_files, 1):
|
||||
vars_in_c = parse_file(c)
|
||||
for v in vars_in_c:
|
||||
name = v["name"]
|
||||
@@ -196,9 +252,12 @@ def analyze_variables_across_files(c_files, h_files, include_dirs):
|
||||
"static": v["static"],
|
||||
"file": v["file"]
|
||||
}
|
||||
optional_printf(PRINT_STATUS, f"Progress: {i}/{total_c}")
|
||||
|
||||
optional_printf(PRINT_STATUS, "Checking which variables need explicit extern declaration...")
|
||||
for name, info in unique_vars.items():
|
||||
optional_printf(PRINT_STATUS, 'Progress: "Checking extern declarations"')
|
||||
total_vars = len(unique_vars)
|
||||
for i, (name, info) in enumerate(unique_vars.items(), 1):
|
||||
if not info["extern"] and not info["static"] and info["file"].endswith('.c'):
|
||||
extern_declared = False
|
||||
for h in h_files_needed:
|
||||
@@ -210,6 +269,7 @@ def analyze_variables_across_files(c_files, h_files, include_dirs):
|
||||
"type": info["type"],
|
||||
"file": info["file"]
|
||||
}
|
||||
optional_printf(PRINT_STATUS, f"Progress: {i}/{total_vars}")
|
||||
|
||||
optional_printf(PRINT_STATUS, "Analysis complete.")
|
||||
optional_printf(PRINT_STATUS, f"\tTotal unique variables found: {len(unique_vars)}")
|
||||
@@ -316,9 +376,21 @@ def analyze_typedefs_and_struct(typedefs, structs):
|
||||
substituted_fields[fname] = resolved_type
|
||||
substituted_structs[resolved_sname] = substituted_fields """
|
||||
|
||||
# Раскрываем typedef'ы
|
||||
optional_printf(PRINT_STATUS, 'Progress: "Resolving typedefs"')
|
||||
total_typedefs = len(typedefs)
|
||||
resolved_typedefs = {}
|
||||
for i, tname in enumerate(typedefs, 1):
|
||||
resolved = resolve_typedef_rec(tname)
|
||||
resolved_typedefs[tname] = resolved
|
||||
optional_printf(4, f"\tTypedef {tname} resolved")
|
||||
optional_printf(PRINT_STATUS, f"Progress: {i}/{total_typedefs}")
|
||||
|
||||
# Теперь раскрываем вложенные структуры
|
||||
optional_printf(PRINT_STATUS, 'Progress: "Resolving structs"')
|
||||
total_structs = len(structs)
|
||||
resolved_structs = {}
|
||||
for sname, fields in structs.items():
|
||||
for i, (sname, fields) in enumerate(structs.items(), 1):
|
||||
if "(unnamed" in sname:
|
||||
optional_printf(4, f" Skipping anonymous struct/union: {sname}")
|
||||
continue
|
||||
@@ -327,13 +399,7 @@ def analyze_typedefs_and_struct(typedefs, structs):
|
||||
resolved_fields = resolve_struct_fields(fields)
|
||||
resolved_structs[sname] = resolved_fields
|
||||
optional_printf(PRINT_DEBUG, f"\tStruct {sname} resolved")
|
||||
|
||||
# Раскрываем typedef'ы в отдельном шаге
|
||||
resolved_typedefs = {}
|
||||
for tname in typedefs:
|
||||
resolved = resolve_typedef_rec(tname)
|
||||
resolved_typedefs[tname] = resolved
|
||||
optional_printf(4, f"\tTypedef {tname} resolved")
|
||||
optional_printf(PRINT_STATUS, f"Progress: {i}/{total_structs}")
|
||||
|
||||
return resolved_typedefs, resolved_structs
|
||||
|
||||
@@ -441,7 +507,9 @@ def analyze_typedefs_and_structs_across_files(c_files, include_dirs):
|
||||
visit(tu.cursor)
|
||||
return typedefs, structs
|
||||
|
||||
for c_file in c_files:
|
||||
optional_printf(PRINT_STATUS, 'Progress: "Resolving structs and typedefs"')
|
||||
total_files = len(c_files)
|
||||
for i, c_file in enumerate(c_files, 1):
|
||||
typedefs_in_file, structs_in_file = parse_file(c_file)
|
||||
for name, underlying in typedefs_in_file.items():
|
||||
if name not in unique_typedefs_raw:
|
||||
@@ -449,6 +517,7 @@ def analyze_typedefs_and_structs_across_files(c_files, include_dirs):
|
||||
for sname, fields in structs_in_file.items():
|
||||
if sname not in unique_structs_raw:
|
||||
unique_structs_raw[sname] = fields
|
||||
optional_printf(PRINT_STATUS, f"Progress: {i}/{total_files}")
|
||||
|
||||
# Теперь раскроем typedef и структуры, учитывая вложения
|
||||
resolved_typedefs, resolved_structs = analyze_typedefs_and_struct(unique_typedefs_raw, unique_structs_raw)
|
||||
@@ -523,9 +592,19 @@ def read_vars_from_xml(xml_path):
|
||||
|
||||
return vars_data
|
||||
|
||||
def make_relative_if_possible(path, base):
|
||||
if not path:
|
||||
return ''
|
||||
if not os.path.isabs(path):
|
||||
return path # уже относительный
|
||||
try:
|
||||
rel = os.path.relpath(path, base)
|
||||
return rel
|
||||
except ValueError as e:
|
||||
print(f"[WARNING] relpath error between '{path}' and '{base}': {e}")
|
||||
return path # оставляем абсолютным
|
||||
|
||||
def generate_xml_output(proj_path, xml_path, unique_vars, h_files_needed, vars_need_extern, structs_xml_path=None, makefile_path=None):
|
||||
|
||||
xml_full_path = os.path.normpath(xml_path)
|
||||
|
||||
# Проверяем, существует ли файл, только тогда читаем из него
|
||||
@@ -579,7 +658,7 @@ def generate_xml_output(proj_path, xml_path, unique_vars, h_files_needed, vars_n
|
||||
ET.SubElement(var_elem, "return_type").text = info.get('return_type', 'int')
|
||||
|
||||
ET.SubElement(var_elem, "type").text = info.get('type', 'unknown')
|
||||
rel_file = os.path.relpath(info.get('file', ''), proj_path) if info.get('file') else ''
|
||||
rel_file = make_relative_if_possible(info.get('file', ''), proj_path).replace("\\", "/")
|
||||
ET.SubElement(var_elem, "file").text = rel_file.replace("\\", "/") if rel_file else ''
|
||||
ET.SubElement(var_elem, "extern").text = str(info.get('extern', False)).lower()
|
||||
ET.SubElement(var_elem, "static").text = str(info.get('static', False)).lower()
|
||||
@@ -600,12 +679,8 @@ def generate_xml_output(proj_path, xml_path, unique_vars, h_files_needed, vars_n
|
||||
ET.SubElement(var_elem, "file").text = rel_file.replace("\\", "/")
|
||||
|
||||
# Форматирование с отступами
|
||||
rough_string = ET.tostring(root, encoding="utf-8")
|
||||
reparsed = minidom.parseString(rough_string)
|
||||
pretty_xml = reparsed.toprettyxml(indent=" ")
|
||||
|
||||
with open(xml_full_path, "w", encoding="utf-8") as f:
|
||||
f.write(pretty_xml)
|
||||
indent_xml(root)
|
||||
ET.ElementTree(root).write(xml_full_path, encoding="utf-8", xml_declaration=True)
|
||||
|
||||
optional_printf(PRINT_STATUS, f"[XML] Variables saved to {xml_full_path}")
|
||||
|
||||
@@ -647,12 +722,8 @@ def write_typedefs_and_structs_to_xml(proj_path, xml_path, typedefs, structs):
|
||||
ET.SubElement(typedefs_elem, "typedef", name=name, type=underlying)
|
||||
|
||||
# Преобразуем в красиво отформатированную XML-строку
|
||||
rough_string = ET.tostring(root, encoding="utf-8")
|
||||
reparsed = minidom.parseString(rough_string)
|
||||
pretty_xml = reparsed.toprettyxml(indent=" ")
|
||||
|
||||
with open(xml_full_path, "w", encoding="utf-8") as f:
|
||||
f.write(pretty_xml)
|
||||
indent_xml(root)
|
||||
ET.ElementTree(root).write(xml_full_path, encoding="utf-8", xml_declaration=True)
|
||||
|
||||
print(f"[XML] Typedefs and structs saved to: {xml_full_path}")
|
||||
|
||||
|
||||
@@ -93,16 +93,8 @@ def parse_vars(filename, typedef_map=None):
|
||||
'static': var.findtext('static', 'false') == 'true',
|
||||
})
|
||||
|
||||
# Преобразуем дерево в строку
|
||||
rough_string = ET.tostring(root, encoding="utf-8")
|
||||
|
||||
# Парсим и форматируем с отступами
|
||||
reparsed = minidom.parseString(rough_string)
|
||||
pretty_xml = reparsed.toprettyxml(indent=" ")
|
||||
|
||||
# Записываем в файл
|
||||
with open(filename, "w", encoding="utf-8") as f:
|
||||
f.write(pretty_xml)
|
||||
indent_xml(root)
|
||||
ET.ElementTree(root).write(filename, encoding="utf-8", xml_declaration=True)
|
||||
|
||||
return vars_list
|
||||
|
||||
|
||||
Reference in New Issue
Block a user