Explain me how libraries are searched in this case - c++

Simplified a little
I have a project with 2 targets (subdirectories): first libMain.so library, who linked with second libDependent.so :
add_library( Main SHARED ${cpp} )
target_link_libraries(Main
Dependent
)
I make a conan package, that contains 2 libraries - libMain.so and libDependent.so inside /package/CONANPACKAGEID/lib folder
In conan also located FindMainPackage.cmake with example code
find_library (Main_LIBRARIES Main Main_d
HINTS ${Main_ROOT_DIR} ${CMAKE_INSTALL_PREFIX}
PATH_SUFFIXES lib
)
and find_path
In my general project I use find_package(Main) and link my third lib with Main
target_link_libraries(General
${Main_LIBRARIES}
)
or
target_link_libraries(General
Main
)
both variants are work
And last I linked this lib with my google test binary
target_link_libraries( Test
GTest::GTest
General
)
If I set to LD_LIBRARY_PATH a variable ${CONAN_LIB_DIRS_MAIN} (equal /package/CONANPACKAGEID/lib) and run binary from build dir from terminal -> it work good, all libs are found
But if I try to run w/o (5) I get the output
error while loading shared libraries: libDependent.so: cannot open shared object file: No such file or directory
I check my binary with ld command and get the output
libMain.so /.conan/data/package/CONANPACKAGEID/lib/libMain.so (0x00007c815718f000)
libDependent.so => **not found**
If I link my General with Dependent
target_link_libraries(General
Main
Dependent
)
all works good and libDependent.so located in conan package near with libMain.so in ld output
My questions:
Why my binary know thatlibMain.so inside conan package folder. This conan package are not exists in lib pathes ( check with ldconfig)
Why my binary cannot find libDependent.so automatically
How I can fix this or how work with situation right?
Thank you for help, my brain in over after 2days...

Related

CMAKE linker does not find library; but library is found with find_library

I have a catkin library under the Name mylib which I build with catkin build
Furthermore, I have a node in which uses functions from this library. I enabled this link as I usually do in the CMakeLists.txt of the node:
find_package(catkin REQUIRED COMPONENTS
mylib
)
add_executable(exec
src/main.cpp
)
target_link_libraries(exec
${catkin_LIBRARIES}
)
However it did not succeed this time. Linker error
I then added:
find_package(catkin REQUIRED COMPONENTS
mylib
)
find_library( MYLIB NAMES
mylib
)
message(${MYLIB})
add_executable(exec
src/main.cpp
)
add_dependencies(exec ${MYLIB})
target_link_libraries(exec
${catkin_LIBRARIES}
${MYLIB}
)
The thing is the message() statement prints the correct path of the library, where i can also find it in the explorer.
However I get the warning:
(add_dependencies): Policy CMP0046 is not set: Error on non-existent dependency in add_dependencies.
Which refers to the exact same path for the library and says it is not existent.
The linker error is
/usr/bin/ld: cannot find -lmylib
Remark: I could solve the error by adding the path to the library manually
link_directories($ENV{HOME}/test/devel/lib)
I do not understand why the library is found first, but cannot be linked as its package name. But it works by providing the full path.
I appreciate any insight!
The library is not in your linker path. E.g. your linker looks in /link and you have a lib in /home. You know where to look and can see it in your file browser but the linker won't find it because it only looks in '/link'.
'find_package' looks for the package and sets some variables but it doesn't change the linker path.
You have to set the linker path by yourself. In most cases find_package sets a variable containing the linker path.
find_package provides some functions like catkin_package(). These functions set your build environment.
catkin_package() is a catkin-provided CMake macro. This is required to specify catkin-specific information to the build system which in turn is used to generate pkg-config and CMake files.
This function must be called before declaring any targets with add_library() or add_executable().

CMake dynamically links `.a` files in `/usr/local/lib`

