【问题标题】:Makefile improvements, dependency generation not functioningMakefile 改进,依赖项生成不起作用
【发布时间】:2012-05-14 09:22:36
【问题描述】:

我目前正在尝试构建一个合适的 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)

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

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

【问题讨论】:

  • 哼!我告诉过你PHONY 技巧只是权宜之计,我们将在下一次迭代中介绍依赖关系处理。如果你想征求其他意见,很好,但不要告诉别人我给了你草率的建议。
  • 对不起,这是一个误解。我很高兴得到您的回答,但想提出一个更详细的问题。如果只是更新旧的,这将是一团糟。非常感谢您的努力和回答。

标签: c++ makefile gnu-make


【解决方案1】:

来了。


尽可能分配simply expanded variables

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

来自GNU Make manual

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


使用substitution referencespatsubst function 将源转换为对象:

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 manual

-MD

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

-MMD

-MD 类似,只是只提及用户头文件,而不是系统头文件。

-MP

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

还可以考虑学习 Paul Smith 的this article(他是 GNU Make 的维护者)。它很好地概述了不同的 autodep 生成方法。

【讨论】:

  • 感谢您的回答,效果很好。我不明白我的变量是否足够“简单”,是否还有任何潜在的改进或这是最优的?
  • @stefan,当它的值调用像wildcard 这样的非平凡函数时,使用简单的扩展变量而不是递归变量会更快。就我个人而言,我只使用递归扩展变量来定义自定义“函数”,这些函数是使用call function 调用的。
  • @stefan,这是一个很好的答案,Paul Smith 的文章就是我正在努力的。我会看看我是否有什么要补充的,并在另一个问题中编辑我的答案。
  • @Beta:非常感谢!我非常感谢你的努力,也喜欢这个答案。如果可以的话,我会多次赞成这个和你的答案,因为这不仅仅是解决方案。这是一个完美的解释,我可以从中学到很多东西。再次感谢你们的出色工作!
  • @stefan,不客气!事实上,出于同样的原因,我对你的两个问题都投了赞成票并加了书签。 ;-)
猜你喜欢
  • 1970-01-01
  • 2016-10-07
  • 2023-04-11
  • 2012-06-20
  • 2011-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-14
相关资源
最近更新 更多