+сделано задание размера короткого имени + добавлена бета поддержка stm:+ - парс переменных из файла преокта Keil или makefile CubeIDE - запись в utf-8 для STM, вместо cp1251 для TMS - другой размер int (32 бита, вместо 16 бит) для STM
682 lines
25 KiB
Python
682 lines
25 KiB
Python
# build command
|
||
# pyinstaller --onefile --distpath . --workpath ./build --specpath ./build generate_debug_vars.py
|
||
# start script
|
||
# generate_debug_vars.exe F:\Work\Projects\TMS\TMS_new_bus\ Src/DebugTools/vars.xml Src/DebugTools
|
||
|
||
import sys
|
||
import os
|
||
import re
|
||
import lxml.etree as ET
|
||
from pathlib import Path
|
||
from xml.dom import minidom
|
||
import myXML
|
||
import argparse
|
||
|
||
shortnameSize = 10
|
||
|
||
# === Словарь соответствия типов XML → DebugVarType_t ===
|
||
type_map_tms = 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')],
|
||
|
||
('struct', 'pt_struct'),
|
||
('union', 'pt_union'),
|
||
|
||
*[(k, 'pt_ptr_int8') for k in ('signed char*', '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'),
|
||
|
||
('struct*', 'pt_ptr_struct'),
|
||
('union*', 'pt_ptr_union'),
|
||
|
||
|
||
*[(k, 'pt_arr_int8') for k in ('signed char[]', '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_arr_struct'),
|
||
('union[]', 'pt_arr_union'),
|
||
])
|
||
# === Словарь соответствия типов XML → DebugVarType_t ===
|
||
type_map_stm32 = dict([
|
||
|
||
*[(k, 'pt_int8') for k in (
|
||
'int8_t', 'signed char', 'char'
|
||
)],
|
||
|
||
# --- 8-bit unsigned ---
|
||
*[(k, 'pt_uint8') for k in (
|
||
'uint8_t', 'unsigned char'
|
||
)],
|
||
|
||
# --- 16-bit signed ---
|
||
*[(k, 'pt_int16') for k in (
|
||
'int16_t', 'short', 'short int', 'signed short', 'signed short int'
|
||
)],
|
||
|
||
# --- 16-bit unsigned ---
|
||
*[(k, 'pt_uint16') for k in (
|
||
'uint16_t', 'unsigned short', 'unsigned short int'
|
||
)],
|
||
|
||
# --- 32-bit signed ---
|
||
*[(k, 'pt_int32') for k in (
|
||
'int32_t', 'int', 'signed', 'signed int'
|
||
)],
|
||
|
||
# --- 32-bit unsigned ---
|
||
*[(k, 'pt_uint32') for k in (
|
||
'uint32_t', 'unsigned', 'unsigned int'
|
||
)],
|
||
|
||
# --- 64-bit signed ---
|
||
*[(k, 'pt_int64') for k in (
|
||
'int64_t', 'long long', 'signed long long', 'signed long long int'
|
||
)],
|
||
|
||
# --- 64-bit unsigned ---
|
||
*[(k, 'pt_uint64') for k in (
|
||
'uint64_t', 'unsigned long long', 'unsigned long long int'
|
||
)],
|
||
|
||
# --- Float ---
|
||
*[(k, 'pt_float') for k in (
|
||
'float', 'float32_t'
|
||
)],
|
||
|
||
# --- Struct and Union ---
|
||
('struct', 'pt_struct'),
|
||
('union', 'pt_union'),
|
||
('struct*', 'pt_ptr_struct'),
|
||
('union*', 'pt_ptr_union'),
|
||
('struct[]', 'pt_arr_struct'),
|
||
('union[]', 'pt_arr_union'),
|
||
|
||
# === POINTERS ===
|
||
|
||
# 8-bit
|
||
*[(k, 'pt_ptr_int8') for k in (
|
||
'int8_t*', 'signed char*', 'char*'
|
||
)],
|
||
*[(k, 'pt_ptr_uint8') for k in (
|
||
'uint8_t*', 'unsigned char*'
|
||
)],
|
||
|
||
# 16-bit
|
||
*[(k, 'pt_ptr_int16') for k in (
|
||
'int16_t*', 'short*', 'short int*', 'signed short*', 'signed short int*'
|
||
)],
|
||
*[(k, 'pt_ptr_uint16') for k in (
|
||
'uint16_t*', 'unsigned short*', 'unsigned short int*'
|
||
)],
|
||
|
||
# 32-bit
|
||
*[(k, 'pt_ptr_int32') for k in (
|
||
'int32_t*', 'int*', 'signed*', 'signed int*'
|
||
)],
|
||
*[(k, 'pt_ptr_uint32') for k in (
|
||
'uint32_t*', 'unsigned*', 'unsigned int*'
|
||
)],
|
||
|
||
# 64-bit
|
||
*[(k, 'pt_ptr_int64') for k in (
|
||
'int64_t*', 'long long*', 'signed long long*', 'signed long long int*'
|
||
)],
|
||
*[(k, 'pt_ptr_uint64') for k in (
|
||
'uint64_t*', 'unsigned long long*', 'unsigned long long int*'
|
||
)],
|
||
|
||
# float*
|
||
*[(k, 'pt_ptr_float') for k in (
|
||
'float*', 'float32_t*'
|
||
)],
|
||
|
||
# === ARRAYS ===
|
||
|
||
# 8-bit
|
||
*[(k, 'pt_arr_int8') for k in (
|
||
'int8_t[]', 'signed char[]', 'char[]'
|
||
)],
|
||
*[(k, 'pt_arr_uint8') for k in (
|
||
'uint8_t[]', 'unsigned char[]'
|
||
)],
|
||
|
||
# 16-bit
|
||
*[(k, 'pt_arr_int16') for k in (
|
||
'int16_t[]', 'short[]', 'short int[]', 'signed short[]', 'signed short int[]'
|
||
)],
|
||
*[(k, 'pt_arr_uint16') for k in (
|
||
'uint16_t[]', 'unsigned short[]', 'unsigned short int[]'
|
||
)],
|
||
|
||
# 32-bit
|
||
*[(k, 'pt_arr_int32') for k in (
|
||
'int32_t[]', 'int[]', 'signed[]', 'signed int[]'
|
||
)],
|
||
*[(k, 'pt_arr_uint32') for k in (
|
||
'uint32_t[]', 'unsigned[]', 'unsigned int[]'
|
||
)],
|
||
|
||
# 64-bit
|
||
*[(k, 'pt_arr_int64') for k in (
|
||
'int64_t[]', 'long long[]', 'signed long long[]', 'signed long long int[]'
|
||
)],
|
||
*[(k, 'pt_arr_uint64') for k in (
|
||
'uint64_t[]', 'unsigned long long[]', 'unsigned long long int[]'
|
||
)],
|
||
|
||
# float[]
|
||
*[(k, 'pt_arr_float') for k in (
|
||
'float[]', 'float32_t[]'
|
||
)],
|
||
])
|
||
type_map = type_map_tms
|
||
stm_flag_global = 0
|
||
def choose_type_map(stm_flag):
|
||
global type_map # объявляем, что будем менять глобальную переменную
|
||
global stm_flag_global # объявляем, что будем менять глобальную переменную
|
||
if stm_flag:
|
||
type_map = type_map_stm32
|
||
stm_flag_global = 1
|
||
else:
|
||
type_map = type_map_tms
|
||
stm_flag_global = 0
|
||
|
||
def map_type_to_pt(typename, varname=None, typedef_map=None):
|
||
typename_orig = typename.strip()
|
||
|
||
# Убираем const и volatile (чтобы не мешали проверке)
|
||
for qualifier in ('const', 'volatile'):
|
||
typename_orig = typename_orig.replace(qualifier, '')
|
||
typename_orig = typename_orig.strip()
|
||
|
||
# Проверка наличия массива [] или указателя *
|
||
is_array = bool(re.search(r'\[.*\]', typename_orig))
|
||
is_ptr = '*' in typename_orig
|
||
|
||
# Убираем все [] и * для получения базового типа
|
||
typename_base = re.sub(r'\[.*?\]', '', typename_orig).replace('*', '').strip()
|
||
typedef_maybe = typename_base
|
||
if typename_base.startswith('struct'):
|
||
typename_base = 'struct'
|
||
if typename_base.startswith('union'):
|
||
typename_base = 'union'
|
||
|
||
# Добавляем [] или * к базовому типу для поиска
|
||
if is_array:
|
||
typename_base = typename_base + '[]'
|
||
elif is_ptr:
|
||
typename_base = typename_base + '*'
|
||
else:
|
||
typename_base = typename_base
|
||
|
||
if typename_base in type_map:
|
||
return type_map[typename_base]
|
||
|
||
|
||
if '_iq' in typename_base and '_iqx' in type_map:
|
||
return type_map['_iqx']
|
||
|
||
# Если есть typedef_map — пробуем по нему
|
||
if typedef_map and typedef_maybe in typedef_map:
|
||
resolved = typedef_map[typedef_maybe].strip()
|
||
|
||
# Убираем const и volatile
|
||
for qualifier in ('const', 'volatile'):
|
||
resolved = resolved.replace(qualifier, '')
|
||
resolved = resolved.strip()
|
||
|
||
# Получаем базовый тип из typedef-а
|
||
base_t = re.sub(r'\[.*?\]', '', resolved).replace('*', '').strip()
|
||
|
||
if base_t.startswith('struct'):
|
||
base_t = 'struct'
|
||
if base_t.startswith('union'):
|
||
base_t = 'union'
|
||
|
||
if is_array:
|
||
base_t += '[]'
|
||
elif is_ptr:
|
||
base_t += '*'
|
||
|
||
# Пробуем по базовому имени
|
||
if base_t in type_map:
|
||
return type_map[base_t]
|
||
if '_iq' in base_t and '_iqx' in type_map:
|
||
return type_map['_iqx']
|
||
|
||
|
||
return 'pt_unknown'
|
||
|
||
|
||
|
||
def get_iq_define(vtype):
|
||
# Убираем все скобки массива, например: _iq[5] → _iq
|
||
vtype = re.sub(r'\[.*?\]', '', vtype).strip()
|
||
|
||
if '_iq' in vtype:
|
||
# Преобразуем _iqXX в t_iqXX
|
||
return 't' + vtype[vtype.index('_iq'):]
|
||
else:
|
||
return 't_iq_none'
|
||
|
||
def add_new_vars_to_xml(proj_path, xml_rel_path, output_path):
|
||
"""
|
||
new_vars — dict: ключ = имя переменной, значение = словарь с info (type, file, extern, static, enable, show_var и т.п.)
|
||
|
||
Если переменной нет в XML (в <variables>), добавляем её и сохраняем XML-файл.
|
||
|
||
Возвращает True если что-то добавлено и XML перезаписан, иначе False.
|
||
"""
|
||
|
||
# Считываем существующие переменные
|
||
parsed_vars = {}
|
||
if os.path.isfile(output_path):
|
||
with open(output_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||
for line in f:
|
||
# {(uint8_t *)&some.deep.var.name , pt_uint16 , t_iq15 , "ShortName"},
|
||
m = re.match(
|
||
r'{\s*\(uint8_t\s*\*\)\s*&([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)\s*,\s*(pt_\w+)\s*,\s*(t?iq_\w+)\s*,\s*"([^"]+)"',
|
||
line)
|
||
if m:
|
||
full_varname = m.group(1) # e.g., some.deep.var.name
|
||
pt_type = m.group(2)
|
||
iq_type = m.group(3)
|
||
return_type = m.group(4)
|
||
shortname = m.group(5)
|
||
|
||
parsed_vars[full_varname] = {
|
||
'pt_type': pt_type,
|
||
'iq_type': iq_type,
|
||
'enable': True,
|
||
'show_var': True,
|
||
'shortname': shortname,
|
||
'return_type': return_type,
|
||
'type': '', # Можешь дополнить из externs
|
||
'file': '', # Можешь дополнить из externs
|
||
'extern': False,
|
||
'static': False,
|
||
'name': full_varname # Сохраняем исходное имя переменной
|
||
}
|
||
|
||
if not parsed_vars:
|
||
print("[INFO] Не удалось найти ни одной переменной в debug_vars.c")
|
||
return False
|
||
|
||
xml_full_path = os.path.join(proj_path, xml_rel_path)
|
||
xml_full_path = os.path.normpath(xml_full_path)
|
||
|
||
tree = ET.parse(xml_full_path)
|
||
root = tree.getroot()
|
||
|
||
vars_section = root.find("variables")
|
||
if vars_section is None:
|
||
vars_section = ET.SubElement(root, "variables")
|
||
|
||
existing_var_names = {v.attrib['name'] for v in vars_section.findall("var")}
|
||
added_count = 0
|
||
|
||
# 3. Добавляем переменные, которых нет в XML
|
||
for name, info in parsed_vars.items():
|
||
if name in existing_var_names:
|
||
# Уже есть — обновляем enable, если нужно
|
||
existing_elem = vars_section.find(f"./var[@name='{name}']")
|
||
if existing_elem is not None:
|
||
manual_elem = existing_elem.find("manual")
|
||
if manual_elem and manual_elem.text == "true":
|
||
show_elem = existing_elem.find("show_var")
|
||
if show_elem is None:
|
||
show_elem = ET.SubElement(existing_elem, "enable")
|
||
enable_elem.text = "true"
|
||
enable_elem = existing_elem.find("enable")
|
||
if enable_elem is None:
|
||
enable_elem = ET.SubElement(existing_elem, "enable")
|
||
enable_elem.text = "true"
|
||
added_count += 1
|
||
continue
|
||
var_elem = ET.SubElement(vars_section, "var", {"name": name})
|
||
manual = ET.SubElement(var_elem, 'manual')
|
||
manual.text = 'true'
|
||
for key, val in info.items():
|
||
elem = ET.SubElement(var_elem, key)
|
||
if isinstance(val, bool):
|
||
elem.text = "true" if val else "false"
|
||
else:
|
||
elem.text = str(val)
|
||
added_count += 1
|
||
|
||
if added_count > 0:
|
||
myXML.fwrite(root, xml_full_path)
|
||
|
||
print(f"[INFO] В XML добавлено новых переменных: {added_count}")
|
||
return True
|
||
else:
|
||
print("[INFO] Все переменные уже есть в XML.")
|
||
return False
|
||
|
||
|
||
def read_vars_from_xml(proj_path, xml_rel_path):
|
||
xml_full_path = os.path.join(proj_path, xml_rel_path)
|
||
xml_full_path = os.path.normpath(xml_full_path)
|
||
|
||
tree = ET.parse(xml_full_path)
|
||
root = tree.getroot()
|
||
|
||
vars_section = root.find("variables")
|
||
includes_section = root.find("includes")
|
||
externs_section = root.find("externs")
|
||
|
||
unique_vars = {}
|
||
vars_need_extern = {}
|
||
|
||
# Читаем переменные из <variables>
|
||
for var in vars_section.findall("var"):
|
||
name = var.attrib["name"]
|
||
var_info = {}
|
||
|
||
# Обрабатываем дочерние элементы (type, file, extern, static и т.п.)
|
||
for child in var:
|
||
text = child.text.strip() if child.text else ""
|
||
# Конвертируем "true"/"false" в bool для extern и static
|
||
if child.tag in ("extern", "static"):
|
||
var_info[child.tag] = (text.lower() == "true")
|
||
else:
|
||
var_info[child.tag] = text
|
||
if child.tag == "enable":
|
||
var_info["enable"] = (text.lower() == "true")
|
||
|
||
# Обрабатываем путь к файлу (если есть)
|
||
if "file" in var_info:
|
||
file_rel = var_info["file"]
|
||
file_full = os.path.normpath(os.path.join(proj_path, file_rel))
|
||
var_info["file"] = file_full
|
||
|
||
unique_vars[name] = var_info
|
||
|
||
# Читаем include-файлы (относительные) и преобразуем в полные пути
|
||
include_files = []
|
||
for node in includes_section.findall("file"):
|
||
rel_path = node.text
|
||
full_path = os.path.normpath(os.path.join(proj_path, rel_path))
|
||
include_files.append(full_path)
|
||
|
||
# Читаем extern переменные из <externs>
|
||
for var in externs_section.findall("var"):
|
||
name = var.attrib["name"]
|
||
type_ = var.find("type").text
|
||
file_rel = var.find("file").text
|
||
file_full = os.path.normpath(os.path.join(proj_path, file_rel))
|
||
vars_need_extern[name] = {
|
||
"type": type_,
|
||
"file": file_full
|
||
}
|
||
|
||
return unique_vars, include_files, vars_need_extern
|
||
|
||
|
||
def read_file_try_encodings(filepath):
|
||
if not os.path.isfile(filepath):
|
||
# Файл не существует — просто вернуть пустую строку или None
|
||
return "", None
|
||
for enc in ['utf-8', 'cp1251']:
|
||
try:
|
||
with open(filepath, 'r', encoding=enc) as f:
|
||
content = f.read()
|
||
return content, enc
|
||
except UnicodeDecodeError:
|
||
continue
|
||
raise UnicodeDecodeError(f"Не удалось прочитать файл {filepath} с кодировками utf-8 и cp1251")
|
||
|
||
|
||
|
||
def generate_vars_file(proj_path, xml_path, output_dir):
|
||
output_dir = os.path.join(proj_path, output_dir)
|
||
os.makedirs(output_dir, exist_ok=True)
|
||
output_path = os.path.join(output_dir, 'debug_vars.c')
|
||
LIBC_path = os.path.join(output_dir, 'debug_tools.c')
|
||
LIBH_path = os.path.join(output_dir, 'debug_tools.h')
|
||
|
||
|
||
# Запись новых переменных для в XML
|
||
add_new_vars_to_xml(proj_path, xml_path, output_path)
|
||
# Генерируем новые переменные
|
||
vars, includes, externs = read_vars_from_xml(proj_path, xml_path)
|
||
|
||
# Сортируем новые переменные по алфавиту по имени
|
||
sorted_new_debug_vars = dict(sorted(vars.items()))
|
||
|
||
new_debug_vars = {}
|
||
|
||
def is_true(val):
|
||
# Преобразуем значение к строке, если оно не None
|
||
# и сравниваем с 'true' в нижнем регистре
|
||
return str(val).lower() == 'true'
|
||
|
||
for vname, info in vars.items():
|
||
# Проверяем, что show_var и enable включены (строки как строки 'true')
|
||
if not is_true(info.get('show_var', 'false')):
|
||
continue
|
||
if not is_true(info.get('enable', 'false')):
|
||
continue
|
||
|
||
vtype = info["type"]
|
||
is_extern = info["extern"]
|
||
is_static = info.get("static", False)
|
||
if is_static:
|
||
continue # пропускаем static переменные
|
||
|
||
path = info["file"]
|
||
|
||
iq_type = info.get('iq_type')
|
||
if not iq_type:
|
||
iq_type = get_iq_define(vtype)
|
||
|
||
pt_type = info.get('pt_type')
|
||
if not pt_type:
|
||
pt_type = map_type_to_pt(vtype, vname)
|
||
|
||
ret_type = info.get('return_type')
|
||
if not ret_type:
|
||
pt_type = 't_iq_none'
|
||
|
||
# Дополнительные поля, например комментарий
|
||
comment = info.get("comment", "")
|
||
short_name = info.get("shortname", f'"{vname}"')
|
||
short_trimmed = short_name[:shortnameSize] # ограничиваем длину до 10
|
||
|
||
if pt_type not in ('pt_struct', 'pt_union'):
|
||
f_name = f'{vname},'
|
||
f_type = f'{pt_type},'
|
||
f_iq = f'{iq_type},'
|
||
f_ret_iq = f'{ret_type},'
|
||
f_short_name = f'"{short_trimmed}"' # оборачиваем в кавычки
|
||
# Добавим комментарий после записи, если он есть
|
||
comment_str = f' // {comment}' if comment else ''
|
||
line = f'{{(uint8_t *)&{f_name:<58} {f_type:<15} {f_iq:<15} {f_ret_iq:<15} {f_short_name:<21}}}, \\{comment_str}'
|
||
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)
|
||
|
||
|
||
# Объединяем все переменные
|
||
all_debug_lines = new_debug_vars.values()
|
||
|
||
out_lines = []
|
||
out_lines.append("// Этот файл сгенерирован автоматически")
|
||
out_lines.append(f'#include "debug_tools.h"')
|
||
|
||
out_lines.append('\n\n// Инклюды для доступа к переменным')
|
||
for incf in includes:
|
||
filename = os.path.basename(incf)
|
||
out_lines.append(f'#include "{filename}"')
|
||
|
||
|
||
out_lines.append('\n\n// Экстерны для доступа к переменным')
|
||
for vname, info in externs.items():
|
||
vtype = info["type"].strip()
|
||
|
||
is_static = info.get("static", False) # <-- добавлено
|
||
if is_static:
|
||
continue # пропускаем static переменные
|
||
|
||
# Попытка выделить размер массива из типа, например int[20]
|
||
array_match = re.match(r'^(.*?)(\s*\[.*\])$', vtype)
|
||
if array_match:
|
||
base_type = array_match.group(1).strip()
|
||
array_size = array_match.group(2).strip()
|
||
out_lines.append(f'extern {base_type} {vname}{array_size};')
|
||
else:
|
||
# Если не массив — обычный extern
|
||
out_lines.append(f'extern {vtype} {vname};')
|
||
|
||
out_lines.append(f'\n\n// Определение массива с указателями на переменные для отладки')
|
||
out_lines.append(f'int DebugVar_Qnt = {len(all_debug_lines)};')
|
||
if stm_flag_global == 0:
|
||
out_lines.append('#pragma DATA_SECTION(dbg_vars,".dbgvar_info")')
|
||
out_lines.append('// pointer_type iq_type return_iq_type short_name')
|
||
out_lines.append('DebugVar_t dbg_vars[] = {\\')
|
||
out_lines.extend(all_debug_lines)
|
||
out_lines.append('};')
|
||
out_lines.append('')
|
||
# Выберем кодировку для записи файла
|
||
# Если встречается несколько, возьмем первую из set
|
||
if stm_flag_global == 0:
|
||
enc_to_write = 'cp1251'
|
||
else:
|
||
enc_to_write = 'utf-8'
|
||
|
||
#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))
|
||
|
||
if os.path.isfile(LIBC_path):
|
||
libc_code, _ = read_file_try_encodings(LIBC_path)
|
||
with open(LIBC_path, 'w', encoding=enc_to_write) as f:
|
||
f.write(libc_code)
|
||
|
||
if os.path.isfile(LIBH_path):
|
||
libh_code, _ = read_file_try_encodings(LIBH_path)
|
||
with open(LIBH_path, 'w', encoding=enc_to_write) as f:
|
||
f.write(libh_code)
|
||
|
||
print(f'Файл debug_vars.c сгенерирован в кодировке, переменных: {len(all_debug_lines)}')
|
||
|
||
|
||
#generate_vars_file("E:/.WORK/TMS/TMS_new_bus/", "Src/DebugTools/vars.xml", "E:/.WORK/TMS/TMS_new_bus/Src/DebugTools/")
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(
|
||
description="Generate debug_vars.c from project XML and output directory.",
|
||
epilog="""\
|
||
Usage example:
|
||
%(prog)s /absolute/path/to/project /absolute/path/to/project/Src/DebugTools/vars.xml /absolute/path/to/project/Src/DebugTools/
|
||
""",
|
||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||
add_help=False
|
||
)
|
||
parser.add_argument("proj_path", help="Absolute path to the project root directory")
|
||
parser.add_argument("xml_path", help="Absolute path to the XML file (must be inside project)")
|
||
parser.add_argument("output_dir", help="Absolute path to output directory (must be inside project)")
|
||
parser.add_argument("-h", "--help", action="store_true", help="Show this help message and exit")
|
||
|
||
# Show help if requested
|
||
if "-h" in sys.argv or "--help" in sys.argv:
|
||
parser.print_help()
|
||
sys.exit(0)
|
||
|
||
# Check minimum args count
|
||
if len(sys.argv) < 4:
|
||
print("Error: insufficient arguments.\n")
|
||
print("Usage example:")
|
||
print(f" {os.path.basename(sys.argv[0])} /absolute/path/to/project /absolute/path/to/project/Src/DebugTools/vars.xml /absolute/path/to/project/Src/DebugTools/\n")
|
||
sys.exit(1)
|
||
|
||
args = parser.parse_args()
|
||
|
||
# Normalize absolute paths
|
||
proj_path = os.path.abspath(args.proj_path)
|
||
xml_path_abs = os.path.abspath(args.xml_path)
|
||
output_dir_abs = os.path.abspath(args.output_dir)
|
||
|
||
# Check proj_path is directory
|
||
if not os.path.isdir(proj_path):
|
||
print(f"Error: Project path '{proj_path}' is not a directory or does not exist.")
|
||
sys.exit(1)
|
||
# Check xml_path inside proj_path
|
||
if not xml_path_abs.startswith(proj_path + os.sep):
|
||
print(f"Error: XML path '{xml_path_abs}' is not inside the project path '{proj_path}'.")
|
||
sys.exit(1)
|
||
# Check output_dir inside proj_path
|
||
if not output_dir_abs.startswith(proj_path + os.sep):
|
||
print(f"Error: Output directory '{output_dir_abs}' is not inside the project path '{proj_path}'.")
|
||
sys.exit(1)
|
||
|
||
# Convert xml_path and output_dir to relative paths *relative to proj_path*
|
||
xml_path_rel = os.path.relpath(xml_path_abs, proj_path)
|
||
output_dir_rel = os.path.relpath(output_dir_abs, proj_path)
|
||
|
||
if not os.path.isdir(proj_path):
|
||
print(f"Error: Project path '{proj_path}' не является директорией или не существует.")
|
||
sys.exit(1)
|
||
|
||
generate_vars_file(proj_path, xml_path_rel, output_dir_rel, 0)
|
||
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|
||
|
||
def run_generate(proj_path, xml_path, output_dir, shortname_size):
|
||
import os
|
||
global shortnameSize
|
||
shortnameSize = shortname_size
|
||
# Normalize absolute paths
|
||
proj_path = os.path.abspath(proj_path)
|
||
xml_path_abs = os.path.abspath(xml_path)
|
||
output_dir_abs = os.path.abspath(output_dir)
|
||
|
||
# Проверка валидности путей
|
||
if not os.path.isdir(proj_path):
|
||
raise FileNotFoundError(f"Project path '{proj_path}' is not a directory or does not exist.")
|
||
|
||
if not xml_path_abs.startswith(proj_path + os.sep):
|
||
raise ValueError(f"XML path '{xml_path_abs}' is not inside the project path '{proj_path}'.")
|
||
|
||
if not output_dir_abs.startswith(proj_path + os.sep):
|
||
raise ValueError(f"Output directory '{output_dir_abs}' is not inside the project path '{proj_path}'.")
|
||
|
||
# Преобразуем к относительным путям относительно проекта
|
||
xml_path_rel = os.path.relpath(xml_path_abs, proj_path)
|
||
output_dir_rel = os.path.relpath(output_dir_abs, proj_path)
|
||
|
||
# Запускаем генерацию
|
||
generate_vars_file(proj_path, xml_path_rel, output_dir_rel)
|