# How to control the location of the c++20 Binary Module Interface (BMI) output directory?

**URL:** https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968
**Category:** Usage
**Created:** [April 24, 2023, 3:49pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968 "2023-04-24T15:49:46Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [April 24, 2023, 3:49pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/1 "2023-04-24T15:49:46Z")

</div>

With this `CMakeLists.txt,` I can build and check the simple example of a module interface.

But `clang-tidy` does not find the `BMI` file?

```cmake
cmake_minimum_required(VERSION 3.26)

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 foo.cxx --precompile -o build/foo.pcm
# clang++ -std=c++20 main.cxx -fprebuilt-module-path=build build/foo.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(BMI ${CMAKE_CURRENT_BINARY_DIR}/foo.pcm)

  # FIXME: This does not work, clang-tidy is a pre, not a post compile step! CK
  #XXX string(CONCAT CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY}"
  #XXX "-extra-arg=-fprebuilt-module-path=${CMAKE_CURRENT_BINARY_DIR}"
  #XXX )
endif()

file(WRITE foo.cppm
     [=[
// Global module fragment 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 foo;

export class foo {
public:
  foo() = default;
  ~foo() = default;
  void helloworld();
};

void foo::helloworld() { std::cout << "hello world\n"; }
]=]
)

file(WRITE main.cxx
     [=[
import foo;

auto main() -> int
{
  foo myFoo;
  myFoo.helloworld();
  return 0;
}
]=]
)

add_library(foo)
target_sources(
  foo
  PUBLIC FILE_SET
         cxx_modules
         TYPE
         CXX_MODULES
         FILES
         foo.cppm
)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  # TODO(CK): How to control the location of the c++20 Binary Module Interface (BMI) output directory?
  #XXX target_compile_options(foo
  #XXX PRIVATE -fmodule-output=${BMI} -x c++-module
  #XXX PUBLIC -fmodule-file=foo=${BMI}
  #XXX )
endif()

add_executable(hello main.cxx)
target_link_libraries(hello foo)

enable_testing()
add_test(NAME hello COMMAND hello)

install(TARGETS foo ARCHIVE PUBLIC_HEADER
  FILE_SET cxx_modules DESTINATION include
  CXX_MODULES_BMI DESTINATION share/cxx-modules
)
include(cpack)

```

---

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [April 24, 2023, 3:55pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/2 "2023-04-24T15:55:00Z")

</div>

This is the Result on OSX with clang v16.0.1:

```bash
bash-3.2$ run-clang-tidy
Enabled checks:
 # ...

/usr/local/opt/llvm/bin/clang-tidy -p=/Users/clausklein/Workspace/cpp/cxx20/test/build /Users/clausklein/Workspace/cpp/cxx20/test/main.cxx
77759 warnings generated.
Suppressed 77759 warnings (77759 in non-user code).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
/usr/local/opt/llvm/bin/clang-tidy -p=/Users/clausklein/Workspace/cpp/cxx20/test/build /Users/clausklein/Workspace/cpp/cxx20/test/foo.cppm
warning: '-x c++-module' after last input file has no effect [clang-diagnostic-unused-command-line-argument]
/Users/clausklein/Workspace/cpp/cxx20/test/foo.cppm:17:11: warning: method 'helloworld' can be made static [readability-convert-member-functions-to-static]
void foo::helloworld() { std::cout << "hello world\n"; }
          ^
85169 warnings generated.
Suppressed 85167 warnings (85167 in non-user code).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
bash-3.2$ cat compile_commands.json 
[
{
  "directory": "/Users/clausklein/Workspace/cpp/cxx20/test/build",
  "command": "/usr/local/opt/llvm/bin/clang++ -std=c++20 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk -o CMakeFiles/foo.dir/foo.cppm.o -c /Users/clausklein/Workspace/cpp/cxx20/test/foo.cppm @CMakeFiles/foo.dir/foo.cppm.o.modmap",
  "file": "/Users/clausklein/Workspace/cpp/cxx20/test/foo.cppm",
  "output": "CMakeFiles/foo.dir/foo.cppm.o"
},
{
  "directory": "/Users/clausklein/Workspace/cpp/cxx20/test/build",
  "command": "/usr/local/opt/llvm/bin/clang++ -std=c++20 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk -o CMakeFiles/hello.dir/main.cxx.o -c /Users/clausklein/Workspace/cpp/cxx20/test/main.cxx @CMakeFiles/hello.dir/main.cxx.o.modmap",
  "file": "/Users/clausklein/Workspace/cpp/cxx20/test/main.cxx",
  "output": "CMakeFiles/hello.dir/main.cxx.o"
}
]bash-3.2$ 
``
```

---

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [April 24, 2023, 3:58pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/3 "2023-04-24T15:58:40Z")

</div>

### And the most important question is where to install the BMI file?

```auto
bash-3.2$ cpack
CPack: Create package using STGZ
CPack: Install projects
CPack: - Install project: clang-tidy-issue []
CPack: Create package
CPack: - package: /Users/clausklein/Workspace/cpp/cxx20/test/build/clang-tidy-issue-0.1.1-Darwin.sh generated.
CPack: Create package using TGZ
CPack: Install projects
CPack: - Install project: clang-tidy-issue []
CPack: Create package
CPack: - package: /Users/clausklein/Workspace/cpp/cxx20/test/build/clang-tidy-issue-0.1.1-Darwin.tar.gz generated.
bash-3.2$ tar tzvf clang-tidy-issue-0.1.1-Darwin.tar.gz
drwxr-xr-x 0 clausklein staff 0 Apr 24 17:56 clang-tidy-issue-0.1.1-Darwin/include/
-rw-r--r-- 0 clausklein staff 348 Apr 24 17:38 clang-tidy-issue-0.1.1-Darwin/include/foo.cppm
drwxr-xr-x 0 clausklein staff 0 Apr 24 17:56 clang-tidy-issue-0.1.1-Darwin/lib/
-rw-r--r-- 0 clausklein staff 17272 Apr 24 17:38 clang-tidy-issue-0.1.1-Darwin/lib/libfoo.a
drwxr-xr-x 0 clausklein staff 0 Apr 24 17:56 clang-tidy-issue-0.1.1-Darwin/share/
drwxr-xr-x 0 clausklein staff 0 Apr 24 17:56 clang-tidy-issue-0.1.1-Darwin/share/cxx-modules/
-rw-r--r-- 0 clausklein staff 13821400 Apr 24 17:38 clang-tidy-issue-0.1.1-Darwin/share/cxx-modules/foo.pcm
bash-3.2$ 

```

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [April 24, 2023, 5:07pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/4 "2023-04-24T17:07:04Z")

</div>

> [@ClausKlein](#):
>
> ` # FIXME: This does not work, clang-tidy is a pre, not a post compile step! CK`

This has not been worked on; `clang-tidy` may need to learn how to read the modmap file `clang` will use (though it should exist by the time we’re compiling…

The fact that it doesn’t work with `compile_commands.json` when the `@modmap` arguments are there tells me that `clang-tidy` is ignoring something…

> [@ClausKlein](#):
>
> And the most important question is where to install the BMI file?

Certainly not `share` as they are arch-dependent. I suspect where they go will be a Linux distro discussion in the FHS umbrella, Apple will just decree for itself, and Microsoft will do Windows things. ISO C++'s SG15 is also likely to discuss such things.

---

<div class="post-metadata">

### Author: ![sharadhr](https://discourse.cmake.org/user_avatar/discourse.cmake.org/sharadhr/32/3528_2.png) [@sharadhr](https://discourse.cmake.org/u/sharadhr)
#### Post date: [June 13, 2023, 2:36pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/5 "2023-06-13T14:36:54Z")

</div>

@ClausKlein, I have been experiencing this issue too, which gives `clang-diagnostic-error` with C++20 modules and `clang-tidy`.

```auto
FAILED: CMakeFiles/caldera_exe.dir/source/main.cpp.obj 
"C:\Program Files\CMake\bin\cmake.exe" -E __run_co_compile --tidy="clang-tidy;--header-filter=^D:/Libraries/Documents/Repositories/caldera;-extra-arg=-fprebuilt-module-path=D:/Libraries/Documents/Repositories/caldera/build/CMakeFiles/caldera_lib.dir;--extra-arg-before=--driver-mode=cl" --source=D:\Libraries\Documents\Repositories\caldera\source\main.cpp -- C:\PROGRA~1\MIB055~1\2022\ENTERP~1\VC\Tools\MSVC\1436~1.325\bin\Hostx64\x64\cl.exe /nologo /TP -ID:\Libraries\Documents\Repositories\caldera\source -external:ID:\Libraries\Documents\Repositories\caldera\build\dev-win64\vcpkg_installed\x64-windows-static-md\include -external:W0 /sdl /guard:cf /utf-8 /diagnostics:caret /w14165 /w44242 /w44254 /w44263 /w34265 /w34287 /w44296 /w44365 /w44388 /w44464 /w14545 /w14546 /w14547 /w14549 /w14555 /w34619 /w34640 /w24826 /w14905 /w14906 /w14928 /w45038 /W4 /permissive- /volatile:iso /Zc:inline /Zc:preprocessor /Zc:enumTypes /Zc:lambda /Zc:__ cplusplus /Zc:externConstexpr /Zc:throwingNew /EHsc /O2 /Ob1 /DNDEBUG -std:c++20 -MD -Zi /showIncludes @CMakeFiles\caldera_exe.dir\source\main.cpp.obj.modmap /FoCMakeFiles\caldera_exe.dir\source\main.cpp.obj /FdCMakeFiles\caldera_exe.dir\ /FS -c D:\Libraries\Documents\Repositories\caldera\source\main.cpp
warning: unknown argument ignored in clang-cl: '-fprebuilt-module-path=D:/Libraries/Documents/Repositories/caldera/build/CMakeFiles/caldera_lib.dir' [clang-diagnostic-unknown-argument]
D:\Libraries\Documents\Repositories\caldera\source\main.cpp:1:8: error: module 'caldera' not found [clang-diagnostic-error]
import caldera;
~~~~~~~^~~~~~~
132051 warnings and 1 error generated.
Error while processing D:\Libraries\Documents\Repositories\caldera\source\main.cpp.
Suppressed 132050 warnings (132050 in non-user code).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
Found compiler error(s).
ninja: build stopped: subcommand failed.

```

I am running Windows 10, running Visual Studio 17.6.2 (`cl.exe` 19.36), CMake 3.26.4, Ninja 1.11.0, and `clang-tidy` 16.0.0 (so effectively everything is the newest possible).

When I tried preparing a minimal working example, all was fine, but in my own project, I didn’t realise I had previously set `CMAKE_CXX_CLANG_TIDY`, and it admittedly took me quite a bit of time to notice the problem.

Like you, I wonder if it has to do with the way CMake is calling `clang-tidy`, or if it’s a bug in `clang-tidy` itself.

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [June 17, 2023, 10:43pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/6 "2023-06-17T22:43:39Z")

</div>

> [@sharadhr](#):
>
> Like you, I wonder if it has to do with the way CMake is calling `clang-tidy`, or if it’s a bug in `clang-tidy` itself.

CMake is generating modules for MSVC to use, If `clang-tidy` cannot consume the `.ifc` files (regardless of the extension), it will always fail to import things.

---

<div class="post-metadata">

### Author: ![Lecris](https://discourse.cmake.org/user_avatar/discourse.cmake.org/lecris/32/3193_2.png) [@Lecris](https://discourse.cmake.org/u/Lecris)
#### Post date: [June 18, 2023, 12:57am UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/7 "2023-06-18T00:57:32Z")

</div>

Can we track this issue upstream?

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [June 18, 2023, 4:16pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/8 "2023-06-18T16:16:05Z")

</div>

Sure. I don’t 100% know what to do with it at the moment right now. Getting modules working at all is higher priority than figuring out heterogeneous tooling. If you want to use `clang-tidy` with modules today, I’d recommend using a matching `clang` toolchain at least.

---

<div class="post-metadata">

### Author: ![sharadhr](https://discourse.cmake.org/user_avatar/discourse.cmake.org/sharadhr/32/3528_2.png) [@sharadhr](https://discourse.cmake.org/u/sharadhr)
#### Post date: [June 19, 2023, 3:45am UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/9 "2023-06-19T03:45:38Z")

</div>

> [@ben.boeckel](#):
>
> If you want to use `clang-tidy` with modules today, I’d recommend using a matching `clang` toolchain at least.

For the record, I have changed my [minimum not-working example](https://gist.github.com/sharadhr/add57e03e62e4e9d6ffaa24bd2485492) linked in the [LLVM Discourse](https://discourse.llvm.org/t/clang-tidy-doesnt-find-standard-c-modules-despite-providing-compilation-database-and-prebuilt-modules-path/71326) thread to use a fully `clang`-only toolchain.

I changed [line 14 of CMakePresets.json](https://gist.github.com/sharadhr/add57e03e62e4e9d6ffaa24bd2485492#file-cmakepresets-json-L14) to `clang.exe`, but I _still_ receive the same error. `test.pcm` is built in `${CMAKE_BINARY_DIR}/CMakeFiles/test_lib.dir/test.pcm`. A full, verbose build log is below:

```log
====================[Build | test | Ninja - Ninja]============================
cmake.exe --build --target test --preset Ninja
[1/8] cmd.exe /C ""C:/Program Files/LLVM/bin/clang-scan-deps.exe" -format=p1689 -- C:\PROGRA~1\LLVM\bin\clang.exe -O0 -std=gnu++20 -D_DEBUG -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd -g -Xclang -gcodeview -x c++ D:/Libraries/Downloads/test/main.cpp -c -o CMakeFiles\test.dir\main.cpp.obj -MT CMakeFiles\test.dir\main.cpp.obj.ddi -MD -MF CMakeFiles\test.dir\main.cpp.obj.ddi.d > CMakeFiles\test.dir\main.cpp.obj.ddi"
[2/8] cmd.exe /C ""C:/Program Files/LLVM/bin/clang-scan-deps.exe" -format=p1689 -- C:\PROGRA~1\LLVM\bin\clang.exe -O0 -std=gnu++20 -D_DEBUG -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd -g -Xclang -gcodeview -x c++ D:/Libraries/Downloads/test/test.cppm -c -o CMakeFiles\test_lib.dir\test.cppm.obj -MT CMakeFiles\test_lib.dir\test.cppm.obj.ddi -MD -MF CMakeFiles\test_lib.dir\test.cppm.obj.ddi.d > CMakeFiles\test_lib.dir\test.cppm.obj.ddi"
[3/8] "C:\Program Files\CMake\bin\cmake.exe" -E cmake_ninja_dyndep --tdi=CMakeFiles\test_lib.dir\CXXDependInfo.json --lang=CXX --modmapfmt=clang --dd=CMakeFiles/test_lib.dir/CXX.dd @CMakeFiles/test_lib.dir/CXX.dd.rsp
[4/8] "C:\Program Files\CMake\bin\cmake.exe" -E __run_co_compile --tidy=clang-tidy;-checks=-*,readability-*;--extra-arg=-Xclang=-fprebuilt-module-path=D:/Libraries/Downloads/test/build/CMakeFiles/test_lib.dir/;-p=D:/Libraries/Downloads/test/build;--extra-arg-before=--driver-mode=g++ --source=D:/Libraries/Downloads/test/test.cppm -- C:\PROGRA~1\LLVM\bin\clang.exe -O0 -std=gnu++20 -D_DEBUG -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd -g -Xclang -gcodeview -MD -MT CMakeFiles/test_lib.dir/test.cppm.obj -MF CMakeFiles\test_lib.dir\test.cppm.obj.d @CMakeFiles\test_lib.dir\test.cppm.obj.modmap -o CMakeFiles/test_lib.dir/test.cppm.obj -c D:/Libraries/Downloads/test/test.cppm
D:\Libraries\Downloads\test\test.cppm:5:30: warning: 5 is a magic number; consider replacing it with a named constant [readability-magic-numbers]
auto f() -> int { return 1 + 5; }
                             ^
[5/8] cmd.exe /C "cd . && "C:\Program Files\CMake\bin\cmake.exe" -E rm -f test_lib.lib && C:\PROGRA~1\LLVM\bin\llvm-ar.exe qc test_lib.lib CMakeFiles/test_lib.dir/test.cppm.obj && C:\PROGRA~1\LLVM\bin\LLVM-R~1.EXE test_lib.lib && cd ."
[6/8] "C:\Program Files\CMake\bin\cmake.exe" -E cmake_ninja_dyndep --tdi=CMakeFiles\test.dir\CXXDependInfo.json --lang=CXX --modmapfmt=clang --dd=CMakeFiles/test.dir/CXX.dd @CMakeFiles/test.dir/CXX.dd.rsp
[7/8] "C:\Program Files\CMake\bin\cmake.exe" -E __run_co_compile --tidy=clang-tidy;-checks=-*,readability-*;--extra-arg=-Xclang=-fprebuilt-module-path=D:/Libraries/Downloads/test/build/CMakeFiles/test_lib.dir/;-p=D:/Libraries/Downloads/test/build;--extra-arg-before=--driver-mode=g++ --source=D:/Libraries/Downloads/test/main.cpp -- C:\PROGRA~1\LLVM\bin\clang.exe -O0 -std=gnu++20 -D_DEBUG -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd -g -Xclang -gcodeview -MD -MT CMakeFiles/test.dir/main.cpp.obj -MF CMakeFiles\test.dir\main.cpp.obj.d @CMakeFiles\test.dir\main.cpp.obj.modmap -o CMakeFiles/test.dir/main.cpp.obj -c D:/Libraries/Downloads/test/main.cpp
FAILED: CMakeFiles/test.dir/main.cpp.obj 
"C:\Program Files\CMake\bin\cmake.exe" -E __run_co_compile --tidy=clang-tidy;-checks=-*,readability-*;--extra-arg=-Xclang=-fprebuilt-module-path=D:/Libraries/Downloads/test/build/CMakeFiles/test_lib.dir/;-p=D:/Libraries/Downloads/test/build;--extra-arg-before=--driver-mode=g++ --source=D:/Libraries/Downloads/test/main.cpp -- C:\PROGRA~1\LLVM\bin\clang.exe -O0 -std=gnu++20 -D_DEBUG -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd -g -Xclang -gcodeview -MD -MT CMakeFiles/test.dir/main.cpp.obj -MF CMakeFiles\test.dir\main.cpp.obj.d @CMakeFiles\test.dir\main.cpp.obj.modmap -o CMakeFiles/test.dir/main.cpp.obj -c D:/Libraries/Downloads/test/main.cpp
D:/Libraries/Downloads/test/main.cpp:1:8: fatal error: module 'test' not found
import test;
~~~~~~~^~~~
1 error generated.
ninja: build stopped: subcommand failed.

```

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [June 19, 2023, 2:59pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/10 "2023-06-19T14:59:59Z")

</div>

> [@sharadhr](#):
>
> `@CMakeFiles\test.dir\main.cpp.obj.modmap`

Is `clang-tidy` not reading this file properly?

---

<div class="post-metadata">

### Author: ![Lecris](https://discourse.cmake.org/user_avatar/discourse.cmake.org/lecris/32/3193_2.png) [@Lecris](https://discourse.cmake.org/u/Lecris)
#### Post date: [June 19, 2023, 3:02pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/11 "2023-06-19T15:02:44Z")

</div>

I think we should create a clean example because when I tried it, clang-tidy failed regardless of cmake configuration, most probably because it does not read module files.

---

<div class="post-metadata">

### Author: ![sharadhr](https://discourse.cmake.org/user_avatar/discourse.cmake.org/sharadhr/32/3528_2.png) [@sharadhr](https://discourse.cmake.org/u/sharadhr)
#### Post date: [June 19, 2023, 3:12pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/12 "2023-06-19T15:12:20Z")

</div>

> [@ben.boeckel](#):
>
> Is `clang-tidy` not reading this file properly?

I think so. Like [I mentioned in the LLVM thread](https://discourse.llvm.org/t/clang-tidy-doesnt-find-standard-c-modules-despite-providing-compilation-database-and-prebuilt-modules-path/71326#:~:text=Even%20if%20I%20run%20clang%2Dtidy%20standalone%2C%20I%20receive%20the%20same%20error%3A), running `clang-tidy` standalone with clang-only tools (i.e. manually pre-compiling) also failed. Which is why I posted the issue there, too.

```auto
> clang-tidy.exe -checks="-*,readability-*" --extra-arg=-Xclang=-fprebuilt-module-path=D:/Libraries/Desktop/test/build/CMakeFiles/test.dir/ -p=D:/Libraries/Desktop/test/build --extra-arg-before=--driver-mode=cl .\main.cpp
1 error generated.
Error while processing D:\Libraries\Desktop\test\.\main.cpp.
D:\Libraries\Desktop\test\main.cpp:1:8: error: module 'test' not found [clang-diagnostic-error]
import test;
~~~~~~~^~~~
Found compiler error(s).

> clang-tidy.exe -checks="-*,readability-*" --extra-arg=-Xclang=-fprebuilt-module-path=D:/Libraries/Desktop/test/build/CMakeFiles/test_lib.dir/ -p=D:/Libraries/Desktop/test/build --extra-arg-before=--driver-mode=cl .\main.cpp
1 error generated.
Error while processing D:\Libraries\Desktop\test\.\main.cpp.
D:\Libraries\Desktop\test\main.cpp:1:8: error: module 'test' not found [clang-diagnostic-error]
import test;
~~~~~~~^~~~
Found compiler error(s).
> 

```

---

<div class="post-metadata">

### Author: ![danakj](https://discourse.cmake.org/user_avatar/discourse.cmake.org/danakj/32/4098_2.png) [@danakj](https://discourse.cmake.org/u/danakj)
#### Post date: [December 19, 2023, 8:36pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/13 "2023-12-19T20:36:27Z")

</div>

Hi, I am here after a lot of scouring the internet due to my own clang tool failing when CMake updated to 3.28. The root of the issue is related to the `@foo.modmap` files as discussed here, but I think the reasons discussed so far miss the cause.

[Nibble Stew: The road to hell is paved with good intentions and C++ modules](https://nibblestew.blogspot.com/2023/10/the-road-to-hell-is-paved-with-good.html?lr=1) explains (the author is not a fan, but please don’t focus on that here) what is going on during compilation with CMake now and what the `@foo.modmap` files are. I could not find any other docs that explained it.

The problem then is that CMake generates a @foo.modmap file _during compilation_ for each .cc file that it builds. The file is not created as a result of configure, and it’s not left behind after compilation, only the .obj file is left behind.

So when I run a clang tool against the command line specified in compile\_commands.json, it fails due to the `@foo.modmap` file not existing. AFAICT the command that CMake writes into the compile\_commands.json is incorrect, since it’s pointing to a file (the modmap file) which does not actually exist (outside of cmake doing compilation which is not what compile\_commands.json is for).

This renders the compile\_commands.json broken, and requires tools to strip out the `@foo.modmap` argument in order to run the clang tool against the command line arguments in the compile\_commands.json file. This will work until code actually uses modules at which point it will fall over again, as the root issue is unresolved: that the command line in the compile\_commands.json file as generated by CMake is not able to independently reproduce the compilation step.

FTR, I am using CMake 3.28.1, and this issue occurs on Windows and Linux, targeting Clang and MSVC. If CMake is wedded to the idea of using a modmap file during compilation, it needs to write them all to disk at the same time that it generates the compile\_commands.json.

My debugging FWIW: [Subdoc fails with missing modmap files · Issue #437 · chromium/subspace · GitHub](https://github.com/chromium/subspace/issues/437)

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [December 21, 2023, 1:48am UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/14 "2023-12-21T01:48:09Z")

</div>

> [@danakj](#):
>
> [Nibble Stew: The road to hell is paved with good intentions and C++ modules](https://nibblestew.blogspot.com/2023/10/the-road-to-hell-is-paved-with-good.html?lr=1) explains (the author is not a fan, but please don’t focus on that here) what is going on during compilation with CMake now and what the `@foo.modmap` files are. I could not find any other docs that explained it.

Please read [the Reddit comments](https://www.reddit.com/r/cpp/comments/17a4t6l/the_road_to_hell_is_paved_with_good_intentions/) on that blog post. I do need to write up docs on how CMake implements modules. It’s on my list.

> [@danakj](#):
>
> If CMake is wedded to the idea of using a modmap file during compilation, it needs to write them all to disk at the same time that it generates the compile\_commands.json.

Then it will need to wait until the build is complete. The `.modmap` files cannot be made at generate time without CMake having to regenerate the build on _every_ `.modmap`-using source file. At that point, the file is the same as made during configure and the `.modmap` files are present for use by any `compile_commands.json`-using tool. Note that there is also no safe place in the graph to place a command to generate `compile_commands.json` because:

- targets may be `EXCLUDE_ALL` and not build via the default target and still reference non-existent files
- if it needs updated (e.g., after a re-configure, it is out-of-date until regenerated)

Note that this has _always_ been an issue with build-generated sources or headers. There is a plan to solve this part of the problem in 2024 with something like a `generated-sources` target (name and details TBD) to generate all sources. A similar target to make `.modmap` files present would also be something that could be done at the same time.

> [@danakj](#):
>
> This will work until code actually uses modules at which point it will fall over again

If the projects do not actually use modules, they can set `CMAKE_CXX_SCAN_FOR_MODULES` to `0` to disable scanning (and therefore `.modmap` file usage).

Note that it will be the least of your worries as the BMIs that are made during the build will be compiler-specific and your tool will (likely) need to generate its own BMIs in order to perform the `import` statements in the analyzed. There are plans for generating enough information for tools to be able to generate their own BMIs in 2024 as well (future work can let CMake drive the tool to make these BMIs, but that is likely farther out).

---

<div class="post-metadata">

### Author: ![danakj](https://discourse.cmake.org/user_avatar/discourse.cmake.org/danakj/32/4098_2.png) [@danakj](https://discourse.cmake.org/u/danakj)
#### Post date: [December 22, 2023, 3:32am UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/15 "2023-12-22T03:32:58Z")

</div>

Thanks, I hope that this can improve in 2024, glad to know it’s on your radar.

One note: I do not see any modmap files in my CMakefiles directories, even after I have done a compile. There’s no point at which the compile\_commands.json command lines work for me. It seems that CMake generates them, does the compile, and then deletes them after the compilation is done.

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [December 22, 2023, 1:39pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/16 "2023-12-22T13:39:18Z")

</div>

`.modmap` files shouldn’t be deleted; I have tons of them in my testing tree. Can you provide a reproducer project to show the problem?

---

<div class="post-metadata">

### Author: ![Lecris](https://discourse.cmake.org/user_avatar/discourse.cmake.org/lecris/32/3193_2.png) [@Lecris](https://discourse.cmake.org/u/Lecris)
#### Post date: [December 22, 2023, 2:43pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/17 "2023-12-22T14:43:28Z")

</div>

> [@ben.boeckel](#):
>
> There is a plan to solve this part of the problem in 2024 with something like a `generated-sources` target (name and details TBD) to generate all sources.

That sounds promising. Basically it would call all `configure_file`, `custom_command`, and cmake native like the cxx module files generation? That would be useful for the cmake pre-commit.

Does this also solve the original issue with `clang-tidy` + `compile_commands.json`?

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [December 22, 2023, 3:03pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/18 "2023-12-22T15:03:42Z")

</div>

> [@Lecris](#):
>
> That sounds promising. Basically it would call all `configure_file`, `custom_command`, and cmake native like the cxx module files generation? That would be useful for the cmake pre-commit.

`configure_file` is configure-time; this would basically make sure any sources with the `GENERATED` property are up-to-date. Or similar; there are a lot of test cases to write and decide support for.

> [@Lecris](#):
>
> Does this also solve the original issue with `clang-tidy` + `compile_commands.json`?

For pre-modules code, yes.

---

<div class="post-metadata">

### Author: ![danakj](https://discourse.cmake.org/user_avatar/discourse.cmake.org/danakj/32/4098_2.png) [@danakj](https://discourse.cmake.org/u/danakj)
#### Post date: [December 22, 2023, 3:26pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/19 "2023-12-22T15:26:16Z")

</div>

> [@ben.boeckel](#):
>
> `.modmap` files shouldn’t be deleted; I have tons of them in my testing tree. Can you provide a reproducer project to show the problem?

This project: `https://github.com/chromium/subspace` on Windows.

In cmd:

```auto
git clone --recurse-submodules https://github.com/chromium/subspace.git test-sus
cd test-sus
vcvars64
"C:\Program Files\CMake\bin\cmake.exe" -B out -G Ninja -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo -DSUBSPACE_BUILD_TESTS=ON
"C:\Program Files\CMake\bin\cmake.exe" --build out

```

No modmap files found in `test-sus\out`. For example `test-sus\out\sus\CMakeFiles\subspace_unittests.dir\fn` contains only two `.obj` files.

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [December 22, 2023, 8:44pm UTC](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968/20 "2023-12-22T20:44:28Z")

</div>

Thanks, reproduced on Windows. And on Linux.

Ok, so the build doesn’t use modules, so we don’t actually do any modmap stuff. The `compile_commands.json` logic reimplements some flag detection; this needs reworked to match.

[Next page](https://discourse.cmake.org/t/how-to-control-the-location-of-the-c-20-binary-module-interface-bmi-output-directory/7968.md?page=2)
