CMake links against wrong version of library - c++

I have some CMake packages that depend on Protobuf v2.5, and one that depend on Protobuf v3.4. I have v2.5 installed system-wide in /usr, whereas v3.4 is only used in a single package. Therefore, I put the headers for v3.4 in a 3rdparty subdirectory inside the package where it is being used, and then I call include_directories(3rdparty) in my CMakeLists.txt so it can be found.
As for the shared libraries, the .so files for v2.5 are present in /usr/lib/x86_64-linux-gnu, and I installed the .so files for v3.4 to /usr/lib. In short, this is what the directory structure looks like:
v2.5:
headers: /usr/include
libraries: /usr/lib/x86_64-linux-gnu
v3.4:
headers: <MY_PACKAGE_SRC_DIRECTORY>/3rdparty
libraries: /usr/lib
Now, the problem arises when I try to link against v3.4. To simplify things, I don't use any CMake module files to find protobuf v3.4, rather I just add a hard-coded path /usr/lib/libprotobuf.so to the list of libraries to link against when creating a target. But even so, when I run ldd my_target_executable, the result is:
libprotobuf.so.8 => /usr/lib/x86_64-linux-gnu/libprotobuf.so.8
meaning that it is linking against the libraries for v2.5 in /usr/lib/x86_64-linux-gnu, even though I added a hard-coded path to the correct .so file in /usr/lib in the call to target_link_libraries when building this executable.
What is worth noting is that if I remove the .so files in /usr/lib/x86_64-linux-gnu, then it does link against the correct .so file in /usr/lib, so it appears that for some reason, CMake searches in /usr/lib/x86_64-linux-gnu before using the library path that I provide it. How can I change this behavior, or fix this problem in any other way?
UPDATE
The library file for v3.4 /usr/lib/x86_64-linux-gnu/libprotobuf.so is a link to /usr/lib/x86_64-linux-gnu/libprotobuf.so.14 which in turn is a link to the actual file /usr/lib/x86_64-linux-gnu/libprotobuf.so.14.0.0.
Now, if I change the hard-coded path that I give in target_link_libraries from /usr/lib/x86_64-linux-gnu/libprotobuf.so to either the second symlink /usr/lib/x86_64-linux-gnu/libprotobuf.so.14, or to the actual file /usr/lib/x86_64-linux-gnu/libprotobuf.so.14.0.0, then my executable correctly links againt v3.4. It appears that the name of the provided symlink has some effect on CMake's behavior.

This isn't cmake specifically but also how things work on Linux with gcc and shared libraries.
http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html
When you specify target_link_libraries( target /usr/lib/x86_64-linux-gnu/libprotobuf.so) this sets up linking as -lprotobuf. In this case it should just use any version of the library it finds first.
target_link_libraries( target /usr/lib/x86_64-linux-gnu/libprotobuf.so.14) adjusts the cmake generated link line to use a specific library version. This seems to tell gcc to link against that version which will change what happens at run-time and library searches.
target_link_libraries
There are some cases where CMake may ask the linker to search for the library (e.g. /usr/lib/libfoo.so becomes -lfoo), such as when a shared library is detected to have no SONAME field. See policy CMP0060 for discussion of another case.

Related

How do you tell CMake to statically link to a library in a package found with find_package?

