CMake can't find external library - c++

I am trying to build an application that uses the LibUSB library.
In a previous question I asked here I was told to use find_path and find_library to make CMake search for the headers and binaries. However even after manually looking up the library's installation locations with dnf and specifying them as PATHS or HINTS I always still get the error:
/usr/bin/ld: cannot find -lUSB
collect2: error: ld returned 1 exit status
Below is the relevent cmakelists.txt, my import line in main.cpp is #include <libusb-1.0/libusb.h>
add_executable(project main.cpp)
find_path(LIBUSB_INCLUDE_DIR
NAMES libusb.h
PATHS "/usr/include/"
PATH_SUFFIXES "include" "libusb")
find_library(LIBUSB_LIBRARY
NAMES USB
HINTS "/usr/lib/" "/usr/lib64/" "/usr/include/"
PATH_SUFFIXES "lib" "lib32" "lib64")
target_include_directories(project PRIVATE "/usr/lib/" "/usr/lib64/")
target_link_libraries(project USB)
Clearly I'm doing something wrong in this kludge of hacks, but could someone tell me what?

You're not using the result of your find operations anywhere. You tell CMake to find the headers and store the found paths in LIBUSB_INCLUDE_DIR, and to find the library and store its location in LIBUSB_LIBRARY, and then you go to ignore these and use hardcoded "/usr/lib/" "/usr/lib64/" and USB instead. Try this:
target_include_directories(project PRIVATE ${LIBUSB_INCLUDE_DIR})
target_link_libraries(project ${LIBUSB_LIBRARY})

As shown Back in the original question, here, all of the finding and including functions can be replaced simply with:
target_link_libraries(project_name <other_dependencies> usb-1.0),
in the file where the build target is defined.

Related

Avoid bad include paths in CMake's pkg-config fallback [duplicate]

