简体   繁体   English

如何将 c++20 模块与 CMake 一起使用?

[英]How to use c++20 modules with CMake?

Clang and MSVC already supports Modules TS from unfinished C++20 standard. ClangMSVC已经支持来自未完成的 C++20 标准的模块 TS Can I build my modules based project with CMake or other build system and how?我可以使用 CMake 或其他构建系统构建基于模块的项目吗?如何构建?

I tried build2 , it supports modules and it works very well, but i have a question about it's dependency management (UPD: question is closed).我试过build2 ,它支持模块并且工作得很好,但我对它的依赖管理有疑问(UPD:问题已关闭)。

CMake currently does not support C++20 modules. CMake 目前不支持 C++20 模块。

See also the relevant issue in the CMake issue tracker .另请参阅CMake 问题跟踪器中的相关问题 Note that supporting modules requires far more support from the build system than inserting a new compiler option.请注意,与插入新的编译器选项相比,支持模块需要来自构建系统的更多支持。 It fundamentally changes how dependencies between source files have to be handled during the build: In a pre-modules world all cpp source files can be built independently in any order.它从根本上改变了在构建过程中必须如何处理源文件之间的依赖关系:在预模块世界中,所有 cpp 源文件都可以按任何顺序独立构建。 With modules that is no longer true, which has implications not only for CMake itself, but also for the downstream build system.使用不再正确的模块,这不仅对 CMake 本身有影响,而且对下游构建系统也有影响。

Take a look at the CMake Fortran modules paper for the gory details.查看CMake Fortran 模块论文以了解详细信息。 From a build system's point of view, Fortran's modules behave very similar to the C++20 modules.从构建系统的角度来看,Fortran 模块的行为与 C++20 模块非常相似。

Update: CMake 3.20 introduces experimental support for Modules with the Ninja Generator (and only for Ninja).更新: CMake 3.20 引入了对带有 Ninja Generator 的模块的实验性支持(并且适用于 Ninja)。 Details can be found in the respective pull request .详细信息可以在相应的拉取请求中找到 At this stage, this feature is still highly experimental and not intended for production use.在此阶段,此功能仍处于高度试验阶段,打算用于生产用途。 If you intend to play around with this anyway, you really should be reading both the Fortran modules paper and the dependency format paper to understand what you're getting into.如果你无论如何都打算玩这个,你真的应该阅读Fortran 模块文件依赖格式文件来了解你正在进入的内容。

This works on Linux Manjaro (same as Arch), but should work on any Unix OS.这适用于 Linux Manjaro(与 Arch 相同),但应该适用于任何 Unix 操作系统。 Of course, you need to build with new clang (tested with clang-10).当然,您需要使用新的 clang 进行构建(使用 clang-10 进行测试)。

helloworld.cpp:你好世界.cpp:

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

main.cpp:主.cpp:

import helloworld;  // import declaration

int main() {
    hello();
}

CMakeLists.txt: 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)

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

This is tracked in this issue: https://gitlab.kitware.com/cmake/cmake/-/issues/18355这在这个问题中被跟踪: https://gitlab.kitware.com/cmake/cmake/-/issues/18355

There is also a CMakeCXXModules repository that adds support for modules to CMake.还有一个 CMakeCXXModules 存储库,它为 CMake 添加了对模块的支持。

https://github.com/NTSFka/CMakeCxxModules https://github.com/NTSFka/CMakeCxxModules

Assuming that you're using gcc 11 with a Makefile generator, the following code should work even without CMake support for C++20:假设您使用带有 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})

Some explanation:一些解释:

  1. A custom target is added to build the standard library modules, in case you want to include standard library header units (search for "Standard Library Header Units" here ).添加自定义目标以构建标准库模块,以防您想包含标准库头单元(在此处搜索“标准库头单元”)。 For simplicity, I just added iostream here.为简单起见,我只是在此处添加了iostream
  2. Next, a function is added to conveniently enable C++20 and Modules TS for targets接下来,添加一个函数,方便为目标启用 C++20 和 Modules TS
  3. We first create an object library to build the user modules我们首先创建一个对象库来构建用户模块
  4. Finally, we create our executable and link it to the object library created in the previous step.最后,我们创建可执行文件并将其链接到在上一步中创建的对象库。

Not consider the following main.cpp :不考虑以下main.cpp

import mymod;

int main() {
    helloModule();
}

