Nie przestawaj make'ing, jeśli polecenie się nie powiedzie, ale sprawdź status wyjścia

22

Próbuję poinstruować GNU Make 3.81, aby nie zatrzymywał się, jeśli polecenie się nie powiedzie (więc poprzedzam to poleceniem -), ale chcę również sprawdzić status wyjścia dla następnego polecenia i wydrukować bardziej pouczający komunikat. Jednak mój Makefile poniżej nie działa:

$ cat Makefile 
all:
    -/bin/false
    ([ $$? -eq 0 ] && echo "success!") || echo "failure!"
$
$ make
/bin/false
make: [all] Error 1 (ignored)
([ $? -eq 0 ] && echo "success!") || echo "failure!"
success!

Dlaczego powyższy plik Makefile odzwierciedla „sukces!” zamiast „porażki!” ?

aktualizacja:

Poniżej i w odpowiedzi na zaakceptowaną odpowiedź poniżej opisano, jak należy ją napisać:

failure:                                                                                                                                                                                                                                      
    @-/bin/false && ([ $$? -eq 0 ] && echo "success!") || echo "failure!"                                                                                                                                                                 
success:                                                                                                                                                                                                                                      
    @-/bin/true && ([ $$? -eq 0 ] && echo "success!") || echo "failure!"     
Marcus Junius Brutus
źródło
2
Możesz zbadać .ONESHELL:dyrektywę.
Jonathan Leffler,
.ONESHELL uruchomi wszystkie bloki paragonów w jednej powłoce, co ma skutek: jeśli pierwsze polecenie się nie powiedzie, następne zostaną wykonane bez problemów. Aby temu zapobiec, .SHELLFLAGS = -ecnależy użyć. Ale w tym przypadku nie można użyć -więcej prefiksu (do osobistego dowodu odbioru), ponieważ marka napisze, że błąd jest ignorowany, ale nadal zawiedzie cały blok. Tak, || :jest jednym z rozwiązań ignorowania polecenia. Ale to nie jest wieloplatformowe (Windows nie ma || :lub || true)
Paul-AG

Odpowiedzi:

14

Każde polecenie aktualizacji w regule Makefile jest wykonywane w osobnej powłoce. Więc $? nie zawiera statusu wyjścia poprzedniej nieudanej komendy, zawiera dowolną wartość domyślną dla $? w nowej powłoce. Właśnie dlatego twoje [$? -eq 0] test zawsze się udaje.

Kyle Jones
źródło
10

Test nie jest potrzebny, $?ponieważ &&działa, jeśli $?wynosi zero i ||jest przeprowadzany w przypadku niezerowej wartości zwracanej.

I nie potrzebujesz minusa, ponieważ wartość zwracana do wykonania jest pobierana z ostatniego wywołania programu linii. To działa dobrze

niepowodzenie:

      @/bin/false && echo "success!" || echo "failure!" 

sukces:

      @/bin/true && echo "success!" || echo "failure!"

Przeciwnie dzieje się: jeśli chcesz napisać własną wiadomość i zepsuć proces tworzenia z niezerową wartością, musisz napisać coś takiego:

niepowodzenie:

      @/bin/false && echo "success!" || { echo "failure!"; exit 1; }
Andreas
źródło
8

Z GNU wykonaj dokumentację :

Kiedy błędy mają zostać zignorowane, z powodu flagi „-” lub „-i”, make traktuje powrót błędu tak jak sukces , z tym wyjątkiem, że wypisuje komunikat informujący o kodzie stanu, przez który powłoka została zamknięta, oraz mówi, że błąd został zignorowany.

Aby wykorzystać makestatus wyjścia w takim przypadku, wykonaj makeze skryptu:

#!/bin/bash
make
([ $? -eq 0 ] && echo "success!") || echo "failure!"

I niech twój Makefile zawiera:

all:
    /bin/false
Timothy Martin
źródło