有没有一种方法可以使用gcc查询直接模块依赖关系?

6

给定一个模块

// a-m.cc
export module A;

import B;
import C;

import "D.h";

...

是否有一种调用gcc的方式(类似于对头文件执行的-M操作),可以列出其他模块和导入头文件的直接依赖关系(即B、C和“D.h”)?

3个回答

2

[编辑]

看起来我们可以使用标志-MMD调用gcc,它也可以跟踪模块依赖关系。以我拥有的一个示例项目为例,我是这样生成它的:

// partition.cpp
export module partition;

import :partition1;
export import :partition2;
export import :partition3;

export void Hello1() { _Hello1(); }

由于某些原因,我需要在编译主模块接口之前先编译分区模块,但或许可以找到某种方式来规避这个问题。我按照以下方式编译上述文件:
g++-11 -std=c++20 -fmodules-ts -c -MMD partition.cpp

这将生成一个名为partition.d的文件,列出模块之间的依赖关系:
partition.o gcm.cache/partition.gcm: partition.cpp
partition.o gcm.cache/partition.gcm: partition:partition3.c++m \
 partition:partition2.c++m partition:partition1.c++m
partition.c++m: gcm.cache/partition.gcm
.PHONY: partition.c++m
gcm.cache/partition.gcm:| partition.o
CXX_IMPORTS += partition:partition3.c++m partition:partition2.c++m \
 partition:partition1.c++m

看起来很有前途,但需要进行更多的研究。

我的解决方案

我已经编写/正在编写这样一个工具。它可以在github上找到:https://github.com/alexpanter/cpp_module_parser

它还没有完成,但实际上是可以使用的。如果有兴趣,我将继续扩展它。

我还有一堆带有模块的小型示例项目,旨在作为进一步研究的起点:https://github.com/alexpanter/modules_testing

GCC

GCC在本地目录中查找预编译的模块(BMI):./gcm.cache/usr/include/c++/11/iostream.gcm 或者(对于本地模块单元)./gcm.cache/,/my-module.gcm

如果用户代码导入了一个模块,那么预编译的模块单元必须已经存在于该目录中,否则编译将失败。很遗憾(至少目前为止)无法指定另一个目录或每个构建命令的自定义目录。这将非常实用,我希望GCC开发人员能在某个时候添加它。gcm.cache/目录由默认模块映射器使用。可以创建自己的模块映射器,但从我所读的内容来看,这听起来像是一个复杂的过程,因为模块映射器本质上是一个Web服务器:

比较

与@Laserskjöld的答案相比,我认为收集预处理器输出也是一种可行的解决方案,因为模块导入/导出命令可以被预处理器识别。但是,我认为这不是一个的解决方案,因为它比我编写的工具慢得多。例如:

module;

#include <iostream>

export module mymodule;

import myothermodule;

export
{
    [...]
}

预处理后,该文件将有大约100000行,并且所有行都需要由预处理器处理。但是使用我的工具(或者一个潜在更有效的工具),模块解析工具只会读取前9行,文件的其余部分将被忽略。 此外,拥有模块的目的是减少对于预处理器的依赖。


1
实际上,您可以使用纯文本文件作为模块映射器。例如,可以参考我为bazel支持所做的工作:https://github.com/rnburn/rules_cc_module/blob/main/util/driver/gcc.py#L17 - Ryan Burn
@RyanBurn 这是一个有趣的概念,但我无法让它工作 - 你有一个示例 Makefile 和一些 cpp 文件,以便我可以重现吗?此外,您是否必须为每个模块指定一行?因为我更愿意使用-fmodule-mapper来指定额外的导入目录。在较大规模上进行逐文件管理似乎有点棘手... - alexpanter
1
那个仓库有一个Docker镜像,您可以调用它来获得可重复的环境。是的,模块映射器的文件版本是每个模块的。对于大型项目,更细粒度的、每个文件的管理通常更好。 - Ryan Burn
@RyanBurn 啊,我以前从未使用过Docker :P 但是感谢你的建议,我可能会有一天去尝试一下。我对模块非常感兴趣!虽然我更喜欢有一个工具自动推断文件依赖关系。目前正在使用premake,并正在为添加gcc模块支持的提案进行工作 - 这可能需要一些时间。 - alexpanter
1
不确定。您可以尝试使用gcc的邮件列表或IRC频道寻求帮助。但是他们的模块支持相当有缺陷和不完整。 - Ryan Burn
显示剩余3条评论

0
GCC 14 支持使用 -fdeps-file=$depoutput -fdeps-format=p1689r5 标志输出 P1689R5。示例:
export module c;
import b;

export int c()
{
  return b();
}

$ g++ -E -fmodules-ts -MD -MF c.cxx.d -fdeps-file=/dev/stdout -fdeps-format=p1689r5 -fdeps-target=c.cxx.o c.cxx -o c.cxx.pp

将生成:
{
"rules": [
{
"primary-output": "c.cxx.o",
"provides": [
{
"logical-name": "c",
"is-interface": true
}
],
"requires": [
{
"logical-name": "b"
}
]
}
],
"version": 0,
"revision": 0
}

-1

我不知道gcc有任何方法可以做到这一点,但我建议编写一个帮助脚本来扩展文件并查找所有名为import的内容,然后您可能需要使用"<来对导入进行排序,并将其输出为头文件。

然后,您需要找到将所选模块导出到与您的导入匹配的文件。也就是说,您需要浏览扩展文件并查找以export module开头的语句。

您可以使用以下方式扩展文件:

g++ -E {include flags, standard version etc} > tmpfile

-E 在 clang++ 中同样适用,但对于 msvc,你需要使用 /E

如果有直接在 gcc 中完成这个的方法,我也很感兴趣,但是问题就出现了:gcc 如何知道在哪里查找模块?

相反,如果您有一个构建系统来跟踪每个模块的位置,您可以像编译命令那样指定它。 (使用 clang,您可以使用-fmodule-file= 指定,我认为在 gcc 中也是一样,但没有测试过)。至少这就是我在我的构建系统中完成的方式。

但是总结一下,回答您的问题。

  • 扩展您的源文件
  • 查找其中所有包含 importexport importexport module 的文件并进行处理。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接