如何在 CMake 中跨多行分割字符串?

我通常在我的项目中有一个策略,那就是永远不要在文本文件中创建超过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 ...)或类似的代码)呢?

72082 次浏览

这仍然有点冗长,但是如果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

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

它们完全等价。

CMake 3.0和更新版本

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

尽管 CMake 3.0和更新的版本支持 line continuation of quoted arguments,但是如果不在字符串中包含缩进空格,就无法缩进第二行或后续行。

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

Update for CMake 3.0和更新版本 :

使用 \可以连续行。参见 最新的 Cmake 文件

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可通过 自酿的Macports和 < a href = “ http://pdb.finkproject.org/pdb/package.php/cmake”rel = “ nofollow noReferrer”> Fink 获得
Windows: cmake-3可以通过 < a href = “ https://Chocolatey.org/package/cmake”rel = “ nofollow norefrer”> Chocolatey 获得

原问题中的示例只是一个相对较短的字符串。对于较长的字符串(包括其他答案中给出的例子) ,括号参数括号参数可能更好。根据文件:

一个开始的括号被写成 [后面跟着零个或更多个 =后面跟着 [。相应的结束括号写成 ],后面跟着相同数量的 =,后面跟着 ]。括号里没有巢。总是可以选择一个唯一的长度,以便开始和结束括号包含其他长度的结束括号。

[...]

例如:

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

对于那些谁被带到这里从 如何将 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})

这可能与我发布的问题无关,不幸的是,我回答的问题被错误地标记为这个问题的重复。

生成器列表的处理和解析方式与字符串不同,因此必须采取其他措施来将生成器列表分割为多行。

为了在代码中保持良好的缩进,这样做非常简单

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

或直接用

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

不过我认为这可能只是概括了要点。