Определить макрос препроцессора через CMake?

#c #cmake

#c #cmake #c-препроцессор

Вопрос:

Как мне определить переменную препроцессора через CMake?

Эквивалентный код будет #define foo .

Ответ №1:

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

Пример с использованием новых add_compile_definitions:

 add_compile_definitions(OPENCV_VERSION=${OpenCV_VERSION})
add_compile_definitions(WITH_OPENCV2)
  

Или:

 add_compile_definitions(OPENCV_VERSION=${OpenCV_VERSION} WITH_OPENCV2)
  

Хорошая часть в этом заключается в том, что он обходит потрепанный трюк, для которого используется CMake add_definitions . CMake — такая потрепанная система, но они, наконец, обретают здравый смысл.

Найдите больше объяснений о том, какие команды использовать для флагов компилятора здесь: https://cmake.org/cmake/help/latest/command/add_definitions.html

Аналогично, вы можете сделать это для каждой цели, как описано в ответе Джима Ханцикера.

Комментарии:

1. Со связанной страницы: «Обратите внимание, что эта команда была заменена альтернативами: используйте add_compile_definitions() для добавления определений препроцессора». Может быть, этот ответ нуждается в редактировании?

2. В cmake 3.10.2 add_compile_definitions выдает CMake Error at CMakeLists.txt:6 (add_compile_definitions): Unknown CMake command "add_compile_definitions". ошибку . Пришлось использовать add_compile_options(-D <your-def>) вместо этого.

3. @mannyglover Я так не думаю, но вы можете установить флаги компилятора с помощью -D, что-то вроде cmake -D CMAKE_CXXFLAGS='-DDEBUG_CHESSBOARD' (не проверено)

4. Это действительно новое… на самом деле это в более современном CMake (> 3.11). Боль в том, что так трудно понять, когда была введена команда.

5. @Sandburg Вы можете открыть ссылку на последнюю документацию: https://cmake.org/cmake/help/v3.17/command/add_compile_definitions.html#command:add_compile_definitions и начать изменять номер версии вниз, пока страница не исчезнет. Это будет версия, в которой она еще не существует. На следующем шаге вы можете перейти к Whats new разделу, чтобы найти новую команду или функцию. Так что это не так сложно.

Ответ №2:

Чтобы сделать это для конкретной цели, вы можете сделать следующее:

 target_compile_definitions(my_target PRIVATE FOO=1 BAR=1)
  

Вы должны сделать это, если у вас более одной цели, которую вы создаете, и вы не хотите, чтобы все они использовали одни и те же флаги. Также смотрите официальную документацию по target_compile_definitions .

Комментарии:

1. @JimHunziker Чем target_compile_definitions(my_target PRIVATE FOO=1) отличается от set_source_files_properties(foo.cpp PROPERTIES COMPILE_DEFINITIONS -DFOO=1) ?

