Inherit include directories from used library in CMake - cmake

If I have a library with public header files which are used by another library's public header files how can I expose the former library's public header directory to a third application which depends only from the latter library without explicitly adding the former library's header files' path to the application's target_include_directories section?
I know this is a bit confusing, here is a simple example:
I have two shared libraries and one application in the same cmake project:
library_foo has a directory which contains its public header files
library_bar has also a directory with its public header files. One of these public header files (lib_bar/bar.h) contains an #include <lib_foo/foo.h> entry, i.e. the public header file has a reference to a public header file defined in library_foo.
library_bar implementation depends on library_foo.
app depends directly only on the library_bar.
app's main.cpp contains an #include <lib_bar/bar.h>.
So, app depends indirectly from library_foo and its header files as well.
I would like to write three CMakeLists.txt files for these three parts of my application. In the CMakeLists.txt of app I would like to specify dependency only to library_bar, i.e. the library and header dependecies from libarary_foo which are specified in library_bar CMakeLists.txt must be transferred to app. How can I do this? I would like to use target_* solution.

Command target_include_directories is used exactly for the purpose of inheritance:
add_library(library_foo SHARED ...)
# Include directories are made an interface of 'foo'.
target_include_directories(library_foo PUBLIC <dir-with-lib_foo/foo.h>)
add_library(library_bar SHARED ...)
target_include_directories(library_bar PUBLIC <dir-with-lib_bar/bar.h>)
# Linking with 'foo' propagates include directories
# and makes these directories an interface of 'bar' too.
target_link_libraries(library_bar library_foo)
add_executable(app ...)
# Application will use include directories both from 'bar' and 'foo'.
target_link_libraries(app library_bar)

Related

Proper use of target_include_directories() in CMake

I`m refactoring a CMake project. I wanted to include pre-compiled third-party .dll library into custom shared library, but have difficulties with populating headers files.
Project structure:
CMakeListst.txt
app
CMakeLists.txt
modules
my-library
include
CMakeLists.txt
...
third-party // precompiled, non-CMake
third-party.dll
include
build
modules
my-library
my-library.dll
include
app
app.exe
I have a third-party library ("third-party"), which I want to include into "my-library". So CMakeLists for "my-library" looks like this:
cmake_minimum_required(VERSION 3.15)
project("my-library" VERSION 0.0.0 LANGUAGES CXX)
set(PUBLIC_HEADERS
include/my-library.h
)
set(SOURCES
my-library.cpp
)
add_library("my-library" SHARED ${PUBLIC_HEADERS} ${SOURCES})
target_link_libraries("my-library" PUBLIC "${CMAKE_SOURCE_DIR}/third-party/third-party.dll")
target_include_directories("my-library" PUBLIC "${CMAKE_SOURCE_DIR}/third-party/include")
target_include_directories("my-library" PUBLIC include)
And then add "my-library" to the app like this:
target_include_directories("app" PRIVATE "${CMAKE_SOURCE_DIR}/modules/my-library/include")
target_link_libraries("app" PRIVATE "${CMAKE_BINARY_DIR}/modules/my-library/my-library.dll")
But I get 'No such file or directory #include "third-party.h"'. In other words, app has no access to third-party inlcude directory.
Though it can be fixed by copying "include" of third-party to "include" of my-library,
is doesn`t look like a correct method. Or is it how includes supposed to be used? What am I missing here? I had a perception, that unlike include_directories() target_include_directories() should export included path.

CMake problem finding an include file in another subproject

I have a project that we will call proj.
It has a subproject call config which does not link to anything because we want to manage it separately from the primary image.
But config has some configuration files, lets call them sys_config.h and user_config.h, that define the structure that others need to access and to be clean about it, I want those stored with the config subproject.
Finally, there is a library, we will call lib that is included in proj. It needs to be able to reference those config include files.
So, the file structure is approximately like this:
proj
main
src
include
(this references and includes lib below)
config
src
sys_config.c
user_config.c
include
config
sys_config.h
user_config.h
mylib
src
mylib.c
#include <config/sys_config.h>
or
#include <sys_config.h>
include
mylib.h
Now, what do I put in lib's CMakeLists.txt to get it to be able to see sys_config.h?
For example, this does not work:
target_include_directories(mylib
PUBLIC include
PRIVATE "{$CMAKE_SOURCE_DIR}/config/include"
)
nor does this work:
target_include_directories(mylib
PUBLIC include
PRIVATE "{$CMAKE_SOURCE_DIR}/config/include/config"
)
nor does this:
target_include_directories(mylib
PUBLIC include
PRIVATE "config/include"
)
My hope was maybe if I go back to the top and include the full path to config/include it would find it but it doesn't.
I feel that I must have a complete misunderstanding of how target_include_directories() is supposed to work and what it is supposed to do for me.
Roger
With modern CMake, notion of a subdir / subproject is to be an independent library / executable. Each subproject (lib / exe) should define set of PUBLIC / PRIVATE headers. See - https://cmake.org/cmake/help/latest/command/target_include_directories.html. Rest dependent projects should just link with subproject libs. Inclusion of libs is internally managed by CMake.
Sample cmake files based on your directory layout (I have not tested them so treat them as reference purpose only).
Top level cmake file # proj/CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
project(main)
subdirs(config)
subdirs(mylib)
add_executable(hello main/src/main.c)
target_link_libraries(hello config mylib)
install(TARGETS hello DESTINATION bin)
config cmake file # proj/config/CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
add_library(config src/sys_config.c src/user_config.c)
target_include_directories(config PUBLIC include)
# uncomment if you have any internal headers within config/src
# target_include_directories(config PRIVATE src)
As best practice keep all your module config exposed headers at config/include/config/. This will make them to be imported as #include "config/sys_config.h" in mylib source files.
mylib cmake file # proj/mylib/CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
add_library(mylib src/mylib.c)
target_include_directories(mylib PUBLIC include)
# uncomment if you have any internal headers in proj/mylib/src
# target_include_directories(mylib PRIVATE src)
target_link_libraries(mylib config)

