CMake:How To Find Libraries: Difference between revisions

From KitwarePublic
Jump to navigationJump to search
Line 77: Line 77:


No matter which mode is used, if the package has been found, a set of variables will be defined:
No matter which mode is used, if the package has been found, a set of variables will be defined:
* <NAME>_FOUND - package has been found or not
* <NAME>_FOUND
* <NAME>_INCLUDE_DIRS or <NAME>_INCLUDES - the include directories to use
* <NAME>_INCLUDE_DIRS or <NAME>_INCLUDES
* <NAME>_LIBRARIES or <NAME>_LIBRARIES - the libraries to link against
* <NAME>_LIBRARIES or <NAME>_LIBRARIES or <NAME>_LIBS
* <NAME>_DEFINITIONS - definitions to use when compiling code
* <NAME>_DEFINITIONS
 
<NAME>_FOUND can be checked to see whether the package has been found or not.
For most packages the resulting variables use the name of the package all uppercased, e.g. LIBFOO_FOUND, for some packages the exact case of the package is used, e.g. LibFoo_FOUND.
These conventions are documented in the file readme.txt in the CMake module directory.


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.
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.

Revision as of 22:11, 4 February 2011

If your software uses external libraries (i.e. libraries not coming with your software), you don't know in advance where its headers and libraries will be located on the system where your software will be compiled. Depending on the location appropriate include directories and library search paths will have to be added to the compile commands.

CMake helps you with this by providing the find_package command.

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

Suppose you want to use the LibXML++ library. In CMakeLists.txt, write

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

If the package is optional, you can omit the REQUIRED keyword and query the boolean variable LibXML++_FOUND afterwards to see if it has been found. 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.


Packages with components

Some libraries are not monolithic, but come with one or more dependent libraries or components. A notable example for this is the Qt library, which ships (among others) with the components QtOpenGL and QtXml. To use both of these components, use the following the find_package command:

find_package(Qt COMPONENTS QtOpenGL QtXml REQUIRED)

Again, you can omit the REQUIRED keyword, if the package is optional for your project. In this case, you can use the <PACKAGE>_<COMPONENT>_FOUND variable (e.g. Qt_QtXml_FOUND) to check if a component has been found. The following invocations of find_package are all equivalent:

find_package(Qt COMPONENTS QtOpenGL QtXml REQUIRED)
find_package(Qt REQUIRED COMPONENTS QtOpenGL QtXml)
find_package(Qt REQUIRED QtOpenGL QtXml)

If you only require some components of a package, and want to use others only, if they are available, you can call find_package twice:

find_package(Qt COMPONENTS QtXml REQUIRED)
find_package(Qt COMPONENTS QtOpenGL)

Alternatively, you can invoke find_package once with all components, but without the REQUIRED keyword and then explicitly check the required components:

find_package(Qt COMPONENTS QtOpenGL QtXml)
if ( NOT Qt_FOUND OR NOT QtXml_FOUND )
  message(FATAL_ERROR "Package Qt and component QtXml required, but not found!")
endif( NOT Qt_FOUND OR NOT QtXml_FOUND )

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. First CMake checks all directories in ${CMAKE_MODULE_PATH}, then it looks in its own module directory <CMAKE_ROOT>/share/cmake-x.y/Modules/.

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 there are currently not yet many libraries which 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 documented here. You may also need the documentation for importing and exporting targets.

For the module system there seems to be no documentation elsewhere, so this article concentrates on it.

No matter which mode is used, if the package has been found, a set of variables will be defined:

  • <NAME>_FOUND
  • <NAME>_INCLUDE_DIRS or <NAME>_INCLUDES
  • <NAME>_LIBRARIES or <NAME>_LIBRARIES or <NAME>_LIBS
  • <NAME>_DEFINITIONS

<NAME>_FOUND can be checked to see whether the package has been found or not. For most packages the resulting variables use the name of the package all uppercased, e.g. LIBFOO_FOUND, for some packages the exact case of the package is used, e.g. LibFoo_FOUND. These conventions are documented in the file readme.txt in the CMake module directory.

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.

The library is considered FOUND only if all the provided variables have valid values.

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