Looking around on the net I have seen a lot of code like this:
include(FindPkgConfig)
pkg_search_module(SDL2 REQUIRED sdl2)
target_include_directories(app SYSTEM PUBLIC ${SDL2_INCLUDE_DIRS})
target_link_libraries(app ${SDL2_LIBRARIES})
However that seems to be the wrong way about doing it, as it only uses the include directories and libraries, but ignored defines, library paths and other flags that might be returned by pkg-config.
What would be the correct way to do this and ensure that all compile and link flags returned by pkg-config are used by the compiled app? And is there a single command to accomplish this, i.e. something like target_use(app SDL2)?
ref:
include()
FindPkgConfig
First of, the call:
include(FindPkgConfig)
should be replaced with:
find_package(PkgConfig)
The find_package() call is more flexible and allows options such as REQUIRED, that do things automatically that one would have to do manually with include().
Secondly, manually calling pkg-config should be avoid when possible. CMake comes with a rich set of package definitions, found in Linux under /usr/share/cmake-3.0/Modules/Find*cmake. These provide more options and choice for the user than a raw call to pkg_search_module().
As for the mentioned hypothetical target_use() command, CMake already has that built-in in a way with PUBLIC|PRIVATE|INTERFACE. A call like target_include_directories(mytarget PUBLIC ...) will cause the include directories to be automatically used in every target that uses mytarget, e.g. target_link_libraries(myapp mytarget). However this mechanism seems to be only for libraries created within the CMakeLists.txt file and does not work for libraries acquired with pkg_search_module(). The call add_library(bar SHARED IMPORTED) might be used for that, but I haven't yet looked into that.
As for the main question, this here works in most cases:
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
...
target_link_libraries(testapp ${SDL2_LIBRARIES})
target_include_directories(testapp PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(testapp PUBLIC ${SDL2_CFLAGS_OTHER})
The SDL2_CFLAGS_OTHER contains defines and other flags necessary for a successful compile. The flags SDL2_LIBRARY_DIRS and SDL2_LDFLAGS_OTHER are however still ignored, no idea how often that would become a problem.
More documentation here http://www.cmake.org/cmake/help/latest/module/FindPkgConfig.html
If you're using cmake and pkg-config in a pretty normal way, this solution works.
If, however, you have a library that exists in some development directory (such as /home/me/hack/lib), then using other methods seen here fail to configure the linker paths. Libraries that are not found under the typical install locations would result in linker errors, like /usr/bin/ld: cannot find -lmy-hacking-library-1.0. This solution fixes the linker error for that case.
Another issue could be that the pkg-config files are not installed in the normal place, and the pkg-config paths for the project need to be added using the PKG_CONFIG_PATH environment variable while cmake is running (see other Stack Overflow questions regarding this). This solution also works well when you use the correct pkg-config path.
Using IMPORTED_TARGET is key to solving the issues above. This solution is an improvement on this earlier answer and boils down to this final version of a working CMakeLists.txt:
cmake_minimum_required(VERSION 3.14)
project(ya-project C)
# the `pkg_check_modules` function is created with this call
find_package(PkgConfig REQUIRED)
# these calls create special `PkgConfig::<MODULE>` variables
pkg_check_modules(MY_PKG REQUIRED IMPORTED_TARGET any-package)
pkg_check_modules(YOUR_PKG REQUIRED IMPORTED_TARGET ya-package)
add_executable(program-name file.c ya.c)
target_link_libraries(program-name PUBLIC
PkgConfig::MY_PKG
PkgConfig::YOUR_PKG)
Note that target_link_libraries does more than change the linker commands. It also propagates other PUBLIC properties of specified targets like compiler flags, compiler defines, include paths, etc., so, use the PUBLIC keyword with caution.
It's rare that one would only need to link with SDL2. The currently popular answer uses pkg_search_module() which checks for given modules and uses the first working one.
It is more likely that you want to link with SDL2 and SDL2_Mixer and SDL2_TTF, etc... pkg_check_modules() checks for all the given modules.
# sdl2 linking variables
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2 SDL2_ttf SDL2_mixer SDL2_image)
# your app
file(GLOB SRC "my_app/*.c")
add_executable(my_app ${SRC})
target_link_libraries(my_app ${SDL2_LIBRARIES})
target_include_directories(my_app PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(my_app PUBLIC ${SDL2_CFLAGS_OTHER})
Disclaimer: I would have simply commented on Grumbel's self answer if I had enough street creds with stackoverflow.
Most of the available answers fail to configure the headers for the pkg-config library. After meditating on the Documentation for FindPkgConfig I came up with a solution that provides those also:
include(FindPkgConfig)
if(NOT PKG_CONFIG_FOUND)
message(FATAL_ERROR "pkg-config not found!" )
endif()
pkg_check_modules(<some-lib> REQUIRED IMPORTED_TARGET <some-lib>)
target_link_libraries(<my-target> PkgConfig::<some-lib>)
(Substitute your target in place of <my-target> and whatever library in place of <some-lib>, accordingly.)
The IMPORTED_TARGET option seems to be key and makes everything then available under the PkgConfig:: namespace. This was all that was required and also all that should be required.
There is no such command as target_use. But I know several projects that have written such a command for their internal use. But every project want to pass additional flags or defines, thus it does not make sense to have it in general CMake. Another reason not to have it are C++ templated libraries like Eigen, there is no library but you only have a bunch of include files.
The described way is often correct. It might differ for some libraries, then you'll have to add _LDFLAGS or _CFLAGS. One more reason for not having target_use. If it does not work for you, ask a new question specific about SDL2 or whatever library you want use.
If you are looking to add definitions from the library as well, the add_definitions instruction is there for that. Documentation can be found here, along with more ways to add compiler flags.
The following code snippet uses this instruction to add GTKGL to the project:
pkg_check_modules(GTKGL REQUIRED gtkglext-1.0)
include_directories(${GTKGL_INCLUDE_DIRS})
link_directories(${GTKGL_LIBRARY_DIRS})
add_definitions(${GTKGL_CFLAGS_OTHER})
set(LIBS ${LIBS} ${GTKGL_LIBRARIES})
target_link_libraries([insert name of program] ${LIBS})

CMake include shared library and it's dependencies

I am trying to include this C++ library with CMake. As I understood, the file that I need to include is located at lib/libbinacpp/lib/libbinacpp.so (relative to the library root folder).
So, I created a new folder, with two subfolders
src, which only contains one file: src/main.cpp (a simple Hello, World)
lib, which contains a folder binacpp, the result of cloning the library
On the top level, I have my CMakeLists.txt. First, I tried to write the following:
cmake_minimum_required(VERSION 3.20)
project(RebalancerBot)
set(CMAKE_CXX_STANDARD 20)
#Include binacpp
add_library(binacpp SHARED IMPORTED)
set_target_properties(binacpp PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/lib/binacpp/lib/libbinacpp/lib/libbinacpp.so)
add_executable(RebalancerBot src/main.cpp)
#Link everything after adding the executable
target_link_libraries(RebalancerBot PRIVATE binacpp)
Which resulted in the error
make[3]: *** No rule to make target 'lib/binacpp/lib/libbinacpp/lib/libbinacpp.so', needed by 'RebalancerBot'. Stop.
To my surprise, I got a different error after replacing ${CMAKE_BINARY_DIR} with the actual root path of my folder (i.e /home/actual_path/). I found this surprising since I thought that ${CMAKE_BINARY_DIR}$ should exactly be the path to the root CMakeLists.txt file. Anyway, after the replacement, I got the following new errors:
/usr/bin/ld: warning: libcrypto.so.1.0.0, needed by ../lib/binacpp/lib/libbinacpp/lib/libbinacpp.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: warning: libwebsockets.so.11, needed by ../lib/binacpp/lib/libbinacpp/lib/libbinacpp.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: ../lib/binacpp/lib/libbinacpp/lib/libbinacpp.so: undefined reference to `lws_client_connect_via_info'
/usr/bin/ld: ../lib/binacpp/lib/libbinacpp/lib/libbinacpp.so: undefined reference to `lws_callback_on_writable'
... (the list goes on)
If I understand correctly, these errors are due to the dependencies of libbinacpp.so. How can fix them?
PS: It should be noted that, if I cd into lib/binacpp/src and make inside of this directory, everything runs without errors.
EDIT: Thanks to #AlanBirtles comment, I found that the error with CMAKE_BINARY_DIR was due to the fact that it unfolds to the directory I run cmake in. What I needed was CMAKE_SOURCE_DIR: the directory containing my cmake file.
CMake offers the possibility to have a build directory which differs from the source directory. A offer we should gladly accept, because it keeps our build system from messing with our source and we can have different build directories, depending on build type, compiler, target system, etc.
${CMAKE_BINARY_DIR} is a shortcut to your top-level build directory. CMake will generate all files here during the build.
${CMAKE_SOURCE_DIR} is where CMake is looking for your sources. CMake is not intended to make changes in your source directory.
If you copied the library to your source directory, you need to reference it with the right shortcut.
By the way, are you sure, you want to copy the library to the source directory? Because you don't need to. You can use find_file (https://cmake.org/cmake/help/latest/command/find_file.html), find_library (https://cmake.org/cmake/help/latest/command/find_library.html), find_path (https://cmake.org/cmake/help/latest/command/find_path.html) or find_program (https://cmake.org/cmake/help/latest/command/find_program.html) to checkout the location of the item you need and use this path to define your imported library.

Linking with CMakeLists: ld cannot find library

I have a CMakeLists.txt file, with the following:
target_link_libraries(${PROJECT_NAME} OpenNI2)
When I run cmake, I receive no errors. But when I run make, I receive the following error:
/usr/bin/ld: cannot find -lOpenNI2
However, I have a file called libOpenNI2.so in my build directory. So why can ld not find this? I thought that the build directory was on the search path for target_link_libraries?
Thanks!
That's because when linking, the linker doesn't look in the current directory but only in a set of predefined directories.
You need to tell CMake where the library is, for example by giving the full path to the library in the target_link_library command, or adding it as an imported library.
it works if adding like:
target_link_libraries(${PROJECT_NAME} /path_to_library_build/libOpenNI2.a)
details:
ld is looking for the libraries in a very short list of folders defined in
/etc/ld.so.conf
and it usually looks like following:
include /etc/ld.so.conf.d/*.conf
and actual paths list from those *.conf files usually is like:
# Legacy biarch compatibility support
/lib32
/usr/lib32
# Multiarch support
/usr/local/lib/x86_64-linux-gnu
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
if your project linking library is not in the folder of this list, ld won't find it unless either a special linking variable set LD_LIBRARY_PATH with the path to your library or a complete path/library name provided in cmake target_link_libraries directive.
details on how to proper setup LD_LIBRARY_PATH variable discussed here

How to configure DBus dependencies with CMake

I am new to CMake and DBus. I am following along the guide here and make a basic program compile and execute.
The first problem that I ran into was my program will not find
<dbus/dbus.h>
I got around that issue by adding some include directories to my CMakeList.txt.
Currently, my CMakeLists.txt looks like this:
...
include_directories(/usr/lib/)
include_directories(/usr/include/dbus-1.0/)
include_directories(/usr/lib/x86_64-linux-gnu/dbus-1.0/include)
include_directories(/usr/include/glib-2.0)
include_directories(/usr/lib/x86_64-linux-gnu/glib-2.0/include/)
set (LIBS
dbus-1
dbus-glib-1
)
add_executable(mydbus mydbus.cpp)
target_link_libraries(mydbus ${LIBS} )
Now, my program is complaining about not being able to find dbus-arch-deps.h
/usr/include/dbus-1.0/dbus/dbus.h:29:33: fatal error: dbus/dbus-arch-deps.h: No such file or directory
#include <dbus/dbus-arch-deps.h>
I know that the solution for this is to use proper command line flags or pkg-config. As discussed here and numerous other posts.
However, I do not know how to configure CMakeLists.txt to have similar effect.
My guess would be to add something like find_package(dbus-1) to CMakeLists.txt. And if that is correct, I am going to have to write my own Finddbus-1.cmake. Does this sound correct? Or is there an easier way?
I will appreciate any pointers.
You may get an existing FindDBus.cmake script (e.g., this one), copy it into your project, and use as
find_package(DBus REQUIRED)
# Use results of find_package() call.
include_directories(${DBUS_INCLUDE_DIRS})
add_executable(mydbus mydbus.cpp)
target_link_libraries(mydbus ${DBUS_LIBRARIES})
Alternatively, as you know pkgconfig can find DBus, you may use CMake module PkgConfig. Actually, FindDBus.cmake script, referenced above, uses PkgConfig module in its implementation. Possible usage could be:
find_package(PkgConfig REQUIRED) # Include functions provided by PkgConfig module.
pkg_check_modules(DBUS REQUIRED dbus-1) # This calls pkgconfig with appropriate arguments
# Use results of pkg_check_modules() call.
include_directories(${DBUS_INCLUDE_DIRS})
link_directories(${DBUS_LIBRARY_DIRS})
add_executable(mydbus mydbus.cpp)
target_link_libraries(mydbus ${DBUS_LIBRARIES})
However, using link_directories is not recommended, it is better to use absolute paths to libraries in target_link_libraries() call. That is why it is better to combine pkg_check_modules with find_library, as it is done in the referenced Find script. That answer describes generic way for use result of pkgconfig in CMake.

Cmake cannot find library using "link_directories"

I Ubuntu, I am learning about cmake and make, and just trying a simple example. I have two directories: src and build. In src, I have two files: main.cpp, and CMakeLists.txt, which has (only) the following text:
add_executable(test main.cpp)
link_directories(/usr/lib/x86_64-linux-gnu)
target_link_libraries(test protobuf)
In /usr/lib/x86_64-linux-gnu, there is a shared library called libprotobuf.so, which I want to link against. My main.cpp uses functions in this library, by including the releveant header file, #include <google/protobuf/message.h>.
Now, in my build directory, I run cmake ../src, and then make. However, I then get linker errors telling me that there are undefined references to some of the functions in the protobuf library. If I do a search through all the files and subdirectories in build, there is not mention of anything related to protobuf.
However, if I remove the link_directories line in my CMakeLists.txt file, and instead write the full path to the library when specifying the executable, i.e. target_link_libraries(test /usr/lib/x86_64-linux-gnu/libprotobuf.so), it compiles and links fine.
Why is link_directories not allowing cmake to find this library?
Do not use link_directories like this in CMake.
This is a common beginner's mistake, as many other build environments work like this, but in CMake it's just asking for trouble. Even the official documentation specifically advises against it:
Note that this command [link_directories] is rarely necessary. Library locations returned
by find_package() and find_library() are absolute paths. Pass these
absolute library file paths directly to the target_link_libraries()
command. CMake will ensure the linker finds them.
So instead, always pass absolute paths to target_link_libraries and use find_library to resolve the link directory:
find_library(PROTOBUF_LIBRARY protobuf HINTS /usr/lib/x86_64-linux-gnu)
target_link_libraries(test PUBLIC ${PROTOBUF_LIBRARY})
This has the huge benefit that you will probably get a diagnostic at CMake configure time if the expected library cannot be found, instead of a random linker error at compile time. Also, this allows the user to specify a library location via the GUI if the target machine has a non-standard directory layout.
So if it doesn't work right away, be sure to check the result of the find_library call and consult the official documentation to track down why it doesn't find your library as intended.
Make sure that your call to link_directories takes place before your call to the relevant add_executable.
I had mistakenly believed it only needed to be before the call to target_link_libraries, but that's not the case. After moving the call, the library is linked properly.
Make sure that the order will be link_directories, set PROJECT_LINK_LIBS, add_executable and then target_link_libraries.
Below is example to demonstarte it:
cmake_minimum_required(VERSION 2.8.9)
project (Logging)
include_directories(include)
file(GLOB LOGGINGSOURCES "libsrc/*.cpp")
file(GLOB SOURCES "src/*.cpp")
add_library(convertString SHARED ${LOGGINGSOURCES})
install(TARGETS convertString DESTINATION /root/Deepak/)
link_directories( /root/Deepak/ )
set(PROJECT_LINK_LIBS libconvertString.so)
add_executable(hello ${SOURCES})
target_link_libraries(hello ${PROJECT_LINK_LIBS} )
Perhaps it's very old topic but none of proposed solutions worked for me. So I had to make my own dirty hack. I do crosscompiling with buildroot and include toolchainfile.cmake.
#...
set(LIB_PATH ${PROJECT_SOURCE_DIR}/relative/path/to/your/lib)
#...
include_directories(/path/to/library/include)
set(LIB_MYLIB ${LIB_PATH}/libmylib.so)
#...
add_executable(${PROJECT_NAME} ${APP_SOURCES})
target_link_libraries(${PROJECT_NAME}
${LIB_MYLIB}
)
Hope this will help