web-dev-qa-db-fra.com

Comment détecter la prise en charge C ++ 11 d'un compilateur avec CMake

Existe-t-il un moyen de laisser CMake détecter automatiquement si un compilateur prend en charge C++ 11 ou non?

Comme il serait agréable d'informer les utilisateurs lors de l'exécution de CMake que le code ne sera pas compilé car le compilateur ne prend pas en charge C++ 11. Pour le moment, j'ai défini les drapeaux C++ 11. Cependant, si un compilateur ne le prend pas en charge, l'utilisateur obtient des erreurs de compilation au lieu d'une erreur lors de l'exécution de CMake.

Parfait serait quelque chose qui fonctionne comme find_package(). Cependant, je n'ai trouvé aucun module ou fonction fournissant les fonctionnalités nécessaires.

De plus, il serait bien d'avoir la fonctionnalité pour détecter si le compilateur a besoin des drapeaux std=c++0x ou std=c++11.

Y a-t-il quelque chose de disponible ou dois-je le développer moi-même?

Ci-dessous se trouve du code que j'utilise jusqu'à présent, mais il ne fonctionne qu'avec les compilateurs GNU'c GCC. Ce serait bien s'il y avait une solution plus générale.

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)
70
tune2fs

Si vous avez CMake version 3.1.0 ou ultérieure, vous pouvez détecter les fonctionnalités C++ prises en charge par votre compilateur 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()

Mais normalement, vous n'avez pas besoin d'utiliser la variable CMake CMAKE_CXX_COMPILE_FEATURES dans vos scripts CMake. Au lieu de cela, il existe deux façons de dire à CMake sous quelle norme C++ vos fichiers C++ doivent être compilés, soit en spécifiant explicitement la norme C++ soit en spécifiant les fonctionnalités C++ requises et en laissant CMake induire la norme C++. CMake s'assurera que le compilateur C++ est invoqué avec les indicateurs de ligne de commande corrects (par exemple -std = c ++ 11).

1. Spécification explicite de la norme C++

Vous pouvez spécifier explicitement la norme C++, en définissant les propriétés CMake CXX_STANDARD et CXX_STANDARD_REQUIRED pour votre cible 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. Spécifier les fonctionnalités C++ requises et laisser CMake induire la norme C++

Vous pouvez utiliser la commande CMake target_compile_features pour spécifier les fonctionnalités C++ qui sont utilisées dans votre cible CMake. À partir de cette liste, CMake induira le standard C++ à utiliser. La propriété globale CMake CMAKE_CXX_images._FEATURES répertorie les fonctionnalités C++ parmi lesquelles vous pouvez choisir.

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()

Par exemple, ce programme C++ avec le nom de fichier main.cc utilise les fonctionnalités de 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;
}

Ce fichier CMakeLists.txt le construirait

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})
94
Erik Sjölund

À ce stade, CMake n'a pas de formulaire pratique pour prendre en charge C++ 11. Idéalement, vous devez spécifier un projet C++ 11 comme ceci:

project(foo CXX11)

au début de votre CMakeLists.txt. Mais le CXX11 le type de projet n'existe pas (encore). Jusque-là, vous pouvez utiliser une technique en deux étapes:

  1. Déterminer le type et la version du compilateur
  2. Ajustez les drapeaux de construction en conséquence.

Par exemple, voici ce que j'utilise pour prendre en charge C++ 11 avec Clang et 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 ()
43
mavam

Au moment d'écrire ces lignes (pré-GCC 4.8) , ce n'est peut-être pas une bonne idée de détecter les drapeaux C++ 11 et de les ajouter. En effet, la modification de la norme (au moins pour GCC) rompt la compatibilité ABI , ce qui peut entraîner des erreurs de liaison.

Par conséquent, l'utilisation de la norme C++ 11 doit être explicitement spécifiée avec le paramètre du compilateur lors de la configuration CMake initiale du projet , par ex.

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

Autrement dit, l'utilisation de -std = c ++ 11 doit être traitée comme un compilateur distinct, qui ne doit pas être mélangé ou modifié dans un projet.

9
Matt McCormick

J'ai trouvé ce script CMake qui prétend faire exactement ce dont vous avez besoin. Il peut également vérifier les fonctionnalités individuelles de C++ 11. Je ne pense pas qu'il puisse décider entre std=C++0x et std=C++11 bien que.

9
Mark

Utilisation:

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()

Cela vient de Activation de C++ 11 (C++ 0x) dans CMake avec des modifications mineures.

8
nkout

Dans CMake 3.1 *, la manière simple et appropriée de procéder consiste à utiliser le CXX_STANDARD propriété pour une cible donnée. Par exemple, étant donné cet exemple simple utilisant auto (nommé main.cpp):

#include <iostream>

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

Le suivant CMakeLists.txt activera la prise en charge de 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
)

Cela ajoutera tous les indicateurs nécessaires tels que -std=c++11. Notez que le CXX_STANDARD_REQUIRED la propriété empêchera la norme de se décomposer en une version antérieure.


La manière appropriée et pas aussi simple de spécifier le CMAKE_CXX_KNOWN_FEATURES que vous utilisez, comme 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)

* Je n'ai pas essayé ceci sur CMake 3.1, mais j'ai vérifié que cela fonctionne dans CMake 3.3. Le documentation pour 3.1 documente cela donc cela devrait fonctionner.

4
Levi Morrison

Nous avons écrit un module CMake pour détecter et activer le support C++ 11 que vous pouvez trouver ici:
https://github.com/NitroShare/CXX11-CMake-Macros

C'est toujours un travail en cours, mais nous l'utilisons pour un certain nombre de projets Qt qui ciblent Windows/Linux/Mac. Actuellement, seuls MSVC++, GCC et Clang sont pris en charge.

Exemple:

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()
1
Nathan Osman