Why CMake does not propagate the PUBLIC include directories between libraries? - c++

I have a C++ project with three shared libraries, let's call them libA, libB and libC. libA is dependant to both libB and libC. All of these three libraries are located inside a folder called utilities. Here is what I have:
Root CMakeLists.txt file:
cmake_minimum_required (VERSION 3.20) # Required because of policy CMP0074
project(Test VERSION 2022)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
file(TO_CMAKE_PATH $ENV{SHARED_PATH} SHARED_PATH)
add_subdirectory (utilities)
for libB I have the CMakeLists.txt like this:
add_library(libB SHARED
inc/libB.h
inc/libBExport.h
src/libB.cpp
)
target_include_directories(libB
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc
PUBLIC ${SHARED_PATH}/inc
)
target_compile_definitions(libB
PRIVATE LIBB_DLL)
and the same goes for libC:
add_library(libC SHARED
inc/libC.h
inc/libCExport.h
src/libC.cpp
)
target_include_directories(libC
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc
PUBLIC ${SHARED_PATH}/inc
)
target_compile_definitions(libC
PRIVATE LIBC_DLL)
Now, the CMakeLists.txt file for libA is like this:
add_library(libA SHARED
inc/libA.h
inc/libAExport.h
src/libA.cpp
)
target_include_directories(libA
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc
PUBLIC ${SHARED_PATH}/inc
)
target_link_directories(libA
PUBLIC libB
PUBLIC libC
)
target_compile_definitions(libA
PRIVATE LIBA_DLL)
Now, I generate the Visual Studio solution file using cmake:
> cmake -G "Visual Studio 16 2019" -A x64
Everything goes fine. But when opening the solution with VS2019, libB and libC are compiling fine, but libA cannot be compiled with this error:
D:\code\proj\utilities\inc\libA.h(5,10): fatal error C1083: Cannot open include file: 'libB.h': No such file or directory
D:\code\proj\utilities\inc\libA.h(9,10): fatal error C1083: Cannot open include file: 'libC.h': No such file or directory
Can someone explain me why the the PUBLIC include directories of libB and libC are not inherited to libA?

Instead of the following:
target_link_directories(libA
PUBLIC libB
PUBLIC libC
)
You should use target_link_libraries().
target_link_libraries(libA
PUBLIC libB
PUBLIC libC
)
This will setup a dependency between the libraries which sets the include path, compiler settings, additional link directories, and additional link libraries.

Related

CMake include header-only-library into another library, make fail

