Search code examples
c++cmakec++20clionc++-modules

Does CMake support converting headers to header modules?


I've just started to learn programming in C++ and the books I use for that focus on the new module system to replace the #include method.

For make I've found Makefiles that allow to first convert the #includes (like iostream for example) into a module file (g++ gives these a .gcm extension) and then use those to compile the binary.

Now since I'm using CLion and it's use of CMake it would be great to be able to do this with CMake but I can only find information on how to do this by making a personal module file that #includes the header and exports the requested functionality by using a function.

Do anyone know if this is (already) possible or do we have to wait for GCC or CLANG to include standard library modules (like print for C++20, or std for C++23) ?

The majority of the information I found solve it like this:

main.cpp

import foo;

int main() {
  print("Hello, World!\n");
}

foo.cppm

module;
#include <iostream>
#include <string>

export module foo;

export void print(std::string s) { std::cout << s; }

CMakeLists.txt

cmake_minimum_required(VERSION 3.28)
project(cmake_module_example CXX)

set(CMAKE_CXX_STANDARD 23)

# Create a library
add_library(my_modules)
# Add the module file to the library
target_sources(my_modules
  PUBLIC
    FILE_SET CXX_MODULES FILES
        foo.cppm
)

# Create an executable
add_executable(hello main.cpp
        main.cpp)
# Link to the library foo
target_link_libraries(hello my_modules)

I'm using Debian 12 and tried with gcc-14 and clang-17 The build tool that CLion uses is ninja, and unless they ship with a newer version the current version is 1.11.1


Solution

  • It is finally there behind an experimental flag in CMake 3.30: https://www.kitware.com/import-std-in-cmake-3-30/

    # CMake 3.30 is required for C++23 `import std` support; we use 3.29.20240416
    # here so that in-development versions satisfy it. 
    cmake_minimum_required(VERSION 3.29.20240416 FATAL_ERROR)
    
    # Set experimental flag to enable `import std` support from CMake.
    # This must be enabled before C++ language support.
    set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD
      # This specific value changes as experimental support evolves. See
      # `Help/dev/experimental.rst` in the CMake source corresponding to
      # your CMake build for the exact value to use.
      "0e5b6991-d74f-4b3d-a41c-cf096e0b2508")
    
    # C++ needs to be enabled.
    project(import_std LANGUAGES CXX)
    
    # Tell CMake that we explicitly want `import std`. This will initialize the
    # property on all targets declared after this to 1
    set(CMAKE_CXX_MODULE_STD 1)
    
    # Make a library.
    add_library(uses_std STATIC)
    # Add sources.
    target_sources(uses_std
      PRIVATE
        uses_std.cxx)
    # Tell CMake we're using C++23 but only C++20 is needed to consume it.
    target_compile_features(uses_std
      PRIVATE   cxx_std_23
      INTERFACE cxx_std_20)
    
    # Make an executable.
    add_executable(main)
    # Note that this source is *not* allowed to `import std` as it ends up
    # with only C++20 support due to the `uses_std` INTERFACE requirements.
    target_sources(main
      PRIVATE
        main.cxx)
    target_link_libraries(main PRIVATE uses_std)
    

    Then you can just import std; and use the Standard Library. Note that importing everything is very fast compared to headers, so there is no need to worry about compiler performance.