Cross-platform

mormot2-pgvector compiles and runs on three target combinations:

OS Compiler Status
Windows x64 FPC 3.2.2 + Lazarus 2 674 / 2 674 tests PASS
Windows x64 Delphi 12 / 36.0 (RAD Studio Athens) 2 656 / 2 656 tests PASS + spawn-the-binary CLI suite
Linux x86_64 FPC 3.2.2 (Ubuntu 24.04 / WSL2) 2 656 / 2 656 tests PASS

mORMot 2 itself is the load-bearing layer: it provides the cross-OS, cross-compiler primitives (file I/O, networking, threading, JSON, ORM) and we follow its conventions everywhere. As a result, the project's own units needed only three small portability tweaks.

The three Delphi-portability fixes

tkAString is FPC-only

MapColumnType in mormot.db.pgvector.orm.pas had a case branch for the FPC-specific tkAString TTypeKind constant. Delphi does not declare it. Wrap with {$ifdef FPC}:

case TypeKind of
  ...
  tkLString,    // long ANSI string
  tkUString,
  tkWString:
    Result := 'TEXT';
  {$ifdef FPC}
  tkAString:    // FPC alias for AnsiString-with-codepage
    Result := 'TEXT';
  {$endif FPC}
  ...
end;

Variant -> Double via AnyVariantToDouble

mORMot's TDocVariantData JSON parser stores some bare-number tokens as varOleStr on Delphi where FPC keeps them as varDouble. A direct d := variant_value cast then raises EVariantTypeCastError on Delphi.

TOllamaEmbeddingClient.ParseEmbeddings now goes through mormot.core.text.AnyVariantToDouble (with a try-except direct-cast fallback for the rare third-party variants):

if not AnyVariantToDouble(inner^.Values[j], d) then
try
  d := inner^.Values[j];
except
  raise EEmbeddingClient.CreateUtf8(
    'Ollama: embeddings[%][%] is not a number', [i, j]);
end;

Byte-level comparison instead of RawUtf8(#$C3#$A9)

Delphi parses string literals like #$C3 as UTF-16 code units and re-encodes them to two UTF-8 bytes when assigned to a RawUtf8. FPC keeps #$C3 as a single raw byte. Tests that asserted on a RawUtf8(#$C3#$A9) literal therefore failed on one compiler.

LoadFile_PreservesNonAsciiBytes in test/test.chunk.pas now uses a PByte cast and compares each byte explicitly:

P := PByte(Pointer(content));
CheckEqual(P[0], $63);   // 'c'
CheckEqual(P[1], $61);   // 'a'
CheckEqual(P[2], $66);   // 'f'
CheckEqual(P[3], $C3);   // 'é' lead byte
CheckEqual(P[4], $A9);   // 'é' continuation byte

That assertion is portable across both compilers.

Building under FPC + Lazarus

The .lpi project files reference mORMot 2 source paths via the OtherUnitFiles and IncludeFiles settings. The exact paths are hard-coded for the developer's machine layout and are read by lazbuild:

lazbuild --build-mode=Default --build-all test\test_runner.lpi
lazbuild --build-mode=Default --build-all examples\01_ingest_mormot2.lpi
lazbuild --build-mode=Default --build-all examples\02_query_cli.lpi
lazbuild --build-mode=Default --build-all examples\03_chat_cli.lpi

Outputs land under build\. To redirect the FPC search paths to your own mORMot 2 location, edit the IncludeFiles and OtherUnitFiles lines in each .lpi, or invoke fpc directly with the -Fu / -Fi flags as the Linux build script does.

Building under Delphi

Delphi's dcc64 does not load .lpi files. The simplest invocation follows the test_runner cross-compile:

$rtl = "C:\dev\IDE\Embarcadero\Studio\23.0\lib\win64\release"
$m   = "C:\path\to\mORMot2\src"

dcc64 -NSSystem;Winapi;System.Win `
      -U$rtl -U$rtl\<lang> `
      -U$m -U$m\core -U$m\db -U$m\net -U$m\crypt `
      -U$m\orm -U$m\rest -U$m\soa -U$m\app -U$m\lib `
      -Usrc `
      -I$m `
      test\test_runner.dpr

The -NS flag list resolves the modern Delphi unit namespaces (System.SysUtils, Winapi.Windows, …) used by the mORMot 2 SysUtils and Windows aliases. The -U<lang> directory carries the localized RTL .dcu set; substitute your install's language code (the example uses fr for the French build).

A dproj is not required; dcc64 on the .dpr is enough for unattended cross-compile validation.

Building under Linux (WSL or native)

The .lpi paths are Windows-specific, so on Linux the recommended path is to invoke fpc directly. The repo ships build-linux.sh and run-linux.sh for WSL:

# from a WSL bash shell
M=/mnt/c/path/to/mORMot2/src
P=/mnt/c/path/to/mormot2-pgvector

cd "$P"
mkdir -p build_linux
fpc -Mobjfpc -Sci \
  -Fi"$M" -Fu"$M" -Fu"$P/src" \
  -Fu"$M/core" -Fi"$M/core" \
  -Fu"$M/orm"  -Fi"$M/orm"  \
  -Fu"$M/db"   -Fi"$M/db"   \
  -Fu"$M/net"  -Fi"$M/net"  \
  -Fu"$M/crypt" -Fi"$M/crypt" \
  -Fu"$M/rest" -Fi"$M/rest" \
  -Fu"$M/soa"  -Fi"$M/soa"  \
  -Fu"$M/app"  -Fi"$M/app"  \
  -Fu"$M/lib"  -Fi"$M/lib"  \
  -FUbuild_linux \
  -obuild_linux/test_runner_linux \
  test/test_runner.dpr

The Linux test_runner runs identically to its Windows counterpart, except that the --noenter flag is silently rejected because the post-test press-key prompt is Windows-only in mORMot's TSynTestsLogged.RunAsConsole. Run without it on Linux:

./build_linux/test_runner_linux

Live PostgreSQL and live Ollama tests skip cleanly when the WSL-internal localhost:5432 / localhost:11434 do not point at the Windows host's loopback. To enable them across the WSL boundary, set the env vars to the Windows host IP visible inside WSL (typically 172.<x>.<y>.<z> or the host's name with .local).

macOS

Not yet validated end-to-end, but expected to work via FPC + Lazarus on the same lazbuild path used on Windows; the only macOS-specific piece would be the libpq location, which SynDBPostgresLibrary (or the --libpq CLI flag) lets you override.