通过解析Makefile检索使用过的头文件名和源文件名

Retrieve used header and source file names by parsing Makefile

本文关键字:文件名 源文件 Makefile 检索      更新时间:2023-10-16

我想从包含未使用目标的Makefile中提取所有*.cc*.h文件名。此处的示例Makefile:https://gist.github.com/berceanu/7554a9c4371b807e425259c7e99b5de9

我试过运行make -Bnd并查看修剪过的文件,但我不知道这是否遗漏了什么。

make -Bnd | grep "Pruning file" | sort | uniq

预期结果:make run在上述Makefile上使用的所有*.h*.cc文件的列表。

您试图从Makefile中提取这些信息的方法可能是错误的。make不知道实际使用了哪些头文件。make只知道您在依赖关系中明确告诉make的头文件,这不是很可靠。Makefile中的信息可能在两个方面是错误的。它可能包含未使用的目标(正如您所注意到的)或未使用的头文件。它可能会丢失Makefile中实际包含但未提及的头文件。更糟糕的是,如果头文件的实际包含取决于宏,如#ifdef XYZ_FEATURE #include "additionalHeaderFile.h" #endif,该怎么办。

至少有三种方法可以生成所需的列表,即编译过程中实际使用的.cc.h文件的列表:

  • (盲目信任Makefile)make -n --print-data-base
  • (遵循构建的真实文件,但也有点难以解析)strace -f make
  • (依赖于GCC、clang、armcc以及可能其他编译器中存在的Makefile生成功能,非常可靠)将CPPFLAGS:=-MMD添加到Makefile,运行make clean,然后运行make,然后使用cat *.d来获得用于构建run的所有.cc.h文件的列表。您甚至可以在不更改Makefile:make clean; make CPPFLAGS:=-MMD && cat *.d | sed -e 's/\//g' -e 's/:/ /g' -e 's/ +/n/g' | sort -u的情况下执行此操作

此外,您在要点中共享的Makefile存在大量问题。

  • 按照惯例,默认目标应命名为all1 2 3。这不需要是二进制文件的名称,它只是一个.PHONY目标
  • 带有对象文件列表的变量应命名为OBJSOBJECTS,而不是OBJ。名称OBJ具有误导性,因为它是单数
  • 使用$(RM)代替rm,这意味着-f,因此在文件不存在的情况下不会出现问题。(副作用是,Makefile将变得更加便携,因为并非所有平台都使用rm来删除文件。)
  • CCD_ 43不是文件,因此应该是CCD_ 44目标
  • clean的配方应该使用::而不是:,这样,将来当Makefile更大并拆分为多个文件时,每个文件都可以有自己的clean目标,而不会出现问题
  • 不特定于规则的变量应在定义时展开,而不是在引用时展开,因此应使用:=而不是=进行定义
  • 使用已经定义的CXX代替C++
  • 不要将选项放入C++/CXX,而是使用LDFLAGS,因为您正在链接
  • 您应该有一个与二进制文件具有相同基本名称的源文件。然后可以使用内置规则进行链接
  • Makefile中的显式依赖关系是维护的难点。每次添加、删除或更改项目头文件的#include语句时,都必须更新Makefile,这很容易忘记,而且很痛苦,尤其是当#include语句在头文件中时。即使尽职调查,这也是一场无形的合并冲突。不应该在Makefile中具有显式依赖项,而应该在Makefile的开头使用CPPFLAGS+=-MMD,在Makefile的末尾附加-include $(wildcard *.d),并将*.d添加到clean中要删除的文件列表中。然后,您可以从Makefile中删除所有依赖规则(链接规则除外)
  • 将二进制命名为run不是一个好主意。如果用户看到您的Makefile有一个run目标,则希望它运行实际程序,而不是链接它
  • 最好把每一个物体都放在自己的一行。当多个开发人员同时更改对象列表时,这大大减少了项目中的合并冲突

实际的Makefile应该是这样的,二进制文件从run重命名为program:

LDFLAGS:=-Wno-deprecated -lm
CPPFLAGS+=-MMD
BINARY:=program
OBJECTS:= 
$(BINARY).o 
binomh.o 
# More objects here
.PHONY: all
all: $(BINARY)
$(BINARY): $(OBJECTS)
.PHONY: clean
clean::
$(RM) 
$(BINARY) 
*.[adios] 
-include $(wildcard *.d)

Makefile将做与Makefile"相同"的事情,但它几乎是免维护的。无需更新依赖关系,因为它们是从C预处理器生成的依赖关系文件中自动获取的。*.[adios]还将删除在将-save-temps添加到CFLAGSCXXFLAGSCPPFLAGS中的任何一个时创建的文件。

众所周知,这种类型的Makefile适用于GCC、clang、AOCC(AMD优化C编译器)和armcc。它可能也适用于许多其他编译器和预处理器,尤其是当它们基于GCC或clang/LLVM或试图与GCC或clang/LLVM兼容时。

顺便说一句,如果你有兴趣知道这对你有用,除了经验之外,还要高度自信:我已经取了你的Makefile,并在其中添加了以下几行,以便重现你的源代码结构。头文件将只是空文件。C++源文件将是#include语句的列表,这些语句取自Makefile中的依赖项。

%.cc:
grep '^$*.o.*:' $(MAKEFILE_LIST) | sed -e 's/.*://' -e 's/.*$*.cc//' -e 's/ ([^ ]+)/#include "1"n/g' >$@
%.h:
touch $@

我建议使用--dry-run --print-data-base来转储目标、它们的依赖关系、规则、变量等的完整数据库,而不是-Bnd