Pytanie Makefile, zależności nagłówka


Powiedzmy, że mam plik Makefile z regułą

%.o: %.c
 gcc -Wall -Iinclude ...

Chcę, aby * .o był przebudowywany, gdy tylko zmieni się plik nagłówka. Zamiast opracowywać listę zależności, zawsze gdy jakiś plik nagłówkowy znajduje się w /include zmiany, wtedy wszystkie obiekty w katalogu muszą zostać przebudowane.

Nie potrafię wymyślić fajnego sposobu na zmianę reguły, by to pomieścić, jestem otwarty na sugestie. Punkty premiowe, jeśli lista nagłówków nie musi być zakodowana na stałe


76
2018-03-07 00:09


pochodzenie


Po napisaniu odpowiedzi poniżej przejrzałem listę i znalazłem: stackoverflow.com/questions/297514/... który wydaje się być duplikatem. Odpowiedź Chrisa Dodda jest odpowiednikiem mojego, chociaż używa innej konwencji nazewnictwa. - dmckee


Odpowiedzi:


Jeśli używasz kompilatora GNU, kompilator może utworzyć listę zależności dla ciebie. Fragment pliku Makefile:

depend: .depend

.depend: $(SRCS)
        rm -f ./.depend
        $(CC) $(CFLAGS) -MM $^ -MF  ./.depend;

include .depend

lub

depend: .depend

.depend: $(SRCS)
        rm -f ./.depend
        $(CC) $(CFLAGS) -MM $^ > ./.depend;

include .depend

gdzie SRCS jest zmienną wskazującą na całą twoją listę plików źródłowych.

Istnieje również narzędzie makedepend, ale nigdy nie lubiłem tego tak bardzo jak gcc -MM


106
2018-05-09 16:04



Podoba mi się ta sztuczka, ale jak mogę ją zdobyć depend uruchomić tylko po zmianie plików źródłowych? Wydaje się działać za każdym razem, niezależnie od ... - chase
@chase: Cóż, błędnie uzależniłem się od plików obiektowych, kiedy to oczywiście powinno znajdować się na źródłach, a kolejność zależności również była zła dla obu celów. To właśnie dostaję do pisania z pamięci. Spróbuj teraz. - dmckee
Czy jest sposobem dodania przed każdym plikiem jakiegoś prefiksu, aby pokazać, że znajduje się on w innym katalogu np build/file.o ? - RiaD
Zmieniłem SRCS na OBJECTS, gdzie OBJECTS są listą moich plików * .o. To, co wydawało się zapobiegać, zależy od uruchamiania za każdym razem, a także przechwyconych zmian tylko w plikach nagłówkowych. Wydaje się to sprzeczne z poprzednimi komentarzami. Czy coś mi brakuje? - BigBrownBear00
lepszą odpowiedź technicznie, ale dłużej i mniej kompleksowo ... - m-ric


Większość odpowiedzi jest zaskakująco skomplikowana lub błędna. Jednak proste i rzetelne przykłady zostały opublikowane w innym miejscu [Przegląd kodu]. Wprawdzie opcje oferowane przez preprocesor gnu są nieco mylące. Jednak usunięcie wszystkich katalogów z miejsca docelowego kompilacji za pomocą -MM jest udokumentowane, a nie błąd [gpp]:

Domyślnie CPP przyjmuje nazwę głównego pliku wejściowego, usuwa wszystkie składniki katalogu i dowolny sufiks pliku, taki jak ".c", i dołącza   typowy sufiks obiektu.

(Nieco nowsze) -MMD opcja jest prawdopodobnie tym, czego potrzebujesz. Dla kompletności przykład pliku makefile, który obsługuje wiele katalogów src i buduje katalogi z pewnymi komentarzami. W przypadku prostej wersji bez katalogów kompilacji zobacz [Przegląd kodu].

CXX = clang++
CXX_FLAGS = -Wfatal-errors -Wall -Wextra -Wpedantic -Wconversion -Wshadow

