Когда заключать в кавычки переменные?
Я пишу макросы CMake впервые, и мне трудно понять, как работают переменные. В частности, ${a}
кажется, имеет другое значение, чем "${a}"
,
Например, здесь: Передача списка в макрос cmake
Я не понимаю, когда я должен добавить цитаты, и каковы основные принципы.
2 ответа
Вы должны помнить о двух принципах CMake:
- CMake - это язык сценариев, и аргументы оцениваются после раскрытия переменных
- 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
Некоторые правила большого пальца
Есть несколько практических правил, которые вы должны рассмотреть:
а) Когда ваша переменная содержит текст - особенно тот, который может содержать точки с запятой - вы должны добавить кавычки.
Причина: точка с запятой является разделителем для элементов списка в 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
Если ваша переменная содержит список, вы обычно не добавляете кавычки.
Причина: если вы даете что-то вроде списка файлов команде CMake, то обычно ожидается список строк, а не одна строка, содержащая список. Разница, которую вы можете увидеть, например, в
foreach()
принятие командыITEMS
или жеLISTS
,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
звонки:
a) Используйте кавычки для аргументов, которые содержат пути к файлам (например, первый аргумент, содержащий сам исполняемый файл).
Обоснование: оно может содержать пробелы и может быть истолковано как отдельные аргументы
COMMAND
вызовб) См. выше, работает также, если переменная
set()
действительно включал цитаты.Используйте кавычки, только если вы хотите объединить что-то в один параметр для передачи в вызываемый исполняемый файл.
Причина: переменная может содержать список параметров, которые - при использовании кавычек - не будут правильно извлечены (точки с запятой вместо пробелов)
Всегда добавляйте
VERBATIM
вариант сadd_custom_target()
/add_custom_command()
Причина: в противном случае кроссплатформенное поведение не определено, и вы можете получить сюрпризы со своими цитируемыми строками.
Рекомендации
Когда следует цитировать ссылки на переменные CMake?
- Когда вам нужно
- Когда вам хочется это сделать, и технически это не имеет значения.
Мне трудно понять, как работают переменные. В частности,
${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}")
сначала проверить, определена ли такая переменная первой)). Поэтому, если вы хотите быть в безопасности, всегда читайте документацию (я бы просто рекомендовал читать документацию в целом).