Find a Source-Compiled Package in Your Project CMakeList

This is a step to step instructions on how to find a source-compiled third party in your project CMakeLists.txt.

The example will be used is the third party lib: Menge https://github.com/MengeCrowdSim/Menge

What’s the situation

  • I have a project that uses features in Menge, when compiling my package, I need to get the binary lib (.so, .a in Linux) and the header files (.h .hpp) of Menge.
  • I source compiled the Menge lib. Following the instructions, I git clone the repo and successfully compiled the package.

Now what

The compiled Menge package has following dirs tree:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
The MENGE_ROOT_DIR is at ~/thirdparty/Menge
Menge_ROOT_DIR
|--doc
|--examples
|--imgs_rsrc
|--Exe (generated after make)
|--projects
|--g++
|--build
|--lib (containes .so)
|--libmengeCore.so
|--libmengeVis.so
|--...
|--src
|--include (containes header)
|--Menge (containes header)
|--MengeCore
|--...
|--...

What I want to do is to tell cmake to find libmengeCore.so, libmengeVis.so in ${MENGE_LIBRARIES} and all of the head files in ${MENGE_INCLUDE_DIRS}.

Package Config

A good practice is to use find_package(Menge REQUIRED) in your CMakeLists.txt. find_package() will need to find a PackageConfig.cmake to load all the libs and dirs. After searching in the package, there is not config.cmake file generated. (There might be a way to auto generate a config.cmake file for cmake version after 2.8.8, check https://cmake.org/cmake/help/v3.4/module/CMakePackageConfigHelpers.html ).

Correction part:
There is a different between Find<PackageName>.cmake and <Package-name>Config.cmake or <Package-name>-config.cmake.

  1. A config-file is named as <Package-name>Config.cmake or <Package-name>-config.cmake is supposed to be generated by the package itself. These files are meant for “native” cmake packages whose own build system use cmake. (Findout more in the following Another way)
  2. A find-module is named as Find<PackageName>.cmake, this is used for finding non-cmake packages, or the CMakelists.txt in package does not include generating config-file part.

Important:

  1. If you are using Find<PackageName>.cmake, you should add the path to CMAKE_MODULE_PATH
  2. If you are using <PackageName>Config.cmake, you should add the path to CMAKE_PREFIX_PATH. Although if you are using a colcon workspace, the colcon will take care of CMAKE_PREFIX_PATH

So Now I need to generate a FindMenge.cmake for this package.

  1. Create FindMenge.cmake in MENGE_ROOT_DIR/cmake
  2. Fill it with:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # set the package root dir
    set( MENGE_PACKAGE_ROOT /headless/thirdpart_lib/Menge)

    # I need to find libmengeCore.so and libmengVis.so for MENGE_LIBRARIES
    # HINTS direct the right path
    find_library(MENGE_LIBRARIES
    NAMES mengeCore mengeVis
    HINTS "${MENGE_PACKAGE_ROOT}/projects/g++/build/lib")

    if(NOT MENGE_LIBRARIES)
    set(MENGE_LIBRARIES_FOUND FALSE)
    endif()

    # I need to include all the header files, so I manually set this var for all header dirs.
    # if you only want to find one header file like xxx.h, you can use
    # find_path(xxx_INCLUDE_DIRS xxx.h)
    set( MENGE_INCLUDE_DIRS "${MENGE_PACKAGE_ROOT}/src/Menge" "${MENGE_PACKAGE_ROOT}/src/include" "${MENGE_PACKAGE_ROOT}/src")

In your own CMakeLists

1
2
3
4
5
6
7
8
9
10
11
12
13
#CMakeLists.txt
#1. You need to add the dir of your Find<PackageName>.cmake in CMAKE_MODULE_PATH, to make sure cmake can find your package config.

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "/headless/thirdpart_lib/Menge/cmake")

# If you generated a <PackageName>Config.cmake, you should use this one
# set( CMAKE_PREFIX_PATH "/headless/thirdpart_lib/Menge/cmake")

#2. find Menge package
find(Menge REQUIRED)

#3. if successfully loaded the package, you can use ${MENGE_LIBRARIES} and ${MENGE_INCLUDE_DIRS} now
#4. if not you can use MESSAGE(${MENGE_INCLUDE_DIRS}) to check.

Another way

Above method does not install the package to the default cmake install directory.

You can directly install the package lib and include dir to the default dir as following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
if (UNIX AND NOT APPLE)

include(GNUInstallDirs)
# copy the libmengeCore.so to CMAKE_INSTALL_LIBDIR
# and create a folder in CMAKE_INSTALL_INCLUDEDIR/Menge
install(TARGETS mengeCore
EXPORT Menge
DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/Menge
)
# copy the *.h inside the following three directories into the header dirs
install(
DIRECTORY
${MENGE_SRC_DIR}/MengeCore
${MENGE_SRC_DIR}/../include/
${MENGE_SRC_DIR}/tinyxml
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/Menge
FILES_MATCHING PATTERN *.h
)

# EXPORT generates and installs a CMake file containing code to import targets from the installation tree into another project.
install(EXPORT Menge
NAMESPACE Menge::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/Menge/cmake
FILE MengeConfig.cmake
)
endif()