How to tell CMake NOT to search system paths for libraries - cmake

I have a project that uses several different open source libraries (FFmpeg, OpenSSL, for example).
No matter what I try, CMake is linking against the SYSTEM installed versions of these libraries, rather than the custom built ones I need for my projects.
Here is an example of what I've tried to add the FFmpeg library 'libswresample':
set(FFMPEG_PATH "/shared/dev/libs/ffmpeg/3.4.2")
find_library(LIBSWRESAMPLE_LIB NAMES swresample
PATHS ${FFMPEG_PATH}/lib/darwin-amd64
NO_DEFAULT_PATH
NO_CMAKE_ENVIRONMENT PATH
NO_CMAKE_PATH
NO_SYSTEM_ENVIRONMENT_PATH
NO_CMAKE_SYSTEM_PATH
NO_CMAKE_FIND_ROOT_PATH)
list(APPEND includes "${FFMPEG_PATH}/include")
link_directories("${FFMPEG_PATH}/lib/${ARCH}")
list(APPEND libs ${LIBSWRESAMPLE_LIB})
MESSAGE(STATUS "libs: ${libs}")
I've tried setting CMAKE_PREFIX_PATH to the location of my shared libraries - no luck.
I've tried this with various combinations of the "NO_SOMETHING_PATH" options, which doesn't seem to help. I also tried just giving ${FFMPEG_PATH} to the PATHS parameter of find_library() and providing PATH_SUFFIXES lib ${ARCH} (which would be ideal, since I build this project for multiple platforms), but that didn't work either.
No matter what I've tried, MESSAGE outputs libs: /usr/local/lib/libswresample.dylib, rather than libs: /shared/dev/libs/ffmpeg/3.4.2/lib/darwin-amd64/libswresample.dylib
I've found several FindFFMpeg modules, but they all are pretty much doing what I'm trying here and wind up finding the system installed FFmpeg libraries rather than the one I actually want to link with.
If I explicitly provide the absolute path to the library I can get it to work, but this is obviously not optimal since some platforms use static libraries, some use shared libs, and so on. If I were to go that route, I'd have to additional work to figure out which platform I'm building for, and that doesn't seem like the right way to go about it anyways.
I know I must be missing something simple. Can anyone point me in the right direction?

The code works, but you should clear CMake cache (remove CMakeCache.txt file from build directory) for re-search the library.
Option NO_DEFAULT_PATH implies all other NO_* options, so you may omit them.

Related

How to use find_package in CMake? (Example: GMP library)

