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