Co oznaczają symbole makefile $ @ i $ <?

416
CC=g++
CFLAGS=-c -Wall
LDFLAGS=
SOURCES=main.cpp hello.cpp factorial.cpp
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=hello

all: $(SOURCES) $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS)
    $(CC) $(LDFLAGS) $(OBJECTS) -o $@

.cpp.o:
    $(CC) $(CFLAGS) $< -o $@

Co robią $@i $<robią dokładnie?

Mohit Deshpande
źródło
5
Powyższy link jest zepsuty, tutaj jest inny: gnu.org/software/make/manual/html_node/Automatic-Variables.html
asciz 26.01.2013
1
Cześć, co oznacza to „.cpp.o:” jako cel? (przed ostatnią linią?).
pseudonym_127
3
„.Cpp.o:” oznacza budowanie „.o” (pliki obiektowe) z „.cpp” (pliki źródłowe)
jaguzu
1
Uważam, że należy zauważyć, że pod poniższym linkiem znajduje się samouczek make, z którego, jak sądzę, Mohit uzyskał plik makefile w swoim poście. mrbook.org/blog/tutorials/make
DeepDeadpool
Microsoft nazywa to Makra nazw plików (dla NMAKE), które są wyraźniejsze niż zmienne automatyczne (dla MAKE). Warto zobaczyć obie strony w celach edukacyjnych.
Ivanzinho

Odpowiedzi:

502

$@to nazwa generowanego pliku i $<pierwszy warunek wstępny (zwykle plik źródłowy). Listę wszystkich tych zmiennych specjalnych można znaleźć w instrukcji GNU Make .

Rozważmy na przykład następującą deklarację:

all: library.cpp main.cpp

W tym przypadku:

  • $@ ocenia na all
  • $< ocenia na library.cpp
  • $^ ocenia na library.cpp main.cpp
bdonlan
źródło
16
Warto zauważyć, że $@niekoniecznie musi to być plik, może to być również nazwa .PHONYcelu.
Efemeryda
Czy mogę dodać do opcji wiersza polecenia to: $@saby wygenerować dane wyjściowe zestawu, takie jak name.os?
huseyin tugrul buyukisik
4
Uwaga, gdy pierwsza zależność jest zmienną reprezentującą listę, $ <jest obliczane po rozwinięciu. Tak więc, gdy LISTA = lib1.cpp lib2.cpp i wszystkie: $ {LIST} main.cpp, $ <jest obliczane tylko na lib1.cpp. Kilka lat temu spędziłem trochę czasu zastanawiając się, co dzieje się w wyniku spowodowanym tym zachowaniem.
Chan Kim,
Ogólnie $ @ odnosi się do nazwy celu, która znajduje się po lewej stronie:
Deepak Kiran
78

$@I $<nazywane są zmienne automatyczne . Zmienna $@reprezentuje nazwę pliku, który został utworzony (tj. Cel) i $<stanowi pierwszy warunek wstępny wymagany do utworzenia pliku wyjściowego.
Na przykład:

hello.o: hello.c hello.h
         gcc -c $< -o $@

Oto hello.oplik wyjściowy. To właśnie się $@rozwija. Pierwsza zależność to hello.c. To właśnie się $<rozwija.

-cFlaga generuje .oplik; zobacz man gccbardziej szczegółowe wyjaśnienie. -oOkreśla plik wyjściowy do tworzenia.

Aby uzyskać więcej informacji, możesz przeczytać ten artykuł o Makefiles Linux .

Możesz także sprawdzić instrukcje GNU make . Ułatwi to tworzenie plików Makefiles i ich debugowanie.

Jeśli uruchomisz to polecenie, wyświetli on bazę danych makefile:

make -p 
zręczny
źródło
1
Twoja odpowiedź brzmi jak $<rozwinięta do hello.c hello.h(oba). Proszę o wyjaśnienie.
Dr Beco,
Tak, obejmie zarówno hello.c, jak i hello.h
zręczny
19
$<to tylko pierwszy element. Aby uwzględnić wszystkie, użyj $^.
Dr Beco,
1
Dr Beco ma rację. Autor powinien zmodyfikować swoją odpowiedź.
PT Huynh,
67

Z zarządzania projektami za pomocą GNU Make, wydanie trzecie, str. 16 (na licencji GNU Free Documentation License ):

Zmienne automatyczne są ustawiane makepo dopasowaniu reguły. Zapewniają dostęp do elementów z listy docelowej i wymagań wstępnych, więc nie trzeba jawnie określać żadnych nazw plików. Są bardzo przydatne do unikania powielania kodu, ale są krytyczne przy definiowaniu bardziej ogólnych reguł wzorców.