I'm trying to use find_package to include libraries in CMake.
This question talks about how to tell CMake to link to the GMP library (external). I am trying to follow the steps of the answer there but do not have any of the <name>Config.cmake or <name>-config.cmake files, as mentioned by some of the comments, which appears to be the default. The answer does not mention any solution for when you don't know how to get/find these files. The comments to that answer link to an old website (external) with a lot of broken links, that describes a list of Load Modules. It's unclear to me where these modules come from and how to get them.
According to the official CMake documentation (external), if the configuration files are not found, find_package falls back from "Module Mode" to "Config Mode". I don't understand what this means and in what cases this would be relevant, especially since the documentation discourages reading about "Config Mode".
The documentation says that
The file is first searched in the CMAKE_MODULE_PATH, then among the Find Modules provided by the CMake installation.
I am still confused about whether these configuration files are supposed to come with CMake or with the library in question and where they are supposed to be located. Probably both are possible but how does one know in a specific case?
Example code, trying to follow modern best practices:
# CMakeLists.txt (not working)
cmake_minimum_required(VERSION 3.2) # I have no idea what version I actually need
project (GMP_demo_project)
# Enable C++17 standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(GMP REQUIRED)
# Create the executable from sources
add_executable(GMP_demo GMP_demo.cpp)
target_link_libraries(GMP_demo gmp gmpxx)
The code outputs an error message along the lines of
CMake Error at CMakeLists.txt:10 (find_package):
By not providing "FindGMP.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "GMP", but
CMake did not find one.
Could not find a package configuration file provided by "GMP" with any of
the following names:
GMPConfig.cmake
gmp-config.cmake
Add the installation prefix of "GMP" to CMAKE_PREFIX_PATH or set "GMP_DIR"
to a directory containing one of the above files. If "GMP" provides a
separate development package or SDK, be sure it has been installed.
Question: How does one, in general, obtain and organize these configuration files (CMake Load Modules)? How can one expect another user to have these files on his system? My question is intended to be general and only use GMP as an example (although I am in fact interested in being able to use it).
Just as an aside, I can compile, link and execute my demo code just fine using gcc GMP_demo.cpp -lstdc++ -lgmp after having installed GMP as suggested by the library documentation. The problem is just getting CMake to do it. I can also just give CMake the absolute path of the library, which would of course be much easier but not portable (assuming one can get find_package to actually work and be portable with reasonable amounts of work).
How does one, in general, obtain and organize these configuration files (CMake Load Modules)?
Broadly speaking, there are three buckets these fall into:
Files provided directly by the package. This is the ideal solution, and would be what CMake calls Config mode. There would be a file called GMPConfig.cmake which cmake could find by searching preconfigured paths, or by providing a specific path at configuration time (cmake -DGMP_Dir=/path/to/GMP/install/root). The advantages of this approach are that generation of GMPConfig.cmake is mostly automatic, and the libraries can include things like installation paths and compilation flags. The disadvantage is that the library develops have to actually go to the effort of leveraging modern CMake, and not everybody does this.
Files provided directly by CMake. For common packages (e.g., boost) CMake ships FindXXX.cmake files that search well-known paths and take care of this for you. These work identically to the above from an end-user perspective, but which Find modules are available depends on the version of CMake you have installed.
Files provided by some random person that are copy/pasted into projects. How these works depends on the person who wrote it, so you'll have to read their documentation. Use your favorite search engine and try to find FindGMP.cmake, then drop it in a module folder somewhere and update CMAKE_MODULE_PATH appropriately.
How can one expect another user to have these files on his system?
It's your job to install whatever dependencies a package requires. Anything using modern CMake (bullet 1 listed above) should install the XXXConfig.cmake file as part of its installation. If a library is built by something other than CMake, you'd have to either hope for bullet #2, or find/write your own FindXXX.cmake file (bullet #3).
For your specific case, you might be better off with find_library, since your sample compilation line looks like it just needs to link.

CMake Find library issues if "lib" is a directory in the path

I have found a workaround for this problem, but I don't know why it works:
I am creating a CMake toolchain for a platform with many libs provided by COMPANY in a few locations.
Note: SDK_PATH is passed as an environment variable.
As an example, the provided C++ lib is at ${SDK_PATH}/COMPANY/stl/libc++-COMPANY/lib/libc++.so and another lib is at ${SDK_PATH}/lib/COMPANY/libCOMPANY_ext_logging.so
For the C++ lib, I can find it as expected with:
find_library(CPP
NAMES
c++
HINTS
${SDK_PATH}/COMPANY/stl/libc++-COMPANY/lib
NO_DEFAULT_PATH
)
However, I can not find the logging library with:
find_library(LOGGING
NAMES
COMPANY_ext_logging
HINTS
${SDK_PATH}/lib/COMPANY
NO_DEFAULT_PATH
)
However, I have found that if I copy the problematic libraries into a further subdirectory "lib" then things work (I guess that is a keyword CMake looks for somehow)
IE if I copy the logging lib to ${SDK_PATH}/lib/COMPANY/lib/libCOMPANY_ext_logging.so and alter the find command to be:
find_library(LOGGING
NAMES
COMPANY_ext_logging
HINTS
${SDK_PATH}/lib/COMPANY/lib
NO_DEFAULT_PATH
)
then things work fine. Which makes little sense to me.
I believe I had this working without the workaround about a year ago, but am unsure (I ran into this while updating both my CMake and the version of the company libraries)
Is there better CMake syntax I could use to avoid needing the workaround and still find libraries at the path ${SDK_PATH}/lib/COMPANY ?
Note2: I have tried using "Paths" rather than "Hints" with no change in results (The successful finds still work, and the failing ones still fail)

How to predict CMake's choice of LIBDIR on a given platform

