Spec-Zone.ru › CMake 3.18

cmake-compile-features(7)

  • Введение
  • Требования к компилятору

    • Требование к стандартам языка
    • Доступность расширений компилятора
  • Необязательные функции компиляции
  • Условные параметры компиляции
  • Поддерживаемые компиляторы

Введение

Исходный код проекта может зависеть от наличия определенных функций компилятора или быть условным в зависимости от них. Возникают три случая: Требования к компилятору, Необязательные функции компиляции и Условные параметры компиляции.

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

Глобальные свойства CMAKE_C_KNOWN_FEATURES, CMAKE_CUDA_KNOWN_FEATURES и CMAKE_CXX_KNOWN_FEATURES содержат все функции, известные CMake, независимо от поддержки компилятором данной функции. Переменные CMAKE_C_COMPILE_FEATURES, CMAKE_CUDA_COMPILE_FEATURES и CMAKE_CXX_COMPILE_FEATURES содержат все функции, которые CMake знает, что известны компилятору, независимо от языка или флагов компиляции, необходимых для их использования.

Функции, известные CMake, в основном имеют ту же конвенцию именования, что и макросы тестирования функций Clang. Есть некоторые исключения, такие как CMake, использующий cxx_final и cxx_override вместо одного cxx_override_control используемого Clang.

Обратите внимание, что нет отдельных свойств или переменных для функций компиляции для языков OBJC или OBJCXX. Они основаны на C или C++ соответственно, поэтому следует использовать свойства и переменные для соответствующего базового языка.

Требования к компилятору

Требования к функциям компиляции можно указать с помощью команды target_compile_features(). Например, если целевой объект должен быть скомпилирован с поддержкой компилятором функции cxx_constexpr:

add_library(mylib requires_constexpr.cpp)
target_compile_features(mylib PRIVATE cxx_constexpr)

При обработке требования к функции cxx_constexpr, cmake(1) обеспечит, что используемый C++ компилятор способен выполнить данную функцию, и добавит необходимые флаги, такие как -std=gnu++11, к строкам компиляции файлов C++ в целевом объекте mylib. Будет выведено сообщение FATAL_ERROR, если компилятор не поддерживает данную функцию.

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

Такие флаги компиляции добавляются даже если компилятор поддерживает конкретную функцию без флага. Например, GNU компилятор поддерживает универсальные шаблоны (с предупреждением) даже если используется -std=gnu++98. CMake добавляет флаг -std=gnu++11 если cxx_variadic_templates указан в качестве требования.

В приведенном выше примере mylib требует cxx_constexpr, когда он сам компилируется, но потребители mylib не обязаны использовать компилятор, поддерживающий cxx_constexpr. Если интерфейс mylib требует функции cxx_constexpr (или любой другой известной функции), это может быть указано с помощью подписей PUBLIC или INTERFACE команды target_compile_features():

add_library(mylib requires_constexpr.cpp)
# cxx_constexpr is a usage-requirement
target_compile_features(mylib PUBLIC cxx_constexpr)

# main.cpp will be compiled with -std=gnu++11 on GNU for cxx_constexpr.
add_executable(myexe main.cpp)
target_link_libraries(myexe mylib)

Требования к функциям оцениваются транзитивно, потребляя реализацию ссылки. См. cmake-buildsystem(7) для получения дополнительной информации о транзитивном поведении свойств сборки и требований к использованию.

Требование к стандартам языка

В проектах, которые используют большое количество общих функций из конкретного стандарта языка (например, C++ 11), можно указать мета-функцию (например, cxx_std_11), которая требует использования режима компилятора, по крайней мере, знакомого с этим стандартом, но может быть и более новым. Это проще, чем указывать все функции по отдельности, но не гарантирует существования какой-либо конкретной функции. Диагностика использования неподдерживаемых функций будет отложена до времени компиляции.

Например, если C++ 11 функции широко используются в заголовочных файлах проекта, то клиенты должны использовать режим компилятора не ниже C++ 11. Это можно запросить с помощью кода:

target_compile_features(mylib PUBLIC cxx_std_11)

