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.6/manual/cmake-compile-features.7.html