Как разбить строки на несколько строк в 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

Hotschke
источник
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}")

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

DLRdave
источник
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.
]=])```
ingomueller.net
источник
Если я правильно понимаю, разрыв строки в источнике также приведет к разрыву строки в строке. Например, после "длины 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!")

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

Wardw
источник