В этом примере CMake обеспечит вызов компилятора в режиме не ниже C++ 11 (или C++ 14, C++ 17, …), добавив флаги, такие как -std=gnu++11, при необходимости. Это относится к исходным файлам в mylib, а также к любым зависимым файлам (которые могут включать заголовки из mylib).

Доступность расширений компилятора

Поскольку свойство целевого объекта CXX_EXTENSIONS установлено по умолчанию в значение ON, CMake по умолчанию использует расширенные варианты диалектов языка, такие как -std=gnu++11 вместо -std=c++11 . Это свойство целевого объекта можно установить в OFF, чтобы использовать нерасширенный вариант флага диалекта. Обратите внимание, что поскольку большинство компиляторов по умолчанию включают расширения, это может привести к появлению проблем с кроссплатформенностью в пользовательском коде или в заголовках сторонних зависимостей.

Необязательные функции компиляции

Функции компиляции могут быть предпочтительны, если они доступны, без создания жесткого требования. Например, библиотека может предоставлять альтернативные реализации в зависимости от того, доступна ли функция cxx_variadic_templates.

#if Foo_COMPILER_CXX_VARIADIC_TEMPLATES
template<int I, int... Is>
struct Interface;

template<int I>
struct Interface<I>
{
  static int accumulate()
  {
    return I;
  }
};

template<int I, int... Is>
struct Interface
{
  static int accumulate()
  {
    return I + Interface<Is...>::accumulate();
  }
};
#else
template<int I1, int I2 = 0, int I3 = 0, int I4 = 0>
struct Interface
{
  static int accumulate() { return I1 + I2 + I3 + I4; }
};
#endif

Такой интерфейс зависит от использования правильных предопределений препроцессора для функций компилятора. CMake может сгенерировать заголовочный файл, содержащий такие определения, с помощью модуля WriteCompilerDetectionHeader. Модуль содержит функцию write_compiler_detection_header, которая принимает параметры для управления содержимым сгенерированного заголовочного файла:

write_compiler_detection_header(
  FILE "${CMAKE_CURRENT_BINARY_DIR}/foo_compiler_detection.h"
  PREFIX Foo
  COMPILERS GNU
  FEATURES
    cxx_variadic_templates
)

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

Для каждой функции, перечисленных в FEATURES, в заголовочном файле создается предопределение препроцессора, определенное как 1 или 0.

Кроме того, некоторые функции требуют дополнительных определений, такие как функции cxx_final и cxx_override. Вместо использования в коде #ifdef, ключевое слово final абстрагируется символом, который определяется либо final, эквивалентом для конкретного компилятора, либо пустой строкой. Таким образом, код C++ может безусловно использовать символ, а поддержка компилятора определяет то, к чему он расширяется:

struct Interface {
  virtual void Execute() = 0;
};

struct Concrete Foo_FINAL {
  void Execute() Foo_OVERRIDE;
};

В этом случае, Foo_FINAL будет расширено до final, если компилятор поддерживает ключевое слово, или до пустой строки в противном случае.

В этом случае код CMake должен включить определённый стандарт языка, если он доступен у компилятора. Переменная свойства целевого объекта CXX_STANDARD может быть установлена в желаемый стандарт языка для конкретного целевого объекта, а CMAKE_CXX_STANDARD может быть установлена, чтобы повлиять на все последующие целевые объекты:

write_compiler_detection_header(
  FILE "${CMAKE_CURRENT_BINARY_DIR}/foo_compiler_detection.h"
  PREFIX Foo
  COMPILERS GNU
  FEATURES
    cxx_final cxx_override
)

# Includes foo_compiler_detection.h and uses the Foo_FINAL symbol
# which will expand to 'final' if the compiler supports the requested
# CXX_STANDARD.
add_library(foo foo.cpp)
set_property(TARGET foo PROPERTY CXX_STANDARD 11)

# Includes foo_compiler_detection.h and uses the Foo_FINAL symbol
# which will expand to 'final' if the compiler supports the feature,
# even though CXX_STANDARD is not set explicitly.  The requirement of
# cxx_constexpr causes CMake to set CXX_STANDARD internally, which
# affects the compile flags.
add_library(foo_impl foo_impl.cpp)
target_compile_features(foo_impl PRIVATE cxx_constexpr)

