import std in CMake 3.30

April 18, 2024

Since C++ named modules left the experimental state in CMake (see import CMake; the Experiment is Over!), CMake development has continued to add more support for modules. The most recent development is the experimental support of import std;. C++23 introduced support for APIs to be available via the named module std. This means that import std; is a valid way to get access to all non-macro C++ APIs instead of including headers one-by-one. This is great news, however to use modules the build system needs to be involved to create the BMI files before the import can happen.

The CMake support for named modules included the ability to export targets for later import. The solution leverages that support with toolchains declaring a target to represent the standard library for each standard. This target is used inside of CMake’s own logic and added to projects automatically; projects do not need to mention the targets in project code at all. When CMake encounters a target which has at least C++23 enabled that also supports C++ modules1, it will query the CXX_MODULE_STD property on the target (which may contain generator expressions2). This property is initialized by the CMAKE_CXX_MODULE_STD variable, if defined. If it is unset, the current behavior is to not use the target. The internal target is not included in any export set or transitive usage requirements as every target (IMPORTED or not) will be recomputed to have this dependency automatically in the generated code as CMake will export the CXX_MODULE_STD property as needed. Projects may detect support for import std with the current toolchain by checking whether the language level (e.g., 23) is in the CMAKE_CXX_COMPILER_IMPORT_STD variable.

Here is an example showcasing some basic usage (here, where consuming a target that imports std itself does not need to know about it because it happens within a source file). Tested with LLVM 18.1.1 (using libc++ with this patch) and MSVC 14.35.

# 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)

With the following uses_std.cxx source:

import std;

void hello_world(std::string const& name)
{
  std::cout << "Hello World! My name is " << name << std::endl;
}

And the executable’s source:

#include <string>

void hello_world(std::string const& name);

int main(int argc, char* argv[])
{
  hello_world(argv[0] ? argv[0] : "Voldemort?");
  return 0;
}

Here are the (anonymized) build commands from LLVM and MSVC compilations of this project:

