46 votes

Méthode moderne pour définir les drapeaux du compilateur dans le projet cmake multiplateforme

Je veux écrire un cmake fichier qui définit les différentes options du compilateur pour clang++, g++ et MSVC en debug et release. Ce que je fais actuellement ressemble à quelque chose comme ceci:

if(MSVC)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++latest /W4")
    # Default debug flags are OK 
    set(CMAKE_CXX_FLAGS_RELEASE "{CMAKE_CXX_FLAGS_RELEASE} /O2")
else()
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1z -Wall -Wextra -Werror")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} some other flags")
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")

    if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
    else()
        # nothing special for gcc at the moment
    endif()
endif()

Mais j'ai quelques problèmes:

  1. D'abord le trivial: Est-il relly pas de commande comme appen qui me permettrait de remplacer set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} Foo") avec append(CMAKE_CXX_FLAGS "Foo")?
  2. J'ai lu plusieurs fois, que l'on ne doit pas définir manuellement CMAKE_CXX_FLAGS et semblables, variables en premier lieu, mais im pas sûr de ce que d'autres mécanisme à utiliser.
  3. Le plus important: La façon dont je le fais ici, j'ai besoin d'un répertoire de construction pour chaque compilateur et de la configuration Idéalement, je voudrais les transformer en havin cibles multiples dans le même répertoire, donc je peut, par exemple, appel make foo_debug_clang.

Donc mes questions sont les

  • a) Est-il une meilleure façon d'écrire th cmake script qui résout mon "points de douleur"? solution pour les points mentionnés ci-dessus?
  • b) Est-il quelque chose comme un acceptées, moderne, meilleures pratiques de façon à mettre en place de tels projets?

La plupart des références que j'ai pu trouver sur internet sont soit obsolètes ou afficher seulement les exemples triviaux. Je suis actuellement en utilisant cmake3.8, mais si cela fait une différence, je suis encore plus intéressé par la réponse pour des versions plus récentes.

24voto

Florian Points 17972

Votre approche - comme @Tsyvarev a commenté être absolument parfait, simplement depuis que vous avez demandé pour la "nouvelle approche" dans CMake voici ce que votre code peut traduire:

cmake_minimum_required(VERSION 3.8)

project(HelloWorld)

string(
    APPEND _opts
    "$<IF:$<CXX_COMPILER_ID:MSVC>,"
        "/W4;$<$<CONFIG:RELEASE>:/O2>,"
        "-Wall;-Wextra;-Werror;"
            "$<$<CONFIG:RELEASE>:-O3>"
            "$<$<CXX_COMPILER_ID:Clang>:-stdlib=libc++>"
    ">"
)

add_compile_options("${_opts}")

add_executable(HelloWorld "main.cpp")

target_compile_features(HelloWorld PUBLIC cxx_lambda_init_captures)

Vous prenez add_compile_options() , et - comme @Al.G. a commenté: "utiliser le sale générateur d'expressions".

Il ya quelques inconvénients de générateur d'expressions:

  1. Le très utile $<IF:...,...,...> expression n'est disponible que dans la version de CMake >= 3.8
  2. Vous devez l'écrire en une seule ligne. Pour éviter cela, j'ai utilisé l' string(APPEND ...), que vous pouvez également l'utiliser pour "optimiser" votre set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ... des appels.
  3. C'est difficile à lire et à comprendre. E. g. les points-virgules sont nécessaires pour en faire une liste des options de compilation (sinon CMake va le citer).

Donc mieux vaut utiliser un plus lisible et rétro-compatible avec approche add_compile_options():

if(MSVC)
    add_compile_options("/W4" "$<$<CONFIG:RELEASE>:/O2>")
else()
    add_compile_options("-Wall" "-Wextra" "-Werror" "$<$<CONFIG:RELEASE>:-O3>")
    if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
        add_compile_options("-stdlib=libc++")
    else()
        # nothing special for gcc at the moment
    endif()
endif()

Et oui, vous n'avez pas spécifier explicitement la norme C++ plus, vous venez de nommer le C++ fonction de votre code/cible ne dépend avec target_compile_features() des appels.

Pour cet exemple j'ai choisi cxx_lambda_init_captures qui serait par exemple un vieux compilateur GCC donner le message d'erreur suivant (comme un exemple de ce qui se passe si un compilateur ne prend pas en charge cette fonction):

