Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 1

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

Что такое CMake для?

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

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

С помощью CMake вам больше не нужно поддерживать отдельные настройки, специфичные для вашей среды компилятора/сборки. У вас есть одна конфигурация, и это работает для многих сред. Cmake может создавать решение Microsoft Visual Studio, проект Eclipse или лабиринт Makefile из тех же файлов без изменения чего-либо в них.

Учитывая набор каталогов с кодом в них, CMake управляет всеми зависимостями, строит заказы и другие задачи, которые ваш проект должен выполнить, прежде чем он может быть скомпилирован. Он НЕ собирает ничего. Чтобы использовать CMake, вы должны сообщить ему (используя файлы конфигурации, называемые CMakeLists.txt), какие исполняемые файлы, которые вам нужно скомпилировать, какие библиотеки они ссылаются, какие каталоги находятся в вашем проекте и что внутри них, а также любые детали, такие как флаги или что-нибудь еще, что вам нужно (CMake достаточно мощный). Если это правильно настроено, вы затем используете CMake для создания всех файлов, которые ваша "собственная среда сборки" по своему выбору должна выполнять свою работу. В linux по умолчанию это означает Makefile. Поэтому, как только вы запустите 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 и выполняет это script, поэтому каждый добавленный таким образом подкаталог должен иметь файл 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, которые будут искать #includes. Вы также можете увидеть команду 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?

(темы AKA, имеющие отношение к вашему пониманию)

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

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

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

Вы можете редактировать некоторые переменные без восстановления дерева сборки. Используйте ccmake для этого (он редактирует файл CMakeCache.txt). Запомните c onfigure, когда закончите с изменениями, а затем 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 script.

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

  • CMAKE_CXX_FLAGS: флаги для передачи компилятору, в данном случае компилятору С++. Также стоит отметить CMAKE_CXX_FLAGS_DEBUG, который будет использоваться вместо этого, если для параметра CMAKE_BUILD_TYPE установлено значение DEBUG. Их больше, посмотрите вики CMake.
  • 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/thenning, но 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 @[email protected]
#define Tutorial_VERSION_MINOR @[email protected]

Результирующий файл, сгенерированный CMake, simple/src/TutorialConfig.h:

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

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

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