Search order

Search order for packages, libs, headers, and programs

This has an impact on the following functions:

  • find_package()
  • find_libary()
  • find_path()
  • find_program()

When the executable hello is looking for the world library, the world library can be in several places:

  • in the same project src/hello
  • in a world project in src/world
  • in a toolchain with a world package
  • in the system, for instance because libworld-dev package is installed.

Here is the order CMake will use to find headers and libraries, in this order:

  • src/hello/path/to/world (if world target is defined when parsing hello‘s CMakeLists)
  • src/world/build/sdk/include (providing there’s depends=world in hello manifest)
  • path/to/toolchain/<name>/world/include (if using a toolchain providing the world package)
  • /usr/include/ (if world is installed in the system)

Note that it makes no difference whether you are using qi_use_lib or find_package.

To make this work, we only use the CMAKE_FIND_ROOT_PATH cmake variable, and qi_use_lib calls find_package without any specific argument

This means that if you really need to find a library inside a toolchain, and never in the system, (for cross-compiling), you should use something like this for the toolchain file of your cross-toolchain

set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
find_package(world)

Why ?

Because we treat the CMAKE_FIND_ROOT_PATH variable very carefully ;)

Here’s what happens when you run qibuild configure hello:

In the main CMakeLists, you have project() before the call to include(qibuild.cmake).

So this means you go through the toolchain file first (calling project() makes cmake include the file passed with -DCMAKE_TOOLCHAIN_FILE.

The toolchain file generated by qitoolchain contains:

list(APPEND CMAKE_FIND_ROOT_PATH /path/to/toolchain/<name>)

(Note that qibuild configure calls cmake with the correct -DCMAKE_TOOLCHAIN_FILE for you)

Then, you go through qibuild.cmake which includes build/dependencies.cmake

The code in dependencies.cmake looks like

list(INSERT CMAKE_FIND_ROOT_PATH 0 /path/to/src/world/build/sdk)

So that sources are searched before the packages from toolchain

Finally, you go through qibuild/general.cmake

Here we do something like

set(CMAKE_FIND_ROOT_PATH "${QI_SDK_DIR}" ${CMAKE_FIND_ROOT_PATH})

so that self build dir is searched first.

Note that we never reset CMAKE_FIND_ROOT_PATH, so that if user set it from command lines, it exists even before going to the toolchain file, so it still has the priority.

Search order for packages

When we call find_package(bar), we have several possible cases

  • We are using a bar-config.cmake that was generated by qibuild.
  • We are using the custom bar-config.cmake in qibuild/cmake/modules. This can happen because the upstream FindBar.cmake does not exist or is not usable. (For instance, the upstream FindGTest.cmake sets GTEST_BOTH_LIBRARIES, instead fo GTEST_LIBRARIES ...)
  • We are using upstream’s CMake FindBar.cmake.

Note

Due to strange CMake rules about case sensitivity, for this to work you it’s best you always use find_package() with an upper-case argument. find_package(GTest) won’t find gtest-config.cmake, but find_package(GTEST) will find it. (strange but true)

That’s why when we call find_package from qi_use_lib we alwayws use the upper-case version of the first argument.

To do this, we have to search for the -config.cmake files generated by qiBuild (or present ni qibuild/cmake/modules, then only for upstream Find-*.cmake in /usr/share/cmake)

This is not hard because find_package can be call with a special argument to only look for -config.cmake files.

From the comments in the cmake code:

# find_package in two calls. The first call:
# Uses NO_MODULE - looks for PKGConfig.cmake, not FindPKG.cmake
# Uses QUIET     - no warning will be generated
# If Config is found, then PKG_DIR will be set so that the following
# find_package knows where to look
find_package(${_pkg} NO_MODULE QUIET)
# _PACKAGE_FOUND is only set when using qibuild/cmake modules,
# see comments in find.cmake for details.
if(NOT ${_U_PKG}_PACKAGE_FOUND)
  find_package(${_pkg} QUIET REQUIRED)
endif()

# Right after find_package_handle_standard_args, ${prefix}_FOUND is
# set correctly.
# For instance, if foo/bar.h is not foud, FOO_FOUND is FALSE.
# But, right after this, since foo-config.cmake HAS been found, CMake
# re-set FOO_FOUND to TRUE.
# So we set ${prefix}_PACKAGE_FOUND in cache...

Search order for cmake specific code

This has an impact on the functions:

  • include()

And most of all, on include(qibuild.cmake)

We have several cases here:

  • qibuild is installed in the system, so qibuild/general.cmake is found in /usr/share/cmake-2.8/Modules/qibuild/general.cmake, and include(qibuild.cmake) just works.
  • we are using a cross toolchain without qibuild, so we have to set CMAKE_MODULE_PATH to CTC_DIR/sysroot/usr/share/cmake-2.8/Modules/qibuild (Assuming qibuild is installed in the sysroot of a cross-toolchain)
  • qibuild is not installed, and we are using a wrapper script using code from ~/src/qibuild.

To find the qibuild cmake files installed in a cross-toolchain, it is enough to do something like:

list(APPEND CMAKE_MODULE_PATH  "${sysroot}/usr/share/cmake/Modules/")

To find the qibuild cmake files while using code from src/qibuild, we do something like:

# in python/qibuild/__init__.py

def get_cmake_qibuild_dir():
    """ Try to guess where the qibuild cmake files are

    """

# in project.bootstrap()
cmake_module_path = get_cmake_qibuild_dir()

Then, when we run qibuild configure hello, the dependencies.cmake file is generated with the correct CMAKE_MODULE_PATH:

set(_qibuild_path "src/qibuild/cmake")  # < this line configured by project.bootstrap()
list(FIND CMAKE_MODULE_PATH "${_qibuild_path}" _found)
if(_found STREQUAL "-1")
  list(APPEND CMAKE_MODULE_PATH "${_qibuild_path}")
endif()