Как разбить строки на несколько строк в CMake?


97

У меня обычно есть политика в моем проекте: никогда не создавать строки в текстовых файлах, длина которых превышает 80 строк, поэтому их легко редактировать во всех типах редакторов (вы знаете, в чем дело). Но с CMake у меня возникает проблема: я не знаю, как разбить простую строку на несколько строк, чтобы избежать одной огромной строки. Рассмотрим этот базовый код:

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

Он уже превышает ограничение в 80 строк. Итак, как мне разбить строку в CMake на несколько строк, не вдаваясь в подробности (несколько list(APPEND ...)или тому подобное)?

Ответы:


90

Обновление для CMake 3.0 и новее :

продолжение строки возможно с помощью \. см. 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.\
")

Наличие версий 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, доступный через Homebrew , Macports и Fink
Windows: cmake-3 доступен через Chocolatey


21
Проблема с подходом CMake 3.0 в том, что он не игнорирует отступы. Это означает, что многострочные строки не могут иметь отступы с остальной частью кода.
void.pointer

@ void.pointer Я столкнулся с той же проблемой, вы догадались, как сделать отступ в многострочном режиме?
user3667089 03

Также, если вы хотите использовать отступ и ограничить ограничение на 80 символов, другой способ - сделать вот так: <code> message ("Это значение переменной:" <br> "$ {varValue}") </code>
munsingh

55

CMake 3.0 и новее

Используйте string(CONCAT)команду:

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

Хотя CMake 3.0 и более поздние версии поддерживают продолжение строки аргументов в кавычках , вы не можете сделать отступ во второй или последующих строках, не включив в строку пробельные символы отступа.

CMake 2.8 и старше

Вы можете использовать список. Каждый элемент списка можно поставить с новой строки:

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

Список, используемый без кавычек, объединяется без пробелов:

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

Если вам действительно нужна строка, вы можете сначала преобразовать список в строку:

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

Любые точки с запятой в исходных строках будут отображаться как разделители элементов списка и удаляться. Их необходимо избежать:

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

1
Для очень длинных строк новая строка после имени переменной еще больше улучшает этот шаблон (в этом примере не требуется).
sage

Из любопытства , было бы правильно предположить, что двойные кавычки в строке также нужно экранировать обратной косой чертой, как и любые обратные косые черты, которые должны отображаться как символ в строке?
Джонатан Леффлер

@JonathanLeffler Да, им нужно сбежать. Языковые правила находятся здесь: cmake.org/cmake/help/latest/manual/… но это действительно сбивает с толку. См. Также: stackoverflow.com/a/40728463
Дуглас Ройдс

9

Это все еще немного многословно, но если ограничение в 80 символов действительно вас беспокоит, вы можете несколько раз добавить к той же переменной:

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

Дает вывод:

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

7

Невозможно разделить строковый литерал на несколько строк в файлах CMakeLists.txt или в сценариях CMake. Если вы включите новую строку в строку, в самой строке будет буквальный перевод строки.

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

Однако CMake использует пробелы для разделения аргументов, поэтому вы можете изменить пробел, являющийся разделителем аргументов, на новую строку в любом месте, не изменяя поведения.

Вы можете перефразировать эту длинную строку:

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

как эти две более короткие строки:

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

Они полностью эквивалентны.


6

Для тех, кто попал сюда из раздела Как разбить выражение генератора CMake на несколько строк? Я хотел бы добавить несколько примечаний.

Метод продолжения строки не будет работать, CMake не может проанализировать список генератора, состоящий из пробелов (отступов) и продолжения строки.

В то время как строковое (CONCAT) решение предоставит выражение генератора, которое может быть вычислено, вычисленное выражение будет заключено в кавычки, если результат содержит пробел.

Для каждой отдельной опции, которую нужно добавить, должен быть создан отдельный список генераторов, поэтому варианты стекирования, как я сделал в следующем, приведут к сбою сборки:

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

Это связано с тем, что полученные параметры передаются компилятору в кавычках.

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

Чтобы оценить длинные выражения генератора, представленные с помощью строкового (CONCAT) решения, каждое выражение генератора должно оцениваться как один параметр без пробелов:

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

Это может быть не связано с вопросом, на который я отправляю ответ, к сожалению, вопрос, на который я отвечаю, ошибочно помечен как дубликат этого вопроса.

Списки генератора не обрабатываются и анализируются так же, как строки, и из-за этого есть дополнительные меры, которые необходимо предпринять, чтобы разделить список генератора на несколько строк.


Возможно, стоит также опубликовать версию этого ответа на связанном «дубликате». Это был ответ, который я искал, когда наткнулся на него.
Кейт Пруссинг,

5

Пример в исходном вопросе касается только относительно короткой строки. Для более длинных строк (включая примеры, приведенные в других ответах) аргумент в скобках может быть лучше. Из документации:

Записывается открывающая скобка, [за которой следует ноль или более, =за которыми следует [. Записывается соответствующая закрывающая скобка, ]за которой следует такой же номер, =а затем ]. Кронштейны не гнездятся. Всегда можно выбрать уникальную длину, чтобы открывающие и закрывающие скобки содержали закрывающие скобки другой длины.

[...]

Например:

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.
]=])```

Если я правильно понимаю, разрыв строки в источнике также приведет к разрыву строки в строке. Например, после "длины 1" будет \ n. Есть ли способ избежать этого?
Лукас

Правильно, будет \n s. Если вы этого не хотите, я не думаю, что аргументы в скобках - ваше решение.
ingomueller.net

Кстати, это действительно для версии 3.x, а не 2.x
Максим Суслов

3

Чтобы сохранить хороший отступ в коде, достаточно просто сделать

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

Или сформируйте строку напрямую с помощью

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

как в ответе Дугласа, у которого есть более подробные сведения. Однако я подумал, что это может просто подвести итог важному моменту.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.