CMakeで文字列を複数の行に分割する方法は?


95

私のプロジェクトでは通常、80行を超えるテキストファイルに行を作成しないようにポリシーを設定しています。そのため、あらゆる種類のエディターで簡単に編集できます(契約はご存知です)。しかし、CMakeを使用すると、単純な文字列を複数の行に分割して1つの大きな行を回避する方法がわからないという問題が発生します。この基本的なコードを考えてみましょう:

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 ...)など)にせずに複数の行に分割するにはどうすればよいですか?

回答:


88

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-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:Homebrewから入手可能なcmake-3 、MacportsFink
Windows:Chocolateyから入手可能なcmake-3


20
CMake 3.0アプローチの問題は、インデントを無視しないことです。つまり、複数行の文字列を残りのコードでインデントすることはできません。
void.pointer 2017年

@ void.pointer同じ問題に遭遇しました、複数行でインデントする方法を理解しましたか?
user3667089 2017

また、インデントを使用し、80文字に制限されている場合、別の方法は次のようにすることです:<code> message( "This is the value of the variable:" <br> "$ {varValue}")</ code>
munsingh

54

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以降では、引用符で囲まれた引数の行の継続がサポートれていますが、文字列にインデントの空白が含まれていないと、2行目以降はインデントできません。

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

1
非常に長い行の場合、変数名の後の改行はこのパターンをさらに改善します(この例では不要です)。
セージ

うち好奇心、文字列内の二重引用符もバックスラッシュでエスケープする必要があり、その必要性は、文字列内の文字として表示することをバックスラッシュを行うことを推測する正しいでしょうか?
ジョナサンレフラー

@JonathanLefflerはい、彼らは脱出する必要があります。言語規則はここにあります:cmake.org/cmake/help/latest/manual/…しかし、混乱を招きます。次も参照してください:stackoverflow.com/a/40728463
Douglas Royds 2018

9

それはまだ少し冗長ですが、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

7

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

これらの2つの短い行として:

set(MYPROJ_VERSION
  "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-${VERSION_EXTRA}")

それらは完全に同等です。


5

元の質問の例は、比較的短い文字列のみに関するものです。より長い文字列(他の回答で示されている例を含む)の場合、角括弧引数の方が適している場合があります。ドキュメントから:

開始角かっこは[、0個以上の=後にが続き[ます。対応する右角かっこが書かれ、]その後に同じ数が=続き、が続き]ます。ブラケットはネストしません。開始ブラケットと終了ブラケットが常に他の長さの終了ブラケットを含むように、固有の長さを選択できます。

[...]

例えば:

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

私がこれを正しく理解している場合、ソースの改行は文字列に改行を導入します。たとえば、「長さ1」の後に\ nがあります。それを回避する方法はありますか?
Lukas Schmelzeisen

正解です。sがあります\n。あなたがそれを望まないのであれば、私はブラケット引数があなたの解決策であるとは思いません。
ingomueller.net

ところで、これはバージョン2.xではなくバージョン3.xで有効です
Maxim Suslov

4

CMakeジェネレーター式を複数の行に分割するにどうすればよいですか?いくつかメモを追加したいと思います。

行継続メソッドは機能しません。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 ’

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

これは、私が回答を投稿している質問とは無関係である可能性があります。残念ながら、私が回答している質問は、この質問の複製として誤ってマークされています。

ジェネレーターリストは、文字列と同じように処理および解析されません。そのため、ジェネレーターリストを複数の行に分割するために必要な追加の対策があります。


リンクされた「複製」にこの回答のバージョンを投稿することもおそらく価値があります。これは私がそれに遭遇したときに私が探していた答えでした。
Keith Prussing

3

コード内で適切なインデントを維持するためには、実行するだけで十分簡単です。

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

または直接文字列を形成する

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

以下のように、ダグラスの答えの詳細を持っています。しかし、これは本質的なポイントを要約するだけかもしれないと思った。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.