簡體   English   中英

如何將 c++20 模塊與 CMake 一起使用?

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

ClangMSVC已經支持來自未完成的 C++20 標准的模塊 TS 我可以使用 CMake 或其他構建系統構建基於模塊的項目嗎?如何構建?

我試過build2 ,它支持模塊並且工作得很好,但我對它的依賴管理有疑問(UPD:問題已關閉)。

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

另請參閱CMake 問題跟蹤器中的相關問題 請注意,與插入新的編譯器選項相比,支持模塊需要來自構建系統的更多支持。 它從根本上改變了在構建過程中必須如何處理源文件之間的依賴關系:在預模塊世界中,所有 cpp 源文件都可以按任何順序獨立構建。 使用不再正確的模塊,這不僅對 CMake 本身有影響,而且對下游構建系統也有影響。

查看CMake Fortran 模塊論文以了解詳細信息。 從構建系統的角度來看,Fortran 模塊的行為與 C++20 模塊非常相似。

更新: CMake 3.20 引入了對帶有 Ninja Generator 的模塊的實驗性支持(並且適用於 Ninja)。 詳細信息可以在相應的拉取請求中找到 在此階段,此功能仍處於高度試驗階段,打算用於生產用途。 如果你無論如何都打算玩這個,你真的應該閱讀Fortran 模塊文件依賴格式文件來了解你正在進入的內容。

這適用於 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)

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

假設您使用帶有 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 ,您的示例應該可以正常編譯(在帶有 gcc 1.11.0 的 Ubuntu WSL 中成功測試)。

更新:有時更改CMakeLists.txt並重新編譯時,可能會遇到錯誤

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

可能的原因是每個新模塊都會嘗試構建標准庫模塊,但我不確定。 不幸的是,我沒有找到合適的解決方案(如果您想添加新的標准模塊,則在gcm.cache目錄已經存在的情況下避免重建是不好的,並且每個模塊都這樣做是一個維護噩夢)。 我的問答解決方案是刪除${CMAKE_BINARY_DIR}/gcm.cache並重建模塊。 不過,我很高興有更好的建議。

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

希望這會有所幫助,一切順利。

CMake 目前不支持 C++20 模塊,就像其他人所說的那樣。 但是,對 Fortran 的模塊支持非常相似,也許可以輕松更改以支持 C++20 中的模塊。

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

現在,也許有一種簡單的方法可以修改它以直接支持 C++20。 沒有把握。 如果你解決了它,值得探索和做一個拉取請求。

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

在等待 CMake 中適當的 C++20 模塊支持時,我發現如果使用 MSVC Windows,現在您可以通過圍繞構建而不是圍繞 CMakeLists.txt 進行黑客攻擊來假裝它就在那里:使用最新的 VS 持續生成生成器,並使用 VS2020 打開/構建 .sln。 IFC 依賴鏈被自動處理( import <iostream>;正常工作)。 沒有嘗試過 Windows clang 或交叉編譯。 這並不理想,但到目前為止,至少是今天另一個體面可行的替代方案。

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

對於 C++20 模塊,文件編譯順序很重要,這是全新的。 這就是為什么實施很復雜並且在 2023 年仍處於實驗階段的原因。請閱讀作者的

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM