Знакомство с CMake. Часть 3. CMakeCache, модули CMake, зависимости сборки.

При переносе проектов, написанных на CMake, могут быть проблемы со сборкой на другой машине. Данная статья расскажет, как решить проблему. Вторая часть статьи посвящена теме создания собственных модулей для CMake. Последняя часть рассказывает про способность CMake обрабатывать зависимости при сборке проектов.

CMakeCache

Как правило, повторный вызов cmake выполняется значительно быстрее, чем первый. Это полезно, так как экономит время при изменении CMakeLists.txt, но данное свойство также может вызывать проблемы у начинающих пользователей CMake. Это происходит потому, что при первом вызове cmake он кэширует значения всех своих переменных в директории сборки в файле CMakeCache.txt. При его выполнении на измененном CMakeLists.txt он активно использует кэшированные значения переменных, поэтому пересоздание Makefile и правил сборки происходит очень быстро.

Также CMake указывает компилятору собирать бинарные файлы в отдельную директорию CMakeFiles, поэтому при пересборке компилируются только измененные исходники. Эта директория создается в папке сборки, как и файл CMakeCache.txt.

Если не почистить проект, при переносе на другую машину могут возникнуть проблемы, т.к. кэшируются абсолютные пути и бинарники. Для его очистки достаточно удалить:

  • CMakeFiles
  • CMakeCache.txt
  • cmake_install.cmake
  • install_manifest.txt
  • Makefile

Но! Проблема переноса проекта возникает только в том случае, если сборка ведется в его директории. В прошлых статьях я рассматривал сборку в отдельной папке, обычно её называют build. В этом случае проект остается нетронутым, и проблемы переноса не возникает. Поэтому я настоятельно рекомендую всегда собирать проекты в отдельных директориях, и просто прописывать в CMakeLists.txt правила для установки. Собранные исполняемые файлы и библиотеки будут устанавливаться в папку проекта, а вся сборка оставаться за кадром:

Создание собственных модулей CMake

CMake — модульная система, и даже из коробки она поддерживает около 300 различных дополнений. Они представлены в виде модулей, содержащих набор дополнительных команд и переменных, облегчающих ту или иную задачу.

Для подключения модуля используется команда:

1
find_package(name [version])

Она читает файл Find.cmake из директории:

1
<CMakeInstallDir>/share/cmake-<version>/Modules/

Для Linux систем это обычно:

1
/usr/share/cmake-<version>/Modules/

Установка своего модуля заключается в копировании файла в эту директорию. В CMake есть переменная CMAKE_ROOT, позволяющая скопировать модуль средствами самого cmake, например:

1
install(FILES "FindFoo.cmake" DESTINATION ${CMAKE_ROOT}/Modules)

Приступим к написанию модуля. Модуль — это прямой аналог CMakeLists.txt, не привязанный к проекту. В нем описываются переменные и команды, которые будут использоваться в подключающих данный модуль проектах. Переменные устанавливаются командой set(), о которой я рассказывал в предыдущей статье, а функционал описывается конструкцией:

1
2
3
4
macro(name [arg0 [arg1 [arg2 ...]]])
  COMMAND(ARGS ...)
  ...
endmacro(name)

Здесь name — имя команды, args — аргументы команды. Все аргументы в виде списка можно получить через ${ARGN}, количество аргументов — ${ARGC}, аргумент № — ${ARGV}, начиная с 0.

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
macro(ping ADDR)
  set(TIMES "")
  set(_IS_OPT false)
  foreach (_A ${ARGN})
    if (_IS_OPT)
      set (TIMES ${_A})
    else ()
      if ("${_A}" STREQUAL "TIMES")
        set(_IS_OPT true)
      endif ()
    endif ()
  endforeach()
  if (${WIN32})
    set(CNT_PREFIX "-n")
  else(${WIN32})
    set(CNT_PREFIX "-c")
  endif(${WIN32})
  if (TIMES STREQUAL "")
  else()
    set(OPTIONS ${CNT_PREFIX} ${TIMES})
  endif()
  execute_process(COMMAND "ping" ${OPTIONS} ${ADDR})
endmacro(ping)

Команда будет пинговать заданный адрес с возможностью выбора количества попыток. Варианты вызова:

1
2
ping(127.0.0.1)
ping(127.0.0.2 TIMES 10)

Первая половина кода проверяет существование и значение необязательного параметра TIMES. Мы проходимся по всем аргументам и ищем «TIMES». Следующий за ним аргумент будет нужным значением. Потом, в зависимости от семейства системы, мы выбираем для команды «ping» имя параметра, устанавливающего количество попыток. Если переменная TIMES не пуста, формируем часть командной строки для «ping» в переменной OPTIONS. В конце идет непосредственно вызов системной команды ping с нашими аргументами.

Модуль не ограничивает нас одним макросом, в нем можно также устанавливать свои переменные и выводить много полезной информации.

Зависимости сборки

У CMake есть одна полезная особенность — он умеет корректно просчитывать зависимости и при пересборке компилируются только нужные файлы. Это особенно важно при разработке больших проектов и программных комплексов, когда сборка всего проекта занимает значительное время. Было бы неоптимально собирать весь проект, если был изменен всего один файл. Однако, если был изменен заголовочный файл, то пересобирать нужно уже по цепочке включений, и при использовании других систем сборки могут возникать ошибки линковки. CMake умеет вычислять зависимости сборки не только от проектных заголовочных файлов, но и от внешних включений. Т.е., если была обновлена внешняя библиотека, он пересоберет все файлы, которые её включают.

Бывают ошибки и у CMake, но я их наблюдал крайне редко. Так что, если Вы разрабатываете проект любой сложности на CMake, можете не думать о порядке сборки и слежения за зависимостями, он всё сделает сам.


Смотрите также

comments powered by Disqus