CMake:How To Find Libraries: Difference between revisions

From KitwarePublic
Jump to navigationJump to search
mNo edit summary
Line 122: Line 122:
By default, all variables are non-cached.
By default, all variables are non-cached.


In order to avoid doing all the library detection again on every run, and more importantly to allow the user to set include dirs and libraries in ccmake, these will have to be cached. Fortunately, this is already taken care of by find_path and find_library, which will cache their variable. If the variable is already set to a valid value (e.g. not -NOTFOUND or undefined), these functions will do nothing, but keep the old value. Similarly, pkg_check_modules does internal caching of the results, so that it does not need to call pkg-config again every time (the result variable itself is not cached, though).
In order to avoid doing all the library detection again on every run, and more importantly to allow the user to set include dirs and libraries in ccmake, these will have to be cached. Fortunately, this is already taken care of by find_path and find_library, which will cache their variables. If the variable is already set to a valid value (e.g. not -NOTFOUND or undefined), these functions will do nothing, but keep the old value. Similarly, pkg_check_modules does internal caching of the results, so that it does not need to call pkg-config again every time.


On the other hand, the output values of the find module (<name>_FOUND, <name>_INCLUDE_DIRS and <name>_LIBRARIES) should not be cached because then modifying the other cached variables would no longer cause the actual output to change and this obviously is not a desired operation.
On the other hand, the output values of the find module (<name>_FOUND, <name>_INCLUDE_DIRS and <name>_LIBRARIES) should not be cached because then modifying the other cached variables would no longer cause the actual output to change and this obviously is not a desired operation.

Revision as of 06:48, 27 October 2008

This article briefly discusses how to use external libraries in a CMake project and then moves on to how to write your own find modules for libraries that don't already have one.

Using external libraries in your project

In CMakeLists.txt, write

find_package(LibXML++ REQUIRED)
include_directories(${LibXML++_INCLUDE_DIRS})
set(LIBS ${LIBS} ${LibXML++_LIBRARIES})

Then, after detecting all the libraries, for your target:

target_link_libraries(exampleProgram ${LIBS})

For this to work, you'll need a file called FindLibXML++.cmake in your CMake module path. Since CMake (currently) doesn't ship one, you'll have to ship it within your project. Create a folder named cmake/Modules/ under your project root, and in the root CMakeLists.txt, include the following code:

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")

As you may have guessed, you need to put the CMake modules that you use, and that CMake has to find automatically, inside that folder.

That's it, usually. Some libraries might require something else, so be sure to look inside that FindSomething.cmake file to see the documentation for that specific library.

How package finding works

The find_package command will look in the module path for Find<name>.cmake, which is the typical way for finding libraries, and the more advanced one of the two. If no such file is found, it looks for <name>Config.cmake or <lower-case-name>-config.cmake, which are supposed to be installed by libraries (but no libraries currently seem to install them) and that don't do detection, but rather just contain hardcoded values for the installed library. The former is called module mode and the latter is called config mode. Creation of config mode files is not (currently) discussed in this article, but you can find information about it in the CMake book and probably in other resources as well. For the module system there seems to be no documentation elsewhere, so this article concentrates on it.

No matter which mode is used, the script should define a set of variables, most importantly <name>_INCLUDE_DIRS and <name>_LIBRARIES which contain information on required include path and libraries to link.

The "REQUIRED" and other optional find_package arguments are forwarded to the module by find_package and the module should affect its operation based on them.

Writing find modules

First of all, notice that the name, or prefix, supplied to find_package is part of the filename and the prefix used for all variables. The case does matter and the names should match exactly. Unfortunately in many cases, even in the modules shipped with CMake, the names do not match, causing various issues. The bundled modules are in general quite buggy in other ways as well (e.g. not obeying the REQUIRED argument), so you should take care before relying on them.

The basic operation of a module should roughly follow this order

  • Use find_package to detect other libraries that the library depends on
    • The arguments QUIETLY and REQUIRED should be forwarded (e.g. if current package was REQUIRED, the depencency should also be)
  • Use pkg-config to detect include/library paths (if pkg-config is available)
  • Use find_path and find_library to look for the header and library files, respectively
    • Paths supplied by pkg-config are used only as hints on where to look
    • CMake has many other hardcoded locations where it looks, too
    • Results should be saved in variables <name>_INCLUDE_DIR and <name>_LIBRARY (note: singular, not plural)
  • If all reqested elements were found <name>_FOUND should be set to TRUE
  • Set <name>_INCLUDE_DIRS to <name>_INCLUDE_DIR <dependency1>_INCLUDE_DIRS ...
  • Set <name>_LIBRARIES to <name>_LIBRARY <dependency1>_LIBRARIES ...
    • Dependencies use plural forms, the package itself uses the singular forms defined by find_path and find_library
  • Print "Found <name>" status message if found or send a fatal error if the package was required and not found

As you can imagine, a script doing all this would be rather long, and when you have dozens of those scripts, for different libraries, it is also a maintenance nightmare. This is why LibFindMacros.cmake was written. It contains various libfind macros taking care of the boring parts that are always the same for every library. With it, the scripts look like this:

# - Try to find ImageMagick++
# Once done, this will define
#
#  Magick++_FOUND - system has Magick++
#  Magick++_INCLUDE_DIRS - the Magick++ include directories
#  Magick++_LIBRARIES - link these to use Magick++

