Wypisz cele / cele w marce GNU, które zawierają zmienne w swojej definicji


100

Mam dość duży plik makefile, który tworzy wiele celów w locie, obliczając nazwy ze zmiennych. (np. foo $ (VAR): $ (PREREQS)). Czy istnieje sposób, w jaki można przekonać gnu make do wyplucia listy celów po rozwinięciu tych zmiennych?

Chciałbym móc znaleźć cele dla aribitrary makefile. Próbuję napisać funkcję uzupełniającą dla mojej powłoki.

Odpowiedzi:


79

Czy możesz przeanalizować dane wyjściowe z make -pn(tj. make --print-data-base --dry-run)? Wyświetla wszystkie zmienne, reguły, reguły niejawne i polecenia, które będą uruchamiane ze szczegółami.


Ta odpowiedź wygląda o wiele ładniej niż druga.
Matt Joiner,

Zgadzam się, że to jest o wiele przyjemniejsze. Wciąż więcej pracy, niż się spodziewałem, ale zaznaczę to jako odpowiedź, ponieważ nic innego nie wydaje się rozsądne, a marka GNU nie ma wygodnej flagi dla tego, czego chcę.
BitShifter,

6
Należy pamiętać, że wynik może zależeć od celu, który określisz, jeśli plik Makefile generuje cele dynamicznie. Wynik może również zależeć od wartości zmiennych środowiskowych lub tworzyć zmienne określone za pomocą polecenia make.
reinierpost

6
Podoba mi się zwięzłość. +1 Dodano mój własny spin (opisany w odpowiedzi poniżej):make -pn | sed -rn '/^[^# \t\.%].*:[^=]?/p'
Jamie

Zaproponuj make -pnr. -rUsuwa ukryte zasady.
sjbx

114
make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'     

Zaczerpnięte z zakończenia make arg, które działa jak urok.


2
próbowałem stworzyć dla tego alias, umieszczając go w podwójnych cudzysłowach, ale to po prostu powoduje, że awk ma błąd składniowy w pierwszym przecinku ... czy masz jakieś przemyślenia, jak poprawnie uciec z tego dla aliasu powłoki (bash)?
drfrogsplat

3
Myślę, że ma to coś wspólnego z tym, gdzie jest zdefiniowane. Myślę, że jeśli jest zdefiniowany jako alias, jego katalog roboczy staje się katalogiem, w którym jest zdefiniowany, i najwyraźniej część awk nie lubi kropki w ukrytych katalogach. Zdefiniowanie funkcji działa.
Ibrahim

Po prostu umieściłem go w skrypcie powłoki ~/binzamiast używać aliasu. Działa świetnie; dzięki!
mpontillo

3
Alias, alias mkl="make -qp | awk -F':' '/^[a-zA-Z0-9][^\$#\/\t=]*:([^=]|\$)/ {split(\$1,A,/ /);for(i in A)print A[i]}' | sort"który pozwoli Ci zaoszczędzić 2 minuty cytowania gier :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
| sort -uwygląda bardziej przydatnie :)
sherpya

14

Nie jestem pewien, czy to tylko kwestia tworzenia GNU, ale działa to dobrze:

make help


Jest to bardzo przydatne, o wiele łatwiejsze do zapamiętania niż niestandardowy alias dla jednej z pozostałych metod.
Ibrahim

