It appears that when clang is invoked through CMake, the system includes are missing. Here is the minimal project that I used to replicate the behavior: cmake-include-fail.zip
Method: I started with a completely fresh install of MacOS 10.14.3. I downloaded and installed CMake 3.14.1 from source. I then installed brew per their instructions:
When I use my hand-rolled Makefile to build the project, /usr/local/include is correctly added to the include path make-success.log. When using the CMake generated Makefiles, /usr/local/include is dropped from the include path cmake-fail.log.
Edited
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Child items 0
Show closed items
No child items are currently assigned. Use child items to break down this issue into smaller parts.
Linked items 0
Link issues together to show that they're related.
Learn more.
Brad Kingchanged title from CMake drops system include paths on MacOS to macOS: /usr/local/include not a system include directory with Command-line Tools
changed title from CMake drops system include paths on MacOS to macOS: /usr/local/include not a system include directory with Command-line Tools
Brad Kingchanged title from macOS: /usr/local/include not a system include directory with Command-line Tools to macOS: /usr/local/include not a system include directory under CMake
changed title from macOS: /usr/local/include not a system include directory with Command-line Tools to macOS: /usr/local/include not a system include directory under CMake
CMake always adds -isysroot explicitly to get the macosx SDK. This behavior that was once required to get anything to compile on the command line. One can see that the flag affects whether /usr/local/include is considered an implicit include directory by the compiler.
The above command-line examples show that macOS command-line compilers no longer require an explicit -isysroot. However, since commit 1f085e11 CMake resolves /usr/bin/cc, /usr/bin/c++, and other macOS compiler wrappers that find a real compiler through xcrun to the path to that real compiler. See its commit message for why. Running the real compiler directly does not use any SDK by default:
In order to make /usr/local/include appear by default we'd need CMake to go back to using the plain /usr/bin/* compilers without unwrapping them, and to not always pass -isysroot. Both of these will require a deeper investigation to determine the larger impact, and likely a policy.
Even if we did both of those, then setting CMAKE_OSX_SYSROOT manually to point at a specific macosx SDK version would still cause /usr/local/include to not be a system include directory. Therefore projects should still be prepared to deal with such cases.
For the example project in the description, NLopt is an external dependency. The convention in CMake projects is to not rely on system include directories for anything outside the standard library. Therefore it should use find_package(NLopt) with a FindNLopt.cmake find module (which exist out there). Short of a full find module, one can ask CMake to find the external header file and add the appropriate include directory using this patch to the example project:
Thanks for your prompt investigation of the matter. I tried that in our real project; find_path() does, in fact, find nlopt.hpp just fine. The problem is that /usr/local/include now shows up in the search list with the rest of the explicit project includes, instead of at the end with the implicit system includes. In our case, this causes the brew-installed copy of protobuf to be picked up before the source copy in our project, which is untenable since they are different versions. I solved the problem by symlinking nlopt.hpp in /Library/Developer/CommandLine/Tools/usr/include, which is on the implicit include search list. That works well enough for now, but I am wondering if you intend to fix this?
Use the SYSTEM option to target_include_directories to tell CMake to treat the directory as a system include directory. FYI, in a full FindNLopt module the result of find_path would be put in the INTERFACE_INCLUDE_DIRECTORIES property of an imported target. When project targets link to that imported target they will get the include directory, and it will automatically be treated as a system include directory.
wondering if you intend to fix this?
Not directly because it is not actually a bug. With -isysroot, the compiler does not use /usr/local/include as an implicit include directory, and CMake is correctly capturing that state of affairs. It is then treated just like any other include directory. In earlier posts I mentioned other paths forward that would make /usr/local/include appear by default, but even if we did that users could still opt-in to -isysroot through CMAKE_OSX_SYSROOT and project code should still handle it.
I think you slightly misunderstood the nature of my issue. I was passing /usr/local/include with the SYSTEM directive. The problem is the order of the search path. By passing it explicitly, I get something like this:
<some project includes>/usr/local/include<some more project includes, including our protobuf dirctory><system implicit includes>
We need /usr/local/include to be at the end of the list with the other implicit includes in order for our version of protobuf to be found instead of the system version.
We are not using any Mac SDK; for us, it would be great if we could opt out of -isysroot altogether, either by setting a variable or policy. That is what I meant by "fix".
Thanks for the pointer - the reason that still doesn't work for us is that we are also including our copy of protobuf as a SYSTEM directory in order to suppress compiler warnings (we compile with -Werror). If this is not classified as a bug, can it be a feature request to be able to opt out of -isysroot?
I've opened #19180 (closed) to record the corresponding feature request to not use -isysroot unless explicitly requested. I'll close this issue in favor of that one, though the side discussions above can continue.