====== 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/'' | 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 |