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)?
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.\
")
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
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.
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")
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With