Import C++ Standard Library Headers on CMake 3.30

Hi,

I’m trying to write a CMakeLists to compile C++ code with import <...>;, like:

// main.cpp
import <iostream>;
int main() {
  std::cout << "Hello World" << std::endl;
  return 0;
}

It can be compiled by a simple command with clang18:

/usr/local/Cellar/llvm/18.1.8/bin/clang++ -std=c++20 -fmodules main.cpp   

But when I tried to write CMakeLists, like this:

# File No.1
cmake_minimum_required(VERSION 3.30)
project(Cpp20Sample CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
add_executable(main main.cpp)

# File No.2
cmake_minimum_required(VERSION 3.30)
project(Cpp20Sample CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(main main.cpp)

# File No.3
cmake_minimum_required(VERSION 3.30)
project(Cpp20Sample CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
add_executable(main)
target_sources(main PUBLIC FILE_SET CXX_MODULES FILES main.cpp)

All of them failed with a message:

error: header file <iostream> (aka '/usr/local/Cellar/llvm/18.1.8/bin/../include/c++/v1/iostream') cannot be imported because it is not known to be a header unit

When I searching for solutions, I found this:

Seems it’s still unsolved on CMake 3.30?

Yes, header units are pretty much the last thing we can support; all kinds of other patterns show up in header unit support only “more complex”.

It can’t, unless the distro ships precompiled header units correctly. Many distros still don’t, e.g. ArchLinux doesn’t. Here’s an example (hello.c++):

import <iostream>;
int main() { std::cout << "hello world\n"; }
$ clang++ -std=c++23 -fmodules hello.c++ -o hello
hello.c++:1:8: error: header file <iostream> (aka '/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/14.2.1/../../../../include/c++/14.2.1/iostream') cannot be imported because it is not known to be a header unit
    1 | import <iostream>;
      |        ^
hello.c++:2:14: error: use of undeclared identifier 'std'
    2 | int main() { std::cout << "hello world\n"; }
      |              ^
2 errors generated.

Not good. However, if we first precompile the header unit manually, as documented here, then it will work just fine:

$ clang++ -std=c++23 -xc++-system-header --precompile iostream -o iostream.pcm
$ clang++ -std=c++23 -fmodule-file=iostream.pcm hello.c++ -o hello
$ ./hello
hello world

The problem is that (1) many of these command-line flags may be clang++-specific and (2) (re)building of standard C++ header units again for each project is not a great approach. Distros should make sure that .pcm files are available out-of-the-box.