I want to statically compile my program against another static library, for this example I'm using zeromq. Here is my CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
add_executable( test test.cpp )
find_library(ZMQ NAMES libzmq.a)
message(STATUS ${ZMQ})
target_link_libraries( test ${ZMQ} )
It finds the .a file when I run mkdir build && cd build && cmake ..
-- /usr/local/lib/libzmq.a
However, if I examine the link.txt file, the library is dynamically linked:
/usr/bin/c++ CMakeFiles/test.dir/test.cpp.o \
-o test -rdynamic /usr/local/lib/libzmq.a
The weird bit is that if I move the file to a different directory, say /usr/lib and run cmake .. once more, it locates the new path to the library:
-- /usr/lib/libzmq.a
But now it has magically changed to static linking:
/usr/bin/c++ CMakeFiles/test.dir/test.cpp.o \
-o test -rdynamic -Wl,-Bstatic -lzmq -Wl,-Bdynamic
The same thing applies to other libraries I'm linking to.
Why are all my libraries in /usr/local/lib being dynamically linked?
You should not use the path directly, and create an imported target instead, so you can explicitly declare it static:
cmake_minimum_required(VERSION 2.6)
add_executable( test test.cpp )
find_library(zmq_location NAMES libzmq.a)
message(STATUS ${zmq_location})
add_library(zmq STATIC IMPORTED)
set_target_properties(zmq PROPERTIES IMPORTED_LOCATION ${zmq_location})
target_link_libraries( test zmq )
This may lead to a situation where the library appears to be linked dynamically, but the cmake source code has the answer:
If the target is not a static library make sure the link
type is shared. This is because dynamic-mode linking can handle
both shared and static libraries but static-mode can handle only
static libraries. If a previous user item changed the link type to
static we need to make sure it is back to shared.
Essentially, it's letting the linker handle detecting that the library is static if currently in dynamic-mode.
The answer to my original question about the difference between /usr/local/lib and /usr/lib is that, by default, /usr/local/lib is not one of the implicit link directories. Therefore, a quick fix is to include this line in the config:
set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES /usr/local/lib ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES})
However, as pointed out in this other answer, referencing files directly is not the way to go, instead one should use add_library.
Here is my DEMO:
find_library(VLQ_LIB NAMES vlq-shared PATHS /usr/local/lib/ REQUIRED)
find_path(VLQ_HEADER vlq.hpp /usr/local/include)
target_link_libraries(myproj PUBLIC VLQ_LIB)
target_include_directories(myproj PUBLIC VLQ_HEADER)
but during run, you still need to copy the shared lib to /lib/ folder, for static lib, you can keep it under /usr/lib

CMake link to dynamic library generated by call to ExternalProject_Add

I have a small test program that I want to link to GLFW. I am currently able to download, configure and build the .dll using ExternalProject_Add command. When I build my test program I get an executable that doesn't run because it can't find the .dll. If I manually copy the .dll to the directory where the executable is, it runs just fine.
How do I get my executable to properly link to the library?
Is there a way to automatically copy the .dll to where it needs to be?
What is the best way to ensure that, when it comes time to package my program, the library is available to use and easily accessible?
CMakeLists.txt:
cmake_minimum_required (VERSION 2.8)
project (GLFW-test)
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/app )
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
# Include OpenGL
find_package(OpenGL REQUIRED)
if (OPENGL_FOUND)
include_directories(${OPENGL_INCLUDE_DIR})
link_libraries(${OPENGL_LIBRARIES})
endif()
# Add directories for library linkage
set(GLFW_LIB_DIR ${CMAKE_BINARY_DIR}/downloads/deps/Build/GLFW_EX/src)
link_directories(${GLFW_LIB_DIR})
# Download and unpack dependencies at configure time
configure_file(deps-CMakeLists.txt downloads/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/downloads)
execute_process(COMMAND ${CMAKE_COMMAND} --build .
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/downloads)
add_subdirectory(${CMAKE_BINARY_DIR}/downloads/deps/Source/GLFW_EX
${CMAKE_BINARY_DIR}/downloads/deps/Build/GLFW_EX
EXCLUDE_FROM_ALL )
include_directories(${CMAKE_BINARY_DIR}/downloads/deps/Source/GLFW_EX/include)
add_executable(GLFW-test src/GLFW-test.cpp)
target_link_libraries (GLFW-test glfw3 ${OPENGL_LIBRARIES})
add_custom_command(TARGET GLFW-test POST_BUILD # Adds a post-build event to MyTest
COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake - E copy_if_different..."
"${GLFW_LIB_DIR}/glfw3.dll" # <--this is in-file
$<TARGET_FILE_DIR:GLFW-test>) # <--this is out-file path
dep-CMakeLists.txt:
cmake_minimum_required (VERSION 2.8)
project (GLFW-dl)
include(ExternalProject)
set_directory_properties(PROPERTIES EP_BASE "./deps/")
# Include GLFW
ExternalProject_Add (
GLFW_EX
GIT_REPOSITORY "https://github.com/glfw/glfw.git"
GIT_TAG "master"
CMAKE_ARGS -DGLFW_BUILD_EXAMPLES=OFF
-DGLFW_BUILD_TESTS=OFF
-DGLFW_BUILD_DOCS=OFF
-DGLFW_INSTALL=OFF
-DBUILD_SHARED_LIBS=ON
UPDATE_COMMAND ""
TEST_COMMAND "" )
UPDATE:
The way I am using ExternalProject_Add is described on this site: https://crascit.com/2015/07/25/cmake-gtest/
It allows the external projects to be configured and built only once during the configure phase of my project. I have changed the directories around a bit from their test program to make things a little easier for when I eventually add more external projects. The test project on the site does not seem to account for dynamic libraries which is what I am trying to do.
UPDATE 2:
I've added 2 set commands to help clean up the build directory towards the top of the CMakeLists file. I also added a command at the bottom which copies .dll that is built from the ExternalProject_Add command to where I need it (next to the final executable). That seems to work for Windows, but it seems a bit hacky and doesn't resolve the errors in my IDE, which is currently Eclipse. Is there still a better way to do this?
Helpful Related topics:
Setting the RPATH for external projects?
Cmake on Windows doesn't add shared library paths (works on linux)
How to copy DLL files into the same folder as the executable using CMake?
How do I get my executable to properly link to the library?
As your second link states, there is no other way than to have .dll in the same directory as executable.
Is there a way to automatically copy the .dll to where it needs to be?
In you main project you already use variable CMAKE_RUNTIME_OUTPUT_DIRECTORY for setup directory where executables and .dlls should be placed after build. You can pass this variable to ExternalProject_add for force it to use same conventions:
ExternalProject_Add (...
CMAKE_ARGS -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
...
)
What is the best way to ensure that, when it comes time to package my program, the library is available to use and easily accessible?
Packaging just uses install-tree of your project. So it is sufficient to install executables and libraries into same location:
set(INSTALL_RUNTIME_DIR bin)
install(TARGETS GLFW-test
RUNTIME DESTINATION ${INSTALL_RUNTIME_DIR}
)
install(FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/glfw3.dll
DESTINATION ${INSTALL_RUNTIME_DIR}
)
Note, that target GLFW_EX obtained from external project has no special type(like executable or library), so you need to install its deliverables using plain filenames.