[1/14] "CLANG_ROOT/bin/clang-scan-deps" -format=p1689 -- CLANG_ROOT/bin/clang++   -stdlib=libc++ -std=gnu++23 -x c++ SRC_DIR/uses_std.cxx -c -o CMakeFiles/uses_std.dir/uses_std.cxx.o -resource-dir "CLANG_ROOT/lib/clang/19" -MT CMakeFiles/uses_std.dir/uses_std.cxx.o.ddi -MD -MF CMakeFiles/uses_std.dir/uses_std.cxx.o.ddi.d > CMakeFiles/uses_std.dir/uses_std.cxx.o.ddi.tmp && mv CMakeFiles/uses_std.dir/uses_std.cxx.o.ddi.tmp CMakeFiles/uses_std.dir/uses_std.cxx.o.ddi
[2/14] "CLANG_ROOT/bin/clang-scan-deps" -format=p1689 -- CLANG_ROOT/bin/clang++  -ICLANG_ROOT/bin/../lib/x86_64-unknown-linux-gnu/../../share/libc++/v1 -stdlib=libc++ -std=gnu++23 -Wno-reserved-module-identifier -x c++ CLANG_ROOT/share/libc++/v1/std.compat.cppm -c -o CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.compat.cppm.o -resource-dir "CLANG_ROOT/lib/clang/19" -MT CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.compat.cppm.o.ddi -MD -MF CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.compat.cppm.o.ddi.d > CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.compat.cppm.o.ddi.tmp && mv CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.compat.cppm.o.ddi.tmp CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.compat.cppm.o.ddi
[3/14] "CLANG_ROOT/bin/clang-scan-deps" -format=p1689 -- CLANG_ROOT/bin/clang++   -stdlib=libc++ -std=gnu++20 -x c++ SRC_DIR/main.cxx -c -o CMakeFiles/main.dir/main.cxx.o -resource-dir "CLANG_ROOT/lib/clang/19" -MT CMakeFiles/main.dir/main.cxx.o.ddi -MD -MF CMakeFiles/main.dir/main.cxx.o.ddi.d > CMakeFiles/main.dir/main.cxx.o.ddi.tmp && mv CMakeFiles/main.dir/main.cxx.o.ddi.tmp CMakeFiles/main.dir/main.cxx.o.ddi
[4/14] CMAKE_ROOT/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/main.dir/CXXDependInfo.json --lang=CXX --modmapfmt=clang --dd=CMakeFiles/main.dir/CXX.dd @CMakeFiles/main.dir/CXX.dd.rsp
[5/14] "CLANG_ROOT/bin/clang-scan-deps" -format=p1689 -- CLANG_ROOT/bin/clang++  -ICLANG_ROOT/bin/../lib/x86_64-unknown-linux-gnu/../../share/libc++/v1 -stdlib=libc++ -std=gnu++23 -Wno-reserved-module-identifier -x c++ CLANG_ROOT/share/libc++/v1/std.cppm -c -o CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.cppm.o -resource-dir "CLANG_ROOT/lib/clang/19" -MT CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.cppm.o.ddi -MD -MF CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.cppm.o.ddi.d > CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.cppm.o.ddi.tmp && mv CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.cppm.o.ddi.tmp CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.cppm.o.ddi
[6/14] CMAKE_ROOT/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/__cmake_cxx23.dir/CXXDependInfo.json --lang=CXX --modmapfmt=clang --dd=CMakeFiles/__cmake_cxx23.dir/CXX.dd @CMakeFiles/__cmake_cxx23.dir/CXX.dd.rsp
[7/14] CMAKE_ROOT/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/uses_std.dir/CXXDependInfo.json --lang=CXX --modmapfmt=clang --dd=CMakeFiles/uses_std.dir/CXX.dd @CMakeFiles/uses_std.dir/CXX.dd.rsp
[8/14] CLANG_ROOT/bin/clang++   -stdlib=libc++ -std=gnu++20 -MD -MT CMakeFiles/main.dir/main.cxx.o -MF CMakeFiles/main.dir/main.cxx.o.d @CMakeFiles/main.dir/main.cxx.o.modmap -o CMakeFiles/main.dir/main.cxx.o -c SRC_DIR/main.cxx
[9/14] CLANG_ROOT/bin/clang++  -ICLANG_ROOT/bin/../lib/x86_64-unknown-linux-gnu/../../share/libc++/v1 -stdlib=libc++ -std=gnu++23 -Wno-reserved-module-identifier -MD -MT CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.cppm.o -MF CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.cppm.o.d @CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.cppm.o.modmap -o CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.cppm.o -c CLANG_ROOT/share/libc++/v1/std.cppm
[10/14] CLANG_ROOT/bin/clang++  -ICLANG_ROOT/bin/../lib/x86_64-unknown-linux-gnu/../../share/libc++/v1 -stdlib=libc++ -std=gnu++23 -Wno-reserved-module-identifier -MD -MT CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.compat.cppm.o -MF CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.compat.cppm.o.d @CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.compat.cppm.o.modmap -o CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.compat.cppm.o -c CLANG_ROOT/share/libc++/v1/std.compat.cppm
[11/14] : && CMAKE_ROOT/bin/cmake -E rm -f lib__cmake_cxx23.a && CLANG_ROOT/bin/llvm-ar qc lib__cmake_cxx23.a  CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.cppm.o CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.compat.cppm.o && CLANG_ROOT/bin/llvm-ranlib lib__cmake_cxx23.a && :
[12/14] CLANG_ROOT/bin/clang++   -stdlib=libc++ -std=gnu++23 -MD -MT CMakeFiles/uses_std.dir/uses_std.cxx.o -MF CMakeFiles/uses_std.dir/uses_std.cxx.o.d @CMakeFiles/uses_std.dir/uses_std.cxx.o.modmap -o CMakeFiles/uses_std.dir/uses_std.cxx.o -c SRC_DIR/uses_std.cxx
[13/14] : && CMAKE_ROOT/bin/cmake -E rm -f libuses_std.a && CLANG_ROOT/bin/llvm-ar qc libuses_std.a  CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.cppm.o CMakeFiles/__cmake_cxx23.dir/CLANG_ROOT/share/libc++/v1/std.compat.cppm.o CMakeFiles/uses_std.dir/uses_std.cxx.o && CLANG_ROOT/bin/llvm-ranlib libuses_std.a && :
[14/14] : && CLANG_ROOT/bin/clang++ -stdlib=libc++ -Wl,-rpath,CLANG_ROOT/lib/x86_64-unknown-linux-gnu CMakeFiles/main.dir/main.cxx.o -o main  libuses_std.a && :
[1/14] VC_ROOT\bin\Hostx64\x64\cl.exe   /DWIN32 /D_WINDOWS /EHsc /Ob0 /Od /RTC1 -std:c++latest -MDd -Zi SRC_DIR\uses_std.cxx -nologo -TP -showIncludes -scanDependencies CMakeFiles\uses_std.dir\uses_std.cxx.obj.ddi -FoCMakeFiles\uses_std.dir\uses_std.cxx.obj
[2/14] VC_ROOT\bin\Hostx64\x64\cl.exe   /DWIN32 /D_WINDOWS /EHsc /Ob0 /Od /RTC1 -std:c++20 -MDd -Zi SRC_DIR\main.cxx -nologo -TP -showIncludes -scanDependencies CMakeFiles\main.dir\main.cxx.obj.ddi -FoCMakeFiles\main.dir\main.cxx.obj
[3/14] VC_ROOT\bin\Hostx64\x64\cl.exe   /DWIN32 /D_WINDOWS /EHsc /Ob0 /Od /RTC1 -std:c++latest -MDd -Zi "TOOLCHAIN_ROOT\modules\std.compat.ixx" -nologo -TP -showIncludes -scanDependencies CMakeFiles\__cmake_cxx23.dir\C_\Program_Files\Microsoft_Visual_Studio\2022\Community\VC\Tools\MSVC\14.36.32532\modules\std.compat.ixx.obj.ddi -FoCMakeFiles\__cmake_cxx23.dir\C_\Program_Files\Microsoft_Visual_Studio\2022\Community\VC\Tools\MSVC\14.36.32532\modules\std.compat.ixx.obj std.compat.ixx
[4/14] CMAKE_ROOT\bin\cmake.exe -E cmake_ninja_dyndep --tdi=CMakeFiles\main.dir\CXXDependInfo.json --lang=CXX --modmapfmt=msvc --dd=CMakeFiles\main.dir\CXX.dd @CMakeFiles\main.dir\CXX.dd.rsp
[5/14] VC_ROOT\bin\Hostx64\x64\cl.exe   /DWIN32 /D_WINDOWS /EHsc /Ob0 /Od /RTC1 -std:c++latest -MDd -Zi "TOOLCHAIN_ROOT\modules\std.ixx" -nologo -TP -showIncludes -scanDependencies CMakeFiles\__cmake_cxx23.dir\C_\Program_Files\Microsoft_Visual_Studio\2022\Community\VC\Tools\MSVC\14.36.32532\modules\std.ixx.obj.ddi -FoCMakeFiles\__cmake_cxx23.dir\C_\Program_Files\Microsoft_Visual_Studio\2022\Community\VC\Tools\MSVC\14.36.32532\modules\std.ixx.obj std.ixx
[6/14] CMAKE_ROOT\bin\cmake.exe -E cmake_ninja_dyndep --tdi=CMakeFiles\__cmake_cxx23.dir\CXXDependInfo.json --lang=CXX --modmapfmt=msvc --dd=CMakeFiles\__cmake_cxx23.dir\CXX.dd @CMakeFiles\__cmake_cxx23.dir\CXX.dd.rsp
[7/14] CMAKE_ROOT\bin\cmake.exe -E cmake_ninja_dyndep --tdi=CMakeFiles\uses_std.dir\CXXDependInfo.json --lang=CXX --modmapfmt=msvc --dd=CMakeFiles\uses_std.dir\CXX.dd @CMakeFiles\uses_std.dir\CXX.dd.rsp
[8/14] VC_ROOT\bin\Hostx64\x64\cl.exe  /nologo /TP   /DWIN32 /D_WINDOWS /EHsc /Ob0 /Od /RTC1 -std:c++20 -MDd -Zi /showIncludes @CMakeFiles\main.dir\main.cxx.obj.modmap /FoCMakeFiles\main.dir\main.cxx.obj /FdCMakeFiles\main.dir\ /FS -c SRC_DIR\main.cxx
[9/14] VC_ROOT\bin\Hostx64\x64\cl.exe  /nologo /TP   /DWIN32 /D_WINDOWS /EHsc /Ob0 /Od /RTC1 -std:c++latest -MDd -Zi /showIncludes @CMakeFiles\__cmake_cxx23.dir\C_\Program_Files\Microsoft_Visual_Studio\2022\Community\VC\Tools\MSVC\14.36.32532\modules\std.ixx.obj.modmap /FoCMakeFiles\__cmake_cxx23.dir\C_\Program_Files\Microsoft_Visual_Studio\2022\Community\VC\Tools\MSVC\14.36.32532\modules\std.ixx.obj /FdCMakeFiles\__cmake_cxx23.dir\__cmake_cxx23.pdb /FS -c "TOOLCHAIN_ROOT\modules\std.ixx" std.ixx
[10/14] VC_ROOT\bin\Hostx64\x64\cl.exe  /nologo /TP   /DWIN32 /D_WINDOWS /EHsc /Ob0 /Od /RTC1 -std:c++latest -MDd -Zi /showIncludes @CMakeFiles\__cmake_cxx23.dir\C_\Program_Files\Microsoft_Visual_Studio\2022\Community\VC\Tools\MSVC\14.36.32532\modules\std.compat.ixx.obj.modmap /FoCMakeFiles\__cmake_cxx23.dir\C_\Program_Files\Microsoft_Visual_Studio\2022\Community\VC\Tools\MSVC\14.36.32532\modules\std.compat.ixx.obj /FdCMakeFiles\__cmake_cxx23.dir\__cmake_cxx23.pdb /FS -c "TOOLCHAIN_ROOT\modules\std.compat.ixx" std.compat.ixx
[11/14] C:\Windows\system32\cmd.exe /C "cd . && VC_ROOT\bin\Hostx64\x64\lib.exe /nologo /machine:x64 /out:__cmake_cxx23.lib CMakeFiles\__cmake_cxx23.dir\C_\Program_Files\Microsoft_Visual_Studio\2022\Community\VC\Tools\MSVC\14.36.32532\modules\std.ixx.obj CMakeFiles\__cmake_cxx23.dir\C_\Program_Files\Microsoft_Visual_Studio\2022\Community\VC\Tools\MSVC\14.36.32532\modules\std.compat.ixx.obj && cd ."
[12/14] VC_ROOT\bin\Hostx64\x64\cl.exe  /nologo /TP   /DWIN32 /D_WINDOWS /EHsc /Ob0 /Od /RTC1 -std:c++latest -MDd -Zi /showIncludes @CMakeFiles\uses_std.dir\uses_std.cxx.obj.modmap /FoCMakeFiles\uses_std.dir\uses_std.cxx.obj /FdCMakeFiles\uses_std.dir\uses_std.pdb /FS -c SRC_DIR\uses_std.cxx
[13/14] C:\Windows\system32\cmd.exe /C "cd . && VC_ROOT\bin\Hostx64\x64\lib.exe /nologo /machine:x64 /out:uses_std.lib CMakeFiles\__cmake_cxx23.dir\C_\Program_Files\Microsoft_Visual_Studio\2022\Community\VC\Tools\MSVC\14.36.32532\modules\std.ixx.obj CMakeFiles\__cmake_cxx23.dir\C_\Program_Files\Microsoft_Visual_Studio\2022\Community\VC\Tools\MSVC\14.36.32532\modules\std.compat.ixx.obj CMakeFiles\uses_std.dir\uses_std.cxx.obj && cd ."
[14/14] C:\Windows\system32\cmd.exe /C "cd . && CMAKE_ROOT\bin\cmake.exe -E vs_link_exe --intdir=CMakeFiles\main.dir --rc=SDK_ROOT\x64\rc.exe --mt=SDK_ROOT\x64\mt.exe --manifests  -- VC_ROOT\bin\Hostx64\x64\link.exe /nologo CMakeFiles\main.dir\main.cxx.obj  /out:main.exe /implib:main.lib /pdb:main.pdb /version:0.0 /machine:x64 /debug /INCREMENTAL /subsystem:console  uses_std.lib  kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."

