Як виявити підтримку компілятора C ++ 11 за допомогою CMake


78

Чи є спосіб дозволити CMake визначати автоматично, якщо компілятор підтримує C ++ 11 чи ні?

Оскільки було б непогано повідомити користувачів під час запуску CMake, що код не буде компілюватися, оскільки компілятор не підтримує C ++ 11. На даний момент я встановив прапори C ++ 11. Однак, якщо компілятор цього не підтримує, користувач отримує помилки компіляції замість помилки під час запуску CMake.

Ідеальним буде те, що працює як find_package(). Однак я не знайшов жодного модуля чи функції, які забезпечують необхідну функціональність.

Додатково було б непогано мати функцію для виявлення, якщо компілятору потрібні прапори std=c++0x або std=c++11.

Чи є щось доступне, чи мені це потрібно розробити самостійно?

Нижче наведено деякий код, який я використовую досі, проте він працює лише з компіляторами GNU'c GCC. Було б непогано, якби було більш загальне рішення.

if(CMAKE_COMPILER_IS_GNUCXX)
   execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
   if (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7)
        message(STATUS "C++11 activated.")
        add_definitions("-std=gnu++11")
   elseif(GCC_VERSION VERSION_GREATER 4.3 OR GCC_VERSION VERSION_EQUAL 4.3)
        message(WARNING "C++0x activated. If you get any errors update to a compiler which fully supports C++11")
        add_definitions("-std=gnu++0x")
   else ()
        message(FATAL_ERROR "C++11 needed. Therefore a gcc compiler with a version higher than 4.3 is needed.")   
   endif()
else(CMAKE_COMPILER_IS_GNUCXX)
   add_definitions("-std=c++0x") 
endif(CMAKE_COMPILER_IS_GNUCXX)

Навіщо використовувати add_definitionsкоманду замість налаштування CMAKE_CXX_FLAGSдля встановлення параметрів компілятора?
Лі Донг,

Відповіді:


104

Якщо у вас CMake версії 3.1.0 або пізнішої, ви можете визначити, які функції C ++ підтримує ваш компілятор C ++

cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
project(foobar CXX)
message("Your C++ compiler supports these C++ features:")
foreach(i ${CMAKE_CXX_COMPILE_FEATURES})
  message("${i}")
endforeach()

Але зазвичай вам не потрібно використовувати змінну CMake CMAKE_CXX_COMPILE_FEATURES у своїх сценаріях CMake. Натомість є два способи визначити CMake, за яким стандартом C ++ слід компілювати ваші файли C ++, або явно вказавши стандарт C ++, або вказавши необхідні функції C ++, і дозвольте CMake викликати стандарт C ++. CMake забезпечить виклик компілятора C ++ із правильними прапорами командного рядка (наприклад, -std = c ++ 11).

1. Явне зазначення стандарту C ++

Ви можете вказати стандарт C ++ явно, встановивши властивості CMake CXX_STANDARD і CXX_STANDARD_REQUIRED для вашої цілі CMake.

$ cat /tmp/src/CMakeLists.txt
project(foobar CXX)
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
add_executable(prog main.cc)
set_property(TARGET prog PROPERTY CXX_STANDARD 11)
set_property(TARGET prog PROPERTY CXX_STANDARD_REQUIRED ON)
$ cat /tmp/src/main.cc
int main() {
  return 0;
}
$ mkdir /tmp/build
$ cd /tmp/build
$ cmake /tmp/src
-- The CXX compiler identification is GNU 4.8.2
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/build
$ make VERBOSE=1 | grep main.cc | grep -- "-c"
/usr/bin/c++    -std=gnu++11 -o CMakeFiles/prog.dir/main.cc.o -c /tmp/src/main.cc
$

2. Визначення необхідних функцій C ++ і нехай CMake індукує стандарт C ++

