Como dividir strings em várias linhas no CMake?

96

Normalmente, tenho uma política em meu projeto, de nunca criar linhas em arquivos de texto que excedam o comprimento de linha de 80, para que sejam facilmente editáveis ​​em todos os tipos de editores (você conhece o negócio). Mas com o CMake, tenho o problema de não saber como dividir uma string simples em várias linhas para evitar uma linha 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}")

Já ultrapassa o limite de 80 linhas. Então, como faço para quebrar uma linha no CMake em várias linhas sem chegar a verbose (várias list(APPEND ...)ou semelhantes)?

Lukas Schmelzeisen
fonte

Respostas:

88

Atualização para CMake 3.0 e mais recente :

a continuação da linha é possível com \. veja 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.\
")

Disponibilidade de versões 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 disponível através do Homebrew , Macports e Fink
Windows: cmake-3 disponível no Chocolatey

Hotschke
fonte
20
O problema com a abordagem do CMake 3.0 é que ela não ignora a indentação. O que significa que strings de várias linhas não podem ser indentadas com o resto do código.
void.pointer de
@ void.pointer Eu tive o mesmo problema, você descobriu como recuar com várias linhas?
user3667089
Além disso, se quiser usar indentação e tiver um limite de 80 caracteres, outra maneira é fazer assim: <code> message ("Este é o valor da variável:" <br> "$ {varValue}") </code>
munsingh
54

CMake 3.0 e mais recente

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

Embora o CMake 3.0 e os mais recentes suportem a continuação de linha de argumentos entre aspas , você não pode indentar a segunda linha ou as linhas subsequentes sem obter o espaço em branco de indentação incluído em sua string.

CMake 2.8 e anterior

Você pode usar uma lista. Cada elemento da lista pode ser colocado em uma nova linha:

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

Uma lista usada sem aspas é concatenada sem espaço em branco:

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

Se você realmente precisa de uma string, pode primeiro converter a lista em uma string:

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

Quaisquer pontos e vírgulas em suas strings originais serão vistos como separadores de elementos de lista e removidos. Eles devem ser escapados:

set(MY_LIST "Hello World "
            "with a \;semicolon")
Douglas Royds
fonte
1
Para linhas muito longas, uma nova linha após o nome da variável melhora ainda mais esse padrão (desnecessário neste exemplo).
sábio
Por curiosidade , seria correto adivinhar que as aspas duplas na string também precisam ser escapadas com uma barra invertida, assim como quaisquer barras invertidas que precisem aparecer como um caractere na string?
Jonathan Leffler
@JonathanLeffler Sim, eles precisariam escapar. As regras de idioma estão aqui: cmake.org/cmake/help/latest/manual/… mas fica confuso. Veja também: stackoverflow.com/a/40728463
Douglas Royds
9

Ainda é um pouco prolixo, mas se o limite de 80 caracteres realmente incomodar você, você pode anexar repetidamente à mesma variável:

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

Dá saída:

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

Não há como dividir um literal de string em várias linhas em arquivos CMakeLists.txt ou em scripts CMake. Se você incluir uma nova linha em uma string, haverá uma nova linha literal na própria string.

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

No entanto, o CMake usa espaços em branco para separar os argumentos, de modo que você pode alterar um espaço que é um separador de argumento em uma nova linha em qualquer lugar que desejar, sem alterar o comportamento.

Você poderia reformular esta linha mais longa:

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

como essas duas linhas mais curtas:

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

Eles são totalmente equivalentes.

DLRdave
fonte
5

O exemplo da pergunta original é apenas sobre uma string relativamente curta. Para strings mais longas (incluindo os exemplos dados em outras respostas), um argumento de colchete pode ser melhor. Da documentação:

Um colchete de abertura é escrito [seguido por zero ou mais =seguido por [. O colchete de fechamento correspondente é escrito ]seguido pelo mesmo número de =seguido por ]. Os colchetes não se aninham. Um comprimento exclusivo pode sempre ser escolhido para os colchetes de abertura e fechamento para conter colchetes de fechamento de outros comprimentos.

[...]

Por exemplo:

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.
]=])```
ingomueller.net
fonte
Se bem entendi, uma quebra de linha na fonte também irá introduzir uma quebra de linha na string. Por exemplo, haverá um \ n após o "comprimento 1". Alguma maneira de evitar isso?
Lukas Schmelzeisen
Isso mesmo, haverá \ns. Se você não quiser isso, não acho que os argumentos dos colchetes sejam sua solução.
ingomueller.net
BTW, é válido para a versão 3.x, não 2.x
Maxim Suslov
4

Para aqueles que foram trazidos aqui de Como faço para dividir uma expressão do gerador CMake em várias linhas? Eu gostaria de adicionar algumas notas.

O método de continuação de linha não funcionará, o CMake não pode analisar uma lista de gerador feita com espaços em branco (indentação) e continuação de linha.

Embora a solução de string (CONCAT) forneça uma expressão geradora que pode ser avaliada, a expressão avaliada será cercada por aspas se o resultado contiver um espaço.

Para cada opção individual a ser adicionada, uma lista de gerador separada deve ser construída, portanto, as opções de empilhamento como fiz a seguir farão com que a compilação falhe:

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

Isso ocorre porque as opções resultantes são passadas para o compilador entre aspas

/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 avaliar expressões geradoras extensas representadas usando a solução de string (CONCAT), cada expressão geradora deve ser avaliada em uma única opção sem espaços:

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

Isso pode não estar relacionado à pergunta para a qual estou postando uma resposta; infelizmente, a pergunta que estou respondendo está marcada incorretamente como uma duplicata desta pergunta.

As listas de geradores não são tratadas e analisadas da mesma forma que as strings e, por isso, há medidas adicionais que devem ser tomadas para dividir uma lista de geradores em várias linhas.

Parker Gibson
fonte
Provavelmente valeria a pena postar uma versão dessa resposta na "duplicata" vinculada. Essa era a resposta que eu procurava quando me deparei com ela.
Keith Prussing
3

Para manter uma boa indentação em seu código, é simples o suficiente apenas fazer

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

Ou forme uma string diretamente com

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

como na resposta de Douglas que tem mais detalhes. No entanto, pensei que isso poderia apenas resumir o ponto essencial.

Wardw
fonte