Istnieje siedem „podstawowych” zmiennych automatycznych:

  • $@: Nazwa pliku reprezentująca cel.

  • $%: Element nazwy pliku specyfikacji członka archiwum.

  • $<: Nazwa pierwszego warunku wstępnego.

  • $?: Nazwy wszystkich wymagań wstępnych, które są nowsze niż cel, oddzielone spacjami.

  • $^: Nazwy plików wszystkich wymagań wstępnych, oddzielone spacjami. Ta lista ma zduplikowane nazwy plików, ponieważ w przypadku większości zastosowań, takich jak kompilacja, kopiowanie itp., Duplikaty nie są potrzebne.

  • $+: Podobnie jak $^, są to nazwy wszystkich wymagań wstępnych oddzielonych spacjami, oprócz tych, które $+obejmują duplikaty. Ta zmienna została stworzona dla określonych sytuacji, takich jak argumenty do linkerów, w których zduplikowane wartości mają znaczenie.

  • $*: Rdzeń docelowej nazwy pliku. Trzon to zazwyczaj nazwa pliku bez przyrostka. Jego stosowanie poza regułami wzorcowymi jest odradzane.

Ponadto każda z powyższych zmiennych ma dwa warianty kompatybilności z innymi markami. Jeden wariant zwraca tylko część wartości katalogu. Jest to sygnalizowane przez dodanie „D” do symbolu $(@D), $(<D)itp drugiej powrotów wariantów tylko część pliku wartości. Jest to sygnalizowane przez nadanie „F” do symbolu, $(@F), $(<F), itd. Należy pamiętać, że nazwy te warianty są więcej niż jeden znak długo i tak musi być ujęty w nawiasy. GNU make zapewnia bardziej czytelną alternatywę dla funkcji dir i notdir.

alex
źródło
37

$@I $<specjalne makra.

Gdzie:

$@ to nazwa pliku celu.

$< to nazwa pierwszej zależności.

Eric
źródło
19

Makefile buduje hellowykonywalny jeśli ktoś z main.cpp, hello.cpp, factorial.cppzmianie. Najmniejszy możliwy Makefile, aby osiągnąć tę specyfikację, mógł być:

hello: main.cpp hello.cpp factorial.cpp
    g++ -o hello main.cpp hello.cpp factorial.cpp
  • pro: bardzo łatwy do odczytania
  • con: koszmar konserwacji, powielanie zależności C ++
  • con: problem wydajności, rekompilujemy cały C ++, nawet jeśli tylko jeden został zmieniony

Aby poprawić powyższe, kompilujemy tylko te pliki C ++, które były edytowane. Następnie po prostu łączymy powstałe pliki obiektów razem.

OBJECTS=main.o hello.o factorial.o

hello: $(OBJECTS)
    g++ -o hello $(OBJECTS)

main.o: main.cpp
    g++ -c main.cpp

hello.o: hello.cpp
    g++ -c hello.cpp

factorial.o: factorial.cpp
    g++ -c factorial.cpp
  • pro: naprawia problem z wydajnością
  • con: nowy koszmar konserwacji, potencjalna literówka w regułach plików obiektowych

Aby to poprawić, możemy zastąpić wszystkie reguły plików obiektowych jedną .cpp.oregułą:

OBJECTS=main.o hello.o factorial.o

hello: $(OBJECTS)
    g++ -o hello $(OBJECTS)

.cpp.o:
    g++ -c $< -o $@
  • pro: powrót do posiadania krótkiego pliku makefile, nieco łatwego do odczytania

Tutaj .cpp.ozasada określa, jak budować anyfile.ood anyfile.cpp.

  • $< pasuje do pierwszej zależności, w tym przypadku anyfile.cpp
  • $@pasuje do cel, w tym przypadku anyfile.o.

Inne zmiany obecne w Makefile to:

  • Ułatwienie zmiany kompilatorów z g ++ na dowolny kompilator C ++.
  • Ułatwienie zmiany opcji kompilatora.
  • Ułatwienie zmiany opcji linkera.
  • Ułatwienie zmiany plików źródłowych i danych wyjściowych C ++.
  • Dodano domyślną regułę „wszystko”, która działa jak szybkie sprawdzenie, aby upewnić się, że wszystkie pliki źródłowe są obecne przed próbą zbudowania aplikacji.
Stephen Quan
źródło
1

na przykład, jeśli chcesz kompilować źródła, ale masz obiekty w innym katalogu:

Musisz zrobić :

gcc -c -o <obj/1.o> <srcs/1.c> <obj/2.o> <srcs/2.c> ...

ale w przypadku większości makr wynikiem będą wszystkie obiekty, a po nich wszystkie źródła, takie jak:

gcc -c -o <all OBJ path> <all SRC path>

więc to nic nie skompiluje ^^ i nie będzie można umieścić plików obiektów w innym katalogu :(

rozwiązaniem jest użycie tych specjalnych makr

$@ $<

wygeneruje to plik .o (obj / plik.o) dla każdego pliku .c w SRC (src / file.c)

$(OBJ):$(SRC)
   gcc -c -o $@ $< $(HEADERS) $(FLAGS)

to znaczy :

    $@ = $(OBJ)
    $< = $(SRC)

ale linie po liniach ZAMIAST wszystkich linii OBJ, po których następują wszystkie linie SRC

Aominé
źródło
Zastanawiam się, co robisz z całym tym czasem, który oszczędzasz, wpisując „u” zamiast „ty”?
Ivanzinho