mesonbuild / meson

The Meson Build System
http://mesonbuild.com
Apache License 2.0
5.57k stars 1.62k forks source link

Incorrect path for precompiled headers in `compile_commands.json` #10590

Open Krasjet opened 2 years ago

Krasjet commented 2 years ago

Describe the bug The -include path for precompiled headers in compile_commands.json file does not include parent directories. Because the path is incorrect, clangd cannot find any headers specified in the precompiled header file and hence cannot provide any completions and diagnoses for those headers.

To Reproduce

The files and directory layout are as follows

.
├── hello.cpp
├── meson.build
└── pch
    └── hello_pch.hpp

pch/hello_pch.hpp

#include <iostream>

hello.cpp

int main()
{
  std::cout << "hello, world!\n";
  return 0;
}

meson.build

project('hello', 'cpp')

executable('hello', 'hello.cpp',
           cpp_pch: 'pch/hello_pch.hpp')

Notice that the precompiled header needs to be placed in a different directory as the source file, as mentioned in the documentation.

After building the project

$ meson build
$ meson compile -C build

There will be a compile_commands.json file generated in the build directory.

[
  {
    "directory": "/pch_test/build",
    "command": "c++ -Ihello.p -I. -I.. -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -O0 -g -MD -MQ hello.p/hello_pch.hpp.gch -MF hello.p/hello_pch.hpp.gch.d -o hello.p/hello_pch.hpp.gch -c ../pch/hello_pch.hpp",
    "file": "../pch/hello_pch.hpp",
    "output": "hello.p/hello_pch.hpp.gch"
  },
  {
    "directory": "/pch_test/build",
    "command": "c++ -Ihello.p -I. -I.. -fdiagnostics-color=always -fpch-preprocess -include hello_pch.hpp -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -O0 -g -MD -MQ hello.p/hello.cpp.o -MF hello.p/hello.cpp.o.d -o hello.p/hello.cpp.o -c ../hello.cpp",
    "file": "../hello.cpp",
    "output": "hello.p/hello.cpp.o"
  }
]

Notice that for the command of hello.cpp, the include flag is -include hello_pch.hpp, but the precompiled header is not present in the build directory.

Expected behavior

Either -include pch/hello_pch.hpp or -include ../pch/hello_pch.hpp should be generated in compile_commands.json.

system parameters

eli-schwartz commented 2 years ago

The path is correct. A precompiled header must be precompiled.

    "command": "c++ -Ihello.p [...] -fpch-preprocess -include hello_pch.hpp  [...]",

When it sees -include hello_pch.hpp, it checks every include directory for a file named hello_pch.hpp .gch, including:

The second location is a path match. Attempting to include ../pch/hello_pch.hpp would mean that the precompiled headers simply would never be found at all, ever, because that path cannot be resolved as a pch file.

Krasjet commented 2 years ago

This is strange. It seems that clangd cannot provide any diagnosis information based on the .gch/.pch file alone.

Here is an equivalent example with cmake:

.
├── hello.cpp
├── CMakeLists.txt
└── pch
    └── hello_pch.hpp

pch/hello_pch.hpp

#include <iostream>

hello.cpp

int main()
{
  std::cout << "hello, world!\n";
  return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(hello LANGUAGES CXX)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

add_executable(hello hello.cpp)
target_precompile_headers(hello PUBLIC pch/hello_pch.hpp)

After building the project, clangd has no problem providing diagnosis info using the generated compile_commands.json

$ mkdir build && cd build
$ CXX=clang++ cmake ..
$ make
[
{
  "directory": "/pch_cmake/build",
  "command": "/usr/bin/clang++    -Winvalid-pch -fpch-instantiate-templates -Xclang -emit-pch -Xclang -include -Xclang /pch_cmake/build/CMakeFiles/hello.dir/cmake_pch.hxx -x c++-header -o CMakeFiles/hello.dir/cmake_pch.hxx.pch -c /pch_cmake/build/CMakeFiles/hello.dir/cmake_pch.hxx.cxx",
  "file": "/pch_cmake/build/CMakeFiles/hello.dir/cmake_pch.hxx.cxx"
},
{
  "directory": "/pch_cmake/build",
  "command": "/usr/bin/clang++    -Winvalid-pch -Xclang -include-pch -Xclang /pch_cmake/build/CMakeFiles/hello.dir/cmake_pch.hxx.pch -Xclang -include -Xclang /pch_cmake/build/CMakeFiles/hello.dir/cmake_pch.hxx -o CMakeFiles/hello.dir/hello.cpp.o -c /pch_cmake/hello.cpp",
  "file": "/pch_cmake/hello.cpp"
}
]

Notice that here cmake is actually including a new generated header called cmake_pch.hxx

/* generated by CMake */

#pragma clang system_header
#ifdef __cplusplus
#include "/pch_cmake/pch/hello_pch.hpp"
#endif // __cplusplus

Without it, clangd cannot find the includes from hello_pch.hpp. Is it possible to do something similar with meson?


p.s. I noticed that clangd is not compatible with gcc-generated pre-compiled headers, though after switching to clang, the meson example still doesn't work. The new compile_commands.json is

[
  {
    "directory": "/pch/build",
    "command": "clang++ -Ihello.p -I. -I.. -fcolor-diagnostics -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -O0 -g -MD -MQ hello.p/hello_pch.hpp.pch -MF hello.p/hello_pch.hpp.pch.d -o hello.p/hello_pch.hpp.pch -c ../pch/hello_pch.hpp",
    "file": "../pch/hello_pch.hpp",
    "output": "hello.p/hello_pch.hpp.pch"
  },
  {
    "directory": "/pch/build",
    "command": "clang++ -Ihello.p -I. -I.. -fcolor-diagnostics -include-pch hello.p/hello_pch.hpp.pch -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -O0 -g -MD -MQ hello.p/hello.cpp.o -MF hello.p/hello.cpp.o.d -o hello.p/hello.cpp.o -c ../hello.cpp",
    "file": "../hello.cpp",
    "output": "hello.p/hello.cpp.o"
  }
]
Krasjet commented 2 years ago

This seems to be related to https://github.com/clangd/clangd/issues/856 and https://github.com/clangd/vscode-clangd/issues/324.

arch1t3cht commented 1 year ago

Tools like clangd explicitly disable all PCH usage (e.g. see https://github.com/llvm/llvm-project/blob/96f303324f58a58a7e58d75f797793c66606e745/clang-tools-extra/clangd/Compiler.cpp#L61-L68), so these kinds of PCH includes in compile_commands.json will just be ignored, which will break any language server features involving these headers.

Would it maybe be possible for meson to replace such -include-pch directives with -include directives pointing to the original (not precompiled) headers when generating compile_commands.json?

eli-schwartz commented 1 year ago

No, because meson doesn't produce compile_commands.json, it simply instructs ninja to emit one based on the ninja rules.

CrendKing commented 4 months ago

Is this being fixed? What is the recommended workaround? I use VSCode C/C++ extension and has the same problem. Currently have to add

#if __INTELLISENSE__
#include "pch/pch.h"
#endif

at the top of every file, which is not good. And I think it only works for the C/C++ extension, not every other language server like clangd.

arch1t3cht commented 4 months ago

If it helps, I ended up just using a manually written sed command to patch my compile_commands.json after every reconfigure... It's stupid but at least it works.

CrendKing commented 4 months ago

Thanks. Just wrap the meson setup in a script that adds -include ../pch/pch.h works. Ugly though.