[CMake] How to set path to library header files?

Domen Vrankar domen.vrankar at gmail.com
Wed Dec 3 17:13:00 EST 2014


2014-12-03 21:22 GMT+01:00 Chris Johnson <cxjohnson at gmail.com>:
> I think I've made it work by adding 1 ugly hack and 1 reasonable directive
> to each library's CMakeLists.txt, which is at least far preferable to adding
> several new include paths to potentially hundreds of programs which refer to
> those libraries.

Years ago when I changed build system on ~75 programs I did exactly
that (added new include paths to each one with include_directories
command in cmake) as everything else would seem to me as an ugly
hack...
You don't need to refer to them with a relative path depending on
where in the build tree you are - when you call porject(project_name)
command you can refer to its location from other CMakeLists.txt with
${project_name_SOURCE_DIR} and ${project_name_BINARY_DIR} so by
putting project command at the top of every library cmake list you
don't need to know the relative location in each executable.

Here's an example:

Executable cmake list:

cmake_minimum_required(VERSION 2.8)
project(main)
add_subdirectory(test_b)
message("src: ${test_b_SOURCE_DIR}") # prints out the path just to
show that it is really set
message("build: ${test_b_BINARY_DIR}") # prints out the path just to
show that it is really set
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} test_b)

And libary cmake list:

project(test_b)
add_library(${PROJECT_NAME} b.cpp)

> The hack is a function which is defined in my global CMake "include" file,
> which when called simply (well, not so simply -- it's a hack!) creates a
> subdirectory of the library's name in the BUILD directory and then copies
> the *.h files from the SOURCE directory to it.
>
> Then I add
>     # Headers used from build location:
>     "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"
> to my existing target_include_directories().
>
> While this appears to work in early testing, I'd sure like a better
> solution.  It's pretty much best practice to install one's private library
> header files in a subdirectory of /usr/include, /usr/local/include, etc.  So
> having to jump through hoops to support that paradigm is kind of strange.

>From what I've understood from the thread I guess that you would like
to access some header files at compile time on a location that would
be accessible after install time.
I've started solving this a while ago with treating every library as
an external dependency even if it is part of the same repository as
the code for the executable.
For every new library that I write I also write a FindSomeLib.cmake
script so that I'm able to refer to the library header and lib files
by calling find_library(SomeLib) and that sets SomeLib_LIBRARY_DIRS
and SomeLib_INCLUDE_DIRS variables that can be used by all the
projects that refer to it.
The logic in FindSomeLib.cmake script that I'm using is a fallback structure:
1. check that the library is not already set
2. check how to set SomeLib_LIBRARY_DIRS and SomeLib_INCLUDE_DIRS variables
   a. if a target by the name of SomeLib already exists in my project
(for e.g. because my uberbuild builds the libraries before it starts
building the executables so that I can add all the targets in one RPM
package for distribution)
   b. else if a certain variable is set then we are in development
mode and we add the library as a sub-directory with
add_subdirectory(path_to_lib lib_name) -> this is useful when we are
developing and fixing bugs both in executable and library code
   c. else if a library is already installed (useful if we are
creating a production build that must link to an exact version of a
library that we have on the production build server)
   d. else -> library not found
3. if library was found -> set the SomeLib_LIBRARY_DIRS and
SomeLib_INCLUDE_DIRS variables accordingly

This way programs/other libraries only set the above variables as
include dir (note that SomeLib_INCLUDE_DIRS may point to more than one
location in a list) and SomeLib_LIBRARY_DIRS and they don't care if
the library was created with external_project command, built as a
target or already existed in the environment (just like they wouldn't
care if a library would be from a different vendor but since it is
ours we can have additional logic as explained above). And if you ever
need to change the structure of the library (for e.g. add more sub
directories or rename the existing directories) you only have to
change the find script and all the project will work out of the box
without changing their CMakeLIsts.txt.

This will still not solve your problem of #include
"lib_name/include_file.hpp" if your library repository is not
structured to have private headers in lib_name subdir but you can hide
away the hack of copying the headers inside the Find cmake script. But
I'd say that not having the right directory structure in the repo
would be something bagging for hacks so in that case repo
restructuring would probably be a better way to go.

Alternative to the solution above would be to treat all libraries as
an ExternalProject
(http://www.cmake.org/cmake/help/v3.0/module/ExternalProject.html)
which can be used to install the library in build directory while
compiling the project but this has some drawbacks (packaging such a
library in the same package as executable with CPack requires more
work, during development you can't treat such a library as part of the
project so you're not able to fix library sources as easy as with the
above solution,...). It also has an advantage - library can be fetched
from a different repository. When I'm using ExternalProject command I
hide it away in Find cmake script as well so that changes to
repository name or something like that doesn't affect all the
executables/libraries that use it...

Hope this helps,
Domen


More information about the CMake mailing list