41

ClangMSVC已经支持来自未完成的 C++20 标准的模块 TS 。我可以使用 CMake 或其他构建系统构建基于模块的项目吗?如何构建?

我尝试了build2,它支持模块并且运行良好,但我对它的依赖管理有疑问(UPD:问题已关闭)。

4

8 回答 8

25

CMake 目前不支持 C++20 模块。

另请参阅CMake 问题跟踪器中的相关问题。请注意,与插入新的编译器选项相比,支持模块需要来自构建系统的更多支持。它从根本上改变了在构建过程中必须如何处理源文件之间的依赖关系:在预模块世界中,所有 cpp 源文件都可以以任何顺序独立构建。对于不再适用的模块,这不仅对 CMake 本身有影响,而且对下游构建系统也有影响。

查看CMake Fortran 模块文件以了解详细信息。从构建系统的角度来看,Fortran 模块的行为与 C++20 模块非常相似。

更新: CMake 3.20 引入了对带有 Ninja Generator 的模块的实验性支持(并且适用于 Ninja)。可以在相应的拉取请求中找到详细信息。在现阶段,此功能仍处于高度试验阶段,适合生产使用。如果你打算玩这个,你真的应该阅读Fortran 模块文件依赖格式文件,以了解你正在进入的内容。

于 2020-04-16T06:55:29.033 回答
21

这适用于 Linux Manjaro(与 Arch 相同),但应该适用于任何 Unix 操作系统。当然,您需要使用新的 clang 构建(使用 clang-10 测试)。

你好世界.cpp:

export module helloworld;
import <cstdio>;
export void hello() { puts("Hello world!"); }

主.cpp:

import helloworld;  // import declaration

int main() {
    hello();
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)
project(main)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(PREBUILT_MODULE_PATH ${CMAKE_BINARY_DIR}/modules)

function(add_module name)
    file(MAKE_DIRECTORY ${PREBUILT_MODULE_PATH})
    add_custom_target(${name}.pcm
            COMMAND
                ${CMAKE_CXX_COMPILER}
                -std=c++20
                -stdlib=libc++
                -fmodules
                -c
                ${CMAKE_CURRENT_SOURCE_DIR}/${ARGN}
                -Xclang -emit-module-interface
                -o ${PREBUILT_MODULE_PATH}/${name}.pcm

            )
endfunction()


add_compile_options(-fmodules)
add_compile_options(-stdlib=libc++)
add_compile_options(-fbuiltin-module-map)
add_compile_options(-fimplicit-module-maps)
add_compile_options(-fprebuilt-module-path=${PREBUILT_MODULE_PATH})

add_module(helloworld helloworld.cpp)
add_executable(main
        main.cpp
        helloworld.cpp
        )
add_dependencies(main helloworld.pcm)

于 2020-06-21T14:22:16.860 回答
6

假设您使用带有 Makefile 生成器的 gcc 11,即使没有 CMake 对 C++20 的支持,以下代码也应该可以工作:

cmake_minimum_required(VERSION 3.19) # Lower versions should also be supported
project(cpp20-modules)