The compiler feature "cxx_lambda_init_captures" is not known to CXX compiler

"GNU"

version 4.8.4.

Et vous devez écrire un script pour construire de multiples configurations avec un "simple" configuration du générateur de makefile ou de l'utilisation d'un "multi" configuration de l'IDE Visual Studio.

Voici les références à des exemples:

J'ai donc testé la suivante avec l' Open Folder Visual Studio 2017 CMake à allier dans cet exemple, la , et compilateurs:

Configurations

CMakeSettings.json

{
    // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file.
    "configurations": [
        {
            "name": "x86-Debug",
            "generator": "Visual Studio 15 2017",
            "configurationType": "Debug",
            "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
            "buildCommandArgs": "-m -v:minimal",
        },
        {
            "name": "x86-Release",
            "generator": "Visual Studio 15 2017",
            "configurationType": "Release",
            "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
            "buildCommandArgs": "-m -v:minimal",
        },
        {
            "name": "Clang-Debug",
            "generator": "Visual Studio 15 2017",
            "configurationType": "Debug",
            "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
            "cmakeCommandArgs": "-T\"LLVM-vs2014\"",
            "buildCommandArgs": "-m -v:minimal",
        },
        {
            "name": "Clang-Release",
            "generator": "Visual Studio 15 2017",
            "configurationType": "Release",
            "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
            "cmakeCommandArgs": "-T\"LLVM-vs2014\"",
            "buildCommandArgs": "-m -v:minimal",
        },
        {
            "name": "GNU-Debug",
            "generator": "MinGW Makefiles",
            "configurationType": "Debug",
            "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
            "variables": [
                {
                    "name": "CMAKE_MAKE_PROGRAM",
                    "value": "${projectDir}\\mingw32-make.cmd"
                }
            ]
        },
        {
            "name": "GNU-Release",
            "generator": "Unix Makefiles",
            "configurationType": "Release",
            "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
            "variables": [
                {
                    "name": "CMAKE_MAKE_PROGRAM",
                    "value": "${projectDir}\\mingw32-make.cmd"
                }
            ]
        }
    ]
}

mingw32-make.cmd

@echo off
mingw32-make.exe %~1 %~2 %~3 %~4

Ainsi, vous pouvez utiliser n'importe quel CMake générateur dans Visual Studio 2017, il y a quelque malsaine citant passe (comme pour le mois de septembre 2017, peut-être corrigé plus tard), qui exige que l' mingw32-make.cmd intermédiaire (en enlevant les guillemets).

3voto

einpoklum Points 2893

Aborder les deux premiers points, mais pas le troisième:

  1. J'ai lu plusieurs fois, que l'on ne doit pas définir manuellement CMAKE_CXX_FLAGS et semblables, variables en premier lieu, mais im pas sûr de ce que d'autres mécanisme à utiliser.

La commande que vous voulez est - set_property. CMake prend en charge un tas de propriétés - pas tout, mais beaucoup - d'une manière qui vous permet d'économiser la peine de le faire par le compilateur de travail spécifique. Par exemple:

set_property(TARGET foo PROPERTY CXX_STANDARD 17)

qui pour certains compilateurs résultat en --std=c++17 mais pour les précédentes avec --std=c++1z (avant le C++17 a été finalisé). ou:

set_property(TARGET foo APPEND PROPERTY COMPILE_DEFINITIONS HELLO WORLD)

entraînera c' -DHELLO -DWORLD pour gcc, clang et MSVC mais pour bizarre compilateurs peuvent utiliser d'autres commutateurs.

Est-il relly pas de commande append qui me permettrait de remplacer set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} Foo") avec append(CMAKE_CXX_FLAGS "Foo")?

set_property peut être utilisé soit en mode ou en mode ajout (voir les exemples ci-dessus).

Je ne peux pas dire si cela est préférable à l' add_compile_options ou target_compile_features, cependant.

1voto

Richard Hodges Points 1972

Une autre façon consiste à utiliser des fichiers .rsp.

 set(rsp_file "${CMAKE_CURRENT_BINARY_DIR}/my.rsp")
configure_file(my.rsp.in ${rsp_file} @ONLY)
target_compile_options(mytarget PUBLIC "@${rsp_file}")
 

ce qui pourrait faciliter l'inclusion d'options multiples et ésotériques.

1voto

user8537712 Points 31

Vous pouvez utiliser target_compile_options () pour "ajouter" les options de compilation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X