Cmake руководство на русском

Время на прочтение
7 мин

Количество просмотров 484K

imageCMake — кроcсплатформенная утилита для автоматической сборки программы из исходного кода. При этом сама CMake непосредственно сборкой не занимается, а представляет из себя front-end. В качестве back-end`a могут выступать различные версии make и Ninja. Так же CMake позволяет создавать проекты для CodeBlocks, Eclipse, KDevelop3, MS VC++ и Xcode. Стоит отметить, что большинство проектов создаются не нативных, а всё с теми же back-end`ами.

Для того что бы собрать проект средствами CMake, необходимо в корне дерева исходников разместить файл CMakeLists.txt, хранящий правила и цели сборки, и произвести несколько простых шагов.
Разберёмся на примерах.

Пример 1. Hello, World:

Для начала напишем простейший хеловорлд и создадим структуру проекта:

main.cpp

#include <iostream>
int main(int argc, char** argv)
{
	std::cout << "Hello, World!" << std::endl;
	return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 2.8) # Проверка версии CMake.
									# Если версия установленой программы
									# старее указаной, произайдёт аварийный выход.

add_executable(main main.cpp)		# Создает исполняемый файл с именем main
									# из исходника main.cpp

Синтаксис CMake похож на синтаксис bash, всё что после символа «#» является комментарием и обрабатываться программой не будет. CMake позволяет не засорять дерево исходных кодов временными файлами — очень просто и без лишних телодвижений сборка производится «Out-of-Source».

Создадим пустую директорию для временных файлов и перейдём туда.

fshp@panica-desktop:~$ mkdir tmp
fshp@panica-desktop:~$ cd tmp/
fshp@panica-desktop:~/tmp$

Теперь запустим команду cmake, передав ей в качестве параметра путь к папке с исходниками:

fshp@panica-desktop:~/tmp$ cmake ~/cmake/example_1/

— Build files have been written to: /home/fshp/tmp
fshp@panica-desktop:~/tmp$
fshp@panica-desktop:~/tmp$ ls
CMakeCache.txt CMakeFiles cmake_install.cmake Makefile
fshp@panica-desktop:~/tmp$

Видим, что в папке появилось несколько временных файлов, необходимых для сборки проекта.
Теперь можно запустить непосредственно make:

fshp@panica-desktop:~/tmp$ make
Scanning dependencies of target main
[100%] Building CXX object CMakeFiles/main.dir/main.cpp.o
Linking CXX executable main
[100%] Built target main
fshp@panica-desktop:~/tmp$ ./main
Hello, World!
fshp@panica-desktop:~/tmp$

Итак, наша программа собралась.
Папку tmp можно очищать\удалять без риска поломать исходники. Если CMakeLists.txt был изменен, то вызов make автоматически запустит cmake. Если исходники были перемещены, то нужно очистить временную директорию и запустить cmake вручную.

Пример 2. Библиотеки:

Если ваш проект содержит библиотеку, то CMake соберет ее без проблем.
Для этого усложним пример.

foo.cpp

#include <iostream>
void hello_world()
{
	std::cout << "Hello, World!" << std::endl;
}

main.cpp

#include "foo.h"
int main(int argc, char** argv)
{
	hello_world();
	return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)	 # Проверка версии CMake.
										# Если версия установленой программы
										# старее указаной, произайдёт аварийный выход.

project(hello_world)			# Название проекта

set(SOURCE_EXE main.cpp)		# Установка переменной со списком исходников для исполняемого файла

set(SOURCE_LIB foo.cpp)			# Тоже самое, но для библиотеки

add_library(foo STATIC ${SOURCE_LIB})	# Создание статической библиотеки с именем foo

add_executable(main ${SOURCE_EXE})	# Создает исполняемый файл с именем main

target_link_libraries(main foo)		# Линковка программы с библиотекой

Переменные могут хранить списки значений, разделённых пробелами\табуляциями\переносами:

set(SOURCE main.cpp foo.cpp)
set(HEADER main.h
			foo.h)

Оба варианта правильные
Что бы получить значение переменной ипользуем конструкцию:

${var_name}

Итак, эта версия нашего проекта включает в себя одну статическую библиотеку, собираемую из исходников. Если заменить «STATIC» на «SHARED», то получим библиотеку динамическую. Если тип библиотеки не указать, по умолчанию она соберётся как статическая.
При линковке указываются все необходимые библиотеки:

target_link_libraries(main  foo
							ogg
							vorbis)

Как и при ручной компиляции, имена библиотек указываются без стандартного префикса «lib».
Итак, сборка библиотек с CMake не вызывает проблем, при этом тип библиотеки статическая\динамическая меняется лишь одним параметром.

Пример 3. Подпроекты:

Подпроекты очень удобны, если ваша программа разбита на несколько библиотек или же проект состоит из нескольких программ.
Каждый подпроект является по сути полноценным проектом и может использоваться самостоятельно.
Теперь у нас «foo» находится в субдирректории и там же находится CMakeLists.txt подпроекта.

CMakeLists.txt

cmake_minimum_required(VERSION 2.8) # Проверка версии CMake.
									# Если версия установленой программы
									# старее указаной, произайдёт аварийный выход.

project(hello_world)				# Название проекта

set(SOURCE_EXE main.cpp)			# Установка переменной со списком исходников

include_directories(foo)			# Расположение заголовочных файлов

add_executable(main ${SOURCE_EXE})	# Создает исполняемый файл с именем main

add_subdirectory(foo)				# Добавление подпроекта, указывается имя дирректории

target_link_libraries(main foo)		# Линковка программы с библиотекой

main.cpp

#include "foo.h"
int main(int argc, char** argv)
{
	hello_world();
	return 0;
}

foo/CMakeLists.txt

