[英]Makefile, header dependencies
假設我有一個帶有規則的 makefile
%.o: %.c
gcc -Wall -Iinclude ...
我希望在頭文件更改時重建 *.o。 每當/include
任何頭文件發生更改時,都必須重新構建該目錄中的所有對象,而不是計算出依賴項列表。
我想不出改變規則以適應這一點的好方法,我願意接受建議。 如果標題列表不必硬編碼,則獎勵積分
如果您使用的是 GNU 編譯器,編譯器可以為您組裝一個依賴項列表。 生成文件片段:
depend: .depend
.depend: $(SRCS)
rm -f "$@"
$(CC) $(CFLAGS) -MM $^ -MF "$@"
include .depend
或者
depend: .depend
.depend: $(SRCS)
rm -f "$@"
$(CC) $(CFLAGS) -MM $^ > "$@"
include .depend
其中SRCS
是指向整個源文件列表的變量。
還有makedepend
工具,但我從來沒有像gcc -MM
那樣喜歡它
大多數答案出人意料地復雜或錯誤。 然而,其他地方已經發布了簡單而強大的示例 [ codereview ]。 誠然,gnu 預處理器提供的選項有點令人困惑。 但是,記錄了使用-MM
從構建目標中刪除所有目錄的記錄,而不是錯誤 [ gpp ]:
默認情況下,CPP 采用主輸入文件的名稱,刪除任何目錄組件和任何文件后綴,例如“.c”,並附加平台的常用對象后綴。
(有點新) -MMD
選項可能是您想要的。 為了完整起見,一個支持多個 src 目錄和帶有一些注釋的構建目錄的 makefile 示例。 對於沒有構建目錄的簡單版本,請參閱 [ 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)
此方法有效,因為如果單個目標有多個依賴項,則這些依賴項會簡單地連接起來,例如:
a.o: a.h
a.o: a.c
./cmd
相當於:
a.o: a.c a.h
./cmd
正如我在這里發布的那樣, gcc 可以同時創建依賴項和編譯:
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
%.o: %.c
$(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<
'-MF' 參數指定一個文件來存儲依賴項。
'-include' 開頭的破折號告訴 Make 在 .d 文件不存在時繼續(例如在第一次編譯時)。
請注意,gcc 中似乎存在關於 -o 選項的錯誤。 如果您將目標文件名設置為 obj/_file__c.o,那么生成的文件.d 仍將包含文件.o,而不是 obj/_file__c.o。
怎么樣:
includes = $(wildcard include/*.h)
%.o: %.c ${includes}
gcc -Wall -Iinclude ...
您也可以直接使用通配符,但我傾向於發現我在不止一個地方需要它們。
請注意,這只適用於小型項目,因為它假定每個目標文件都依賴於每個頭文件。
上面 Martin 的解決方案效果很好,但不處理駐留在子目錄中的 .o 文件。 Godric 指出 -MT 標志解決了這個問題,但它同時阻止了 .o 文件被正確寫入。 以下將解決這兩個問題:
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
%.o: %.c
$(CC) $(CFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $<
$(CC) $(CFLAGS) -o $@ $<
這將很好地完成工作,甚至可以處理指定的子目錄:
$(CC) $(CFLAGS) -MD -o $@ $<
用 gcc 4.8.3 測試過
這是一個兩行:
CPPFLAGS = -MMD
-include $(OBJS:.c=.d)
這適用於默認的 make 配方,只要您在OBJS
中有所有目標文件的OBJS
。
Sophie's answer 的一個稍微修改的版本,它允許將 *.d 文件輸出到不同的文件夾(我只會粘貼生成依賴文件的有趣部分):
$(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 $@
注意參數
-MT $@
用於確保生成的 *.d 文件中的目標(即目標文件名)包含 *.o 文件的完整路徑,而不僅僅是文件名。
我不知道為什么在將 -MMD 與 -c結合使用時不需要此參數(如 Sophie 的版本)。 在這種組合中,它似乎將 *.o 文件的完整路徑寫入 *.d 文件。 如果沒有這種組合,-MMD 也只會將沒有任何目錄組件的純文件名寫入 *.d 文件。 也許有人知道為什么 -MMD 與 -c 結合使用時會寫出完整路徑。 我在 g++ 手冊頁中沒有找到任何提示。
我更喜歡這個解決方案,而不是邁克爾威廉姆森接受的答案,它捕獲對源+內聯文件的更改,然后是源+標題,最后是僅源。 這里的優點是如果只進行一些更改,則不會重新編譯整個庫。 對於有幾個文件的項目來說,這不是一個很大的考慮,但如果你有 10 個或 100 個源,你會注意到差異。
COMMAND= gcc -Wall -Iinclude ...
%.o: %.cpp %.inl
$(COMMAND)
%.o: %.cpp %.hpp
$(COMMAND)
%.o: %.cpp
$(COMMAND)
以下對我有用:
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
%.o: %.cpp
$(CXX) $(CFLAGS) -MMD -c -o $@ $<
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.