Seltsames Verhalten bei der Automatisierung mit "make -f"

7

Ich habe eine Reihe von Verzeichnissen, von denen einige Makefiles enthalten, und einige der Makefiles haben cleanZiele. Im übergeordneten Verzeichnis habe ich ein einfaches Skript:

#!/bin/bash

for f in *; do
        if [[ -d $f && -f $f/makefile ]]; then
                echo "Making clean in $f..."
                make -f $f/makefile clean
        fi
done

Dies macht eine seltsame Sache, wenn es ein Verzeichnis mit einem Makefile ohne ein (definiertes) "sauberes" Ziel trifft. Zum Beispiel gegeben zwei Verzeichnisse, oneund two, enthaltend;

eins / makefile

clean:
        -rm *.x

zwei / makefile

clean:

Im zweiten Fall ist "clean" ohne Anweisungen vorhanden. Wenn Sie also "make clean" twoausführen, erhalten Sie:

make: Nothing to be done for `clean'.

Versus wenn es kein "sauberes" gäbe:

make: *** No rule to make target `clean'.  Stop.

Für das Problem, das ich beschreiben werde, ist das Ergebnis jedoch dasselbe, unabhängig davon, ob das Ziel vorhanden, aber undefiniert oder einfach nicht vorhanden ist. Wird clean.shaus dem übergeordneten Verzeichnis ausgeführt.

Making clean in one...
rm *.x
rm: cannot remove `*.x': No such file or directory
make: [clean] Error 1 (ignored)

Also onebrauchte ich keine Reinigung. Keine große Sache, das ist wie erwartet. Aber dann:

Making clean in two...
cat clean.sh >clean 
chmod a+x clean

Warum die cat clean.sh>cleanetc.? Hinweis: Ich habe genau wie oben gezeigt ein minimales Beispiel erstellt - es gibt keine anderen Dateien oder Verzeichnisse (nur clean.sh, die Verzeichnisse eins und zwei und die sehr minimalen Makefiles). Aber nachdem clean.sh ausgeführt wurde, makehat es kopiert clean.sh > cleanund ausführbar gemacht. Wenn ich dann wieder clean.sh laufen lasse:

Making clean in one...
make: `clean' is up to date.
Making clean in two...
make: `clean' is up to date.
Press any key to continue...

Noch seltsamer, weil jetzt überhaupt nicht die angegebenen Makefiles verwendet werden - es wird ein "aktuelles" Mystery-Ziel verwendet.

Ich habe ein möglicherweise verwandtes Phänomen festgestellt: Wenn Sie eine der Testklauseln in clean.sh wie folgt entfernen:

#       if [[ -d $f && -f $f/makefile ]]; then
        if [[ -d $f ]]; then

Und erstellen Sie ein Verzeichnis threeohne Makefile. Ein Teil der Ausgabe umfasst:

Making clean in three...
make: three/makefile: No such file or directory
make: *** No rule to make target `three/makefile'.  Stop.

Dass es keine solche Datei oder kein solches Verzeichnis gibt, verstehe ich, aber warum sucht er makedann nach einem Ziel mit diesem Namen? Die Manpage scheint ziemlich einfach zu sein:

-f Datei, --file = Datei, --makefile = DATEI

  Use file as a makefile.
Goldlöckchen
quelle

Antworten:

9

Dieses Verhalten ist kein Fehler. Es ist eine Funktion. Eine Funktion und ein möglicher Benutzerfehler, um genau zu sein.

Die fragliche Funktion ist eine der impliziten Regeln von Make. In Ihrem Fall die implizite Regel zum "Erstellen" von *.shDateien. Der Benutzerfehler, Ihr Fehler, ändert das Arbeitsverzeichnis nicht, bevor das Makefile in den Unterverzeichnissen aufgerufen wird.


TL; DR: Um dies zu beheben, können Sie einen oder mehrere der folgenden Schritte ausführen:

  1. Korrigieren Sie das Shell-Skript, um das Arbeitsverzeichnis zu ändern:

    #!/bin/bash
    
    for f in *; do
            if [[ -d $f && -f $f/makefile ]]; then
                    echo "Making clean in $f..."
                    (cd $f; make clean)
            fi
    done
    
  2. Machen Sie die leeren Regeln explizit:

    clean: ;
    
  3. Machen Sie die cleanZiele falsch:

    .PHONY: clean
    

Ausführliche Erklärung:

Make hat eine Reihe impliziter Regeln. Dies ermöglicht es, make für einfache Projekte aufzurufen, ohne ein Makefile zu schreiben.

Versuchen Sie dies für eine Demonstration:

  1. Erstellen Sie ein leeres Verzeichnis und wechseln Sie in das Verzeichnis
  2. Erstellen Sie eine Datei mit dem Namen clean.sh.
  3. Lauf make clean

Ausgabe:

$ make clean
cat clean.sh >clean 
chmod a+x clean

BAM! Das ist die Kraft impliziter Regeln. Weitere Informationen finden Sie im make-Handbuch zu impliziten Regeln .


Ich werde versuchen, die verbleibende offene Frage zu beantworten:

Warum wird die implizite Regel für das erste Makefile nicht aufgerufen? Weil Sie die implizite Regel mit Ihrer expliziten cleanRegel überschrieben haben .

Warum cleanüberschreibt die Regel im zweiten Makefile die implizite Regel nicht? Weil es kein Rezept hatte. Regeln ohne Rezept überschreiben die impliziten Regeln nicht, sondern fügen lediglich die Voraussetzungen hinzu. Weitere Informationen finden Sie im make-Handbuch zu mehreren Regeln . Siehe auch das Make-Handbuch zu Regeln mit explizit leeren Rezepten .

Warum ist es ein Fehler, das Arbeitsverzeichnis nicht zu ändern, bevor ein Makefile in einem Unterverzeichnis aufgerufen wird? Weil make das Arbeitsverzeichnis nicht ändert. Make funktioniert im geerbten Arbeitsverzeichnis. Nun, technisch gesehen ist dies nicht unbedingt ein Fehler, aber meistens ist es ein Fehler. Möchten Sie, dass die Makefiles in den Unterverzeichnissen in den Unterverzeichnissen funktionieren? Oder möchten Sie, dass sie im übergeordneten Verzeichnis arbeiten?

Warum ignoriert make die explizite cleanRegel aus dem ersten Makefile beim zweiten Aufruf von clean.sh? Denn jetzt existiert die Zieldatei cleanbereits. Da die Regel cleankeine Voraussetzungen hat, muss das Ziel nicht neu erstellt werden. Weitere Informationen zu diesem Problem finden Sie im make-Handbuch zu falschen Zielen .

Warum wird three/makefilebeim dritten Aufruf nach dem Ziel gesucht ? Weil make immer versucht, die Makefiles neu zu erstellen, bevor etwas anderes getan wird. Dies gilt insbesondere dann, wenn das Makefile explizit mit angefordert wird, -faber nicht vorhanden ist. Weitere Informationen finden Sie im make-Handbuch zum erneuten Erstellen von Makefiles .

Lesmana
quelle
Ich denke, diese Antwort würde verbessert, indem hinzugefügt wird, dass für jede jeweilige Version von GNU make (ja, andere Varianten existieren und werden verwendet) make -npf /dev/null(oder gmakeje nach System) verwendet werden kann, um die Datenbank impliziter Regeln für genau diese Version zu sichern. Hat mir oft geholfen.
0xC0000022L