我正在尝试构建一个合适的Makefile。
我想要的是完全控制正在发生的事情,所以我不想要任何第三方软件。
我当前的尝试对我来说似乎是逻辑,但由于依赖关系生成无效,我有点卡住了。
为了更好的可读性,完整的Makefile被分解成小块。如果有什么需要改进,我将不胜感激任何评论。
首先,我有以下静态定义
CXX = g++
CXXFLAGS = -Wall \
-Wextra \
-Wuninitialized \
-Wmissing-declarations \
-pedantic \
-O3 \
-p -g -pg
LDFLAGS = -p -g -pg
DEPFLAGS = -MM
Afaik这应该没问题。使分析标志可选,但这并不重要。
SRC_DIR = ./src
OBJ_DIR = ./obj
SRC_EXT = .cpp
OBJ_EXT = .o
TARGET = ./bin/my_target
SRCS = $(wildcard $(SRC_DIR)/*$(SRC_EXT))
OBJS = $(subst $(SRC_DIR), $(OBJ_DIR), $(SRCS:$(SRC_EXT)=$(OBJ_EXT)))
DEP = depend.main
基本上,这应该只从子文件夹*.cpp
中提取所有src
个文件,另外用./src
和./obj
替换.cpp
.o
作为对象的名称。
.PHONY: clean all depend
all: $(TARGET)
$(TARGET): $(OBJS)
@echo "-> linking $@"
@$(CXX) $^ $(LDFLAGS) -o $@
$(OBJ_DIR)/%.$(EXT_OBJ):
@echo "-> compiling $@"
@$(CXX) $(CXXFLAGS) -c $< -o $@
Afaik,这个块 - 提供了一个有效的依赖文件 - 应该进行所有必要的编译和链接。
clean:
@echo "removing objects and main file"
@rm -f $(OBJS) $(TARGET)
应该是不言自明和正确的,还是我在这里遗漏了什么?
$(SRC_DIR)/%.$(SRC_EXT):
$(CXX) $(DEPFLAGS) -MT \
"$(subst $(SRC_DIR),$(OBJ_DIR),$(subst $(SRC_EXT),$(OBJ_EXT),$@))" \
$(addprefix ,$@) >> $(DEP);
clear_dependencies:
@echo "-> (re-)building dependencies";
@$(RM) $(DEP)
depend: clear_dependencies $(SRCS)
这是非功能性部分。我打算做的是使用g++
编译器标志-MM
来自动创建依赖项,并使用-MT
使用与默认路径不同的路径。
产生的依赖关系应该看起来像
./obj/main.o: ./src/main.cpp ./src/some_header_file.h
不幸的是,这将永远不会被调用,我不知道为什么会这样。
在similar question中,用户Beta很乐意通过添加.Phony
来提供临时解决方案,但这会对重建每个对象产生副作用而无需任何更改。
最后,只有一行
-include $(DEP)
包含依赖文件,一旦创建。
任何提供任何部分提示的答案都非常受欢迎。 所以我的问题是:我可以做得更好或者更“干净”,为什么依赖生成不起作用?
答案 0 :(得分:9)
到此为止。
尽可能分配simply expanded variables:
SRCS := $(wildcard $(SRC_DIR)/*$(SRC_EXT))
递归扩展变量的另一个缺点是每次扩展变量时都会执行定义中引用的任何函数。这使
make
运行得更慢;更糟糕的是,它导致wildcard
和shell
函数产生不可预测的结果,因为您无法轻易控制它们何时被调用,甚至多少次。
使用substitution references或patsubst
function将来源转换为对象:
OBJS := $(SRCS:$(SRC_DIR)/%$(SRC_EXT)=$(OBJ_DIR)/%$(OBJ_EXT))
在编译模式规则中指定适当的先决条件。这是强制性,以使您的目标文件保持最新并在源更改时更新它们。
$(OBJ_DIR)/%$(OBJ_EXT) : $(SRC_DIR)/%$(SRC_EXT)
@echo "-> compiling $@"
@$(CXX) $(CXXFLAGS) -o $@ -c $<
同时编译源并为它们生成依赖文件。使用-MMD -MP
标志可以使事情正常工作(只需将它们附加到CXXFLAGS
)。
CXXFLAGS += -MMD -MP
-include $(OBJS:$(OBJ_EXT)=.d)
来自GCC manual:
-MD
-MD
相当于-M -MF
文件,但不暗示-E
。驱动程序根据是否给出-o选项来确定文件。如果是,则驱动程序使用其参数但后缀为.d
,否则它将获取输入文件的名称,删除任何目录组件和后缀,并应用.d
后缀。
-MMD
与
-MD
类似,只提及用户头文件,而不是系统头文件。
-MP
此选项指示CPP为除主文件之外的每个依赖项添加虚假目标,从而导致每个依赖项无任何依赖。如果删除头文件而不更新
make
以匹配,则这些虚拟规则可解决错误Makefile
。
另外考虑研究Paul Smith的this article(他是GNU Make的维护者)。它给出了不同自动生成方法的相当好的概述。