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. Будет выведено сообщение об ошибке, если компилятор не поддерживает функцию.
Точные флаги компиляции и стандарт языка преднамеренно не являются частью пользовательского интерфейса для этого случая использования. 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.
© 2000–2019 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.5/manual/cmake-compile-features.7.html