clang++ does not respect -isystem flag, different behavior from g++

Viewed 397

On my macOS system, I have the following installed:

  • Protobuf 3.14.0 via brew install protobuf@3.14
  • g++ 10.2.0_4 via brew install gcc@10
  • clang++ 1200.0.32.29 via XCode

When I check out Google's Protobuf repo and build a file using clang++, it seems to ignore the -isystem flag that I pass:

git clone https://github.com/protocolbuffers/protobuf.git && cd protobuf && git checkout 326ea555b
clang++ -std=c++17 -isystem src -c src/google/protobuf/any_lite.cc

This gives an error:

src/google/protobuf/any_lite.cc:56:19: error: return type of out-of-line definition of 'google::protobuf::internal::AnyMetadata::InternalPackFrom' differs from that in the declaration
bool AnyMetadata::InternalPackFrom(const MessageLite& message,
~~~~              ^
/usr/local/include/google/protobuf/any.h:108:8: note: previous declaration is here
  void InternalPackFrom(const MessageLite& message,
  ~~~~ ^
1 error generated.

This happens because #include <google/protobuf/any.h> finds the file /usr/local/include/google/protobuf/any.h. I would expect it to find the file src/google/protobuf/any.h, because that file exists and I passed -isystem src. The signature of the private function InternalPackFrom has changed since version 3.14.0, hence the error.

Also, when I try replacing clang++ with g++-10, I get a successful build. (I was under the impression that Clang strives for flag-for-flag compatibility with GCC):

git clone https://github.com/protocolbuffers/protobuf.git && cd protobuf && git checkout 326ea555b
g++-10 -std=c++17 -isystem src -c src/google/protobuf/any_lite.cc

Why does clang++ ignore the -isystem flag here?

2 Answers

That's not because of -isystem src. The problem happens because clang++ contains the path /usr/local/include in its list of standard include directories, and g++ doesn't.

You can check the list of standard include directories with the following commands:

clang++ -nostdlib -Wp,-v -E -

g++-10 -Wp,-v -E -

To avoid including /usr/local/include/google/protobuf/any.h by clang++ you can either remove the file or use -nostdinc and -nostdinc++ flags and pass old the include directories manually.

Combining answers/comments from Anton Malyshev and Ave Milia, the apparent reason is that Apple's custom clang++ version (installed when you install XCode) always inserts /usr/local/include at the start of the system include path list, above any other entries, including -isystem options passed on the command line. (This is probably a bug). You can confirm this by running

clang++ -isystem /tmp -Wp,-v -E -

and noting the lines

#include <...> search starts here:
 /usr/local/include
 /tmp

in the output.

Neither upstream clang++ nor g++ have this behavior, so it is a quirk of Apple's build.

To fix this problem, you can install llvm with Homebrew:

brew install llvm

and then put Homebrew's clang++ on your path by following the instructions output by

brew link llvm
Related