Note that both of these cases show a __cmake_cxx23 target that compiles the standard library’s modules to provide object code for them. This is the core reason why the feature is behind an experimental feature gate as it is an open question of whether this causes ODR violations in practice. These objects are provided as a STATIC library to ensure that they are available through any consumer of a target using import std while also allowing the linker to drop unused objects (e.g., the std.compat module’s object file).

A huge thank you to Bloomberg for funding the work to implement this in CMake.

Future work towards supporting import std from C++20 (an extension that implementations have stated support for) is possible, but held off for now in order to gain experience with import std as a non-extension part of C++23.

Go forth and test! Bug reports welcome 🙂 .

  1. Targets declare support through the CXX_SCAN_FOR_MODULES property.
    ↩︎
  2. Though not all generator expressions are allowed. Configuration-dependent ($<CONFIG>) and consumer-querying ($<TARGET_PROPERTY:prop>) expressions are not supported as whether a target does import std should not depend on the consumer (at all) or configuration (this may be possible, but prior to defining such support officially, it is denied). ↩︎

4 comments to import std in CMake 3.30

  1. Sir, I am newbie in cmake, can you provide me command to build with llvm in Ubuntu? Thank you so much!

    1. CMake Error in CMakeLists.txt:
      The “CXX_MODULE_STD” property on the target “uses_std” requires that the
      “__CMAKE::CXX23” target exist, but it was not provided by the toolchain.

      — Generating done (0.0s)
      CMake Generate step failed. Build files cannot be regenerated correctly.
      And I get this error when run this cmake command: CXX=clang++ CC=clang cmake -GNinja ..

Leave a Reply