Функция write_compiler_detection_header также создает код совместимости для других функций, которые имеют стандартные эквиваленты. Например, функция cxx_static_assert эмулируется с помощью шаблона и абстрагируется через макросы функций <PREFIX>_STATIC_ASSERT и <PREFIX>_STATIC_ASSERT_MSG.

Условные параметры компиляции

Библиотеки могут предоставлять совершенно разные заголовочные файлы в зависимости от запрашиваемых функций компилятора.

Например, заголовок по адресу with_variadics/interface.h может содержать:

template<int I, int... Is>
struct Interface;

template<int I>
struct Interface<I>
{
  static int accumulate()
  {
    return I;
  }
};

template<int I, int... Is>
struct Interface
{
  static int accumulate()
  {
    return I + Interface<Is...>::accumulate();
  }
};

в то время как заголовок по адресу no_variadics/interface.h может содержать:

template<int I1, int I2 = 0, int I3 = 0, int I4 = 0>
struct Interface
{
  static int accumulate() { return I1 + I2 + I3 + I4; }
};

Было бы возможно написать абстрактный заголовок interface.h, содержащий что-то вроде:

#include "foo_compiler_detection.h"
#if Foo_COMPILER_CXX_VARIADIC_TEMPLATES
#include "with_variadics/interface.h"
#else
#include "no_variadics/interface.h"
#endif

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

CMake предоставляет COMPILE_FEATURES generator expression для реализации таких условий. Это может быть использовано с командами свойств сборки, такими как target_include_directories() и target_link_libraries() для установки соответствующих buildsystem свойств:

add_library(foo INTERFACE)
set(with_variadics ${CMAKE_CURRENT_SOURCE_DIR}/with_variadics)
set(no_variadics ${CMAKE_CURRENT_SOURCE_DIR}/no_variadics)
target_include_directories(foo
  INTERFACE
    "$<$<COMPILE_FEATURES:cxx_variadic_templates>:${with_variadics}>"
    "$<$<NOT:$<COMPILE_FEATURES:cxx_variadic_templates>>:${no_variadics}>"
  )

Затем код, использующий эти свойства, просто ссылается на foo целевой объект как обычно и использует каталог включения, соответствующий данной функции.

add_executable(consumer_with consumer_with.cpp)
target_link_libraries(consumer_with foo)
set_property(TARGET consumer_with CXX_STANDARD 11)

add_executable(consumer_no consumer_no.cpp)
target_link_libraries(consumer_no foo)

Поддерживаемые компиляторы

В настоящее время CMake поддерживает C++ standards и compile features, доступные от следующих compiler ids на указанных версиях:

  • AppleClang: Apple Clang для Xcode версии 4.4+.
  • Clang: Компилятор Clang версии 2.9+.
  • GNU: Компилятор GNU версии 4.4+.
  • MSVC: Microsoft Visual Studio версии 2010+.
  • SunPro: Oracle SolarisStudio версии 12.4+.
  • Intel: Компилятор Intel версии 12.1+.

В настоящее время CMake поддерживает C standards и compile features, доступные от следующих compiler ids на указанных версиях:

  • все компиляторы и версии, перечисленные выше, для C++.
  • GNU: Компилятор GNU версии 3.4+.

В настоящее время CMake поддерживает C++ standards и их сопутствующие мета-функции (например, cxx_std_11) доступные от следующих compiler ids на указанных версиях:

  • Cray: Cray Compiler Environment версии 8.1+.
  • PGI: PGI версии 12.10+.
  • XL: IBM XL версии 10.1+.

В настоящее время CMake поддерживает C standards и их сопутствующие мета-функции (например, c_std_99) доступные от следующих compiler ids на указанных версиях:

  • все компиляторы и версии, перечисленные выше, только с мета-функциями для C++.
  • TI: Компилятор Texas Instruments.

В настоящее время CMake поддерживает CUDA standards и их сопутствующие мета-функции (например, cuda_std_11) доступные от следующих compiler ids на указанных версиях:

  • NVIDIA: Компилятор NVIDIA nvcc версии 7.5+.

© 2000–2020 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.18/manual/cmake-compile-features.7.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API