从一个巨大的 CPP 项目的依赖图中提取一个自治块?
Extract an autonomous chunk of the dependency graph of a huge CPP project?
考虑 Chromium
代码库。如果我没记错的话,它很大,大约 4gb 的纯代码。但是无论它多么庞大,它在本质上仍然是模块化的。它在内部实现了很多有趣的功能。
我的意思是,例如,我想从源代码中提取 websocket
实现,但手工操作并不容易。好的,如果我们转到 https://github.com/chromium/chromium/tree/main/net/websockets,我们会看到很多头文件。要将代码编译为“库”,我们需要它们 + 它们在 .cpp
文件中的实现。但诀窍是这些头文件 include
其他头文件在 chromium
项目的其他目录中。轮到他们 include
其他人...
但是 如果没有循环依赖,我们应该能够到达这棵树的根,头文件不会 include
任何东西(或将include
已编译的库),这应该意味着此依赖子树所需的所有文件都已就位,因此我们可以将原始代码库的一部分与其余部分分开编译。
就是这个主意。至少理论上是这样。
有人知道怎么做吗?我找到了 this repo and this repo,但它们只显示依赖关系图,没有从中提取树的功能。
我想应该已经有一个工具了。 google 很难说出来。或许我弄错了,这种方法实际上行不通?
您的编译器几乎肯定能够提取此依赖信息,以便它可用于帮助构建系统确定增量构建。例如,在 gcc
中,我们有 -MMD
标志。
假设我们有四个编译单元,ball.cpp
、football.cpp
、basketball.cpp
和hockey.cpp
。每个源文件都包含一个同名的头文件。此外,football.hpp
和 basketball.hpp
每个都包含 ball.hpp
.
如果我们运行
g++ -MMD -c -o football.o football.cpp
g++ -MMD -c -o basketball.o basketball.cpp
g++ -MMD -c -o hockey.o hockey.cpp
g++ -MMD -c -o ball.o ball.cpp
然后这将产生,除了目标文件之外,一些名称如 basketball.d
的文件包含依赖信息如
basketball.o: basketball.cpp basketball.h ball.h
将它们读入 python 脚本很简单,然后只需合并要包含的文件的所有依赖项即可。
编辑:事实上,python 甚至可能有点矫枉过正。在上面的情况下,如果你想获得任何包含单词“ball”的所有依赖项,你可以这样做
$ cat *.d | awk -F: ' ~ "ball" { print }' | xargs -n 1 echo | sort | uniq
这将输出
ball.cpp
ball.h
basketball.cpp
basketball.h
football.cpp
football.h
如果您不习惯阅读 UNIX 管道,这:
- 连接当前目录中的所有 *.d 文件;
- 遍历它们 line-by-line,将每行拆分为由
:
个字符分隔的字段;
- 为第一个字段(即目标)与正则表达式“ball”匹配的任何行打印出第二个字段(即依赖项列表);
- 将结果分成单独的行;
- 对结果行进行排序;和
- 丢弃任何重复项。
您可以看到,这生成了 ball-related 文件所依赖的所有内容的列表,但跳过了 hockey.cpp
和 hockey.hpp
,它们不是任何具有“ball”的文件的依赖项以它的名字。 (当然,在你的情况下,你可能会使用“websockets”而不是“ball”,如果有一些目录结构而不是根目录中的所有内容,你可能需要做一些补偿。)
考虑 Chromium
代码库。如果我没记错的话,它很大,大约 4gb 的纯代码。但是无论它多么庞大,它在本质上仍然是模块化的。它在内部实现了很多有趣的功能。
我的意思是,例如,我想从源代码中提取 websocket
实现,但手工操作并不容易。好的,如果我们转到 https://github.com/chromium/chromium/tree/main/net/websockets,我们会看到很多头文件。要将代码编译为“库”,我们需要它们 + 它们在 .cpp
文件中的实现。但诀窍是这些头文件 include
其他头文件在 chromium
项目的其他目录中。轮到他们 include
其他人...
但是 如果没有循环依赖,我们应该能够到达这棵树的根,头文件不会 include
任何东西(或将include
已编译的库),这应该意味着此依赖子树所需的所有文件都已就位,因此我们可以将原始代码库的一部分与其余部分分开编译。
就是这个主意。至少理论上是这样。
有人知道怎么做吗?我找到了 this repo and this repo,但它们只显示依赖关系图,没有从中提取树的功能。
我想应该已经有一个工具了。 google 很难说出来。或许我弄错了,这种方法实际上行不通?
您的编译器几乎肯定能够提取此依赖信息,以便它可用于帮助构建系统确定增量构建。例如,在 gcc
中,我们有 -MMD
标志。
假设我们有四个编译单元,ball.cpp
、football.cpp
、basketball.cpp
和hockey.cpp
。每个源文件都包含一个同名的头文件。此外,football.hpp
和 basketball.hpp
每个都包含 ball.hpp
.
如果我们运行
g++ -MMD -c -o football.o football.cpp
g++ -MMD -c -o basketball.o basketball.cpp
g++ -MMD -c -o hockey.o hockey.cpp
g++ -MMD -c -o ball.o ball.cpp
然后这将产生,除了目标文件之外,一些名称如 basketball.d
的文件包含依赖信息如
basketball.o: basketball.cpp basketball.h ball.h
将它们读入 python 脚本很简单,然后只需合并要包含的文件的所有依赖项即可。
编辑:事实上,python 甚至可能有点矫枉过正。在上面的情况下,如果你想获得任何包含单词“ball”的所有依赖项,你可以这样做
$ cat *.d | awk -F: ' ~ "ball" { print }' | xargs -n 1 echo | sort | uniq
这将输出
ball.cpp
ball.h
basketball.cpp
basketball.h
football.cpp
football.h
如果您不习惯阅读 UNIX 管道,这:
- 连接当前目录中的所有 *.d 文件;
- 遍历它们 line-by-line,将每行拆分为由
:
个字符分隔的字段; - 为第一个字段(即目标)与正则表达式“ball”匹配的任何行打印出第二个字段(即依赖项列表);
- 将结果分成单独的行;
- 对结果行进行排序;和
- 丢弃任何重复项。
您可以看到,这生成了 ball-related 文件所依赖的所有内容的列表,但跳过了 hockey.cpp
和 hockey.hpp
,它们不是任何具有“ball”的文件的依赖项以它的名字。 (当然,在你的情况下,你可能会使用“websockets”而不是“ball”,如果有一些目录结构而不是根目录中的所有内容,你可能需要做一些补偿。)