I am building a library from source and an executable that depends on the library. The library depends on a third-party library, libjpeg, which I would like to handle via static linking. libjpeg is installed on my system. I can see the .a file at /usr/lib/x86_64-linux-gnu/libjpeg.a(I am building on Ubuntu.)
When processing my library's CMakeLists.txt file, CMake successfully finds libjpeg but its output announces that is has found the dynamic version of libjpeg i.e. the output from CMake is
-- Found JPEG: /usr/lib/x86_64-linux-gnu/libjpeg.so (found version "80")
then later in the CMakeLists.txt I link my library to libjpeg via
target_link_libraries( my_library
PRIVATE JPEG
)
and building is succesful. However when I then link to my_library from my_executable I get the following error:
[100%] Linking CXX executable my_executable
/usr/bin/ld: cannot find -lJPEG
collect2: error: ld returned 1 exit status
which I take to mean that the problem is that since my_library is linking to libjpeg dynamically I would need to target_link_libraries to libjpeg from the executable rather than the library. In any case what I want to do is completely hide my_library's dependency on libjpeg.
Is there some way to tell find_package to prefer static libraries?
Before anything, you should fix your script:
target_link_libraries(my_library PRIVATE JPEG::JPEG)
You have to give the imported target name to target_link_libraries.
That being said, no, not directly. find_package itself does nothing of the sort, it simply looks for the correct script and calls it. Thus the actual behavior is library-specific. For libjpeg, probably it finds a copy of this script and runs it.
For libjpeg in particular, FindJPEG.cmake provides no way to tell it to prefer static or dynamic version.
However, you can define JPEG_LIBRARY_RELEASE before calling find_package, to force it to find a specific version. For instance, if you do:
cmake -DJPEG_LIBRARY_RELEASE=/usr/lib/x86_64-linux-gnu/libjpeg.a .....
Then you should get a static build (replace with JPEG_LIBRARY_DEBUG to define the debug version). You can do the same with pretty much any detected thing where you want to force the detection of something else, as most script will not overwrite values defined on the command line.
If that suits your workflow, perfect. Otherwise, you won't have a clean solution with CMake alone, and you should add a dependency manager on top of that (vcpkg, conan.io).
You can indicate the name:
find_library(JPEGLIB REQUIRED NAMES libjpeg.a)
add_executable(myapp myapp.c)
target_link_libraries(myapp PRIVATE ${JPEGLIB})
Each library name given to the NAMES option is first considered as a library file name and then considered with platform-specific prefixes (e.g. lib) and suffixes (e.g. .so). Therefore one may specify library file names such as libfoo.a directly. This can be used to locate static libraries on UNIX-like systems.

How to let Cmake Install to link my executables to my target shared libraries [duplicate]

I have a question related to how to install a built executable program with cmake when it relies on some external libraries. Suppose my executable is abc, and it relies on two external libraries: lib1.so and lib2.so. The structure of the codes are as follows:
-.........
|----bin (lib1.so lib2.so)
|----include(lib1.h lib2.h)
|----src(main.cpp)
When the executable program is installed using the following cmake commands:
INSTALL(TARGETS ${Exe_Name}
RUNTIME DESTINATION Path to bin
LIBRARY DESTINATION Path to bin)
I expect that the executable program will be in the same directory with lib1.so and lib2.so. However, when I execute the built program in the installation folder, I met the following error:
error while loading shared libraries: lib1 can not open shared object file No such file or directory
If I use ldd to check the executable, I found lib1.so and lib2.so not found. After searching for possible solutions, I found if I call the executable in this way, then it worked:
LD_LIBRARY_PATH=./ ./my_program_run
Then my question is how I can let my executable program knows the locations of the shared libraries with cmake when it is installed? Thanks.
This is best solved this with the RPATH of the final executable. RPATH is a hardcoded search path for the executable itself, and allows the use of the string $ORIGIN, which expands to the location of the executable at runtime. See this reference: http://man7.org/linux/man-pages/man8/ld.so.8.html
CMake strips the rpath of a binary at installation time, to avoid the binary picking up libraries littered around your development tree. But it also provides a simple way to modify the installation rpath for exactly this reason. Here's the short answer:
IF(UNIX)
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH}:\$ORIGIN/../bin:\$ORIGIN")
ENDIF()
This particular example appends to the existing rpath, and adds . and ../bin to the search path, all relative to the location of the binary.
Some developers claim that adjusting the RPATH of the binary is not a good idea. In the ideal world, all the libraries would live in the system library directories. But if you take this to the extreme, you end up with Windows (at least the older ones), where c:\windows\system32 is full of junk that came from who knows where, and may or may not conflict with other software, etc. Using rpath and installing everything in one place seems like a great solution.
If the application is to be cleanly installed to a standard linux distribution, then you should either install the supporting shared libraries into a standard location (/usr/lib), or you should add the libraries location to the ld.so config, by create an /etc/ld.so.conf.d/myprogram.conf file containing the name of the directory the libraries are in.
If the installation is temporary or more ad-hoc, then a script to set the LD_LIBRARY_PATH is suitable.
The libraries are searched in the predefined locations which includes standard library paths configured with ld.so.conf and LD_LIBRARY_PATH. You can also try to compile your app with -rpath, but it is not recommended by some developers. I suggest you to create a wrapper script which will set LD_LIBRARY_PATH and run the real application like that:
"theapp" script:
#!/bin/sh
dir="`dirname \"$0\"`"
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}"$dir"
exec "$dir/theapp.real" # your real application
The application, script and libraries should be in the same location (under bin/).

How to link shared libraries in custom path directly without specifying RPATH?