cmake_minimum_required(VERSION 2.8) # Проверка версии CMake.
									# Если версия установленой программы
									# старее указаной, произайдёт аварийный выход.

project(foo)				# Название проекта

set(SOURCE_LIB foo.cpp)		# Установка переменной со списком исходников

add_library(foo STATIC ${SOURCE_LIB})# Создание статической библиотеки

foo/foo.cpp

#include <iostream>
void hello_world()
{
	std::cout << "Hello, World!" << std::endl;
}

В файле подпроекта ничего нового для вас нет. А вот в основном файле новые команды:

include_directories(foo)

main.cpp мы не меняли, а foo.h перенесли. Команда указывает компилятору, где искать заголовочные файлы. Может быть вызвана несколько раз. Хидеры будут искаться во всех указаных директориях.

add_subdirectory(foo)

Указываем директорию с подпроектом, который будет собран как самостоятельный.
Вывод: проекты на CMake можно объединять в довольно сложные иерархические структуры, причем каждый подпроект в реальности является самостоятельным проектом, который в свою очередь может сам состоять из подпроектов. Это позволяет легко разбить вашу программу на необходимое количество отдельных модулей. Примером такого подхода может служить KDE.

Пример 4. Поиск библиотек:

CMake обладает достаточно развитыми средствами поиска установленых библиотек, правда они не встроеные, а реализованы в виде отдельных модулей. В стандартной поставке довольно много модулей, но некоторые проекты (например Ogre) поставляют свои. Они позволяют системе автоматически определить наличие необходимых для линковки проекта библиотек.
На debian модули располагаются в /usr/share/cmake-2.8/Modules/ (у вас версия может отличаться). За поиск библиотек отвечают модули, называющиеся FindNAME.cmake, где NAME — имя библиотеки.

find_package(SDL REQUIRED)
if(NOT SDL_FOUND)
	message(SEND_ERROR "Failed to find SDL")
	return()
else()
	include_directories(${SDL_INCLUDE_DIR})
endif()
##########################################################
find_package(LibXml2 REQUIRED)
if(NOT LIBXML2_FOUND)
	message(SEND_ERROR "Failed to find LibXml2")
	return()
else()
	include_directories(${LIBXML2_INCLUDE_DIR})
endif()
##########################################################
find_package(Boost COMPONENTS thread-mt REQUIRED)
if(NOT Boost_FOUND)
	message(SEND_ERROR "Failed to find boost::thread-mt.")
	return()
else()
	include_directories(${Boost_INCLUDE_DIRS})
endif()
##########################################################
target_link_libraries(${TARGET} 
								${SDL_LIBRARY}
								${LIBXML2_LIBRARIES}
								${Boost_LIBRARIES})

Думаю, смысл должен быть понятен. Первый и второй блок — поиск библиотеки. Если в системе её нет, выведется сообщение об ошибке и завершается выполнение cmake. Третий блок похож, только он ищет не целый пакет библиотек, а лишь необходимый компонент. Каждый такой автоматизированый поиск определяет после выполнения как минимум 3 переменные:
SDL_FOUND, LIBXML2_FOUND, Boost_FOUND — признак присутствия бибилиотеки;
SDL_LIBRARY, LIBXML2_LIBRARIES, Boost_LIBRARIES — имена библиотек для линковки;
SDL_INCLUDE_DIR, LIBXML2_INCLUDE_DIR, Boost_INCLUDE_DIRS — пути к заголовочным файлам.
Если с первыми более или менее понятно, то вторые и третьи мне доставили много хлопот — половина имеет имена в единственном числе, половина — во множественном. Но оказалось, это легко отследить. В каждом модуле вначале есть коментарии, там описаны определяемые переменные. Посмотрите, например, /usr/share/cmake-2.8/Modules/FindLibXml2.cmake
Как видите, CMake способен сам определить наличие и местоположение необходимых библиотек и заголовочных файлов. В принципе, это должна уметь любая система автоматической сборки, иначе смысл в ней?

Пример 5. Внешние библиотеки и объектные файлы:

Если вы пишите для «дяди», а злой «дядя» любит самописные библиотеки и делиться исходниками не желает, поэтому присылает готовую библиотеку, то вы по адресу.
Объектные файлы в CMake стоят на ряду с исходниками — достаточно включить объектник в список файлов для компиляции.
С библиотеками потуже. Как известно, статическая библиотека это не что иное, как ar-архив, внутри которого лежат обычные объектники, никак не связаные между собой. Вы, наверное, уже догадались, как я поступал сначала. Да, просто потрошил библиотеку. Но потом был найден способ поэлегантнее:

add_library(netutil STATIC IMPORTED)
set_property(TARGET netutil PROPERTY
             IMPORTED_LOCATION Binary/game_client/libnetutil.a)

Слово «IMPORTED», указывает, что библиотека берётся извне.
В CMake каждая цель имеет параметры, а set_property позволяет их изменять.
Линкуется такая библиотека стандартно:

target_link_libraries(${TARGET} netutil)

Для динамических библиотек все аналогично, только тип «SHARED», расширение — «.so».
К сожалению, поддержка несистемных библиотек реализована немного костыльно. Возможно, я просто не знаю правильного варианта, поэтому буду рад, если «ткнете мордочкой». С другой стороны это не навороченый экзоскелет с системой жизнеобеспечения, а простейший костыль из двух строк.

Генераторы:

Как было сказано в начале, CMake умеет генерировать множество различных видов проектов. Это удобно и позволяет использовать CMake для практически любой популярной IDE.
Если запустить cmake без параметров, в конце будут описаны доступные генераторы. Пользоваться так:

fshp@panica-desktop:~/tmp$ cmake ~/cmake/example_3/ -G «KDevelop3 — Unix Makefiles»