2. @JohnStrood Разница заключается в уровне области видимости. target_compile_definitions задает значение для ВСЕГО исполняемого файла / библиотеки, где как ‘set_source_files_properties` задает значение только для указанного файла. Последняя команда позволяет компилировать файлы с использованием другого языка; т.е.: set_source_files_properties(compile_me_as_objc.c PROPERTIES -x objective-c . Обратите внимание, что -x objective-c здесь указан флаг, специфичный для GCC / Clang .

Ответ №3:

Другие решения, предложенные на этой странице, полезны для некоторых версий Cmake> 3.3.2 . Вот решение для версии, которую я использую (т.Е. 3.3.2 ). Проверьте версию вашего Cmake с помощью $ cmake --version и выберите решение, которое соответствует вашим потребностям. Документацию cmake можно найти на официальной странице.

С CMake версии 3.3.2, чтобы создать

 #define foo
  

Мне нужно было использовать:

 add_definitions(-Dfoo)   # <--------HERE THE NEW CMAKE LINE inside CMakeLists.txt
add_executable( ....)
target_link_libraries(....)
  

и для того, чтобы иметь определение макроса препроцессора, подобное этому другому:

 #define foo=5
  

строка настолько изменена:

 add_definitions(-Dfoo=5)   # <--------HERE THE NEW CMAKE LINE inside CMakeLists.txt
add_executable( ....)
target_link_libraries(....)
  

ПОЖАЛУЙСТА, ОБРАТИТЕ ВНИМАНИЕ (как предлагает @squareskittles в одном из комментариев): «если вы используете CMake 3.3.2 , вы должны use add_definitions() или target_compile_definitions() . Более современная команда, add_compile_definitions() , не была добавлена до CMake 3.12

Комментарии:

1. Согласно документации , это решение на самом деле является более старым, более устаревшим подходом. Другие ответы предлагают более современные решения.

2. Когда я писал ответ, я попробовал другое решение, но ни одно из них не работало.

3. @squareskittles, есть идеи, почему решение другого ответа не сработало правильно? CMake выдает ошибки, если я их попробую

4. Если вы используете CMake 3.3.2, как вы указали в своем ответе, вы должны использовать add_definitions() или target_compile_definitions() . Более современная команда, add_compile_definitions() , не была добавлена до CMake 3.12. @Leos313

5. @squareskittles, правильно! ответ обновлен вашей информацией!!

Ответ №4:

1.) target_compile_definitions

Если вы используете CMake 3.X, первым выбором для добавления макроса препроцессора должно быть target_compile_definitions .

Причина, по которой вы должны предпочесть этот подход любому другому подходу, заключается в том, что он target основан на детализации. Т.Е. макрос будет добавлен только в ваш exe / library.

Вот общий пример:

 if (WIN32)
    target_compile_definitions(my_lib PRIVATE   
       # Prevents Windows.h from adding unnecessary includes    
       WIN32_LEAN_AND_MEAN  
       # Prevents Windows.h from defining min/max as macros 
       NOMINMAX 
    )   
endif() 
  

2.) add_compile_definitions

Новое в версии 3.12.

Найдите больше объяснений о том, какие команды использовать для флагов компилятора здесь: https://cmake.org/cmake/help/latest/command/add_definitions.html

add_compile_definitions применяет макросы к любым целям, которые определены после вызова.

Здесь та же логика, что и выше, с add_compile_definitions .

 add_compile_definitions(WIN32_LEAN_AND_MEAN NOMINMAX)
add_library(my_lib)
  

Если вы используете этот подход, будьте осторожны, если вы являетесь проектом верхнего уровня.
В противном случае, если пользователи используют вашу библиотеку с помощью add_subdirectory, у них могут возникнуть проблемы.

3.) Другие менее рекомендуемые способы

Эти подходы действительно больше не рекомендуются. Из-за того, что он не является модульным, плохо масштабируется, Не поддерживает выражения генератора и т. Д.

Почему target_compile_definitions лучше / предпочтительнее?

  • Читателям вашего кода CMake гораздо понятнее, как это работает.
  • При необходимости позволяет использовать PRIVATE / PUBLIC / INTERFACE. Что может облегчить жизнь пользователям вашей библиотеки.
  • Он намного более модульный.

Применение флагов препроцессора (или любого флага компилятора) глобально может создать скрытые зависимости в вашей сборке.

По сути, думайте о add_compile_definitions как о глобальных в C / C . Иногда они вам нужны, но будьте осторожны.

Ответ №5:

я хотел бы рекомендовать использовать target_*** операции вместо add_*** операций, когда ваше решение включает в себя много проектов.

Ответ №6:

вот пример, в котором вы можете передавать значения из CMAKE в код C . Скажем, вы хотите передать:

  • флаг, здесь: BOOST («true» или «false»)
  • строка версии программного обеспечения (например: «1.0.0»)

Я рекомендую передавать их в виде строк. Итак, когда вы создаете программное обеспечение с помощью CMAKE, вы можете передавать параметры, например, если оно было создано с использованием библиотеки boost, версию программного обеспечения, извлеченную из переменной CMAKE (чтобы вы изменили это число только в одном месте), см. Ниже.

В CMakeLists.txt:

add_compile_definitions(BOOST=»${BOOST}» Software_VERSION=»$ {PROJECT_VERSION}» )

В вашем коде .cpp:

std::cout << «Версия программного обеспечения: » << Software_VERSION << » BOOST: » << BOOST << «n»;

Надеюсь, это поможет. С уважением.