通过解析Makefile检索使用过的头文件名和源文件名
Retrieve used header and source file names by parsing Makefile
我想从包含未使用目标的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
存在大量问题。
- 按照惯例,默认目标应命名为
all
1 2 3。这不需要是二进制文件的名称,它只是一个.PHONY
目标 - 带有对象文件列表的变量应命名为
OBJS
或OBJECTS
,而不是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
添加到CFLAGS
、CXXFLAGS
或CPPFLAGS
中的任何一个时创建的文件。
众所周知,这种类型的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
- 从命令行c++发送文本文件名
- 如何在OMNET++中指定与命令行参数组合的输出文件名
- 为什么文件名被设置为一个点,而不是在读取矢量中的文件名时
- 为测试目标创建具有不同源文件夹的文件
- 将图像添加到资源文件夹UWP C++
- 当Microsoft文档仅包含 C# 示例时,如何查找 C++ 包含文件名
- 视觉工作室项目.提取源文件夹名称
- 生成一个生成文件,该生成文件使用Automake在一个步骤中编译和链接所有源文件
- 如何在CPP的给定目录中列出UTF编码的文件名?
- make 命令如何避免重新编译未更改的源文件?
- C++两个源文件之间共享的枚举的静态实例
- 如何使用Qt 3D库加载和显示搅拌机.obj源文件场景
- 在Visual Studio 2019中保存时ReadDirectoryChangesW文件名的问题
- C++:在Windows中浏览名称中带有点的文件名
- 我在 ifstream input_file(文件名)行中收到错误"no matching function to call";
- 不带预处理器的调用方法/文件的文件名/行号
- 在 C++ 中为文件名添加时间戳
- 生成文件:动态源文件名和对象目录
- 为什么当我在 std::string 中打开文件名的 fstream 时出现"no matching function"错误?
- 无法使用 CMake 从其他文件夹添加源文件