【问题标题】:How to split strings across multiple lines in CMake?如何在 CMake 中跨多行拆分字符串?
【发布时间】:2011-11-30 01:17:37
【问题描述】:

我的项目中通常有一个政策,即永远不要在文本文件中创建超过 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 ...) 等)?

【问题讨论】:

    标签: cmake


    【解决方案1】:

    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 向后移植: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 可通过HomebrewMacportsFink获得
    Windows:cmake-3 可通过Chocolatey获得

    【讨论】:

    • CMake 3.0 方法的问题在于它不会忽略缩进。这意味着多行字符串不能与其余代码缩进。
    • @void.pointer 我遇到了同样的问题,你知道如何用多行缩进吗?
    • 另外,如果想使用缩进并且限制为 80 个字符,那么另一种方法是这样做: message("这是变量的值:" <br> "${ varValue}")
    【解决方案2】:

    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 和更新版本支持 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")
    

    【讨论】:

    • 对于很长的行,变量名后面的换行符甚至进一步改进了这种模式(在这个例子中是不必要的)。
    • curiosity之外,猜测字符串中的双引号也需要用反斜杠转义是否正确,任何需要作为字符出现在字符串中的反斜杠也是如此?
    • @JonathanLeffler 是的,他们需要转义。语言规则在这里:cmake.org/cmake/help/latest/manual/… 但它确实让人困惑。另见:stackoverflow.com/a/40728463
    【解决方案3】:

    对于那些从How do I split a CMake generator expression to multiple lines? 被带到这里的人,我想补充一些说明。

    续行方法不起作用,CMake 无法解析由空格(缩进)和续行组成的生成器列表。

    虽然 string(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})
    

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

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

    【讨论】:

    • 在链接的“重复”上发布此答案的一个版本可能是值得的。当我遇到它时,这就是我正在寻找的答案。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      原始问题中的示例仅涉及相对较短的字符串。对于较长的字符串(包括其他答案中给出的示例),bracket argument 可能会更好。来自文档:

      左括号写为[,后跟零个或多个=,再后跟[。相应的右括号写为],后跟相同数量的=,然后是]。括号不嵌套。可以始终为左括号和右括号选择唯一的长度,以包含其他长度的右括号。

      [...]

      例如:

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

      【讨论】:

      • 如果我理解正确的话,源代码中的换行符也会在字符串中引入换行符。例如,“length 1.”后面会有一个\n。有什么办法可以避免吗?
      • 没错,会有\ns。如果您不希望这样,我认为括号参数不是您的解决方案。
      • 顺便说一句,它适用于版本 3.x,而不是 2.x
      【解决方案6】:

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

      它们是完全等价的。

      【讨论】:

        【解决方案7】:

        要在代码中保持良好的缩进,只需这样做就足够简单了

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

        或者直接用

        组成一个字符串
        string(CONCAT MYSTR "This and " "that "
                            "and me too!")
        

        Douglas' answer,谁有更多细节。但是我认为这可能只是总结了要点。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-06-11
          • 2014-06-19
          • 1970-01-01
          • 2016-07-03
          • 2010-09-15
          • 2011-04-16
          • 2021-10-24
          相关资源
          最近更新 更多