[英]How to use c++20 modules with CMake?
Clang and MSVC already supports Modules TS from unfinished C++20 standard. Clang和MSVC已经支持来自未完成的 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:一些解释:
iostream
here.为简单起见,我只是在此处添加了iostream
。 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 扩展名。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.