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 insrc/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 parsinghello
‘s CMakeLists)src/world/build/sdk/include
(providing there’sdepends=world
inhello
manifest)path/to/toolchain/<name>/world/include
(if using a toolchain providing theworld
package)/usr/include/
(ifworld
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
find_package(qibuild)
.
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-config.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
inqibuild/cmake/modules
. This can happen because the upstreamFindBar.cmake
does not exist or is not usable. (For instance, the upstreamFindGTest.cmake
setsGTEST_BOTH_LIBRARIES,
instead ofGTEST_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 always 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 in 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 find_package(qibuild)
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
, andfind_package(qibuild)
just works. - we are using a cross toolchain without qibuild, so we have to set
CMAKE_MODULE_PATH
toCTC_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()