So erkennen Sie die C ++ 11-Unterstützung eines Compilers mit CMake

77

Gibt es eine Möglichkeit, CMake automatisch erkennen zu lassen, ob ein Compiler C ++ 11 unterstützt oder nicht?

Es wäre schön, die Benutzer während des CMake-Laufs darüber zu informieren, dass der Code nicht kompiliert wird, da der Compiler C ++ 11 nicht unterstützt. Im Moment habe ich die C ++ 11 Flags gesetzt. Wenn ein Compiler dies jedoch nicht unterstützt, erhält der Benutzer Kompilierungsfehler anstelle eines Fehlers während des CMake-Laufs.

Perfekt wäre etwas, das funktioniert find_package(). Ich habe jedoch kein Modul oder keine Funktion gefunden, die die erforderliche Funktionalität bietet.

Zusätzlich wäre es schön, die Funktion zu haben, um zu erkennen, ob der Compiler die Flags std=c++0xoder benötigt std=c++11.

Ist etwas verfügbar oder muss ich es selbst entwickeln?

Unten ist ein Code, den ich bisher verwende, der jedoch nur mit GNU'c GCC-Compilern funktioniert. Es wäre schön, wenn es eine allgemeinere Lösung gäbe.

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)
tune2fs
quelle
Warum sollten Sie den add_definitionsBefehl anstelle der Einstellung verwenden CMAKE_CXX_FLAGS, um die Compileroptionen festzulegen?
Li Dong

Antworten:

103

Wenn Sie CMake Version 3.1.0 oder höher haben, können Sie feststellen, welche C ++ - Funktionen Ihr C ++ - Compiler unterstützt

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

Normalerweise müssen Sie die CMake-Variable CMAKE_CXX_COMPILE_FEATURES in Ihren CMake-Skripten nicht verwenden. Stattdessen gibt es zwei Möglichkeiten, wie Sie CMake mitteilen können, unter welchem ​​C ++ - Standard Ihre C ++ - Dateien kompiliert werden sollen, indem Sie entweder den C ++ - Standard explizit angeben oder die erforderlichen C ++ - Funktionen angeben und CMake den C ++ - Standard einführen lassen. CMake stellt sicher, dass der C ++ - Compiler mit den richtigen Befehlszeilenflags aufgerufen wird (z. B. -std = c ++ 11).

1. Geben Sie den C ++ - Standard explizit an

Sie können den C ++ - Standard explizit angeben, indem Sie die CMake-Eigenschaften CXX_STANDARD und CXX_STANDARD_REQUIRED für Ihr CMake-Ziel festlegen .

$ 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. Geben Sie die erforderlichen C ++ - Funktionen an und lassen Sie CMake den C ++ - Standard einführen

Mit dem CMake-Befehl target_compile_features können Sie die C ++ - Funktionen angeben, die in Ihrem CMake-Ziel verwendet werden. Aus dieser Liste veranlasst CMake die Verwendung des C ++ - Standards. Die globale CMake-Eigenschaft CMAKE_CXX_KNOWN_FEATURES listet die C ++ - Funktionen auf, aus denen Sie auswählen können.

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

Dieses C ++ - Programm mit dem Dateinamen main.cc verwendet beispielsweise die C ++ 11-Funktionen: 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;
}

Diese CMakeLists.txt-Datei würde es erstellen

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})
Erik Sjölund
quelle
12
Es ist ein bisschen schmerzhaft, dass dies pro Ziel gemacht werden muss. Es wäre bequem zu sagen "benutze C ++ 11 einfach überall" und sei damit fertig.
Marc Glisse
5
Scheint, als würde CXX_STANDARD 11es nicht mit dem GLOBALEigenschaftsbereich funktionieren , was total scheiße ist.
Abergmeier
9
@MarcGlisse @abergmeier Um es im aktuellen CMake-Bereich zu definieren, können Sieset(CMAKE_CXX_STANDARD 11)
typ1232
6
Ich habe immer noch einige Probleme mit diesem Ansatz, da er -std=gnu++11nicht standardmäßige Erweiterungen hinzufügt und aktiviert. Ich möchte wirklich nur std=c++11eine Fehlermeldung erhalten, wenn ich versuche, eine dieser nicht standardmäßigen Erweiterungen zu verwenden, Portabilität zuerst.
user1942027
8
@ RaphaelMiedl Ich habe diese Frage gestellt und eine Antwort bekommen. siehe Link . Grundsätzlich verwenden Sie SET (CMAKE_CXX_EXTENSIONS OFF).
doc07b5
42

Derzeit verfügt CMake nicht über ein geeignetes Formular zur Unterstützung von C ++ 11. Idealerweise würden Sie ein C ++ 11-Projekt wie folgt angeben:

project(foo CXX11)

am Anfang Ihres CMakeLists.txt. Der CXX11Projekttyp existiert aber (noch) nicht. Bis dahin können Sie eine zweistufige Technik anwenden:

  1. Bestimmen Sie den Compilertyp und die Version
  2. Passen Sie die Build-Flags entsprechend an.

