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

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

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

3 ответа

Решение

При написании сценариев 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 с графическим интерфейсом перед их генерацией. Поэтому они - по сравнению с обычными переменными - имеют type и 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,
  • Вы можете удалить записи в кеше с помощью 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 (здесь нет необходимости для включения ${...})
    • True, если константа 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.

Рекомендации

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

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

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

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 документы по переменным

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

Другие вопросы по тегам