include(LibFindMacros)

# Dependencies
libfind_package(Magick++ Magick)

# Use pkg-config to get hints about paths
libfind_pkg_check_modules(Magick++_PKGCONF ImageMagick++)

# Include dir
find_path(Magick++_INCLUDE_DIR
  NAMES Magick++.h
  PATHS ${Magick++_PKGCONF_INCLUDE_DIRS}
)

# Finally the library itself
find_library(Magick++_LIBRARY
  NAMES Magick++
  PATHS ${Magick++_PKGCONF_LIBRARY_DIRS}
)

# Set the include dir variables and the libraries and let libfind_process do the rest.
# NOTE: Singular variables for this library, plural for libraries this this lib depends on.
set(Magick++_PROCESS_INCLUDES Magick++_INCLUDE_DIR Magick_INCLUDE_DIRS)
set(Magick++_PROCESS_LIBS Magick++_LIBRARY Magick_LIBRARIES)
libfind_process(Magick++)

In the first line, LibFindMacros is included. For this to work, the LibFindMacros.cmake file must be placed in the module path, as it is not currently shipped in the CMake distribution.

Dependencies (optional)

libfind_package functions similarly to find_package, except that it forwards the QUIETLY and REQUIRED arguments. For this to work, the first parameter supplied is the name of the current package. I.e. here Magick++ depends on Magick. Other arguments such as version could be added after Magick and they'd just be forwarded to the CMake internal find_package command. Have one of these lines for every library that your library depends on, and be sure to supply find modules for them as well.

Pkg-config (optional)

You should not just call pkg-config and return whatever it returned even when it is available. Instead, we'll let CMake do the detection, as it would without pkg-config, but with additional hints on where to look. One big reason for this is that this way the user can override or help library detection by manually defining the paths using ccmake, as is conventional with CMake. There are also cases when pkg-config just supplies incorrect information (wrong compiler, etc).

libfind_pkg_check_modules is a convenience wrapper for CMake's own pkg-config modules, with the intention of making things easier. You don't need to test for CMake version, load the appropriate module, check if it loaded, etc. when all you really want to do is a simple optional check. The arguments are the same as for pkg_check_modules: first the prefix for returned variables, then package name (as it is known by pkg-config). This defines <prefix>_INCLUDE_DIRS and other such variables.

Finding files

After that the actual detection takes place. Supply a variable name as the first argument for both find_path and find_library. If you need multiple include paths, use find_path multiple times with different variable names. The same goes for find_library.

  • NAMES specifies one or more names for the target and if any matches, that one is chosen. In find_path you should use the main header or whatever is #included in the C/C++ code. This may also contain a folder, e.g. alsa/asound.h and then it will give the parent directory of the folder that asound.h is in as result.
  • PATHS is used to provide additional search paths for CMake to look into and it should not generally be defined for things other than pkg-config (CMake has its built-in defaults and more can be added as required by various config variables). If you are not using it, leave out the entire section.
  • PATH_SUFFIXES (not present in this example) is useful for libraries that on some systems put their files in paths like /usr/include/ExampleLibrary-1.23/ExampleLibrary/main.h; in this case you'd use NAMES ExampleLibrary/main.h PATH_SUFFIXES ExampleLibrary-1.23. Multiple suffixes may be specified and CMake will try them all, and also no suffix at all, in all include directories, and in the bare prefix as well.

The library names do not contain the lib prefix used on UNIX system, nor any file extension or compiler specifications, as CMake will platform-independently detect them. The library version number is still needed, if present in the body part of the library file name.

Final processing

Three items done, four to go. Fortunately, those last ones are rather mechanical and can be taken care of by the libfind_process macro and the last three lines of the example file. You will need to set <name>_PROCESS_INCLUDES with the names of all variables to be included in <name>_INCLUDE_DIRS, and <name>_PROCESS_LIBS with the names of all variables to be included in <name>_LIBRARIES. Then call libfind_process(<NAME>) and it'll do the rest.

Performance and caching

The CMake variable system is far more complex than it may seem first. Some variables are cached and some are not. The cached variables may be cached as internal (not possible to edit with ccmake) or as external (have a type and a documentation string and can be modified in ccmake). Further the external variables may be set advanced, so that they'll only be seen in the advanced mode of ccmake.

By default, all variables are non-cached.

In order to avoid doing all the library detection again on every run, and more importantly to allow the user to set include dirs and libraries in ccmake, these will have to be cached. Fortunately, this is already taken care of by find_path and find_library, which will cache their variables. If the variable is already set to a valid value (e.g. not -NOTFOUND or undefined), these functions will do nothing, but keep the old value. Similarly, pkg_check_modules does internal caching of the results, so that it does not need to call pkg-config again every time.

On the other hand, the output values of the find module (<name>_FOUND, <name>_INCLUDE_DIRS and <name>_LIBRARIES) should not be cached because then modifying the other cached variables would no longer cause the actual output to change and this obviously is not a desired operation.

Common bugs in find modules

  • Different case or slightly different name in filename and in variables
  • The module does not check <name>_FIND_REQUIRED or <name>_FIND_QUIETLY - and thus the find_package arguments QUIET and REQUIRED will have no effect
  • <name>_INCLUDE_DIRS and <name>_LIBRARIES are not set, but instead only the singular forms are available
  • No pkg-config is used (none of the modules shipped with CMake use pkg-config)

Links