fix(platform): win64-сборка падала на GetEnvironmentVariable

Модуль 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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bkwwyj7xVRrqnSVEseTRfV
This commit is contained in:
2026-08-24 21:31:59 +03:00
co-authored by Claude Opus 5
parent fe3d862d00
commit f2ecd63fe1
3 changed files with 83 additions and 3 deletions
+7 -3
View File
@@ -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;
+44
View File
@@ -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"
+32
View File
@@ -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.