Какой синтаксис CMake для установки и использования переменных?

168

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

Какой синтаксис для установки и использования переменных в CMake?

CivFan
источник

Ответы:

281

При написании сценариев 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})

Документация

Область действия или «Какое значение имеет моя переменная?»

Сначала есть «Нормальные переменные» и вещи, которые вы должны знать об их области действия:

  • Нормальные переменные видимы CMakeLists.txtони установлены в и все , называется оттуда ( add_subdirectory(), include(), macro()и function()).
  • add_subdirectory()И function()команды являются особенными, потому что они открывают, свои собственные рамки.
    • Значения переменных set(...)там видны только там, и они копируют все нормальные переменные уровня области действия, из которого они вызываются (называемые родительской областью действия).
    • Поэтому, если вы находитесь в подкаталоге или функции, вы можете изменить уже существующую переменную в родительской области с помощью set(... PARENT_SCOPE)
    • Вы можете использовать это, например, в функциях, передавая имя переменной в качестве параметра функции. Примером может быть function(xyz _resultVar)установкаset(${_resultVar} 1 PARENT_SCOPE)
  • С другой стороны, все, что вы устанавливаете include()или macro()скрипты, изменяет переменные непосредственно в области, откуда они вызываются.

Во-вторых, это «Кэш глобальных переменных». Что нужно знать о кеше:

  • Если в текущей области не определена нормальная переменная с заданным именем, CMake будет искать соответствующую запись в Cache.
  • Значения кэша хранятся в CMakeCache.txtфайле в вашем каталоге двоичного вывода.
  • Значения в Cache могут быть изменены в приложении с графическим интерфейсом CMake перед их генерацией. Поэтому они - по сравнению с обычными переменными - имеют a typeи a docstring. Я обычно не использую графический интерфейс, поэтому я использую set(... CACHE INTERNAL "")для установки своих глобальных и постоянных значений.

    Обратите внимание, что INTERNALтип переменной кеша подразумеваетFORCE

  • В сценарии CMake вы можете изменять существующие записи в кэше, только если используете set(... CACHE ... FORCE)синтаксис. Это поведение используется, например, самим CMake, потому что обычно оно не заставляет сами записи Cache, и поэтому вы можете предварительно определить его с другим значением.

  • Вы можете использовать командную строку для установки записей в 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 a b c)есть "a;b;c"и set(MyVar "a b c")есть"a b c"
    • Рекомендуется всегда использовать кавычки с одним исключением, если вы хотите предоставить список в виде списка
    • Обычно предпочитают list()команду для обработки списков
  • Весь объем вопроса описан выше. Особенно рекомендуется использовать functions()вместо, macros()потому что вы не хотите, чтобы ваши локальные переменные отображались в родительской области видимости.
  • Много переменных , используемой CMake устанавливается с project()и enable_language()вызовами. Поэтому может быть важно установить некоторые переменные перед использованием этих команд.
  • Переменные окружения могут отличаться от того, где CMake сгенерировал среду make, и когда файлы make используются.
    • Изменение переменной среды не запускает процесс генерации заново.
    • Особенно сгенерированная среда IDE может отличаться от вашей командной строки, поэтому рекомендуется переносить переменные среды в нечто, что кэшируется.

Иногда помогают только отладочные переменные. Следующее может помочь вам:

  • Просто используйте старый printfстиль отладки с помощью message()команды. Есть также некоторые готовые к использованию модули, поставляемые с самим CMake: CMakePrintHelpers.cmake , CMakePrintSystemInformation.cmake
  • Посмотрите в CMakeCache.txtфайл в вашей директории двоичного вывода. Этот файл создается даже в случае сбоя фактической генерации вашей среды создания.
  • Используйте variable_watch (), чтобы увидеть, где ваши переменные читаются / записываются / удаляются.
  • Посмотрите в свойствах каталога CACHE_VARIABLES и VARIABLES
  • Позвоните, cmake --trace ...чтобы увидеть полный процесс разбора CMake. Это своего рода последний резерв, потому что он генерирует много продукции.

