Когда заключать в кавычки переменные?

Я пишу макросы CMake впервые, и мне трудно понять, как работают переменные. В частности, ${a} кажется, имеет другое значение, чем "${a}",

Например, здесь: Передача списка в макрос cmake

Я не понимаю, когда я должен добавить цитаты, и каковы основные принципы.

2 ответа

Решение

Вы должны помнить о двух принципах CMake:

  1. CMake - это язык сценариев, и аргументы оцениваются после раскрытия переменных
  2. CMake различает обычные строки и переменные списка (строки с разделителями точки с запятой)

Примеры

  • set(_my_text "A B C") с message("${_my_text}") даст A B C
  • set(_my_list A B C) с message("${_my_list}") даст A;B;C
  • set(_my_list "A" "B" "C") с message("${_my_list}") даст A;B;C
  • set(_my_list "A" "B" "C") с message(${_my_list}) даст ABC

Некоторые правила большого пальца

Есть несколько практических правил, которые вы должны рассмотреть:

  1. а) Когда ваша переменная содержит текст - особенно тот, который может содержать точки с запятой - вы должны добавить кавычки.

    Причина: точка с запятой является разделителем для элементов списка в CMake. Поэтому поместите кавычки вокруг текста, который должен быть единым (он работает везде и лично для меня выглядит лучше с подсветкой синтаксиса CMake)

    РЕДАКТИРОВАТЬ: Спасибо за подсказку от @schieferstapel

    б) Если быть более точным: переменное содержимое с пробелами, в которых уже есть кавычки, сохраняет эти кавычки (представьте, что оно получает часть содержимого переменной). Это работает везде также без кавычек (нормальные или определяемые пользователем параметры функции) с явным исключением if() вызовы, где CMake повторно интерпретирует содержимое переменных без кавычек после раскрытия переменных (см. также практическое правило № 3 и политику CMP0054: интерпретировать только if() аргументы как переменные или ключевые слова, когда без кавычек)

    Примеры:

    • set(_my_text "A B C") с message(${_my_text}) также дал бы A B C
    • set(_my_text "A;B;C") с if (${_my_text} STREQUAL "A;B;C") даст if given arguments: "A" "B" "C" "STREQUAL" "A;B;C" Unknown arguments specified
  2. Если ваша переменная содержит список, вы обычно не добавляете кавычки.

    Причина: если вы даете что-то вроде списка файлов команде CMake, то обычно ожидается список строк, а не одна строка, содержащая список. Разница, которую вы можете увидеть, например, в foreach() принятие команды ITEMS или же LISTS,

  3. if() заявления являются особым случаем, когда вы обычно даже не ставите скобки.

    Причина: строка может - после раскрытия - снова вычислить имя переменной. Чтобы предотвратить это, рекомендуется просто назвать переменную, содержимое которой вы хотите сравнить (например, if (_my_text STREQUAL "A B C")).


COMMAND Примеры

  • set(_my_text "A B C") с COMMAND "${CMAKE_COMMAND}" -E echo "${_my_text}" было бы
    • вызов cmake.exe -E echo "A B C" на VS/Windows
    • вызов cmake -E echo A\ B\ C на GCC/Ubuntu
    • дать A B C
  • set(_my_text "A B C") с COMMAND "${CMAKE_COMMAND}" -E echo "${_my_text}" VERBATIM было бы
    • вызов cmake.exe -E echo "A B C" на VS/Windows
    • вызов cmake -E echo "A B C" на GCC/Ubuntu
    • дать A B C
  • set(_my_list A B C) с COMMAND "${CMAKE_COMMAND}" -E echo "${_my_list}" было бы
    • вызов cmake.exe -E echo A;B;C
    • дать A, B: command not found, C: command not found
  • set(_my_list A B C) с COMMAND "${CMAKE_COMMAND}" -E echo "${_my_list}" VERBATIM было бы
    • вызов cmake.exe -E echo "A;B;C"
    • дать A;B;C
  • set(_my_list "A" "B" "C") с COMMAND "${CMAKE_COMMAND}" -E echo "${_my_list}" VERBATIM было бы
    • вызов cmake.exe -E echo "A;B;C"
    • дать A;B;C
  • set(_my_list "A" "B" "C") с COMMAND "${CMAKE_COMMAND}" -E echo ${_my_list} VERBATIM было бы
    • вызов cmake.exe -E echo A B C
    • дать A B C
  • set(_my_list "A + B" "=" "C") с COMMAND "${CMAKE_COMMAND}" -E echo ${_my_list} VERBATIM было бы
    • вызов cmake.exe -E echo "A + B" = C
    • дать A + B = C

Некоторые правила большого пальца с add_custom_target() / add_custom_command() / execute_process()

