Installed module library can't find imported headers [Ubuntu 22.04 LTS, CMake 3.28.1, Ninja 1.11.1 and Clang 17.0.6]

Using Ubuntu 22.04 LTS, CMake 3.28.1, Ninja 1.11.1 and Clang 17.0.6, I’m trying to create a library using modules, supported under these conditions, as described at import-cmake-the-experiment-is-over.

I managed to successfully create a library with modules, and link it to an executable in the same project.

My library is quite simple:


// robocin/utility/concepts.ixx

module;

#include <concepts>

#include <google/protobuf/message_lite.h>

export module robocin.utility:concepts;

export namespace robocin {

template <class T>

concept protobufish = std::derived_from<std::remove_cvref_t<T>, ::google::protobuf::MessageLite>;

} // namespace robocin


// robocin/utility/utility.ixx

export module robocin.utility;

export import :concepts;

  • Here is my rule responsible for creating a library.
// CMakeLists.txt

robocin_cpp_library(
  NAME utility
  MODS utility.ixx
  concepts.ixx
  DEPS protobuf::libprotobuf
)

robocin_cpp_executable(
  NAME local_target
  SRCS local_target_main.cpp
  DEPS utility
)


// robocin/utility/local_target_main.cpp

#include <iostream>
#include <vector>

import robocin.utility;

int main() { return robocin::protobufish<std::vector<int>>; }

Compile and works as expected.

However, when I tried to install it and use it in an executable in another project, the library created with modules was unable to find the included dependency:

// CMakeLists.txt

robocin_cpp_executable(
  NAME external_target
  SRCS external_target_main.cpp
  DEPS common::utility # utility library generated by robocin/utility project and installed on the system.
)


// experimental/external_target_main.cpp (same as robocin/utility/local_target_main.cpp)

#include <iostream>
#include <vector>

import robocin.utility;

int main() { return robocin::protobufish<std::vector<int>>; }

Compiling output:


[proc] Executing command: /usr/local/bin/cmake --build /workspaces/project/experiments/cpp-modules/build --config Debug --target all -j 6 --

[build] [1/6 16% :: 0.058] Scanning /usr/local/modules/concepts.ixx for CXX dependencies

[build] FAILED: CMakeFiles/common__utility@synth_ee677d4352ba.dir/90ece7fad90b.bmi.ddi

[build] "/usr/bin/clang-scan-deps-17" -format=p1689 -- /usr/bin/clang++ -DROBOCIN_PROJECT_NAME=\"common\" -DROBOCIN_PROJECT_PATH=\"/tmp/common/cpp\" -I/tmp/common/cpp -I/tmp/common/cpp/build -g -std=gnu++23 --precompile -x c++ /usr/local/modules/concepts.ixx -c -o CMakeFiles/common__utility@synth_ee677d4352ba.dir/90ece7fad90b.bmi -MT CMakeFiles/common__utility@synth_ee677d4352ba.dir/90ece7fad90b.bmi.ddi -MD -MF CMakeFiles/common__utility@synth_ee677d4352ba.dir/90ece7fad90b.bmi.ddi.d > CMakeFiles/common__utility@synth_ee677d4352ba.dir/90ece7fad90b.bmi.ddi.tmp && mv CMakeFiles/common__utility@synth_ee677d4352ba.dir/90ece7fad90b.bmi.ddi.tmp CMakeFiles/common__utility@synth_ee677d4352ba.dir/90ece7fad90b.bmi.ddi

[build] Error while scanning dependencies for /usr/local/modules/concepts.ixx:

[build] /usr/local/modules/concepts.ixx:4:10: fatal error: 'google/protobuf/message_lite.h' file not found

[build] ninja: build stopped: subcommand failed.

  • Here is the generated CMake file, which I imagine is imported by the executable for linking dependencies.

Previously this same library was a header, and it worked in both cases.

A complete example with all the files is available on GitHub: robocin/ssl-core/tree/cpp-modules.

Thanks for the report; I don’t have time to dig into it right now, but I added it to my “C++ modules” task pile when I get the chance.

