Главная » Хабрахабр » [Из песочницы] Полное руководство по CMake. Часть первая: Синтаксис

[Из песочницы] Полное руководство по CMake. Часть первая: Синтаксис

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

В Вашем распоряжении, с функциональной стороны, есть лишь команды, которые могут образовываться в довольно сложные конструкции. Язык CMake, будучи транслированным в нативный файл сборки (например, Makefile или Ninja), определяет процесс всего управления проектом. С них мы и начнём.

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

  1. Установите программу CMake с официального сайта
  2. Создайте на рабочем столе текстовый файл CMakeLists.txt
  3. Добавьте в начало файла cmake_minimum_required(VERSION 3.0)
  4. Скопируйте туда исходные тексты необходимых примеров
  5. Если у Вас установлен консольный CMake, то запустить скрипт можно с помощью команды "cmake .". Если у Вас графический CMake, то в первые два верхних поля приложения вбейте адрес Вашего рабочего стола, затем нажмите кнопку Generate. Результат появится в нижнем текстовом поле.

Чтобы вызвать команду, необходимо написать её имя, а затем передать ей обрамлённые в круглые скобки аргументы, отделённые символами пробелов. Команды в CMake подобны функциям во многих языках программирования. В приведённом примере команде message передаются шесть аргументов для вывода в консоль:

# Напечатает в консоль "CMake is the most powerful buildsystem!"
message("CMake " "is " "the " "most " "powerful " "buildsystem!")

Необрамлённые аргументы не позволяют производить подобных вещей и не могут включать в себя символы ()#"\ и пробелы, однако более удобны для использования. Аргументы, обрамлённые в двойные кавычки, позволяют внутри себя совершать экранирование и подстановку переменных. Пример:

# Напечатает "Hello, my lovely CMake", один таб и "!":
message("Hello, my lovely CMake\t!") # Напечатает "Hello,_my_lovely_CMake!" без пробелов:
message(Hello,_my_lovely_CMake!)

Об этой особенности поведовали в комментариях. Стоит отметить, что аргумент Walk;around;the;forest расширится до списка Walk around the forest, так как любой необрамлённый аргумент автоматически расширяется до списка значений (при условии, что значения изначального аргумента разделены символами точки с запятой), но с обрамлённым в двойные кавычки аргументом такая трансформация не происходит.

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

Получить значение переменной можно по конструкции $. Переменные можно определить путём вызова команды set, а удалить вызовом unset. Пример:

# Определить переменную VARIABLE со значением "Mr. Thomas":
set(VARIABLE "Mr. Thomas") # Напечает "His name is: Mr. Thomas":
message("His name is: " ${VARIABLE}) # Удалить переменную VARIABLE:
unset(VARIABLE)

Логические выражения используются при проверки условий и могут принимать одно из двух значений: правда или ложь. Прежде чем приступать к изучению условных операторов и циклических конструкций, необходимо понимать работу логических выражений. Выражение 88 EQUAL 88 обратится в правду, 63 GREATER 104 обратится в ложь. Например, выражение 52 LESS 58 обратится в правду, так как 52 < 58. Полный список логических выражений можно посмотреть тут. Сравнивать можно не только числа, но и строки, версии, файлы, принадлежность к списку и регулярные выражения.

В данном примере сработает лишь первый условный оператор, который проверяет, что 5 > 1. Условные операторы в CMake работают в точности как в других языках программирования. Блоки команд elseif(5 LESS 1) и else() необязательны, а endif() обязательна и сигнализирует о завершении предыдущих проверок. Второе и третье условия ложны, так как 5 не может быть меньше или равняться одному.

# Напечатает "Of course, 5 > 1!":
if(5 GREATER 1) message("Of course, 5 > 1!")
elseif(5 LESS 1) message("Oh no, 5 < 1!")
else() message("Oh my god, 5 == 1!")
endif()

В приведённом примере устанавливается значение переменной VARIABLE в Airport, а затем четыре вложенные команды последовательно исполняются пока значение переменной VARIABLE будет равняться Airport. Циклы в CMake подобны циклам других языков программирования. Команда endwhile() сигнализирует о завершении списка вложенных в цикл команд. Последняя четвёртая команда set(VARIABLE "Police station") устанавливает значение проверяемой переменной в Police station, поэтому цикл сразу остановится, не дойдя до второй итерации.

# Напечатает в консоль три раза "VARIABLE is still 'Airport'":
set(VARIABLE Airport)
while(${VARIABLE} STREQUAL Airport) message("VARIABLE is still '${VARIABLE}'") message("VARIABLE is still '${VARIABLE}'") message("VARIABLE is still '${VARIABLE}'") set(VARIABLE "Police station")
endwhile()

Когда значений в списке не остаётся, то цикл завершает своё выполнение. Данный пример цикла foreach работает следующим образом: на каждой итерации данного цикла переменной VARIABLE присваивается следующее значение из списка Give me the sugar please!, а затем исполняется команда message(${VARIABLE}), которая выводит текущее значение переменной VARIABLE. Команда endforeach() сигнализирует о завершении списка вложенных в цикл команд.

# Напечатает "Give me the sugar please!" с новых строк:
foreach(VARIABLE Give me the sugar please!) message(${VARIABLE})
endforeach()

Первый цикл в данном примере на место списка генерирует целые числа от 0 до 10, второй цикл генерирует в диапазоне от 3 до 15, а третий цикл работает в сегменте от 50 до 90, но с шагом 10. Существуют ещё 3 формы записи цикла foreach.

# Напечатает "0 1 2 3 4 5 6 7 8 9 10" с новых строк:
foreach(VARIABLE RANGE 10) message(${VARIABLE})
endforeach() # Напечатает "3 4 5 6 7 8 9 10 11 12 13 14 15" с новых строк:
foreach(VARIABLE RANGE 3 15) message(${VARIABLE})
endforeach() # Напечатает "50 60 70 80 90" с новых строк:
foreach(VARIABLE RANGE 50 90 10) message(${VARIABLE})
endforeach()

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

# Определение функции "print_numbers":
function(print_numbers NUM1 NUM2 NUM3) message(${NUM1} " " ${NUM2} " " ${NUM3})
endfunction() # Определение макроса "print_words":
macro(print_words WORD1 WORD2 WORD3) message(${WORD1} " " ${WORD2} " " ${WORD3})
endmacro() # Вызов функции "print_numbers", которая напечатает "12 89 225":
print_numbers(12 89 225) # Вызов макроса "print_words", который напечатает "Hey Hello Goodbye":
print_words(Hey Hello Goodbye)

Параметры видимы лишь определяемой функции, значит вне функции доступ к её параметрам мы получить не можем. Команда function первым аргументов принимает имя будущей функции, а остальные аргументы — это имена параметров, с которыми можно работать как с обычными переменными. Более того, все другие переменные, определяемые и переопределяемые внутри функции, видны лишь ей самой.

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

Как отметили в комментариях, макросы в CMake подобны макросам в препроцессоре языка Си: если в тело макроса поместить команду return(), то произойдёт выход из вызвавшей функции (или из всего скрипта), что демонстрирует данный пример:

# Определить макрос, содержащий команду выхода:
macro(demonstrate_macro) return()
endmacro() # Определить функцию, вызывающую предыдущий макрос:
function(demonstrate_func) demonstrate_macro() message("The function was invoked!")
endfunction() # Напечатает "Something happened with the function!"
demonstrate_func()
message("Something happened with the function!")

В приведённом выше примере функция demonstrate_func не успеет напечатать сообщение The function was invoked!, так как прежде, на место вызова макроса demonstrate_macro будет подставлена и выполнена команда выхода.

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

PARENT_SCOPE). Как упомянули в комментариях, переменные можно определять в "родительской" области видимости с помощью команды set(VARIABLE ... Данный пример демонстрирует эту особенность:

# Функция, определяющая переменную "VARIABLE" со значением
# "In the parent scope..." в родительской области видимости:
function(demonstrate_variable) set(VARIABLE "In the parent scope..." PARENT_SCOPE)
endfunction() # Определить переменную "VARIABLE" в текущей области видимости:
demonstrate_variable() # Теперь возможно получить к переменной "VARIABLE" доступ:
message("'VARIABLE' is equal to: ${VARIABLE}")

Если из определения переменной VARIABLE убрать PARENT_SCOPE, то переменная будет доступна лишь функции demonstrate_variable, а в глобальной области видимости она примет пустое значение.

Следующая статья выйдет примерно через пару дней и будет вводить в использование системы сборки CMake. На этом синтаксис языка CMake заканчивается. До скорых встреч!


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

В России приступили к тестированию отечественного нейроинтерфейса «Нейрочат»

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

Зрители не могут отличить нативную картинку 4K от интерполяции

Такие выводы можно сделать из результатов российского исследования, проведённого холдингом «Ромир». Человеческого зрения недостаточно, чтобы отличить настоящее видео 4K от картинки, которую получили из изображения HDTV с помощью интерполяции. Опрошенным показывали на телеэкране фрагменты двух видеороликов и спрашивали о восприятии ...