1

I'm trying to use cmake to install "Skeltrack" into a shared library. Here's my fork of the project: https://github.com/birgersp/Skeltrack/tree/cmake

From my understanding, target_include_directories enables a package to "save" the location of the headers it needs, so the application using my library does not have to include this directory for the library to run. Is this correct?

It seems that if my library needs some headers, I am required to include these headers in my application even though I used target_include_directories in my library...

The library's CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)

project(Skeltrack)

# Set output folders
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib)

# Find source files
file(GLOB SOURCES src/*.c)

# Include header files
include_directories(include)

# Create shared library
add_library(${PROJECT_NAME} STATIC ${SOURCES})

# Include Glib library
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
find_package(Glib REQUIRED)
target_link_libraries(${PROJECT_NAME} ${Glib_LIBRARIES})
target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${Glib_INCLUDE_DIRS})

# Install library
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})

# Install library headers
file(GLOB HEADERS include/*.h)
install(FILES ${HEADERS} DESTINATION include/${PROJECT_NAME})

My application's CMakeListst.txt

cmake_minimum_required(VERSION 2.4.0)

project(skeltrack-test)

# Find source files
file(GLOB SOURCES src/*.cpp)

# Create executable
add_executable(${PROJECT_NAME} ${SOURCES})

# Find and link Skeltrack library
find_library(SKELTRACK Skeltrack PATH_SUFFIXES Skeltrack)
target_link_libraries(${PROJECT_NAME} ${SKELTRACK})

# Find and include Skeltrack library headers
find_path(SKELTRACK_INCDLUDE_DIRS skeltrack.h PATH_SUFFIXES Skeltrack)
target_include_directories(${PROJECT_NAME} PUBLIC ${SKELTRACK_INCDLUDE_DIRS})

Make output:

[ 50%] Building CXX object CMakeFiles/skeltrack-test.dir/src/main.o
In file included from /usr/local/include/Skeltrack/skeltrack-skeleton.h:26:0,
                 from /usr/local/include/Skeltrack/skeltrack.h:26,
                 from /home/birger/Workspace/skeltrack-test/src/main.cpp:2:
/usr/local/include/Skeltrack/skeltrack-joint.h:26:18: fatal error: glib.h: No such file or directory
compilation terminated.
CMakeFiles/skeltrack-test.dir/build.make:62: recipe for target 'CMakeFiles/skeltrack-test.dir/src/main.o' failed
make[2]: *** [CMakeFiles/skeltrack-test.dir/src/main.o] Error 1
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/skeltrack-test.dir/all' failed
make[1]: *** [CMakeFiles/skeltrack-test.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2

Seems I'm required to include the Glib header directory in my application, to be enable the library to run? How can I avoid having include the headers twice?

birgersp
  • 3,909
  • 8
  • 39
  • 79
  • Self-propagating include directories with `target_include_directories()` only work with a real target created e.g. with `find_package()` not with `find_library()`. – Florian Jul 04 '16 at 11:38
  • I guess I have to install the Skeltrack library as a package, or is there another way to solve this? – birgersp Jul 04 '16 at 11:54
  • I'm not sure what exactly you're trying to accomplish. Something like [Making cmake library accessible by other cmake packages automatically](http://stackoverflow.com/questions/33462209/making-cmake-library-accessible-by-other-cmake-packages-automatically)? – Florian Jul 04 '16 at 11:59
  • I'm trying to build my application without having to include the directory of headers that the library needs. These headers should have been included when I built the library, I'm failing to see why they're not. – birgersp Jul 04 '16 at 13:28
  • Let me try to rephrase: How can build my library so that it can find the headers it needs to run in my application? – birgersp Jul 04 '16 at 13:49

1 Answers1

2

Let's check out the manual for target_include_directories to get a better idea for how it works:

target_include_directories(<target> [SYSTEM] [BEFORE]  
   <INTERFACE|PUBLIC|PRIVATE> [items1...]   [<INTERFACE|PUBLIC|PRIVATE>
   [items2...] ...])

Specify include directories to use when compiling a given target. The named must have been created by a command such as add_executable() or add_library() and must not be an IMPORTED target.

[...]

The INTERFACE, PUBLIC and PRIVATE keywords are required to specify the scope of the following arguments. PRIVATE and PUBLIC items will populate the INCLUDE_DIRECTORIES property of . PUBLIC and INTERFACE items will populate the INTERFACE_INCLUDE_DIRECTORIES property of . The following arguments specify include directories.

The secret sauce lies with the INTERFACE_INCLUDE_DIRECTORIES target property:

When target dependencies are specified using target_link_libraries(), CMake will read this property from all target dependencies to determine the build properties of the consumer.

So, to sum up: When linking against a target, CMake will inherit all include directories from that target's INTERFACE_INCLUDE_DIRECTORIES property.

For this to work you need to ensure two things: First, your project has to link against a CMake target (because no target means no target properties) and its INTERFACE_INCLUDE_DIRECTORIES target property must expose the include directories correctly.

Now let's take a look at what you are doing:

find_library(SKELTRACK Skeltrack PATH_SUFFIXES Skeltrack)
target_link_libraries(${PROJECT_NAME} ${SKELTRACK})

Hmm... That doesn't look like a target. find_library finds files, not targets. Indeed, inspecting the value of ${SKELTRACK} will reveal that it is pointing to a filename. A check if(TARGET ${SKELTRACK}) will fail.

This is because the mechanism that you use to handle dependencies predates the interface target property system. It relies solely on file paths and thus requires you to pass all relevant options to downstream programs manually.

Unfortunately, you cannot get the more comfortable behavior for free. Someone has to create a target for Skeltrack in your application's build environment and populate its target properties accordingly. That requires CMake code. Usually it is the dependency's responsibility to provide that code (although nothing stops you from writing it yourself; it's just a bit painful to write and maintain).

CMake provides a convenient mechanism for auto-generating such code for your packages. Unfortunately, Skeltrack's build system would have to do this so that your application can benefit from it.

Since you already forked the project, why not add support for this to their build system and open a pull request?

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166