CMake find_package found but can't link to library - cmake

I have in my workspace a library "serial" (serial.lib) and exe project, where the exe needs to link to the library for building and linking.
In my exe CMakeList.txt, itdoesn't seem to have any problem with find_package(serial):
Found serial: 1.2.1 (D:/../install/share/serial/cmake)
Here is my CMake:
...
include_directories(include ${serial_INCLUDE_DIRS})
find_package(serial REQUIRED)
target_link_libraries(${PROJECT_NAME} serial)
#Do i need the following?
add_dependencies(${PROJECT_NAME} serial)
...
However, it can't seem to find the "serial.lib" which was already built and available in the install\lib\serial directory.
${serial_INCLUDE_DIRS} prints out the correct directory: install/include
If I add the line:
add_dependencies(${PROJECT_NAME} serial)
I will end up with error: The dependency target "serial" of target "test" does not exist.
What was missing? Is there a need to add the link_directories(...)? How do I specify that, if ${serial_INCLUDE_DIRS} can point to the correct directory, is there such a thing as ${serial_LIBS} which will point to the library directory? I tried the ${serial_LIBS} and this seems to be just empty.

Related

Cmake and vcpkg - find the correct library name

I have troubles finding out the right "library target name" to be used in a cmake file, for packages installed using vcpkg.
In example, I installed the gtest package using vcpkg install gtest. My sample cmake file looks like:
#CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(example)
add_executable(main main.cpp)
find_package(gtest REQUIRED)
target_link_libraries(main gtest) # here, "gtest" is not the right name!
Running cmake, a solution for Visual Studio is generated, but after running cmake --build ., I get the error:
../use-cmake-vcpkg\main.cpp(1): fatal error C1083: Cannot open include file: 'gtest/gtest.h': No such file or directory ..
Turns out the line: target_link_libraries(main gtest) isn't correct, and I need to use another "name" to include/link the gtest package.
Is there a way (using cmake or vcpkg) to find out what is the correct target name to be used? (for gtest in this case, but also for any other pacakage?)
When use find_package(XXX), it can work in two modes: MODULE and CONFIG. And resulted variables and targets of this call depend on the mode.
If FindXXX.cmake file exists (and can be found), the MODULE mode is used and given file is processed. Otherwise, if the package is shipped with XXXConfig.cmake file, CONFIG mode is used and given file is processed. If none of this file exists, CMake emits an error (if called with REQUIRED keyword) or a warning (without REQUIRED keyword).
In case of gtest package, CMake is shipped with FindXXX.cmake script, so this script is processed in MODULE mode. You may find description of this script in the documentation, which tells that you need to use GTest::GTest target for link with gtest:
target_link_libraries(main GTest::GTest)
Not all packages provide a CMake library definition. If you're lucky, then vcpkg install will show you the name:
$ ./vcpkg install openssl
The package openssl is compatible with built-in CMake targets:
find_package(OpenSSL REQUIRED)
target_link_libraries(main PRIVATE OpenSSL::SSL OpenSSL::Crypto)
This will work even if you've already installed the package, so you can use it anytime to query the package name.
On the other hand, if vcpkg install <pkg> doesn't say anything about CMake, then you need to include it manually in your CMake file, by finding the include path and the library files.
Here is an example of such a case, here for the live555 library:
# Use one of the headers to locate the include location
find_path(LIVE555_INCLUDE_DIR liveMedia.hh)
# Find the libraries
find_library(LIVE555_LIBRARY1 liveMedia)
find_library(LIVE555_LIBRARY2 groupsock)
find_library(LIVE555_LIBRARY3 BasicUsageEnvironment)
find_library(LIVE555_LIBRARY4 UsageEnvironment)
add_executable(rtsp testRTSPClient.cpp)
target_include_directories(rtsp PRIVATE ${LIVE555_INCLUDE_DIR})
target_link_libraries(rtsp PRIVATE ${LIVE555_LIBRARY1} ${LIVE555_LIBRARY2} ${LIVE555_LIBRARY3} ${LIVE555_LIBRARY4})

find SFML with CMake on Windows