I am on Ubuntu 16.04, and I am required to use an external library (MCR). It puts all of it's shared libraries inside the MATLAB/bin/glnxa64/ folder. I only need the libmx.so in there but there are libraries in there that has the exact same name as the ones in /usr/lib but they are actually different (because the file size is different), for example libtiff.so.5.
This becomes a problem because when I use the find_library function in CMake to add MATLAB/bin/glnxa64/ into RPATH in order to link to libmx.so for my application, since my application also depends on another external library (OpenCV) that was linked to the libtiff.so.5 in /usr/lib when it was built, when I compile my application it shows an compilation error of
/usr/lib/libopencv_imgcodecs.so.3.3.0: undefined reference to
`TIFFReadDirectory#LIBTIFF_4.0'
It is because my application is trying to link to the libtiff.so.5 in MATLAB/bin/glnxa64/ instead of /usr/lib because RPATH has a higher priority than the default directories. What is the best way to solve this?
I tried renaming the libtiff.so.5 in MATLAB/bin/glnxa64/ to something like libtiff_old.so.5. This solves it but is very ugly.
Is there anyway I can alternate the search priority so RPATH goes after the default directories? Or is there something I can do in CMake so my application can directly link to libmx.so without having to add MATLAB/bin/glnxa64/ into RPATH to mess things up?
Well, first it would be good to see your cmake file, but alas..
You can try using this, the <...> is your base directory for the MATLAB install, which might be /usr/local/ or /opt.
find_library (MATLAB_RUNTIME libmx
PATHS <...>/MATLAB/bin/glnxa64/
NO_DEFAULT_PATH )

Including Shared Libraries (.so) with CMake

I've been trying to include different types of libraries with CMake.
.a
.dylib
.so
I finally, got both the .a and .dylib to work with this code.
find_library(libname NAMES libcef.dylib PATHS ${libname_PATH})
along with this, underneath where I add_executable to initialize all my files for the build.
target_link_libraries(${PROJECT_NAME} ${libname})
However, I tried using the same code on a .so file and it doesn't seem to work.
I get this statement from cmake when I try building.
Target "project name" links to item
-- path of file --
which is a full-path but not a valid library file name.
I'm not sure if this is the correct way to handle .so files or perhaps I'm not even fully understanding what an .so file is. Any input and/or clarification would be much appreciated.
edit:
THEORY- my theory is because it doesn't have a lib in front of the name of the library name its called ffmpegsumo.so. However, when i try renaming it the file name still saves into the variable name very strange.
The same should work with .so files also, just make sure the required .so file is present at ${libname_PATH} which you have given.
find_library treats all types (.a / .so/ .dylib/ .dll) the same way. Problem may be the following
-- path not set up correctly
-- error because of absolute path
-- .so not present
-- If the error is from build (not from configure only) the .so might be corrupt, try replacing it
--Your library does not seem to be valid
Shared libraries are linked dynamically. That means that your OS will automatically look for and load the .so files when the time comes to run the application. You only need to tell cmake the name of the library and the OS will take care of the rest.
For example, if you want to link to the dynamic library for libSDL.so, you just say: target_link_libraries(${PROJECT_NAME} SDL)
As a sanity check, your linker will look to make sure that the SDL library does exist on your computer. That's why you might get a linking error if that library is not available at link-time, even if it's really a dynamic library.

Error loading shared libraries of boost

I am working on centos. I installed boost version 1.45.0 on my system. The programs are compiled correctly but whenever I type command to see output it gives following error:
./a.out: error while loading shared libraries:
libboost_thread.so.1.45.0: cannot open shared object file: No such
file or directory
In addition to the other answers, you can also set the DT_RPATH elf tag when linking your executable
-Wl,-rpath,/path/to/boost/libraries -L /path/to/boost/libraries -lboost_whatever
This way you don't have to remember to set your LD_LIBRARY_PATH if the libraries are installed in a non-standard location.
How did you install the boost libraries?
The problem you're likely having is that the linker can not find the libraries, and when you built your program, you had to manually specify additional library paths to search for libraries.
A quick fix you can do is to set LD_LIBRARY_PATH to include the directory where the boost thread library is:
export LD_LIBRARY_PATH=/path/to/boost/libs:$LD_LIBRARY_PATH
./runExecutable
You need to set the LD_LIBRARY_PATH environment variable to include the path to the Boost libraries (they're possibly in /usr/local/lib, etc).
In bash, this is simply
export LD_LIBRARY_PATH=/path/to/boost:$LD_LIBRARY_PATH