有没有办法用 gcc 查询直接模块依赖关系?

Is there a way to query direct module dependencies with gcc?

给定一个模块

// a-m.cc
export module A;

import B;
import C;

import "D.h";

...

有没有一种调用 gcc 的方法(类似于 -M 对 headers 的作用),它将列出对其他模块的直接依赖关系并导入 headers(即 B、C 和“ D.h")?

我不知道 gcc 有什么方法可以做到这一点,但我建议编写一个辅助脚本来扩展文件并查找名为 import 的所有内容,然后您可能需要使用 [= 对导入进行排序12=] 和 < 并将它们输出为 headers.

然后您需要找到导出所选模块的文件以与您的导入相匹配。也就是说,您需要遍历扩展文件并找到以 export module.

开头的语句

您使用

展开文件
g++ -E {include flags, standard version etc} > tmpfile

-E 也适用于 clang++,但对于 msvc,您需要 /E

如果有直接的方法可以用 gcc 做到这一点,我也很想知道,但随之而来的问题是:gcc 如何知道在哪里寻找模块?

相反,如果您有一个跟踪每个模块所在位置的构建系统,您可以将其指定为编译命令。 (使用 clang,您可以使用 -fmodule-file= 指定它,我认为它与 gcc 相同,但尚未测试)。至少那是我在 my build system.

中所做的

但总结一下我对你问题的回答。 我认为您需要:

  • 扩展您的源文件
  • 找到所有包含 importexport importexport module 的文件并处理它们。

[编辑]

看来我们可以使用标志 -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 在本地目录中查找预编译模块 (BMI):./gcm.cache/usr/include/c++/11/iostream.gcm 或(对于本地模块单元)./gcm.cache/,/my-module.gcm.

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

比较

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

module;

#include <iostream>

export module mymodule;

import myothermodule;

export
{
    [...]
}

预处理后,此文件将有 ~100000 行长,所有行都需要由预处理程序处理。但是使用我的工具(或可能更有效的工具),模块解析工具只会读取前 9 行,文件的其余部分将被忽略。 此外,拥有模块的目的是减少对预处理器的依赖。