I compiled SFML on windows (MinGW) and find myself with a directory structure with bin, include, lib and a cmake/Modules/FindSFML.cmake file.
I can't find how to use SFML in the CMakeLists.txt, whether it is
find_package(SFML REQUIRED)
or
find_package(SFML CONFIG REQUIRED)
All I get when I use cmake is:
CMake Error at CMakeLists.txt:34 (find_package):
Could not find a package configuration file provided by "SFML" with any of
the following names:
SFMLConfig.cmake
sfml-config.cmake
Add the installation prefix of "SFML" to CMAKE_PREFIX_PATH or set
"SFML_DIR" to a directory containing one of the above files. If "SFML"
provides a separate development package or SDK, be sure it has been
installed.
I tried setting manually:
SFML_DIR:PATH=E:/code/libraries/SFML-2.4.0
CMAKE_MODULE_PATH:PATH=E:/code/libraries/SFML-2.4.0
or
CMAKE_MODULE_PATH:PATH=E:/code/libraries/SFML-2.4.0/cmake/modules
But I'm not making any progress.
This is the step by step guide we were provided (picture). When this is done you have to remember that you need to link the librarys to your program, with this line:
target_link_libraries (<Name of project here> sfml-graphics sfml-window sfml-system)
if you want to use audi library in SFML just add sfml-audio at the end and so on.
in debug mode you should have a -dat the end of each library. Like this sfml-graphics-d.
If you do some big change in CMake or changing project you need to add:
-IC:/dev/libs/SFML/include -LC:/dev/libs/SFML/libback again.
(You can add the folder where you want). -Iis for Include folders and -Lis for all lib folders, you need this letter in front of all paths.
If you have problems linking /bin folder up you can drop all the .dll files inside System32 folder(for 64-bit, SysWOW64 for 32-bit).
And remember, have a GW that is matching SFML.
When I got same error in cmake-gui, all I did - added entry with name "SFMLDIR" and value with path to sfml directory.

Making a CMake library accessible by other CMake packages automatically

