Как используется CMake?

Крайне сложно получить какую-либо полезную информацию о CMake как новичке. До сих пор я видел несколько уроков о том, как настроить какой-то очень простой проект или другой. Тем не менее, ни один из них не объясняет причину чего-либо, что показано в них, всегда оставляя много дыр для заполнения.

Что означает вызов CMake для CMakeLists? Он должен вызываться один раз для каждого дерева сборки или как? Как использовать разные настройки для каждой сборки, если все они используют одинаковые списки CMakeList из одного и того же источника? Почему каждому подкаталогу нужны свои собственные списки CMakeLists? Имеет ли смысл использовать CMake в списках CMakeLists, отличных от корневого каталога проекта? Если да, то в каких случаях? В чем разница между указанием того, как создать исполняемый файл или библиотеку из CMakeLists в их собственном подкаталоге, по сравнению с указанием в CMakeLists в корне всего исходного кода? Могу ли я сделать проект для Eclipse и другой для Visual Studio, просто изменив параметр -G при вызове CMake? Это даже как это используется?

Ни один из учебных пособий, страниц документации или вопросов / ответов, которые я видел до сих пор, не дает полезного понимания понимания того, как использовать CMake. Примеры просто не исчерпывающие. Независимо от того, какие уроки я читаю, я чувствую, что упускаю что-то важное.

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

2 ответа

Решение

Для чего нужен CMake?

Согласно Википедии:

CMake - это [...] программное обеспечение для управления процессом сборки программного обеспечения с использованием метода, независимого от компилятора. Он предназначен для поддержки иерархий каталогов и приложений, которые зависят от нескольких библиотек. Он используется в сочетании с собственными средами сборки, такими как make, Xcode Apple и Microsoft Visual Studio.

С CMake вам больше не нужно поддерживать отдельные настройки, специфичные для вашей среды компилятора / сборки. У вас есть одна конфигурация, и она работает для многих сред.

CMake может генерировать решение Microsoft Visual Studio, проект Eclipse или лабиринт Makefile из одних и тех же файлов, ничего не изменяя в них.

Имея кучу каталогов с кодом в них, CMake управляет всеми зависимостями, порядками сборки и другими задачами, которые необходимо выполнить вашему проекту, прежде чем его можно будет скомпилировать. Это на самом деле ничего не компилирует. Чтобы использовать CMake, вы должны сообщить ему (используя конфигурационные файлы CMakeLists.txt), какие исполняемые файлы вам нужны, какие библиотеки они ссылаются, какие каталоги есть в вашем проекте и что внутри них, а также какие-либо подробности, такие как флаги или что-то еще, что вам нужно (CMake довольно мощный).

Если это правильно настроено, вы затем используете CMake для создания всех файлов, которые ваша "родная среда сборки" должна выполнять. В Linux по умолчанию это означает Makefiles. Так что, как только вы запустите CMake, он создаст кучу файлов для собственного использования плюс некоторые Makefile s. Все, что вам нужно сделать после этого, это набирать в консоли "make" из корневой папки каждый раз, когда вы закончите редактирование кода, и bam создает скомпилированный и связанный исполняемый файл.

Как работает CMake? Что оно делает?

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

simple/
  CMakeLists.txt
  src/
    tutorial.cxx
    CMakeLists.txt
  lib/
    TestLib.cxx
    TestLib.h
    CMakeLists.txt
  build/

Содержимое каждого файла показано и обсуждено позже.

CMake настраивает ваш проект в соответствии с корнем CMakeLists.txt вашего проекта, и делает это в любом каталоге вы выполнили cmake из в консоли. Выполнение этого из папки, которая не является корнем вашего проекта, приводит к тому, что называется сборкой вне источника, что означает, что файлы, созданные во время компиляции (файлы obj, lib, исполняемые файлы, вы знаете), будут помещены в указанную папку. Хранится отдельно от фактического кода. Это помогает уменьшить беспорядок и является предпочтительным по другим причинам, которые я не буду обсуждать.

Я не знаю, что произойдет, если вы выполните cmake на любом другом, кроме корня CMakeLists.txt,

В этом примере, так как я хочу, чтобы все это размещалось внутри build/ папку, сначала я должен перейти туда, затем передать CMake каталог, в котором рут CMakeLists.txt проживает.

cd build
cmake ..

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

simple/build/
  CMakeCache.txt
  cmake_install.cmake
  Makefile
  CMakeFiles/
    (...)
  src/
    CMakeFiles/
      (...)
    cmake_install.cmake
    Makefile
  lib/
    CMakeFiles/
      (...)
    cmake_install.cmake
    Makefile

Каковы все эти файлы? Единственное, о чем вам нужно беспокоиться, это Makefile и папки проекта.

Обратите внимание на src/ а также lib/ папки. Они были созданы потому, что simple/CMakeLists.txt указывает на них с помощью команды add_subdirectory(<folder>), Эта команда говорит CMake искать в указанной папке другую CMakeLists.txt файл и выполнить этот скрипт, поэтому каждый подкаталог, добавленный таким образом, должен иметь CMakeLists.txt файл внутри. В этом проекте simple/src/CMakeLists.txt описывает, как построить фактический исполняемый файл и simple/lib/CMakeLists.txt описывает, как построить библиотеку. Каждая цель, которая CMakeLists.txt Описание по умолчанию будет размещено в его подкаталоге в дереве сборки. Итак, после быстрого

make

в консоли сделано из build/ добавлены некоторые файлы:

simple/build/
  (...)
  lib/
    libTestLib.a
    (...)
  src/
    Tutorial
    (...)

Проект построен, и исполняемый файл готов к выполнению. Что вы делаете, если хотите, чтобы исполняемые файлы помещались в определенную папку? Установите соответствующую переменную CMake или измените свойства определенной цели. Подробнее о переменных CMake позже.

Как мне сказать CMake, как построить мой проект?

Вот объясненное содержимое каждого файла в исходном каталоге:

simple/CMakeLists.txt:

cmake_minimum_required(VERSION 2.6)

project(Tutorial)

# Add all subdirectories in this project
add_subdirectory(lib)
add_subdirectory(src)

Минимальная требуемая версия всегда должна быть установлена ​​в соответствии с предупреждением, которое выдает CMake, когда вы этого не делаете. Используйте любую версию CMake.

Имя вашего проекта может быть использовано позже, и это указывает на то, что вы можете управлять более чем одним проектом из одних и тех же файлов CMake. Я не буду углубляться в это, хотя.

Как упоминалось ранее, add_subdirectory() добавляет папку в проект, что означает, что CMake ожидает, что он будет иметь CMakeLists.txt в течение которого он будет работать, прежде чем продолжить. Кстати, если у вас есть определенная функция CMake, вы можете использовать ее из других CMakeLists.txt в подкаталогах, но вы должны определить его, прежде чем использовать add_subdirectory() или это не найдет. Тем не менее, CMake более умён в отношении библиотек, так что это, вероятно, единственный раз, когда вы столкнетесь с такой проблемой.

simple/lib/CMakeLists.txt:

add_library(TestLib TestLib.cxx)

Чтобы создать свою собственную библиотеку, вы даете ей имя, а затем перечисляете все файлы, из которых она построена. Непосредственная. Если ему нужен другой файл, foo.cxx, чтобы быть скомпилированным, вы бы вместо этого написать add_library(TestLib TestLib.cxx foo.cxx), Это также работает для файлов в других каталогах, например add_library(TestLib TestLib.cxx ${CMAKE_SOURCE_DIR}/foo.cxx), Подробнее о переменной CMAKE_SOURCE_DIR позже.

Еще одна вещь, которую вы можете сделать с этим, это указать, что вы хотите общую библиотеку. Пример: add_library(TestLib SHARED TestLib.cxx), Не бойтесь, это где CMake начинает делать вашу жизнь проще. Независимо от того, является ли он общим или нет, теперь все, что вам нужно для использования библиотеки, созданной таким образом, - это имя, которое вы дали здесь. Имя этой библиотеки теперь TestLib, и вы можете ссылаться на нее из любого места в проекте. CMake найдет его.

