Makefile, header dependencies

Powiedzmy, że mam makefile z regułą

%.o: %.c
 gcc -Wall -Iinclude ...
Chcę *.o do przebudowy za każdym razem, gdy zmieni się plik nagłówkowy. Zamiast tworzyć listę zależności, za każdym razem, gdy zmienia się jakikolwiek plik nagłówka w /include, wszystkie obiekty w katalogu muszą zostać przebudowane. Nie mogę wymyślić ładnego sposobu na zmianę zasady, aby to zaakceptować, jestem otwarty na sugestie. Punkty bonusowe, jeśli lista nagłówków nie musi być zakodowana na twardo
Author: Mike, 2010-03-07

10 answers

JeĹ "li uĺźywasz kompilatora GNU, kompilator moĹźe zgromadziÄ ‡ dla Ciebie listÄ ™ zaleĹźnoĹ" ci. Fragment 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 {[2] } jest zmienną wskazującą na całą listę plików źródłowych.

Jest też narzędzie makedepend, ale nigdy nie podobało mi się tak bardzo, jak gcc -MM

 118
Author: dmckee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-11 07:39:09

Większość odpowiedzi jest zaskakująco skomplikowana lub błędna. Jednak proste i solidne przykłady zostały opublikowane gdzie indziej [codereview ]. Co prawda opcje dostarczane przez preprocesor gnu są nieco mylące. Jednak usunięcie wszystkich katalogów z celu kompilacji za pomocą -MM jest udokumentowane, a nie błąd [gpp]:

Domyślnie CPP pobiera nazwę głównego pliku wejściowego, usuwa dowolny komponenty katalogów i dowolne przyrostki plików takie jak".c", oraz dodaje przyrostek obiektowy platformy.

(nieco nowsza) -MMD opcja jest prawdopodobnie tym, czego chcesz. Dla kompletności przykład pliku makefile, który obsługuje wiele src dirs i budować dirs z kilkoma komentarzami. Dla prostej wersji bez dirs kompilacji zobacz [codereview ].

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 jednego celu, zależności są po prostu połączone, np.:

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

Jest odpowiednik:

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

Jak wspomniano w: Makefile wiele linii zależności dla jednego celu?

 80
Author: Sophie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:09:54

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 do przechowywania zależności.

Myślnik na początku '- include ' mówi Make, aby kontynuował, gdy .plik d nie istnieje (np. przy pierwszej kompilacji).

Uwaga w gcc pojawił się błąd dotyczący opcji-o. Jeśli ustawisz nazwę pliku obiektu na obj / _ file _ _ c. O, to wygenerowany plik .d będzie nadal zawierać Plik .o, nie obj / _ file_ _ c. O.

 26
Author: Martin Fido,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:25:57

A może coś w stylu:

includes = $(wildcard include/*.h)

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

Możesz również użyć wildcards bezpośrednio, ale zazwyczaj uważam, że potrzebuję ich w więcej niż jednym miejscu.

Zauważ, że działa to dobrze tylko w małych projektach, ponieważ zakłada, że każdy plik obiektowy zależy od KAŻDEGO pliku nagłówkowego.

 23
Author: Michael Williamson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-26 10:28:03

Powyższe rozwiązanie Martina działa świetnie, ale nie radzi sobie .o pliki, które znajdują się w podkatalogach. Godric wskazuje, że flaga-MT zajmuje się tym problemem, ale jednocześnie zapobiega .o Plik od poprawnego napisania. Poniższe rozwiążą oba te problemy:

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

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $<
    $(CC) $(CFLAGS) -o $@ $<
 4
Author: michael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-01 00:05:58

To wykona zadanie po prostu dobrze, a nawet obsłuży określone subdiry:

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

Przetestowano go z gcc 4.8.3

 3
Author: g24l,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-15 12:36:29

Oto dwulinik:

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

Działa to z domyślną recepturą make, o ile masz listę wszystkich plików obiektowych w OBJS.

 3
Author: tbodt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-07 22:40:20

Wolę to rozwiązanie, niż zaakceptowaną odpowiedź Michaela Williamsona, łapie zmiany w sources+inline files, następnie sources+headers, i wreszcie tylko sources. Zaletą jest to, że cała biblioteka nie jest rekompilowana, jeśli tylko kilka zmian zostanie dokonanych. Nie jest to ogromne znaczenie dla projektu z kilkoma plikami, Bur 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)
 1
Author: Nicholas Hamilton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-16 12:44:42

Dla mnie działa:

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

-include $(DEPS)

%.o: %.cpp
    $(CXX) $(CFLAGS) -MMD -c -o $@ $<
 0
Author: Marcel Keller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-15 19:34:05

Nieco zmodyfikowana wersja odpowiedzi , która pozwala na wypisanie *.pliki d do innego folderu (wkleję tylko interesującą część, która generuje pliki zależności):

$(OBJDIR)/%.o: %.cpp
# Generate dependency file
    mkdir -p $(@D:$(OBJDIR)%=$(DEPDIR)%)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -MM -MT $@ $< -MF $(@:$(OBJDIR)/%.o=$(DEPDIR)/%.d)
# Generate object file
    mkdir -p $(@D)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@

Zauważ, że parametr

-MT $@

Służy do zapewnienia, że cele (tj. nazwy plików obiektowych) w wygenerowanym*.pliki d zawierają pełną ścieżkę do *.o pliki i nie tylko nazwa pliku.

Nie wiem dlaczego ten parametr nie jest potrzebny przy użyciu-MMD w kombinacji z-c (jak w wersji ). W tej kombinacji wydaje się zapisywać pełną ścieżkę *.o pliki do *.pliki D. Bez tej kombinacji,- MMD zapisuje do *tylko czyste nazwy plików bez żadnych składników katalogowych.pliki D. Może ktoś wie dlaczego-MMD pisze pełną ścieżkę w połączeniu z-c. nie znalazłem żadnej podpowiedzi na stronie podręcznika g++.

 0
Author: MaximumFPS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-22 18:24:02