From f2ecd63fe1b1cf6a6194391e67c430b417b7222d Mon Sep 17 00:00:00 2001 From: Vladimir Date: Mon, 24 Aug 2026 21:31:59 +0300 Subject: [PATCH] =?UTF-8?q?fix(platform):=20win64-=D1=81=D0=B1=D0=BE=D1=80?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BF=D0=B0=D0=B4=D0=B0=D0=BB=D0=B0=20=D0=BD?= =?UTF-8?q?=D0=B0=20GetEnvironmentVariable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Модуль Windows стоит в uses ПОСЛЕ SysUtils, поэтому его трёхпараметрический GetEnvironmentVariable(PChar;PChar;DWORD) перекрывает однопараметрический из SysUtils, и три вызова в PlatformUtils падали с «wrong number of parameters». Лечение — явная квалификация SysUtils.GetEnvironmentVariable. ★Сломалось это не сейчас: unit Windows появился в uses ещё в d209697 (вместе с QueryPerformanceCounter для монотонных часов), и с тех пор под win64 не собиралось вовсе — просто сборочная машина туда не заходила. GetAppCfgDir с %APPDATA% живёт с e9f4bf1 и до d209697 работал. Новый стенд test/platform. Кросс-RTL обычно не установлен, поэтому ветки {$IFDEF WINDOWS} и {$IFDEF DARWIN} на Linux не компилируются ВООБЩЕ, и ошибка в них всплывает только на сборочной машине. Стенд переписывает копию PlatformUtils.pas так, чтобы платформенные условия читались как свои (-dSIMWIN / -dSIMMAC), и компилирует без линковки (-Cn): системных функций тут нет, но синтаксис, типы и — главное — разрешение имён проверяются по-настоящему. Для Windows подкладывается заглушка unit Windows, объявляющая ровно те имена, которыми настоящий перекрывает SysUtils; на ней и держится вся проверка. ★Негативный контроль: на коде до правки стенд выдаёт РОВНО те же три ошибки (строки 210, 271, 273), что и сборочная машина. Чего он не делает: живых вызовов системных счётчиков — их правильность доказывает только прогон на самой платформе. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Bkwwyj7xVRrqnSVEseTRfV --- PlatformUtils.pas | 10 +++++-- test/platform/run.sh | 44 ++++++++++++++++++++++++++++++ test/platform/win_stub/Windows.pas | 32 ++++++++++++++++++++++ 3 files changed, 83 insertions(+), 3 deletions(-) create mode 100755 test/platform/run.sh create mode 100644 test/platform/win_stub/Windows.pas diff --git a/PlatformUtils.pas b/PlatformUtils.pas index f396aa3..89c8a38 100644 --- a/PlatformUtils.pas +++ b/PlatformUtils.pas @@ -207,7 +207,10 @@ begin end; // Бандл под LaunchServices не может передать argv, поэтому тот же // переключатель читается из окружения (Info.plist -> LSEnvironment). - S := LowerCase(Trim(GetEnvironmentVariable('EWSDR_OPENGL'))); + // ★Явная квалификация: на Windows в uses стоит unit Windows, и его + // GetEnvironmentVariable(PChar;PChar;DWORD) перекрывает однопараметрический + // из SysUtils — сборка под win64 падала на «wrong number of parameters». + S := LowerCase(Trim(SysUtils.GetEnvironmentVariable('EWSDR_OPENGL'))); if (S = '1') or (S = 'true') or (S = 'yes') or (S = 'on') then Exit(True); end; @@ -268,9 +271,10 @@ end; function GetAppCfgDir: string; begin {$IFDEF WINDOWS} - Result := GetEnvironmentVariable('APPDATA'); + // Квалификация обязательна — см. HasOpenGLSpectrumSwitch. + Result := SysUtils.GetEnvironmentVariable('APPDATA'); if Result = '' then - Result := GetEnvironmentVariable('LOCALAPPDATA'); + Result := SysUtils.GetEnvironmentVariable('LOCALAPPDATA'); if Result = '' then Result := ExtractFilePath(ParamStr(0)); Result := IncludeTrailingPathDelimiter(Result) + 'ewsdr' + PathDelim; diff --git a/test/platform/run.sh b/test/platform/run.sh new file mode 100755 index 0000000..64da918 --- /dev/null +++ b/test/platform/run.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# Проверка ветвей PlatformUtils под Windows и macOS БЕЗ этих платформ. +# +# Зачем. Кросс-RTL обычно не установлен, поэтому ветки {$IFDEF WINDOWS} и +# {$IFDEF DARWIN} на Linux не компилируются ВООБЩЕ — ошибка в них всплывает +# только на сборочной машине, через полчаса после коммита. Так и вышло: unit +# Windows, добавленный ради QueryPerformanceCounter, перекрыл однопараметрический +# SysUtils.GetEnvironmentVariable, и win64-сборка легла на трёх вызовах. +# +# Как. Копию PlatformUtils.pas переписываем так, чтобы платформенные условия +# читались как наши собственные (-dSIMWIN / -dSIMMAC), и компилируем БЕЗ +# ЛИНКОВКИ (-Cn): системных функций на Linux нет, но синтаксис, типы и — главное — +# разрешение имён проверяются по-настоящему. Для Windows подкладывается +# заглушка unit Windows с теми же перекрывающими объявлениями (win_stub/). +# +# Чего проверка НЕ делает: живых вызовов системных счётчиков. Их правильность +# доказывает только прогон на самой платформе. +set -e +cd "$(dirname "$0")" +SRC=../../PlatformUtils.pas +WORK=$(mktemp -d) +trap 'rm -rf "$WORK"' EXIT + +# ── Windows ── +mkdir -p "$WORK/win" +cp win_stub/Windows.pas "$WORK/win/" +sed -e 's/{\$IFDEF WINDOWS}/{$IFDEF SIMWIN}/g' \ + -e 's/{\$IFNDEF WINDOWS}/{$IFNDEF SIMWIN}/g' "$SRC" > "$WORK/win/PlatformUtils.pas" +( cd "$WORK/win" && fpc -B -Mobjfpc -dHEADLESS -dSIMWIN -Fu. -FU. -Cn PlatformUtils.pas >"$WORK/win.log" 2>&1 ) \ + || { echo "WINDOWS-ветка НЕ компилируется:"; cat "$WORK/win.log"; exit 1; } +echo " ok PlatformUtils: ветка WINDOWS компилируется (заглушка unit Windows перекрывает SysUtils, как настоящий)" + +# ── macOS ── +mkdir -p "$WORK/mac" +sed -e 's/{\$IFDEF LINUX}/{$IFDEF SIMLINUX}/g' \ + -e 's/{\$IFDEF DARWIN}/{$IFDEF SIMMAC}/g' \ + -e 's/{\$IF DEFINED(DARWIN) AND NOT DEFINED(HEADLESS)}/{$IF DEFINED(SIMMAC) AND NOT DEFINED(HEADLESS)}/g' \ + "$SRC" > "$WORK/mac/PlatformUtils.pas" +( cd "$WORK/mac" && fpc -B -Mobjfpc -dHEADLESS -dSIMMAC -Fu. -FU. -Cn PlatformUtils.pas >"$WORK/mac.log" 2>&1 ) \ + || { echo "DARWIN-ветка НЕ компилируется:"; cat "$WORK/mac.log"; exit 1; } +echo " ok PlatformUtils: ветка DARWIN компилируется (mach_absolute_time, без линковки)" + +echo +echo "Итого: 2 проверки, провалено 0" diff --git a/test/platform/win_stub/Windows.pas b/test/platform/win_stub/Windows.pas new file mode 100644 index 0000000..0eabea6 --- /dev/null +++ b/test/platform/win_stub/Windows.pas @@ -0,0 +1,32 @@ +unit Windows; + +{ + Заглушка модуля Windows для проверки WINDOWS-ветки на Linux. + + Объявляет РОВНО те имена, которыми настоящий Windows перекрывает SysUtils — + само перекрытие и ловит ошибки, невидимые на Linux: unit Windows стоит в uses + ПОСЛЕ SysUtils, поэтому его трёхпараметрический GetEnvironmentVariable + выигрывает у однопараметрического из SysUtils, и вызов падает с «wrong number + of parameters». Ровно так win64-сборка и легла. +} + +{$MODE Delphi} + +interface + +type + DWORD = LongWord; +function GetEnvironmentVariable(lpName, lpBuffer: PChar; nSize: DWORD): DWORD; stdcall; +function QueryPerformanceCounter(var lpPerformanceCount: Int64): LongBool; stdcall; +function QueryPerformanceFrequency(var lpFrequency: Int64): LongBool; stdcall; +function GetTickCount64: QWord; stdcall; +implementation +function GetEnvironmentVariable(lpName, lpBuffer: PChar; nSize: DWORD): DWORD; stdcall; +begin Result := 0; end; +function QueryPerformanceCounter(var lpPerformanceCount: Int64): LongBool; stdcall; +begin lpPerformanceCount := 123456789; Result := True; end; +function QueryPerformanceFrequency(var lpFrequency: Int64): LongBool; stdcall; +begin lpFrequency := 10000000; Result := True; end; +function GetTickCount64: QWord; stdcall; +begin Result := 42; end; +end.