Есть ли лучший способ перечислить зависимости? Определенно да. Проверьте ниже, чтобы узнать больше об этом.

simple/lib/TestLib.cxx:

#include <stdio.h>

void test() {
  printf("testing...\n");
}

simple/lib/TestLib.h:

#ifndef TestLib
#define TestLib

void test();

#endif

simple/src/CMakeLists.txt:

# Name the executable and all resources it depends on directly
add_executable(Tutorial tutorial.cxx)

# Link to needed libraries
target_link_libraries(Tutorial TestLib)

# Tell CMake where to look for the .h files
target_include_directories(Tutorial PUBLIC ${CMAKE_SOURCE_DIR}/lib)

Команда add_executable() работает точно так же, как add_library() За исключением, конечно, он будет генерировать исполняемый файл вместо. На этот исполняемый файл теперь можно ссылаться как на цель для таких вещей, как target_link_libraries(), Так как tutorial.cxx использует код, найденный в библиотеке TestLib, вы указываете это на CMake, как показано.

Точно так же любые.h файлы # включены из любых источников в add_executable() которые не находятся в той же директории, что и источник, должны быть как-то добавлены. Если бы не target_include_directories() команда, lib/TestLib.h не будет найден при составлении учебника, поэтому весь lib/ папка добавляется в каталоги для поиска по #include. Вы также можете увидеть команду include_directories() который действует аналогичным образом, за исключением того, что вам не нужно указывать цель, поскольку он устанавливает ее глобально для всех исполняемых файлов. Еще раз, я объясню CMAKE_SOURCE_DIR позже.

simple/src/tutorial.cxx:

#include <stdio.h>
#include "TestLib.h"
int main (int argc, char *argv[])
{
  test();
  fprintf(stdout, "Main\n");
  return 0;
}

Обратите внимание, как включен файл "TestLib.h". Не нужно указывать полный путь; CMake позаботится обо всем этом за кулисами благодаря target_include_directories(),

Технически говоря, в таком простом дереве исходных текстов вы можете обойтись без CMakeLists.txt под lib/ а также src/ и просто добавив что-то вроде add_executable(Tutorial src/tutorial.cxx) в simple/CMakeLists.txt, Это зависит от вас и потребностей вашего проекта.

Что еще я должен знать, чтобы правильно использовать CMake?

(АКА темы, относящиеся к вашему пониманию)

Поиск и использование пакетов . Ответ на этот вопрос объясняет это лучше, чем я когда-либо мог.

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

Переменные CMake: их много, поэтому последует краткий курс, который поможет вам выбрать правильный путь. CMake wiki - это хорошее место для получения более подробной информации о переменных и якобы других вещах.

Вы можете редактировать некоторые переменные, не перестраивая дерево сборки. Используйте для этого ccmake (он редактирует CMakeCache.txt файл). Запомни c настроить после завершения изменений, а затем g создавать make-файлы с обновленной конфигурацией.

Прочитайте ранее упомянутый учебник, чтобы узнать об использовании переменных, но вкратце: set(<variable name> value) изменить или создать переменную. ${<variable name>} использовать это.

  • CMAKE_SOURCE_DIR: Корневой каталог источника. В предыдущем примере это всегда равно /simple
  • CMAKE_BINARY_DIR: Корневой каталог сборки. В предыдущем примере это равно simple/build/, но если ты побежал cmake simple/ из папки, такой как foo/bar/etc/ тогда все ссылки на CMAKE_BINARY_DIR в этой сборке дерево станет /foo/bar/etc,
  • CMAKE_CURRENT_SOURCE_DIR: Каталог, в котором находится текущий CMakeLists.txt находится в. Это означает, что он меняется во всем: печать этого из simple/CMakeLists.txt доходность /simple и печатать его из simple/src/CMakeLists.txt доходность /simple/src,
  • CMAKE_CURRENT_BINARY_DIR: Вы поняли идею. Этот путь будет зависеть не только от папки, в которой находится сборка, но и от текущей CMakeLists.txt расположение скрипта.

