Inhaltsverzeichnis

Lyx – Compiler selbst kompilieren

Der Lyx-Compiler lyxc ist in Free Pascal (FPC) implementiert. Das hat einen wesentlichen Vorteil: FPC ist selbst plattformübergreifend verfügbar und erzeugt native Binaries ohne externe Laufzeitabhängigkeiten — genau wie Lyx selbst.

Diese Anleitung beschreibt, wie lyxc aus dem Quellcode gebaut wird: für das eigene System (native Build), für andere Zielplattformen (Cross-Build) und im Debug-Modus für die Compiler-Entwicklung.


1. Voraussetzungen

Free Pascal Compiler (FPC)

FPC ist die einzige zwingend notwendige Abhängigkeit. Version 3.2.2 oder neuer wird empfohlen.

# Ubuntu / Debian
sudo apt install fpc

# macOS (Homebrew)
brew install fpc

# Fedora / RHEL
sudo dnf install fpc

# Windows — Installer von fpc.freepascal.org
# Nach der Installation: fpc.exe im PATH

# Version prüfen
fpc -iV

Erwartete Ausgabe:

3.2.2

Optionale Werkzeuge

Werkzeug Zweck Pflicht?
make Build-Automatisierung Empfohlen
git Quellcode holen und aktualisieren Empfohlen
arm-linux-gnueabihf- Toolchain Cross-Build für ARM64 Nur bei Cross-Build
riscv64-linux-gnu- Toolchain Cross-Build für RISC-V Nur bei Cross-Build
xtensa-esp32-elf- Toolchain Cross-Build für ESP32 Nur bei Cross-Build
Lazarus IDE Grafische Entwicklungsumgebung für FPC Nein

2. Quellcode beziehen

git clone https://github.com/seolizer/lyxc.git
cd lyxc

Oder als Tarball-Download ohne Git:

curl -L https://seolizer.de/lyx/releases/v0.8.5/lyxc-src.tar.gz | tar xz
cd lyxc-src/


3. Verzeichnisstruktur

lyxc/
├── lyxc.lpr              Haupt-Projektdatei (FPC-Einstiegspunkt)
├── Makefile              Build-Automatisierung
├── src/                  Quellcode des Compilers
│   ├── frontend/         Lexer, Parser, AST-Aufbau
│   ├── ir/               Intermediate Representation, Optimierungspässe
│   ├── backend/          Codegenerator — ein Unterverzeichnis pro Ziel
│   │   ├── x86_64/       x86_64 ELF (Linux, macOS)
│   │   ├── arm64/        ARM64 ELF (Linux, Raspberry Pi)
│   │   ├── macosx64/     x86_64 Mach-O (macOS Intel)
│   │   ├── win_arm64/    ARM64 PE (Windows ARM)
│   │   ├── macho/        Mach-O gemeinsame Teile
│   │   ├── elf/          ELF gemeinsame Teile
│   │   ├── pe/           PE/COFF gemeinsame Teile
│   │   ├── riscv/        RISC-V 64 ELF
│   │   ├── xtensa/       Xtensa ELF (ESP32)
│   │   ├── esp32/        ESP32-spezifische Erweiterungen
│   │   └── arm_cm/       ARM Cortex-M (Bare-Metal)
│   └── util/             Gemeinsame Hilfsfunktionen
├── stdlib/               Lyx-Standardbibliothek (std/ und data/)
│   ├── std/              Standard-Units (.lyx-Quelldateien)
│   └── data/             Daten-Units (.lyx-Quelldateien)
├── tests/                Compiler-Testsuite
└── lib/                  Vorkompilierte FPC-Units (Build-Artefakte)


4. Native Build (Host-System)

Schnellbuild mit Make

make

Das Makefile erkennt das Host-System automatisch und wählt die passenden Flags. Das fertige Binary liegt danach unter ./lyxc.

Manueller FPC-Aufruf

Der vollständige Build-Befehl, wie ihn das Makefile intern verwendet:

mkdir -p lib && fpc \
    -Mobjfpc \
    -Sh \
    -O2 \
    -FUlib/ \
    -Fusrc/util/ \
    -Fusrc/frontend/ \
    -Fusrc/ir/ \
    -Fusrc/backend/ \
    -Fusrc/backend/x86_64/ \
    -Fusrc/backend/elf/ \
    -Fusrc/backend/pe/ \
    -Fusrc/backend/arm64/ \
    -Fusrc/backend/macho/ \
    -Fusrc/backend/xtensa/ \
    -Fusrc/backend/esp32/ \
    -Fusrc/backend/macosx64/ \
    -Fusrc/backend/win_arm64/ \
    -Fusrc/backend/riscv/ \
    -Fusrc/backend/arm_cm/ \
    lyxc.lpr -olyxc 2>&1