# Final binary
BIN = mybin
# Put all auto generated stuff to this build dir.
BUILD_DIR = ./build

# List of all .cpp source files.
CPP = main.cpp $(wildcard dir1/*.cpp) $(wildcard dir2/*.cpp)

# All .o files go to build dir.
OBJ = $(CPP:%.cpp=$(BUILD_DIR)/%.o)
# Gcc/Clang will create these .d files containing dependencies.
DEP = $(OBJ:%.o=%.d)

# Default target named after the binary.
$(BIN) : $(BUILD_DIR)/$(BIN)

# Actual target of the binary - depends on all .o files.
$(BUILD_DIR)/$(BIN) : $(OBJ)
    # Create build directories - same structure as sources.
    mkdir -p $(@D)
    # Just link all the object files.
    $(CXX) $(CXX_FLAGS) $^ -o $@

# Include all .d files
-include $(DEP)

# Build target for every single object file.
# The potential dependency on header files is covered
# by calling `-include $(DEP)`.
$(BUILD_DIR)/%.o : %.cpp
    mkdir -p $(@D)
    # The -MMD flags additionaly creates a .d file with
    # the same name as the .o file.
    $(CXX) $(CXX_FLAGS) -MMD -c $< -o $@

.PHONY : clean
clean :
    # This should remove all generated files.
    -rm $(BUILD_DIR)/$(BIN) $(OBJ) $(DEP)

Ta metoda działa, ponieważ jeśli istnieje wiele linii zależności dla pojedynczego celu, zależności są po prostu łączone, np .:

a.o: a.h
a.o: a.c
    ./cmd

jest równa:

a.o: a.c a.h
    ./cmd

jak wspomniano w: Makefile wiele linii zależności dla pojedynczego celu? 


38
2018-03-23 16:27



Podoba mi się to rozwiązanie. Nie chcę wpisywać polecenia make depend. Przydatne !! - Robert
Wystąpił błąd ortograficzny w wartości zmiennej OBJ: CPP powinien przeczytać CPPS - ctrucza
To jest moja preferowana odpowiedź; +1 dla ciebie. Jest to jedyna strona na tej stronie, która ma sens i obejmuje (dla tego, co widzę) wszystkie sytuacje, w których konieczna jest rekompilacja (unikanie niepotrzebnej kompilacji, ale wystarczająca) - Joost
Dzięki @ctrucza, powinno być teraz naprawione. - Sophie
Po wyjęciu z pudełka nie udało mi się znaleźć nagłówków dla mnie, chociaż hpp i cpp są w tym samym reż. - villasv


Jak napisałem tutaj gcc może tworzyć zależności i kompilować w tym samym czasie:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<

Parametr "-MF" określa plik, w którym będą przechowywane zależności.

Myślnik na początku "-include" mówi, aby kontynuować, gdy plik .d nie istnieje (np. Przy pierwszej kompilacji).

Uwaga: wydaje się, że w gcc występuje błąd dotyczący opcji -o. Jeśli ustawisz nazwę pliku obiektu, na przykład obj / _file__c.o, wtedy wygenerujesz plik.d nadal będzie zawierać plik.o, nie obj / _file__c.o.


24
2018-03-07 00:15



Kiedy próbuję tego, powoduje to, że wszystkie moje pliki .o są tworzone jako puste pliki. Mam swoje obiekty w podfolderze kompilacji (więc $ OBJECTS zawiera build / main.o build / smbus.o build / etc ...) i to z pewnością tworzy pliki .d jak opisałeś pozorny błąd, ale to na pewno nie buduje wcale plików .o, ale robi to, jeśli usunę -MM i -MF. - bobpaul
Użycie -MT rozwiąże notatkę w ostatnich linijkach twojej odpowiedzi, która aktualizuje cel każdej listy zależności. - Godric Seer
Próbowałem tego w różnych kombinacjach i to nie działa ... - g24l
@bobpaul ponieważ man gcc mówi -MM implikuje -E, który "zatrzymuje się po wstępnym przetworzeniu". Potrzebujesz -MMD zamiast: stackoverflow.com/a/30142139/895245 - Ciro Santilli 新疆改造中心 六四事件 法轮功


Co powiesz na coś takiego:

includes = $(wildcard include/*.h)

%.o: %.c ${includes}
    gcc -Wall -Iinclude ...

Możesz również używać symboli wieloznacznych bezpośrednio, ale staram się znaleźć, że potrzebuję ich w więcej niż jednym miejscu.

Zauważ, że działa to dobrze tylko w przypadku małych projektów, ponieważ zakłada, że ​​każdy plik obiektowy zależy od każdego pliku nagłówkowego.


21
2018-04-01 00:05



dzięki, sprawiłem, że było to o wiele bardziej skomplikowane, niż było to konieczne - Mike
Działa to jednak problem polegający na tym, że każdy plik obiektowy jest rekompilowany, za każdym razem, gdy dokonywana jest niewielka zmiana, tzn. Jeśli masz 100 plików źródłowych / nagłówkowych, a wprowadzasz małą zmianę tylko na jedną, wszystkie 100 zrekompilowane. . - Nicholas Hamilton
Naprawdę powinieneś zaktualizować swoją odpowiedź, aby powiedzieć, że jest to bardzo nieefektywny sposób, ponieważ odbudowuje WSZYSTKIE pliki za każdym razem, gdy DOWOLNY plik nagłówkowy zostanie zmieniony. Pozostałe odpowiedzi są znacznie lepsze. - xaxxon
To bardzo złe rozwiązanie. Pewnie, że będzie działać na małym projekcie, ale dla każdego zespołu wielkości produkcji i budowy, doprowadzi to do strasznego czasu kompilacji i stanie się odpowiednikiem biegu make clean all każdego razu. - Julien Guertault


Powyższe rozwiązanie Martina działa świetnie, ale nie obsługuje plików .o znajdujących się w podkatalogach. Godric zwraca uwagę, że flaga -MT zajmuje się tym problemem, ale jednocześnie zapobiega poprawnemu zapisaniu pliku .o. Poniższe zadbają o oba te problemy:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $<
    $(CC) $(CFLAGS) -o $@ $<

4
2017-12-15 12:36





Wykona to dobrze, a nawet obsługuje określone poddziały:

    $(CC) $(CFLAGS) -MD -o $@ $<

przetestował go za pomocą gcc 4.8.3


3
2018-06-16 12:44





Wolę to rozwiązanie od zaakceptowanej odpowiedzi Michaela Williamsona, przechwytuje zmiany w źródłach i plikach inline, następnie źródłach i nagłówkach, a ostatecznie tylko źródłach. Zaletą jest to, że cała biblioteka nie jest rekompilowana, jeśli wprowadzono tylko kilka zmian. Nie ma wielkiego znaczenia dla projektu z kilkoma plikami, jeśli masz 10 lub 100 źródeł, zauważysz różnicę.

COMMAND= gcc -Wall -Iinclude ...

%.o: %.cpp %.inl
    $(COMMAND)

%.o: %.cpp %.hpp
    $(COMMAND)

%.o: %.cpp
    $(COMMAND)

0
2017-12-15 19:34



Działa to tylko wtedy, gdy nie masz żadnych plików nagłówkowych, które wymagałyby rekompilacji plików cpp innych niż odpowiedni plik implementacji. - matec


Poniższe działa dla mnie:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.cpp
    $(CXX) $(CFLAGS) -MMD -c -o $@ $<

0
2018-02-07 22:40





Oto dwuwymiarowy:

CPPFLAGS = -MMD
-include $(OBJS:.c=.d)

Działa to z domyślną receptą make tak długo, jak długo masz listę wszystkich plików obiektów OBJS.


0