[Documentation] [TitleIndex] [WordIndex

Обзор

Файл CMakeLists.txt является исходным для работы системы сборки CMake. Любой CMake-совместимый пакет содержит один или несколько файлов CMakeLists.txt, которые описывают процесс сборки программного кода и место его дальнейшей установки. Файл CMakeLists.txt, используемый catkin, является обычным "ванильным" файлом CMakeLists.txt с некоторыми дополнительными ограничениями.

Общая структура и последовательность

Файл CMakeLists.txt ДОЛЖЕН соответствовать указанному формату, иначе ваши пакеты не будут корректно собраны. Порядок в конфигурации ИМЕЕТ значение.

  1. Требуемая версия CMake (cmake_minimum_required)

  2. Имя пакета (project())

  3. Поиск других пакетов CMake/Catkin, необходимых для сборки (find_package())

  4. Указание файлов сообщений/сервисов/действий (add_message_files(), add_service_files(), add_action_files())

  5. Запуск обработки файлов сообщений/сервисов/действий (generate_messages())

  6. Экспорт данных из пакета (catkin_package())

  7. Собираемые библиотеки/модули (add_library()/add_executable()/target_link_libraries())

  8. Собираемые тесты (catkin_add_gtest())

  9. Правила установки (install())

Версия CMake

Каждый файл CMakeLists.txt для catkin должен начинаться с указания необходимой версии CMake. Для catkin требуется версия 2.8.3 или выше.

cmake_minimum_required(VERSION 2.8.3)

Имя пакета

Далее следует имя пакета, которое задаётся функцией project. Вот так выглядит создание пакета с именем robot_brain.

project(robot_brain)

Позже, вы можете ссылаться на это имя в любом месте скрипта CMake, используя переменную ${PROJECT_NAME}.

Поиск зависимых пакетов CMake

Далее мы должны указать, какие другие пакеты CMake следует найти для сборки нашего проекта. Для этого используется функция CMake find_package. Всегда есть как минимум одна зависимость от catkin:

find_package(catkin REQUIRED)

Если ваш проект зависит от других wet пакетов, они автоматически преобразуются в компоненты (в понимании CMake) catkin. Если вместо простого использования find_package для поиска таких пакетов, вы опишете их как компоненты, это весьма облегчит вашу жизнь. Например, если вы используете пакет nodelet.

find_package(catkin REQUIRED COMPONENTS nodelet)

NB: Вы должны использовать find_package для поиска компонентов времени сборки. Вы не должны добавлять зависимости времени исполнения.

Также вы можете сделать так:

find_package(catkin REQUIRED)
find_package(nodelet REQUIRED)

Но это не лучший способ.

Что делает find_package()?

Если система CMake нашла пакет при вызове find_package, в её окружении создаются различные переменные, которые предоставляют информацию о найденом пакете. Эти переменные окружения могут быть позднее использованы в скрипте CMake. Переменные окружения описывают где находятся экспортируемые заголовочные файлы и исходные файлы пакетов, от каких библиотек зависит пакет, и где эти библиотеки находятся. Имена всегда имеют вид <имя пакета>_<свойство>:

Почему пакеты catkin указываются как компоненты?

Catkin packages are not really components of catkin. Rather the components feature of CMake was utilized in the design of catkin to save you significant typing time.

For catkin packages, if you find_package them as components of catkin, this is advantageous as a single set of environment variables is created with the catkin_ prefix. Для примера, let us say you were using the package nodelet in your code. Рекомендуется такой путь поиска пакетов:

find_package(catkin REQUIRED COMPONENTS nodelet)

Это означает, что пути include, библиотеки и прочее, экспортируемое by nodelet также добавляются к to the catkin_ variables. For example, catkin_INCLUDE_DIRS contains the include paths not only for catkin but also for nodelet as well! This will come in handy later.

We could alternatively find_package nodelet on its own:

find_package(nodelet)

This means the nodelet paths, libraries and so on would not be added to catkin_ variables.

This results in nodelet_INCLUDE_DIRS, nodelet_LIBRARIES, and so on. The same variables are also created using

find_package(catkin REQUIRED COMPONENTS nodelet)

Boost

If using C++ and Boost, you need to invoke find_package() on Boost and specify which aspects of Boost you are using as components. For example, if you wanted to use Boost threads, you would say:

find_package(Boost REQUIRED COMPONENTS thread)

catkin_package()

catkin_package() это макрос CMake предоставляемый catkin. Он необходим для того, чтоб предоставить системе сборки информацию, специфичную для catkin. Система сборки, в свою очередь, используется чтоб производить файлы pkg-config и CMake.

Эта функция должна быть вызвана до объявления любых целей сборки функциями add_library() или add_executable(). Функция имеет 5 необязательных параметров:

Полная документация по этому макросу находитсяздесь.

Пример:

catkin_package(
   INCLUDE_DIRS include
   LIBRARIES ${PROJECT_NAME}
   CATKIN_DEPENDS roscpp nodelet
   DEPENDS eigen opencv)

Запись означает, что экспортированные заголовочные файлы попадают в папку "include", которая находится внутри папки пакета . Переменная окружения ${PROJECT_NAME} evaluates to whatever you passed to the project() function earlier, in this case it will be "robot_brain". "roscpp" + "nodelet" are packages that need to be present to build/run this package, and "eigen" + "opencv" are system dependencies that need to be present to build/run this package.

Указываем цели сборки

Цели сборки могут иметь различные формы, но обычно есть две возможности:

Наименование целей

It is very important to note that the names of build targets in catkin must be unique regardless of the folders they are built/installed to. This is a requirement of CMake. However, unique names of targets are only necessary internally to CMake. One can have a target renamed to something else using the set_target_properties() function:

Example:

set_target_properties(rviz_image_view
                      PROPERTIES OUTPUT_NAME image_view
                      PREFIX "")

This will change the name of the target rviz_image_view to image_view in the build and install outputs.

Custom output directory

While the default output directory for executables and libraries is usual set to a reasonable value it must be customized in certain cases. I.e. a library containing Python bindings must be placed in a different folder to be importable in Python:

Example:

set_target_properties(python_module_library
  PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION})

