Wenn ein Buildfile einmal nicht so arbeitet wie erwartet, beginnt die Suche nach der Fehlerursache – wer regelmäßig Makefiles bearbeitet, weiß wie mühsam das sein kann. Yabu ist in dieser Hinsicht nicht einfacher als Make. Im Folgenden sind einige Tips und Erklärungen zusammengestellt, die bei der Behebung von Fehlern helfen können.
Alle Fehlermeldungen von Yabu haben einen 3-stelligen Kurznamen, zum Beispiel S01 für "Syntaxfehler". Im Abschnitt findet man für einige Fehlermeldungen Hinweise zu möglichen Ursachen und Behebung.
Wenn eine Fehlermeldung nicht unmittelbar einleuchtend ist, sollte man Yabu ein zweites Mal mit der Option -v aufrufen. Im Fehlerfalle gibt Yabu dann den vollständigen Programmkontext aus, was zum Verständnis des Fehlers nützlich sein kann. Noch mehr Ausgaben bekommt man mit -vv, -vvv und -vvvv; jedes "v" erhöht den Umfang der Ausgaben.
Normalerweise versucht Yabu, nach einem Fehler fortzufahren und die nicht betroffenen Ziele trotzdem zu erreichen. Das führt manchmal dazu, daß der gleiche Fehler immer wieder auftritt und die Ausgabe unübersichtlich wird. In diesem Fall sollte man Yabu mit der Option -k aufrufen oder in .yaburc bzw. im Buildfile "max_warnings=0" setzen.
Viele Terminals beherrschen Zeichenattribute wie "fett", "invers", "unterstrichen" oder verschiedene Schriftfarben. Yabu kann diese Fähigkeiten nutzen, um Fehlermeldungen optisch hervorzuheben. Das erleichtert die Suche nach Fehlermeldungen, vor allem bei längeren Programmläufen mit vielen Ausgaben. Die folgenden Einstellungen in .yaburc funktionieren zum Beispiel mit dem weit verbreiteten "xterm" (zumindest mit neueren Versionen):
highlight_e=\e[01;31m|\e[0m
Sie bewirken, daß Fehlermeldungen in roter Schrift erscheinen.
Ausgabe von Daten mit -D
Mit dem Kommandozeilenschalter "-D Auswahl" kann man bei Bedarf die Ausgabe zusätzlicher Daten veranlassen. Das Argument Auswahl ist eine Kombination der folgenden Buchstaben:
Ausgabe aller Regeln
Ausgabe aller Ziele nach Abschluß von Phase 1. Hier werden nur die Ziele ausgegeben, die in der Statusdatei (Buildfile.state) gespeichert sind.
Ausgabe aller Ziele nach Abschluß von Phase 2.
Ausgabe der Programmeinstellungen. Die in eckigen Klammern angegebene Zahl gibt an, woher der jeweilige Wert stammt:
[0] |
Standardwert |
[5] |
Globale Konfigurationsdatei (yabu.cfg) |
[4] |
Globale Konfigurationsdatei (yabu.cfg) |
[5] |
Konfiguratiosdatei des Benutzers ($HOME/.yaburc) |
[10] |
|
[20] |
Kommandozeile |
Ausgabe aller Optionen
Ausgabe aller Variablen
Alle Ausgaben
Die Buchstaben lassen sich beliebig kombinieren, zum Beispiel:
yabu -D rvc
Ausgabe von Meldungen mit -v
Normalerweise gibt Yabu eine Zeile aus, wenn ein Ziel veraltet war und das zugehörige Skript ausgeführt wird. Durch Aufruf mit -v, -vv, -vvv usw. kann man den Umfang der Ausgaben schrittweise steigern. Um einem Fehler auf die Spur zu kommen, oder um zu verstehen warum Yabu ein bestimmtes Skript ausführt bzw. nicht ausführt, beginnt man sinnvollerweise mit -v oder -vv.
Mit -v nennt Yabu die Namen der verarbeiteten Dateien, also Buildfile, Buildfile.state und
.include
-Dateien. Ist ein Ziel veraltet, dann gibt Yabu den Namen der (ersten) Quelle
aus, die jünger als das Ziel ist.
Im Falle eines Fehlers gibt Yabu zusätzlich zu der Fehlermeldung den aktuellen
Programmkontext aus.
Durch Angabe von -vv läßt sich die Arbeit von Yabu in Phase 2 im Detail mitverfolgen. Zusätzlich zu den unter -v beschriebenen Ausgaben gibt Yabu alle auftretenden Ziele aus, und beschreibt ihre Abhängigkeiten. Ist ein Ziel noch aktuell, dann gibt Yabu auch diese Tatsache in einer eigenen Meldung aus.
Die höheren Ausgabestufen ab -vvv liefern weitere Details, unter anderem zur Auswahl der Regeln. Hat man ein Problem bereits eingekreist, dann kann es sinnvoll sein, sich diese Ausgaben genauer anzusehen. Um den Umfang der Meldungen zu reduzieren, sollte man -vvv (und höher) nur im Zusammenhang mit einem einzelnen Ziel verwenden. Beispiel:
yabu -vvvv yabu3.o
Fehlersuche in Skripten: -e, -n, -k und -j
Normalerweise zeigt Yabu die ausgeführten Kommandos nicht an.
Nur wenn ein Skript mit einem Exit-Code ungleich 0 endet,
gibt Yabu zusammen mit der Fehlermeldung das vollständige Skript aus.
Dieses Verhalten läßt sich über zwei Programmeinstellungen steuern:
echo_after_error
(Default: true) bestimmt, ob Yabu nach einem Fehler
das Skript ausgibt, und mit echo
oder -e
(Default: false) legt man fest,
daß Yabu alle Skripte ausgeben soll.
Die Einstellung echo=true
ist nur in Ausnahmefällen sinnvoll.
Zur Fehlersuche ist sie aber sehr nützlich, insbesondere wenn man sie
mit -nn
kombiniert. Zum Beispiel zeigt
yabu -nne prog1.o
an, welche Kommandos Yabu ausführen würde, um prog1.o zu erzeugen.
Yabu versucht nach einem Fehler normalwerweise, weitere Ziele zu
erreichen. Das kann manchmal dazu führen, daß die gleiche Fehlermeldung
sehr oft wiederholt wird. In diesem Fall ist es sinnvoll, mit
max_warnings=0
oder -k
die Ausführung nach dem ersten Fehler
abzubrechen.
Parallelisierung verhindern: -j und -p
Die Fehlersuche wird manchmal dadurch erschwert, daß Yabu mehrere
Skript parallel ausführt. Das betrifft die "lokale" Ausführung durch
Yabu selbst und auch die Verteilung von Skripten auf Server.
Manche Fehler treten auch nur dann auf, wenn Skripte parellel laufen.
Deshalb gibt es zwei Einstellungen, mit denen sich die Parallelisierung
unterbinden läßt: use_server=false
(oder -j
) verhindert, daß Yabu
Skripte auf einem Server ausführt, und parallel_build=false
(oder -p
)
erzwingt die sequentielle Ausführung von Skripten durch Yabu selbst.
Manchmal ergibt sich bei der Fehlersuche die Frage, welchen Wert eine bestimmte Variable hat. Der Wert entsteht möglicherweise durch Zusammenwirken vieler, über das ganze Buildfile verstreuter Zuweisungen. Dazu kommen unter Umständen noch verschiedene Konfigurationen, welche ebenfalls den Wert einer Variablen beeinflussen. Ein Beispiel:
LOCAL_john=/home/john/local CC=$(LOCAL)/bin/gcc LOCAL=$(LOCAL_$(USER)) USER=john LOCAL[linux]=/usr
Deshalb ist es in der Praxis oft sehr mühsam, den Wert einer Variable "zu Fuß" durch Betrachten des Buildfiles zu bestimmen. Viel einfacher ist es, Yabu diese Arbeit zu überlassen. Die folgenden, durch [JGC07] inspirierten Regeln helfen dabei:
print-%:: echo "%=$(%)" print-%/%:: [%2] echo "%1[%2]=$(%1)"
Mit diesen beiden Regeln im Buildfile kann man auf einfache Weise beliebige Variablenwerte abfragen:
# yabu -q print-CC CC=/home/john/local/bin/gcc
Oder, wenn der Wert in einer bestimmten Konfiguration gefragt ist:
# yabu -q print-CC/+linux+gcc CC[+linux+gcc]=/usr/bin/gcc
Manchmal möchte man aber nicht nur den Wert einer Variablen wissen,
sondern auch, wie der Wert enstanden ist.
Eine erste Hilfe dabei ist, sich mit der Option -D v
die Definitionen
aller Variablen ausgeben zu lassen:
# yabu -D v ... ----- Variablen ----- ... LOCAL_john=/home/john/local CC=$(LOCAL)/bin/gcc LOCAL=$(LOCAL_$(USER)) [+gaga]=/usr/gaga USER=john ...
Mit -vvvv gibt Yabu unter anderem alle Variablendefinitionen aus. Das beantwortet zwar noch nicht die Frage "Wie kam es zu dem Wert CC=/usr/bin/gcc?", doch immerhin findet man hier alle beteiligten Zuweisungen übersichlich zusammengefaßt.
G01: Speicheranforderung gescheitert
Das Betriebssystem konnte eine Speicheranforderung nicht erfüllen.
G10: Datei kann nicht geöffnet werden
Yabu kann eine Datei nicht öffnen. Warum, sollte aus der Fehlermeldung hervorgehen.
G20: Fehler bei einer Systemfunktion
Eine Systemfunktion, typischerweise im Zusammenhang mit Dateien oder der Ausführung eines Skriptes, ist fehlgeschlagen. Mögliche Ursachen sind mangelnde Systemresourcen oder fehlende Zugriffsrechte.
G21: Verzeichnis konnte nicht angelegt werden
Yabu hat erfolglos versucht, ein fehlendes Verzeichnis anzulegen.
Dieser Fehler kann nur auftreten, wenn auto_mkdir=true
eingestellt ist.
G30: Fehler im Skript
Bei der Ausführung eines Skriptes ist ein Fehler aufgetreten. Das betreffende Ziel (und alle davon abhängigen Ziele) werden als fehlerhaft gewertet. Yabu versucht jedoch, alle übrigen, davon unabhängigen Ziele zu erreichen. Dies kann man mit der Option -k verhindern.
Der Fehler G30 kann auch dann auftreten, wenn Yabu ein Skript im Zuge
einer .include
- oder !configure
-Anweisung ausführt.
Die Fehlerursache ist möglicherweise aus den Fehlerausgaben des
Skriptes ersichtlich.
Man beachte, daß die Standardausgabe des Skriptes nicht sichtbar ist,
da sie von Yabu verarbeitet wird.
G41: Unbekannte Option
Die angegebene Kommandozeilenoption ist unbekannt. Eine mögliche Ursache ist, daß das Leerzeichen zwischen einer Option und ihrem Argument fehlt. Zum Beispiel:
yabu -fmybuildfile <----- FEHLER
Eine Hilfe zur Kommandozeilensyntax läßt sich mit
yabu -?
ausgeben.
G42: Fehlendes Argument
Auf der Kommandozeile wurde eine Option benutzt, die ein Argument benötigt, das Argument fehlt jedoch. Zum Beispiel:
yabu -f <----- FEHLER
G43: Falsches Argument hinter -y
Das auf -y folgende Argument ist keiner der erlaubten Namen "mt", "mtid" oder "cksum".
S01: Allgemeiner Syntaxfehler
Diser Fehler wird ausgegeben, wenn die Syntax des Buildfiles nicht korrekt ist, und keine spezielle Fehlermeldung hierfür existiert.
S02: Element nicht abgeschlossen
Ein syntaktisches Element wurde nicht ordnungsgemäß abgeschlossen.
Beispiele sind ein fehlendes .endforeach
, oder eine fehlende
schließende Klammer (")", "]" oder "}").
B21: Ziel wurde nicht erzeugt
Das Skript zu einer Regel wurde zwar fehlerfrei ausgeführt, aber die erwartete Zieldatei existiert nicht. Eine mögliche Ursache ist, daß Regel und Skript nicht zusammenpassen, wie im folgenden Beispiel:
file.o: file.c cc -o file.obj file.c
Yabu erwartet, daß nach Ausführung der Regel die Datei "file.o" existiert, tatsächlich erzeugt das Skript aber "file.obj".
Eine weitere typische Fehlerursache ist, daß die Regel gar keine Datei erzeugt, aber trotzdem mit der Syntax für dateierzeugende Regeln geschrieben wurde:
run-tests : test1 test2 ./test1 ./test2
Hier dient "run-tests" lediglich als Abkürzung zur Ausführung der beiden Testprogramme. In solchen Fällen muß man eine Alias-Regel verwenden:
run-tests :: test1 test2 ./test1 ./test2
Wie beim Fehler G30 gilt: das betroffene Ziel wird als fehlerhaft markiert, Yabu bricht jedoch nicht ab, sondern versucht alle übrigen, davon unabhängigen Ziele zu aktualisieren. Dies läßt sich mit der Option -k verhindern.
Wenn man Yabu mit der Option -n aufruft, wird nicht nur die Ausführung von Skripten unterdrückt, sondern auch der Fehler B21.
L01: Element ist nicht definiert
Dieser Fehler tritt auf, wenn man eine Variable, Option o.ä. verwendet, ohne sie vorher zu definieren. Folgende Situationen führen zu dem Fehler:
Eine nicht definierte Variable wird verwendet. Als "Verwendung" einer
Variable zählt auch die Erweiterung des Wertes mit +=
oder ?=
:
VAR += xyz <----- FEHLER, falls VAR nicht vorher definiert
Eine bedingte Zuweisung an eine Variable erfolgt ohne vorherige Zuweisung des Standardwertes. Beispiel:
CC[linux]=/usr/bin/gcc ----> Fehler CC=cc
Richtig wäre
CC=cc CC[linux]=/usr/bin/gcc
Ist xxx der Name einer Optionsgruppe, und ist aktuell keine der enthaltenen Optionen aktiv, dann führt die Verwendung von $(.xxx) ebenfalls zu dem Fehler L01.
Ein benanntes Makroargument, das nicht in der Makrodefinition als formales Argument aufgeführt ist.
Ein Platzhalter für Quellen ist nicht definiert. Beispiel:
prog.o: prog.c cc -o $(0) $(2) <----- FEHLER: $(2) nicht definiert
Ein %-Platzhalter in einer Prototyp-Regel ist nicht definiert. Beispiel:
%.o: %.c cc -o $(0) %2.c <----- FEHLER: %2 nicht definiert
Ein Platzhalter in einer Transformationsregel ist nicht definiert.
L03: Element ist bereits definiert
Dieser Fehler bedeutet, daß eine Variable, eine Option oder ein Makro bereits definiert ist. Er tritt auch auf, wenn man in einem Makroaufruf ein Argument mehrfach angibt:
.mymacro arg=value arg=value <----- FEHLER
Optionen und Variablen mit gleichen Namen sind ebenfalls nicht erlaubt und führen zu dem Fehler L03:
!options compiler(gcc ms intel) compiler=GCC <----- FEHLER ('compiler' bereits definiert) gcc=not unix <----- FEHLER ('gcc' bereits definiert)
L04: Zirkuläre Abhängigkeit
Dieser Fehler tritt auf wenn ein Ziel, eine Variable oder ein Makro von sich selbst abhängt. Das würde bei der Auswertung bzw. Ausführung zu einer endlosen Schleife führen und ist deshalb verboten. Zirkuläre Abhängigkeiten entstehen oft indirekt über mehrere "Zwischenstationen". Im folgenden Beispiel hängt die Variable A über B und C von sich selbst ab:
A=aaa-$(B) B=$(C)-bbb C=ccc-$(A)
Da die Anweisungen über das Buildfile verstreut sein können, sind zirkuläre Abhängigkeiten nicht leicht zu erkennen. Auch Yabu bemerkt die Schleife erst, wenn das Ziel, die Variable bzw. das Makro tatsächlich verwendet werden. Die obigen drei Zeilen allein würde Yabu nicht beanstanden; erst wenn der Wert einer der drei Variablen ermittelt wird – zum Beispiel bei Ausführung eines Skriptes, welches $(A) enthält – kommt es zu einem Fehler.
Bei Zielen kann es zu einer ähnlichen Schleifenbildung kommen, im einfachsten Fall durch zwei Regeln der folgenden Art:
a: b b: a
In der Praxis treten meist größere Schleifen aus 3 oder mehr Zielen auf. Auch hier erkennt Yabu den Fehler erst, wenn eines der betroffenen Ziele tatsächlich erreicht werden soll.
Ein anderer Typ von Schleifen kann bei der Verwendung von Prototyp-Regeln entstehen. Dazu ein Beispiel:
a%: ba% b%: ab%
Angewendet auf das Ziel "a" würden diese beiden Regeln eine unendliche Liste von Quellen produzieren, nämlich "a", "ba", "aba", "baba" usw. Da hier kein Ziel zweimal auftritt, versagt die Schleifenerkennung. Yabu beschränkt jedoch die Rekursionstiefe bei der Ermittlung der Quellen auf 50. Beim Überschreiten dieser Grenze bricht Yabu ebenfalls mit dem Fehler L04 ab.
L05: Ziel ist keine reguläre Datei
Diese Fehlermeldung entsteht, wenn folgende Bedingungen zusammenkommen:
das angegebene Ziel existiert bereits,
es gibt keine Regel für dieses Ziel,
es handelt sich nicht um eine reguläre Datei (sondern zum Beispiel um ein Verzeichnis).
Die Idee dabei ist, daß Ziele ohne Regel normalerweise nur die eigentlichen Quelldateien des Projektes sind, aber niemals Verzeichnisse oder andere spezielle Dateien. Der Fehler tritt beispielsweise auf, wenn das Verzeichnis "sources" existiert, im Buildfile nicht vorkommt, und der Benutzer
yabu sources
ausführt.
L11: Zu viele Platzhalter
Ein Ziel in einer Regel enthält mehr als 9 "%"-Platzhalter, oder die linke Seite einer Transformationsregel enthält mehr als 9 "*"-Platzhalter.
L12: Definieren von Optionen nicht möglich
Dieser Fehler tritt auf, wenn eine !options
-Anweisung nach einer bedingten
Zuweisung steht. Zum Beispiel:
!options a VAR= VAR[a]=b !options b <----- FEHLER
Es ist zulässig, Optionen in mehreren !options
-Anweisungen zu definieren.
Dabei müssen aber alle !options
-Anweisungen vor der ersten bedingten Zuweisung
stehen.
L13: Zuweisung an Systemvariable
Eine Variable, deren Name mit "_" beginnt, kann nicht verändert werden.
L14: Wert paßt nicht zur Transformationsregel
Auf eine Variable wurde eine Transformationsregel angewendet, aber eines der zu transformierenden Wörter paßt nicht zur linken Seite der Regel. Zum Beispiel paßt in
OBJS=aaa.o bbb.o ccc.c ddd.o SRCS=$(OBJS:*.o=*.c) <----- FEHLER
der dritte Wert (ccc.c) nicht zur linken Seite (*.o) der Transformationsregel.
Mit einer erweiterten Form der Transformationsregel kann man Yabu dazu bringen, die nicht passenden Wörter zu ignorieren. Das obige Beispiel würde dann so aussehen:
OBJS=aaa.o bbb.o ccc.c ddd.o SRCS=$(OBJS?:*.o=*.c) <----- aaa.c bbb.c ddd.c
L24: Widersprüchliche Optionswerte
Eine Konfiguration enthält widersprüchliche Werte für eine Option, also sowohl "+" als auch "-". Das kann zum Beispiel in einer bedingten Zuweisung vorkommen:
CC[debug+gcc-debug]=/usr/bin/gcc <----- FEHLER
oder, weniger offensichtlich:
!configuration [gcc] CFLAGS[debug-gcc]=-g <----- FEHLER
Der Fehler tritt auch dann auf, wenn man versucht, mehr als eine Option aus einer Optionsgruppe einzuschalten. Das ist ebenfalls verboten, denn mit dem Einschalten einer Option werden alle anderen Optionen in der Gruppe implizit ausgeschaltet. Auch hierzu ein Beispiel:
!options type(debug release) CFLAGS[debug+release]=-g <----- FEHLER
L31: Mehrdeutige Regeln
Für das angegebene Ziel ist keine eindeutige Regel definiert. Dieser Fehler tritt auf, wenn es für das betreffende Ziel zwei oder mehr Regeln mit Skript gibt (Regeln ohne Skript sind dagegen in beliebiger Anzahl erlaubt). Ein Beispiel:
all:: test-abc.exe %.exe: %.c $(CC) -o $(0) $(1) test-%.exe: test/%.c $(CC) -o $(0) $(1)
Für Yabu haben beide Regeln die gleiche Priorität, selbst wenn die zweite "spezifischer" aussieht als die erste. Es hilft auch nicht, daß in diesem Falle beide Skripte identisch sind.
Details zur Auswahl der Regeln siehe in .
L32: Widersprüchliche Regeltypen
Für ein Ziel existieren sowohl normale (":") als auch Alias-Regeln ('::'). Das ist nicht erlaubt; alle Regeln für ein Ziel müssen vom gleichen Typ sein.
L35: Widersprüchliche bedingte Zuweisungen
Für die genannte Variable gibt es in der aktuellen Konfiguration widersprüchliche Wertzuweisungen. Ein Beispiel:
!options eins zwei CFLAGS="" CFLAGS[eins] = -Ieins/include CFLAGS[zwei] = -Izwei/include all:: [eins+zwei] echo "$(CFLAGS)" <----- FEHLER
Diesen Fehler kann man in der Regel vermeiden, indem man die bedingten Zuweisungen mit "+=" an Stelle von "=" schreibt. In jeder Konfiguration darf es nämlich nur eine "="-Zuweisung, aber beliebig viele "+="-Zuweisungen geben.
L33: Keine Regel gefunden
Für das angegebene Ziel existiert im Buildfile keine passende Regel. Das bedeutet entweder, daß es gar keine Regel für das Ziel gibt, oder daß die vorhandenen Regeln nicht zur aktuellen Konfiguration passen. Ein einfaches Beispiel für den zweiten Fall:
all:: [xzy] target1 target1: [-xyz] ...
Hier würde Yabu versuchen, "target1" in der Konfiguration [xyz] zu erreichen. Die dazu passende Regel verlangt aber die Konfiguration [-xyz] und wird deshalb – ohne Fehlermeldung – verworfen. Sofern das Buildfile keine weitere passende Regel enthält, tritt der Fehler L33 auf.
Einen Sonderfall bilden Ziele, die beim Aufruf von Yabu als Argument angegeben werden. Für ein solches Ziel verlangt Yabu immer die Existenz einer passenden Regel. Dahinter steht die Überlegung, daß der Benutzer
yabu source.c
nur eingibt, wenn Yabu damit etwas sinnvolles anfangen kann, mit anderen Worten, wenn es eine Regel für "source.c" gibt. Gibt es keine Regel, dann meldet Yabu den Fehler L33, selbst wenn die Datei "source.c" bereits existiert.
L40: Zu viele verschachtelte Include-Dateien
.include
-Anweisungen können bis zu einer Tiefe von 10 verschachtelt werden.
Wird diese Grenze überschritten, dann bricht Yabu mit diesem Fehler ab.
Eine mögliche Ursache ist, daß .include
-Anweisungen in verschiedenen
Dateien unbeabsichtigt eine Schleife bilden, zum Beispiel:
#Buildfile.1 include Buildfile.2
und
#Buildfile.2 include Buildfile.1