通过解析 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
文件的列表。
而不是-Bnd
,我建议使用--dry-run --print-data-base
来转储目标的完整数据库、它们的依赖关系、规则、变量等。
您尝试从 Makefile
中提取此信息的方法可能是错误的方法。 make
不知道实际上 使用了哪些头文件。 make
只知道您在依赖项中明确告诉 make
哪些头文件,这不是很可靠。 Makefile
中的信息可能在两个方面是错误的。它可能包含未使用的目标(正如您所注意到的)或未使用的头文件。它可能会遗漏 Makefile
中实际包含但未提及的头文件。更糟糕的是,如果头文件的实际包含取决于宏,比如 #ifdef XYZ_FEATURE #include "additionalHeaderFile.h" #endif
.
至少有三种方法可以生成所需的列表,即编译期间实际使用的 .cc
和 .h
文件的列表:
- (盲目相信
Makefile
)make -n --print-data-base
- (跟build的真实文件一样,但也有点难解析)
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
删除文件。)
clean
不是文件,因此应该是 .PHONY
目标。
clean
应该使用 ::
而不是 :
作为它的配方,以便将来当 Makefile
更大并分成多个文件时,每个文件都可以有自己的 clean
目标没有问题。
- 非特定规则的变量应该在定义时扩展,而不是在引用时扩展,因此使用
:=
而不是 =
定义。
- 而不是
C++
使用已经定义的 CXX
。
- 不要将选项放入
C++
/CXX
,而是使用 LDFLAGS
,因为你 linking.
- 您应该有一个与二进制文件具有相同基本名称的源文件。然后你可以使用内置规则 linking.
Makefile
中的显式依赖项是维护的难题。每次添加、删除或更改项目头文件的 #include
语句时,都必须更新 Makefile
,这很容易忘记,而且很痛苦,尤其是当 #include
语句在头文件中。即使尽职调查,这也是一种无形的合并冲突。您应该在 Makefile
的开头使用 CPPFLAGS+=-MMD
并在 Makefile
的末尾附加 -include $(wildcard *.d)
,而不是在 Makefile
中具有显式依赖关系,并且将 *.d
添加到 clean
中要删除的文件列表中。然后,您可以从 Makefile
中删除 all 依赖规则(linkage 除外)。
- 将二进制文件命名为
run
不是一个好主意。看到你的 Makefile
有一个 run
目标的用户会期望这个 运行 实际程序,而不是 link 它。
- 最好将提到的每个对象单独占一行。当多个开发人员同时更改对象列表时,这会显着减少项目中的合并冲突。
您的实际 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
那样做 "same" 的事情,但它几乎 无需维护 。无需更新依赖项,因为它们是自动从 C 预处理器生成的依赖项文件中获取的。如果您将 -save-temps
添加到任何 CFLAGS
、CXXFLAGS
或 CPPFLAGS
.
,*.[adios]
也会删除创建的文件
已知这种类型的 Makefile
适用于 GCC、clang、AOCC(AMD 优化 C 编译器)和 armcc。它可能也适用于许多其他编译器和预处理器,尤其是当它们基于或试图与 GCC 或 clang/LLVM.
兼容时
顺便说一句,如果你有兴趣知道这对你有用,除了经验之外,我有很高的信心:我已经拿走了你的 Makefile
并在其中添加了以下几行以重现你的源代码代码结构。头文件只是空文件。 C++ 源文件将是从 Makefile
.
中的依赖项中获取的 #include
语句的列表
%.cc:
grep '^$*\.o.*:' $(MAKEFILE_LIST) | sed -e 's/.*://' -e 's/.*$*\.cc//' -e 's/ \([^ ]\+\)/#include ""\n/g' >$@
%.h:
touch $@
我想从还包含未使用目标的 Makefile 中提取所有 *.cc
和 *.h
文件名。示例 Makefile 在这里:https://gist.github.com/berceanu/7554a9c4371b807e425259c7e99b5de9
我已经尝试 运行 make -Bnd
并查看了修剪后的文件,但我不知道这是否遗漏了任何内容。
make -Bnd | grep "Pruning file" | sort | uniq
预期结果:make run
在上述 Makefile 中使用的所有 *.h
和 *.cc
文件的列表。
而不是-Bnd
,我建议使用--dry-run --print-data-base
来转储目标的完整数据库、它们的依赖关系、规则、变量等。
您尝试从 Makefile
中提取此信息的方法可能是错误的方法。 make
不知道实际上 使用了哪些头文件。 make
只知道您在依赖项中明确告诉 make
哪些头文件,这不是很可靠。 Makefile
中的信息可能在两个方面是错误的。它可能包含未使用的目标(正如您所注意到的)或未使用的头文件。它可能会遗漏 Makefile
中实际包含但未提及的头文件。更糟糕的是,如果头文件的实际包含取决于宏,比如 #ifdef XYZ_FEATURE #include "additionalHeaderFile.h" #endif
.
至少有三种方法可以生成所需的列表,即编译期间实际使用的 .cc
和 .h
文件的列表:
- (盲目相信
Makefile
)make -n --print-data-base
- (跟build的真实文件一样,但也有点难解析)
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
删除文件。) clean
不是文件,因此应该是.PHONY
目标。clean
应该使用::
而不是:
作为它的配方,以便将来当Makefile
更大并分成多个文件时,每个文件都可以有自己的clean
目标没有问题。- 非特定规则的变量应该在定义时扩展,而不是在引用时扩展,因此使用
:=
而不是=
定义。 - 而不是
C++
使用已经定义的CXX
。 - 不要将选项放入
C++
/CXX
,而是使用LDFLAGS
,因为你 linking. - 您应该有一个与二进制文件具有相同基本名称的源文件。然后你可以使用内置规则 linking.
Makefile
中的显式依赖项是维护的难题。每次添加、删除或更改项目头文件的#include
语句时,都必须更新Makefile
,这很容易忘记,而且很痛苦,尤其是当#include
语句在头文件中。即使尽职调查,这也是一种无形的合并冲突。您应该在Makefile
的开头使用CPPFLAGS+=-MMD
并在Makefile
的末尾附加-include $(wildcard *.d)
,而不是在Makefile
中具有显式依赖关系,并且将*.d
添加到clean
中要删除的文件列表中。然后,您可以从Makefile
中删除 all 依赖规则(linkage 除外)。- 将二进制文件命名为
run
不是一个好主意。看到你的Makefile
有一个run
目标的用户会期望这个 运行 实际程序,而不是 link 它。 - 最好将提到的每个对象单独占一行。当多个开发人员同时更改对象列表时,这会显着减少项目中的合并冲突。
您的实际 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
那样做 "same" 的事情,但它几乎 无需维护 。无需更新依赖项,因为它们是自动从 C 预处理器生成的依赖项文件中获取的。如果您将 -save-temps
添加到任何 CFLAGS
、CXXFLAGS
或 CPPFLAGS
.
*.[adios]
也会删除创建的文件
已知这种类型的 Makefile
适用于 GCC、clang、AOCC(AMD 优化 C 编译器)和 armcc。它可能也适用于许多其他编译器和预处理器,尤其是当它们基于或试图与 GCC 或 clang/LLVM.
顺便说一句,如果你有兴趣知道这对你有用,除了经验之外,我有很高的信心:我已经拿走了你的 Makefile
并在其中添加了以下几行以重现你的源代码代码结构。头文件只是空文件。 C++ 源文件将是从 Makefile
.
#include
语句的列表
%.cc:
grep '^$*\.o.*:' $(MAKEFILE_LIST) | sed -e 's/.*://' -e 's/.*$*\.cc//' -e 's/ \([^ ]\+\)/#include ""\n/g' >$@
%.h:
touch $@