Include Paths and Library Paths

Prior to specifying targets, you need to specify where resources can be found for said targets, specifically header files and libraries:

include_directories()

The argument to include_directories should be the *_INCLUDE_DIRS variables generated by your find_package calls and any additional directories that need to be included. If you are using catkin and Boost, your include_directories() call should look like:

include_directories(include ${Boost_INCLUDE_DIRS} ${catkin_INCLUDE_DIRS})

The first argument "include" indicates that the include/ directory within the package is also part of the path.

The CMake link_directories() function can be used to add additional library paths, however, this is not recommended. All catkin and CMake packages automatically have their link information added when they are find_packaged. Simply link against the libraries in target_link_libraries()

Example:

link_directories(~/my_libs)

Please see this cmake thread to see a detailed example of using target_link_libraries() over link_directories().

Executable Targets

To specify an executable target that must be built, we must use the add_executable() CMake function.

add_executable(myProgram src/main.cpp src/some_file.cpp src/another_file.cpp)

This will build a target executable called myProgram which is built from 3 source files: src/main.cpp, src/some_file.cpp and src/another_file.cpp.

Library Targets

The add_library() CMake function is used to specify libraries to build. By default catkin builds shared libraries.

add_library(${PROJECT_NAME} ${${PROJECT_NAME}_SRCS})

Use the target_link_libraries_function to specify which libraries an executable target links against. This is done typically after an add_executable() call. Add ${catkin_LIBRARIES} if ros is not found.

Синтакс:

target_link_libraries(<executableTargetName>, <lib1>, <lib2>, ... <libN>)

Пример:

add_executable(foo src/foo.cpp)
add_library(moo src/moo.cpp)
target_link_libraries(foo moo)  -- This links foo against libmoo.so

Note that there is no need to use link_directories() in most use cases as that information is automatically pulled in via find_package().

Цели сообщений, сервисов и действий

Файлы сообщений (.msg), сервисов (.srv), и действий (.action) в ROS требуют предварительного шага обработки (препроцессора), перед тем, как они могут быть скомпилированы и использованы пакетами ROS. Смысл этих макросов в том, чтоб создать programming language-specific files so that one can utilize messages, services, and actions in their programming language of choice. Система сборки будет generate bindings, используя все доступные препроцессоры (то есть gencpp, genpy, genlisp и прочие).