Почему это важно? Исходных файлов явно не будет в дереве сборки. Если вы попробуете что-то вроде target_include_directories(Tutorial PUBLIC ../lib) в предыдущем примере этот путь будет относительно дерева сборки, то есть он будет похож на запись ${CMAKE_BINARY_DIR}/lib, который заглянет внутрь simple/build/lib/, Там нет файлов.h; самое большее вы найдете libTestLib.a, Ты хочешь ${CMAKE_SOURCE_DIR}/lib вместо.

  • CMAKE_CXX_FLAGS: Флаги для передачи компилятору, в данном случае компилятору C++. Также стоит отметить CMAKE_CXX_FLAGS_DEBUG который будет использоваться вместо, если CMAKE_BUILD_TYPE установлен на DEBUG. Есть больше как это; Проверьте CMake Wiki.
  • CMAKE_RUNTIME_OUTPUT_DIRECTORY: Скажите CMake, куда поместить все исполняемые файлы при сборке. Это глобальная настройка. Вы можете, например, установить его bin/ и там все аккуратно расположено. EXECUTABLE_OUTPUT_PATH похоже, но не рекомендуется, если вы наткнетесь на него.
  • CMAKE_LIBRARY_OUTPUT_DIRECTORY Аналогичным образом, глобальная настройка для указания CMake, куда поместить все файлы библиотеки.

Свойства цели: вы можете установить свойства, которые влияют только на одну цель, будь то исполняемый файл или библиотека (или архив... вы поняли). Вот хороший пример того, как его использовать (с set_target_properties(),

Есть ли простой способ автоматически добавлять источники к цели? Используйте GLOB, чтобы перечислить все в данном каталоге под той же переменной. Пример синтаксиса FILE(GLOB <variable name> <directory>/*.cxx),

Можете ли вы указать разные типы сборки? Да, хотя я не уверен в том, как это работает или в каких ограничениях. Это, вероятно, требует некоторого if/then'ning, но CMake действительно предлагает некоторую базовую поддержку без какой-либо настройки, например, по умолчанию для CMAKE_CXX_FLAGS_DEBUG, например. Вы можете установить тип сборки из CMakeLists.txt подать через set(CMAKE_BUILD_TYPE <type>) или вызвав CMake из консоли с соответствующими флагами, например cmake -DCMAKE_BUILD_TYPE=Debug,

Какие-нибудь хорошие примеры проектов, которые используют CMake? В Википедии есть список проектов с открытым исходным кодом, которые используют CMake, если вы хотите посмотреть на это. До сих пор онлайн-уроки были для меня всего лишь разочарованием, однако этот вопрос переполнения стека имеет довольно классную и простую для понимания настройку CMake. Это стоит посмотреть.

Использование переменных из CMake в вашем коде: Вот быстрый и грязный пример (адаптированный из некоторого другого урока):

simple/CMakeLists.txt:

project (Tutorial)

# Setting variables
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 1)

# Configure_file(<input> <output>)
# Copies a file <input> to file <output> and substitutes variable values referenced in the file content.
# So you can pass some CMake variables to the source code (in this case version numbers)
configure_file (
  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
  "${PROJECT_SOURCE_DIR}/src/TutorialConfig.h"
)

simple/TutorialConfig.h.in:

// Configured options and settings
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

Полученный файл, сгенерированный CMake, simple/src/TutorialConfig.h:

// Configured options and settings
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 1

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

Для всего остального Stack Overflow полон конкретных вопросов и кратких ответов, что отлично подходит для всех, кроме непосвященных.

Вот некоторые (трудно найти, как все о CMake) видео:

Надеюсь, это поможет (и позор CMake и его плохой документации, # ^ &!):)

Другие вопросы по тегам