Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CMake: ordering of include directories (How to mix system- and user-based include paths?)

Tags:

c++

linker

cmake

I've got a CMake project that includes and links against two libraries, say A and B (actually it's more than two and one of them is boost stuff, but that doesn't really matter here). Both are located via FindSomething.cmake scripts that (correctly) populate the standard CMake variables such that include directories are added via

INCLUDE_DIRECTORIES(${A_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(${B_INCLUDE_DIRS})

and linking is later done via

TARGET_LINK_LIBRARIES(mytarget ${A_LIBRARIES} ${B_LIBRARIES})

Now, the problem is that both libraries can either reside in a user based location or in the system directories (I'm on linux by the way, CMake 2.8.2) - or in both. Let's say A is only in $HOME/usr/include and $HOME/usr/lib while B (boost in my case) resides in both the system paths (/usr/include and /usr/lib) AND in the user based paths - in different versions. The find scripts can be made to find either the system or the user-based library B, this works.

The trouble starts when I want to link against B from the system paths.${B_INCLUDE_DIRS} and ${B_LIBRARIES} correctly point to the system-wide locations of the headers and libraries. But there is still ${A_INCLUDE_DIRS} that points to a non-system include directory and ultimately also the headers for library B are taken from this location, while the linking for B uses the version from the system paths (via ${B_LIBRARIES}) which leads to conflicts, i.e. linking errors.

Changing the order of the INCLUDE_DIRECTORIES statements does not seem to change anything. I checked the origin of the symbols that cause the linking errors via nm --line-numbers on the object files.

What can I do? Is there a trick to

  • force the ordering of the include directories (even if this would mean to give precedence to a system path although there is also a user-based location specified)?
  • tell CMake to use ${A_INCLUDE_DIRS} for all headers from A and ${B_INCLUDE_DIRS} for all headers from B?
like image 891
janitor048 Avatar asked Oct 13 '11 10:10

janitor048


People also ask

How do you add include directories to CMake?

First, you use include_directories() to tell CMake to add the directory as -I to the compilation command line. Second, you list the headers in your add_executable() or add_library() call.

Where does CMake look for include files?

cmake is searched first in CMAKE_MODULE_PATH , then in the CMake module directory. There is one exception to this: if the file which calls include() is located itself in the CMake builtin module directory, then first the CMake builtin module directory is searched and CMAKE_MODULE_PATH afterwards.

What is the difference between include_directories and target_include_directories?

include_directories(x/y) affects directory scope. All targets in this CMakeList, as well as those in all subdirectories added after the point of its call, will have the path x/y added to their include path. target_include_directories(t x/y) has target scope—it adds x/y to the include path for target t .

What is the include directory?

By convention, the contents of the include directory are the headers exposed for public consumption. The source directory may have headers for internal use, but those are not meant to be distributed with the compiled library.


2 Answers

Here's what CMake says about include_directories():

include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])

You can specify that you want to have include directories searched before or after the system include directories at the time that you tell it about those directories.

You may also be specific to a target:

target_include_directories(target [SYSTEM] [BEFORE] [items1...] [ [items2...] ...])

like image 118
Jay Avatar answered Nov 07 '22 19:11

Jay


If A and B are different libraries containing different header files and paths, there should be no problem doing what you are doing right now.

That being said, if A and B are similar libraries containing header files of the same name at the same location, that is problematic. In that case, the order of the include_directory() call is important. I ran a little test where I had three copies of a header file. The first copy is located in my system path (say /usr/include). The other copies are located in two user-defined locations (say /tmp/include1 and /tmp/include2). The file in /tmp/include1 is found and used first if I put the include_directory() call in the following order:

include_directory("/tmp/include1")
include_directory("/tmp/include2")

The file in /tmp/include2 is found and used first if I put the include_directory() call in the following order:

include_directory("/tmp/include2")
include_directory("/tmp/include1")

If I put no include_directory() statement, then the header in the system path is found and used.

You may want to re-check how your FindSomething.cmake are written. The search order of the find_*() CMake commands can be found in the CMake documentation,

As far as I can remember, there is now way of telling CMake to use ${A_INCLUDE_DIRS} for all headers from A and ${B_INCLUDE_DIRS} for all headers from B if the header file can be found in both location. It all depends in what order the include_directory() call are made. If the FindSomething.cmake are written properly, if the CMAKE_MODULE_PATH (this is the location where CMake will look for the Find*.cmake files) is set properly and all the paths are good, then you should be good to go. If not, I will need more information on your current CMake/library setup.

like image 44
fmorency Avatar answered Nov 07 '22 20:11

fmorency