Thanks @ben.boeckel! Something that could also help would be to have a practical example of an installable C++ module library that uses third parties/external dependencies, which is exactly my problem. I can try to understand the error better from a functional example, and if I find the solution, I can clarify the error here to help anyone else who faces the same problem in the future.

As it’s something new, I couldn’t find any examples in this direction.

The export-* tests here might have a simple case to look at. If something more complicated confuses things, let’s figure out where it breaks down.

I attached a small producer + consumer pair of projects to the following issue recently. It may serve to show you how to do things.

https://gitlab.kitware.com/cmake/cmake/-/issues/25594

I looked at the links you pointed out and ended up making some adjustments to my rules because of this, but they didn’t solve the problem I reported.

However, I noticed that when I added the path of the third party libraries to this IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES variable in the auto-generated CMake file, the compilation worked normally.

Trying to figure out how to make it work, I realized that I had to add:

foreach (DEP ${DEPENDENCIES})
  get_target_property(dep_include_dirs ${DEP} INTERFACE_INCLUDE_DIRECTORIES)
  target_include_directories(${LIBRARY_NAME} PRIVATE ${dep_include_dirs})
endforeach ()

Which I didn’t need before for my headers library :thinking:.

Now it seems to work normally.

Do you link to targets for your dependencies? Can you try to boil this down to a minimal example that I can investigate (assuming it is not covered by Craig’s 25594 example)?

@josecruz Any update on a minimal reproducer?

FWIW, this test shows that includes work, but it is its own provided headers. I’ll try to see about adding a test that transitive dependencies get propagated properly.

I’ve made a reproducer case; tracking it down.

Fix is here: https://gitlab.kitware.com/cmake/cmake/-/merge_requests/9265

Sorry for the delay, Ben!

Here’s a minimal example within what I interacted with the problem: GitHub - joseviccruz/cmake-module-error

In this test I use the GoogleTest library, installed in the /opt directory.

The three root scripts execute the installation of a simple library with a header or module in /usr/local and then try to execute that library in another project.

Thanks for the test cases. I tested it with 3.29.0-rc2 and things worked (after making changes to install to $PWD/install and use m gcc build instead).

I have tested it with clang-17 on OSX too, but the error occurs:

bash-5.2$ gmake -n 
cd module-lib && cmake --workflow --preset default --fresh && cmake --build --preset default --target install
cd module-exe && cmake --workflow --preset default --fresh && cmake --build --preset default --target test
cd header-lib && cmake --workflow --preset default --fresh && cmake --build --preset default --target install
cd header-exe && cmake --workflow --preset default --fresh && cmake --build --preset default --target test
TEST_FORCING_MODULE_ERROR=ON gmake working
gmake[1]: Entering directory '/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error'
cd module-lib && cmake --workflow --preset default --fresh && cmake --build --preset default --target install
cd module-exe && cmake --workflow --preset default --fresh && cmake --build --preset default --target test
gmake[1]: Leaving directory '/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error'
bash-5.2$ 
bash-5.2$ make clean
rm -rf build stagedir
bash-5.2$ gmake failing
TEST_FORCING_MODULE_ERROR=ON gmake working
gmake[1]: Entering directory '/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error'
cd module-lib && cmake --workflow --preset default --fresh && cmake --build --preset default --target install
Executing workflow step 1 of 2: configure preset "default"

Preset CMake variables:

  BUILD_SHARED_LIBS="NO"
  CMAKE_BUILD_TYPE="Release"
  CMAKE_CXX_SCAN_FOR_MODULES:BOOL="TRUE"
  CMAKE_CXX_STANDARD="23"
  CMAKE_INSTALL_PREFIX:PATH="/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir"
  CMAKE_PREFIX_PATH:STRING="/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir"
  TEST_FORCING_MODULE_ERROR="ON"

