In CMake, we use TARGET_LINK_LIBRARIES() to link a shared library to an library/executable.
For example:
TARGET_LINK_LIBRARIES(ExecutableName xxx)
where ExecutableName - is the name of executable
xxx - is the library name.
As of my understanding CMake searches for "libxxx.so" at the paths mentioned in LINK_DIRECTORIES() macro.
But if I have a third party library with name "libxxx.a" then how do I link the library to the executable using CMake.
Thank you for your help in advance!
You should always try to give either full paths or CMake targets to target_link_libraries.
Since you do not seem to build the dependency as part of the CMake project, the only way to obtain a CMake target to link against, is to create an imported target. This is usually quite tedious to do manuall, so unless the dependency already provides a config file with an imported target, you probably do not want to go down that road. Imported targets are the most convenient to use, but only if you can get CMake to write the for you.
So, absolute paths it is then. You obviously would not want to hardcode absolute library paths in your CMakeLists. As pointed out in your question, the desired behavior is that you specify just the name of a library and CMake should be able to figure out its location automatically. This is exactly what find_library does for you.
To link against a library xxx, you would do something like this:
find_library(LIB_LOCATION xxx)
target_link_libraries(ExecutableName ${LIB_LOCATION})
Note that find_library provides a plethora of options to further specify where to look for the requested library. Get rid of your existing link_directories call and add the respective paths as hints to find_library instead.
This approach is both more flexible when porting your CMake code to other platforms and more easy to debug if something goes wrong than your initial approach.
Just specifying the library filename should work:
TARGET_LINK_LIBRARIES(ExecutableName libxxx.a)
and
TARGET_LINK_LIBRARIES(ExecutableName xxx)
actually should work too as that would not look for the .so but for a libxxx.a file in the search paths.
Related
My motivation comes from this:
I want to find a library named libboost_python38.so, if I use find_library command like this, I cannot find the library:
find_library(USD_BOOST_PYTHON boost_python HINTS ${USD_LIBRARY_DIRECTORY})
Only when I use this command, the library can be found:
find_library(USD_BOOST_PYTHON boost_python38 HINTS ${USD_LIBRARY_DIRECTORY})
This method will make my cmake file lose some cross platform capabilities, so I hope to search this library by some fuzzy search method. Can I do this in a better way?
No particularly, but you can use the NAMES parameter, and pass a few candid names that find_library should look for, see here. You can also provide a few different PATH and PATH_SUFFIXES which could help.
If you still need more logic to find your library, then you most likely need to deal with them before your find_library call, e.g., conclude the name of your library beforehand, put it in a variable, and pass that as the name of the library to the find_library.
Don't use find_library for this purpose. Instead make use of the fact that boost comes with cmake configuration scripts and use find_package.
find_package(Boost REQUIRED COMPONENTS python)
target_link_libraries(my_target PRIVATE Boost::python)
This has the added benefit of any dependencies being added to my_target automatically alongside the necessary include directories.
If you've installed the package to the default location, no additional info should be required. If you've built boost yourself and installed it to a non-standard location, you may need to provide info about the install location, e.g. by adding the install location to CMAKE_PREFIX_PATH.
Note: find_package has additional parameters that allow you to restrict the acceptable boost version if desired.
I have this project structure:
and I'm trying to set CMakeList.txt files properly. My aim is simply linking the library to main.cpp. So I have these CMakeList.txt files:
library/CMakeList.txt:
source/CMakeList.txt:
root:
When I try to build the project, I get this error:
ld: library not found for -lMyLib
Where is the mistake, and how should I properly organize structure for such projects?
You used find_library yet you did not use ${MyLib} variable. Consult find_library documentation.
You used link_directories, yet it's local to the directory it was used in. Upper directories are unaffected.
Use an IMPORTED library or an INTERFACE library to link with .a file. See https://stackoverflow.com/a/10550334/9072753 and https://stackoverflow.com/a/41909627/9072753 answers. If it's one library only, I think prefer IMPORTED. Move target_include_directories(Source ...) to library/CMakeLists.txt and instead of Source add include directories to your library, it's conceptually the library include directory.
I recommend, subjective, depens: move target_include_directories(Source.. inside source/CMakeList.txt. I.e. keep all Source related stuff inside source/CMakeLists.txt. And maybe add EXCLUDE_FROM_ALL to add_subdirectory(library).
Use set_target_properties instead of set(CMAKE_CXX_STANDARD. Overall, prefer to solely use *target* specific stuff instead of set(...).
I noticed a strange behavior when trying to change some existing code. What I noticed is that we call target_link_libraries() and this finds a Qt5 library with no issues, but calling find_library() doesn't find the same library. Here's the code I have:
list(APPEND CMAKE_PREFIX_PATH ${QT_ROOT})
find_package(Qt5Widgets REQUIRED)
target_link_libraries(projectName Qt5::Widgets)
find_library(QTWIDGETSFOUND NAMES Qt5::Widgets)
message(STATUS ${QTWIDGETSFOUND})
The result of this is:
QTWIDGETSFOUND-NOTFOUND
However, if I look into a generated file, link.txt, I can find this line:
/home/user/Documents/work/test/build/venv/.conan/data/Qt/5.11.3/org/dev/package/80043e232e8ab07f4b25e67652a9490d9ad33d91/lib/libQt5Widgets.so.5.11.3
I conclude that target_link_libraries() can find the Qt5:Widgets library, but find_library() cannot. I find this behavior quite strange.
My use case is that I want to target my library, which I do just fine, but then get the path to the library it found and copy that library file somewhere. I can't find a way to get a path from target_link_libraries(), so I figured that I would just call find_library() after to get the path of that library and copy it from there. The only possible answer I see to my problem is that Qt5:Widgets isn't the actual name of the library, but that target_link_libraries() can resolve it from the find_package() statement I made above and that find_library() isn't able to for some reason.
So, here are my questions:
Why is the piece of code I porived not doing what I thought it would do?
How can I make find_library() work in the provided code sample?
If no one can find a solution for the above, what other ways could I get a path to a library I just linked with target_link_libraries() other than find_library() that would be simple?
Thanks!
I have a library (namely CGAL). It provides a FindMODULE.cmake file for a third-party library not shipped with it CGAL (namely Intel TBB). Unfortunately, this file has a bug that I need to fix. (The bug seems to be related to incompatible directory structures, but that's not the point here.)
So the CMakeLists.txt of my project has a line:
find_package( TBB )
This will invoke FindTBB.cmake which is provided in the directory structure of CGAL.
Now, I need to fix a bug in FindTBB.cmake. I'd like to just copy that file and put the fixed version directly into my project directory.
How can I tell CMake to use FindTBB.cmake in my project directory (instead of the one provided by CGAL) when calling find_package?
Alternative approach:
Copy the file to your module directory and modify it.
Call find_package(TBB) before you do anything related to CGAL.
Use the CMake call used to find / interact with CGAL. If you are lucky, the results of all checks are cached and it work. It still might not work, depending on what is actually done.
The find_package function might be too heavy in this case, you can try just including your relocated .cmake file like this.
include(<local path>/FindTBB.cmake)
I have a specific question which serves as context for a more general question.
There is a scientific package called LAMMPS, and it is usually used as an executable. However, it supports use as a "library". To try to do things right, I put it in /usr/local/lib/lammps. It contains a lammps/src/ directory, which has around 40 source files. Using the instructions provided, I compiled lammps as a .so file in lammps/src/liblammps_serial.so.
I also have separate code in "~/code/ljtube/". This uses cmake to try to find the library. Thus, I wrote a FindLAMMPS.txt so that I could use
FIND_PACKAGE (lammps)
in my CMakeLists. I modified the libtool config file to search in /usr/local/ successfully. I found that it searches in /usr/local/lib/ for a .so file and in /usr/local/include/ for a .h file. So I made a dynamic link to the .so file in /usr/local/lib/, and I copied the .h file from the lammps/src/ to /usr/local/include/.
CMake can now find those two files, but it cannot link to anything else in lammps/src/. It seems absurd to need to make a separate FIND_PACKAGE for each of the .h's I want to include (group.h, fix.h, force.h, pair.h, etc.). It also seems ridiculous to dump the whole package of .h files into the /usr/local/include/ directory. I will be using this code both locally and on a cluster, and possibly distributing it to other group members.
How can I make CMake find what I want to find without hard coding in the location of /usr/local/lib/lammps/src/? Phrased more generically, how should I manage large packages like these to make them easy to link to in the code I write, even if the original developer did not use the best conventions?
(As a side note, I am using a shared library because it seems like the right choice, but I'm not especially married to it. Should I be using a static library? Is there a way for CMake to find an already-compiled library relative to the current source directory, and might that be a better way to implement this? I know that I will be using LAMMPS in multiple projects, so having a local shared copy superficially seems to make the most sense.)
Normally a find_package call yields a variable specifying the path to the "includes" folder of the package. This would then be added in the caller's CMakeLists.txt via include_directories.
For example, to use find_package for boost, you could do:
find_package(Boost) # sets ${Boost_INCLUDE_DIRS} and ${Boost_LIBRARIES}
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(foo foo.cc)
target_link_libraries(foo ${Boost_LIBRARIES})
endif()
Regarding your side note, you could use find_library and/or find_path to find the library and its headers given a known location.
Both these commands can be invoked in such a way as to avoid searching in common locations, e.g. by setting PATHS to the known location and using NO_DEFAULT_PATH in the find commands.
Another alternative is for your projects to make use of the ExternalProject_Add function which is described in more detail in this article. From this article:
The ExternalProject_Add function makes it possible to say “download this project from the internet, run its configure step, build it and install it”
A downside to this approach is that each of your projects would end up with its own copy of the third party sources and lib.