Ви можете використовувати команду CMake target_compile_features, щоб вказати функції C ++, які використовуються у вашій цілі CMake. З цього списку CMake буде наводити стандарт C ++, який буде використовуватися. Глобальна властивість CMake CMAKE_CXX_KNOWN_FEATURES перелічені функції C ++, з яких ви можете вибрати.

cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
message("Your CMake version supports these C++ features:")
get_property(known_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
foreach(i ${known_features})
  message("${i}")
endforeach()

Наприклад, ця програма C ++ з іменем файлу main.cc використовує можливості C ++ 11: cxx_strong_enums , cxx_constexpr , cxx_auto_type

#include <cstdlib>

int main(int argc, char *argv[]) {
  enum class Color { Red, Orange, Yellow, Green, Blue, Violet };
  constexpr float a = 3.1415f;
  auto b = a;
  return EXIT_SUCCESS;
}

Цей файл CMakeLists.txt створить його

cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
project(foobar CXX)
add_executable(foobar main.cc)                                                                                                                                                                                                                                                     
set(needed_features
    cxx_strong_enums
    cxx_constexpr
    cxx_auto_type)
target_compile_features(foobar PRIVATE ${needed_features})

12
Трохи боляче, що це потрібно робити для цілі. Було б зручно сказати "просто використовуй C ++ 11 скрізь" і закінчити з цим.
Марк Глісс,

5
Здається, CXX_STANDARD 11це не працює з GLOBALвластивістю, яка абсолютно відстій.
abergmeier

9
@MarcGlisse @abergmeier Щоб визначити це в поточному діапазоні CMake, ви можетеset(CMAKE_CXX_STANDARD 11)
typ1232

6
Я все ще маю деякі проблеми з цим підходом, оскільки він додає -std=gnu++11та дозволяє нестандартні розширення. Я дійсно хотів би std=c++11і отримати помилку лише в тому випадку, якщо спробую спочатку використати одне з цих нестандартних розширень, портативність.
user1942027

8
@RaphaelMiedl Я задав це запитання і отримав відповідь. дивіться посилання . В основному ви використовуєте SET (CMAKE_CXX_EXTENSIONS OFF).
doc07b5

42

На даний момент CMake не має зручної форми для підтримки C ++ 11. В ідеалі ви б вказали проект С ++ 11 таким чином:

project(foo CXX11)

на початку вашого CMakeLists.txt. Але CXX11тип проекту ще не існує (поки що). До цього часу ви можете використовувати двоступеневу техніку:

  1. Визначте тип і версію компілятора
  2. Відповідно відрегулюйте прапори збірки.

Наприклад, це те, що я використовую для підтримки C ++ 11 з Clang та GCC:

# Initialize CXXFLAGS.
set(CMAKE_CXX_FLAGS                "-Wall -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG          "-O0 -g")
set(CMAKE_CXX_FLAGS_MINSIZEREL     "-Os -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE        "-O4 -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")

# Compiler-specific C++11 activation.
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
    execute_process(
        COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
    if (NOT (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7))
        message(FATAL_ERROR "${PROJECT_NAME} requires g++ 4.7 or greater.")
    endif ()
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
else ()
    message(FATAL_ERROR "Your C++ compiler does not support C++11.")
endif ()

Зараз минуло більше шести місяців з моменту вашої відповіді. Чи знаєте ви про якісь оновлення? Дивно, що це питання ще не вирішено.
Джек Поулсон,

2
На жаль, я досі не знаю жодного кращого рішення. Можливо, це допомагає підняти цю стурбованість у списку розсилки CMake.
mavam

4
Привіт, Джек і Матіас, я знаю, що цей коментар запізнився, але схоже, що до CMake нарешті додана підтримка C ++ 11: public.kitware.com/Bug/view.php?id=13842 Я розумію, що це буде бути частиною CMake 3.1, який планується випустити на початку листопада.
Райан

1
Класно, як тільки це стане більш масовим, я оновлю відповідь. Схоже, тепер можна пов'язати версію С ++ із ціллю CMake через set_property(TARGET tgt PROPERTY CXX_STANDARD 11).
mavam

@LucasB, правда, але Ерік вже пропонує набагато приємнішу відповідь, яку ОП, мабуть, повинна прийняти в цей момент.
mavam

9

На момент написання цієї статті (до GCC 4.8) , виявляти прапори C ++ 11 та додавати їх може бути непогано. Це тому, що зміна стандарту (принаймні для GCC) порушує сумісність ABI , що може призвести до помилок посилання.

Отже, використання стандарту C ++ 11 має бути чітко зазначеним із налаштуванням компілятора під час початкової конфігурації CMake проекту , наприклад

CXX='g++ -std=c++11' cmake /path/to/source

Тобто, використання -std = c ++ 11 слід розглядати як окремий компілятор, який не слід змішувати чи змінювати в проекті.


Тепер, коли ми перебуваємо після GCC 4.8, ситуація така сама?
Джек Поулсон,

По суті, так. Або, можливо, потрібно буде додати визначення компілятора тощо. Більше інформації про GCC 5 знаходиться тут: developerblog.redhat.com/2015/02/05/gcc5-and-the-c11-abi
Matt McCormick

8

Використання:

include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
    message(FATAL_ERROR "Compiler ${CMAKE_CXX_COMPILER} has no C++11 support.")
endif()

Це з Увімкнення C ++ 11 (C ++ 0x) у CMake з незначними змінами.


2
Привіт! На жаль, ваше рішення працює лише для компіляторів, які підтримують вказані прапори для встановлення стандартної версії С ++. У випадку компілятора, який підтримує C ++ 11, але не підтримує ці прапори, ваше рішення дасть помилково негативне значення. Наприклад, MS VS 2015 не пройде.
Інокентій Алайцев

6

Якщо ви використовуєте CMake 3.8 або новішу версію, ви можете скористатися функцією cxx_std_11, яка вимагає C ++ 11 або вище :

target_compile_features(Hello PUBLIC cxx_std_11)

У CMake 3.1 * та новіших версіях правильний, простий спосіб зробити це - використовувати CXX_STANDARDвластивість для даної цілі. Наприклад, наведено цей простий приклад з використанням auto(named main.cpp):

#include <iostream>

int main() {
    auto num = 10;
    std::cout << num << std::endl;
    return 0;
}

Наступне CMakeLists.txtдозволить підтримку C ++ 11:

cmake_minimum_required(VERSION 3.3)
project(Hello CXX)

set(SOURCE_FILES main.cpp)
add_executable(Hello ${SOURCE_FILES})

set_property(TARGET Hello PROPERTY
    CXX_STANDARD 11
    CXX_STANDARD_REQUIRED ON
)

Це додасть будь-які необхідні прапори, такі як -std=c++11. Зауважте, що CXX_STANDARD_REQUIREDвластивість запобіжить розпаду стандарту до попередньої версії.


Ще один правильний, але не такий простий спосіб вказати те, CMAKE_CXX_KNOWN_FEATURESщо ви використовуєте, наприклад cxx_auto_type:

cmake_minimum_required(VERSION 3.3)
project(Hello CXX)

set(SOURCE_FILES main.cpp)
add_executable(Hello ${SOURCE_FILES})
target_compile_features(Hello PRIVATE cxx_auto_type)

* Я не пробував це на CMake 3.1, але переконався, що він працює в CMake 3.3. Документація 3.1 робить задокументувати тому він повинен працювати.


1

Ми написали модуль CMake для виявлення та включення підтримки C ++ 11, який ви можете знайти тут:
https://github.com/NitroShare/CXX11-CMake-Macros

Це все ще незавершене виробництво, але ми використовуємо його для ряду проектів Qt, орієнтованих на Windows / Linux / Mac. В даний час підтримуються лише MSVC ++, GCC та Clang.

Приклад:

include(CXX11)

check_for_cxx11_compiler(CXX11_COMPILER)

# If a C++11 compiler is available, then set the appropriate flags
if(CXX11_COMPILER)
    enable_cxx11()
endif()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.