MaskRay / ccls

C/C++/ObjC language server supporting cross references, hierarchies, completion and semantic highlighting
Apache License 2.0
3.72k stars 254 forks source link

Unity build indexing cpp files #948

Closed agudbrand closed 11 months ago

agudbrand commented 11 months ago

I'm trying to get ccls to work with Unity builds, builds where you only use one translation unit. I have the following example project: main.cpp:

#include "Apple.h"
#include "Apple.cpp"

int main() {
   Apple a;
   eat(a);
}

Apple.h:

struct Apple {
  f32 nutrients;
};

Apple.cpp:

void eat(Apple& a) {
  a.nutrients -= 5.0;
}

Observed behavior

I compile with the command: clang++ main.cpp

And I use BEAR to generate a compile_commands.json for this build, which results in:

[
  {
    "arguments": [
      "/usr/bin/clang++",
      "-c",
      "main.cpp"
    ],
    "directory": "/home/sushi/src/test",
    "file": "/home/sushi/src/test/main.cpp"
  },
  {
    "arguments": [
      "/usr/bin/clang-16",
      "-cc1",
      "-triple",
      "x86_64-pc-linux-gnu",
      "-emit-obj",
      "-mrelax-all",
      "-disable-free",
      "-clear-ast-before-backend",
      "-disable-llvm-verifier",
      "-discard-value-names",
      "-main-file-name",
      "-mrelocation-model",
      "pic",
      "-pic-level",
      "2",
      "-pic-is-pie",
      "-mframe-pointer=all",
      "-fmath-errno",
      "-ffp-contract=on",
      "-fno-rounding-math",
      "-mconstructor-aliases",
      "-funwind-tables=2",
      "-target-cpu",
      "x86-64",
      "-tune-cpu",
      "generic",
      "-mllvm",
      "-treat-scalable-fixed-error-as-warning",
      "-debugger-tuning=gdb",
      "-fcoverage-compilation-dir=/home/sushi/src/test",
      "-resource-dir",
      "/usr/lib/clang/16",
      "-internal-isystem",
      "/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../include/c++/13.2.1",
      "-internal-isystem",
      "/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../include/c++/13.2.1/x86_64-pc-linux-gnu",
      "-internal-isystem",
      "/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../include/c++/13.2.1/backward",
      "-internal-isystem",
      "/usr/lib/clang/16/include",
      "-internal-isystem",
      "/usr/local/include",
      "-internal-isystem",
      "/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../x86_64-pc-linux-gnu/include",
      "-internal-externc-isystem",
      "/include",
      "-internal-externc-isystem",
      "/usr/include",
      "-fdeprecated-macro",
      "-fdebug-compilation-dir=/home/sushi/src/test",
      "-ferror-limit",
      "19",
      "-stack-protector",
      "2",
      "-fgnuc-version=4.2.1",
      "-fcxx-exceptions",
      "-fexceptions",
      "-fcolor-diagnostics",
      "-faddrsig",
      "-D__GCC_HAVE_DWARF2_CFI_ASM=1",
      "-x",
      "c++",
      "-o",
      "/tmp/main-56cf55.o",
      "main.cpp"
    ],
    "directory": "/home/sushi/src/test",
    "file": "/home/sushi/src/test/main.cpp",
    "output": "/tmp/main-56cf55.o"
  }
]

The file Apple.cpp is not there (as it would be if I structured the project normally and ran bear -- clang++ main.cpp Apple.cpp) which I figure is why ccls does not even activate in my editor (neovim with coc.nvim) when I open Apple.cpp.

Expected behavior

I expect that ccls activates and is able to provide information about the Apple struct inside of Apple.cpp, but I don't know of anyway to force it to activate here.

I think that a long time ago I had tried to force ccls to recognize it by putting the cpp files into ccls, but that causes a bunch of errors since it thinks that the cpp file is self contained.

System information

I figure I'm missing something, but I've been over the docs several times and I can't figure out how to get ccls to work in non-self-contained cpp files. The only cpp language server I've seen be able to do this is intellisense, but I want to move away from VSCode so bad.

FAKERINHEART commented 11 months ago

You include the Apple.cpp, so it is not a compilation unit.

agudbrand commented 11 months ago

You include the Apple.cpp, so it is not a compilation unit. Yep.

I managed to find a way to trick clangd into working correctly, and I assume it would work fine with ccls too incase anyone else ever runs into this issue. I wrote a script to generate the compile_commands.json file manually. Since we typically write our own compiling scripts in our projects, we have access to all compiler switches, but I figure that for projects using a system like make you should be able to just directly modify the compile_commands.json file with the following method.

Clang allows you to specify an '-include' switch, which just tells it to treat the file its going to compile as if whatever follows that switch as having been #included into the file. So I wrote a script to extract all the headers that we include in our project's main.cpp file. But since the compile_commands.json file will only generate an entry for the main.cpp file, you also have to go and generate entries for all other cpp files included in you main file as well. Finally you just '-include' all of your headers into all of the cpp files and it works perfectly.