cmake include dependencies using find_package from installed target - c++

I am currently migrating a couple of CodeBlock projects to use CMake and Visual Studio Code. I am facing an issue related to the include directory not being known by the consuming target.
I followed a guide so far as I was not aware of the "modern CMake way" using targets at all until yet. Mainly referring to here: https://rix0r.nl/blog/2015/08/13/cmake-guide/ and the official CMake docs.
There are two libraries (.so). One of them needs to other one to be there (build) when it get's loaded by the application later on.
I am not sure if everything is even intended to work like I assume it to work so I will just continue with the code and the expected result.
Library A (which is needed by Library B) has following CMakeLists
cmake_minimum_required(VERSION 3.0.0)
project(A)
set(SOURCE somesource.c)
include(GNUInstallDirs)
find_package(JNI REQUIRED)
add_library(JNI SHARED IMPORTED)
set_property(TARGET JNI PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ENV{JAVA_HOME}/include $ENV{JAVA_HOME}/include/linux)
set_property(TARGET JNI PROPERTY IMPORTED_LOCATION ${JNI_LIBRARIES})
add_library(${PROJECT_NAME} SHARED ${SOURCE})
target_include_directories(${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include $ENV{JAVA_HOME}/include $ENV{JAVA_HOME}/include/linux>
$<INSTALL_INTERFACE:include>
)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Config
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(EXPORT ${PROJECT_NAME}Config DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake)
Notice I only have one include file which is public and located in "include", so its used by the library itself but should also be part of the install interface
used by the "consuming" target.
Library B (which uses Library A and its header)
cmake_minimum_required(VERSION 3.0)
project(B)
set(SOURCE somesource.c)
add_library(${PROJECT_NAME} SHARED ${SOURCE})
find_package(A REQUIRED)
target_include_directories(${PROJECT_NAME}
PRIVATE ${PROJECT_SOURCE_DIR}
)
So in Library B I assume that I do not need to add the include_directories from Library A again. This is why I actually did the target approach so there is no need to keep track of linker/include dependencies. "find_package" should handle these on it's own right? I do not link neither because it is a shared object, not a static library.
Both libraries use the same CMAKE_INSTALL_PREFIX ofc and library A has been installed (make install). I double-checked on this. In case of using a different prefix or not doing the install, CMake already complains about not finding it.
So considering the issue now:
Within a source file from library B, I am using
#include <libcobjava.h>
resulting in an error when building as the compiler complains about "No such file or directory" Am I missing something? The file is definitely located at
CMAKE_INSTALL_PREFIX/include.
Apart from that as I am still new to CMake: Is this even the way you would do things if you own all the source?
Edit: Some more details on the follow-up issue as discussed in comments. Library A is libcobjava.
First I had the target_include_directories part as following (including the path where jni.h is located in $<INSTALL_INTERFACE:)
target_include_directories(${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include $ENV{JAVA_HOME}/include $ENV{JAVA_HOME}/include/linux>
$<INSTALL_INTERFACE:include $ENV{JAVA_HOME}/include $ENV{JAVA_HOME}/include/linux>
)
results in following error:
[cmake] Configuring done
[cmake] CMake Error in CMakeLists.txt:
[cmake] Target "libcobjava" INTERFACE_INCLUDE_DIRECTORIES property contains path:
[cmake]
[cmake] "/media/rbs42/data/Gebos/RBS42/tools/libcobjava/${_IMPORT_PREFIX}/include"
[cmake]
[cmake] which is prefixed in the source directory.
[cmake]
[cmake]
[cmake] Generating done
[cms-driver] Error during CMake configure: [cmake-server] Failed to compute build system.
This is why I removed $ENV{JAVA_HOME}/include $ENV{JAVA_HOME}/include/linux> from $<INSTALL_INTERFACE again.
The actual error I am getting in library B now is during compiling:
[build] /media/rbs42/data/rbs42/usr/include/libcobjava.h:1:10: fatal error: jni.h: No such file or directory
[build] #include <jni.h>
because library B does not have the include directories from library A that are not included in the $<INSTALL_INTERFACE. This is what one would expect. And thats the reason I added the following code before in library A.
set_property(TARGET JNI PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ENV{JAVA_HOME}/include $ENV{JAVA_HOME}/include/linux)
which I assumed is taking care of propagating the include dependencies from library A to the "consuming" library B, although they are not part of the $<INSTALL_INTERFACE.

Related

CMake can not find SFML lib

I have a project depends on SFML lib on C++. I trying to make it with CMake.
CMakeLists.txt is:
cmake_minimum_required(VERSION 3.16.3)
project(3D_Renderer_from_scratch)
set(CMAKE_CXX_STANDARD 17)
include_directories(headers source)
set(SFML_STATIC_LIBRARIES TRUE)
find_package(SFML COMPONENTS window graphics system)
set(SOURCES
Main.cpp
source/Application.cpp
source/Box.cpp
source/Camera.cpp
source/FileReader.cpp
source/KeyboardHandler.cpp
source/Sphere.cpp
source/Triangle.cpp
source/Window.cpp
source/World.cpp
)
add_executable(executable ${SOURCES})
target_link_libraries(executable ${SFML_LIBRARIES} ${SFML_DEPENDENCIES})
After running cmake . I have the following error:
$ cmake .
-- Requested SFML configuration (Static) was not found
CMake Warning at CMakeLists.txt:10 (find_package):
Found package configuration file:
/usr/lib/x86_64-linux-gnu/cmake/SFML/SFMLConfig.cmake
but it set SFML_FOUND to FALSE so package "SFML" is considered to be NOT
FOUND.
-- Configuring done
-- Generating done
-- Build files have been written to: /home/mcjohn974/3D_Renderer_from_scratch
How can I fix it ? (sfml lib is already installed)
As already mentioned in the comments (why not write it as an answer?), CMake was looking for static SFML libraries and it most likely just found shared libraries.
Either make sure that static libraries (the ones with the -s suffix) are provided.
Or don't request static libraries, which you can achieve be not setting SFML_STATIC_LIBRARIES.

(C++17; Boost) CMake unable to find the requested Boost libraries

I am new to C++ and want to include the boost library (specifically the filesystem part which needs to be built) in my project. I tried many solutions from other stackoverflow users but they didn't help me at all. I am using CLion with CMake.
The main.cpp is calling the other .cpp files inside the modules/ folder.
The file structure:
ProjectName
>boost
>lots of folders and .hpp files
>cmake-build-debug
>modules
encryption.cpp
encryption.h
output.cpp
output.h
CMakeLists.txt
main.cpp
The boost folder doesn't contain the entirety of boost when you download and extract it. I dragged the boost folder inside of boost_1_72_0 in my project (just so you know that there's no libs folder, etc. inside)
The CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(ProjectName)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++ -static-libgcc")
set(SOURCE_FILES
main.cpp
modules/encryption.cpp modules/encryption.h modules/output.cpp modules/output.h
)
set(Boost_ARCHITECTURE -x64)
set(BOOST_ROOT boost/)
set(Boost_INCLUDE_DIRS boost/filesystem)
find_package(Boost COMPONENTS system filesystem REQUIRED)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
endif()
add_executable(ProjectName ${SOURCE_FILES})
target_link_libraries(ProjectName ${Boost_LIBRARIES})
output.cpp
// some includes //
#define BOOST_FILESYSTEM_NO_DEPRECATED
#include "../boost/filesystem.hpp"
// some code //
The Error message:
CMake Error at C:/Program Files/JetBrains/CLion 2019.1.4/bin/cmake/win/share/cmake-3.14/Modules/FindBoost.cmake:2147 (message):
Unable to find the requested Boost libraries.
Unable to find the Boost header files. Please set BOOST_ROOT to the root
directory containing Boost or BOOST_INCLUDEDIR to the directory containing
Boost's headers.
Call Stack (most recent call first):
CMakeLists.txt:15 (find_package)
-- Configuring incomplete, errors occurred!
See also "C:/Users/username/Desktop/C++/ProjectName/cmake-build-debug/CMakeFiles/CMakeOutput.log".
mingw32-make.exe: *** [cmake_check_build_system] Error 1
Makefile:235: recipe for target 'cmake_check_build_system' failed
I know that it's basically telling me what I have to do but I don't know what's exactly meant by the "root directory" of boost, by the directory "containing Boost's headers" and how to put everything together.
Many thanks in advance!
I dragged the boost folder inside of boost_1_72_0 in my project
Looks like you just copied boost source into your project dir.
You have to compile boost since you need filesystem. Or you can get boost from:
vcpkg - it's the easiest way for you. I am highly recommended this way.
Sourceforge.
Conan
I don't know what's exactly meant by the "root directory"...
Since you are using boost by calling find_package(Boost) - CMake uses FindBoost module. It will try to find your boost installation inside system PATH variable or in some other "standard" places. Your boost "installation" is not common, so you have to specify where boost is with BOOST_ROOT variable. set(BOOST_ROOT boost/) is incorrect way to do this. You have to specify absolute path like set(BOOST_ROOT "C:/lib/boost/boost17.2")
or relative to current CMakeList.txt - set(BOOST_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/boost}.
With right installed boost all you have to do is:
find_package(Boost REQUIRED [COMPONENTS <libs>...])
target_link_libraries(main PRIVATE ${Boost_LIBRARIES})
target_include_directories(main PRIVATE ${Boost_INCLUDE_DIRS})
Usually, you don't need to set Boost_ARCHITECTURE and Boost_INCLUDE_DIRS CMake does it for you.
When you use find_package with REQUIRED option you don't need to check whether the library found or not since CMake raises an error when a library isn't found.
BOOST_ROOT is a directory when boost installed or unpacked.
BOOST_INCLUDEDIR is a directory with boost headers (usually it's BOOST_ROOT/boost). So try to set the full path to your boost_1_72_0 directory to BOOST_ROOT CMake variable.
Also, I had a problem with COMPONENTS option. Try to remove it if errors remain.

How to make CMAKE find specific version of lib curl on OSX?

Question is: How do I get CMAKE to set/make the CURL_INCLUDE_DIR be "/usr/local/Cellar/curl/7.75.0/include/"?
No matter what I do, CMAKE only finds the Xcode lib version/location.
My CMAKE file contains:
set(CURL_ROOT_DIR /usr/local/Cellar/curl/7.75.0/include/)
find_package(curl REQUIRED)
message(STATUS ">>> CURL Dir Found: " ${CURL_INCLUDE_DIR})
No matter what I've tried, the above CMAKE code will result in a "CURL_INCLUDE_DIR" of
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/include
Note, utilizing "include(findcurl)" instead of "find_package(curl REQUIRED)" gives same result.
I've resorted to adding
set(CMAKE_CXX_FLAGS "-I/usr/local/Cellar/curl/7.57.0/include
-L/usr/local/Cellar/curl/7.57.0/lib")
to get CMAKE to actually utilize the lib version/location that I want. Is there a better way that I'm failing to figure out?
The cmake FindCURL module (at least in the version I have, 3.5.1) does not make reference to CURL_ROOT_DIR anywhere, so I don't believe setting that variable will have any effect.
In fact the FindCURL module is really quite basic and doesn't offer any means for customising where to look, which means it will be looking only in "standard" paths (eg /usr/lib, /usr/local/lib etc)
You can, however, use find_library which takes a PATHS argument.
set(CURL_PATH "/usr/local/Cellar/curl/7.75.0")
find_library(
LIB_CURL
NAMES
curl
PATHS
${CURL_PATH}/lib)
At this point you will have a variable ${LIB_CURL} which points to the location of libcurl
Link against the library and set the include path:
You can then just link against ${LIB_CURL} directly, and manually specify the include path for your target too, such as:
add_executable(
foo
main.cpp)
target_link_libraries(
foo
${LIB_CURL})
target_include_directories(
foo
SYSTEM
PRIVATE "${CURL_PATH}/include")
IMPORTED library target:
Another option, if you want to be fancy, is to create an IMPORTED library target using ${LIB_CURL}, and set the INTERFACE_INCLUDE_DIRECTORIES on that target, so consumers get the include path set automatically.
add_library(
libcurl
IMPORTED)
set_target_properties(
libcurl
PROPERTIES
IMPORTED_LOCATION "${LIB_CURL}")
set_target_properties(
libcurl
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${CURL_PATH}/include")
Now you will just be able to link against target libcurl directly, and your app will automatically have its include paths updated
add_executable(
foo
main.cpp)
target_link_libraries(
foo
libcurl)

Unable to include external previous built .a libraries in cmake

I have a libgarithm.a library previously compiled and have a header file garith.h how could i import it in my cmake project . I have included header files from
include_directories("/home/gaurav/Desktop/garith-lib/include")
but unable to link libraries and it is giving a comile time error
--- undefined reference to `multi(int, int)' the function in my library
You should create an imported target for your library and then use target_link_libraries:
add_library(garithm STATIC IMPORTED)
set_property(TARGET garithm PROPERTY IMPORTED_LOCATION
/path/to/libgarithm.a
)
set_property(TARGET garithm PROPERTY INTERFACE_INCLUDE_DIRECTORIES
/home/gaurav/Desktop/garith-lib/include
)
...
add_executable(foo main.cpp)
target_link_libraries(foo garithm)
Include directories are declared on the imported target as well so you don't have to call include_directories
EDIT: target_include_directoriesdoesn't work with imported targets, set the property INTERFACE_INCLUDE_DIRECTORIES instead
Starting with CMake 3.12.1, target_include_directories as well as other syntactic sugar for normal targets is also supported with imported targets.
For those writing cmake files with these statements, please do add cmake_minimum_required(3.12)
If you are getting errors such when compiling a particular third party library, check the cmake --version

Custom library in application, do I have to include the library dependency headers twice?

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?
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?