8
Niezupełnie działa na GNU Make 3.81, prawdopodobnie cel w twoich plikach makefiles: "make: *** Brak reguły
określającej cel jako`

8
To po prostu wywołuje cel "help" w Makefile. Jeśli istnieje, wydrukuje coś (nie pełną listę celów), jeśli nie istnieje (typowa sytuacja), wyskoczy.
pfalcon

2
Dobra wiadomość jest taka, że ​​cmake wydaje się generować ładny, łatwy do odczytania cel „pomocy”.
Stéphane

To jest niesamowite! :) Idź cmake
Jeef

10

Kilku respondentów zasugerowało użycie make -pn, które wypisze bazę danych reguł, ale niczego nie wykona - mniej więcej. Problem z tym podejściem polega na tym, -nże nadal wywołuje wszystkie rekurencyjne marki i nadal wykonuje o wiele więcej pracy niż to konieczne, ponieważ wypisuje każde polecenie, które wywołałoby w normalnej kompilacji. Bardziej wydajnym rozwiązaniem byłoby utworzenie trywialnego pliku makefile dummy.mk o następującej zawartości:

__all_targets__: ; #no-op

Teraz wywołaj make as make -p -f Makefile -f dummy.mk __all_targets__. W przypadku każdej znaczącej budowy różnica w ilości produkcji generowanej przez markę jest znacząca. Na przykład:

$ gmake -pn | wc
 138985 2632330 69612711
$ gmake -f Makefile -f /tmp/dummy.mk -pn __all_targets__ | wc
  21673   93437  878985

Czas wykonania również był znacznie lepszy - 2,063 s dla pierwszej wersji, 0,059 s dla drugiej.


1
Zgrabny pomysł, doceni to każdy, kto pracuje z dużymi systemami kompilacji. Statystyki dla Androida Gingerbread tree (nie używa rekurencyjnej marki, więc oszczędności nie są ogromne, tylko dobre): " time make -pn | wc -l": 1338738 real 0m47.754s." time make -f Makefile -f dummy.mk -pn __all_targets__ | wc -l": 938621 real 0m40.219s.
pfalcon

Słuszna uwaga. Strona makepodręcznika dla wydaje się sugerować coś podobnego, ale przyjaznego, a mianowiciemake -p -f/dev/null
Davide

@Davide to nie zadziała: określenie -f /dev/nullzapobiegnie makeparsowaniu zwykłych plików makefile, więc otrzymasz wydruk tylko reguł wbudowanych. Dlatego moje rozwiązanie określa -f Makefile -f dummy.mk. Ale to też nie wystarczy, ponieważ polecenie -n, makerozpocznie wykonywanie reguł z pliku makefile. Niestety nie znam żadnych modyfikacji, które mogłyby uprościć rozwiązanie, tak jak zostało to zaprezentowane pierwotnie.
Eric Melski

Oczywiście, że masz rację. Jednak strona podręcznika make zawiera błąd, ponieważ stwierdza: "Aby wydrukować bazę danych bez próby przerobienia jakichkolwiek plików, użyj make -p -f / dev / null"
Davide

Nieznaczna korekta mojego poprzedniego komentarza: powinno brzmieć "... bo bez -n ..."
Eric Melski


7

Edycja: FYI repozytorium git do zakończenia debian bash w innej odpowiedzi zawiera teraz ulepszoną wersję tego skryptu dostosowaną do przypadków użycia bash.

#!/bin/bash

SCRIPT='
  /^# Make data base/,/^# Files/d             # skip until files section
  /^# Not a target/,+1          d             # following target isnt
  /^\.PHONY:/                   d             # special target
  /^\.SUFFIXES:/                d             # special target
  /^\.DEFAULT:/                 d             # special target
  /^\.PRECIOUS:/                d             # special target
  /^\.INTERMEDIATE:/            d             # special target
  /^\.SECONDARY:/               d             # special target
  /^\.SECONDEXPANSION/          d             # special target
  /^\.DELETE_ON_ERROR:/         d             # special target
  /^\.IGNORE:/                  d             # special target
  /^\.LOW_RESOLUTION_TIME:/     d             # special target
  /^\.SILENT:/                  d             # special target
  /^\.EXPORT_ALL_VARIABLES:/    d             # special target
  /^\.NOTPARALLEL:/             d             # special target
  /^\.ONESHELL:/                d             # special target
  /^\.POSIX:/                   d             # special target
  /^\.NOEXPORT:/                d             # special target
  /^\.MAKE:/                    d             # special target

# The stuff above here describes lines that are not
#  explicit targets or not targets other than special ones
# The stuff below here decides whether an explicit target
#  should be output.

  /^[^#\t:=%]+:([^=]|$)/ {                    # found target block
    h                                         # hold target
    d                                         # delete line
  }
  /^# File is an intermediate prerequisite/ { # nope
    s/^.*$//;x                                # unhold target
    d                                         # delete line
  }
  /^([^#]|$)/ {                               # end of target block
    s/^.*$//;x                                # unhold target
    s/:.*$//p                                 # write current target
    d                                         # hide any bugs
  }
'

make -npq .DEFAULT 2>/dev/null | sed -n -r "$SCRIPT" \
  | sort | uniq

Jest to znacznie bardziej kompletny skrypt niż skrypt uzupełniający debian bash, ponieważ zapewnia wyniki dla wygenerowanych reguł, o które zadaje się pytanie, a najczęściej głosowana odpowiedź (skrypt uzupełniający debian bash na serwerze debian git) nie jest wystarczająca.

To nie jest oryginalny skrypt, z którym się łączyłem, ale jest znacznie prostszy i odrobinę szybszy.


BTW, mods, jeśli ta odpowiedź nie jest w pełni zgodna ze wszystkimi zasadami z powodu niuansów jej tekstu, przed usunięciem prosimy o komentarz. Dokonam wszelkich zgodnych z prawem korekt niezbędnych, aby na to pytanie rzeczywiście udzielona była pełna i ważna odpowiedź.
Codeshot

W ramach szybkiego testu złapałem kod źródłowy gcc i uruchomiłem ./configure && time ~ / makecomp.sh | wc -l, gdzie ~ / makecomp.sh jest moją odpowiedzią. ... konfiguruj wyjście ... config.status: tworzenie Makefile 3312 real 0m0.401s użytkownik 0m0.412s sys 0m0.028s
codehot

Jako kolejny test użyłem pliku makefile demo pod adresem codehot.blogspot.co.uk/2012/08/…, który generuje pseudo-reguły do ​​budowania i sprawdzania przejść testów jednostkowych. Ten przykład wykorzystuje więcej funkcji make niż typowy wieloplatformowy projekt autoconf, więc skrypt w tej odpowiedzi zwraca 14 rzeczywistych celów, podczas gdy zakończenie bash dla make na ubuntu zwraca tylko dwie przydatne reguły, a następnie 5 plików źródłowych.
codehot

4

To jest kod aliasu oparty na rozwiązaniu Todda Hodesa

alias mtargets='make -qp | awk -F":" "/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split(\$1,A,/ /);for(i in A)print A[i]}"'

3

To da ładny wynik:

make -pn | perl -F: -ane 'print "$F[0]\n" if /^\w+\s*:/' | sort


2

Chyba trochę się spóźniłem na to przyjęcie, ale jeśli szukasz konkretnego polecenia, możesz spróbować

make -qp | grep -v '^# ' | grep -v '^[[:space:]]' | grep --only-matching '^.*:'

To w większości działa, chociaż nadal może zawierać pewne elementy niebędące celem, takie jak vpathdyrektywa. Jeśli nie polegasz na makewbudowanych regułach, możesz użyć make -qpRjako pierwszego polecenia w potoku.


Wypróbowałem to z freetz (.org). Wyświetla wiele dołączonych plików makefile, ale nie zawiera listy wszystkich celów.
Robert Siemer

1

Rozwiązanie Ruby:

`make -qp`.split("\n").select{|l| l =~ /^\w/ && l !~ /=/}.map{|l| l.sub(/:.*/,'')}

1

Pracuję nad powłoką Solaris 10 i Turbo C. Podane rozwiązanie nie działa dla mojego projektu makefile. nawet po zmianie powyższego wiersza poleceń na składnię tcsh. Jednak dowiedziałem się, że możesz uzyskać łatwe rozwiązanie za pomocą

remake --tasks | grep -v "clean some static output or just grep tabs on start of line" | awk ´{print $1}´

wersja remake:

remake --version

jest

GNU Make 3.82+dbg0.8
Built for sparc-sun-sparc2-9

i kilka innych nieistotnych danych o wersji


0

Szukałem tego samego pytania i wymyśliłem ten spin:

make -pn | sed -rn '/^[^# \t\.%].*:/p'

To usuwa wszystkie linie komentarzy, reguły wzorców (linie zaczynające się od tabulatorów), wszystkie elementy wewnętrzne (przykłady .c.oi %.o: %.cwzorce).


nie przeczytał komentarza z zaakceptowanej odpowiedzi: +1 dla odpowiedzi Jacka ... gist.github.com/777954 - pvandenberk 13 stycznia o 14:47
Jamie

0

Znalazłem to rozwiązanie w innym wątku:

sh -c "cat Makefile | egrep \"^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$\""

Możesz również dodać go do Makefile:

list:
    sh -c "cat Makefile | egrep \"^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$\\""

I wykonaj make list.


-1

Jasne, ale kiedy chcesz, żeby je wypluła?

Aby zgłosić nazwę celu po uruchomieniu reguły, umieść wiersz w regule:

foo$(VAR): $(PREREQS)
    @echo now making the foo target: $@
    do_other_stuff...

Aby wypluć je wszystkie na raz, możesz stworzyć osobny cel PHONY:

.PHONY: show_vars
show_vars:
    @echo foo$(VAR)
    @echo bar$(PARAM) blah$(FLAG)
    # and so on

Można to uczynić warunkiem wstępnym domyślnego celu:

all: show_vars
    ...

EDYTOWAĆ:
Chcesz sposobu, aby pokazać wszystkie możliwe cele dowolnego pliku makefile, co, jak przypuszczam, oznacza nieinwazyjne. Dobrze...

Aby to zrobić dokładnie i móc poradzić sobie z wyrafinowanymi plikami makefile, np. Zawierającymi reguły skonstruowane przez evalinstrukcje, musiałbyś napisać coś zbliżonego do emulatora Make. Niepraktyczny.

Aby zobaczyć cele prostych reguł, możesz napisać plik makefile, który działałby jak skaner makefile, działający na dowolnym pliku makefile:

  1. Pobierz wszystkie nazwy celów z pliku makefile za pomocą seda.
  2. `include` makefile, aby użyć go do rozwinięcia zmiennych.
  3. Użyj `show_%:; echo $$ * `wypisuje wszystkie cele

To byłaby imponująca praca. Czy na pewno ten cel jest wart wysiłku?


Nie do końca to, czego chcę. Chciałbym móc wypluć listę celów dla dowolnego pliku makefile, który mogę mieć. Próbuję napisać funkcję uzupełniania argumentów dla mojej powłoki i nie mogę polegać na plikach makefile, które mają jakąkolwiek sensowną pomoc.
BitShifter

-4
grep ^[a-z].*\:$ Makefile | sed s,:,,g

1
-1: To podejście daje fałszywe alarmy nawet dla prostych przypisań zmiennych ( :=).
thiton

Powinieneś był to najpierw przetestować. Dobrze, że spróbujesz.
Konrad Viltersten
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.