How to add FindPackage() script for libraries not already supported by cmake.steemCreated with Sketch.

in #cmake7 months ago (edited)

Sometimes a developer wants to make sure compilation of a program doesn't fail even if user hasn't installed all the dependencies... With cmake it is easy to add optional packages that will only be built if the dependency hasn't been installed.

As an example, I will use libqrencode, which uses header qrencode.h and shared library libqrencode.so.

In your CMakeLists.txt:

set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

find_package(QRencode)

if(QRENCODE_FOUND)
  include_directories(SYSTEM ${QRENCODE_INCLUDE_DIRS})
else()
  include_directories(libqrencode)
  set(WITH_TESTS NO)
  set(WITH_TOOLS NO)
  add_subdirectory(libqrencode)
endif()

CMAKE_MODULE_PATH tells cmake where to find custom cmake modules. find_package() loads FindQRencode.cmake from directory specified in CMAKE_MODULE_PATH.
QRENCODE_FOUND is set if FindQRencode.cmake finds libqrencode. If it is not found, the else() block will add embedded libqrencode sources from directory libqrencode under CMAKE_CURRENT_SOURCE_DIR. Any unnecessary build steps can be disabled before calling add_subdirectory(). In this example we disabled building "tests" and "tools".

In cmake/FindQRencode.cmake:

find_path(QRENCODE_ROOT_DIR
    NAMES include/qrencode.h
)

find_library(QRENCODE_LIBRARIES
    NAMES qrencode
    HINTS ${QRENCODE_ROOT_DIR}/lib
)

find_path(QRENCODE_INCLUDE_DIRS
    NAMES qrencode.h
    HINTS ${QRENCODE_ROOT_DIR}/include
)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(QRencode DEFAULT_MSG
    QRENCODE_LIBRARIES
    QRENCODE_INCLUDE_DIRS
)

mark_as_advanced(
    QRENCODE_ROOT_DIR
    QRENCODE_LIBRARIES
    QRENCODE_INCLUDE_DIRS
)

QRENCODE_ROOT is used to find both header and library. It can be specified in cmake command line if libqrencode is installed in non-standard location.

QRENCODE_LIBRARIES contains full path of qrencode library (libqrencode.so.3 or libqrencode.so.4 on Linux).

QRENCODE_INCLUDE_DIRS contains path for the header qrencode.h.

"QRencode" is the package name, as seen in find_package() and find_package_handle_standard_args().
"qrencode" is base name of the library... On Linux and most Unix-like systems it is usually prepended with "lib" and appended with ".so" (shared library) or ".a" (static library). Shared library name can also contain version number (.3 or .4 for libqrencode).

find_package_handle_standard_args() sets QRENCODE_FOUND variable if the specified variables have been set.

mark_as_advanced() will hide the variable names unless user explicitly asks to show "advanced" variables.