# Add target to build iostream module
add_custom_target(std_modules ALL
    COMMAND ${CMAKE_COMMAND} -E echo "Building standard library modules"
    COMMAND g++ -fmodules-ts -std=c++20 -c -x c++-system-header iostream
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

# Function to set up modules in GCC
function (prepare_for_module TGT)
    target_compile_options(${TGT} PUBLIC -fmodules-ts)
    set_property(TARGET ${TGT} PROPERTY CXX_STANDARD 20)
    set_property(TARGET ${TGT} PROPERTY CXX_EXTENSIONS OFF)
    add_dependencies(${TGT} std_modules)
endfunction()

# Program name and sources
set (TARGET prog)
set (SOURCES main.cpp)
set (MODULES mymod.cpp)

# Setup program modules object library
set (MODULE_TARGET prog-modules)
add_library(${MODULE_TARGET} OBJECT ${MODULES})
prepare_for_module(${MODULE_TARGET})

# Setup executable
add_executable(${TARGET} ${SOURCES})
prepare_for_module(${TARGET})

# Add modules to application using object library
target_link_libraries(${TARGET} PRIVATE ${MODULE_TARGET})

一些解释:

  1. 添加自定义目标以构建标准库模块,以防您想要包含标准库头单元(在此处搜索“标准库头单元” )。为简单起见,我只是iostream在这里添加。
  2. 接下来,添加一个功能,方便为目标启用 C++20 和 Modules TS
  3. 我们首先创建一个对象库来构建用户模块
  4. 最后,我们创建可执行文件并将其链接到上一步中创建的对象库。

不考虑以下情况main.cpp

import mymod;

int main() {
    helloModule();
}

mymod.cpp

module;
export module mymod;

import <iostream>;

export void helloModule() {
    std::cout << "Hello module!\n";
}

使用上述CMakeLists.txt内容,您的示例应该可以正常编译(在 Ubuntu WSL 中使用 gcc 1.11.0 成功测试)。

更新: 有时在更改CMakeLists.txt和重新编译时,您可能会遇到错误

error: import "/usr/include/c++/11/iostream" has CRC mismatch

可能原因是每个新模块都会尝试构建标准库模块,但我不确定。不幸的是,我没有找到合适的解决方案(gcm.cache如果要添加新的标准模块,如果目录已经存在,则避免重建是不好的,并且为每个模块执行此操作是维护的噩梦)。我的 Q&D 解决方案是删除${CMAKE_BINARY_DIR}/gcm.cache和重建模块。不过,我很高兴有更好的建议。

于 2021-05-05T20:01:08.617 回答
2

我找不到对模块的 Cmake 支持。这是一个如何使用 clang 使用模块的示例。我正在使用 Mac,这个示例在我的系统上运行良好。我花了很长时间才弄清楚这一点,所以不确定这在 linux 或 Windows 上有多普遍。

文件 driver.cxx 中的源代码

import hello;
int main() { say_hello("Modules"); } 

文件 hello.cxx 中的源代码

#include <iostream>
module hello;
void say_hello(const char *n) {
  std::cout << "Hello, " << n << "!" << std::endl;
}

文件 hello.mxx 中的源代码

export module hello;
export void say_hello (const char* name);

并使用上述源文件编译代码,这里是终端上的命令行

clang++ \
  -std=c++2a                        \
  -fmodules-ts                      \
  --precompile                      \
  -x c++-module                     \
  -Xclang -fmodules-embed-all-files \
  -Xclang -fmodules-codegen         \
  -Xclang -fmodules-debuginfo       \
  -o hello.pcm hello.mxx

clang++ -std=c++2a -fmodules-ts -o hello.pcm.o -c hello.pcm

clang++ -std=c++2a -fmodules-ts -x c++ -o hello.o \
  -fmodule-file=hello.pcm -c hello.cxx

clang++ -std=c++2a -fmodules-ts -x c++ -o driver.o \
  -fmodule-file=hello=hello.pcm -c driver.cxx

clang++ -o hello hello.pcm.o driver.o hello.o

并在下一次编译时获得干净的开始

rm -f *.o
rm -f hello
rm -f hello.pcm

预期产出

./hello
Hello, Modules!

希望这会有所帮助,一切顺利。

于 2020-02-06T05:04:45.647 回答
1

在等待 CMake 中适当的 C++20 模块支持时,我发现如果使用 MSVC Windows,现在您可以通过围绕构建而不是围绕 CMakeLists.txt 进行黑客攻击来假装它就在那里:使用最新的 VS 持续生成生成器,并使用 VS2020 打开/构建 .sln。IFC 依赖链得到自动处理(import <iostream>;正常工作)。没有尝试过 Windows clang 或交叉编译。这并不理想,但到目前为止,至少是今天另一个体面可行的替代方案。

重要的事后思考:使用 .cppm 和 .ixx 扩展名。

于 2022-01-22T18:36:00.993 回答
0

CMake 附带对 C++20 模块的实验性支持: https ://gitlab.kitware.com/cmake/cmake/-/blob/master/Help/dev/experimental.rst

还有一个 CMakeCXXModules 存储库,为 CMake 添加了对模块的支持。

https://github.com/NTSFka/CMakeCxxModules

于 2022-02-14T22:51:02.253 回答
0

添加 MSVC 版本(修改自 @warchantua 的答案):

cmake_minimum_required(VERSION 3.16)

project(Cpp20)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(PREBUILT_MODULE_DIR ${CMAKE_BINARY_DIR}/modules)
set(STD_MODULES_DIR "D:/MSVC/VC/Tools/MSVC/14.29.30133/ifc/x64") # macro "$(VC_IFCPath)" in MSVC

function(add_module name)
    file(MAKE_DIRECTORY ${PREBUILT_MODULE_DIR})
    add_custom_target(${name}.ifc
            COMMAND
                ${CMAKE_CXX_COMPILER}
                /std:c++latest
                /stdIfcDir ${STD_MODULES_DIR}
                /experimental:module
                /c
                /EHsc
                /MD
                ${CMAKE_CURRENT_SOURCE_DIR}/${ARGN}
                /module:export
                /ifcOutput
                ${PREBUILT_MODULE_DIR}/${name}.ifc
                /Fo${PREBUILT_MODULE_DIR}/${name}.obj
            )
endfunction()

set(CUSTOM_MODULES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/modules)
add_module(my_module ${CUSTOM_MODULES_DIR}/my_module.ixx)

add_executable(test
    test.cpp
    )
target_compile_options(test
    BEFORE
    PRIVATE
    /std:c++latest
    /experimental:module
    /stdIfcDir ${STD_MODULES_DIR}
    /ifcSearchDir ${PREBUILT_MODULE_DIR}
    /reference my_module=${PREBUILT_MODULE_DIR}/my_module.ifc
    /EHsc
    /MD
)
target_link_libraries(test ${PREBUILT_MODULE_DIR}/my_module.obj)
add_dependencies(test my_module.ifc)
于 2022-01-06T05:26:22.220 回答
-1

CMake 目前不像其他人所说的那样支持 C++20 模块。但是,对 Fortran 的模块支持非常相似,也许可以很容易地将其更改为支持 C++20 中的模块。

http://fortranwiki.org/fortran/show/Build+tools

现在,也许我有一种简单的方法可以修改它以直接支持 C++20。没有把握。如果您解决了它,值得探索并提出拉取请求。

于 2020-04-30T11:22:39.907 回答