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

标签 c++ linux makefile gnu-make

我想从包含未使用目标的Makefile中提取所有*.cc*.h文件名。这里是Makefile示例:https://gist.github.com/berceanu/7554a9c4371b807e425259c7e99b5de9
我试过运行make -Bnd并查看修剪过的文件,但我不知道这是否遗漏了什么。
make -Bnd | grep "Pruning file" | sort | uniq
预期结果:上述生成文件上*.h使用的所有*.ccmake run文件的列表。

最佳答案

您试图从Makefile中提取此信息的方法可能是错误的。make不知道实际使用了哪些头文件。make只知道您在依赖项中明确告诉了make哪些头文件,这不是很可靠。Makefile中的信息有两种可能是错误的。它可以包含未使用的目标(如您所注意到的)或未使用的头文件。它可能会丢失实际包含但未在Makefile中提及的头文件。更糟糕的是,如果头文件的实际包含依赖于宏,比如#ifdef XYZ_FEATURE #include "additionalHeaderFile.h" #endif,该怎么办。
至少有三种方法可以生成所需的列表,即编译过程中实际使用的.cc.h文件列表:
(盲目信任Makefilemake -n --print-data-base
(遵循构建的真实文件,但也有点难以解析)strace -f make
(依赖于GCC、clang、armcc和其他编译器中的一个特性来生成Makefile,非常可靠)将CPPFLAGS:=-MMD添加到Makefile,运行make clean,然后make,然后使用cat *.d获取用于生成.cc的所有.hrun文件的列表。你甚至可以在不改变Makefilemake clean; make CPPFLAGS:=-MMD && cat *.d | sed -e 's/\\//g' -e 's/:/ /g' -e 's/ \+/\n/g' | sort -u的情况下做到这一点。
另外,您在要点中共享的Makefile也有很多问题。
按照惯例,默认目标应命名为all123。这不需要是二进制文件的名称,它只是一个.PHONY目标。
带有对象文件列表的变量应命名为OBJSOBJECTS,而不是OBJ。这个名字有误导性,因为它是单数的。
而不是OBJ使用rm,这意味着$(RM),因此在文件不存在的情况下不会有麻烦。(作为副作用,-f将变得更加便携,因为并非所有平台都使用Makefile来删除文件。)
rm不是文件,因此应该是clean目标。
.PHONY应该使用clean而不是::作为其配方,以便将来当:更大并拆分为多个文件时,每个文件都可以有自己的Makefile目标而不会出现问题。
非规则特定的变量应在定义时展开,而不是在引用时展开,因此应使用clean而不是:=来定义。
使用已定义的=而不是C++
使用CXX,而不是将选项放入C++/CXX,因为您正在链接。
应该有一个与二进制文件具有相同基名的源文件。然后可以使用内置规则进行链接。
LDFLAGS中的显式依赖关系是维护的一个难题。每次添加、删除或更改项目头文件的Makefile语句时,#include都必须更新,这很容易忘记,而且这是一种痛苦,尤其是当Makefile语句位于头文件中时。即使尽职尽责,这也是一场看不见的合并冲突。您应该在#include开头使用Makefile,在CPPFLAGS+=-MMD结尾附加Makefile,并将-include $(wildcard *.d)添加到要在Makefile中删除的文件列表中,而不是在*.d中具有显式依赖关系。然后可以从clean中删除所有依赖关系规则(除了链接的规则)。
命名二进制Makefile不是一个好主意。看到您的run有一个Makefile目标的用户会希望它运行实际的程序,而不是链接它。
最好把每一个对象单独放在一行。当多个开发人员同时更改对象列表时,这显著减少了项目中的合并冲突。
实际的run应该是这样的,二进制文件从Makefile重命名为run

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)

这个program将做与您的Makefile相同的事情,但它几乎是免维护的。不需要更新依赖项,因为它们是从C预处理器生成的依赖项文件中自动获取的。Makefile还将删除在您将*.[adios]添加到任何-save-tempsCFLAGSCXXFLAGS时创建的文件。
众所周知,这种类型的CPPFLAGS适用于GCC、clang、AOCC(AMD optimizec Compiler)和armcc。它可能也适用于其他一些编译器和预处理器,特别是当它们基于GCC或clang/LLVM或试图与GCC或clang/LLVM兼容时。
顺便说一句,如果你有兴趣知道这对你有用的话,除了经验之外,还有很高的信心:我已经把你的Makefile添加到它上面,以便复制你的源代码结构。头文件将只是空文件。C++源文件将是从Makefile中的依赖项获取的#include语句列表。
%.cc:
    grep '^$*\.o.*:' $(MAKEFILE_LIST) | sed -e 's/.*://' -e 's/.*$*\.cc//' -e 's/ \([^ ]\+\)/#include "\1"\n/g' >$@

%.h:
    touch $@

关于c++ - 通过解析Makefile检索使用的头文件和源文件名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54029987/

相关文章:

c++ - 如何检查和处理前提条件违规?

c++ - 有状态 lambda 问题 - Microsoft 编译器版本 19.16.27024.1

makefile - 怎么办 "make <subdir> <target>"?

c++ - 尽管我设置了 CC=g++,为什么我的 makefile 仍调用 gcc?

c++ - 使用CMake构建Qt项目并从QMainWindow继承导致未引用的vtable错误

android - native Android 崩溃 - 无效位图

linux - 不相关进程之间的 mmap 文件

Linux根据名称长度查找文件和文件夹但输出完整路径

linux - mv 'x' 和 'y' 是同一个文件

c - 在 Ubuntu 上编译 PJSIP 时出错