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()
Другие вопросы по тегам