This tutorial assumes that you already have a CMake-based project.
We will see how qiBuild can help you writing less code, while staying close to the “official” CMake recommendations when dealing with the Find<>.cmake or <>-config.cmake files.
In this tutorial, we will use a simple project called foobar.
It is pure CMake code, there is a foo library, and a bar executable linking with the foo library.
Extract the archive in you qiBuild worktree, you should end up with something like:
.qi
|__ build.cfg
|__ foobar
|__ CMakeLists.txt
|__ bar
|__ CMakeLists.txt
|__ bar
|__ bar.h
|__ bar.cpp
|__ foo
|__ CMakeLists.txt
|__ main.cpp
The standard CMakeLists.txt for such a project looks like this
# main CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
project(foobar)
add_subdirectory(bar)
add_subdirectory(foo)
# bar/CMakeLists.txt:
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_library(bar
bar/bar.h
bar/bar.cpp)
install(TARGETS bar
RUNTIME DESTINATION "lib"
ARCHIVE DESTINATION "lib"
LIBRARY DESTINATION "lib")
install(FILES bar/bar.h
DESTINATION "include/bar")
# foo/CMakeLists.txt:
include_directories(${CMAKE_SOURCE_DIR}/bar)
add_executable(foo main.cpp)
target_link_libraries(foo bar)
install(TARGETS foo DESTINATION "bin")
You have to specify install rules for every target
If you move the bar library to an other directory, you will have to fix foo‘s CMakeLists
You cannot use foobar as a subdirectory of a new project (because of the use of CMAKE_SOURCE_DIR)
You have a standard layout when you install your targets:
<prefix>
|__ lib
|__ libbar.a
|__ bin
|__ foo
|__ include
|__ bar
|__ bar.h
But it has nothing to do with where targets are in your build directory. (foo is somewhere in build/foo/ and libbar.a in build/bar).
find_path(BAR_INCLUDE_DIR bar/bar.h)
find_library(BAR_LIBRARY bar)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(bar
DEFAULT_MSG
BAR_INCLUDE_DIR
BAR_LIBRARY)
mark_as_advanced(${BAR_INCLUDE_DIR} ${BAR_LIBRARY})
(and of course create the install rule for the bar-config.cmake)
find_package(bar)
include_directories(${BAR_INCLUDE_DIRS})
add_executable(myexe ...)
target_link_libraries(myexe ${BAR_LIBRARIES})
This assumes that the person has installed the bar packaged somewhere CMake can find it. (For instance in /usr/local/share/cmake/bar-config.cmake), or that he sets -DBAR_DIR.
It the person also happens to have the foboar sources built somewhere, it cannot use them...
Neither libbar or bar.h can be found by CMake: bar.h is hidden somewhere in the sources of foobar, and libbar.a somewhere in the build directory of foobar, so it is impossible to use the carefully home-made bar-config.cmake, unless you install libbar to /usr/local/lib/libbar.a for instance.
The motivation for qiBuild is to help solve this CMake limitations with a clean, easy way, while staying the more compatible possible with other CMake projects.
Add a qibuild.cmake file at the root of the project and have it included right after the project() line.
The qibuild.cmake file can be found in qibuild/cmake/qibuild/templates/qibuild.cmake
Copy-paste this file at the root of the foobar project, then modify the CMakeLists.txt to have:
cmake_minimum_required(VERSION 2.8)
project(foobar)
include(qibuild.cmake)
We wanted to have this explicit step.
The ‘qibuild.cmake’ file does 3 things:
So here you should write a dependencies.cmake file in your build dir looking like
list(APPEND CMAKE_MODULE_PATH
"/path/to/qibuild"
)
Or just use qibuid configure which will do the job for you.
Replace the add_library by qi_create_lib, and fix the install rules:
qi_create_lib(bar
SRC bar/bar.h bar/bar.cpp)
qi_install_header(bar HEADERS bar/bar.h SUBFOLDER bar)
Using qi_create_lib and :ref:qi_install_header` will have the following effects:
Add the following line in bar’s CMakeLists:
qi_stage_lib(bar)
And replace code in foo’s CMakeLists to have
qi_use_lib(foo bar)
(no need to call include_directories or target_link_libraries anymore)
So what happened?
Two versions of the foo-config.cmake file have been generated:
So, since the layout in build/sdk is the same as the layout when the library is installed, and since the foo-config file has been automatically generated (along with the install rules), it makes no difference whether you want to find the bar library you have just built in the foobar project, using the bar library you have just built in a other project, or using the installed bar library.
Finding the bar-config.cmake in foobar/build/skd from an other project is as easy as:
list(APPEND CMAKE_FIND_ROOT_PATH "/path/to/foobar/build/sdk")
Finding the bar-config.cmake once bar has been installed in as easy as:
# No qiBuild required: the installed bar-config.cmake contains
# no qibuild-sepecific code:
find_package(bar)
include_directories(${BAR_INCLUDE_DIRS})
add_library(foo)
target_link_libraries(${BAR_LIBRARIES})
# Or, still using qibuild:
qi_use_lib(... bar)
Note
We always generate variables in the form <PREFIX>_INCLUDE_DIRS and <PREFIX>_LIBRARIES (all upper case, no version number, plural form)
This is what the final code looks like when you’re done:
# Main CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(foobar)
include(qibuild.cmake)
add_subdirectory(bar)
add_subdirectory(foo)
# build/dependencies.cmake
set(CMAKE_MODULE_PATH "path/to/qibuild/cmake/qibuild/cmake")
# bar/CMakeLists.txt
include_directories(".")
qi_create_lib(bar
SRC
bar/bar.h
bar/bar.cpp
)
qi_install_header(bar bar/bar.h)
qi_stage_lib(bar)
# foo/CMakeLists.txt
qi_create_bin(foo main.cpp)
qi_use_lib(foo bar)
Less code, so many features !
qi_use_lib(foo bar)