target_include_directories - INTERFACE doesn't export an include path

I have created a very simple cmake project for testing cmake features. The project directory contains two libraries. I would like to export MyLibA include path.
The main CMakeLists.txt:
cmake_minimum_required(VERSION 3.11)
project(TestProject)
add_subdirectory(MyLibA)
add_subdirectory(MyLibB)
MyLibA CMakeLists.txt:
add_library(MyLibA SHARED)
target_sources(MyLibA PRIVATE fileA.h fileA.cpp)
target_include_directories(MyLibA INTERFACE "${CMAKE_SOURCE_DIR}/MyLibA")
MyLibB CMakeLists.txt:
add_library(MyLibB SHARED)
target_sources(MyLibB PRIVATE fileB.h fileB.cpp)
target_link_libraries(MyLibB PRIVATE /home/user/MyProjects/CmakeTestProject/build/MyLibA/libMyLibA.so)
I have exported an include path using INTERFACE keyword but the following include in fileB.h:
#include "fileA.h"
is not found. What am I doing wrong ?
What am I doing wrong?
Several things:
Never put absolute paths in your CMakeLists.txt and always link to targets rather than library files.
# Linking to a target propagates usage requirements, like include paths.
target_link_libraries(MyLibB PRIVATE MyLibA)
CMAKE_SOURCE_DIR is not what you think. It refers always to the top-level build directory, which is a bad assumption if your project might be an add_subdirectory or FetchContent target. Your usage can be replaced by:
# Not optimal, see below.
target_include_directories(MyLibA INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}")
Missing $<BUILD_INTERFACE:...> on include path, if you intend to export your targets. When targets are exported, their properties are copied verbatim to the output. Not guarding the local include path with $<BUILD_INTERFACE:...> will break users of the exported target.
target_include_directories(
MyLibA
INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
)
Instead of
target_link_libraries(MyLibB PRIVATE <path/to/MyLibA/file>)
use
target_link_libraries(MyLibB PRIVATE MyLibA)
This is how CMake is intended to be used: when link with the library target, CMake automatically transforms that into the path and actually propagates all interface properties of the target.

Add include header generated in configure_file

I have a cmake project with an application and a library. The library has one header file generated with configure_file. The problem is that the application code cannot find the generated header file.
What is the proper way to include the generated header file path to the application -I options? target_link_libraries adds the path, but to the source but not to the binary directory?
Is it possible to add a property to the library when add_library is used so that this property can be used when target_link_libraries is used?
If you have generated your header file with
configure_file(config.h.in config.h #ONLY)
and have your library target already created with
add_library(libtarget ${SOURCE_FILES})
it's simply a call to
target_include_directories(libtarget PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
under the assumption that all commands are given in this order and are located in the same CMakeLists.txt file.

CMake target_sources and install

I am having a hard time figuring out how to install PUBLIC headers specified in target_sources().
It appears that target_sources() is somewhat of a mystery for anything other than adding private sources to an executable.
After reading a lot of material, where especially this blog entry was helpful, I managed to understand & bypass the issue with target_sources() and PUBLIC files. A CMakeLists.txt in one of the many subdirectories of my C++ library project looks like this:
target_sources(mylib
PRIVATE
foo.cpp
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/foo.hpp>
$<INSTALL_INTERFACE:foo.hpp>
)
This allows me to successfully build the library. However, upon installation, the header file(s) listed in the PUBLIC section of target_sources() are never installed.
My installation looks like this:
install(
TARGETS
mylib
EXPORT mylib-targets
LIBRARY
DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT lib
ARCHIVE
DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT lib
RUNTIME
DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT bin
INCLUDES
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mylib
However, this doesn't install any of the headers.
This stackoverflow answer mentions the use of PUBLIC_HEADER but reading the documentation doesn't give me the feeling that that is relevant in my case.
Question: What is the proper way of installing PUBLIC or INTERFACE headers using install()?
Background: My goal is to have a separate CMakeLists.txt in every subdirectory of my source tree. Each of these lists is supposed to use target_sources() to add PRIVATE and PUBLIC files to the build. All PUBLIC (and INTERFACE) sources should be installed during installation.
PUBLIC section of target_sources command has nothing common with public headers installed with PUBLIC_HEADER option of install command.
The headers you want to treat as public for your library should be listed in PUBLIC_HEADER property for the target.
Documentation for install(TARGETS) has a nice example of setting and installing private headers. In your case this would be:
# This defines `foo.hpp` located in the current source directory as a 'public header'.
set_target_properties(mylib PROPERTIES PUBLIC_HEADER foo.hpp)
# This install public headers among with the library.
install(
TARGETS
mylib
PUBLIC_HEADER
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mylib
# ... other options
)
See also that related question: How to configure CMakeLists.txt to install public headers of a shared library?.
Note also, that INCLUDES and PUBLIC_HEADERS clauses for install(TARGETS) command are different things:
INCLUDES specifies include directory for the installed library
PUBLIC_HEADERS specifies directory where all target's public headers will be copied upon installation.
E.g. if you want your header file to be used via
#include <mylib/foo.h>
then you need provide following options for install() command:
PUBLIC_HEADER
# The header will be places at ${CMAKE_INSTALL_INCLUDEDIR}/mylib/foo.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mylib
INCLUDES
# This directory will be used as include directory.
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
So <include-directory> joined (via /) with the relative path in #include<> directive will give absolute path to the header file.