clang++ ignores -MD flag when I build with CMake and ninja

Viewed 689

I've created a very simple C++/CMake project:

CMakeLists.txt (note the -MD flag):

cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
add_executable(moop main.cc)
target_compile_options(moop PRIVATE -MD)

main.cc:

#include "moop.hh"
int main( int, char** ) { return 0; }

moop.hh:

#pragma once

From the project root, I run the following:

mkdir build && cd build
cmake -G Ninja -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..
cat compile_commands.json

cat compile_commands.json outputs (note the -MD flag):

{
  "directory": "/home/zbardoo/moop/build",
  "command": "/usr/bin/clang++      -MD -o CMakeFiles/moop.dir/main.cc.o -c /home/zbardoo/moop/main.cc",
  "file": "/home/zbardoo/moop/main.cc"
}

If I then run ninja, the executable moop is successfully built. However, moop.cc.d is nowhere to be found. But if I then copy and paste the command value from compile_commands.json and run it:

/usr/bin/clang++      -MD -o CMakeFiles/moop.dir/main.cc.o -c /home/zbardoo/moop/main.cc

The file /home/zbardoo/moop/build/CMakeFiles/moop.dir/main.cc.d appears:

zbardoo@localhost:~/moop/build$ cat CMakeFiles/moop.dir/main.cc.d 
CMakeFiles/moop.dir/main.cc.o: /home/zbardoo/moop/main.cc \
  /home/zbardoo/moop/moop.hh

Why doesn't ninja obey the -MD flag in the compile_commands.json file?

2 Answers

Clang is not the culprit, it's ninga

The explanation for the missing CMakeFiles/moop.dir/main.cc.d can be found if you look in the generated build/rules.ninja and find:

rule CXX_COMPILER__moop
  depfile = $DEP_FILE
  deps = gcc
  command = /usr/bin/clang++  $DEFINES $INCLUDES $FLAGS -MD -MT $out -MF $DEP_FILE -o $out -c $in
  description = Building CXX object $out

Note:

  deps = gcc

Then see the Ninja manual: C/C++ header dependencies -> deps:

deps (Available since Ninja 1.3.)

It turns out that for large projects (and particularly on Windows, where the file system is slow) loading these dependency files on startup is slow.

Ninja 1.3 can instead process dependencies just after they’re generated and save a compacted form of the same information in a Ninja-internal database.

Ninja supports this processing in two forms.

deps = gcc specifies that the tool outputs gcc-style dependencies in the form of Makefiles. Adding this to the above example will cause Ninja to process the depfile immediately after the compilation finishes, then delete the .d file (which is only used as a temporary).

(My emphasis).

It looks like the equivalent header dependency information is available through ninja -t deps:

zbardoo@localhost:~/moop/build$ ninja -t deps
CMakeFiles/moop.dir/main.cc.o: #deps 2, deps mtime 1547243911 (VALID)
    ../main.cc
    ../moop.hh

However, it appears that this dependency information is available whether or not I specify -MD through target_compile_options.

Related