I have one project that produces a library:
project (myCoolLibrary)
ADD_LIBRARY(my_cool_library SHARED ${mysources_SRC})
And another project that should be using this library:
find_package (myCoolLibrary REQUIRED)
INCLUDE_DIRECTORIES("${myCoolLibrary_INCLUDE_DIRS}" )
add_executable(myCoolExe ${my_sources_SRC} )
TARGET_LINK_LIBRARIES(myCoolExe ${myCoolLibrary_LIBRARIES} )
Is there a way that I can change the first file so that the second file works automatically? That by running CMake on the first file and then running make on the output, then running CMake on the second file, CMake is able to find the package?
An answer where I just give the address of where the first project is built to the second package is also acceptable.
Taking the code found in a blog post by #daniperez - Use CMake-enabled libraries in your CMake project (III) - I've come up with the following minimal solution:
myCoolLibrary/CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project(myCoolLibrary)
function(my_export_target _target _include_dir)
file(
WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Config.cmake"
"
include(\"\$\{CMAKE_CURRENT_LIST_DIR\}/${_target}Targets.cmake\")
set_property(
TARGET ${_target}
APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES \"${_include_dir}\"
)
"
)
export(TARGETS ${_target} FILE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Targets.cmake")
# NOTE: The following call can pollute your PC's CMake package registry
# See comments/alternatives below
export(PACKAGE ${_target})
endfunction(my_export_target)
...
add_library(${PROJECT_NAME} SHARED ${mysources_SRC})
my_export_target(${PROJECT_NAME} "${CMAKE_CURRENT_SOURCE_DIR}")
myCoolExe/CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project(myCoolExe)
find_package(myCoolLibrary REQUIRED)
...
add_executable(${PROJECT_NAME} ${my_sources_SRC})
target_link_libraries(${PROJECT_NAME} myCoolLibrary)
To make it reusable I have packed everything into my_export_target(). And I'm friend of self-propagating properties like INTERFACE_INCLUDE_DIRECTORIES.
As commented by #ruslo, using export(PACKAGE ...) can pollute your package registry. So alternatively you can:
Write the target configuration files directly to some dedicated place specific for a certain toolchain
See e.g. How to install your custom CMake-Find module and 0003659: FIND_PACKAGE command improvements.
Set CMAKE_MODULE_PATH via the second project's CMake command line (injecting the search path(s) from the outside). If you are building the two projects anyway with a build script, then this is the most direct way to propagate the module search path(s).
Additional References
export()
CMake/Tutorials/Package Registry
Unable to find Eigen3 with CMake
How to instruct CMake to use the build architecture compiler

GLEW with CMakeLists

I have the following CMakeLists to include glew into my project. Compiling the project works fine but when I run the project I get an error saying that the program can't find glew32.dll. Any ideas why?? Thanks for answers!
set(CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/external/glew-1.12.0")
set(CMAKE_LIBRARY_PATH "${CMAKE_SOURCE_DIR}/external/glew-1.12.0/lib/Release/Win32/")
find_package(GLEW REQUIRED)
if(GLEW_FOUND)
message("GLEW Found!")
include_directories(${GLEW_INCLUDE_DIRS})
link_libraries(${GLEW_LIBRARIES})
add_definitions(${GLEW_DEFINITIONS})
else(GLEW_FOUND)
message(FATAL_ERROR "GLEW NOT Found")
endif(GLEW_FOUND)
add_executable(Project ${CODE})
target_link_libraries(Project ${GLEW_LIBRARIES})
You need to have the directory where glew32.dll can be found in your environment's PATH variable. If you're using MSVC set the Environment textbox in your property pages of your application:
PATH=<dir-of-glew32.dll>;%PATH%
If you're running from shell, issue the same command on the command line.
Another option is to use GLEW as a static library. In that case you will not need glew32.dll, the entire glew library will be linked into your program. Check out the GLEW github repository and study the script cmake-testbuild.sh about how to use GLEW as a static library.
Please note, that the GLEW github repository does not contain certain generated files. If you can't run make extensions on your platform, use this repository: glew-with-extensions which already contains the generated files.

CMAKE RPATH not working - could not find shared object file

I am trying to get rid of setting LD_LIBRARY_PATH everytime time I run my program. After adding in the library and targeting my executable to the library, when I run it tells me it can not open shared object library, no such file or directory.
In my CMakeLists.txt I have:
add_library(heart SHARED ${HEART_FILES})
add_executable(run ${RUN_FILES})
target_link_libraries(run heart)
set(CMAKE_SKIP_BUILD_PATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH "~/person/target/usr/local/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
I set an absolute link to my library folder to test out whether this would create an rpath to my library and it seems like there isn't. I have checked and made sure that the shared library is indeed in lib. libheart.so is the file that is being linked. What else am I missing?
It is because you build heart and run from the same cmake project:
CMAKE_INSTALL_RPATH_USE_LINK_PATH is an interesting and very useful option. When building a target with RPATH, CMake determines the RPATH by using the directories of all libraries to which this target links. Some of these libraries may be located in the same build tree, e.g. libbar.so, these directories are also added to the RPATH.
If this option is enabled, all these directories except those which are also in the build tree will be added to the install RPATH automatically. The only directories which may then still be missing from the RPATH are the directories where the libraries from the same project (i.e. libbar.so) are installed to. If the install directory for the libraries is not one of the systems default library directories, you have to add this directory yourself to the install RPATH by setting CMAKE_INSTALL_RPATH accordingly
You can try this:
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
More documentation here cmake rpath handling
EDIT:
Only this should work:
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
add_library(heart SHARED ${HEART_FILES})
add_executable(run ${RUN_FILES})
target_link_libraries(run heart)
install(
TARGETS heart run
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
)
Clean your build directory and then:
cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=/home/person/target/usr/local ..
make install
At the end of the g++ line Linking CXX executable run you should see like -Wl,-rpath,/home/person/target/usr/local/lib
If you want a fully relocatable package:
set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
PS: are you sur that it is libheart.so that is not found ?
In your CMake file, set the RPATH before defining the targets. The CMAKE_INSTALL_RPATH must be defined before calling add_executable(), otherwise it has no effect.
I had a similar issue as the original post. I created executables which linked to external shared libraries. This approach compiled and executed fine from the build directory. However, the executable that was installed to a separate directory could not find a shared library at runtime:
error while loading shared libraries: libxxxx.so.1: cannot open shared object file: No such file or directory
To solve, I
1) upgraded to CMake 3.17
2) used Craig Scott's recommended:
set(CMAKE_INSTALL_RPATH $ORIGIN)
as explained in his talk
3) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) as directly mentioned to solve this error in the second common question in Kitware's documention
4) Put all this before adding the targets as mentioned in this post
5) Used the "$ORIGIN/../lib" syntax instead of Craig's Scott's mentioned $ORIGIN as mentioned by #explo91
In summary, and to my suprise, only the "$ORIGIN/../lib" before the target definition was necessary from above (I tested the other combinations which did not fix the cannot open shared object file runtime issue).
Anyway the solution I finally applied, which may be of better, fine-grained CMake style or at least may be helpful to others on their RPATH journey is:
set_target_properties(target_defined_above PROPERTIES INSTALL_RPATH "$ORIGIN/../lib")