Добавить общее ядро DEIF, эмуляцию приборов и переносимые панели Python/Qt. Расширить загрузчики F2812 и F28335, обновление Flash и профиль STM Invertor. Добавить альтернативный МНК Eigen, модели неисправностей и диагностику сигналов. Обновить справочники устройств, проекты CCS и регрессионные проверки.
64 lines
11 KiB
Python
64 lines
11 KiB
Python
import argparse # Разбираем путь выходной библиотеки и целевую архитектуру настольной сборки.
|
||
import hashlib # Проверяем закреплённый архив Eigen перед использованием заголовков.
|
||
import os # Находим стандартный каталог установщика Visual Studio в Windows.
|
||
from pathlib import Path # Разрешаем пути независимо от текущего рабочего каталога сборки.
|
||
import platform # Выбираем расширение библиотеки и инструментарий целевой ОС.
|
||
import shutil # Находим доступный системный компилятор для Unix-подобных ОС.
|
||
import subprocess # Запускаем компиляторы с проверкой кода завершения.
|
||
import tarfile # Читаем заголовки и лицензии из проверенного архива без сетевого доступа.
|
||
|
||
ROOT = Path(__file__).resolve().parent # Все исходники и закреплённая зависимость расположены рядом со скриптом.
|
||
CORE = ROOT.parents[1] / "c" / "set-protocol" # Подключаем исходный C99-алгоритм из того же репозитория, не создавая его копию.
|
||
SHA256 = "8586084f71f9bde545ee7fa6d00288b264a2b7ac3607b974e54d13e7162c1c72" # Контрольная сумма официального архива Eigen 3.4.0 из GitLab.
|
||
|
||
def headers(directory): # Подготавливаем только необходимые заголовки и лицензии в каталоге конкретной сборки.
|
||
archive = ROOT / "vendor" / "eigen-3.4.0.tar.gz" # Закреплённый архив позволяет собирать EXE без скачивания зависимостей.
|
||
if hashlib.sha256(archive.read_bytes()).hexdigest() != SHA256: # Повреждённая или подменённая зависимость не должна попадать в бинарный модуль.
|
||
raise RuntimeError("Неверная SHA-256 архива Eigen 3.4.0") # Останавливаем сборку с явным указанием нарушенной проверки.
|
||
destination = directory / "eigen-3.4.0" # Раздельный каталог исключает пересечение разных целевых сборок.
|
||
with tarfile.open(archive, "r:gz") as source: # Читаем gzip-архив штатной библиотекой Python, совместимой с Python 3.8.
|
||
for member in source.getmembers(): # Рассматриваем каждый элемент, но не используем небезопасную полную распаковку архива.
|
||
parts = Path(member.name).parts[1:] # Удаляем единственный верхний каталог официального дистрибутива.
|
||
if not member.isfile() or not parts or any(part in ("..", "/", "\\") for part in parts): # Разрешаем только обычные файлы с безопасным относительным путём.
|
||
continue # Пропускаем ссылки, каталоги и элементы, способные выйти за корень распаковки.
|
||
if parts[0] != "Eigen" and not parts[0].startswith("COPYING"): # Для QR достаточно основного дерева Eigen и его лицензионных файлов.
|
||
continue # Примеры, тесты и неподдерживаемые модули не нужны при сборке приложения.
|
||
target = destination.joinpath(*parts) # Сохраняем исходное расположение внутренних include относительно корня Eigen.
|
||
target.resolve().relative_to(destination.resolve()) # Дополнительно проверяем итоговый абсолютный путь перед записью.
|
||
target.parent.mkdir(parents=True, exist_ok=True) # Создаём только каталог текущего безопасного файла.
|
||
with source.extractfile(member) as content: # Открываем содержимое обычного файла внутри проверенного архива.
|
||
target.write_bytes(content.read()) # Сохраняем оригинальные заголовки без изменения стороннего кода.
|
||
return destination # Возвращаем корень, который компилятор должен добавить в include-пути.
|
||
|
||
def build(output, arch): # Собираем отдельный модуль, сохраняя основное C99-ядро без зависимости от C++.
|
||
output = output.resolve() # Абсолютный путь надёжен при смене рабочего каталога компилятора.
|
||
directory = output.parent / (".signal-eigen-" + arch) # Объектные файлы x86 и x64 не смешиваются.
|
||
directory.mkdir(parents=True, exist_ok=True) # Создаём каталог выходной библиотеки и промежуточных файлов.
|
||
include = headers(directory) # Проверяем архив и получаем локальные заголовки Eigen до запуска компилятора.
|
||
if platform.system() == "Windows": # На Windows используем тот же установленный MSVC, что основная DLL.
|
||
vswhere = Path(os.environ.get("ProgramFiles(x86)", r"C:\Program Files (x86)")) / "Microsoft Visual Studio/Installer/vswhere.exe" # Стандартный путь установщика соответствует основному сборщику SETGUI.
|
||
installation = subprocess.check_output([str(vswhere), "-latest", "-products", "*", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "-property", "installationPath"], text=True).strip() # Находим установку с компиляторами C/C++ для обеих архитектур.
|
||
if not installation: # Пустой ответ означает отсутствие подходящего инструментария.
|
||
raise RuntimeError("Не найдены Visual Studio C++ Build Tools") # Сообщаем причину вместо создания приложения без выбранного варианта Eigen.
|
||
vcvars = Path(installation) / "VC/Auxiliary/Build" / ("vcvars32.bat" if arch == "x86" else "vcvars64.bat") # Настраиваем среду именно под разрядность Python и EXE.
|
||
common = ["cl", "/nologo", "/O2", "/MT", "/DPCAN_ABI_BUILD_DLL", "/I" + str(CORE / "include")] # Оба варианта C и Eigen используют оптимизацию и статический CRT без новой зависимости от установленного runtime.
|
||
commands = [common + ["/std:c11", "/c", str(CORE / "src/set_signal.c"), "/Fo" + str(directory / "signal.obj")], common + ["/std:c++14", "/EHsc", "/I" + str(include), "/c", str(ROOT / "signal_eigen.cpp"), "/Fo" + str(directory / "eigen.obj")], ["link", "/nologo", "/DLL", "/MACHINE:" + arch.upper(), "/SUBSYSTEM:WINDOWS,6.01", "/OUT:" + str(output), str(directory / "signal.obj"), str(directory / "eigen.obj")]] # Отдельно компилируем C99 и C++, а PE-подсистему закрепляем на Windows 7 (6.01).
|
||
for command in commands: # Последовательные этапы используют только готовые объектные файлы предыдущих команд.
|
||
subprocess.run('call "%s" && %s' % (vcvars, subprocess.list2cmdline(command)), cwd=directory, shell=True, check=True) # Batch-настройка MSVC и компилятор выполняются в одном процессе cmd.
|
||
else: # Настольные Linux/macOS используют системные C/C++-компиляторы и тот же исходный алгоритм.
|
||
cc, cxx = shutil.which("cc"), shutil.which("c++") # C-исходник компилируем как C99, а Eigen исключительно как C++14.
|
||
if not cc or not cxx: # Обе стадии необходимы для сохранения языковой границы общего ядра.
|
||
raise RuntimeError("Нужны компиляторы C и C++") # Отсутствие инструментария не приводит к скрытому отключению Eigen.
|
||
subprocess.run([cc, "-O2", "-std=c99", "-fPIC", "-I" + str(CORE / "include"), "-c", str(CORE / "src/set_signal.c"), "-o", str(directory / "signal.o")], check=True) # Получаем позиционно-независимый объект оригинального C99-ядра.
|
||
subprocess.run([cxx, "-O2", "-std=c++14", "-fPIC", "-shared", "-I" + str(CORE / "include"), "-I" + str(include), str(ROOT / "signal_eigen.cpp"), str(directory / "signal.o"), "-o", str(output)], check=True) # Линкер C++ добавляет свою стандартную библиотеку к общему настольному модулю.
|
||
for name in ("COPYING.MPL2",): # Распространяем основной текст лицензии вместе с бинарным компонентом.
|
||
shutil.copyfile(include / name, output.parent / ("Eigen-" + name)) # Имя явно указывает, к какой встроенной библиотеке относится лицензия.
|
||
print(output) # Показываем точный путь успешно собранного компонента для журналов сборки.
|
||
|
||
if __name__ == "__main__": # CLI используется как вручную, так и сборщиком EXE.
|
||
parser = argparse.ArgumentParser(description="Сборка альтернативного МНК Eigen QR") # Объясняем назначение отдельного настольного модуля.
|
||
parser.add_argument("--output", type=Path, required=True) # Вызывающий определяет выходной путь и расширение DLL/SO.
|
||
parser.add_argument("--arch", choices=("x86", "x64"), default="x64") # Windows 7 x86 явно выбирает 32-битный PE вместо значения по умолчанию.
|
||
arguments = parser.parse_args() # Проверяем обязательные параметры до работы с файловой системой.
|
||
build(arguments.output, arguments.arch) # Полностью выполняем сборку либо завершаем процесс исключением и ненулевым кодом.
|