How to split strings across multiple lines in CMake?

65,863

Solution 1

Update for CMake 3.0 and newer :

line continuation is possible with \. see 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.\
")

Availability of CMake versions:

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 available through Homebrew, Macports and Fink
Windows: cmake-3 available through Chocolatey

Solution 2

CMake 3.0 and newer

Use the string(CONCAT) command:

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

Although CMake 3.0 and newer support line continuation of quoted arguments, you cannot indent the second or subsequent lines without getting the indentation whitespace included in your string.

CMake 2.8 and older

You can use a list. Each element of the list can be put on a new line:

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

A list used without quotes is concatenated without white-space:

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

If you really need a string, you can convert the list to a string first:

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

Any semicolons in your original strings will be seen as list element separators, and removed. They must be escaped:

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

Solution 3

For those who were brought here from How do I split a CMake generator expression to multiple lines? I would like to add some notes.

The line continuation method will not work, CMake cannot parse a generator list made with whitespace (indentation) and line continuation.

While the string(CONCAT) solution will provide a generator expression that can be evaluated, the evaluated expression will be surrounded by quotes if the result contains a space.

For each individual option to be added a separate generator list must be constructed, so stacking options like I have done in the following will cause the build to fail:

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

This is because the resulting options are passed to the compiler in quotes

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

To evaluate lengthy generator expressions represented using the string(CONCAT) solution, each generator expression must evaluate to a single option with no spaces:

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

This may be unrelated to the question I am posting an answer to, unfortunately the question I am answering is wrongfully marked as a duplicate of this question.

Generator lists are not handled and parsed the same way as strings are, and because of this, there are additional measures one must take to split a generator list across multiple lines.

Solution 4

It's still a little verbose, but if the 80 char limit really bugs you then you could repeatedly append to the same 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}")

Gives output:

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

Solution 5

The example in the original question is only about a relatively short string. For longer strings (including the examples given in other answers), a bracket argument could be better. From the documentation:

An opening bracket is written [ followed by zero or more = followed by [. The corresponding closing bracket is written ] followed by the same number of = followed by ]. Brackets do not nest. A unique length may always be chosen for the opening and closing brackets to contain closing brackets of other lengths.

[...]

For example:

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.
]=])
Share:
65,863

Related videos on Youtube

Lukas Schmelzeisen
Author by

Lukas Schmelzeisen

PhD student at the WeST-Institute lead by Steffen Staab. Research interests include Machine Learning, Natural Language Processing, and the Semantic Web. Open to internships in research and industry internationally.

Updated on January 01, 2022

Comments

  • Lukas Schmelzeisen
    Lukas Schmelzeisen over 2 years

    I usually have a policy in my project, to never create lines in text files that exceed a line length of 80, so they are easily editable in all kinds of editors (you know the deal). But with CMake I get the problem that I do not know how to split a simple string into multiple lines to avoid one huge line. Consider this basic code:

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

    It already exceeds the 80 column limit. So how do I break a line in CMake into multiple lines without getting to verbose (multiple list(APPEND ...) or the like)?

  • void.pointer
    void.pointer over 7 years
    Problem with the CMake 3.0 approach is that it doesn't ignore indentation. Which means multi-line strings can't be indented with the rest of the code.
  • user3667089
    user3667089 over 7 years
    @void.pointer I ran into the same problem, did you figure out how to indent with multi-line?
  • sage
    sage over 7 years
    For very long lines, a newline after the variable name even further improves this pattern (unnecessary in this example).
  • Jonathan Leffler
    Jonathan Leffler over 5 years
    Out of curiosity, would it be correct to guess that double quotes in the string also need to be escaped with a backslash, and so do any backslashes that need to appear as a character in the string?
  • Douglas Royds
    Douglas Royds over 5 years
    @JonathanLeffler Yes, they would need escaping. Language rules are here: cmake.org/cmake/help/latest/manual/… but it does get confusing. See also: stackoverflow.com/a/40728463
  • Lukas Schmelzeisen
    Lukas Schmelzeisen almost 5 years
    If I understand this correctly, A linebreak in the source will also introduce a linebreak in the string. For example, there will be a \n after the "length 1.". Any way to avoid that?
  • ingomueller.net
    ingomueller.net almost 5 years
    That's correct, there will be \ns. If you don't want that, I don't think that bracket arguments are your solution.
  • Maxim Suslov
    Maxim Suslov over 4 years
    BTW, it is valid for version 3.x, not 2.x
  • munsingh
    munsingh almost 4 years
    Also if want to use indentation and are 80 char limit bound then another way is to do like this: <code> message("This is the value of the variable: " <br> "${varValue}") </code>
  • Keith Prussing
    Keith Prussing almost 4 years
    It would probably be worthwhile to also post a version of this answer on the linked "duplicate". This was the answer I was looking for when I came across it.