如何生成C++20模块接口及可被外部项目引用的库包?
我希望使用C++20模块创建一个基础库,并在外部简单项目中导入它,重点关注基于模块的库的输出包。
根据CPPCon相关演讲的结论,最佳方案是创建模块接口文件以及.lib或.dll这类二进制库。我当前使用的基础CMakeLists.txt如下:
cmake_minimum_required(VERSION 3.28) project(MyLib LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_EXTENSIONS NO) set(CMAKE_CXX_STANDARD_REQUIRED YES) add_library(foo) target_sources(foo PUBLIC FILE_SET CXX_MODULES FILES foo.cppm)
foo.cppm的内容如下:
module; #include <iostream> export module foo; export class foo { public: foo() = default; ~foo() = default; void helloworld(); }; void foo::helloworld() { std::cout << "hello world\n"; }
目前构建后仅得到.lib文件作为输出。请问如何生成模块接口,以及如何制作可被消费项目使用的合格包?
假设消费项目使用完全相同的开发环境(CMake 3.28、MSVC 17.9.3),无需考虑二进制兼容性问题。
1. 模块接口文件的生成与定位
MSVC下C++20模块的接口文件是.ifc格式,CMake在构建模块时已经自动生成了该文件,默认存放在构建目录的中间文件路径中(比如build/Debug/foo.dir/下)。你需要通过CMake的安装规则将其导出到指定位置,方便消费项目访问。
2. 调整CMake配置以生成可消费的包
修改库的CMakeLists.txt,添加安装规则、导出配置,让CMake生成消费项目所需的模块接口、二进制库和配置文件:
cmake_minimum_required(VERSION 3.28) project(MyLib LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_EXTENSIONS NO) set(CMAKE_CXX_STANDARD_REQUIRED YES) add_library(foo) target_sources(foo PUBLIC FILE_SET CXX_MODULES FILES foo.cppm) # 设置安装目录,可根据需求调整 set(INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/install") set(CMAKE_INSTALL_PREFIX "${INSTALL_DIR}") # 安装库的二进制文件和模块接口 install(TARGETS foo EXPORT fooTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin FILE_SET CXX_MODULES DESTINATION cmake/modules) # 导出目标配置文件 install(EXPORT fooTargets FILE fooTargets.cmake NAMESPACE MyLib:: DESTINATION cmake) # 生成包配置文件,方便消费项目用find_package导入 include(CMakePackageConfigHelpers) configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/fooConfig.cmake" INSTALL_DESTINATION cmake) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/fooConfig.cmake" DESTINATION cmake)
需要在库的源码目录下创建Config.cmake.in文件,内容如下:
@PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/fooTargets.cmake") check_required_components(foo)
3. 构建并安装库
执行以下命令完成构建和安装:
cmake -S . -B build cmake --build build --config Debug cmake --install build --config Debug
安装完成后,install目录下会有:
lib/foo.lib:静态库文件cmake/modules/foo.ifc:模块接口文件cmake/fooConfig.cmake、cmake/fooTargets.cmake:CMake配置文件
4. 消费项目的配置
在外部消费项目的CMakeLists.txt中,通过find_package导入库并使用模块:
cmake_minimum_required(VERSION 3.28) project(ConsumerProject LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_EXTENSIONS NO) set(CMAKE_CXX_STANDARD_REQUIRED YES) # 指定库的安装目录路径 set(MyLib_DIR "/path/to/MyLib/install/cmake") find_package(MyLib REQUIRED) add_executable(consumer main.cpp) target_link_libraries(consumer PRIVATE MyLib::foo)
消费项目的main.cpp中直接导入模块使用:
import foo; int main() { foo f; f.helloworld(); return 0; }
5. 打包成可分发的包
如果需要生成可分发的安装包,可以使用CMake的CPack功能,在库的CMakeLists.txt末尾添加:
include(CPack)
然后执行:
cmake --build build --target package
会生成如MyLib-1.0.0-win64.zip之类的压缩包,包含所有必要的文件。
内容的提问来源于stack exchange,提问作者Erwan Douaille