Zum Beispiel unterstütze ich damit C ++ 11 mit Clang und 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 ()
Mavam
quelle
Seit Ihrer Antwort sind mehr als sechs Monate vergangen. Kennen Sie Updates? Es scheint seltsam, dass dieses Problem noch nicht behoben ist.
Jack Poulson
2
Leider ist mir noch keine bessere Lösung bekannt. Vielleicht hilft es, dieses Problem auf der CMake-Mailingliste anzusprechen.
Mavam
4
Hallo Jack und Matthias, ich weiß, dass dieser Kommentar sehr spät ist, aber es sieht so aus, als ob CMake endlich C ++ 11-Unterstützung hinzugefügt wurde: public.kitware.com/Bug/view.php?id=13842 Mein Verständnis ist, dass dies der Fall sein wird Seien Sie Teil von CMake 3.1, dessen Veröffentlichung für Anfang November geplant ist.
Ryan
1
Cool, sobald dies mehr Mainstream wird, werde ich die Antwort aktualisieren. Es sieht so aus, als könnte man jetzt die C ++ - Version über mit einem CMake-Ziel verknüpfen set_property(TARGET tgt PROPERTY CXX_STANDARD 11).
Mavam
@LucasB, stimmt das, aber Eric liefert bereits eine viel schönere Antwort, die das OP an dieser Stelle wahrscheinlich akzeptieren sollte.
Mavam
9

Zum Zeitpunkt dieses Schreibens (vor GCC 4.8) ist es möglicherweise keine gute Idee, C ++ 11-Flags zu erkennen und hinzuzufügen. Dies liegt daran, dass eine Änderung des Standards (zumindest für GCC) die ABI-Kompatibilität beeinträchtigt , was zu Verbindungsfehlern führen kann.

Daher sollte die Verwendung des C ++ 11-Standards bei der anfänglichen CMake-Konfiguration des Projekts explizit mit der Compilereinstellung angegeben werden , z

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

Das heißt, die Verwendung von -std = c ++ 11 sollte wie ein separater Compiler behandelt werden, der in einem Projekt nicht gemischt oder geändert werden darf.

Matt McCormick
quelle
Ist die Situation nach dem GCC 4.8 dieselbe?
Jack Poulson
Im Wesentlichen ja. Oder es muss möglicherweise eine Compilerdefinition hinzugefügt werden usw. Weitere Informationen zu GCC 5 finden Sie hier: developerblog.redhat.com/2015/02/05/gcc5-and-the-c11-abi
Matt McCormick
8

Verwenden:

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

Dies ist auf das Aktivieren von C ++ 11 (C ++ 0x) in CMake mit geringfügigen Änderungen zurückzuführen.

nkout
quelle
2
Hallo! Leider funktioniert Ihre Lösung nur für Compiler, die bestimmte Flags zum Festlegen der C ++ - Standardversion unterstützen. Im Falle eines Compilers, der C ++ 11 unterstützt, diese Flags jedoch nicht unterstützt, gibt Ihre Lösung ein falsches Negativ aus. Beispielsweise wird MS VS 2015 nicht bestanden.
Innokentiy Alaytsev
6

Wenn Sie CMake 3.8 oder höher verwenden, können Sie die Funktion verwenden cxx_std_11, die C ++ 11 oder höher anfordert :

target_compile_features(Hello PUBLIC cxx_std_11)

In CMake 3.1 * und höher besteht die richtige und einfache Möglichkeit darin, die CXX_STANDARDEigenschaft für ein bestimmtes Ziel zu verwenden. Beispiel für dieses einfache Beispiel mit auto(benannt main.cpp):

#include <iostream>

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

Folgendes CMakeLists.txtaktiviert die C ++ 11-Unterstützung:

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
)

Dadurch werden alle erforderlichen Flags hinzugefügt, z -std=c++11. Beachten Sie, dass die CXX_STANDARD_REQUIREDEigenschaft verhindert, dass der Standard auf eine frühere Version abfällt.


Eine weitere geeignete , aber nicht so einfache Art und Weise die angeben , CMAKE_CXX_KNOWN_FEATURESdass Sie verwenden, wie zum Beispiel 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)

* Ich habe dies in CMake 3.1 nicht versucht, aber überprüft, dass es in CMake 3.3 funktioniert. Die Dokumentation zu 3.1 dokumentiert dies, sodass es funktionieren sollte.

Levi Morrison
quelle
1

Wir haben ein CMake-Modul zum Erkennen und Aktivieren der C ++ 11-Unterstützung geschrieben, das Sie hier finden:
https://github.com/NitroShare/CXX11-CMake-Macros

Es ist noch in Arbeit, aber wir verwenden es für eine Reihe von Qt-Projekten, die auf Windows / Linux / Mac abzielen. Derzeit werden nur MSVC ++, GCC und Clang unterstützt.

Beispiel:

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