如何在CMake中使用C++20模块?

bbmckpt7  于 2023-02-01  发布在  其他
关注(0)|答案(9)|浏览(1046)

ClangMSVC已经支持未完成的C++20标准中的Modules TS。我可以使用CMake或其他构建系统构建基于模块的项目吗?
我尝试了build2,它支持模块,工作非常好,但我有一个关于它的依赖性管理的question(UPD:问题已关闭)。

laximzn5

laximzn51#

CMake目前不支持C20模块。
另请参阅the relevant issue in the CMake issue tracker。请注意,支持模块比插入新编译器选项需要构建系统提供更多的支持。它从根本上改变了构建期间处理源文件之间依赖关系的方式:在前模块时代,所有的cpp源文件都可以按照任何顺序独立构建,而模块不再是这样,这不仅对CMake本身,而且对下游的构建系统都有影响。
看看CMake Fortran modules paper的血腥细节,从构建系统的Angular 来看,Fortran模块的行为与C
20模块非常相似。

**更新:*CMake 3.20引入了对Ninja Generator模块的实验性支持(并且 * 仅 * 用于Ninja)。详细信息可以在respective pull request中找到。在这个阶段,这个特性仍然是高度实验性的, 不 * 打算用于生产用途。如果你打算玩这个游戏,你真的应该阅读Fortran modules paperdependency format paper来了解你正在进入的内容。

m528fe3b

m528fe3b2#

这可以在Linux Manjaro(和Arch一样)上工作,但是应该可以在任何Unix操作系统上工作。当然,你需要用新的clang(用clang-10测试过)来构建。
helloworld.cpp:

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

main.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)
oxosxuxt

oxosxuxt3#

假设您使用的是带有Makefile生成器的gcc 11,那么下面的代码即使在C++20没有CMake支持的情况下也应该可以运行:

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.如果您希望包含标准库头单元(搜索"Standard Library Header Units" here),则添加一个定制目标来构建标准库模块。
1.接下来,添加一个函数,以便为目标启用C++20和TS模块
1.我们首先创建一个对象库来构建用户模块
1.最后,我们创建可执行文件并将其链接到上一步中创建的对象库。
不考虑以下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并重建模块。我很高兴有更好的建议。

v64noz0r

v64noz0r4#

CMake提供对C++20模块的实验性支持:https://gitlab.kitware.com/cmake/cmake/-/blob/master/Help/dev/experimental.rst
本期对此进行了跟踪:https://gitlab.kitware.com/cmake/cmake/-/issues/18355
还有一个CMakeCXXModules存储库,它为CMake添加了对模块的支持。
https://github.com/NTSFka/CMakeCxxModules

lo8azlld

lo8azlld5#

在等待CMake中适当的C++20模块支持的同时,我发现如果使用MSVC Windows,现在你可以通过围绕构建而不是围绕CMakeLists.txt来假装它在那里:用最新的VS生成器持续生成,用VS 2020打开/构建.sln。IFC依赖链会自动得到处理(import <iostream>;只是工作)。还没有尝试过Windows clang或交叉编译。这不是理想的,但至少现在是另一个体面的可行的替代品。
重要的事后思考:使用.cppm和.ixx扩展名。

qqrboqgw

qqrboqgw6#

CMake目前并不像其他人所说的那样支持C20模块。然而,Fortran的模块支持非常相似,也许这可以很容易地改变为支持C20中的模块。
http://fortranwiki.org/fortran/show/Build+tools
现在,也许我有一个简单的方法来修改这个直接支持C++20。不确定。这是值得探索和做一个拉请求,如果你解决它。

zvokhttg

zvokhttg7#

添加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)
k5ifujac

k5ifujac8#

我找不到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!

希望这对你有帮助,一切顺利。

jjhzyzn0

jjhzyzn09#

在C++20模块中,文件编译顺序很重要,这是全新的。这就是为什么实现很复杂,并且在2023年仍处于试验阶段。请阅读作者blogpost

相关问题