Preset environment variables:

  CPM_USE_LOCAL_PACKAGES="NO"
  PATH="/Users/clausklein/.local/bin:/usr/local/opt/llvm/bin://Users/clausklein/perl5/bin:/usr/local/opt/net-snmp/bin:/usr/local/opt/coreutils/libexec/gnubin:/usr/local/libexec/bin:/usr/local/opt/curl/bin:/usr/local/opt/libxml2/bin:/usr/local/opt/libxslt/bin:/usr/local/opt/expat/bin:/usr/local/opt/net-snmp/bin:/Users/clausklein/Library/Python/3.9/bin:/usr/local/opt/python/libexec/bin:/usr/local/opt/sqlite/bin:/usr/local/bin:/usr/local/sbin:/Users/clausklein/scripts:/Users/clausklein/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/X11/bin:/Library/Apple/usr/bin:/usr/local/MacGPG2/bin:/usr/texbin"

-- The CXX compiler identification is Clang 17.0.6
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/local/opt/llvm/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE  
-- Using GTest: 1.14.0
CMake Warning at /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/helpers.cmake:108 (message):
  TEST_FORCING_MODULE_ERROR
Call Stack (most recent call first):
  test-library/CMakeLists.txt:1 (robocin_cpp_library)


-- Configuring done (1.9s)
-- Generating done (0.0s)
-- Build files have been written to: /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/build/module-lib

Executing workflow step 2 of 2: build preset "default"

[4/4] Linking CXX static library test-library/libtest_class.a
[0/1] Install the project...
-- Install configuration: "Release"
-- Installing: /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir/include/test-library/test_class.ixx
-- Installing: /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir/lib/libtest_class.a
-- Installing: /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir/modules/test_class.ixx
-- Installing: /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir/modules/test.pcm
-- Installing: /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir/lib/cmake/module_lib/module_libConfig.cmake
-- Installing: /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir/lib/cmake/module_lib/module_libConfig-release.cmake
-- Installing: /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir/lib/cmake/module_lib/cxx_modules/cxx-modules-module_libTargets.cmake
-- Installing: /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir/lib/cmake/module_lib/cxx_modules/cxx-modules-module_libTargets-Release.cmake
-- Installing: /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir/lib/cmake/module_lib/cxx_modules/target-test_class-Release.cmake
cd module-exe && cmake --workflow --preset default --fresh && cmake --build --preset default --target test
Executing workflow step 1 of 2: configure preset "default"

Preset CMake variables:

  BUILD_SHARED_LIBS="NO"
  CMAKE_BUILD_TYPE="Release"
  CMAKE_CXX_SCAN_FOR_MODULES:BOOL="TRUE"
  CMAKE_CXX_STANDARD="23"
  CMAKE_INSTALL_PREFIX:PATH="/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir"
  CMAKE_PREFIX_PATH:STRING="/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir"
  TEST_FORCING_MODULE_ERROR="ON"

Preset environment variables:

  CPM_USE_LOCAL_PACKAGES="NO"
  PATH="/Users/clausklein/.local/bin:/usr/local/opt/llvm/bin://Users/clausklein/perl5/bin:/usr/local/opt/net-snmp/bin:/usr/local/opt/coreutils/libexec/gnubin:/usr/local/libexec/bin:/usr/local/opt/curl/bin:/usr/local/opt/libxml2/bin:/usr/local/opt/libxslt/bin:/usr/local/opt/expat/bin:/usr/local/opt/net-snmp/bin:/Users/clausklein/Library/Python/3.9/bin:/usr/local/opt/python/libexec/bin:/usr/local/opt/sqlite/bin:/usr/local/bin:/usr/local/sbin:/Users/clausklein/scripts:/Users/clausklein/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/X11/bin:/Library/Apple/usr/bin:/usr/local/MacGPG2/bin:/usr/texbin"

-- The CXX compiler identification is Clang 17.0.6
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/local/opt/llvm/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE  
-- Using GTest: 1.14.0
-- Using module_lib: '/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir/lib/cmake/module_lib'
-- Configuring done (2.0s)
-- Generating done (0.0s)
CMake Warning:
  Manually-specified variables were not used by the project:

    TEST_FORCING_MODULE_ERROR


-- Build files have been written to: /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/build/module-exe

Executing workflow step 2 of 2: build preset "default"

