Forma moderna de establecer indicadores de comstackción en el proyecto cmake multiplataforma

Quiero escribir un archivo cmake que establezca diferentes opciones de comstackción para clang ++, g ++ y MSVC en comstackciones de depuración y liberación. Lo que estoy haciendo actualmente se ve así:

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

Pero tengo un par de problemas con esto:

  1. Primero, lo trivial: ¿no hay realmente ningún comando como appen que me permita reemplazar el set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} Foo") con append(CMAKE_CXX_FLAGS "Foo") ?
  2. He leído varias veces, que uno no debe establecer manualmente CMAKE_CXX_FLAGS y variables similares en primer lugar, pero no estoy seguro de qué otro mecanismo usar.
  3. Lo más importante: la forma en que lo hago aquí, necesito un directorio de comstackción separado para cada comstackdor y configuración. Idealmente, me gustaría transformar eso en tener múltiples objectives en el mismo directorio para poder, por ejemplo, llamar a make foo_debug_clang .

Entonces mis preguntas son

  • a) ¿Hay una mejor manera de escribir la secuencia de comandos de cmake que resuelve mis “puntos de dolor”? solución a los puntos mencionados anteriormente?
  • b) ¿Hay algo así como una mejor práctica aceptada y moderna de cómo configurar tales proyectos?

La mayoría de las referencias que pude encontrar en Internet están desactualizadas o muestran solo ejemplos triviales. Actualmente uso cmake3.8, pero si eso marca alguna diferencia, estoy aún más interesado en la respuesta para las versiones más recientes.

Su enfoque sería, como @Tsyvarev ha comentado, absolutamente correcto, ya que ha pedido el “nuevo” enfoque en CMake, aquí es a lo que se traduciría su código:

 cmake_minimum_required(VERSION 3.8) project(HelloWorld) string( APPEND _opts "$," "/W4;$<$:/O2>," "-Wall;-Wextra;-Werror;" "$<$:-O3>" "$<$:-stdlib=libc++>" ">" ) add_compile_options("${_opts}") add_executable(HelloWorld "main.cpp") target_compile_features(HelloWorld PUBLIC cxx_lambda_init_captures) 

Toma add_compile_options() y – como @ Al.G. ha comentado – “use las expresiones del generador sucio”.

Hay algunas desventajas de las expresiones del generador:

  1. La muy útil expresión $ solo está disponible en CMake versión> = 3.8
  2. Tienes que escribirlo en una sola línea. Para evitarlo, utilicé la string(APPEND ...) , que también puede usar para “optimizar” su set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ... llamadas.
  3. Es difícil de leer y entender Por ejemplo, los puntos y comas son necesarios para que sea una lista de opciones de comstackción (de lo contrario, CMake lo citará).

Así que es mejor utilizar un enfoque más legible y compatible con versiones anteriores con add_compile_options() :

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

Y sí, ya no especifica explícitamente el estándar C ++, simplemente llama a la función C ++ que su código / destino depende de las llamadas target_compile_features() .

Para este ejemplo, he elegido cxx_lambda_init_captures que, por ejemplo, para un comstackdor GCC anterior, da el siguiente error (como un ejemplo de lo que sucede si un comstackdor no admite esta característica):

 The compiler feature "cxx_lambda_init_captures" is not known to CXX compiler "GNU" version 4.8.4. 

Y necesita escribir un script de contenedor para crear múltiples configuraciones con un generador de archivos make “de configuración única” o usar un IDE de “configuración múltiple” como Visual Studio.

Aquí están las referencias a ejemplos:

  • ¿CMake siempre genera configuraciones para todas las posibles configuraciones de proyectos?
  • ¿Cómo le digo a CMake que use Clang en Windows?
  • Cómo agregar la comstackción de Linux al proyecto Cmake en Visual Studio

Así que probé lo siguiente con el soporte de Open Folder Visual Studio 2017 CMake para combinar en este ejemplo los comstackdores cl , clang y mingw :

Configuraciones

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 

De modo que puede usar cualquier generador de CMake desde Visual Studio 2017, hay algunas citas insalubres en curso (en septiembre de 2017, tal vez corregidas más adelante) que requieren el intermediario mingw32-make.cmd (eliminando las comillas).

Otra forma es usar archivos .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}") 

lo que podría facilitar la administración de la inclusión de opciones múltiples y esotéricas.

Puede usar target_compile_options () para “agregar” opciones de comstackción.

Abordar los dos primeros puntos, pero no el tercero:

  1. He leído varias veces, que uno no debe establecer manualmente CMAKE_CXX_FLAGS y variables similares en primer lugar, pero no estoy seguro de qué otro mecanismo usar.

El comando que desea es set_property . CMake admite un conjunto de propiedades, no todo, pero muchas, de forma que le ahorra la molestia de realizar un trabajo específico del comstackdor. Por ejemplo:

 set_property(TARGET foo PROPERTY CXX_STANDARD 17) 

que para algunos comstackdores dará como resultado --std=c++17 pero para los anteriores con --std=c++1z (antes de que finalizara C ++ 17). o:

 set_property(TARGET foo APPEND PROPERTY COMPILE_DEFINITIONS HELLO WORLD) 

resultará en -DHELLO -DWORLD para gcc, clang y MSVC, pero para los comstackdores extraños podrían usar otros switches.

¿Realmente no hay un comando como append que me permita reemplazar el set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} Foo") con append(CMAKE_CXX_FLAGS "Foo")?

set_property se puede usar en el modo de configuración o en el modo de adición (ver ejemplos anteriores).

target_compile_features embargo, no puedo decir si esto es preferible a add_compile_options o target_compile_features .