Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to split strings across multiple lines in CMake?

Tags:

cmake

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

like image 650
Lukas Schmelzeisen Avatar asked Oct 03 '11 16:10

Lukas Schmelzeisen


4 Answers

Update for CMake 3.0 and newer :

line continuation is possible with \. see the latest cmake docs

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

like image 128
Hotschke Avatar answered Oct 23 '22 19:10

Hotschke


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")
like image 40
Douglas Royds Avatar answered Oct 23 '22 18:10

Douglas Royds


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.

like image 11
Parker Gibson Avatar answered Oct 23 '22 18:10

Parker Gibson


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
like image 9
Rian Sanderson Avatar answered Oct 23 '22 20:10

Rian Sanderson