Специальный синтаксис

  • Переменные среды
    • Вы можете читать $ENV{...}и записывать set(ENV{...} ...)переменные окружения
  • Выражения генератора
    • Выражения генератора $<...>оцениваются только тогда, когда генератор CMake записывает среду make (это сравнение с обычными переменными, которые заменяются парсером "на месте")
    • Очень удобно, например, в командных строках компилятора / компоновщика и в средах с несколькими конфигурациями
  • Ссылки
    • С помощью ${${...}}вы можете дать имена переменных в переменной и ссылаться на ее содержимое.
    • Часто используется при указании имени переменной в качестве параметра функции / макроса.
  • Постоянные значения (см. if()Команду)
    • С помощью if(MyVariable)вы можете напрямую проверить переменную на true / false (здесь нет необходимости для включения ${...})
    • Правда , если постоянная 1, ON, YES, TRUE, Y, или ненулевое число.
    • Ложная если постоянный 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, пустая строка, или заканчивается в суффиксе -NOTFOUND.
    • Этот синтаксис часто используется для чего-то подобного if(MSVC), но он может сбивать с толку тех, кто не знает этого ярлыка синтаксиса.
  • Рекурсивные замены
    • Вы можете создавать имена переменных, используя переменные. После того, как CMake подставил переменные, он снова проверит, является ли результат самой переменной. Это очень мощная функция, используемая в самом CMake, например, в качестве шаблона.set(CMAKE_${lang}_COMPILER ...)
    • Но учтите, что это может вызвать головную боль в if()командах. Вот пример, где CMAKE_CXX_COMPILER_IDесть "MSVC"и MSVCесть "1":
      • if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") верно, потому что это оценивает if("1" STREQUAL "1")
      • if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") ложно, потому что это оценивает if("MSVC" STREQUAL "1")
      • Таким образом, лучшим решением здесь будет - см. Выше - напрямую проверить if(MSVC)
    • Хорошей новостью является то, что это было исправлено в CMake 3.1 с введением политики CMP0054 . Я бы порекомендовал всегда устанавливать cmake_policy(SET CMP0054 NEW)«интерпретировать if()аргументы как переменные или ключевые слова без кавычек».
  • option()команда
    • В основном это только кэшированные строки, которые могут быть ONили, OFFи они допускают некоторую специальную обработку, например, зависимости
    • Но имейте в optionвиду , не путайте с setкомандой. Присвоенное значение на optionсамом деле является только «начальным значением» (переданным один раз в кэш на первом этапе настройки) и впоследствии должно быть изменено пользователем через графический интерфейс CMake .

Ссылки

Florian
источник
Когда я использую if ("${MyString}" ...)я вижу предупреждение: Policy CMP0054 is not set: Only interpret if() arguments as variables or keywords when unquoted. См. Например, Build 367 . Любые идеи?
jww
И удаление кавычек ${MyString}приводит к куче ошибок для «если даны аргументы ...», таких как ошибка CMake рядом с if: «если даны аргументы», за которыми следуют паратезы, «НЕ», «РАВНЫЕ» и тому подобное .
jww
@jww Предупреждение означает, что MyStringоно содержит имя переменной, которая затем будет разыменована . Я считаю, что никто не хочет такого OLDповедения. Таким образом , с моей точки зрения его абсолютно безопасным и обратную совместимость просто установить политику , CMP0054чтобы NEW(см обсуждение здесь ).
Флориан
@jww И самый безопасный способ избежать этих проблем / предупреждений - это просто сделать if (MyString ...)(если ваш код дает предупреждение).
Флориан,
Спасибо. Мы удалили все вхождения ${MyString}и заменили его MyString(или я думаю, что мы удалили их все). До сих пор нет радости: Сборка 372 . Дерьмо даже не исходит из нашего кода. Похоже, что идет от CMake. Строка 283 есть if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC").
17
18

Вот пара основных примеров, чтобы начать быстро и грязно.

Одна переменная элемента

Установить переменную:

SET(INSTALL_ETC_DIR "etc")

Используйте переменную:

SET(INSTALL_ETC_CROND_DIR "${INSTALL_ETC_DIR}/cron.d")

Многоэлементная переменная (т.е. список)

Установить переменную:

SET(PROGRAM_SRCS
        program.c
        program_utils.c
        a_lib.c
        b_lib.c
        config.c
        )

Используйте переменную:

add_executable(program "${PROGRAM_SRCS}")

CMake документы по переменным

CivFan
источник
1

$ENV{FOO}для использования, где FOOвыбирается из переменной среды. в противном случае используйте as ${FOO}, где FOOнаходится некоторая другая переменная. Для настройки SET(FOO "foo")будет использоваться в CMake.

parasrish
источник