Comment diviser des chaînes sur plusieurs lignes dans CMake?


96

J'ai généralement une politique dans mon projet, de ne jamais créer de lignes dans les fichiers texte qui dépassent une longueur de ligne de 80, donc ils sont facilement modifiables dans toutes sortes d'éditeurs (vous connaissez l'affaire). Mais avec CMake, j'ai le problème que je ne sais pas comment diviser une simple chaîne en plusieurs lignes pour éviter une ligne énorme. Considérez ce code de base:

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}")

Il dépasse déjà la limite de 80 lignes. Alors, comment diviser une ligne dans CMake en plusieurs lignes sans devenir verbeux (multiple list(APPEND ...)ou similaire)?

Réponses:


88

Mise à jour pour CMake 3.0 et plus récent :

la poursuite de la ligne est possible avec \. voir 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.\
")

Disponibilité des versions 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 via Homebrew , Macports et Fink
Windows: cmake-3 disponible via Chocolatey


20
Le problème avec l'approche CMake 3.0 est qu'elle n'ignore pas l'indentation. Ce qui signifie que les chaînes multilignes ne peuvent pas être mises en retrait avec le reste du code.
void.pointer

@ void.pointer J'ai rencontré le même problème, avez-vous trouvé comment mettre en retrait avec plusieurs lignes?
user3667089

Aussi, si vous voulez utiliser l'indentation et que la limite de 80 caractères est limitée, une autre façon est de faire comme ceci: <code> message ("Ceci est la valeur de la variable:" <br> "$ {varValue}") </code>
munsingh le

54

CMake 3.0 et plus récent

Utilisez la string(CONCAT)commande:

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}")

Bien que CMake 3.0 et les versions ultérieures prennent en charge la continuation des arguments entre guillemets , vous ne pouvez pas mettre en retrait la deuxième ou les lignes suivantes sans obtenir l'espace blanc d'indentation inclus dans votre chaîne.

CMake 2.8 et versions antérieures

Vous pouvez utiliser une liste. Chaque élément de la liste peut être mis sur une nouvelle ligne:

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}")

Une liste utilisée sans guillemets est concaténée sans espace blanc:

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

Si vous avez vraiment besoin d'une chaîne, vous pouvez d'abord convertir la liste en chaîne:

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

Tous les points-virgules dans vos chaînes d'origine seront considérés comme des séparateurs d'éléments de liste et supprimés. Ils doivent être échappés:

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

1
Pour les très longues lignes, une nouvelle ligne après le nom de la variable améliore encore davantage ce modèle (inutile dans cet exemple).
sage

Par curiosité , serait-il correct de deviner que les guillemets doubles dans la chaîne doivent également être échappés avec une barre oblique inverse, de même que les barres obliques inverses qui doivent apparaître comme un caractère dans la chaîne?
Jonathan Leffler

@JonathanLeffler Oui, ils auraient besoin de s'échapper. Les règles de langue sont ici: cmake.org/cmake/help/latest/manual/ ... mais cela devient déroutant. Voir aussi: stackoverflow.com/a/40728463
Douglas Royds

9

C'est encore un peu verbeux, mais si la limite de 80 caractères vous bogue vraiment, vous pouvez ajouter à plusieurs reprises la même 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}")

Donne sortie:

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

7

Il n'existe aucun moyen de diviser un littéral de chaîne sur plusieurs lignes dans les fichiers CMakeLists.txt ou dans les scripts CMake. Si vous incluez une nouvelle ligne dans une chaîne, il y aura une nouvelle ligne littérale dans la chaîne elle-même.

# 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}")

Cependant, CMake utilise des espaces pour séparer les arguments, vous pouvez donc changer un espace qui est un séparateur d'arguments en une nouvelle ligne où vous le souhaitez, sans changer le comportement.

Vous pouvez reformuler cette ligne plus longue:

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

comme ces deux lignes plus courtes:

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

Ils sont tout à fait équivalents.


5

L'exemple de la question d'origine ne concerne qu'une chaîne relativement courte. Pour les chaînes plus longues (y compris les exemples donnés dans d'autres réponses), un argument entre crochets pourrait être préférable. De la documentation:

Un crochet ouvrant est écrit [suivi de zéro ou plus =suivi de [. Le crochet fermant correspondant est écrit ]suivi du même nombre de =suivi de ]. Les supports ne s'emboîtent pas. Une longueur unique peut toujours être choisie pour que les crochets d'ouverture et de fermeture contiennent des crochets de fermeture d'autres longueurs.

[...]

Par exemple:

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 je comprends bien, un saut de ligne dans la source introduira également un saut de ligne dans la chaîne. Par exemple, il y aura un \ n après "longueur 1.". Un moyen d'éviter cela?
Lukas Schmelzeisen

C'est exact, il y aura l' \nart. Si vous ne le souhaitez pas, je ne pense pas que les arguments entre parenthèses soient votre solution.
ingomueller.net

BTW, il est valide pour la version 3.x, pas 2.x
Maxim Suslov

4

Pour ceux qui ont été amenés ici de Comment diviser une expression de générateur CMake en plusieurs lignes? Je voudrais ajouter quelques notes.

La méthode de continuation de ligne ne fonctionnera pas, CMake ne peut pas analyser une liste de générateurs faite avec des espaces (indentation) et une continuation de ligne.

Alors que la solution de chaîne (CONCAT) fournira une expression de générateur qui peut être évaluée, l'expression évaluée sera entourée de guillemets si le résultat contient un espace.

Pour chaque option individuelle à ajouter, une liste de générateurs distincte doit être construite, donc les options d'empilement comme je l'ai fait dans ce qui suit entraînera l'échec de la construction:

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

C'est parce que les options résultantes sont passées au compilateur entre guillemets

/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 ’

Pour évaluer de longues expressions de générateur représentées à l'aide de la solution de chaîne (CONCAT), chaque expression de générateur doit s'évaluer en une seule option sans espaces:

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

Cela n'a peut-être aucun rapport avec la question à laquelle je poste une réponse, malheureusement, la question à laquelle je réponds est à tort marquée comme un double de cette question.

Les listes de générateurs ne sont pas gérées et analysées de la même manière que les chaînes, et pour cette raison, il existe des mesures supplémentaires à prendre pour diviser une liste de générateurs sur plusieurs lignes.


Il serait probablement intéressant de publier également une version de cette réponse sur le "duplicata" lié. C'était la réponse que je cherchais quand je suis tombée dessus.
Keith Prussing le

3

Pour maintenir une bonne indentation dans votre code, il suffit de faire

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

Ou formez une chaîne directement avec

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

comme dans la réponse de Douglas qui a plus de détails. Cependant, j'ai pensé que cela pourrait simplement résumer le point essentiel.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.