¿Cómo dividir cadenas en varias líneas en CMake?


96

Por lo general, tengo una política en mi proyecto, para nunca crear líneas en archivos de texto que excedan una longitud de línea de 80, por lo que son fácilmente editables en todo tipo de editores (ya conoce el trato). Pero con CMake tengo el problema de que no sé cómo dividir una cadena simple en varias líneas para evitar una línea enorme. Considere este código básico:

set(MYPROJ_VERSION_MAJOR "1")
set(MYPROJ_VERSION_MINOR "0")
set(MYPROJ_VERSION_PATCH "0")
set(MYPROJ_VERSION_EXTRA "rc1")
set(MYPROJ_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-${VERSION_EXTRA}")

Ya supera el límite de 80 líneas. Entonces, ¿cómo puedo dividir una línea en CMake en varias líneas sin llegar a ser detallado (múltiples list(APPEND ...)o similares)?

Respuestas:


88

Actualización para CMake 3.0 y versiones posteriores :

la continuación de línea es posible con \. ver cmake-3.0-doc

message("\
This is the first line of a quoted argument. \
In fact it is the only line but since it is long \
the source code uses line continuation.\
")

Disponibilidad de versiones de CMake:

Debian Wheezy (2013): 2.8.9
Debian Wheezy-backports: 2.8.11
Debian Jessy (2015): 3.0.2
Ubuntu 14.04 (LTS): 2.8.12
Ubuntu 15.04: 3.0.2
Mac OSX: cmake-3 disponible a través de Homebrew , Macports y Fink
Windows: cmake-3 disponible a través de Chocolatey


20
El problema con el enfoque de CMake 3.0 es que no ignora la sangría. Lo que significa que las cadenas de varias líneas no se pueden sangrar con el resto del código.
void.pointer

@ void.pointer Me encontré con el mismo problema, ¿descubrió cómo sangrar con varias líneas?
user3667089

Además, si desea usar sangría y tiene un límite de 80 caracteres, otra forma es hacer esto: <code> message ("Este es el valor de la variable:" <br> "$ {varValue}") </code>
munsingh

54

CMake 3.0 y más reciente

Usa el string(CONCAT)comando:

set(MYPROJ_VERSION_MAJOR "1")
set(MYPROJ_VERSION_MINOR "0")
set(MYPROJ_VERSION_PATCH "0")
set(MYPROJ_VERSION_EXTRA "rc1")
string(CONCAT MYPROJ_VERSION "${MYPROJ_VERSION_MAJOR}"
                             ".${MYPROJ_VERSION_MINOR}"
                             ".${MYPROJ_VERSION_PATCH}"
                             "-${MYPROJ_VERSION_EXTRA}")

Aunque CMake 3.0 y las versiones más recientes admiten la continuación de la línea de los argumentos entre comillas , no puede sangrar la segunda línea o las siguientes sin obtener el espacio en blanco de sangría incluido en su cadena.

CMake 2.8 y anteriores

Puede utilizar una lista. Cada elemento de la lista se puede colocar en una nueva línea:

set(MYPROJ_VERSION_MAJOR "1")
set(MYPROJ_VERSION_MINOR "0")
set(MYPROJ_VERSION_PATCH "0")
set(MYPROJ_VERSION_EXTRA "rc1")
set(MYPROJ_VERSION_LIST "${MYPROJ_VERSION_MAJOR}"
                        ".${MYPROJ_VERSION_MINOR}"
                        ".${MYPROJ_VERSION_PATCH}"
                        "-${MYPROJ_VERSION_EXTRA}")

Una lista utilizada sin comillas se concatena sin espacios en blanco:

message(STATUS "Version: " ${MYPROJ_VERSION_LIST})
-- Version: 1.0.0-rc1

Si realmente necesita una cadena, puede convertir la lista en una cadena primero:

string(REPLACE ";" "" MYPROJ_VERSION "${MYPROJ_VERSION_LIST}")
message(STATUS "Version: ${MYPROJ_VERSION}")
-- Version: 1.0.0-rc1

Cualquier punto y coma en sus cadenas originales se verá como separador de elementos de lista y se eliminará. Deben escapar:

set(MY_LIST "Hello World "
            "with a \;semicolon")

1
Para líneas muy largas, una nueva línea después del nombre de la variable mejora aún más este patrón (innecesario en este ejemplo).
sabio

Por curiosidad , ¿sería correcto adivinar que las comillas dobles en la cadena también deben escaparse con una barra invertida, y también las barras invertidas que deben aparecer como un carácter en la cadena?
Jonathan Leffler

@JonathanLeffler Sí, necesitarían escapar. Las reglas del idioma están aquí: cmake.org/cmake/help/latest/manual/… pero se vuelve confuso. Véase también: stackoverflow.com/a/40728463
Douglas Royds

9

Todavía es un poco detallado, pero si el límite de 80 caracteres realmente te molesta, puedes agregar repetidamente la misma variable:

set(MYPROJ_VERSION_MAJOR "1")
set(MYPROJ_VERSION_MINOR "0")
set(MYPROJ_VERSION_PATCH "0")
set(MYPROJ_VERSION_EXTRA "rc1")
set(MYPROJ_VERSION "${MYPROJ_VERSION_MAJOR}.")
set(MYPROJ_VERSION "${MYPROJ_VERSION}${MYPROJ_VERSION_MINOR}.")
set(MYPROJ_VERSION "${MYPROJ_VERSION}${MYPROJ_VERSION_PATCH}-")
set(MYPROJ_VERSION "${MYPROJ_VERSION}${MYPROJ_VERSION_EXTRA}")
message(STATUS "version: ${MYPROJ_VERSION}")

Da salida:

$ cmake  ~/project/tmp
-- version: 1.0.0-rc1
-- Configuring done
-- Generating done
-- Build files have been written to: /home/rsanderson/build/temp

7

No hay forma de dividir un literal de cadena en varias líneas en archivos CMakeLists.txt o en scripts CMake. Si incluye una línea nueva dentro de una cadena, habrá una línea nueva literal en la cadena en sí.

# Don't do this, it won't work, MYPROJ_VERSION will contain newline characters:
set(MYPROJ_VERSION "${VERSION_MAJOR}.
  ${VERSION_MINOR}.${VERSION_PATCH}-
  ${VERSION_EXTRA}")

Sin embargo, CMake usa espacios en blanco para separar argumentos, por lo que puede cambiar un espacio que es un separador de argumentos en una nueva línea en cualquier lugar que desee, sin cambiar el comportamiento.

Podrías reformular esta línea más larga:

set(MYPROJ_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-${VERSION_EXTRA}")

como estas dos líneas más cortas:

set(MYPROJ_VERSION
  "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-${VERSION_EXTRA}")

Son completamente equivalentes.


5

El ejemplo de la pregunta original trata solo de una cadena relativamente corta. Para cadenas más largas (incluidos los ejemplos dados en otras respuestas), un argumento de corchetes podría ser mejor. De la documentación:

Un corchete de apertura se escribe [seguido de cero o más =seguido de [. El corchete de cierre correspondiente se escribe ]seguido del mismo número de =seguido de ]. Los soportes no encajan. Siempre se puede elegir una longitud única para los soportes de apertura y cierre para contener soportes de cierre de otras longitudes.

[...]

Por ejemplo:

message([=[
This is the first line in a bracket argument with bracket length 1.
No \-escape sequences or ${variable} references are evaluated.
This is always one argument even though it contains a ; character.
The text does not end on a closing bracket of length 0 like ]].
It does end in a closing bracket of length 1.
]=])```

Si entiendo esto correctamente, un salto de línea en la fuente también introducirá un salto de línea en la cadena. Por ejemplo, habrá un \ n después de la "longitud 1". ¿Alguna forma de evitar eso?
Lukas Schmelzeisen

Eso es correcto, habrá \ns. Si no quiere eso, no creo que los argumentos entre corchetes sean su solución.
ingomueller.net

Por cierto, es válido para la versión 3.x, no 2.x
Maxim Suslov

4

Para aquellos que fueron traídos aquí desde ¿Cómo divido una expresión generadora de CMake en varias líneas? Me gustaría agregar algunas notas.

El método de continuación de línea no funcionará, CMake no puede analizar una lista generadora hecha con espacios en blanco (sangría) y continuación de línea.

Si bien la solución de cadena (CONCAT) proporcionará una expresión generadora que se puede evaluar, la expresión evaluada estará rodeada de comillas si el resultado contiene un espacio.

Para que se agregue cada opción individual, se debe construir una lista de generador separada, por lo que las opciones de apilamiento como lo he hecho a continuación harán que la compilación falle:

string(CONCAT WARNING_OPTIONS "$<"
    "$<OR:"
        "$<CXX_COMPILER_ID:MSVC>,"
        "$<STREQUAL:${CMAKE_CXX_SIMULATE_ID},MSVC>"
    ">:"
    "/D_CRT_SECURE_NO_WARNINGS "
">$<"
    "$<AND:"
        "$<CXX_COMPILER_ID:Clang,GNU>,"
        "$<NOT:$<STREQUAL:${CMAKE_CXX_SIMULATE_ID},MSVC>>"
    ">:"
    "-Wall -Werror "
">$<"
    "$<CXX_COMPILER_ID:GNU>:"
    "-Wno-multichar -Wno-sign-compare "
">")
add_compile_options(${WARNING_OPTIONS})

Esto se debe a que las opciones resultantes se pasan al compilador entre comillas.

/usr/lib64/ccache/c++  -DGTEST_CREATE_SHARED_LIBRARY=1 -Dgtest_EXPORTS -I../ThirdParty/googletest/googletest/include -I../ThirdParty/googletest/googletest -std=c++11 -fno-rtti -fno-exceptions -fPIC    -std=c++11 -fno-rtti -fno-exceptions -Wall -Wshadow -DGTEST_HAS_PTHREAD=1 -fexceptions -Wextra -Wno-unused-parameter -Wno-missing-field-initializers "-Wall -Werror -Wno-multichar -Wno-sign-compare " -fdiagnostics-color -MD -MT ThirdParty/googletest/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o -MF ThirdParty/googletest/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o.d -o ThirdParty/googletest/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o -c ../ThirdParty/googletest/googletest/src/gtest-all.cc
c++: error: unrecognized command line option ‘-Wall -Werror -Wno-multichar -Wno-sign-compare ’

Para evaluar expresiones generadoras largas representadas mediante la solución de cadena (CONCAT), cada expresión generadora debe evaluar una sola opción sin espacios:

string(CONCAT WALL "$<"
    "$<AND:"
        "$<CXX_COMPILER_ID:Clang,GNU>,"
        "$<NOT:$<STREQUAL:${CMAKE_CXX_SIMULATE_ID},MSVC>>"
    ">:"
    "-Wall"
">")
string(CONCAT WERROR "$<"
    "$<AND:"
        "$<CXX_COMPILER_ID:Clang,GNU>,"
        "$<NOT:$<STREQUAL:${CMAKE_CXX_SIMULATE_ID},MSVC>>"
    ">:"
    "-Werror"
">")
message(STATUS "Warning Options: " ${WALL} ${WERROR})
add_compile_options(${WALL} ${WERROR})

Esto puede no estar relacionado con la pregunta a la que estoy publicando una respuesta; desafortunadamente, la pregunta que estoy respondiendo está marcada incorrectamente como un duplicado de esta pregunta.

Las listas de generadores no se manejan ni analizan de la misma manera que las cadenas, y debido a esto, hay medidas adicionales que se deben tomar para dividir una lista de generadores en varias líneas.


Probablemente valdría la pena publicar también una versión de esta respuesta en el "duplicado" vinculado. Esta fue la respuesta que estaba buscando cuando la encontré.
Keith Prussing

3

Para mantener una buena sangría en su código, es bastante sencillo hacerlo

message("These strings " "will all be "
        "concatenated. Don't forget "
        "your trailing spaces!")

O forma una cadena directamente con

string(CONCAT MYSTR "This and " "that "
                    "and me too!")

como en la respuesta de Douglas, que tiene más detalles. Sin embargo, pensé que esto podría resumir el punto esencial.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.