Ответ 1
При написании сценариев CMake нужно много знать о синтаксисе и о том, как использовать переменные в CMake.
Синтаксис
Строки с использованием set()
:
-
set(MyString "Some Text")
-
set(MyStringWithVar "Some other Text: ${MyString}")
-
set(MyStringWithQuot "Some quote: \"${MyStringWithVar}\"")
Или со string()
:
-
string(APPEND MyStringWithContent " ${MyString}")
Списки, использующие set()
:
-
set(MyList "a" "b" "c")
-
set(MyList ${MyList} "d")
Или лучше со list()
:
-
list(APPEND MyList "a" "b" "c")
-
list(APPEND MyList "d")
Списки имен файлов:
-
set(MySourcesList "File.name" "File with Space.name")
-
list(APPEND MySourcesList "File.name" "File with Space.name")
-
add_excutable(MyExeTarget ${MySourcesList})
Документация
- CMake/Синтаксис языка
- CMake: переменные списки строк
- CMake: полезные переменные
- Команда CMake
set()
- CMake
string()
команда - Команда CMake
list()
- Cmake: выражения генератора
Область действия или "Какое значение имеет моя переменная?"
Сначала есть "Нормальные переменные" и вещи, которые вы должны знать об их области действия:
- Нормальные переменные видны в
CMakeLists.txt
они установлены, и все, что оттудаadd_subdirectory()
,include()
,macro()
иfunction()
). -
add_subdirectory()
иfunction()
являются специальными, потому что они открывают свою собственную область видимости.- Значения переменных
set(...)
там видны только там, и они копируют все нормальные переменные уровня области действия, из которого они вызываются (называемые родительской областью действия). - Поэтому, если вы находитесь в подкатегории -D или в функции, вы можете изменить уже существующую переменную в родительской области с помощью
set(... PARENT_SCOPE)
- Вы можете использовать это, например, в функциях, передавая имя переменной в качестве параметра функции. В качестве примера можно привести
function(xyz _resultVar)
которой установлено значениеset(${_resultVar} 1 PARENT_SCOPE)
- Значения переменных
- С другой стороны, все, что вы устанавливаете в скриптах
include()
илиmacro()
будет изменять переменные непосредственно в области их вызова.
Во-вторых, это "Кэш глобальных переменных". Что нужно знать о кеше:
- Если в текущей области не определена нормальная переменная с заданным именем, CMake будет искать соответствующую запись в Cache.
- Значения кэша хранятся в файле
CMakeCache.txt
в вашей директории двоичного вывода. -
Значения в Cache могут быть изменены в приложении CMake GUI перед их генерацией. Поэтому они - по сравнению с обычными переменными - имеют
type
иdocstring
. Обычно я не использую графический интерфейс, поэтому я используюset(... CACHE INTERNAL "")
для установки моих глобальных и постоянных значений.Обратите внимание, что тип переменной
INTERNAL
кеша подразумеваетFORCE
-
В сценарии CMake вы можете изменять существующие записи в кэше, только если используете синтаксис
set(... CACHE... FORCE)
. Это поведение используется, например, самим CMake, потому что обычно оно не заставляет сами записи Cache, и поэтому вы можете предварительно -D определить его с другим значением. - Вы можете использовать командную строку для установки записей в Cache с помощью синтаксиса
cmake -D var:type=value
, простоcmake -D var=value
илиcmake -C CMakeInitialCache.cmake
. - Вы можете сбросить записи в Cache с помощью
unset(... CACHE)
.
Кэш является глобальным, и вы можете установить его практически в любом месте ваших сценариев CMake. Но я бы порекомендовал вам дважды подумать о том, где использовать переменные Cache (они глобальные и постоянные). Обычно я предпочитаю set_property(GLOBAL PROPERTY...)
и set_property(GLOBAL APPEND PROPERTY...)
определять свои собственные непостоянные глобальные переменные.
Ловушки переменных и "Как отладить изменения переменных?"
Чтобы избежать ошибок, вы должны знать следующее о переменных:
- Локальные переменные скрывают кэшированные переменные, если оба имеют одинаковое имя
- Команды
find_...
- в случае успеха - записывают свои результаты в виде кэшированных переменных, "чтобы ни один вызов не выполнялся снова" - Списки в CMake - это просто строки с разделителями точек с запятой, поэтому кавычки важны
-
set(MyVar abc)
-"a;b;c"
аset(MyVar "abc")
-"abc"
- Рекомендуется всегда использовать кавычки с одним исключением, если вы хотите предоставить список в виде списка
- Обычно предпочитают команду
list()
для обработки списков
-
- Весь объем вопроса описан выше. Особенно рекомендуется использовать
functions()
вместоmacros()
потому что вы не хотите, чтобы ваши локальные переменные отображались в родительской области. - Многие переменные, используемые CMake, устанавливаются с помощью вызовов
project()
иenable_language()
. Поэтому может быть важно установить некоторые переменные перед использованием этих команд. - Переменные среды могут отличаться от того, где CMake сгенерировал среду make, и когда файлы make используются.
- Изменение переменной среды не запускает процесс генерации заново.
- Особенно сгенерированная среда IDE может отличаться от вашей командной строки, поэтому рекомендуется переносить переменные среды во что-то, что кэшируется.
Иногда помогают только отладочные переменные. Следующее может помочь вам:
- Просто используйте старый стиль отладки
printf
с помощью командыmessage()
. Есть также некоторые готовые к использованию модули, поставляемые с самим CMake: CMakePrintHelpers.cmake, CMakePrintSystemInformation.cmake - Посмотрите в файл
CMakeCache.txt
в вашей директории двоичного вывода. Этот файл генерируется даже в случае сбоя фактической генерации среды make. - Используйте variable_watch(), чтобы увидеть, где ваши переменные читаются/записываются/удаляются.
- Посмотрите в свойствах каталога CACHE_VARIABLES и VARIABLES
- Вызовите
cmake --trace...
чтобы увидеть полный процесс разбора CMake. Такого рода последний резерв, потому что он генерирует много продукции.
Специальный синтаксис
- Переменные среды
- Вы можете читать
$ENV{...}
и записывать переменные окруженияset(ENV{...}...)
- Вы можете читать
- Выражения генератора
- Выражения генератора
$<...>
оцениваются только тогда, когда генератор CMake записывает среду make (это сравнение с обычными переменными, которые заменяются парсером "на месте") - Очень удобно, например, в командных строках компилятора/компоновщика и в средах с множественной конфигурацией -C
- Выражения генератора
- Рекомендации
- С
${${...}}
вы можете давать имена переменных в переменной и ссылаться на ее содержимое. - Часто используется при указании имени переменной в качестве параметра функции/макроса.
- С
- Постоянные значения (см. Команду
if()
)- С помощью
if(MyVariable)
вы можете напрямую проверить переменную на true/false (здесь нет необходимости для вложенных${...}
) - Истинно, если константа равна
1
,ON
,YES
,TRUE
,Y
или ненулевое число. - False, если константа
0
,OFF
,NO
,FALSE
,N
,IGNORE
,NOTFOUND
, пустая строка или заканчивается суффиксом-NOTFOUND
. - Этот синтаксис часто используется для чего-то вроде
if(MSVC)
, но он может сбивать с толку тех, кто не знает этого ярлыка синтаксиса.
- С помощью
- Рекурсивные замены
- Вы можете создавать имена переменных, используя переменные. После того, как CMake подставил переменные, он снова проверит, является ли результат самой переменной. Это очень мощная функция, используемая в самом CMake, например, как
set(CMAKE_${lang}_COMPILER...)
шаблоновset(CMAKE_${lang}_COMPILER...)
- Но учтите, что это может вызвать головную боль в командах
if()
. Вот пример, гдеCMAKE_CXX_COMPILER_ID
равен"MSVC"
аMSVC
равен"1"
:-
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
имеет значение true, поскольку оно оценивается какif("1" STREQUAL "1")
-
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
имеет значение false, поскольку оно оценивается какif("MSVC" STREQUAL "1")
- Таким образом, лучшим решением здесь будет - см. Выше - напрямую проверить,
if(MSVC)
-
- Хорошей новостью является то, что это было исправлено в CMake 3.1 с введением политики CMP0054. Я бы рекомендовал всегда устанавливать в
cmake_policy(SET CMP0054 NEW)
значение "интерпретировать аргументыif()
как переменные или ключевые слова в кавычках".
- Вы можете создавать имена переменных, используя переменные. После того, как CMake подставил переменные, он снова проверит, является ли результат самой переменной. Это очень мощная функция, используемая в самом CMake, например, как
- Команда
option()
- В основном это просто кэшированные строки, которые могут быть только
ON
илиOFF
и они допускают некоторую специальную обработку, например, зависимости - Но имейте в виду, не путайте
option
с командойset
. Значение, данноеoption
на самом деле является только "начальным значением" (переданным один раз в кэш на первом этапе настройки) и впоследствии должно быть изменено пользователем через графический интерфейс CMake.
- В основном это просто кэшированные строки, которые могут быть только