Vega — Portabel bauen

Ein Quelltext, drei Ziele: X11 unter Linux, Win32 unter Windows, Ring-3 unter Lyx OS. Der Unterschied steht nicht im Programm, sondern im Include-Pfad des Übersetzungsaufrufs.

Vega-Guides · Backends · Bauen und Testen · Cross-Compilation


Der Trick: eine Unit, dreimal vorhanden

Vega.BackendAuto liegt je einmal unter platform/linux, platform/win64 und platform/lyxos. Alle drei exportieren dieselbe Funktion:

pub fn NewPlatformBackend(): IVegaBackend

Welche gebunden wird, entscheidet die letzte -I-Angabe — deshalb steht der Plattformpfad hinter dem Projektpfad.

lyxc app.lyx -I . -I platform/linux                            -o app       # X11
lyxc app.lyx -I . -I platform/win64 --target=win64 --format=pe -o app.exe   # Win32
lyxc app.lyx -I . -I platform/lyxos                            -o app.elf   # Lyx OS

 
Für Lyx OS wird derzeit ohne --target=lyxos gebaut — als Übergang, nicht als Dauerzustand.

Das native Zielformat von Lyx OS ist LYX!, der Maschinencode-Container des LX-34-Loaders; --target=lyxos erzeugt ihn (Magic 4C 59 58 21, nachgemessen mit lyxc 1.1.4B), und PrintLn arbeitet dort inzwischen — allerdings nur mit einem Zeichenketten-Literal (#1716). ELF wird von Lyx OS nur weiterhin mitgeladen.

Was Vega an diesem Ziel bislang hindert: alloc und free fehlen (#1718 — deshalb die eigene platform/lyxos/Vega/Mem.lyx über mmap), und die stdlib-Importe brechen an fehlenden Builtin-Namen (#1717: std.fs schon bei FileExists, std.io bei StrNew); im Backend selbst fehlen die Builtin-IDs 3 und 6–15 (#1715). Solange das so ist, ist der ELF-Weg der einzige, der wirklich läuft. Sobald die Lücken zu sind, ist --target=lyxos der richtige Weg.

Warum keine Fallunterscheidung im Code

TARGET_OS gibt es (1 = Linux, 3 = Windows), und es ändert sich mit –target. Für die Backendwahl reicht es trotzdem nicht: Beide Zweige müssten übersetzt und gebunden werden, und extern-Deklarationen sind harte Abhängigkeiten — auch in einem Zweig, der nie läuft. Eine Linux-Binary mit eingebundenem Win32-Backend verlangt beim Start kernel32.dll und startet gar nicht erst.

Deshalb die Trennung über den Pfad. Es ist dasselbe Verfahren, mit dem Lazarus seine Widgetsets auswählt.


Beispiel

Dieses Programm ist unverändert für alle drei Ziele übersetzbar:

unit Main;

import std.io;
import Vega.Types;
import Vega.Events;
import Vega.Canvas;
import Vega.Theme;
import Vega.Control;
import Vega.Font;
import Vega.Strings;
import Vega.Lists;
import Vega.Style;
import Vega.StdCtrls;
import Vega.Backend;
// Diese Unit gibt es dreimal — unter platform/linux, platform/win64 und
// platform/lyxos. Welche gilt, entscheidet der Include-Pfad, nicht der Code.
import Vega.BackendAuto;
import Vega.Forms;

var g_app: TApplication;
var g_stand: TLabel;
var g_zaehler: int64;

fn OnZaehl(sender: TControl): void {
  g_zaehler := g_zaehler + 1;
  g_stand.SetCaption(StrConcat("Geklickt: ", IntToStr(g_zaehler)));
}

fn OnBeenden(sender: TControl): void { g_app.Terminate(); }

fn main(): int64 {
  g_app := new TApplication(NewPlatformBackend());
  if (!g_app.Ready) { PrintLn("Kein Fenstersystem erreichbar."); return 1; }

  var f: TForm := new TForm(320, 160, "Portabel");
  g_zaehler := 0;

  g_stand := new TLabel("Geklickt: 0");
  g_stand.SetBounds(16, 16, 280, 18);
  g_stand.SetParent(f);

  var b1: TButton := new TButton("Zaehlen");
  b1.SetBounds(16, 56, 130, 28);
  b1.OnClick := OnZaehl;
  b1.SetParent(f);

  var b2: TButton := new TButton("Beenden");
  b2.SetBounds(166, 56, 130, 28);
  b2.OnClick := OnBeenden;
  b2.SetParent(f);

  if (!g_app.ShowForm(f)) { return 2; }
  g_app.Run();
  g_app.Shutdown();
  return 0;
}

Nachgemessen mit lyxc 1.1.3I — dieselbe Datei, drei Aufrufe:

Ziel Aufruf Ergebnis
Linux/X11 -I . -I platform/linux ELF 64-bit, 787 KB
Windows -I . -I platform/win64 --target=win64 --format=pe PE32+ x86-64, 803 KB
Lyx OS -I . -I platform/lyxos ELF 64-bit statisch, 764 KB

Die PE-Datei wurde zusätzlich unter Wine gestartet und zeigt ihr Fenster.


Was portabel ist und was nicht

Portabel Nicht portabel
Alles aus Vega.* — Controls, Layout, Themes, LFM Direkte extern-Aufrufe in Systembibliotheken
Zeichnen, Schriften, Ereignisse Pfade mit / oder \\ fest im Code
std-Units ohne Systembezug Annahmen über Zeilenenden oder Dateirechte

Die HAL ist bewusst klein — 21 Methoden. Alles darüber ist geteilter Code und läuft überall gleich; genau deshalb ist sie ein Interface.


Bauen mit Skript

Im Repository nimmt build.sh die Unterscheidung über den Dateinamen ab: ein Beispiel name_win wird als PE gebaut, name_lyxos für Lyx OS, alles andere für X11. Für ein eigenes Projekt genügt dasselbe Muster — drei Aufrufe, ein Quelltext.


Fallstricke

  • Plattformpfad vor dem Projektpfad. Die letzte -I-Angabe gewinnt; steht platform/linux zuerst, greift sie nicht.
  • –target=win64 ohne platform/win64. Dann wird das X11-Backend in eine PE-Datei gebunden — sie startet, findet aber kein Fenstersystem.
  • –target=lyxos benutzt, solange die Builtins fehlen. Der Bau bricht bei der ersten alloc- oder std.fs-Stelle ab; siehe Kasten. Es ist das künftige, noch nicht das gangbare Ziel.
  • Backend direkt importiert. import Vega.Backend.X11 bindet fest an X11; portable Programme nehmen Vega.BackendAuto.

Letzte Aktualisierung: 2026-08-20 — drei Ziele mit lyxc 1.1.3I aus derselben Datei gebaut und mit file geprüft; der Lyx-OS-Abschnitt am 2026-08-20 gegen lyxc 1.1.4B nachgemessen (LYX!-Container, fehlende Builtins alloc und readlink).