LLVM Discussion Forums

In included file: 'stdlib.h' file not found

I’m running clangd (v10) with a large/complex project. I’ve tried generating a compile_commands in several different ways (all have the same issue). When I open a TU, It quickly hit the pp_file_not_found error shown in the title and at that point it seems like most of the processing stops and I end-up with lots my symbols/types not found.

What’s going on here? What kind of config hints do I need to give to clangd so that it can find stdlib and other system headers? I should also mention that my project uses gcc to compile and thus the command entries in compile_commands all use the gcc/g++ path.

I’ve tried running clangd with verbose logging and I can see that it’s getting my compile commands info just fine, although I do also see that it’s deciding to pass -resource-dir=/home/linuxbrew/.linuxbrew/Cellar/llvm/10.0.1/lib/clang/10.0.1 which seems a little odd if I’m compiling with gcc, but probably not a big deal.

What other diagnostics/logging can I look out to figure out why I’m getting this error?

Thanks.

The usual setup is that clang/clangd will detect standard library etc paths according to standard system paths and also looking at the command in compile_commands.json. It’s meant to “just work” but this is somewhat fragile.

It works pretty well on Linux, though I know the Mac driver has some modifications (the “system compiler” is always clang these days, so it might not be as good at detecting gcc installs).

The extra info that would be useful is:

  • the compile command (particularly any specified gcc path, and any -I, -isystem, etc flags)
  • the actual path to the stdlib.h file that’s found by gcc but not clangd
  • the “cc1 command” from the verbose logs (this is after the driver is done probing the system, so any implicit -I lines are added etc)

As for workarounds, you need some extra include paths to the command clangd uses to parse the file. Doing this by hand in compile_commands.json is obviously awkward, but there are better ways:

  • in clangd 11 (currentnly in RC) you can use the configuration file to inject flags such as -I
  • in earlier versions, I believe clangd will respect the C_INCLUDE_PATH and CXX_INCLUDE_PATH environment variables - this is an unintended feature but you may be able to make use of it.

@sam-mccall thanks for your reply. Here is the only relevant verbose log output I could find:

[10:26:39.565] Updating file /home/mellery/work/....../main.cpp with command
[/home/mellery/.cache/bazel/_bazel_mellery/b052ff1511dd109786611a0d4bd0bd6c/execroot]
/usr/bin/g++ --driver-mode=g++ <SOME PROJECT DEFINES> -DLINUX -DNDEBUG -O3 -fopenmp -std=gnu++14 <HUNDREDS of project internal paths included with mix of -I and -isystem> -isystem /usr/local/cuda/include -I. -Ibazel-out/k8-opt/bin -x c++ -c ...../main.cpp -fsyntax-only -resource-dir=/home/linuxbrew/.linuxbrew/Cellar/llvm/10.0.1/lib/clang/10.0.1
V[10:26:39.568] Driver produced command: cc1 -cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -disable-free -disable-llvm-verifier -discard-value-names -main-file-name main.cpp -mrelocation-model static -mthread-model posix -mframe-pointer=none -fmath-errno -fno-rounding-math -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /home/linuxbrew/.linuxbrew/Cellar/llvm/10.0.1/lib/clang/10.0.1 <HUNDREDS OF INCLUDES> <SOME PROJECT DEFINES> -D LINUX -D NDEBUG -I . -I bazel-out/k8-opt/bin -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/x86_64-linux-gnu/c++/9 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/x86_64-linux-gnu/c++/9 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/backward -internal-isystem /usr/local/include -internal-isystem /home/linuxbrew/.linuxbrew/Cellar/llvm/10.0.1/lib/clang/10.0.1/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -std=gnu++14 -fdeprecated-macro -fdebug-compilation-dir /home/mellery/.cache/dazel/_dazel_mellery/b052ff1511dd109786611a0d4bd0bd6c/execroot -ferror-limit 19 -fmessage-length 0 -fopenmp -fgnuc-version=4.2.1 -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -vectorize-loops -vectorize-slp -faddrsig -x c++ ..../main.cpp
V[10:26:39.568] Building first preamble for /home/mellery/work/sdk/tools/experimental/mapperception/map_fusion/main.cpp
V[10:26:41.245] index AST for ...../main.cpp (main=false):
  symbol slab: 42863 symbols, 14047880 bytes
  ref slab: 0 symbols, 0 refs, 136 bytes
  relations slab: 1143 relations, 34840 bytes

