Makefile, dependencies de header

Digamos que eu tenha um makefile com a regra

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

Eu quero que * .o seja reconstruído sempre que um arquivo de header for alterado. Em vez de elaborar uma lista de dependencies, sempre que qualquer arquivo de header em /include alterações, todos os objects no diretório deverão ser recriados.

Não consigo pensar em uma boa maneira de mudar a regra para acomodar isso, estou aberto a sugestões. Pontos de bônus se a lista de headers não precisar ser codificada

Se você estiver usando um compilador GNU, o compilador pode montar uma lista de dependencies para você. Fragmento Makefile:

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

ou

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

onde SRCS é uma variável apontando para sua lista inteira de arquivos de origem.

Há também a ferramenta makedepend , mas eu nunca gostei tanto quanto gcc -MM

A maioria das respostas é surpreendentemente complicada ou errônea. No entanto, exemplos simples e robustos foram publicados em outro lugar [ codereview ]. É certo que as opções fornecidas pelo pré-processador do gnu são um pouco confusas. No entanto, a remoção de todos os diretórios do destino de compilation com -MM está documentada e não é um bug [ gpp ]:

Por padrão, o CPP obtém o nome do arquivo de input principal, exclui todos os componentes de diretório e qualquer sufixo de arquivo, como ‘.c’, e anexa o sufixo de object usual da plataforma.

A opção (um pouco mais nova) -MMD é provavelmente o que você quer. Para completar, um exemplo de um makefile que suporta vários diretórios src e cria diretórios com alguns comentários. Para uma versão simples sem diretórios de construção, consulte [ 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) 

Esse método funciona porque, se houver várias linhas de dependência para um único destino, as dependencies serão unidas, por exemplo:

 ao: ah ao: ac ./cmd 

é equivalente a:

 ao: ac ah ./cmd 

como mencionado em: Makefile múltiplas linhas de dependência para um único alvo?

Como eu postei aqui o gcc pode criar dependencies e compilar ao mesmo tempo:

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

O parâmetro '-MF' especifica um arquivo para armazenar as dependencies em.

O traço no início de '-include' diz Make para continuar quando o arquivo .d não existe (por exemplo, na primeira compilation).

Note que parece haver um bug no gcc em relação à opção -o. Se você definir o nome do arquivo do object para dizer obj / _file__c.o, então o arquivo gerado .d ainda conterá o arquivo .o, não obj / _file__c.o.

Que tal algo como:

 includes = $(wildcard include/*.h) %.o: %.c ${includes} gcc -Wall -Iinclude ... 

Você também pode usar os curingas diretamente, mas eu acho que preciso deles em mais de um lugar.

Observe que isso funciona bem apenas em projetos pequenos, pois supõe que cada arquivo de object depende de cada arquivo de header.

A solução de Martin acima funciona muito bem, mas não manipula arquivos .o que residem em subdiretórios. Godric aponta que o sinalizador -MT cuida desse problema, mas impede simultaneamente que o arquivo .o seja gravado corretamente. A seguir, vamos cuidar de ambos os problemas:

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

Isso fará o trabalho muito bem e até mesmo manipulará os subdiretórios especificados:

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

testado com o gcc 4.8.3

Eu prefiro esta solução, sobre a resposta aceita por Michael Williamson, ele pega mudanças em fonts + arquivos embutidos, então fonts + headers e finalmente somente fonts. A vantagem aqui é que a biblioteca inteira não é recompilada se apenas algumas mudanças forem feitas. Não é uma consideração enorme para um projeto com um par de arquivos, mas se você tiver 10 ou 100 fonts, você notará a diferença.

 COMMAND= gcc -Wall -Iinclude ... %.o: %.cpp %.inl $(COMMAND) %.o: %.cpp %.hpp $(COMMAND) %.o: %.cpp $(COMMAND) 

O seguinte funciona para mim:

 DEPS := $(OBJS:.o=.d) -include $(DEPS) %.o: %.cpp $(CXX) $(CFLAGS) -MMD -c -o $@ $<  

Aqui está um verso:

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

Isso funciona com a receita padrão, desde que você tenha uma lista de todos os seus arquivos de object no OBJS .