and mymod.cpp :mymod.cpp

module;
export module mymod;

import <iostream>;

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

Using the above CMakeLists.txt , your example should compile fine (successfully tested in Ubuntu WSL with gcc 1.11.0).使用上面的CMakeLists.txt ,您的示例应该可以正常编译(在带有 gcc 1.11.0 的 Ubuntu WSL 中成功测试)。

Update: Sometimes when changing the CMakeLists.txt and recompiling, you may encounter an error更新:有时更改CMakeLists.txt并重新编译时,可能会遇到错误

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

Probably the reason is that every new module will attempt to build the standard library modules, but I'm not sure.可能的原因是每个新模块都会尝试构建标准库模块,但我不确定。 Unfortunately I didn't find a proper solution to this (avoiding rebuild if the gcm.cache directory already exists is bad if you want to add new standard modules, and doing it per-module is a maintenance nightmare).不幸的是,我没有找到合适的解决方案(如果您想添加新的标准模块,则在gcm.cache目录已经存在的情况下避免重建是不好的,并且每个模块都这样做是一个维护噩梦)。 My Q&D solution is to delete ${CMAKE_BINARY_DIR}/gcm.cache and rebuild the modules.我的问答解决方案是删除${CMAKE_BINARY_DIR}/gcm.cache并重建模块。 I'm happy for better suggestions though.不过,我很高兴有更好的建议。

I was not able to find Cmake support for modules.我找不到 Cmake 对模块的支持。 Here is an example how to use modules using clang.这是一个如何使用 clang 使用模块的示例。 I am using Mac and this example works ok on my system.我使用的是 Mac,这个例子在我的系统上运行正常。 It took me quite a while to figure this out so unsure how general this is across linux or Windows.我花了很长时间才弄明白这一点,所以不确定这在 linux 或 Windows 上有多普遍。

Source code in file driver.cxx文件 driver.cxx 中的源代码

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

Source code in file hello.cxx文件 hello.cxx 中的源代码

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

Source code in file hello.mxx文件 hello.mxx 中的源代码

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

And to compile the code with above source files, here are command lines on terminal并使用上述源文件编译代码,这里是终端上的命令行

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

and to get clean start on next compile并在下一次编译时干净地开始

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

expected output预期产出

./hello
Hello, Modules!

Hope this helps, all the best.希望这会有所帮助,一切顺利。

CMake does not currently support C++20 modules like the others have stated. CMake 目前不支持 C++20 模块,就像其他人所说的那样。 However, module support for Fortran is very similar, and perhaps this could be easily changed to support modules in C++20.但是,对 Fortran 的模块支持非常相似,也许可以轻松更改以支持 C++20 中的模块。

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

Now, perhaps there i an easy way to modify this to support C++20 directly.现在,也许有一种简单的方法可以修改它以直接支持 C++20。 Not sure.没有把握。 It is worth exploring and doing a pull request should you resolve it.如果你解决了它,值得探索和做一个拉取请求。

Add MSVC version (revised from @warchantua 's answer):添加 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)

While waiting for proper C++20 modules support in CMake, I've found that if using MSVC Windows, for right now you can make-believe it's there by hacking around the build instead of around CMakeLists.txt: continously generate with latest VS generator, and open/build the .sln with VS2020.在等待 CMake 中适当的 C++20 模块支持时,我发现如果使用 MSVC Windows,现在您可以通过围绕构建而不是围绕 CMakeLists.txt 进行黑客攻击来假装它就在那里:使用最新的 VS 持续生成生成器,并使用 VS2020 打开/构建 .sln。 The IFC dependency chain gets taken care of automatically ( import <iostream>; just works). IFC 依赖链被自动处理( import <iostream>;正常工作)。 Haven't tried Windows clang or cross-compiling.没有尝试过 Windows clang 或交叉编译。 It's not ideal but for now at least another decently workable alternative today, so far.这并不理想,但到目前为止,至少是今天另一个体面可行的替代方案。

Important afterthought: use .cppm and .ixx extensions.重要的事后思考:使用 .cppm 和 .ixx 扩展名。

With C++20 Modules the file compilation order matters, which is totally new.对于 C++20 模块,文件编译顺序很重要,这是全新的。 That's why the implementation is complicated and still experimental in 2023. Please read the authors blogpost这就是为什么实施很复杂并且在 2023 年仍处于实验阶段的原因。请阅读作者的

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM