I have a question about the experimental CMake support for C++ Modules
with clang++ on OSX .
According this Standard C++ Modules — Clang 17.0.0git documentation , it
is quit simple to compile and link a C++ Module
example and very fast:
clang++ -std=c++20 -x c++-module greeting.cppm --precompile -o build/greeting.pcm
clang++ -std=c++20 hello.cpp -fprebuilt-module-path=build build/greeting.pcm -o build/hello
Note: The PCM file ist linked!
But with CMake this is done, which is much more complicated and slow:
bash-3.2$ ninja -v hello
[1/8] "/usr/local/Cellar/llvm/16.0.3/bin/clang-scan-deps" -format=p1689 -- /usr/local/opt/llvm/bin/clang-16 -std=c++20
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk -x c++
/Users/clausklein/Workspace/cpp/cxx20/test/hello.cpp -c -o CMakeFiles/hello.dir/hello.cpp.o -MT
CMakeFiles/hello.dir/hello.cpp.o.ddi -MD -MF CMakeFiles/hello.dir/hello.cpp.o.ddi.d > CMakeFiles/hello.dir/hello.cpp.o.ddi
[2/8] "/usr/local/Cellar/llvm/16.0.3/bin/clang-scan-deps" -format=p1689 -- /usr/local/opt/llvm/bin/clang-16 -std=c++20
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk -x c++
/Users/clausklein/Workspace/cpp/cxx20/test/greeting.cppm -c -o CMakeFiles/greeting.dir/greeting.cppm.o -MT
CMakeFiles/greeting.dir/greeting.cppm.o.ddi -MD -MF CMakeFiles/greeting.dir/greeting.cppm.o.ddi.d >
CMakeFiles/greeting.dir/greeting.cppm.o.ddi
[3/8] /usr/local/Cellar/cmake/3.26.3/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/greeting.dir/CXXDependInfo.json
--lang=CXX --modmapfmt=clang --dd=CMakeFiles/greeting.dir/CXX.dd @CMakeFiles/greeting.dir/CXX.dd.rsp
[4/8] /usr/local/opt/llvm/bin/clang-16 -std=c++20 -isysroot
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk -MD -MT
CMakeFiles/greeting.dir/greeting.cppm.o -MF CMakeFiles/greeting.dir/greeting.cppm.o.d
@CMakeFiles/greeting.dir/greeting.cppm.o.modmap -o CMakeFiles/greeting.dir/greeting.cppm.o -c
/Users/clausklein/Workspace/cpp/cxx20/test/greeting.cppm @CMakeFiles/greeting.dir/greeting.cppm.o.modmap
clang-16: warning: '-x c++-module' after last input file has no effect [-Wunused-command-line-argument]
[5/8] : && /usr/local/Cellar/cmake/3.26.3/bin/cmake -E rm -f libgreeting.a && /usr/bin/ar qc libgreeting.a
CMakeFiles/greeting.dir/greeting.cppm.o && /usr/local/opt/llvm/bin/llvm-ranlib libgreeting.a &&
/usr/local/Cellar/cmake/3.26.3/bin/cmake -E touch libgreeting.a && :
[6/8] /usr/local/Cellar/cmake/3.26.3/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/hello.dir/CXXDependInfo.json
--lang=CXX --modmapfmt=clang --dd=CMakeFiles/hello.dir/CXX.dd @CMakeFiles/hello.dir/CXX.dd.rsp
[7/8] /usr/local/opt/llvm/bin/clang-16 -std=c++20 -isysroot
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk -MD -MT
CMakeFiles/hello.dir/hello.cpp.o -MF CMakeFiles/hello.dir/hello.cpp.o.d @CMakeFiles/hello.dir/hello.cpp.o.modmap -o
CMakeFiles/hello.dir/hello.cpp.o -c /Users/clausklein/Workspace/cpp/cxx20/test/hello.cpp
@CMakeFiles/hello.dir/hello.cpp.o.modmap
[8/8] : && /usr/local/opt/llvm/bin/clang-16 -isysroot
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk -Wl,-search_paths_first
-Wl,-headerpad_max_install_names -lstdc++ CMakeFiles/hello.dir/hello.cpp.o -o hello libgreeting.a && :
bash-3.2$ find . -name '*.modmap' | xargs head
==> ./CMakeFiles/greeting.dir/greeting.cppm.o.modmap <==
-x c++-module
-fmodule-output=CMakeFiles/greeting.dir/greeting.pcm
==> ./CMakeFiles/hello.dir/hello.cpp.o.modmap <==
-fmodule-file=greeting=CMakeFiles/greeting.dir/greeting.pcm
bash-3.2$
Note: The object library is linked and I need use -lstdc++
?
This is my CMakeLists.txt:
set(CMAKE_EXPORT_COMPILE_COMMANDS YES)
project(clang-tidy-issue LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_EXTENSIONS NO)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_CXX_SCAN_FOR_MODULES YES)
# First find clang-tidy, this also allows users to provide hints
find_program(CLANG_TIDY NAMES clang-tidy REQUIRED)
# https://clang.llvm.org/docs/StandardCPlusPlusModules.html#how-to-build-projects-using-modules
# clang++ -std=c++20 -x c++-module greeting.cppm --precompile -o build/greeting.pcm
# clang++ -std=c++20 hello.cpp -fprebuilt-module-path=build build/greeting.pcm -o build/hello
#
# see https://gitlab.kitware.com/cmake/cmake/-/blob/v3.26.3/Help/dev/experimental.rst?ref_type=tags#c-20-module-apis
# and https://gitlab.kitware.com/cmake/cmake/-/blob/v3.26.3/.gitlab/ci/cxx_modules_rules_gcc.cmake?ref_type=tags
# https://gitlab.kitware.com/cmake/cmake/-/blob/v3.26.3/.gitlab/ci/cxx_modules_rules_clang.cmake?ref_type=tags
set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API 2182bf5c-ef0d-489a-91da-49dbc3090d2a)
set(CMake_TEST_CXXModules_UUID "a246741c-d067-4019-a8fb-3d16b0c9d1d3")
# Workaround for C++Modules: Missing module map flags in exported compile commands
# see https://gitlab.kitware.com/cmake/cmake/-/issues/24618
set(CMAKE_CXX_COMPILE_OBJECT "${CMAKE_CXX_COMPILE_OBJECT} @<OBJECT>.modmap")
set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
"<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E -x c++ <SOURCE>"
" -MT <DYNDEP_FILE> -MD -MF <DEP_FILE>"
" -fmodules-ts -fdep-file=<DYNDEP_FILE> -fdep-output=<OBJECT> -fdep-format=trtbd"
" -o <PREPROCESSED_SOURCE>"
)
set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "gcc")
set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG "-fmodules-ts -fmodule-mapper=<MODULE_MAP_FILE> -fdep-format=trtbd -x c++")
set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY};-checks=*,-llvmlibc-*,-fuchsia-*,-cppcoreguidelines-init-variables")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "clang")
set(CMAKE_EXE_LINKER_FLAGS -lstdc++)
endif()
file(WRITE greeting.cppm
[=[
// Global Module Fragment (GMF) where #includes can happen
module;
#include <iostream>
// first thing after the Global module fragment must be a module command
// XXX import std.core;
export module greeting;
export class greeting {
public:
greeting() = default;
~greeting() = default;
void helloworld();
};
void greeting::helloworld() { std::cout << "hello world\n"; }
]=]
)
file(WRITE hello.cpp
[=[
import greeting;
auto main() -> int
{
greeting greeter;
greeter.helloworld();
return 0;
}
]=]
)
add_library(greeting)
target_sources(
greeting
PUBLIC FILE_SET
cxx_modules
TYPE
CXX_MODULES
FILES
greeting.cppm
)
add_executable(hello hello.cpp)
target_link_libraries(hello greeting)
1 Like
ben.boeckel
(Ben Boeckel (Kitware))
May 11, 2023, 2:44am
3
This is because you know the module name and requirements. CMake cannot generate command lines like this because for any given source:
it does not know what module it provides (only that it provides one)
it does not know what modules it requires
CMake could do this if we did scanning as part of configure/generate. However, that then means that CMake needs to reconfigure/generate on any change to such files because it could, theoretically, change the set of provides/requires. It may well be slower to compile, but the mechanics of making such commands lines would inevitably be slower because a CMake configure/generate cycle would then be injected into any module-using or module-providing source file edit.
Why is this needed? It’s not needed in my testing or our CI (though I don’t know the exact versions in use).
What do you mean “the object library is linked”? I see no object (OBJECT
?) library in your example. There is a greeting
library which is linked from hello
which does indeed require the libgreeting.a
on the link line of hello
.
I meaned the object file is used instead of the precompiled Module (pcm) file.
ben.boeckel
(Ben Boeckel (Kitware))
May 11, 2023, 10:34am
5
Yes, the object file has what is needed for the library/executable. Your examples are skipping the -c
part because they are single-file artifacts; any more source files and you’d need to do the same thing too.
1 Like
Only the module implementation unit
(.cpp) files needs to to by compiled.
ben.boeckel
(Ben Boeckel (Kitware))
May 11, 2023, 12:53pm
7
Module interfaces can have object code as well, in general.