c++ utilizar Maneira moderna de definir sinalizadores de compilador no projeto cmake de plataforma cruzada




manual cmake (4)

Sua abordagem iria - como comentou @Tsyvarev - estar absolutamente bem, só porque você pediu pela "nova" abordagem no CMake, aqui está o que seu código traduziria:

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)

Você pega add_compile_options() e - como @ Al.G. comentou - "use as expressões do gerador sujo".

Existem algumas desvantagens das expressões geradoras:

  1. A muito útil expressão $<IF:...,...,...> está disponível apenas na versão CMake> = 3.8
  2. Você tem que escrever em uma única linha. Para evitá-lo eu usei a string(APPEND ...) , que você também pode usar para "otimizar" o seu set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ... chamadas.
  3. É difícil ler e entender. Por exemplo, os pontos e vírgulas são necessários para torná-lo uma lista de opções de compilação (caso contrário, o CMake irá citá-lo).

Portanto, é melhor usar uma abordagem compatível com versões mais legíveis e compatíveis com 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()

E sim, você não especifica mais explicitamente o padrão C ++, basta nomear o recurso C ++ de que seu código / destino depende de chamadas target_compile_features() .

Para este exemplo eu escolhi cxx_lambda_init_captures que por exemplo, um compilador GCC mais antigo deu o seguinte erro (como um exemplo, o que acontece se um compilador não suportar este recurso):

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

"GNU"

version 4.8.4.

E você precisa escrever um script wrapper para construir múltiplas configurações com um gerador de makefile de "configuração única" ou usar um IDE de "multi-configuração" como o Visual Studio.

Aqui estão as referências aos exemplos:

Então testei o seguinte com o suporte ao Open Folder Visual Studio 2017 CMake para combinar neste exemplo os compiladores cl , clang e mingw :

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

Portanto, você pode usar qualquer gerador CMake de dentro do Visual Studio 2017, há algumas citações não íntegras acontecendo (como em setembro de 2017, talvez corrigidas posteriormente) que requerem que o intermediário mingw32-make.cmd (removendo as aspas).

Eu quero escrever um arquivo cmake que define diferentes opções de compilador para clang ++, g ++ e MSVC em compilações de depuração e lançamento. O que estou fazendo atualmente é algo como isto:

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

Mas eu tenho alguns problemas com isso:

  1. Primeiro o trivial: há relly nenhum comando como appen que me permita substituir set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} Foo") com append(CMAKE_CXX_FLAGS "Foo") ?
  2. Eu li várias vezes, que um não deve definir manualmente CMAKE_CXX_FLAGS e variáveis ​​semelhantes, em primeiro lugar, mas não tenho certeza qual outro mecanismo para usar.
  3. Mais importante: A maneira que eu faço isso aqui, eu preciso de um diretório de compilação separado para cada compilador e configuração Idealmente eu gostaria de transformar isso em ter vários destinos no mesmo diretório para que eu possa, por exemplo, chamar make foo_debug_clang .

Então minhas perguntas são

  • a) Existe uma maneira melhor de escrever o roteiro cmake que resolve meus "pontos de dor"? solução para os pontos acima mencionados?
  • b) Existe algo como uma prática moderna e bem aceita de como configurar tais projetos?

A maioria das referências que eu encontrei na internet estão desatualizadas ou mostram apenas exemplos triviais. Atualmente estou usando cmake3.8, mas se isso faz alguma diferença, estou ainda mais interessado na resposta para versões mais recentes.


Abordando os dois primeiros pontos, mas não o terceiro:

  1. Eu li várias vezes, que um não deve definir manualmente CMAKE_CXX_FLAGS e variáveis ​​semelhantes, em primeiro lugar, mas não tenho certeza qual outro mecanismo para usar.

O comando que você quer é set_property . O CMake suporta um monte de propriedades - não tudo, mas muito - de uma maneira que evita o trabalho de fazer um trabalho específico do compilador. Por exemplo:

set_property(TARGET foo PROPERTY CXX_STANDARD 17)

o que para alguns compiladores resultará em --std=c++17 mas para os anteriores com --std=c++1z (antes que C ++ 17 fosse finalizado). ou:

set_property(TARGET foo APPEND PROPERTY COMPILE_DEFINITIONS HELLO WORLD)

O resultado será -DHELLO -DWORLD para gcc, clang e MSVC, mas para compiladores estranhos pode usar outros switches.

Existe relly nenhum comando como append que me permita substituir set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} Foo") com append(CMAKE_CXX_FLAGS "Foo")?

set_property pode ser usado no modo set, ou no modo append (veja os exemplos acima).

Eu não posso dizer se isso é preferível a add_compile_options ou target_compile_features , no entanto.


Outra maneira é usar arquivos .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}")

o que pode facilitar a inclusão de opções múltiplas e esotéricas.


Você pode usar target_compile_options () para "acrescentar" opções de compilação.





cmake