In my application suite I build Libtiff from source and then link it to an application that I write myself. Libtiff's CMakeLists.txt files specify that the static libraries go into the library location CMAKE_INSTALL_FULL_LIBDIR, determined by CMake's GNUInstallDirs option.
When I first built and tested my application, I did so in Debian and on this platform CMAKE_INSTALL_FULL_LIBDIR gets set to ${CMAKE_INSTALL_PREFIX}/lib. Well and good, so in my own application's CMakeLists.txt file I told it to search for the the static tiff libraries there. No problem...
Now I've taken the same build suite to a Red Hat Linux platform (Pengwin Enterprise for WSL), and it turns out that here CMAKE_INSTALL_FULL_LIBDIR gets set to ${CMAKE_INSTALL_PREFIX}/lib64. I've checked the CMake documentation and it seems to say that in fact the choice of 'lib' or 'lib64' is determined automatically and is platform-dependent.
So, in my own application's CMakeLists.txt file, is there a way of finding out which it is on my current platform? How else am I supposed to guess where to look for the library? I've looked round but I can't find a CMAKE standard variable that holds the platform-dependent string, so the only things I can think of are:
Try 'lib', and if that doesn't work try 'lib64', or
Create a new CMAKE_INSTALL_FULL_LIBDIR in my own CMakeLists.txt file and read the end of it
...but both these seem clunkily inelegant and there surely must be a better way.
GNUInstallDirs module documents the CMAKE_INSTALL_LIBDIR variable which sounds like it contains the value you want.
CMAKE_INSTALL_LIBDIR = lib
CMAKE_INSTALL_FULL_LIBDIR = /lib
Also find_library should also be searching lib and lib64 as necessary in the default path search even when using CMAKE_LIBRARY_PATH to define a custom prefix.

How to find RelWithDebInfo or MinSizeRel libraries with CMake?

I'm trying to link my project to a external library that I also developed in which also also use CMake to build. When I try to find RelWithDebInfo or MinSizeRel like this:
FIND_LIBRARY(PCM_LIBRARY_DEBUG pcm
PATHS #CMAKE_LIBRARY_OUTPUT_DIRECTORY#
#CMAKE_LIBRARY_OUTPUT_DIRECTORY#/Debug
NO_DEFAULT_PATH
)
FIND_LIBRARY(PCM_LIBRARY_RELEASE pcm
PATHS #CMAKE_LIBRARY_OUTPUT_DIRECTORY#
#CMAKE_LIBRARY_OUTPUT_DIRECTORY#/Release
#CMAKE_LIBRARY_OUTPUT_DIRECTORY#/MinSizeRel
#CMAKE_LIBRARY_OUTPUT_DIRECTORY#/RelWithDebInfo
NO_DEFAULT_PATH
)
SET(PCM_LIBRARIES debug ${PCM_LIBRARY_DEBUG} optimized ${PCM_LIBRARY_RELEASE})
It does not search in ather directories that are not Release or Debug. I also tried creating PCM_LIBRARY_RELWITHDEBINFO and PCM_LIBRARY_MINSIZEREL but the same thing happens because there is only debug and optimized prefixes in SET. Anyone knows how can I link the correct libraries?
This is unfortunately one of the shortcomings of using find_library. There is no easy way around this without introducing tons of boilerplate code.
The problem here is that when passing files as dependencies to target_link_libraries, you can only distinguish between debug and optimized. If you need more fine-grained control, you will have to manipulate the respective target properties like LINK_INTERFACE_LIBRARIES directly. This is not only quite cumbersome, it also requires detailed knowledge about the inner workings of CMake's property system.
Fortunately, there is another way: The aforementioned limitation only applies when specifying dependencies via filenames. When specifying them as targets, this problem does not occur. The most obvious example is if a library and the executable that depends on it are built from the same source:
add_library(foo_lib some_files.cpp)
add_executable(bar_exe more_files.cpp)
target_link_libraries(bar_exe PUBLIC foo_lib)
This 'just works'. The correct library will be chosen for each build configuration. Things get a little more complicated if the library and the executable live in different independent projects. In that case the library has to provide a configure file with an exported target in addition to the binary files.
Instead of calling find_library to locate the binaries, the dependent executable now just loads that config file and can then use the imported target as if it was a target from the same project.
Many modern libraries already use this approach instead of the classical find_library technique (Qt5 is a prominent example). So if you are at liberty to change the CMakeLists of your dependency and you do not need to support very old CMake versions (<2.6), this is probably the way to go.

Package & library management & installation, and interface with 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.