Dieses Tutorial führt Schritt für Schritt durch den vollständigen Entwicklungszyklus eines sicherheitskritischen Lyx-Programms — von der Projektstruktur über das Schreiben von Safety-konformem Code bis hin zur Erzeugung aller Zertifizierungsnachweise nach DO-178C.
Als durchgehendes Beispiel dient ein Altitude Monitor: eine Komponente, die Druckrohdaten in Höhenmeter umrechnet, Grenzwerte überwacht und einen Warnung-Status ausgibt. Einfach genug, um den Fokus auf den Prozess zu legen — komplex genug, um alle relevanten Safety-Features zu zeigen.
Zielgruppe: Entwickler, die erstmals ein DO-178C-konformes Programm mit Lyx erstellen oder den gesamten Toolchain-Workflow kennenlernen wollen.
Eine klare Verzeichnisstruktur ist die Grundlage für nachvollziehbare, auditierbare Builds. DO-178C verlangt, dass Quellcode, Tests und Nachweise klar voneinander getrennt sind.
mkdir -p altitude_monitor/{src,tests,build,evidence}
cd altitude_monitor
| Verzeichnis | Inhalt |
|---|---|
src/ | Produktiver Quellcode (.lyx-Dateien) |
tests/ | Testfälle und Testprogramme |
build/ | Compiler-Ausgaben (Binaries, .lyu-Units) |
evidence/ | Zertifizierungsnachweise (Reports, Logs, Graphs) |
Struktur nach dem Tutorial:
altitude_monitor/
├── src/
│ ├── types.lyx # Gemeinsame Typdefinitionen
│ └── altitude.lyx # Hauptkomponente
├── tests/
│ └── altitude_test.lyx # Testprogramm
├── build/
│ ├── altitude.elf
│ └── altitude_test.elf
└── evidence/
├── call_graph.dot
├── coverage.html
├── static_analysis.log
├── stack_report.log
└── provenance.log
Alle gemeinsamen Typen kommen in eine eigene Datei. Range-Typen sind das erste Sicherheitsnetz — sie machen ungültige Zustände auf Typebene unmöglich.
Datei: src/types.lyx
unit AltitudeMonitor.Types;
// Physikalische Wertebereiche — Compile-Zeit- und Laufzeit-geprüft
type Altitude = int64 range -1000..15000; // Meter über MSL
type Pressure = int64 range 10000..115000; // Pascal (10–1150 hPa)
type Temperature = int64 range -90..60; // Grad Celsius
type Confidence = int64 range 0..100; // Prozent
// Warnstatus — geschlossene Menge, kein ungültiger Wert möglich
type WarningLevel = enum {
NOMINAL,
CAUTION, // Annäherung an Grenzwert
WARNING, // Grenzwert überschritten
CRITICAL // Zweiter Grenzwert überschritten — sofortiger Handlungsbedarf
};
// Sensor-Rohdaten vom ADC
type SensorFrame = struct {
pressure_raw: int64;
temperature_raw: int64;
valid: bool;
sequence_id: int64;
};
// Ausgabe der Altitude-Monitor-Komponente
type AltitudeResult = struct {
altitude: Altitude;
warning: WarningLevel;
confidence: Confidence;
valid: bool;
};
Die Hauptkomponente enthält die eigentliche Berechnungslogik. Jede Funktion, die im Regelzyklus aufgerufen wird, bekommt die entsprechenden Safety-Pragmas.
Datei: src/altitude.lyx
unit AltitudeMonitor;
import AltitudeMonitor.Types;
import std.math;
// ── Grenzwerte ────────────────────────────────────────────────────────────────
con CAUTION_ALTITUDE: Altitude := 12000; // Meter — Vorsichtsgrenze
con WARNING_ALTITUDE: Altitude := 13500; // Meter — Warngrenze
con CRITICAL_ALTITUDE: Altitude := 14500; // Meter — Kritische Grenze
con SEA_LEVEL_PRESSURE: f64 := 101325.0; // Pascal
con SCALE_HEIGHT: f64 := 8434.0; // Meter (barometrische Formel)
con MIN_CONFIDENCE: Confidence := 70; // Unter diesem Wert: Ergebnis ungültig
// ── Interne Hilfsfunktionen ───────────────────────────────────────────────────
// Berechnet Höhe aus Luftdruck nach barometrischer Höhenformel
// Kein @flight_crit — wird nur aus validierten Funktionen aufgerufen
@stack_limit(128)
fn PressureToAltitude(pressure_pa: f64): f64 {
con EXPONENT: f64 := 0.190263;
var ratio: f64 := pressure_pa / SEA_LEVEL_PRESSURE;
return SCALE_HEIGHT * (1.0 - math.Pow(ratio, EXPONENT));
}
// Berechnet Konfidenzwert aus Sensorkonsistenz (vereinfacht)
@stack_limit(128)
fn ComputeConfidence(frame: SensorFrame): Confidence {
if (!frame.valid) { return 0; }
// Plausibilitätsprüfung Druck vs. Temperatur (vereinfacht)
var p_ok: bool := (frame.pressure_raw >= 10000) && (frame.pressure_raw <= 115000);
var t_ok: bool := (frame.temperature_raw >= -90) && (frame.temperature_raw <= 60);
if (!p_ok || !t_ok) { return 40; }
return 95;
}
// Bestimmt Warnstufe aus berechneter Höhe
@stack_limit(128)
fn ClassifyAltitude(alt: Altitude): WarningLevel {
if (alt >= CRITICAL_ALTITUDE) { return WarningLevel.CRITICAL; }
if (alt >= WARNING_ALTITUDE) { return WarningLevel.WARNING; }
if (alt >= CAUTION_ALTITUDE) { return WarningLevel.CAUTION; }
return WarningLevel.NOMINAL;
}
// ── Hauptfunktion ─────────────────────────────────────────────────────────────
@dal(B)
@flight_crit
@stack_limit(512)
@wcet(800)
pub fn ProcessSensorFrame(frame: SensorFrame): AltitudeResult {
// Ungültige oder nicht plausible Frames sicher ablehnen
if (!frame.valid) {
return AltitudeResult {
altitude: 0,
warning: WarningLevel.NOMINAL,
confidence: 0,
valid: false
};
}
// TMR-Schutz für den berechneten Höhenwert
@redundant var altitude_raw: f64 :=
PressureToAltitude(frame.pressure_raw as f64);
// Clamping in den gültigen Range-Typ — Range-Typ übernimmt weitere Prüfung
var alt_clamped: int64 := altitude_raw as int64;
if (alt_clamped < -1000) { alt_clamped := -1000; }
if (alt_clamped > 15000) { alt_clamped := 15000; }
@redundant var altitude: Altitude := alt_clamped;
var conf: Confidence := ComputeConfidence(frame);
var warning: WarningLevel := ClassifyAltitude(altitude);
// Ergebnis als ungültig markieren, wenn Konfidenz zu niedrig
var result_valid: bool := (conf >= MIN_CONFIDENCE);
return AltitudeResult {
altitude: altitude,
warning: warning,
confidence: conf,
valid: result_valid
};
}
Tests in Lyx sind gewöhnliche Programme, die die zu testende Unit importieren. Für MC/DC müssen alle atomaren Bedingungen unabhängig voneinander den Gesamtausdruck beeinflussen — die Testfälle müssen diese Kombinationen explizit abdecken.
Datei: tests/altitude_test.lyx
unit AltitudeMonitor.Tests;
import AltitudeMonitor;
import AltitudeMonitor.Types;
import std.test;
// ── Hilfsfunktion ─────────────────────────────────────────────────────────────
fn MakeFrame(pressure: int64, temp: int64, valid: bool): SensorFrame {
return SensorFrame {
pressure_raw: pressure,
temperature_raw: temp,
valid: valid,
sequence_id: 1
};
}
// ── Testfälle für ProcessSensorFrame ─────────────────────────────────────────
// TC-01: Ungültiger Frame → result.valid = false, warning = NOMINAL
fn TC01_InvalidFrame(): void {
var frame := MakeFrame(101325, 15, false);
var result := ProcessSensorFrame(frame);
test.Assert(!result.valid, "TC-01: result.valid soll false sein");
test.Assert(result.warning == WarningLevel.NOMINAL, "TC-01: warning soll NOMINAL sein");
test.Assert(result.confidence == 0, "TC-01: confidence soll 0 sein");
}
// TC-02: Normalbetrieb, Meeresspiegel — NOMINAL
fn TC02_NominalSealevel(): void {
var frame := MakeFrame(101325, 15, true);
var result := ProcessSensorFrame(frame);
test.Assert(result.valid, "TC-02: result.valid soll true sein");
test.Assert(result.warning == WarningLevel.NOMINAL, "TC-02: warning soll NOMINAL sein");
test.Assert(result.confidence >= 70, "TC-02: confidence soll >= 70 sein");
}
// TC-03: Reiseflug ~10.000 m — NOMINAL (unterhalb CAUTION-Grenze)
fn TC03_CruiseAltitude(): void {
var frame := MakeFrame(26436, -45, true); // ~10.000 m
var result := ProcessSensorFrame(frame);
test.Assert(result.valid, "TC-03: result soll valid sein");
test.Assert(result.warning == WarningLevel.NOMINAL, "TC-03: 10.000m soll NOMINAL sein");
}
// TC-04: CAUTION-Bereich (~12.500 m)
fn TC04_CautionAltitude(): void {
var frame := MakeFrame(19000, -55, true); // ~12.500 m
var result := ProcessSensorFrame(frame);
test.Assert(result.valid, "TC-04: result soll valid sein");
test.Assert(result.warning == WarningLevel.CAUTION, "TC-04: soll CAUTION sein");
}
// TC-05: WARNING-Bereich (~13.800 m)
fn TC05_WarningAltitude(): void {
var frame := MakeFrame(16000, -60, true); // ~13.800 m
var result := ProcessSensorFrame(frame);
test.Assert(result.valid, "TC-05: result soll valid sein");
test.Assert(result.warning == WarningLevel.WARNING, "TC-05: soll WARNING sein");
}
// TC-06: CRITICAL-Bereich (~14.700 m)
fn TC06_CriticalAltitude(): void {
var frame := MakeFrame(14000, -62, true); // ~14.700 m
var result := ProcessSensorFrame(frame);
test.Assert(result.valid, "TC-06: result soll valid sein");
test.Assert(result.warning == WarningLevel.CRITICAL, "TC-06: soll CRITICAL sein");
}
// TC-07: Druck außerhalb Plausibilitätsbereich → confidence < MIN → result.valid = false
fn TC07_ImplausiblePressure(): void {
var frame := MakeFrame(5000, 15, true); // Druck zu niedrig (< 10000 Pa)
var result := ProcessSensorFrame(frame);
test.Assert(!result.valid, "TC-07: implausible pressure soll result.valid = false ergeben");
}
// TC-08: Temperatur außerhalb Plausibilitätsbereich
fn TC08_ImplausibleTemperature(): void {
var frame := MakeFrame(101325, 80, true); // Temperatur zu hoch (> 60°C)
var result := ProcessSensorFrame(frame);
test.Assert(!result.valid, "TC-08: implausible temperature soll result.valid = false ergeben");
}
// TC-09: Grenzwert genau an CAUTION-Grenze (Boundary Value)
fn TC09_BoundaryCAUTION(): void {
// Druck der ~12.000 m entspricht (Boundary Value Analysis)
var frame := MakeFrame(19330, -56, true);
var result := ProcessSensorFrame(frame);
test.Assert(result.valid, "TC-09: Boundary-Frame soll valid sein");
// Warning ist CAUTION oder NOMINAL je nach exaktem Rundungsverhalten
}
// TC-10: MC/DC — frame.valid=false dominiert, Pressure/Temp irrelevant
fn TC10_MCDC_InvalidOverrides(): void {
var frame := MakeFrame(5000, 80, false); // Beide Werte außerhalb + invalid
var result := ProcessSensorFrame(frame);
test.Assert(!result.valid, "TC-10: MCDC: invalid-Flag dominiert");
test.Assert(result.confidence == 0, "TC-10: MCDC: confidence soll 0 sein");
}
// ── Einstiegspunkt ────────────────────────────────────────────────────────────
fn main(): int64 {
test.Begin("AltitudeMonitor — Testlauf");
TC01_InvalidFrame();
TC02_NominalSealevel();
TC03_CruiseAltitude();
TC04_CautionAltitude();
TC05_WarningAltitude();
TC06_CriticalAltitude();
TC07_ImplausiblePressure();
TC08_ImplausibleTemperature();
TC09_BoundaryCAUTION();
TC10_MCDC_InvalidOverrides();
test.End();
return 0;
}
Der erste Build dient der Überprüfung: Sind alle Safety-Regeln eingehalten? Compiliert der Code ohne Warnungen?
# Typen kompilieren (vorkompilierte Unit erzeugen)
lyxc src/types.lyx \
--compile-unit \
--lint \
-o build/types.lyu
# Hauptkomponente kompilieren — vollständige Safety-Prüfung
lyxc src/altitude.lyx \
-I build/ \
--lint \
--static-analysis \
--stack-check \
--target=arm64 \
-o build/altitude.elf
Erwartete Ausgabe bei korrektem Code:
[lint] AltitudeMonitor — OK (0 warnings, 0 errors)
[stack] ProcessSensorFrame: 312 bytes / 512 limit — OK
[stack] PressureToAltitude: 96 bytes / 128 limit — OK
[stack] ComputeConfidence: 80 bytes / 128 limit — OK
[stack] ClassifyAltitude: 64 bytes / 128 limit — OK
[build] build/altitude.elf — OK
Häufige Lint-Fehler und ihre Bedeutung:
| Fehlermeldung | Bedeutung | Lösung |
|---|---|---|
heap allocation in @flight_crit function | new innerhalb einer @flight_crit-Funktion | new in die Initialisierungsphase verschieben |
unbounded loop in @flight_crit context | Schleife ohne limit(N) | limit(N) hinzufügen |
missing @stack_limit on @flight_crit function | Kein Stack-Budget definiert | @stack_limit(N) ergänzen |
range violation: value 16000 exceeds Altitude range | Konstante verletzt Range-Typ | Konstante oder Typ korrigieren |
@wcet annotation missing for DAL-B function | DAL-B verlangt WCET-Budget | @wcet(N) ergänzen |
# Testprogramm kompilieren
lyxc tests/altitude_test.lyx \
-I build/ \
--lint \
-o build/altitude_test.elf
# Tests ausführen
./build/altitude_test.elf
Erwartete Ausgabe:
=== AltitudeMonitor — Testlauf ===
[PASS] TC-01: result.valid soll false sein
[PASS] TC-01: warning soll NOMINAL sein
[PASS] TC-01: confidence soll 0 sein
[PASS] TC-02: result.valid soll true sein
[PASS] TC-02: warning soll NOMINAL sein
[PASS] TC-02: confidence soll >= 70 sein
[PASS] TC-03: result soll valid sein
[PASS] TC-03: 10.000m soll NOMINAL sein
[PASS] TC-04: result soll valid sein
[PASS] TC-04: soll CAUTION sein
[PASS] TC-05: result soll valid sein
[PASS] TC-05: soll WARNING sein
[PASS] TC-06: result soll valid sein
[PASS] TC-06: soll CRITICAL sein
[PASS] TC-07: implausible pressure soll result.valid = false ergeben
[PASS] TC-08: implausible temperature soll result.valid = false ergeben
[PASS] TC-09: Boundary-Frame soll valid sein
[PASS] TC-10: MCDC: invalid-Flag dominiert
[PASS] TC-10: MCDC: confidence soll 0 sein
=== ALLE TESTS BESTANDEN (19/19) ===
Die statische Analyse sucht nach Problemen, die Compiler und Tests allein nicht finden — unerreichbarer Code, potenzielle Null-Pointer, nicht initialisierte Variablen, Division durch null.
lyxc src/altitude.lyx \
-I build/ \
--static-analysis \
2>&1 | tee evidence/static_analysis.log
Beispielausgabe:
[analysis] AltitudeMonitor — ProcessSensorFrame
[OK] No unreachable code detected
[OK] No null-pointer dereferences detected
[OK] No uninitialized variables
[OK] No division-by-zero paths
[OK] All enum cases handled in ClassifyAltitude
[analysis] Completed — 0 issues
Call-Graph erzeugen (zeigt, welche Funktion welche aufruft — Pflichtnachweis für DO-178C):
lyxc src/altitude.lyx \
-I build/ \
--call-graph \
-o evidence/call_graph.dot
# Visualisierung als PNG (benötigt Graphviz)
dot -Tpng evidence/call_graph.dot -o evidence/call_graph.png
Der Call-Graph zeigt u.a., dass ProcessSensorFrame genau drei interne Funktionen aufruft (PressureToAltitude, ComputeConfidence, ClassifyAltitude) und keine externen oder nicht validierten Funktionen — das ist für DAL-B eine Anforderung.
Der Stack-Report dokumentiert den maximalen Stack-Verbrauch jeder Funktion auf dem gesamten Call-Graph. Er ist der Nachweis, dass kein Stack-Overflow auftreten kann.
lyxc src/altitude.lyx \
-I build/ \
--stack-check \
2>&1 | tee evidence/stack_report.log
Beispielausgabe:
[stack-check] AltitudeMonitor
ProcessSensorFrame 312 / 512 bytes OK
PressureToAltitude 96 / 128 bytes OK
ComputeConfidence 80 / 128 bytes OK
ClassifyAltitude 64 / 128 bytes OK
Worst-case call depth: ProcessSensorFrame → PressureToAltitude
Worst-case stack total: 408 bytes
[stack-check] All limits satisfied — PASS
MC/DC (Modified Condition/Decision Coverage) ist für DAL-A und DAL-B vorgeschrieben. Jede atomare Bedingung in einem booleschen Ausdruck muss nachweislich unabhängig voneinander den Gesamtausdruck beeinflussen.
Schritt 1 — Instrumentiertes Binary erzeugen:
lyxc src/altitude.lyx \
-I build/ \
--mcdc-instrument \
-o build/altitude_instrumented.elf
Schritt 2 — Tests mit dem instrumentierten Binary laufen lassen:
lyxc tests/altitude_test.lyx \
-I build/ \
--link build/altitude_instrumented.elf \
-o build/altitude_test_cov.elf
./build/altitude_test_cov.elf
# Coverage-Daten werden automatisch in altitude_monitor.coverage geschrieben
Schritt 3 — Coverage-Report erzeugen:
lyxc --coverage-report altitude_monitor.coverage \
-o evidence/coverage.html
Beispiel-Report-Ausgabe (Konsolenübersicht):
Coverage Report — AltitudeMonitor
══════════════════════════════════════════════════════════════
Function Line Branch MC/DC
──────────────────────────────────────────────────────────────
ProcessSensorFrame 100% 100% 100% ✓
PressureToAltitude 100% 100% 100% ✓
ComputeConfidence 100% 100% 88% ✗ ← DAL-B: OK, DAL-A: nicht ausreichend
ClassifyAltitude 100% 100% 100% ✓
──────────────────────────────────────────────────────────────
GESAMT 100% 100% 97%
══════════════════════════════════════════════════════════════
DAL-B Anforderung: 100% MC/DC für alle @dal(B)-Funktionen — PASS
Was das Ergebnis bedeutet: ComputeConfidence hat 88 % MC/DC. Für DAL-B ist das kein Problem — die Funktion ist nicht mit @dal(B) annotiert. Wäre das Projekt DAL-A, müsste ein weiterer Testfall ergänzt werden, der die fehlende Kombination abdeckt.
Der Report zeigt, welche Bedingung fehlt:
[mcdc-gap] ComputeConfidence — Bedingung 'frame.valid' hat keinen Test, bei dem
'p_ok && t_ok = true' aber 'frame.valid' allein das Ergebnis bestimmt.
Fehlende Kombination: valid=true, p_ok=true, t_ok=true → nur valid=false dreht Ergebnis.
Zusätzlichen Testfall ergänzen:
// TC-11: MC/DC — valid=false bei sonst gültigen Werten
fn TC11_MCDC_ValidAlone(): void {
var frame_valid := MakeFrame(101325, 15, true);
var frame_invalid := MakeFrame(101325, 15, false);
var r_valid := ProcessSensorFrame(frame_valid);
var r_invalid := ProcessSensorFrame(frame_invalid);
// Nur 'valid' unterscheidet sich — MC/DC-Nachweis für diese Bedingung
test.Assert( r_valid.valid, "TC-11: valid=true → result.valid = true");
test.Assert(!r_invalid.valid, "TC-11: valid=false → result.valid = false");
}
Für die Zertifizierung muss nachweisbar sein, dass der gelieferte Maschinencode aus dem geprüften Quellcode entstanden ist — bit-identisch, reproduzierbar.
# Reproduzierbaren Final-Build mit vollständigem Audit-Log erzeugen
lyxc src/altitude.lyx \
-I build/ \
--lint \
--static-analysis \
--stack-check \
--provenance \
--trace-passes \
--target=arm64 \
-o build/altitude_final.elf \
2>&1 | tee evidence/provenance.log
–provenance schreibt für jede IR-Transformation einen signierten Eintrag ins Log:
[provenance] Source: src/altitude.lyx SHA256: a3f8...c291
[provenance] Unit: build/types.lyu SHA256: 7b21...e04a
[provenance] Pass 01: parse → AST
[provenance] Pass 02: type-check → typed AST
[provenance] Pass 03: range-check → annotated AST
[provenance] Pass 04: lower → IR
[provenance] Pass 05: mcdc-instrument → instrumented IR
[provenance] Pass 06: codegen arm64 → ELF
[provenance] Output: build/altitude_final.elf SHA256: 19d4...f7a2
[provenance] Compiler: lyxc v0.8.5-aerospace
[provenance] Flags: --lint --static-analysis --stack-check --provenance --trace-passes --target=arm64
[provenance] Timestamp: 2026-05-22T14:30:00Z
–build-info zeigt die Compiler-Version und Build-Konfiguration, die im Abnahmedokument festgehalten wird:
lyxc --build-info
lyxc version: 0.8.5-aerospace
Target: arm64-linux-elf
Build date: 2026-05-22
Reproducible: yes
FIPS: no
Die folgende Tabelle zeigt, welche Schritte aus diesem Tutorial für welches DAL-Level verpflichtend sind.
| Nachweis | DAL-A | DAL-B | DAL-C | DAL-D |
|---|---|---|---|---|
–lint (DO-178C-Konformitätsprüfung) | ✓ | ✓ | ✓ | ✓ |
–static-analysis | ✓ | ✓ | ✓ | — |
–stack-check (Stack-Report) | ✓ | ✓ | — | — |
–call-graph (Call-Graph-Dokumentation) | ✓ | ✓ | ✓ | — |
–mcdc-instrument + 100 % MC/DC | ✓ | ✓ | — | — |
| Statement Coverage 100 % | ✓ | ✓ | ✓ | ✓ |
| Branch Coverage 100 % | ✓ | ✓ | ✓ | — |
–provenance (Traceability) | ✓ | ✓ | — | — |
–trace-passes (Audit-Log) | ✓ | — | — | — |
@redundant (TMR) für Zustandsvariablen | ✓ (empfohlen) | — | — | — |
@integrity(mode: lockstep) | ✓ (empfohlen) | — | — | — |
| Boundary Value Analysis in Tests | ✓ | ✓ | ✓ | — |
@dal-Annotation im Quellcode | ✓ | ✓ | ✓ | ✓ |
Das folgende Skript führt alle Schritte in der richtigen Reihenfolge aus und legt alle Nachweise im evidence/-Verzeichnis ab. Es kann als Basis für eine CI/CD-Pipeline oder einen manuellen Abnahme-Build dienen.
#!/bin/bash
set -e # Abbruch bei jedem Fehler
echo "=== AltitudeMonitor — Safety Build ==="
# 1. Typen-Unit kompilieren
lyxc src/types.lyx \
--compile-unit \
--lint \
-o build/types.lyu
# 2. Hauptkomponente — Lint + Statische Analyse
lyxc src/altitude.lyx \
-I build/ \
--lint \
--static-analysis \
--stack-check \
--call-graph -o evidence/call_graph.dot \
--target=arm64 \
-o build/altitude.elf \
2>&1 | tee evidence/static_analysis.log
# 3. Tests bauen und ausführen
lyxc tests/altitude_test.lyx \
-I build/ \
--lint \
-o build/altitude_test.elf
./build/altitude_test.elf
# 4. MC/DC-Instrumentierung und Coverage
lyxc src/altitude.lyx \
-I build/ \
--mcdc-instrument \
--target=arm64 \
-o build/altitude_instrumented.elf
lyxc tests/altitude_test.lyx \
-I build/ \
--link build/altitude_instrumented.elf \
-o build/altitude_test_cov.elf
./build/altitude_test_cov.elf
lyxc --coverage-report altitude_monitor.coverage \
-o evidence/coverage.html
# 5. Final-Build mit Provenance
lyxc src/altitude.lyx \
-I build/ \
--lint \
--static-analysis \
--stack-check \
--provenance \
--trace-passes \
--target=arm64 \
-o build/altitude_final.elf \
2>&1 | tee evidence/provenance.log
# 6. Call-Graph als PNG (optional, benötigt Graphviz)
if command -v dot &> /dev/null; then
dot -Tpng evidence/call_graph.dot -o evidence/call_graph.png
fi
echo ""
echo "=== Build erfolgreich ==="
echo "Nachweise in evidence/:"
ls -lh evidence/
| Schritt | Kommando | Artefakt |
|---|---|---|
| Typen-Unit | lyxc –compile-unit –lint | build/types.lyu |
| Safety-Build | –lint –static-analysis –stack-check | build/altitude.elf |
| Tests ausführen | ./build/altitude_test.elf | Konsolenausgabe |
| Statische Analyse | –static-analysis | evidence/static_analysis.log |
| Call-Graph | –call-graph | evidence/call_graph.dot |
| Stack-Report | –stack-check | evidence/stack_report.log |
| MC/DC-Coverage | –mcdc-instrument + –coverage-report | evidence/coverage.html |
| Provenance | –provenance –trace-passes | evidence/provenance.log |