CMake сравнить с пустой строкой с STREQUAL не удалось
Я всегда думаю, что если вы хотите сравнить две строки (но не переменные), все, что вам нужно сделать, это заключить в кавычки так:
if("${A}" STREQUAL "some string")
но теперь я узнаю, что этот код иногда печатают oops
:
cmake_minimum_required(VERSION 2.8)
if("d" STREQUAL "")
message("oops...")
endif()
Может быть, это ошибка (потому что она печатает с Xcode, а не с make)? Или есть какие-то специальные переменные?
- cmake: 2.8.12, 2.8.11.2
- xcode: 4.6.2, 5.0.1
Обновить
Есть командная строка без описанных проблем:
string(COMPARE EQUAL "${A}" "" result)
if(result)
message("...")
endif()
Обновление 2
Поведение, которое я ожидал реализовать с CMake 3.1.0 (см. CMP0054).
Результат теста3.0.2:
CMake version: 3.0.2
Quoted test
Surprise!
Unquoted test
Surprise!
Результат теста3.1.0:
CMake version: 3.1.0
Quoted test
OK
Unquoted test
Surprise!
1 ответ
Вы столкнулись с довольно раздражающим поведением CMake "это не ошибка, это особенность". Как объяснено в документации команды if:
The if command was written very early in CMake's history, predating the ${}
variable evaluation syntax, and for convenience evaluates variables named
by its arguments as shown in the above signatures.
Что ж, удобство оказалось неудобством. В вашем примере строка "d"
рассматривается как переменная с именем d
посредством if
команда. Если переменная d
в случае определения пустой строки, оператор сообщения выведет "oops...", например:
set (d "")
if("d" STREQUAL "")
# this branch will be taken
message("oops...")
else()
message("fine")
endif()
Это может дать удивительные результаты для таких утверждений, как
if("${A}" STREQUAL "some string")
потому что может быть непреднамеренное двойное расширение первого аргумента, если переменная A
бывает определено для строки, которая также является именем переменной CMake, например:
set (A "d")
set (d "some string")
if("${A}" STREQUAL "some string")
# this branch will be taken
message("oops...")
else()
message("fine")
endif()
Возможные обходные пути:
Вы можете добавить символ суффикса к строке после ${}
расширение, которое не позволяет оператору if выполнять автоматическую оценку:
set (A "d")
set (d "some string")
if("${A} " STREQUAL "some string ")
message("oops...")
else()
# this branch will be taken
message("fine")
endif()
Не использовать ${}
расширение:
set (A "d")
set (d "some string")
if(A STREQUAL "some string")
message("oops...")
else()
# this branch will be taken
message("fine")
endif()
Чтобы предотвратить непреднамеренную оценку на правой стороне STREQUAL
использование MATCHES
вместо этого с регулярным выражением CMake:
if(A MATCHES "^value$")
...
endif()
Приложение: CMake 3.1 больше не делает двойные расширения для аргументов в кавычках. Смотрите новую политику.
Начиная с CMake 3.1, есть новые расширения переменных правил вif()
. Они включены, если вы:
- установлен
cmake_minimum_required(3.1)
(или выше) в верхней части файла проекта, или - используйте более низкий минимальный номер версии, но вручную установите политику CMP0054 на
NEW
.
Даже в этом случае остается верным то, что первый аргумент в пользу if
расширяется значением переменной, соответствующей этому имени, если она существует:
set (d "")
if(d STREQUAL "")
# this branch will be taken
message("oops...")
else()
message("fine")
endif()
Однако теперь это отключено, если указан первый аргумент:
set (d "")
if("d" STREQUAL "")
message("oops...")
else()
# due to quotes around "d" in if statement,
# this branch will be taken
message("fine")
endif()
Если вы действительно хотите проверить содержимое переменной на соответствие значению, вы можете использовать классический синтаксис без кавычек или использовать"${d}"
синтаксис, который вы предложили. Благодаря новым правилам, это никогда не столкнется с проблемой двойного расширения, упомянутой в ответе сакры:
set (A "d")
set (d "some string")
if("${A}" STREQUAL "d")
# this branch will be taken
message("fine")
elseif("${A}" STREQUAL "some string")
message("oops...")
else()
message("??")
endif()