Erklärung der FPC-Parameter:

Parameter Bedeutung
-Mobjfpc Object Pascal-Dialekt (Klassen, generische Units)
-Sh Strings als AnsiString (nicht ShortString) — wichtig für Unicode-Handling
-O2 Compiler-Optimierung Stufe 2 — gutes Verhältnis Buildzeit/Performance
-O3 Maximale Optimierung — langsamerer Build, schnelleres lyxc-Binary
-FUlib/ Ausgabeverzeichnis für kompilierte .ppu-Dateien
-Fu<pfad>/ Suchpfad für FPC-Units (einmal pro Unterverzeichnis)
-olyxc Name des Ausgabe-Binaries
-Tlinux Zielbetriebssystem (linux, win64, darwin)
-Px86_64 Zielprozessor (x86_64, arm64, riscv64)
-g Debug-Symbole einbetten
-gl Zeilennummern in Stack-Traces
-gh Heap-Trace aktivieren (für Speicherleck-Diagnose)
-dDEBUG Conditional Compilation: Debug-Blöcke aktivieren
-Xs Strip Symbols — kleineres Binary, kein Debugging

Build für verschiedene Host-Plattformen

# Linux x86_64 → lyxc für Linux (Standard)
fpc -Tlinux -Px86_64 ... lyxc.lpr -olyxc

# macOS Intel → lyxc für macOS x86_64
fpc -Tdarwin -Px86_64 ... lyxc.lpr -olyxc

# macOS Apple Silicon → lyxc für ARM64
fpc -Tdarwin -Parm64 ... lyxc.lpr -olyxc

# Windows x86_64 → lyxc.exe
fpc -Twin64 -Px86_64 ... lyxc.lpr -olyxc.exe


5. Cross-Compilation des Compilers

Ein auf Linux x86_64 gebautes lyxc kann Code für alle Zielplattformen erzeugen — das ist gewöhnliche Cross-Compilation von Lyx-Programmen. Hier geht es um etwas anderes: Das lyxc-Binary selbst für eine andere Plattform bauen, z.B. um es auf einem Raspberry Pi auszuführen.

FPC unterstützt Cross-Compilation des Compilers direkt. Voraussetzung ist eine passende Binutils-Toolchain für die Zielplattform.

Linux → ARM64 (z.B. Raspberry Pi 4)

# Toolchain installieren
sudo apt install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu

# lyxc für ARM64 bauen
fpc -Tlinux -Parm64 \
    -XPaarch64-linux-gnu- \
    -Mobjfpc -Sh -O2 \
    -FUlib_arm64/ \
    -Fusrc/util/ -Fusrc/frontend/ -Fusrc/ir/ \
    -Fusrc/backend/ -Fusrc/backend/arm64/ -Fusrc/backend/elf/ \
    -Fusrc/backend/x86_64/ -Fusrc/backend/pe/ \
    -Fusrc/backend/macho/ -Fusrc/backend/xtensa/ \
    -Fusrc/backend/esp32/ -Fusrc/backend/riscv/ \
    -Fusrc/backend/arm_cm/ -Fusrc/backend/macosx64/ \
    -Fusrc/backend/win_arm64/ \
    lyxc.lpr -olyxc-arm64

Das Binary lyxc-arm64 kann auf den Raspberry Pi übertragen und dort ausgeführt werden.

Linux → RISC-V 64

sudo apt install gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu

fpc -Tlinux -Priscv64 \
    -XPriscv64-linux-gnu- \
    -Mobjfpc -Sh -O2 \
    -FUlib_riscv64/ \
    -Fusrc/util/ -Fusrc/frontend/ -Fusrc/ir/ \
    -Fusrc/backend/ -Fusrc/backend/riscv/ -Fusrc/backend/elf/ \
    -Fusrc/backend/x86_64/ -Fusrc/backend/arm64/ -Fusrc/backend/pe/ \
    -Fusrc/backend/macho/ -Fusrc/backend/xtensa/ \
    -Fusrc/backend/esp32/ -Fusrc/backend/arm_cm/ \
    -Fusrc/backend/macosx64/ -Fusrc/backend/win_arm64/ \
    lyxc.lpr -olyxc-riscv64

Linux → Windows x86_64

sudo apt install mingw-w64

fpc -Twin64 -Px86_64 \
    -Mobjfpc -Sh -O2 \
    -FUlib_win64/ \
    -Fusrc/util/ -Fusrc/frontend/ -Fusrc/ir/ \
    -Fusrc/backend/ -Fusrc/backend/x86_64/ -Fusrc/backend/pe/ \
    -Fusrc/backend/elf/ -Fusrc/backend/arm64/ \
    -Fusrc/backend/macho/ -Fusrc/backend/xtensa/ \
    -Fusrc/backend/esp32/ -Fusrc/backend/riscv/ \
    -Fusrc/backend/arm_cm/ -Fusrc/backend/macosx64/ \
    -Fusrc/backend/win_arm64/ \
    lyxc.lpr -olyxc.exe