Для обработки сообщений, сервисов и действий есть соответствующие три макроса:

Эти макросы должны предшествовать вызову следующего макроса, который запускает обработку:

 generate_messages()

Важные условия и ограничения

 find_package(catkin REQUIRED COMPONENTS ...)
 add_message_files(...)
 add_service_files(...)
 add_action_files(...)
 generate_messages(...)
 catkin_package(...)
 ...

catkin_package(
 ...
 CATKIN_DEPENDS message_runtime ...
 ...)

find_package(catkin REQUIRED COMPONENTS message_generation)

  add_dependencies(some_target ${PROJECT_NAME}_generate_messages_cpp)

Example

If your package has two messages in a directory called "msg" named "MyMessage1.msg" and "MyMessage2.msg" and these messages depend on std_msgs and sensor_msgs and a service in a directory called "srv" named "MyService.srv" then you will need the following in your CMakeLists.txt:

   1   # Get the information about this package's buildtime dependencies
   2   find_package(catkin REQUIRED
   3     COMPONENTS message_generation std_msgs sensor_msgs)
   4 
   5   # Declare the message files to be built
   6   add_message_files(FILES
   7     MyMessage1.msg
   8     MyMessage2.msg
   9   )
  10 
  11   # Declare the service files to be built
  12   add_service_files(FILES
  13     MyService.srv
  14   )
  15 
  16   # Actually generate the language-specific message and service files
  17   generate_messages(DEPENDENCIES std_msgs sensor_msgs)
  18 
  19   # Declare that this catkin package's runtime dependencies
  20   catkin_package(
  21    CATKIN_DEPENDS message_runtime std_msgs sensor_msgs
  22   )

If, additionally, you want to build actionlib actions, and have an action specification file called "MyAction.action" in the "action" directory, you must add actionlib_msgs to the list of components which are find_packaged with catkin and add the following call before the call to generate_messages(...):

add_action_files(FILES
  MyAction.action
)

Furthermore the package must have a build dependency on actionlib_msgs.

Unit Tests

There is a catkin-specific macro for handling gtest-based unit tests called catkin_add_gtest().

catkin_add_gtest(myUnitTest test/utest.cpp)

Optional Step: Specifying Installable Targets

After build time, targets are placed into the devel space of the catkin workspace. However, often we want to install targets to the system (information about installation paths can be found in REP 122) so that they can be used by others or to a local folder to test a system-level installation. In other words, if you want to be able to do a "make install" of your code, you need to specify where targets should end up.

This is done using the CMake install() function which takes as arguments:

Take as an example:

install(TARGETS ${PROJECT_NAME}
  ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

Besides these standard destination some files must be installed to special folders. I.e. a library containing Python bindings must be installed to a different folder to be importable in Python:

install(TARGETS python_module_library
  ARCHIVE DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}
  LIBRARY DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}
)

Installing Python Executable Scripts

For Python code, the install rule looks different as there is no use of the add_library() and add_executable() functions so as for CMake to determine which files are targets and what type of targets they are. Instead, use the following in your CMakeLists.txt file:

catkin_install_python(PROGRAMS scripts/myscript
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})

Detailed information about installing python scripts and modules, as well as best practices for folder layout can be found in the catkin manual.

Installing header files

Header files must also be installed to the "include" folder, This is often done by installing the files of an entire folder (optionally filtered by filename patterns and excluding SVN subfolders). This can be done with an install rule that looks as follows:

install(DIRECTORY include/${PROJECT_NAME}/
  DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
  PATTERN ".svn" EXCLUDE
)

or if the subfolder under include does not match the package name:

install(DIRECTORY include/
  DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION}
  PATTERN ".svn" EXCLUDE
)

Installing roslaunch Files or Other Resources

Other resources like launchfiles can be installed to ${CATKIN_PACKAGE_SHARE_DESTINATION:

install(DIRECTORY launch/
  DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
  PATTERN ".svn" EXCLUDE)

2023-10-28 13:03