Jak podzielić ciągi na wiele linii w CMake?

97

Zwykle mam w swoim projekcie zasadę, aby nigdy nie tworzyć wierszy w plikach tekstowych, które przekraczają długość linii 80, więc można je łatwo edytować we wszelkiego rodzaju edytorach (znasz ofertę). Ale w przypadku CMake pojawia się problem polegający na tym, że nie wiem, jak podzielić prosty ciąg na wiele linii, aby uniknąć jednej ogromnej linii. Rozważ ten podstawowy kod:

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

Już przekracza limit 80 linii. Jak więc podzielić wiersz w CMake na wiele wierszy bez przechodzenia do szczegółów (wiele list(APPEND ...)lub tym podobnych)?

Lukas Schmelzeisen
źródło

Odpowiedzi:

90

Aktualizacja dla CMake 3.0 i nowszych :

kontynuacja linii jest możliwa dzięki \. zobacz 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.\
")

Dostępność wersji 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 dostępny przez Homebrew , Macports i Fink
Windows: cmake-3 dostępny przez Chocolatey

Hotschke
źródło
21
Problem z podejściem CMake 3.0 polega na tym, że nie ignoruje ono wcięć. Co oznacza, że ​​ciągi wieloliniowe nie mogą być wcięte w pozostałej części kodu.
void.pointer
@ void.pointer Natknąłem się na ten sam problem, czy wiesz, jak tworzyć wcięcia za pomocą wielu linii?
user3667089
Jeśli chcesz użyć wcięcia i masz ograniczenie do 80 znaków, możesz zrobić to w inny sposób: <code> komunikat ("To jest wartość zmiennej:" <br> "$ {varValue}") </code>
munsingh
55

CMake 3.0 i nowsze

Użyj string(CONCAT)polecenia:

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

Chociaż CMake 3.0 i nowsze obsługują kontynuację wierszy argumentów cytowanych w cudzysłowie , nie można wciskać drugiego lub kolejnych wierszy bez umieszczania w ciągu znaków białych znaków wcięcia.

CMake 2.8 i starsze

Możesz użyć listy. Każdy element listy można umieścić w nowej linii:

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

Lista używana bez cudzysłowów jest łączona bez spacji:

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

Jeśli naprawdę potrzebujesz łańcucha, możesz najpierw przekonwertować listę na ciąg:

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

Wszelkie średniki w oryginalnych ciągach znaków będą widoczne jako separatory elementów listy i zostaną usunięte. Muszą uciec:

set(MY_LIST "Hello World "
            "with a \;semicolon")
Douglas Royds
źródło
1
W przypadku bardzo długich linii znak nowej linii po nazwie zmiennej jeszcze bardziej ulepsza ten wzorzec (w tym przykładzie jest to zbędne).
sage
Z ciekawości , czy poprawne byłoby odgadnięcie, że podwójne cudzysłowy w ciągu muszą być również poprzedzone ukośnikiem odwrotnym, a więc czy wszelkie odwrotne ukośniki, które muszą występować jako znak w ciągu?
Jonathan Leffler
@JonathanLeffler Tak, potrzebowaliby ucieczki. Zasady językowe są tutaj: cmake.org/cmake/help/latest/manual/ ... ale robi się to mylące. Zobacz też: stackoverflow.com/a/40728463
Douglas Royds
9

Nadal jest trochę rozwlekły, ale jeśli limit 80 znaków naprawdę Cię denerwuje, możesz wielokrotnie dodawać do tej samej zmiennej:

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

Daje wynik:

$ cmake  ~/project/tmp
-- version: 1.0.0-rc1
-- Configuring done
-- Generating done
-- Build files have been written to: /home/rsanderson/build/temp
Rian Sanderson
źródło
7

Nie ma sposobu, aby podzielić literał ciągu na wiele wierszy w plikach CMakeLists.txt lub w skryptach CMake. Jeśli umieścisz znak nowej linii w ciągu, w samym ciągu będzie istniał dosłowny znak nowej linii.

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

Jednak CMake używa białych znaków do oddzielania argumentów, więc możesz zmienić spację, która jest separatorem argumentów, na znak nowej linii w dowolnym miejscu, bez zmiany zachowania.

Możesz zmienić sformułowanie tej dłuższej linii:

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

jako te dwie krótsze linie:

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

Są całkowicie równoważne.

DLRdave
źródło
6

Dla tych, którzy zostali przywiezieni z Jak podzielić wyrażenie generatora CMake na wiele wierszy? Chciałbym dodać kilka uwag.

Metoda kontynuacji linii nie zadziała, CMake nie może przeanalizować listy generatorów utworzonej z użyciem białych znaków (wcięć) i kontynuacji linii.

Chociaż rozwiązanie typu string (CONCAT) zapewni wyrażenie generatora, które można ocenić, oszacowane wyrażenie zostanie otoczone cudzysłowami, jeśli wynik zawiera spację.

Dla każdej opcji, która ma zostać dodana, należy utworzyć oddzielną listę generatorów, więc opcje układania w stos, takie jak poniżej, spowodują niepowodzenie kompilacji:

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

Dzieje się tak, ponieważ wynikowe opcje są przekazywane do kompilatora w cudzysłowach

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

Aby obliczyć długie wyrażenia generatora reprezentowane za pomocą rozwiązania typu string (CONCAT), każde wyrażenie generatora musi wyliczać na pojedynczą opcję bez spacji:

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

Może to nie mieć związku z pytaniem, na które piszę odpowiedź, niestety pytanie, na które odpowiadam, jest błędnie oznaczone jako duplikat tego pytania.

Listy generatorów nie są obsługiwane i przetwarzane w taki sam sposób jak łańcuchy, dlatego też istnieją dodatkowe środki, które należy podjąć, aby podzielić listę generatorów na wiele wierszy.

Parker Gibson
źródło
Prawdopodobnie warto byłoby również zamieścić wersję tej odpowiedzi w połączonym „duplikacie”. To była odpowiedź, której szukałem, kiedy ją spotkałem.
Keith Prussing
5

Przykład w pierwotnym pytaniu dotyczy tylko stosunkowo krótkiego ciągu. W przypadku dłuższych łańcuchów (w tym przykładów podanych w innych odpowiedziach) argument w nawiasach mógłby być lepszy. Z dokumentacji:

Nawias otwierający jest zapisywany, [po którym następuje zero lub więcej, =a następnie [. Po odpowiednim nawiasie zamykającym ]następuje ta sama liczba, =po której następuje] . Wsporniki nie zagnieżdżają się. Zawsze można wybrać unikalną długość nawiasów otwierających i zamykających, aby zawierały nawiasy zamykające o innej długości.

[…]

Na przykład:

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
źródło
Jeśli dobrze to rozumiem, podział wiersza w źródle spowoduje również podział wiersza w ciągu. Na przykład po „długości 1” będzie znak \ n. Jakiś sposób, aby tego uniknąć?
Lukas Schmelzeisen
Zgadza się, będzie \ns. Jeśli tego nie chcesz, nie sądzę, aby argumenty w nawiasach były rozwiązaniem.
ingomueller.net
BTW, to jest ważne dla wersji 3.x, a nie 2.x
Maxim Suslov
3

Aby zachować dobre wcięcia w kodzie, wystarczy to zrobić

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

Lub utwórz ciąg bezpośrednio za pomocą

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

jak w odpowiedzi Douglasa, który ma więcej szczegółów. Pomyślałem jednak, że może to podsumować najważniejszy punkt.

wardw
źródło