Make-Targets für Cross-Builds

Das Makefile stellt fertige Targets bereit:

make cross-arm64      # lyxc-arm64
make cross-riscv64    # lyxc-riscv64
make cross-win64      # lyxc.exe
make all-targets      # Alle Ziele auf einmal


6. Standard-Library einrichten

Nach dem Build muss lyxc wissen, wo die Lyx-Units liegen. Es gibt drei Wege:

Option A: Umgebungsvariable (Entwicklung)

export LYX_PATH=/pfad/zu/lyxc/stdlib
./lyxc mein_programm.lyx -o mein_programm

Option B: Systemweite Installation

# Binary installieren
sudo cp lyxc /usr/local/bin/lyxc

# Standard-Library installieren
sudo mkdir -p /usr/local/lib/lyx
sudo cp -r stdlib/std  /usr/local/lib/lyx/std
sudo cp -r stdlib/data /usr/local/lib/lyx/data

# lyxc sucht /usr/local/lib/lyx/ standardmäßig — keine Variable nötig
lyxc mein_programm.lyx -o mein_programm

Option C: Suchpfad per Flag

./lyxc mein_programm.lyx -I ./stdlib/ -o mein_programm

Standard-Library vorkompilieren

Vorkompilierte .lyu-Dateien beschleunigen den Build erheblich. Die Units werden einmalig kompiliert und dann wiederverwendet:

# Alle Standard-Units zu .lyu vorkompilieren
make stdlib

# Oder einzeln
./lyxc stdlib/std/io.lyx --compile-unit -o stdlib/std/io.lyu
./lyxc stdlib/std/math.lyx --compile-unit -o stdlib/std/math.lyu

Danach sucht lyxc bei import std.io automatisch nach io.lyu vor io.lyx.


7. Debug-Build für Compiler-Entwicklung

Wer am Compiler selbst arbeitet, braucht einen Build mit Debug-Symbolen und Heap-Trace:

mkdir -p lib_debug && fpc \
    -Mobjfpc -Sh \
    -g -gl -gh \
    -dDEBUG \
    -FUlib_debug/ \
    -Fusrc/util/ -Fusrc/frontend/ -Fusrc/ir/ \
    -Fusrc/backend/ -Fusrc/backend/x86_64/ -Fusrc/backend/elf/ \
    -Fusrc/backend/pe/ -Fusrc/backend/arm64/ -Fusrc/backend/macho/ \
    -Fusrc/backend/xtensa/ -Fusrc/backend/esp32/ \
    -Fusrc/backend/macosx64/ -Fusrc/backend/win_arm64/ \
    -Fusrc/backend/riscv/ -Fusrc/backend/arm_cm/ \
    lyxc.lpr -olyxc-debug 2>&1

make debug     # Kurzform über Makefile

Mit dem Debug-Build erhält man bei einem Compiler-Absturz (Internal Compiler Error) einen vollständigen Stack-Trace:

An unhandled exception occurred at $00000000004A3B21:
EAccessViolation: Access violation
  $00000000004A3B21  EMITMOVREG,  line 847 of backend/x86_64/emit.pas
  $000000000049F102  CODEGENEXPR,  line 312 of backend/x86_64/codegen.pas
  $000000000047E8CD  COMPILEFN,  line 198 of ir/lower.pas
  $000000000043A012  MAIN,  line 44 of lyxc.lpr

Der Debug-Build aktiviert außerdem alle {$IFDEF DEBUG}-Blöcke im Quellcode — das sind ausführliche Log-Ausgaben zu AST-Aufbau, IR-Generierung und Registerallokation:

./lyxc-debug test.lyx --trace-passes -o test 2>&1 | head -50


8. Compiler-Testsuite ausführen

Im Verzeichnis tests/ liegen Lyx-Quelldateien, deren erwartetes Verhalten (Ausgabe, Fehlercode, Kompilierfehler) in Metadateien beschrieben ist.

# Alle Tests ausführen
make test

# Einzelne Testkategorie
make test-codegen     # Codegenerator-Tests
make test-frontend    # Lexer/Parser-Tests
make test-safety      # DO-178C / Safety-Pragma-Tests
make test-cross       # Cross-Compilation-Tests (langsam)

Beispielausgabe:

[PASS] tests/codegen/arith.lyx
[PASS] tests/codegen/structs.lyx
[PASS] tests/frontend/generics.lyx
[FAIL] tests/safety/stack_limit.lyx
  Expected: "error: stack limit exceeded"
  Got:      "warning: stack limit exceeded"
