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 по 6.2. -
Clang: Компилятор Clang версий 2.9 по 3.4. -
GNU: GNU компилятор версий 4.4 по 5.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 по 5.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++.
В настоящее время CMake поддерживает CUDA standards от следующих compiler ids начиная с указанных версий:
-
NVIDIA: NVIDIA nvcc компилятор 7.5 по 8.0.
© 2000–2019 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.11/manual/cmake-compile-features.7.html