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) для получения дополнительной информации о транзитивном поведении свойств сборки и требованиях к использованию.
Поскольку свойство целевого объекта 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 знает о language 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 по 2015. -
SunPro: Oracle SolarisStudio версии 12.4. -
Intel: Intel компилятор версий с 12.1 по 16.0 на платформах UNIX.
© 2000–2019 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.7/manual/cmake-compile-features.7.html