Spec-Zone.ru › CMake 3.15

cmake-compile-features(7)

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

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

Введение

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

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

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

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

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

Требования к функциям компиляции могут быть заданы с помощью команды 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 (или выше), добавляя необходимые флаги, такие как -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 до 9.2.
  • Clang: Версии компилятора Clang от 2.9 до 6.0.
  • GNU: Версии компилятора GNU от 4.4 до 8.0.
  • MSVC: Версии Microsoft Visual Studio от 2010 до 2017.
  • SunPro: Версии Oracle SolarisStudio от 12.4 до 12.6.
  • Intel: Версии компилятора Intel от 12.1 до 17.0.

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

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

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

  • Cray: Cray Compiler Environment версии 8.1 до 8.5.8.
  • PGI: PGI версии 12.10 до 17.5.
  • XL: IBM XL версии 10.1 до 13.1.5.

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

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

CMake в настоящее время знает о CUDA standards от следующих compiler ids, начиная с версий, указанных для каждого:

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

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

Spec-Zone.ru

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