[PASS] tests/cross/arm64_basic.lyx
...
Tests: 247 passed, 1 failed

Einzelnen Test manuell ausführen:

./lyxc tests/codegen/arith.lyx -o /tmp/test_arith && /tmp/test_arith


9. Entwicklungsworkflow

Typischer Ablauf beim Arbeiten am Compiler:

# 1. Änderung in einer Backend-Datei vornehmen
#    z.B. src/backend/x86_64/emit.pas

# 2. Nur geänderte Units neu bauen (FPC inkrementell)
make

# 3. Einzelnen Test ausführen
./lyxc tests/codegen/mein_test.lyx -o /tmp/out && /tmp/out

# 4. Mit Debug-Ausgabe prüfen
./lyxc-debug tests/codegen/mein_test.lyx --trace-passes --emit-asm -o /tmp/out

# 5. Assembler-Ausgabe prüfen (was der Codegen erzeugt hat)
./lyxc tests/codegen/mein_test.lyx --emit-asm 2>&1 | less

# 6. Gesamte Testsuite grün machen
make test

# 7. Release-Build
make release    # -O3, stripped, alle Targets

FPC baut inkrementell: Nur Units, deren Quelldatei neuer als die .ppu-Datei in lib/ ist, werden neu kompiliert. Ein Rebuild nach einer Änderung in emit.pas dauert typisch 3–8 Sekunden.


10. Troubleshooting

"Unit not found: frontend/lexer"

FPC findet eine Unit nicht. Ursachen und Lösungen:

# Fehlende -Fu-Option prüfen — jedes Unterverzeichnis braucht seinen eigenen -Fu
fpc ... -Fusrc/frontend/ -Fusrc/ir/ ...

# lib/-Verzeichnis fehlt
mkdir -p lib

# FPC-Unit-Cache löschen und neu bauen
rm -rf lib/
make clean && make

"Error: Identifier not found: AnsiString"

FPC läuft im falschen Modus. -Mobjfpc oder -Mobjfpc fehlt:

fpc -Mobjfpc -Sh ...

Linker-Fehler bei Cross-Build

# Fehler: "ld: cannot find -lgcc"
# → Passende Binutils-Toolchain fehlt

# ARM64
sudo apt install binutils-aarch64-linux-gnu

# RISC-V
sudo apt install binutils-riscv64-linux-gnu

# Toolchain-Präfix prüfen
aarch64-linux-gnu-ld --version

"Fatal: Cannot find unit System" (macOS)

FPC findet seine eigenen RTL-Units nicht — passiert nach einem FPC-Upgrade ohne Neuinstallation der RTL:

# FPC RTL-Pfad prüfen
fpc -iSP    # System-Pfad anzeigen

# RTL neu bauen (aus FPC-Quellen)
cd /pfad/zu/fpc-source/rtl
make

# Oder FPC komplett neu installieren
brew reinstall fpc

Langsamer Build — nur einzelne Units geändert

FPC ist inkrementell, aber nur wenn das lib/-Verzeichnis erhalten bleibt:

# Falsch — löscht den Cache
make clean && make

# Richtig — inkrementell, nur Geänderte werden neu kompiliert
make

lyxc findet Standard-Units nicht

# Fehler: "error: unit 'std.io' not found"

# Prüfen welche Pfade lyxc durchsucht
./lyxc --version   # gibt den konfigurierten Stdlib-Pfad aus

# Manuell setzen
export LYX_PATH=/pfad/zu/lyxc/stdlib
./lyxc mein_programm.lyx -o mein_programm

# Oder als Flag
./lyxc mein_programm.lyx -I ./stdlib/ -o mein_programm

Internal Compiler Error (ICE)

Ein Bug im Compiler. Reproduzieren und melden:

# Minimales Testprogramm erstellen, das den Fehler auslöst
# Debug-Build für Stack-Trace
./lyxc-debug test_minimal.lyx -o /tmp/out 2>&1

# Stack-Trace + Lyx-Quellcode als Bug-Report einreichen


Kurzreferenz: Make-Targets

Target Aktion
make Native Debug-Build (schnell)
make release Optimierter Release-Build (-O3, stripped)
make debug Debug-Build mit Symbolen und Heap-Trace
make stdlib Standard-Library vorkompilieren
make test Gesamte Testsuite ausführen
make test-codegen Nur Codegen-Tests
make test-safety Nur Safety-Tests
make cross-arm64 lyxc-binary für ARM64 bauen
make cross-riscv64 lyxc-binary für RISC-V 64 bauen
make cross-win64 lyxc.exe für Windows bauen
make all-targets Alle Cross-Build-Targets
make clean Build-Artefakte löschen (lib/, lyxc, lyxc-debug)
make install Binary + Stdlib in /usr/local/ installieren
make uninstall Installation rückgängig machen