C++20 module seems to be broken (missing transitive usage) in a very specific case

I am learning to use C++20 module with CMake 2.38.0-rc4, and I noticed a possible bug in CMake.

CMakeLists.txt:

cmake_minimum_required(VERSION 3.28.0)

project(MyLib LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 23)

add_library(my-lib)

target_sources(my-lib PRIVATE FILE_SET CXX_MODULES FILES
    mod1.cpp
    mod3.cpp # import mod2
    mod4.cpp # import mod2
    mod2.cpp # import mod1
    mod5.cpp # import mod4
    mod6.cpp # import mod5
)

All module units are empty except for the imports as indicated in the comments above.

When compiling this project against Clang, I got

[13/14] Building CXX object CMakeFiles/my-lib.dir/mod6.cpp.o
/mnt/d/test/test-module/mod6.cpp:2:1: warning: it is deprecated to read module 'mod1' implicitly; it is going to be removed in clang 18; consider to specify the dependencies explicitly [-Wread-modules-implicitly]
    2 | import mod5;
      | ^
1 warning generated.
warning: it is deprecated to read module 'mod1' implicitly; it is going to be removed in clang 18; consider to specify the dependencies explicitly [-Wread-modules-implicitly]
1 warning generated.

It seems that CMake forgets to tell Clang the location of mod1.pcm when compiling mod6.cpp.

Note that the order of files in target_souces is significant. The warning might not occur if the files are listed in a different order.

Does this sound like a bug of CMake? Is this a known bug?

For the reference, here are the contents of `mod1.cpp` through `mod6.cpp`

mod1.cpp:

export module mod1;

mod2.cpp:

export module mod2;
import mod1;

mod3.cpp:

export module mod3;
import mod2;

mod4.cpp:

export module mod4;
import mod2;

mod5.cpp:

export module mod5;
import mod4;

mod6.cpp:

export module mod6;
import mod5;

It does sound like a bug; not known before.

I’m guessing that we need to do 2 full passes over the modules in order to support transitive module tracking properly. Thanks for the test case.

Thanks; do you mind filing an issue to track this?

With this example I see the same warning:

module;

//XXX #include <fmt/core.h>
import fmt;

module algo;

void Algo::helloWorld()
{
  fmt::print("hello {}\n", m_name);
}
bash-5.2$ ninja Algo -j 1
[4/6] Building CXX object CMakeFiles/Algo.dir/algo-interface.cppm.o
warning: it is deprecated to read module 'fmt' implicitly; it is going to be removed in clang 18; consider to specify the dependencies explicitly [-Wread-modules-implicitly]
1 warning generated.
[5/6] Building CXX object CMakeFiles/Algo.dir/algo-impl.cpp.o
warning: the form '-fmodule-file=<BMI-path>' is deprecated for standard C++ named modules;consider to use '-fmodule-file=<module-name>=<BMI-path>' instead [-Weager-load-cxx-named-modules]
warning: the form '-fmodule-file=<BMI-path>' is deprecated for standard C++ named modules;consider to use '-fmodule-file=<module-name>=<BMI-path>' instead [-Weager-load-cxx-named-modules]
/Users/clausklein/Workspace/cpp/cxx20/cmake-init-modules/algo-impl.cpp:6:1: warning: it is deprecated to read module 'fmt' implicitly; it is going to be removed in clang 18; consider to specify the dependencies explicitly [-Wread-modules-implicitly]
    6 | module algo;
      | ^
3 warnings generated.
[6/6] Linking CXX shared library libAlgo.dylib
bash-5.2$ 

bash-5.2$ cmake --version
cmake version 3.29.20240409-g24f1e7c

CMake suite maintained and supported by Kitware (kitware.com/cmake).
bash-5.2$ clang --version
Homebrew clang version 17.0.6
Target: x86_64-apple-darwin23.4.0
Thread model: posix
InstalledDir: /usr/local/opt/llvm/bin
bash-5.2$ 

I is the extended example from @craig.scott C++20 Modules, CMake, And Shared Libraries

Can you please provide:

  • .ddi file for algo-interface.cppm and algo-impl.cpp
  • .modmap for algo-interface.cppm.o and algo-impl.cpp.o
  • CXX*.json files for the Algo target
  • cmake --version

One thing I see is that you’re importing in the global module fragment; this is certainly weird at least. Does this work instead?

module algo;

import fmt;

// …

Yes, but now it fails with different error:

Executing workflow step 2 of 3: build preset "dev"

[19/27] Building CXX object CMakeFiles/Algo.dir/algo-impl.cpp.o
FAILED: CMakeFiles/Algo.dir/algo-impl.cpp.o 
/usr/local/opt/llvm/bin/clang++ -DAlgo_EXPORTS -DFMT_HEADER_ONLY=1 -I/Users/clausklein/Workspace/cpp/cxx20/cmake-init-modules/build/dev -isystem /Users/clausklein/.cache/CPM/fmt/c3ebf53335b44df838d9982d1a0e35afe190e34f/include -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast -g -std=c++20 -arch x86_64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.4.sdk -fPIC -fvisibility=hidden -fvisibility-inlines-hidden -MD -MT CMakeFiles/Algo.dir/algo-impl.cpp.o -MF CMakeFiles/Algo.dir/algo-impl.cpp.o.d @CMakeFiles/Algo.dir/algo-impl.cpp.o.modmap -o CMakeFiles/Algo.dir/algo-impl.cpp.o -c /Users/clausklein/Workspace/cpp/cxx20/cmake-init-modules/algo-impl.cpp
/Users/clausklein/Workspace/cpp/cxx20/cmake-init-modules/algo-impl.cpp:8:8: fatal error: module 'fmt' not found
    8 | import fmt;
      | ~~~~~~~^~~
1 error generated.
[21/27] Linking CXX shared library _deps/fmt-build/libfmtd.10.2.1.dylib
ninja: build stopped: subcommand failed.
make: *** [all] Error 1
bash-5.2$ cd build/dev/
bash-5.2$ tree _deps/fmt-build/
_deps/fmt-build/
|-- CMakeFiles
|   |-- Export
|   |   `-- b834597d9b1628ff12ae4314c3a2e4b8
|   |       |-- fmt-targets-debug.cmake
|   |       `-- fmt-targets.cmake
|   `-- fmt.dir
|-- cmake_install.cmake
|-- fmt-config-version.cmake
|-- fmt-config.cmake
|-- fmt-targets.cmake
|-- fmt.o
|-- fmt.pc
|-- fmt.pcm
`-- libfmtd.10.2.1.dylib

5 directories, 10 files
bash-5.2$ 

@DanielaE It seems, that the fmt v10.2.1 C++20 module does not build to be usable?

you find the contents here Use C++20 fmt module #3

I don’t see your build scanning the fmt module. You cannot use BMI files not compiled as part of the project. It seems that fmt is not being provided as an IMPORTED target with CXX_MODULES-typed FILE_SET sources to let CMake know about its modules.