Cmake linking to shared library cannot find library

On Ubuntu, I have two directories: build and src. In src, my CMakeLists.txt file has the lines:
add_executable(Test main.cpp)
target_link_libraries(Test libCamera.so)
After running cmake in the build directory (cmake ../src), I then copy my library file libCamera.so into the build directory. After running make, the main.cpp.o file compiles successfully, but I receive the following error during linking:
/usr/bin/ld: cannot find -lCamera
Why is this? The shared library is in the same directory that I am building in... and the same thing happens if I copy the library to /usr/bin...
You should not put prefix lib and suffix .so of the library, so just use:
target_link_libraries(Test Camera)
if your library not found you may need to add directory, where library is located:
link_directories( /home/user/blah ) # for specific path
link_directories( ${CMAKE_CURRENT_BINARY_DIR} ) # if you put library where binary is generated
Note: you copied lib to /usr/bin but unlike Windows where dll files stored with executables, in Linux that is not the case, so it would be /usr/lib, not /usr/bin. Also you may change LD_LIBRARY_PATH variable to make your program to find a library in a custom location.

external library linked with cmake can not be found at runtime

I have linked an external c++-library to an existing cmake project using the following cmake commands:
SET (some_src
.
.(sourcefiles here)
.
.
.
.
)
ADD_LIBRARY(some_proj SHARED ${some_src})
# Adding precompiled NURBS lib
SET(nurbs_libs ${CMAKE_SOURCE_DIR}/src/nurbs/libs)
ADD_LIBRARY( nurbs_C SHARED IMPORTED )
SET_TARGET_PROPERTIES( nurbs_C PROPERTIES
IMPORTED_LOCATION ${nurbs_libs}/surface.so.1.0
LINKER_LANGUAGE "CXX")
TARGET_LINK_LIBRARIES( some_proj nurbs_C )
SET(CMAKE_INSTALL_PATH ${nurbs_libs})
INSTALL(TARGETS some_proj DESTINATION lib)
install(FILES ${nurbs_libs}/surface.so.1.0 DESTINATION lib)
When trying to run a program that uses the library I keep getting the error
reels: error while loading shared libraries:
surface.so.1: cannot open shared object file: No such file or directory
I tried playing with LD_LIBRARY_PATH and RPATH but to no avail. I suspect I did something wrong with the cmake file. How can I handle this problem?
Because you have binary file compiled, there is probably no problem with your CMakeLists.txt. Use ldd to find out which libraries you are missing. Also check that you have surface.so.1 present on disk (probably it would be link directing to surface.so.1.0)