Closed joelhock closed 6 years ago
but it's a difficult road to navigate.
Yes, parsing the project config is easily the most convoluted part of cquery. libclang does not parse arguments as clang does (it misses some functionality, like system directory resolution), and cquery also supports non-clang style arguments.
The FindSystemIncludeDirectory logic is important because by default libclang will fail to resolve things like <vector>
. I'd be very happy to have a PR improving the logic around it.
It seems libclang looks for the libc++ includes in ../include
relative to the realpath of libclang.dylib
, which the downloaded tar correctly positions (and brew and xctoolchain from what I can tell), but I've certainly used libclang.dylib
s where ../include
didn't exist. And, now I remember that I set -target x86_64-unknown-linux-gnu
, mainly because we're doing cross-platform work and are targeting linux from a mac dev host (so we need the target to set the correct default linux defines), but that setting the target triple also undoes some of the braindeadness in mac libclang that causes it to not respect -resource-dir
and perhaps other oddities, so I'm a bizarre user.
Anyway, here are two suggestions. I can make a PR that does either one or both, and it might be best to just wait to see if anyone else chimes in as to whether these would be helpful.
GetSystemIncludes
results via -idirafter
/ --include-directory-after=
so that the libclang built-ins are preferred, if available.-nostdinc
, -nostdinc++
, -nobuiltininc
, and -stdlib=libstdc++
to GetSystemIncludes
's kFlagsToPass
The above sounds reasonable to me. wdyt @bstaletic?
@joelhock Thanks for reporting this.
Adding -nostdinc
and others mentioned in point 2 sounds good.
As for -idirafter
, it shouldn't be necessary because the associated path shall be picked up in IncludeComplete::Rescan()
. Note that for this to work the -idirafter
needs to exist in kPathArgs
in src/project.cc
. This is fine for -idirafter
, but --include-directory-after
is not there.
yes, i noticed i would have to add --include-directory-after
to kPathArgs
to use it; i'll do that anyway as a separate PR and also make a PR for point 2 of my list.
@bstaletic Sorry, I'm not following what you're saying about IncludeComplete::Rescan()
. Using -idirafter
instead of -isystem
in ExtractSystemIncludePaths
does make a difference for me. The clang binary that gets executed is the system clang, so its (incompatible) resource dir gets added via -isystem/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.1.0/include
, and this trumps the resource dir that the downloaded libclang has baked in. When that system path is instead added via -idirafter
, it is searched after the resource dir that is baked-in to libclang, so the correct one is used. At least "correct" from my perspective; I wouldn't be shocked if someone else expected the reverse to be true, but the current setup does seem much more likely to foist an incompatible resource dir onto libclang.
Yes, -idirafter
does change system headers. What I'm saying is that it shouldn't matter. The only reason you would want to put something in the FindSystemIncludeDirectories()
is to have cquery find "implicit" include paths. -idirafter
requires you to write a path as well, which means that that include path will be picked up when cquery scans the directories for include headers, which happens in IncludeComplete::Rescan()
. If we place -idirafter
in kPathArgs
and kFlagsToPass
, the path associated with -idirafter
will end up in your flags twice, once with -isystem
and once with -idirafter
.
ok, put the couple actionable items in referenced PRs. feel free to close this.
@bstaletic after re-reading your last comment, I think I was miscommunicating. I was ultimately suggesting something like this:
+++ b/src/clang_system_include_extractor.cc
@@ -50,7 +50,7 @@ std::vector<std::string> ExtractSystemIncludePaths(
line = line.substr(0, line.size() - sizeof(kIncludePostfixToRemove) + 1);
output.push_back("-iframework" + line);
} else {
- output.push_back("-isystem" + line);
+ output.push_back("-isystem-after" + line);
}
}
If I'm not understanding what you're saying, feel free to ignore me. If it's a problem for other people, I'm sure it'll come up again.
I couldn't find the the documentation on -isystem-after
. Does it "just" add system include paths after -isystem
? If so then I'm not sure if it would make any difference.
Note that -isystem
flags are parsed "left to right" and that the "discovered" system paths are (or at least should be) appended to the user provided flags. Therefore the discovered paths come after user specified paths.
@bstaletic, it turns out -isystem-after
is supported only on the windows version of clang, although
This concept is actually useful on other targets (e.g. Linux) and would be really easy to support on the core toolchain. http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20160613/162393.html
And, actually it seems that -idirafter
does everything that I was expecting -isystem-after
to do, so just pretend I never mentioned -isystem-after
.
I realize the paths discovered by FindSystemIncludeDirectories()
will come after the user-provided flags. I was proposing something that would also put them after the directories that are implicitly added by libclang itself, namely the resource dir, since the resource dir found for the system clang (in my case) by FindSystemIncludeDirectories()
is not compatible with the libclang.dylib downloaded and used by cquery.
FindSystemIncludeDirectories()
for me runs clang++ -E -xc++ - -v -isystem/Volumes/hrt < /dev/null
and finds:
#include <...> search starts here:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1
/usr/local/include
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.1.0/include
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
/usr/include
/System/Library/Frameworks (framework directory)
/Library/Frameworks (framework directory)
End of search list.
the downloaded libclang is compatible with the resource dir discovered by this (if i copy the included binary to the logical location):
/usr/local/lib/clang+llvm-6.0.0-x86_64-apple-darwin/bin/clang -E -xc++ - -v < /dev/null
#include <...> search starts here:
/usr/local/include
/usr/local/lib/clang+llvm-6.0.0-x86_64-apple-darwin/lib/clang/6.0.0/include
/usr/include
/System/Library/Frameworks (framework directory)
/Library/Frameworks (framework directory)
End of search list.
so if I have a project where -isystem/Users/joel/myproj-system-includes
is a user-provided flag, the effective search path for the current code is:
/usr/local/lib/clang+llvm-6.0.0-x86_64-apple-darwin/bin/clang -E -xc++ - -v -isystem/Users/joel/myproj-system-includes -isystem/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1 -isystem/usr/local/include -isystem/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.1.0/include -isystem/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -iframework/System/Library/Frameworks -iframework/Library/Frameworks < /dev/null
#include <...> search starts here:
/Users/joel/myproj-system-includes
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1
/usr/local/include
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.1.0/include
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
/System/Library/Frameworks (framework directory)
/Library/Frameworks (framework directory)
/usr/local/lib/clang+llvm-6.0.0-x86_64-apple-darwin/lib/clang/6.0.0/include
/usr/include
End of search list.
which results in errors like this when parsing a file: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.1.0/include/emmintrin.h2261:19 use of undeclared identifier '__builtin_ia32_pavgb128'
, since the resource dir for the system clang is searched before the resource dir for the downloaded clang.
if instead we use idirafter
like: /usr/local/lib/clang+llvm-6.0.0-x86_64-apple-darwin/bin/clang -E -xc++ - -v -isystem/Users/joel/myproj-system-includes -idirafter/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1 -idirafter/usr/local/include -idirafter/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.1.0/include -idirafter/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -iframework/System/Library/Frameworks -iframework/Library/Frameworks < /dev/null
we get:
/Users/joel/myproj-system-includes
/System/Library/Frameworks (framework directory)
/Library/Frameworks (framework directory)
/usr/local/include
/usr/local/lib/clang+llvm-6.0.0-x86_64-apple-darwin/lib/clang/6.0.0/include
/usr/include
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.1.0/include
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
End of search list.
and the resource dir that libclang is compatible with is found first. It would seem to me that many users would run into this; the fact that they haven't suggests maybe something is even weirder with my setup than i thought.
I'm on my phone, so excuse my brevity.
MacOS and iOS have been known to cause these issues. The problem is that the downloaded (open source) libclang lacks Apple's special sauce to make it work properly. System includes are just the beginning of the story. The open source libclang won't even be able to parse files in /Applications/Xcode/whatever
.
Your best bet is to avoid open source libclang on Apple operating systems.
@jacobdufault I wonder if there is anything we can do to lower the number of "cquery is broken" reports when it is just "Apple broke open source libclang".
I'm not sure, if we get a significant number of bug reports we can try adding more diagnostics.
Unfortunately, ycmd did have enough of reports exactly like this one for me to remember that open source libclang is broken on an OS I don't use.
It's rather simple - if the project is an Xcode project, bundled libclang will have problems parsing it.
Examples of this kinds of issues:
Slightly related but not exactly the same (Apple shuffling system include paths, just to screw with people leading projects like cquery and ycmd):
Another thing I noticed while searching through our issue tracker, MacOS users tend to use --system-libclang
.
Yikes! That's a thorough documentation of a hard road ycmd trekked through that cquery would be wise to learn from. I'd appreciate if you didn't have to go through extreme contortions to use the open-source clang download, but it makes sense to me after reading those posts that having system clang be the default on mac has its advantages.
77d6ff0da7 enabled the
FindSystemIncludeDirectories
functionality for mac. My project is perhaps a bit weird, but we are doing cross-platform development for a linux target on a mac editor, using a.cquery
file.FindSystemIncludeDirectories
passes only--gcc-toolchain
and--sysroot
on to the discovery, but other flags also influence the output of the command, namely-nostdinc
,-nostdinc++
,-nobuiltininc
,-resource-dir=xxxxx
, and-stdlib=xxxxx
.The
FindSystemIncludeDirectories
logic seems to undo the-resource-dir
handling that is curated through the rest of the code, since the clang command does not receive any-resource-dir
flag (or the baked-in hardcode), and its output flags will take precedence over any-resource-dir
-induced flags. The result being the flags gotten from the clang binary used for compilation may have an incompatible resource dir wrt the libclang that cquery uses.I'm happy to make a pull request, but I'm not sure what the philosophy is behind
FindSystemIncludeDirectories
, so comments are welcome. PerhapsFindSystemIncludeDirectories
should always specify-nobuiltininc
sinceresource-dir
is handled by other parts of the code. I can just-I
the resource dir in my.cquery
file, but I feel like the current code exacerbates differences between the cquery libclang and the project clang binary in an unexpected way. I doubt there's a "right" answer here, but perhaps there's something better--certainly cquery is doing an admirable job in getting sane compilation flags, but it's a difficult road to navigate.