Заключение:

Это не перевод мануала, а результат использования CMake в одном коммерческом проекте. Буду рад, если статья поможет хотя бы одному человеку — на русском языке подобной документации довольно мало.

Чем понравился CMake лично мне:

  • один проект — один файл. Не нужно хранить кучу скриптов настройки, сборки и прочего хлама;
  • Скорость работы в сравнении с autotools;
  • простой и понятный синтаксис, конечно с элегантностью питона не потягаться, но и не брейнфак, в конце концов.;
  • является front-end`ом для множества IDE;
  • отображение прогресса — довольно удобно;
  • цветной вывод — в серые будни немного краски не помешает;

Для Sublime Text есть плагин, добавляющий подсветку синтаксиса CMake, он так и называется — «CMake».
Примеры

Руководство по взаимодействию с пользователем

  • Introduction

    • Инструмент командной строки
    • cmake-gui tool
  • Генерация системы сборки

    • Командная строка среды
    • Командная строка -G опция
    • Выбор генератора в смоделированном виде
  • Установка Строить Переменные

    • Установка переменных в командной строке
    • Установка переменных с помощью cmake-gui
    • Кэш CMake
  • Presets

    • Использование предустановок в командной строке
    • Использование предустановок в смейк-гуи
  • Ссылка на систему Buildsystem

    • Выбор цели
    • Указание программы создания
  • Software Installation
  • Running Tests

Introduction

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

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

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

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

Инструмент CMake может сообщать пользователю об ошибках при обработке предоставленных файлов CMake, например сообщать, что компилятор не поддерживается, или компилятор не поддерживает требуемый параметр компиляции, или зависимость не может быть найдена. Эти ошибки должны быть устранены пользователем путем выбора другого компилятора, installing dependencies или указания CMake, где их найти, и т. Д.

Инструмент командной строки

Простое, но типичное использование cmake(1) со свежей копией исходного кода программного обеспечения — это создать каталог сборки и вызвать там cmake:

$ cd some_software-1.4.2
$ mkdir build
$ cd build
$ cmake .. -DCMAKE_INSTALL_PREFIX=/opt/the/prefix
$ cmake --build .
$ cmake --build . --target install

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

Инструмент CMake может сообщать предупреждения, которые предназначены для поставщика программного обеспечения, а не для потребителя программного обеспечения. Такие предупреждения заканчиваются на «Это предупреждение для разработчиков проекта». Пользователи могут отключить такие предупреждения, передав флаг -Wno-dev в cmake(1) .

cmake-gui tool

Пользователи, более привыкшие к графическим интерфейсам, могут использовать инструмент cmake-gui(1) для вызова CMake и создания системы сборки.

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

Choosing source and binary directories

Генерация системы сборки

Существует несколько инструментов пользовательского интерфейса, которые можно использовать для создания системы сборки из файлов CMake. Инструменты ccmake(1) и cmake-gui(1) помогают пользователю настроить различные необходимые параметры. Инструмент cmake(1) можно вызвать, чтобы указать параметры в командной строке. В этом руководстве описаны параметры, которые можно установить с помощью любого из инструментов пользовательского интерфейса, хотя режим настройки параметра отличается для каждого инструмента.

Командная строка среды

При вызове cmake(1) с системой сборки из командной строки, такой как Makefiles или Ninja , необходимо использовать правильную среду сборки, чтобы гарантировать доступность инструментов сборки. CMake должен иметь возможность найти подходящий build tool , компилятор, компоновщик и другие инструменты по мере необходимости.

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

При кросс-компиляции некоторые платформы могут требовать установки переменных окружения или предоставлять скрипты для установки окружения.

Visual Studio предоставляет несколько командных vcvarsall.bat сценариев vcvarsall.bat для настройки правильных сред для систем сборки командной строки. Хотя при использовании генератора Visual Studio не обязательно использовать соответствующую среду командной строки, это не имеет недостатков.

При использовании Xcode может быть установлено более одной версии Xcode.Какая из них может быть выбрана несколькими разными способами,но наиболее распространенными из них являются:

  • Установка версии по умолчанию в настройках Xcode IDE.
  • Установка версии по умолчанию с помощью xcode-select командной строки xcode-select .
  • Overriding the default version by setting the DEVELOPER_DIR environment variable when running CMake and the build tool.

Для удобства cmake-gui(1) предоставляет редактор переменных окружения.

Командная строка -G опция

CMake по умолчанию выбирает генератор на основе платформы.Обычно генератора по умолчанию достаточно,чтобы пользователь мог приступить к сборке программного обеспечения.

Пользователь может переопределить генератор по умолчанию с помощью опции -G :

$ cmake .. -G Ninja

Вывод cmake --help включает список generators доступных пользователю на выбор. Обратите внимание, что имена генераторов чувствительны к регистру.

В Unix-подобных системах (включая Mac OS X) генератор Unix Makefiles используется по умолчанию. Вариант этого генератора также можно использовать в Windows в различных средах, таких как NMake Makefiles и MinGW Makefiles . Эти генераторы генерируют вариант Makefile , который можно запустить с помощью make , gmake , nmake или подобных инструментов. Дополнительную информацию о целевых средах и инструментах см. в документации по отдельным генераторам.

Генератор Ninja доступен на всех основных платформах. ninja — это инструмент сборки, похожий по сценариям использования на make , но с упором на производительность и эффективность.

В Windows cmake(1) можно использовать для создания решений для IDE Visual Studio. Версии Visual Studio можно указать по названию продукта IDE, которое включает четырехзначный год. Предоставляются псевдонимы для других средств, с помощью которых иногда упоминаются версии Visual Studio, например, две цифры, соответствующие версии продукта компилятора VisualC ++, или их сочетание:

$ cmake .. -G "Visual Studio 2019"
$ cmake .. -G "Visual Studio 16"
$ cmake .. -G "Visual Studio 16 2019"

Генераторы Visual Studio могут ориентироваться на разные архитектуры. Можно указать целевую архитектуру с помощью опции -A :

cmake .. -G "Visual Studio 2019" -A x64
cmake .. -G "Visual Studio 16" -A ARM
cmake .. -G "Visual Studio 16 2019" -A ARM64

В Apple генератор Xcode можно использовать для создания файлов проекта для Xcode IDE.

Некоторые IDE, такие как KDevelop4, QtCreator и CLion, имеют встроенную поддержку систем сборки на основе CMake. Эти IDE предоставляют пользовательский интерфейс для выбора базового генератора для использования, обычно это выбор между Makefile или генератором на основе Ninja .

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

При создании файлов проектов и решений Visual Studio доступны несколько других параметров, которые можно использовать при первоначальном запуске cmake(1) .

Набор инструментов Visual Studio можно указать с помощью параметра -T :

$ 
$ cmake.exe .. -G "Visual Studio 16 2019" -A x64 -T ClangCL
$ 
$ cmake.exe .. -G "Visual Studio 16 2019" -A x64 -T v120_xp

В то время как параметр -A указывает архитектуру _target_, параметр -T может использоваться для указания деталей используемой цепочки инструментов. Например, -Thost=x64 можно указать для выбора 64-битной версии инструментов хоста. Ниже показано, как использовать 64-битные инструменты, а также строить для 64-битной целевой архитектуры:

$ cmake .. -G "Visual Studio 16 2019" -A x64 -Thost=x64

Выбор генератора в смоделированном виде

Кнопка «Configure» вызывает новое диалоговое окно для выбора используемого генератора CMake.

Configuring a generator

Все генераторы, доступные в командной строке, также доступны в cmake-gui(1) .

Choosing a generator

При выборе генератора Visual Studio доступны дополнительные опции по настройке генерируемой архитектуры.

Choosing an architecture for Visual Studio generators

Установка Строить Переменные

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

Для управления сборками могут быть доступны и другие специфические для проекта переменные,такие как включение или выключение компонентов проекта.

CMake не предоставляет соглашения о том, как такие переменные именуются между различными предоставленными системами сборки, за исключением того, что переменные с префиксом CMAKE_ обычно относятся к параметрам, предоставляемым самим CMake, и не должны использоваться в сторонних параметрах, которые должны использовать свой собственный префикс. вместо. Инструмент cmake-gui(1) может отображать параметры в группах, определяемых их префиксом, поэтому третьим лицам имеет смысл убедиться, что они используют самосогласованный префикс.


© 2000–2022 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.24/guide/user-interaction/index.html


CMake

3.25

  • Creating Packages

    На данном этапе проект MathFunctions экспортирует целевую информацию,необходимую для использования другими проектами.

  • CMake Tutorial

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

  • Установка переменных в командной строке

    Переменные CMake могут быть заданы в командной строке как при создании начальной сборки:так и при последующих вызовах cmake(1):Можно использовать флаг -U

  • Использование руководства по зависимостям

    Использование готовых пакетов с помощью find_package()Проекты часто зависят от других активов и артефактов.

Эта статья находится в разработке!

Содержание

  • 1 Что это и зачем нужно
    • 1.1 Краткое описание
  • 2 Старт
  • 3 Подробное описание
    • 3.1 Указание необходимой версии cmake
    • 3.2 Название проекта
    • 3.3 Переменные
    • 3.4 Устанавливаем команды компилятору
    • 3.5 Папка с хедерами
    • 3.6 Самое важное — подключение библиотек
    • 3.7 Пример хорошего CMakeLists.txt и где он будет лежать
    • 3.8 Как создать библиотеку в поддиректории и слинковать ее с основной программой
  • 4 Как использовать CMake в связке с QtCreator
    • 4.1 Как добавить header в проект, чтобы его было видно в списке файлов

Что это и зачем нужно

CMake — кроссплатформенная автоматизированная система сборки проектов.
Непосредственно сборкой она не занимается, а только генерирует Makefile, который потом будет выполнен утилитой make.

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

Краткое описание

Если нет желания/времени/сил читать весь туториал и Вы используете какой-нибудь QtCreator (или любая другая IDE, умеющая работать с cmake), то:

  • Создайте в IDE проект под cmake
  • Найдите в папке с проектом CMakeFiles.txt
  • Пробегитесь глазами по туториалу, соотнося его с вашим CMakeFiles.txt

Про подключение библиотек рекомендуется все-таки прочитать целиком.

Старт

Предполагается, что найти и скачать сам cmake ты, %username%, в состоянии. //а если нет?

Предположим, у Вас есть исходничек «test.cpp» (// а если нет?)(А если нет, то CMake тебе трогать рано).
Для начала нужно создать файлик для cmake, который обычно называют «CMakeLists.txt», и написать туда вот это:

add_executable(test test.cpp)

Теперь запускаем (из консоли) в этой папке команду «cmake CMakeLists.txt» (аргументом можно передавать не только файл, но и директорию, в которой он лежит, тогда cmake найдет его сам).

cmake будет использовать переданный (или найденный) файл проекта (тот самый CMakeLists.txt), и в текущей директории будет создавать проект.
Проект — это много-много файлов и директорий (примечание: поэтому лучше запускать cmake из другой директории, чтобы можно было, например, быстро удалить все бинарники), из которых нас больше всего интересует Makefile.

Makefile — это файл, нужный для утилиты make.
Именно она запускает компиляторы, линковщики и прочие радости. Запускаем make в каталоге сборки (т.е. там же, где Вы запускали cmake).
В консоли вылезет примерно такой текст:

Scanning dependencies of target test
[100%] Building CXX object CMakeFiles/test.dir/test.cpp.o
Linking CXX executable test
[100%] Built target test

А у Вас в папочке появится исполняемый файл «test». Запустите, убедитесь, что это действительно то, что ожидается от компиляции файла «test.cpp».

Подробное описание

Поразбираемся с различными возможностями cmake.

Указание необходимой версии cmake

cmake_minimum_required(VERSION 2.6)

Указывайте высокую минимальную версию CMake.
Если используемая версия cmake меньше 2.6, он не захочет работать. Писать эту команду всегда — хороший стиль (cmake будет пыхтеть и обижаться, если вы не укажете версию, но собирать всё равно всё будет).

Название проекта

project(visualization)

Указывает, что этот cmake-файл является корневым для некоторого проекта. С проектами связаны определенные переменные и поведение cmake (читайте документацию).

Переменные

В cmake можно создавать текстовые переменные. Команда

set(VARIABLE The variable's value)

запишет в переменную «VARIABLE» значение «The variable’s value».
Чтобы где-либо использовать значение этой переменной, нужно написать ${VARIABLE}.

Чтобы добавить к переменной некий текст, можно сделать так:

set(VARIABLE "${VARIABLE} new text")

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

Пример коше’гного проекта со списком сорцов в отдельной переменной:

cmake_minimum_required(VERSION 2.6)

set(SOURCES test.cpp lib1.cpp lib2.cpp)

add_executable(test ${SOURCES})

Устанавливаем команды компилятору

add_definitions(-DSOME_IMPORTANT_DEFINITION)

Эта команда используется для установки дефайнов, которыe можно проверить в коде через, например, #ifdef SOME_IMPORTANT_DEFINITION.

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")

Эта команда добавит к флагам, используемым при сборке c++-кода, флаги -std=c++11 и -Wall.

Кто не знает: «-std=c++11» включает в gcc поддержку стандарта c++11, «-Wall» говорит gcc выводить все предупреждения (очень советую, помогает отловить много глупых багов и писать аккуратный код).

Если ваша версия GCC меньше, чем 4.7.0, вместо -std=c++11 нужно использовать -std=c++0x.

В GCC 4.8.0 появился флаг -std=c++1y, в котором начинают реализовывать фичи следующего стандарта.

Папка с хедерами

Допустим, Вы хотите, чтобы хедеры (файлики, подключаемые через #include) искались еще и в каталогах «headers/» и «more_headers/»:

include_directories("headers/" "more_headers/")

Надеюсь, и это понятно.

Самое важное — подключение библиотек

Научимся искать и подключать библиотеки при помощи cmake на примере Boost.
Для начала установим переменные для буста:

set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREADED ON)

Первое — мы не хотим, чтобы буст подключался к нам статически (т.е. хотим динамическую линковку). Если ты, %username%, не знаешь, что это, пока просто забей и используй этот флаг так, как написано. Но в ближайшее время узнай, о чем речь.
Второй флаг разрешает бусту внутри своих магических реализаций использовать треды для распараллеливания и прочих радостей.

Итак, мы установили флаги. Давайте найдем буст!

Допустим, нам нужны компоненты буста под названием chrono (библиотека для работы со временем) и filesystem (библиотека для работы с файловой системой):

find_package(Boost COMPONENTS chrono filesystem REQUIRED)

Win, будут искаться только нужные библиотеки, и их расположение будет записано в переменную Boost_LIBRARIES.

Опция «REQUIRED» говорит о том, что библиотека необходима проекту.
Без нее cmake решит, что отсутствие данной библиотеки — не так уж и страшно, и будет собирать дальше.

Добавим директории с хедерами буста для поиска в них хедеров:

include_directories(${Boost_INCLUDE_DIRS})

Итак, осталось найденные библиотеки подключить к исполняемому файлу.

target_link_libraries(test ${Boost_LIBRARIES})

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

Заметим, что эту команду нужно вызывать после того, как создан target сборки (через add_executable).

Пример хорошего CMakeLists.txt и где он будет лежать

Итак, полный пример использования всего этого. У нас есть некая директория (отныне считаем ее «/sources»), и в ней лежат исходники

/sources/lib1/main.cpp
/sources/lib2/main.cpp
/sources/main.cpp

В корне «/» лежит файл «/CMakeLists.txt»:

cmake_minimum_required(VERSION 2.8)
project(cmake-example)

set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREADED ON)
find_package(Boost COMPONENTS chrono filesystem REQUIRED)

set(CMAKE_CXX_FLAGS "$\$${CMAKE_CXX_FLAGS} -std=c++11 -Wall")

set(SRC_LIST lib1/main.cpp lib2/main.cpp main.cpp)

add_executable($\$${PROJECT_NAME} $\$${SRC_LIST})
target_link_libraries($\$${PROJECT_NAME} $\$${Boost_LIBRARIES})

Если Вам что-то в нём не понятно — перечитайте соответствующую информацию выше.

Создаем директорию «/build» (не «/sources/build»), переходим в нее, запускаем в ней «cmake ..».
«..» — метка родительской директории.
cmake возьмет из нее наш CMakeLists.txt и по нему создаст проект в папке «/build».
Чтобы проект собрать, запускаем «make» в той же папке «/build».

Таким образом, в корне у нас есть:

  • CMakeLists.txt
  • директория с исходниками
  • каталог сборки

Все разделено, автоматизировано и удобно.

Как создать библиотеку в поддиректории и слинковать ее с основной программой

Пусть в ./ лежит основной проект, а в ./subdir мы хотим сделать либу, а в ./build построить проект.

./subdir/CMakeLists.txt

project(MegaLibrary)
set(SOURCES lib.cpp)
set(HEADERS lib.h)
add_library(lib $\$${SOURCES} $\$${HEADERS})
target_include_directories(lib PUBLIC $\$${CMAKE_CURRENT_SOURCE_DIR})

./CMakeLists.txt

project(MainProject)
set(MAIN_PROJECT_SRC_LIST main)
# Other stuff
add_executable(main $\$${MAIN_PROJECT_SRC_LIST})
add_subdirectory(subdir)
target_link_libraries(main lib)

Теперь можно в файлах основного проекта делать #include «lib.h» (см. документацию по target_include_directories).

В ./build запускаем «cmake .. && make» и получаем собранный проект.

Как использовать CMake в связке с QtCreator

Интеграция с cmake у QtCreator не очень тесная, тем не менее, работать с ним можно.

Создаем новый проект без использования Qt, выбираем «Проект на С++ с использованием CMake». Создастся дефолтный файл сборки, который просто добавляет все исходники в директории проекта и компилирует их в один бинарник.

Если вы создали файл header.h в директорию проекта, просто строчку

add_executable($\$${PROJECT_NAME} $\$${SRC_LIST})

измените на

add_executable($\$${PROJECT_NAME} $\$${SRC_LIST} "header.h")

Введение

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

Автор: Энди Седильник, Билл Хоффман, Брэд Кинг, Кен Мартин, Александр Нойндорф.

Год: 2000

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

CMake или Make

Make (или, скорее, Makefile) — это система сборки, которая управляет компилятором и другими инструментами сборки для создания вашего кода.

CMake — это генератор систем сборки, он может создавать файлы Makefile. Поэтому, если у вас есть проект, независимый от платформы, CMake — это способ сделать его независимым от системы.

Вот преимущества CMake перед Make:

  1. Язык, используемый для написания файлов CMakeLists.txt, удобочитаем и понятен.
  2. Кроссплатформенное обнаружение системных библиотек/функций/заголовков/флагов.
  3. Легче компилировать ваши файлы в общую библиотеку независимо от платформы и в целом проще в использовании, чем Make.
  4. CMake не только полагается на «Make» для сборки проекта, но и поддерживает несколько генераторов, таких как Xcode, Eclipse, Visual Studio и т. д.

Чтобы упростить задачу, вы можете выполнить большинство функций, доступных в CMake, с помощью Make, но с ДОПОЛНИТЕЛЬНЫМИ УСИЛИЯМИ.

CMakeLists.txt и процесс компиляции

CMake задуман как кроссплатформенный диспетчер процессов сборки, поэтому он определяет собственный язык сценариев с определенным синтаксисом и встроенными функциями. Язык CMake записывается либо в CMakeLists.txt, либо файл заканчивается файлом ‹project_name›.cmake.

Процесс компиляции в CMake состоит из двух этапов:
1. Конфигурация
2. Генерация

Команды конфигурации и генерации (сборки) указываются в одном месте и полностью обрабатываются CMake.

Конфигурация: здесь выполняется сценарий CMakeLists.txt. Этот скрипт отвечает за определение целей. Здесь target может быть исполняемым файлом, библиотекой или каким-либо другим выходом конвейера сборки ( конвейер означает файл, который может помочь компилятору искать исполняемые файлы/библиотеки для ссылки на этот проект, который может присутствовать в системной библиотеке, пока вы пытаетесь для кросс-компиляции это будет видно).

Если шаг настройки завершится успешно — это означает, что CMakeLists.txt завершен без ошибок — CMake сгенерирует конвейер сборки, используя цели, определенные сценарием ( CMakeLists.txt ). Тип создаваемого конвейера сборки зависит от типа используемого генератора.
CmakeCache.txt создается таким образом, что он может помочь сократить время создания конвейера в будущем.

Генерация. Этот этап предназначен для создания исполняемого файла в зависимости от целей, созданных на этапе конфигурации.

Вышеупомянутые детали могут быть визуализированы из рисунка ниже.

Иерархия файлов для CMake

Мы попробуем собрать cmakeSampleProject, который пытается выполнять сложение и умножение (определяемые пользователем функции).

Примечание:
1. Наличие файла сценария CmakeLists.txt в каждой папке обязательно.
2. →: представлять папки
3. ->: представлять файлы

Команды для настройки и сборки

Теперь давайте посмотрим, как создать и запустить исполняемый файл для cmakeSampleProject.

$ cmake -H. -Bbuild
Создает файлы конфигурации CMake внутри папки build.

$ cmake — build build — -j3
Создаст выходной файл SampleProjectOutput в папке сборки.

$ ./build/SampleProjectOutput
Hello World
Для запуска исполняемого файла.

Переменные CMake

Вот некоторые глобальные переменные CMake, которые вы должны знать

  1. CMAKE_BINARY_DIR: если вы создаете исходный код, это то же самое, что и CMAKE_SOURCE_DIR, в противном случае это каталог верхнего уровня вашего дерева сборки.
  2. CMAKE_SOURCE_DIR: это каталог, из которого был запущен CMake, т. е. исходный каталог верхнего уровня.
  3. EXECUTABLE_OUTPUT_PATH: установите эту переменную, чтобы указать общее место, куда CMake должен помещать все исполняемые файлы (вместо CMAKE_CURRENT_BINARY_DIR).
    Пример: SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
  4. LIBRARY_OUTPUT_PATH: установите эту переменную, чтобы указать общее место, куда CMake должен поместить все библиотеки (вместо CMAKE_CURRENT_BINARY_DIR).
    Пример: SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
  5. PROJECT_NAME: имя проекта, заданное командой PROJECT().
  6. PROJECT_SOURCE_DIR: содержит полный путь к корню исходного каталога вашего проекта, т. е. к ближайшему каталогу, где CMakeLists.txt содержит команду PROJECT().

Команды Cmake

Это некоторые наиболее часто используемые команды CMake,

  1. cmake_minimum_required (): эта команда гарантирует, что для процесса сборки требуется минимальная версия CMake.
    В случае сбоя команды возникает исключение.
    Пример: cmake_minimum_required (ВЕРСИЯ 2.6)
  2. project (): эта команда используется для присвоения имени проекту, над которым вы работаете.
    Пример: проект (учебник)
  3. add_executable(): здесь вы говорите CMake, чтобы начать процесс компиляции.
    В приведенном ниже примере мы говорим о компиляции с помощью test.cpp.
    Пример: add_executable (Учебник tutorial.cpp)
  4. configure_file (): копирует файл ‹входной› в файл ‹выходной› и заменяет значения переменных, указанные в содержимом файла.
    синтаксис: configure_file(‹input› ‹output›)
    Пример: configure_file («${PROJECT_SOURCE_DIR}/TutorialConfig.h.in» «${PROJECT_BINARY_DIR}/TutorialConfig.h»)
  5. option (): предоставляет параметр, который пользователь может выбрать по желанию.
    синтаксис: option(‹option_variable› «строка справки, описывающая параметр» [начальное значение])
    В приведенном ниже например, мы говорим о компиляции с помощью test.cpp.
    Ex: опция (USE_MYMATH «Использовать математическую реализацию, предоставленную учебником» ON)
  6. include_directories(): загружает и запускает CMake с файлами, которые требуется включить.
    Пример: include_directories («${PROJECT_SOURCE_DIR}/MathFunctions»).
  7. add_subdirectory(): добавляет запрошенный подкаталог в сборку.
    Пример: add_subdirectory (MathFunctions).
  8. set (): используется для установки заданного значения переменной CMake/cache/environment.
    Пример: set (Tutorial_VERSION_MAJOR 1)
  9. target_link_libraries (): связывает библиотеки с исполняемым файлом.
    Пример: target_link_libraries (учебник ${EXTRA_LIBS})
  10. check_function_exists (): используется для проверки существования системной функции, если она присутствует, можно установить макрос.
    Пример: check_function_exists (printf HAVE_PRINTF)
  11. install (): указывает правила, которым необходимо следовать во время установки, может быть выполнено извне с помощью команды $ make install

Ex:

установить (TARGETS Tutorial DESTINATION bin)

установить (ФАЙЛЫ «${PROJECT_BINARY_DIR}/TutorialConfig.h» НАЗНАЧЕНИЕ включают)

В приведенном выше примере TARGETS используется для исполняемых файлов, а для библиотечных файлов используется FILES.

Параметры CMake:

Вот некоторые варианты, используемые наиболее часто,

  1. — построить ‹dir›: построить бинарное дерево проекта, сгенерированное CMake.
  2. -i: запуск в режиме мастера, режим мастера запускает CMake в интерактивном режиме без графического интерфейса.
  3. -D ‹var›:‹type›=‹value›: создать запись кэша cmake. При первом запуске cmake в пустом дереве сборки создается кэш CMake.
    Он также используется для перезаписи значений переменных на этапе настройки.
  4. -H: распечатать информацию об использовании и выйти.
  5. -B: указывает каталог сборки.

Кросс-компиляция с помощью CMake:

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

Пример: $ cmake -H. -Bcross_build -DCMAKE_CXX_FLAGS=»-g -std=c++11 -Wall» -DCMAKE_TOOLCHAIN_FILE=./sample_cross_compile_toolchain_file.cmake -DCMAKE_BUILD_TYPE=Debug

В приведенном выше примере мы перезаписываем определенный «CMAKE_TOOLCHAIN_FILE» на «./sample_cross_compile_toolchain_file.cmake», используя параметр «-D».

Практические примеры:

Эта ссылка ведет к учебнику Git Hub CMake, следуйте инструкциям, приведенным в разделе «Практические примеры» на этой вики-странице.

Ссылки:

Некоторое время назад мы познакомились с Autotools. Несмотря на то, что Autotools до сих пор используется во многих известных проектах с открытым исходным кодом, инструмент этот трудно назвать особо удобным. Кроме того, нормально работает он только в *nix системах, а в каком-нибудь Windows пользоваться Autotools, скажем так, весьма непросто. В общем, Autotools — это легаси, и нормальные программисты в наше время пытаются использовать CMake или, например, SCons. В этой заметке мы познакомимся с CMake.

Говоря простыми словами, CMake — это такая штука, в которой вы описываете проект, а она вам генерирует Makefile’ы в *nix системах, проекты Visual Studio под Windows, файлы конкретных редакторов и IDE, например Sublime Text, Code::Blocks, Eclipse или KDevelop, и так далее. Несмотря на спорный в некоторых моментах синтаксис, в последнее время CMake становится стандартом де-факто в мире C/C++. В частности, CMake используется в LLVM, Qt, MariaDB, Blender, KiCad, GNU Radio и ряде других проектов. Кроме того, в CLion, IDE для C/C++ от компании JetBrains, по умолчанию также создаются проекты, основанные на CMake.

Примечание: В этом контексте вас может заинтересовать заметка Как править в CLion код любых проектов на С++, даже тех, в которых не используется CMake.

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

cmake_minimum_required(VERSION 3.1)

# так пишутся комментарии

project(project_name)

find_library(PTHREAD_LIBRARY pthread)
find_library(PCRE_LIBRARY pcre)

include_directories(include)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED on)
set(CMAKE_CXX_FLAGS «${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror»)

add_executable(main src/Main.cpp src/HttpServer.cpp)

target_link_libraries(main ${PTHREAD_LIBRARY} ${PCRE_LIBRARY})

Хочется надеяться, какая строчка здесь что означает, пояснять не нужно. Затем исходники складываются в каталог src, а заголовочные файлы — в каталог include. Для сборки проекта говорим:

mkdir build
cd build
cmake ..
make

Просто, не правда ли?

Помимо приведенного выше find_library в CMake есть ряд скриптов для подключения конкретных библиотек. В частности, подключение OpenGL осуществляется как-то так:

find_package(OpenGL REQUIRED)

include_directories(${OPENGL_INCLUDE_DIR})

target_link_libraries(main ${OPENGL_LIBRARY} ${CMAKE_DL_LIBS})

CMake можно указать конкретный тип Makefile’ов, которые вы хотите получить на выходе:

cmake -G «Unix Makefiles» ..
cmake -G «MinGW Makefiles» ..
# для просмотра списка всех доступных генераторов:
cmake -G

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

cmake -G Ninja ..
ninja -j1

Выбор между отладочной и релизной сборкой осуществляется так:

cmake -DCMAKE_BUILD_TYPE=Release -G Ninja ..
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -G Ninja ..
cmake -DCMAKE_BUILD_TYPE=MinSizeRel -G Ninja ..
# Debug используется по умолчанию
cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja ..

Вместо запуска напрямую make или ninja можно сказать что-то вроде:

cmake —build . —config Release —target main

Можно выбрать конкретный компилятор для сборки проекта

cmake -DCMAKE_C_COMPILER=`which clang` \
  -DCMAKE_CXX_COMPILER=`which clang++` -G Ninja ..

… а также указать дополнительные флаги компиляции:

cmake -DCMAKE_C_FLAGS=«-O0 -g» -DCMAKE_CXX_FLAGS=«-O0 -g» ..

Например, для определения степени покрытия кода тестами при помощи lcov нужно сказать что-то вроде:

cmake -DCMAKE_C_FLAGS=«-O0 -g -fprofile-arcs -ftest-coverage» \
  -DCMAKE_EXE_LINKER_FLAGS=«-lgcov» ..

В мире C/C++ нередко бывает, что сторонние библиотеки, использующие CMake, подключаются к проекту при помощи сабмодулей Git. Подключение таких библиотек к проекту осуществляется довольно просто:

cmake_minimum_required(VERSION 2.8)

project(c-algorithms-examples)

include_directories(deps/algorithms/include)
add_subdirectory(deps/algorithms/src)

add_executable(rbtree_example rbtree_example.c)
target_link_libraries(rbtree_example CAlgorithms)

В свою очередь, у библиотеки файл src/CMakeList.txt должен быть примерно таким:

cmake_minimum_required(VERSION 2.8)

project(c-algorithms)

add_library(CAlgorithms STATIC
  struct/ilist.c
  struct/rbtree.c
  struct/htable.c
  common/utils.c
)

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

Например, в корне библиотеки CMakeList.txt может быть таким:

cmake_minimum_required(VERSION 2.8)

project(c-algorithms-root)

enable_testing()

include_directories(include)

add_subdirectory(src)
add_subdirectory(test)

Непосредственно тесты добавляются в проект следующим образом:

cmake_minimum_required(VERSION 2.8)

project(c-algorithms-struct-tests)

set(CMAKE_C_FLAGS «${CMAKE_C_FLAGS} -O0 -g»)

add_executable(test_htable test_htable.c)
target_link_libraries(test_htable CAlgorithms)

add_executable(test_rbtree test_rbtree.c)
target_link_libraries(test_rbtree CAlgorithms)

add_test(test_htable «./test_htable»)
add_test(test_rbtree «./test_rbtree»)

Запуск тестов осуществляется простой командой:

make test
# или, с включением отладочного вывода:
make test ARGS=«-V»
# или, если используете Ninja:
ninja test

… выполненной в каталоге build. Если вас интересует тема написания модульных тестов на C++, она более подробно раскрыта в заметке Тестирование кода на C++ с помощью Google Test.

Если же вы используете какой-нибудь PyTest, просто допишите в CMakeList.txt что-то вроде:

find_package(PythonInterp REQUIRED)

enable_testing()

add_test(NAME python_test
         COMMAND py.test —capture=no ${CMAKE_SOURCE_DIR}/tests/run.py)

Вывод тестов пишется в файл Testing/Temporary/LastTest.log. Кстати, подробности о переменных окружения, доступных в CMake, таких, как CMAKE_SOURCE_DIR, можно найти здесь.

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

cmake -DLLDB_DISABLE_CURSES:BOOL=TRUE …
cmake -DASSIMP_BUILD_ASSIMP_TOOLS=OFF …

Опции обычно описывают в документации, но в крайнем случае их можно посмотреть и через curses-интерфейс:

В рамках одного поста, конечно, не представляется возможным рассмотреть все возможности CMake. Однако представленной выше информации вам должно вполне хватить в 90% случаев. Полноценные рабочие примеры использования CMake вы найдете, например, в этом, этом, а также в этом репозиториях на GitHub. Примеры использования опций и условных операторов можно найти в репозиториях уже упомянутых Assimp и LLDB. Ну и, конечно же, массу полезного вы найдете на официальном сайте CMake.

А пользуетесь ли вы CMake и если да, используете ли какие-то его возможности, о которых не было рассказано выше?

Метки: C/C++, Кроссплатформенность.

Понравилась статья? Поделить с друзьями:
  • Модульное оригами лебедь пошаговая инструкция маленький
  • Типовая производственная инструкция для стропальщика по новым правилам
  • Эритромицин инструкция уколы по применению взрослым
  • Препарат каратэ зеон инструкция по применению
  • Для политики военного коммунизма характерно введение централизованного руководства