Есть несколько практических правил, которые вы должны учитывать при использовании переменных в COMMAND звонки:

  1. a) Используйте кавычки для аргументов, которые содержат пути к файлам (например, первый аргумент, содержащий сам исполняемый файл).

    Обоснование: оно может содержать пробелы и может быть истолковано как отдельные аргументы COMMAND вызов

    б) См. выше, работает также, если переменная set() действительно включал цитаты.

  2. Используйте кавычки, только если вы хотите объединить что-то в один параметр для передачи в вызываемый исполняемый файл.

    Причина: переменная может содержать список параметров, которые - при использовании кавычек - не будут правильно извлечены (точки с запятой вместо пробелов)

  3. Всегда добавляйте VERBATIM вариант с add_custom_target() / add_custom_command()

    Причина: в противном случае кроссплатформенное поведение не определено, и вы можете получить сюрпризы со своими цитируемыми строками.

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

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

  1. Когда вам нужно
  2. Когда вам хочется это сделать, и технически это не имеет значения.

Мне трудно понять, как работают переменные. В частности,${a}кажется, имеет другое значение, чем"${a}".

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

Я могу придумать пару сценариев, в которых это имеет значение: вызовы/вызовы команд и некоторые конкретные команды, которые имеют особое поведение для аргументов, которые именуют переменные, но не являются вычисляемыми ссылками на переменные (Пример.).

Для командных вызовов в целом

Документацию по ссылкам на переменные можно найти здесь . Обратите внимание, что команды в CMake включают функции и макросы (соответствующая документация ). TL;DR заключается в том, что если вы ссылаетесь на несуществующую переменную без передачи, CMake оценит ссылку на пустую строку. Вот почему первыйmessageвызов выше печатает "foo: ", и почему первый вызовset(foo ${foo} abc)(который ссылаетсяfooво втором аргументе) не будет вызывать ошибку (пока вы не используете--warn-uninitialized)

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

Документацию по аргументам команды можно найти здесь .

  • Из раздела о цитируемых аргументах:

    Содержимое аргумента в кавычках состоит из всего текста между открывающими и закрывающими кавычками. Оцениваются как экранироватьEscape-последовательности , так и ссылки на переменные.Аргумент в кавычках всегда передается при вызове команды как один аргумент.

  • Из раздела об аргументах без кавычек:

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

Вот почему я говорю «[переменные в кавычках], когда вам нужно».

Остальной решающий фактор относительно того, что вам нужно заключить в кавычки, чтобы получить желаемое поведение при вызове команд, будет зависеть от того, как тело команды обрабатывает переданные ей аргументы (после того, как CMake выполнил отмену экранирования, оценку ссылки на переменную и т. д. ). Для получения более подробной информации обратитесь к документации по конкретной команде или к документации или реализации функции/макроса (см. также соответствующую документацию по аргументам функции , аргументам макросов и предостережениям по аргументам макросов ).

Для командования

Хорошо, тогда почему я еще сказал « когда вам хочется это сделать, и технически это не имеет значения »? Из-за дополнительного поведения команды.

В любом месте подписи «подкоманды» вы видите «<condition>", обратитесь к документации по синтаксису условий , в которой говорится:

if(<constant>)
Истинно, если константа1,ON,YES,TRUE,Yили ненулевое число (включая числа с плавающей запятой). Ложь, если константа0,OFF,NO,FALSE,N,IGNORE,NOTFOUND, пустая строка или заканчивается суффиксом-NOTFOUND. Именованные логические константы не чувствительны к регистру. Если аргумент не является одной из этих конкретных констант, он рассматривается как переменная или строка (см. «Расширение переменных» ниже) и применяется одна из следующих двух форм.

if(<variable>)
Истинно, если задана переменная, для которой определено значение, не являющееся ложной константой. В противном случае — false, в том числе, если переменная не определена. Обратите внимание, что аргументы макроса не являются переменными. Переменные среды также не могут быть протестированы таким образом, напримерif(ENV{some_var})всегда будет иметь значение false.

if(<string>)
Строка в кавычках всегда имеет значение false, если:

Значение строки является одной из истинных констант или

Политикане установлено значение NEW, а значение строки является именем переменной, на которую влияетCMP0054поведение.

Кроме того, многие сигнатуры/"подкоманды" команды принимают аргументы вида<variable|string>

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

Если вы хотите быть уверены, что что-то, что вы намереваетесь сделать строкой в ​​команде, которая попытается «автоматически ссылаться» на переменные по имени в строках без кавычек, предотвратите это, заключив это в кавычки. Таким образом, это охватывает обе причины: как «когда вам нужно», так и «когда вам хочется это сделать, и технически это не имеет значения» (я люблю цитировать вещи, которые я собираюсь использовать в качестве строк в этих контекстах, просто для душевного спокойствия). ).

Обратите внимание, что такое поведение не обязательно является чем-то особенным дляif(...). Вы также можете написать функцию, которая делает подобные вещи (пример.${${ARGV0}}(отмените ссылку один раз, чтобы получить значение аргумента, затем снова отмените ссылку, чтобы рассматривать значение как имя другой переменной или дополнительно выполнитьif(DEFINED "${ARGV0}")сначала проверить, определена ли такая переменная первой)). Поэтому, если вы хотите быть в безопасности, всегда читайте документацию (я бы просто рекомендовал читать документацию в целом).

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