Подключить профили прошивки из JSON и подготовку Python для CCS/Keil. Добавить SCI-программатор F28335 и поддержку STM32 через J-Link. Обновить Linux-консоль, CAN485 и Modbus и проверки общих портов.
88 lines
16 KiB
PowerShell
88 lines
16 KiB
PowerShell
[CmdletBinding(PositionalBinding = $false)] # Именованные служебные параметры не должны поглощать позиционный путь запускаемого Python-скрипта.
|
||
param( # Единая точка входа CCS/Keil работает без зависимости от python.exe в PATH.
|
||
[string]$ResolveFile, # При настройке IDE записывает точный путь проверенного интерпретатора в UTF-8.
|
||
[switch]$CheckOnly, # Проверка окружения не скачивает runtime и не запускает проектный скрипт.
|
||
[Parameter(Position = 0, ValueFromRemainingArguments = $true)][string[]]$PythonArguments # Путь скрипта и все оставшиеся аргументы передаются Python без позиционной привязки к ResolveFile.
|
||
) # Завершает описание публичных параметров обвязки.
|
||
$ErrorActionPreference = 'Stop' # Любая ошибка подготовки возвращается IDE как отказ шага сборки.
|
||
$env:PYTHONUTF8 = '1' # Python 3.8+ передаёт пути и диагностику в UTF-8 независимо от локальной кодовой страницы Windows.
|
||
[Console]::OutputEncoding = New-Object Text.UTF8Encoding($false) # Windows PowerShell 5 должен читать вывод Python в той же кодировке.
|
||
$OutputEncoding = [Console]::OutputEncoding # Согласует кодировку дочерних native-процессов с JSON и сообщениями SETGUI.
|
||
function Test-FirmwarePython([string]$Candidate) { # Проверяет настоящий Python вместо одноимённого псевдонима Microsoft Store.
|
||
if (-not $Candidate -or -not (Test-Path -LiteralPath $Candidate -PathType Leaf) -or $Candidate -like '*WindowsApps*') { return $null } # Пропускает отсутствующие файлы и псевдонимы магазина без открытия окна установки.
|
||
try { # Неподходящий кандидат не мешает проверить остальные установленные интерпретаторы.
|
||
$result = & $Candidate -c 'import sys,json,hashlib,struct,tempfile,ctypes,xml.etree.ElementTree;sys.exit(1) if sys.version_info<(3,8) else print(sys.executable)' 2>$null # Общим инструментам нужны Python 3.8+ и только стандартная библиотека.
|
||
if ($LASTEXITCODE -eq 0 -and $result) { return [string]($result | Select-Object -Last 1) } # Возвращает фактический exe, а не имя команды или путь launcher.
|
||
} catch {} # Ошибка запуска одного кандидата оставляет поиск остальных доступным.
|
||
return $null # Отсутствие пригодного runtime обрабатывается общей установкой ниже.
|
||
} # Завершает проверку одного интерпретатора.
|
||
function Find-FirmwarePython { # Ищет готовый runtime до любых сетевых действий.
|
||
if ($env:FIRMWARE_PYTHON) { # Явный выбор администратора проекта имеет приоритет над автоматическим поиском.
|
||
$chosen = Test-FirmwarePython $env:FIRMWARE_PYTHON # Проверяет указанный абсолютный путь тем же набором импортов.
|
||
if (-not $chosen) { throw 'FIRMWARE_PYTHON должен указывать на работающий Python 3.8+.' } # Неверное явное значение не подменяется другой версией незаметно.
|
||
return $chosen # Оба шага IDE используют выбранный интерпретатор.
|
||
} # После явной настройки остальные кандидаты не нужны.
|
||
$cache = Join-Path $env:LOCALAPPDATA 'SET/FirmwareBuild' # Локальный runtime принадлежит пользователю и не требует администратора.
|
||
foreach ($relative in @('python-3.13.12-amd64/python.exe', 'python-3.8.10-win32/python.exe')) { # Порядок предпочитает современный runtime, сохраняя совместимость с Windows 7.
|
||
$chosen = Test-FirmwarePython (Join-Path $cache $relative) # Проверяет ранее подготовленную локальную копию.
|
||
if ($chosen) { return $chosen } # Повторные сборки обходятся без скачивания и установки.
|
||
} # Завершает поиск локального управляемого runtime.
|
||
foreach ($command in @('python', 'python3')) { # Установленный Python можно использовать без изменения пользовательского PATH.
|
||
$candidate = Get-Command $command -CommandType Application -ErrorAction SilentlyContinue | Select-Object -First 1 # Получает полный путь исполняемого файла команды.
|
||
if ($candidate) { $chosen = Test-FirmwarePython $candidate.Source; if ($chosen) { return $chosen } } # Не принимает неподходящую версию за готовое окружение.
|
||
} # После PATH проверяет зарегистрированные установки без требования перезапуска IDE.
|
||
foreach ($registry in @('HKCU:/Software/Python/PythonCore', 'HKLM:/Software/Python/PythonCore', 'HKLM:/Software/WOW6432Node/Python/PythonCore')) { # PEP 514 учитывает установки текущего пользователя, x64 и x86.
|
||
foreach ($version in @(Get-ChildItem -LiteralPath $registry -ErrorAction SilentlyContinue | Sort-Object PSChildName -Descending)) { # Перебирает только зарегистрированные версии Python.
|
||
$key = Get-Item -LiteralPath ($version.PSPath + '/InstallPath') -ErrorAction SilentlyContinue # Реестр хранит корневой каталог независимой установки.
|
||
if ($key) { $chosen = Test-FirmwarePython (Join-Path ([string]$key.GetValue('')) 'python.exe'); if ($chosen) { return $chosen } } # Возвращает первую реально работающую версию 3.8+.
|
||
} # Завершает перебор версий одного раздела реестра.
|
||
} # Ни один существующий runtime не оказался пригодным.
|
||
return $null # Вызывающий подготовит переносимый runtime, если проверка допускает установку.
|
||
} # Завершает поиск установленного Python.
|
||
try { # Все ошибки возвращаются одним ненулевым кодом, понятным CCS/Keil.
|
||
$pythonExe = Find-FirmwarePython # Использует готовую установку без изменения окружения машины.
|
||
if (-not $pythonExe -and $CheckOnly) { throw 'Python 3.8+ не найден; примените автоматическую настройку проекта для подготовки runtime.' } # Режим диагностики не устанавливает ПО.
|
||
if (-not $pythonExe) { # Установка нужна только компьютеру без подходящего Python.
|
||
$legacy = [Environment]::OSVersion.Version -lt [Version]'6.2' -or -not [Environment]::Is64BitOperatingSystem # Windows 7 и x86 используют последнюю совместимую бинарную ветку 3.8.
|
||
$version = if ($legacy) { '3.8.10' } else { '3.13.12' } # Зафиксированные версии делают подготовку воспроизводимой.
|
||
$arch = if ($legacy) { 'win32' } else { 'amd64' } # Разрядность runtime не зависит от разрядности установленной IDE.
|
||
$expected = if ($legacy) { '760dc79bcb434ee80b1001a30bb6f798287881851bac6d8137867894d40ef1fc' } else { '76f238f606250c87c6beac75dccd35ee99070a13490555936abb6cb64ecce3d0' } # SHA-256 официальных embeddable ZIP с python.org проверен при подготовке обвязки.
|
||
$cache = Join-Path $env:LOCALAPPDATA 'SET/FirmwareBuild' # Runtime хранится отдельно от исходников и сохраняется после Clean.
|
||
$destination = Join-Path $cache ('python-' + $version + '-' + $arch) # Каждый вариант получает независимый каталог пользователя.
|
||
New-Item -ItemType Directory -Force -Path $cache | Out-Null # Создаёт только собственный каталог инструментов сборки.
|
||
$temporary = Join-Path $cache ('download-' + [Guid]::NewGuid().ToString('N')) # Уникальный staging исключает частично установленный runtime.
|
||
New-Item -ItemType Directory -Path $temporary | Out-Null # Распаковка выполняется вне постоянного каталога.
|
||
try { # Ошибка сети или распаковки не должна повредить ранее установленный Python.
|
||
$archive = Join-Path $temporary 'python.zip' # Собственный архив удаляется вместе со staging.
|
||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # Загрузка использует TLS 1.2 также на Windows 7 с обновлённой .NET.
|
||
$client = New-Object Net.WebClient # WebClient доступен штатной Windows PowerShell без внешних модулей.
|
||
try { $client.DownloadFile(('https://www.python.org/ftp/python/' + $version + '/python-' + $version + '-embed-' + $arch + '.zip'), $archive) } finally { $client.Dispose() } # Скачивает строго официальный зафиксированный пакет Python.
|
||
$sha = [Security.Cryptography.SHA256]::Create() # Проверяет полные байты архива до запуска любого файла из него.
|
||
try { $actual = ([BitConverter]::ToString($sha.ComputeHash([IO.File]::ReadAllBytes($archive)))).Replace('-', '').ToLowerInvariant() } finally { $sha.Dispose() } # Нормализует SHA-256 для точного сравнения с закреплённым значением.
|
||
if ($actual -ne $expected) { throw 'SHA-256 архива Python не совпал с ожидаемым значением.' } # Неподтверждённый архив никогда не распаковывается.
|
||
Add-Type -AssemblyName System.IO.Compression.FileSystem # Использует штатную .NET 4.5+ для ZIP без зависимости от Python.
|
||
$unpacked = Join-Path $temporary 'runtime' # Отделяет исполняемые файлы от скачанного ZIP.
|
||
[IO.Compression.ZipFile]::ExtractToDirectory($archive, $unpacked) # Распаковывает проверенный официальный ZIP во временный каталог.
|
||
$checked = Test-FirmwarePython (Join-Path $unpacked 'python.exe') # До публикации проверяет версию и необходимые модули runtime.
|
||
if (-not $checked) { throw 'Подготовленный Python не запускается; проверьте Universal C Runtime Windows.' } # Отсутствующая системная зависимость не выдаётся за успех установки.
|
||
if (Test-Path -LiteralPath $destination) { throw ('Каталог runtime занят непригодной установкой: ' + $destination) } # Не удаляет существующие пользовательские файлы автоматически.
|
||
Move-Item -LiteralPath $unpacked -Destination $destination # Атомарно публикует только полностью проверенную локальную установку.
|
||
$pythonExe = Join-Path $destination 'python.exe' # Все последующие вызовы используют точный путь установленного runtime.
|
||
} finally { # Очищает только собственный staging после проверки его абсолютного расположения.
|
||
$resolved = [IO.Path]::GetFullPath($temporary) # Нормализует путь перед рекурсивным удалением на Windows.
|
||
if ($resolved.StartsWith([IO.Path]::GetFullPath($cache) + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) { Remove-Item -LiteralPath $resolved -Recurse -Force -ErrorAction SilentlyContinue } # Удаляет исключительно созданную текущим вызовом папку внутри собственного кэша.
|
||
} # Завершает подготовку одного переносимого runtime.
|
||
} # Готовый Python найден или успешно подготовлен.
|
||
if ($ResolveFile) { [IO.File]::WriteAllText([IO.Path]::GetFullPath($ResolveFile), $pythonExe, (New-Object Text.UTF8Encoding($false))) } # GUI получает точный путь без разбора локализованного вывода консоли.
|
||
if ($PythonArguments.Count) { # Непустые аргументы означают запуск шага сборки после подготовки зависимостей.
|
||
$bootstrap = 'import os,runpy,sys;p=sys.argv[1];sys.argv=sys.argv[1:];sys.path.insert(0,os.path.dirname(os.path.abspath(p)));runpy.run_path(p,run_name=''__main__'')' # Одинарные Python-кавычки сохраняются при передаче через Windows PowerShell 5 и изолированный runtime.
|
||
& $pythonExe -c $bootstrap @PythonArguments # Передаёт исходные аргументы отдельными элементами без повторного разбора командной оболочкой.
|
||
exit $LASTEXITCODE # Ошибка Python останавливает prebuild/postbuild с исходным кодом завершения.
|
||
} # В режиме настройки достаточно успешной подготовки runtime.
|
||
Write-Output $pythonExe # Терминал показывает фактически выбранный интерпретатор.
|
||
exit 0 # Успех означает работающий Python с проверенной стандартной библиотекой.
|
||
} catch { # Единая диагностика доступна как в IDE, так и в окне настройки SETGUI.
|
||
[Console]::Error.WriteLine('[firmware-build] ' + $_.Exception.Message) # Показывает конкретную причину отказа без скрытого перехода к другой цепочке.
|
||
exit 2 # Ненулевой код запрещает дальнейшие стадии неподготовленной сборки.
|
||
} # Завершает общий запуск runtime и инструментов прошивки.
|