[1/6] Scanning /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir/modules/test_class.ixx for CXX dependencies
FAILED: CMakeFiles/module_lib__test_class@synth_800cc612d0fe.dir/9ece086e9bac.bmi.ddi 
"/usr/local/Cellar/llvm/17.0.6_1/bin/clang-scan-deps" -format=p1689 -- /usr/local/opt/llvm/bin/clang++ -DPROJECT_PATH=\"/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/module-lib\" -I/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/module-lib -I/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/build/module-lib -O3 -DNDEBUG -std=gnu++23 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk --precompile -x c++ /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir/modules/test_class.ixx -c -o CMakeFiles/module_lib__test_class@synth_800cc612d0fe.dir/9ece086e9bac.bmi -MT CMakeFiles/module_lib__test_class@synth_800cc612d0fe.dir/9ece086e9bac.bmi.ddi -MD -MF CMakeFiles/module_lib__test_class@synth_800cc612d0fe.dir/9ece086e9bac.bmi.ddi.d > CMakeFiles/module_lib__test_class@synth_800cc612d0fe.dir/9ece086e9bac.bmi.ddi.tmp && mv CMakeFiles/module_lib__test_class@synth_800cc612d0fe.dir/9ece086e9bac.bmi.ddi.tmp CMakeFiles/module_lib__test_class@synth_800cc612d0fe.dir/9ece086e9bac.bmi.ddi
Error while scanning dependencies for /Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir/modules/test_class.ixx:
/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/stagedir/modules/test_class.ixx:3:10: fatal error: 'gtest/gtest.h' file not found
ninja: build stopped: subcommand failed.
gmake[1]: *** [GNUmakefile:5: working] Error 1
gmake[1]: Leaving directory '/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error'
gmake: *** [GNUmakefile:8: failing] Error 2
bash-5.2$ 

I have tested it on CI with ubuntu 22.04 and it works?
see Use setup-cpp to speeup docker build · ClausKlein/cmake-module-error@5d74595 · GitHub

But on my OSX, I get an error.

snipped of module_libConfig.cmake

# Create imported target module_lib::test_class
add_library(module_lib::test_class STATIC IMPORTED)

set_target_properties(module_lib::test_class PROPERTIES
  IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS "PROJECT_PATH=\"/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/module-lib\""
  IMPORTED_CXX_MODULES_COMPILE_FEATURES "cxx_std_23"
  IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES "/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/module-lib;/Users/clausklein/Workspace/cpp/cxx20/cmake-module-error/build/module-lib"
  IMPORTED_CXX_MODULES_LINK_LIBRARIES "\$<COMPILE_ONLY:GTest::gtest>"
  INTERFACE_COMPILE_FEATURES "cxx_std_23"
  INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
  INTERFACE_LINK_LIBRARIES "GTest::gtest"
)

IMHO: find_dependency(Gtest) is missing too!

see Use setup-cpp to speeup docker build by ClausKlein · Pull Request #1 · ClausKlein/cmake-module-error · GitHub

The error occurs, if GTest is installed to /opt or /usr/local which are not in default include path!

CMake doesn’t generate anything like that today; you’ll need to provide that call.

But I added it, only it does not help:

include(CMakeFindDependencyMacro)
find_dependency(GTest 1.14 CONFIG REQUIRED)

include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")

@ben.boeckel It does still not work on OSX. What is different on Apple?

bash-5.2$ find stagedir/include -name gtest.h
stagedir/include/gtest/internal/custom/gtest.h
stagedir/include/gtest/gtest.h

bash-5.2$ ll stagedir/lib/cmake/GTest/*.cmake
-rw-r--r-- 1 clausklein 5.3K Feb 24 22:21 stagedir/lib/cmake/GTest/GTestTargets.cmake
-rw-r--r-- 1 clausklein 2.3K Feb 24 22:21 stagedir/lib/cmake/GTest/GTestTargets-release.cmake
-rw-r--r-- 1 clausklein 1.9K Feb 24 22:21 stagedir/lib/cmake/GTest/GTestConfigVersion.cmake
-rw-r--r-- 1 clausklein 1.1K Feb 24 22:21 stagedir/lib/cmake/GTest/GTestConfig.cmake

bash-5.2$ cmake --version
cmake version 3.28.3
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.3.0
Thread model: posix
InstalledDir: /usr/local/opt/llvm/bin

bash-5.2$