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