[CMake] CMake performs search for includes/libs in non-default compiler search paths.

Michael Wild themiwi at gmail.com
Fri Jan 28 00:44:19 EST 2011


On 01/27/2011 08:08 PM, Óscar Fuentes wrote:
> Michael Wild <themiwi at gmail.com> writes:
> 
>>> Okay, cmake removes duplicated directories that already are on the
>>> list. So what?
>>>
>>> First, I hope that cmake does not optimize this series:
>>>
>>> A B C A
>>>
>>> ... to this:
>>>
>>> A B C
>>>
>>> or to this:
>>>
>>> B C A
>>>
>>> Removing adjacent duplicates is fine, but removing duplicates in general
>>> makes difficult to debug the build and may affect semantics.
>>>
>>> Second, the topic here is that we are forced to jump through hops
>>> because cmake tries to be helpful. It looks for files into places but
>>> then it is up to us to figure out if those places must be added to the
>>> list of header and library paths. That's not convenient at all, quite
>>> the contrary, it is messy and confusing, as we are experiencing.
>>
>> The point I (and Andreas) wanted to make is that you can just add it. If
>> it's a system directory, CMake is going to filter it out (I didn't say
>> duplicates, I said "doesn't even show up ONCE", which means *zero* times).
> 
> This is a moot point, because /usr/local/include and several other paths
> which are searched by default by cmake are not system directories in
> FreeBSD.

No it's not. If you have e.g. /usr/local/include/foo.h and you do

find_path(FOO_INCLUDE_DIR foo.h)
if(FOO_INCLUDE_DIR)
  include_directories("${FOO_INCLUDE_DIR}")
endif()

you don't have to care, because -I/usr/local/include will show up on the
command line. If foo.h happened to be in /usr/include, things would be
(kind of) fine again, because CMake filters that one out and thus
doesn't clutter your command line.

After all, you do use CMake because it's smarter than your compiler.
Otherwise you could just use some dump shell script.

> 
>> And yes, CMake simplifies A B C A to A B C, which is perfectly fine
>> because the trailing A doesn't have any effect whatsoever.
> 
> The semantics of #include is one of the most underspecified points of
> the C++ standard. It is up to the compiler vendor to decide what to do
> with #include. Each vendor uses its own algorithm (unless they decided
> on cloning the behavior of somebody else's compiler) and I've suffered
> subtle bugs due to that fact while porting applications. IIRC the
> standard says nothing about rewinding the search list for every lookup,
> but I'm ready to admit that in practice removing trailing duplicates
> makes no effect for all existing compilers and the vast majority of
> cmake users will appreciate that feature, although I wouldn't bet my
> neck on it.

That's true. But I bet that no vendor would be that insane to search the
include-directories in reverse order, or make it probabilistic, making
it more likely that a header gets picked up from a certain directory if
it is mentioned more often ;-)

> 
>> But you could have found that out by yourself in about 20 seconds...
> 
> Please spare the inflammatory language for some other thread. Thanks.

Sorry for being snarky. I can only blame a foul mood due to a nasty cold
I caught...

> 
>> IMHO you're the one trying to jump through hoops that aren't even there...
> 
> Okay, so if I have this:
> 
> check_include_file(foo.h HAVE_FOO_H)
> 
> and it succeeds, I have no guarantees that a program such as this:
> 
> #include "foo.h"
> 
> will compile, becasue foo.h may be found on a directory not included on
> the compiler's default search list.
> 
> Okay, so I have to add something like
> 
>   figure_out_where_foo_h_really_is
>   include_directories( where_foo_h_is )
> 
> Not nice, but let's go ahead.
> 
> We end having something like /usr/local/include (or /opt/local/include
> or /opt/pkg/include etc) on the search list.
> 
> We now test for the presence of some third party library ("thelib"). We
> have an user-settable variable for using just in case we want to use an
> specific install, and we use it:
> 
> cmake -DTHELIB_IS_HERE=/home/oscar/thelib .
> 
> The search mechanism for thelib looks into that directory first and
> succeeds, as it should. We add the corresponding directory:
> 
> include_directories( ${THELIB_INCLUDE_PATH} )
> 
> But there is another install of thelib on /usr/local, and that install
> is the one that will be used by our build because we end having this
> search list:
> 
> /usr/local/include /home/oscar/thelib/include
> 
> Have I missed something?

I reiterate Andreas' answer from yesterday. But there IS one point where
the removal of system directories could wreak potential havoc, but even
then, there's nothing CMake could do about it. Assume this situation:

/usr/include/foo.h
/usr/local/include/foo.h
/usr/local/include/bar.h

Now, if you want to mix-and-match, by having this include-path:
-I/usr/include -I/usr/local/include

(i.e. use /usr/include/foo.h and /usr/local/include/bar.h) you're in
trouble, because CMake will filter out the /usr/include directory,
leaving you with /usr/local/include/foo.h being found. Rats.

However, even if CMake didn't filter out, this situation could easily
break, because the author of the CMakeLists.txt file usually isn't aware
of your special situation. So he writes something like this:

find_path(BAR_INCLUDE_DIR bar.h)
find_path(FOO_INCLUDE_DIR foo.h)
include_directories(${BAR_INCLUDE_DIR} ${FOO_INCLUDE_DIR})

This results in (assuming CMake didn't filter):

-I/usr/local/include -I/usr/include

BOOOM. Reversing the directories in the include_directories() call would
solve the problem in this particular situation, but might break on
another machine, where the bar.h file is present twice. The problem is
always there if you have multiple packages installed in the same prefix
and you have multiple prefixes with containing conflicting packages.
There's really nothing CMake can do about this, it's an intrinsic flaw
of how includes are handled.


Michael


More information about the CMake mailing list