I have the following folder structure:
- lib/
-libA/
-include/
- libA.h
-src/
- libA.cpp
-CMakeLists.txt (1)
-libB/
-headerB1.h
-headerB2.h
-headerBx.h
-CMakeLists.txt (2)
- src/
-main.cpp
- CMakeLists.txt (3)
in libA.h I try to include one of the header files from libB. In this case that would be headerB1.h by doing
#include <headerB1.h>
But unfortunately, I get the following error in cmake:
fatal error C1083: cannot open include file: 'headerB1.h': No such file or directory from the libA.h file.
I have the following CMake contents:
/CMakeLists.txt (3)
cmake_minimum_required(VERSION 3.10)
project(project)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(SOURCES
src/main.cpp
)
add_executable(${PROJECT_NAME} ${SOURCES})
add_subdirectory("lib/libA")
add_subdirectory("lib/libB")
target_link_libraries(${PROJECT_NAME} PUBLIC libA)
target_link_libraries(${PROJECT_NAME} PUBLIC libB)
lib/libA/CMakeLists.txt (1)
add_library(libA STATIC)
target_sources(libA PRIVATE src/libA.cpp)
target_include_directories(libA
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
)
lib/libB/CMakeLists.txt (2)
add_library(libB INTERFACE)
target_include_directories(libB INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
library B (libB) is a header only library in my view (correct me if I'm using the incorrect terms).
So libA actually 'uses' libB but somehow, adding or creating libB is not executed as I thought it would. What are my misconceptions here and how can I solve it?
main.cpp:
#include <libA.h> //This works!
int main(void) {
//do something with libA class / functions
}
libA.h:
#include <headerB1.h> //This causes the cmake (mingw32-make.exe) error
I use:
cmake
vscode
mingw32-make
I actually did take a look at: CMake include library in another library
But making my CMakeLists (1) like the following code did not work for me unfortunately. Or, again, I have misunderstood the solution.
add_library(libA SHARED)
target_sources(libA PRIVATE src/libA.cpp)
target_include_directories(libA
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_link_libraries(libA PRIVATE libB)
target_include_directories(libA PUBLIC ${CMAKE_ROOT}/lib/libB)
Ok, this was actually pretty straightforward for a solution but it does make sense that libB would have to be compiled first because libA makes use of it.
So, in CMakeLists.txt (3) I have switched the two following lines:
add_subdirectory("lib/libA")
add_subdirectory("lib/libB")
to
add_subdirectory("lib/libB")
add_subdirectory("lib/libA")
And I also made sure to have added the following code snippet like suggested in the other stackoverflow question, to the libA CMakeLists.txt (1). CMake include library in another library
target_link_libraries(libA PRIVATE libB)
target_include_directories(libA PUBLIC ${CMAKE_ROOT}/lib/libB)
Now, all compiles as expected without errors.

CMake Include paths - library that depend on external library

Take for example this project structure
project
CMakeLists.txt
main.cpp
libA
src.cpp
CMakeLists.txt
libB
foo.h
foo.cpp
CMakeLists.txt
src.cpp and main.cpp:
#include "foo.h"
.
.
.
I need both src.cpp and main.cpp to have libB in their include path, what I've tried to do is:
libB/CMakeLists.txt:
add_library(libB SHARED
src.cpp)
libA/CMakeLists.txt:
add_library(libA SHARED
foo.cpp)
target_link_libraries(libA libB)
project/CMakeLists.txt:
add_subdirectory(libA)
add_subdirectory(libB)
add_executable(App main.cpp)
target_include_directories(App PUBLIC libB)
target_link_libraries(App libA libB)
And yet I get an error that
src.cpp: fatal error: foo.h: No such file or directory
In the top-level:
cmake_minimum_required(VERSION 3.21)
project(project)
option(BUILD_SHARED_LIBS "Build shared libs instead of static" ON)
add_subdirectory(libA)
add_subdirectory(libB)
add_executable(App main.cpp)
target_link_libraries(App PRIVATE libA libB)
In libA:
add_library(libA src.cpp)
# Use PUBLIC below if libA exposes libB's types in its API.
target_link_libraries(libA PRIVATE libB)
In libB:
add_library(libB foo.cpp)
target_include_directories(
libB PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
)
PUBLIC and INTERFACE properties on targets propagate to their linkees. Thus, when we set up the include directories on libB, any target linking to it will get its source directory added it its include path.
We guard the include directory with $<BUILD_INTERFACE:...> so that if you ever install() the library somewhere it might be re-used in another CMake build, it won't remember details of your specific file system.

CMake 2.8.12 with nested dependencies, resulting in multiple 'inclusion'

I have widely-separated dirs, each with a CMakeLists.txt. I'd like to keep them independently-buildable. Two are libs; libA, libB. libB uses libA. exeC uses libA and libB.
When I organize them via add_subdirectory(), as in:
libB (CMakeLists.txt):
add_subdirectory($DIRA somebuild)
target_linked_libraries(libB PUBLIC libA)
exeC (CMakeLists.txt):
add_subdirectory($DIRA somebuild)
add_subdirectory($DIRB somebuild)
target_linked_libraries(exeC PUBLIC libB libA)
and then building exeC, CMake complains about the target name of libA not being unique (CMP0002). If I squelch that warning, I get funky results as the docs warn against.
When I organize them via: add_library(), and add_dependencies() as in:
libB (CMakeLists.txt):
add_library(libA SHARED IMPORTED)
set_target_properties(libA PROPERTIES IMPORTED_LOCATION $DIRA)
add_dependencies(libB libA)
target_linked_libraries(libB PUBLIC libA)
exeC (CMakeLists.txt):
add_library(libA SHARED IMPORTED)
set_target_properties(libA PROPERTIES IMPORTED_LOCATION $DIRA)
add_dependencies(exeC libA)
add_library(libB SHARED IMPORTED)
set_target_properties(libB PROPERTIES IMPORTED_LOCATION $DIRB)
add_dependencies(exeC libB)
target_linked_library(exeC PUBLIC libA libB)
It doesn't seem like the dependencies propagate - when I build exeC, it chokes on not finding libA and libB there, as indeed the docs warn about, although I thought I saw some other doc that said they would build if the required libs were all based on CMake. I'm using CMake 2.8.12.
Is it possible to have nested inclusions like this, or is that not The CMake Way?
edit: it further turns out, that one solution - relying on the transitivity of add_subdirectory(), isn't enough because my case is actually diamond-shaped, I have libB1 and libB2 who each 'include' libA and are included by exeC. It looks like include guards may be the way to go. I'll acknowledge #Drop's answer as correct, if I get this working.

CMake linking 2 libraries with relative path

my project structure:
...
CMakeLists.txt
src/
project/
libA/
CMakeLists.txt
libA.h
libA.cpp
libB/
CMakeLists.txt
libB.h
libB.cpp
build/
CMakelists.txt
project(project)
# set some global dirs
set(LIB_A_DIR "src/project/libA")
set(LIB_B_DIR "src/project/libB")
# include header directories
include_directories(${LIB_A_DIR})
include_directories(${LIB_B_DIR})
# generating linkable libraries
add_subdirectory(${LIB_A_DIR})
add_subdirectory(${LIB_B_DIR})
src/project/libA/CMakeLists.txt
# define libA sources
set(LIB_A_SOURCES libA.cpp libA.h)
# generate libA library
add_library(libA SHARED ${LIB_A_SOURCES})
src/project/libB/CMakeLists.txt
# define libB sources
set(LIB_B_SOURCES libB.cpp libB.h)
# generate libB library
add_library(libB SHARED ${LIB_B_SOURCES})
# link libraries
target_link_libraries(libB PRIVATE libA)
now if i run cmake .. && make from the build folder any function call made from libB works just fine. But as soon as i want to move this file structure the link breaks.
For example when i did cp -r build/ build_copy/ and clear the build directory with rm -rf build/* any code that includes libB throws a error while loading shared libraries: liblibB.so: cannot open shared object file: No such file or directory
how do i get my link to use the relative file path?
side note: i need them to be 2 seperate shared libraries combined with a link
You need to set the runtime-path of libA to where it is installed. This is done with the -rpath linker option.
Since the C++ frontend g++ or clang++ will be used when linking, you need to use the -Wl flag to pass the -rpath option to the linker.
You can set flag with the LINK_FLAGS target property for libB with the set_target_properties command:
set_target_properties(libB
PROPERTIES LINK_FLAGS "-Wl,-rpath,/path/to/libA/installation")
as in CMake linking 2 libraries with relative path comment:
Assign a variable with the absolute path of the relative path to the library:
get_filename_component(LIB_A_PATH ../lib/liba.a ABSOLUTE)
Then use the absolute path to link the lib (here statically):
add_library( lib_a STATIC IMPORTED )
set_target_properties(lib_a PROPERTIES IMPORTED_LOCATION ${LIB_A_PATH})
target_link_libraries(${PROJECT_NAME} lib_a)

Various libs in subfolders, how do I export them to the parent?

I have a code folder structure like this:
project/
CMakeLists.txt
src/
project.cpp
lib/
libA/
CMakeLists.txt
src/
libA.cpp
include/
libA.h
libB/
CMakeLists.txt
src/
libB.cpp
include/
libB.h
Now, libA is totally standalone, so I have a CMakeLists.txt similar to this:
cmake_minimum_required(VERSION 2.8)
project ( libA )
set ( LIBA_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE )
include_directories ( include/ )
file (GLOB LIBA_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}
src/libA.cpp
)
add_library ( libA STATIC ${LIBA_SOURCES} )
However, libB depends on libA. How can I export the libA.a output path to be used for linking to libB, and the corresponding include path?
And furthermore, how do I pass that data to the parent CMakeLists.txt for the main project?
I haven't done this personally, so this answer may only be a starting point, but Qt uses cmake and is able to tie all the include directories and library dependencies into each 'module' (library). If you accomplish that, then you can simply do
add_executable(myExe ...)
target_link_libraries(myExe libB)
to the executable in your main cmake project.
Looking through the Qt scripts, it appears what you need to set (for each library) is:
add_library(libA ...)
set_target_properties(libA PROPERTIES
"INTERFACE_LINK_LIBRARIES" "${LIBRARIES_LIBA_DEPENDS_ON}"
"INTERFACE_INCLUDE_DIRECTORIES" "${LIBA_INCLUDE_DIRECTORIES}"
)
then for libB
add_library(libB ...)
set_target_properties(libB PROPERTIES
"INTERFACE_LINK_LIBRARIES" "libA"
"INTERFACE_INCLUDE_DIRECTORIES" "${LIBB_INCLUDE_DIRECTORIES}"
)
The cool thing about this method is that when done properly, you can chain together as many libraries as you like without having to worry about the combinatorial explosion of dependencies and include dirs.
Taking #epicbrew and #NicolasHolthaus answers/comments I just wanted to show how the complete example would look in a newer version of CMake and discuss some of the implications:
project/CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project( project )
add_subdirectory( lib/libB )
add_subdirectory( lib/libA )
add_executable( project src/project.cpp )
target_link_libraries( project libB )
project/lib/libA/CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project( libA )
add_library( libA STATIC src/libA.cpp include/libA.h )
target_include_directories( libA PUBLIC include )
project/lib/libB/CMakeLists.txt
add_library( libB STATIC src/libB.cpp include/libB.h )
target_include_directories( libB PUBLIC include )
target_link_libraries( libB libA )
As you can see
You need to know in the main CMakeLists.txt file that you also need libA. I was deliberately reversing the add_subdirectory() calls to show that the order doesn't matter when using target_link_libraries() (sort of allows target forward declarations).
You don't need the local include_directories() call anymore it's an implicit part of the target_include_directories().
With target_include_directories(... PUBLIC ...) those include paths are self-propagating.
You don't need to add the ${CMAKE_CURRENT_SOURCE_DIR} prefixes, those are the defaults.
I just added the source/header files directly to the add_executable()/add_library() calls for compactness of this example code. Normally I also would place those in local variables.
libB is not standalone, but you could change that e.g. by adding add_subdirectory( ../libA libA ) to its CMakeLists.txt file. Obviously you then have to remove add_subdirectory( lib/libA ) from the main CMakeLists.txt.
If you want to optimize the CMake processing a little you should not repeat the project() commands. If you still want libA to be self-sufficient you could add if ( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )/endif() around the cmake_minimum_required(VERSION 3.0)/project( libA ) calls.
CMake knows about all of the targets that you've built, so there's no need to export information up to the parent. You simply tell CMake that libB links against libA and CMake takes care of putting the correct path on the linker command line. So just specify libA as a link dependency of libB in your libB/CMakeLists.txt:
target_link_libraries(libB libA)
For the include path you either need to set variables in the parent along the lines of the following in the top level project/CMakeLists.txt:
set(LIBA_INCLUDES libA/include)
set(LIBB_INCLUDES libB/include)
Then reference these in the subprojects CMakeList.txt files.
Or, you could use a relative path in your libB/CMakeLists.txt file:
include_directories(../libA/include)