Note that I redacted any include paths (hundreds of them…) that are project internal. We use a mix of -I and -isystem to include those paths. The entire cc1 command ends-up being about 6800 chars long, and it did cross my mind that it could be exceeded some arg/length limit, although I see no message to that effect anywhere.

I notice above that it’s picking-up some stdlib 9 paths…that’s seems a little odd to me. I’m pretty sure the stdlib file that gcc-7 would use is /usr/include/c++/7/stdlib.h.

The platform is linux, ubuntu 18.04. Gcc version that I’m building with is

13:08 $ /usr/bin/g++ --version
g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

When I query that gcc for includes, I get:

13:18 $ /usr/bin/g++ -E -x c++ - -v < /dev/null
Using built-in specs.
COLLECT_GCC=/usr/bin/g++
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 7.5.0-3ubuntu1~18.04' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
COLLECT_GCC_OPTIONS='-E' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/7/cc1plus -E -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE - -mtune=generic -march=x86-64 -fstack-protector-strong -Wformat -Wformat-security
ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/7"
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/7/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/include/c++/7
 /usr/include/x86_64-linux-gnu/c++/7
 /usr/include/c++/7/backward
 /usr/lib/gcc/x86_64-linux-gnu/7/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/7/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "<stdin>"
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-E' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'

clangd/llvm is installed using linuxbrew…that’s mostly because I’m lazy and don’t want to build from source, but I could try other apt packages if you think that could make a difference.

One other note: I did try explicitly adding the include paths from g++ (above) to my compile commands and it didn’t make any difference.

Thanks for the details!

Sorry, I should have read more carefully, I saw “cellar” and started thinking about Mac.

My best guess is the version 9 directories are indeed the problem, my recollection is that the clang driver will try to find the highest-numbered subdirectory under certain paths. So maybe you have some parts of GCC 9 but not libstdc++, so clangd is getting confused.

If this is right, it suggests a few possible fixes:

  • install libstdc++ 9 so clangd can find it
  • remove all traces of GCC 9 so clangd doesn’t get confused (not recommended, may break stuff)
  • use the --query-driver=/usr/bin/g++ flag to clangd, which will try to request the right paths from GCC (similar to how you did)

Not sure why modifying compile_commands didn’t work, though

I uninstalled all gcc-8 and 9 packages and stdlibs, so I think I’ve eliminated that oddity BUT I still have the original error (stdlib.h not found). That said, I can get the error to change to some other missing header file by moving things around in the command line in the DB. This is a huge project (LOTS of sources and include paths) and it’s just really challenging to figure out what’s setting clangd off course. Could be pragmas, bad include paths, conflicting files…we have all those things and more in a large code-base. I’ll keep digging around, but I suspect the only way I’m going to be able to use clangd is with heuristic/approximate command lines that perhaps make some simplifying assumptions. That’s ultimately less accurate, but at least it gets you something that works.

Thanks for your help - I’ll keep digging into it as time permits.

I think I figured it out. I tracked down the problematic include chain to:

#include <string>
  #include <bits/basic_string.h>
    #include <ext/string_conversions.h>
      #include <cstdlib>
        #define _GLIBCXX_INCLUDE_NEXT_C_HEADERS
        #include_next <stdlib.h>
        #undef _GLIBCXX_INCLUDE_NEXT_C_HEADERS

which lead me down the rabbit hole of GNU “Wrapper Headers”. I think the real stdlib.h we want is in /usr/include…and If I understand correctly, that means that include path needs to come AFTER the c++ stdlib header that is wrapping it. I then realized that I had a few explicit -isystem /usr/include directives in my command line preceding the stdlib paths that clang inserts later on…I think the explicit specifying of /usr/include was messing with the include_next nonsense. When I removed them, it started working.

Moral: don’t explicitly list /usr/include as one of your build include paths…let the clang driver insert it (but only after the stdlib paths).