4

我目前正在尝试构建一个合适的 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++Compiler 标志-MM来自动创建依赖项并使用-MT与默认路径不同的路径。产生的依赖应该看起来像

./obj/main.o: ./src/main.cpp ./src/some_header_file.h

不幸的是,这永远不会被调用,我不知道为什么会这样。在一个类似的问题中,用户 Beta 很高兴地通过添加 a 提供了一个临时解决方案,.Phony但这对重建每个对象而不做任何更改有副作用。

最后,只有一行

-include $(DEP)

包含依赖文件,一旦创建。

非常欢迎提供有关任何部分的一些提示的任何答案。所以我的问题是:我可以做得更好或者“更清洁”,为什么依赖生成不起作用?

4

1 回答 1

9

开始。


尽可能分配简单的扩展变量

SRCS := $(wildcard $(SRC_DIR)/*$(SRC_EXT))

来自GNU Make 手册

[递归扩展变量]的另一个缺点是每次扩展变量时都会执行定义中引用的任何函数。这会使make运行速度变慢;更糟糕的是,它会导致wildcardandshell函数给出不可预测的结果,因为您无法轻松控制它们何时被调用,甚至调用多少次。


使用替换引用patsubst函数将源转换为对象:

OBJS := $(SRCS:$(SRC_DIR)/%$(SRC_EXT)=$(OBJ_DIR)/%$(OBJ_EXT))

在编译模式规则中指定适当的先决条件。这对于让 Make 保持您的目标文件保持最新并在源更改时更新它们是必需的。

$(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 手册

-MD

-MD等价于-M -MF file,只是-E没有暗示。驱动程序根据是否给出 -o 选项来确定文件。如果是,驱动程序使用其参数,但后缀为.d,否则它采用输入文件的名称,删除所有目录组件和后缀,并应用.d后缀。

-MMD

就像-MD除了只提到用户头文件,而不是系统头文件。

-MP

此选项指示 CPP 为除主文件之外的每个依赖项添加虚假目标,从而导致每个依赖项都不依赖任何内容。make如果您删除头文件而不更新Makefile以匹配,这些虚拟规则会解决错误。

还可以考虑研究Paul Smith 的这篇文章(他是 GNU Make 的维护者)。它很好地概述了不同的 autodep 生成方法。

于 2012-05-04T21:25:27.453 回答