วิธีการแยกสตริงในหลายบรรทัดใน CMake?


98

ฉันมักจะมีนโยบายในโครงการของฉันที่จะไม่สร้างบรรทัดในไฟล์ข้อความที่มีความยาวเกิน 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 บรรทัดแล้ว ดังนั้นฉันจะแบ่งบรรทัดใน CM ออกเป็นหลายบรรทัดโดยไม่ต้องใช้ verbose (หลายlist(APPEND ...)หรือคล้ายกัน) ได้อย่างไร

คำตอบ:


90

อัปเดตสำหรับ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: cmake-3 พร้อมใช้งานผ่านHomebrew , MacportsและFink
Windows: cmake-3 พร้อมใช้งานผ่านChocolatey


21
ปัญหาเกี่ยวกับแนวทาง CMake 3.0 คือไม่ละเว้นการเยื้อง ซึ่งหมายความว่าสตริงหลายบรรทัดไม่สามารถเยื้องกับส่วนที่เหลือของโค้ดได้
void.pointer

@ void.pointer ฉันพบปัญหาเดียวกันคุณหาวิธีการเยื้องด้วย multi-line ได้อย่างไร?
user3667089

นอกจากนี้หากต้องการใช้การเยื้องและมีขีด จำกัด 80 อักขระที่ผูกไว้อีกวิธีหนึ่งคือทำเช่นนี้: <code> ข้อความ ("นี่คือค่าของตัวแปร:" <br> "$ {varValue}") </code>
munsingh

55

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 และใหม่กว่าสนับสนุนความต่อเนื่องของอาร์กิวเมนต์ที่ยกมาคุณไม่สามารถเยื้องบรรทัดที่สองหรือถัดไปโดยไม่ได้รับช่องว่างการเยื้องรวมอยู่ในสตริงของคุณ

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
สำหรับบรรทัดที่ยาวมากการขึ้นบรรทัดใหม่หลังชื่อตัวแปรจะช่วยปรับปรุงรูปแบบนี้ให้ดียิ่งขึ้นไปอีก (ไม่จำเป็นในตัวอย่างนี้)
ปราชญ์

ด้วยความอยากรู้อยากเห็นมันจะถูกต้องหรือไม่ที่จะเดาว่าเครื่องหมายคำพูดคู่ในสตริงจะต้องมีเครื่องหมายแบ็กสแลชด้วยเช่นกันและแบ็กสแลชใด ๆ ที่ต้องปรากฏเป็นอักขระในสตริง
Jonathan Leffler

@JonathanLeffler ใช่พวกเขาต้องการการหลบหนี กฎของภาษาอยู่ที่นี่: cmake.org/cmake/help/latest/manual/…แต่มันทำให้สับสน ดูเพิ่มเติมที่: stackoverflow.com/a/40728463
Douglas Royds

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

เป็นสองบรรทัดที่สั้นกว่านี้:

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

พวกเขาเทียบเท่าทั้งหมด


6

ตัวอย่างในคำถามเดิมเป็นเพียงเกี่ยวกับสตริงที่ค่อนข้างสั้น สำหรับสตริงที่ยาวขึ้น (รวมถึงตัวอย่างที่ให้ไว้ในคำตอบอื่น ๆ ) อาร์กิวเมนต์วงเล็บน่าจะดีกว่า จากเอกสารประกอบ:

วงเล็บเปิดเป็นลายลักษณ์อักษร[ตามด้วยศูนย์หรือมากกว่าตามมาด้วย= [วงเล็บปิดที่สอดคล้องกันเป็นลายลักษณ์อักษร]ตามด้วยหมายเลขเดียวกันของตามมาด้วย= ]วงเล็บไม่ทำรัง อาจมีการเลือกความยาวที่ไม่ซ้ำกันเสมอสำหรับวงเล็บเปิดและวงเล็บปิดเพื่อให้มีวงเล็บปิดที่มีความยาวอื่น ๆ

[... ]

ตัวอย่างเช่น:

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

ถ้าฉันเข้าใจสิ่งนี้ถูกต้อง linebreak ในแหล่งที่มาจะแนะนำ linebreak ในสตริงด้วย ตัวอย่างเช่นจะมี \ n อยู่หลัง "length 1" วิธีใดที่จะหลีกเลี่ยงสิ่งนั้น?
Lukas Schmelzeisen

ถูกต้องจะมี\ns ถ้าคุณไม่ต้องการเช่นนั้นฉันไม่คิดว่าการโต้แย้งแบบวงเล็บเป็นทางออกของคุณ
ingomueller.net

BTW ใช้ได้กับเวอร์ชัน 3.x ไม่ใช่ 2.x
Maxim Suslov

6

สำหรับผู้ที่ถูกนำมาที่นี่จากฉันจะแยกนิพจน์ตัวสร้าง 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})

คำถามนี้อาจไม่เกี่ยวข้องกับคำถามที่ฉันกำลังโพสต์คำตอบ แต่น่าเสียดายที่คำถามที่ฉันกำลังตอบถูกทำเครื่องหมายว่าซ้ำกับคำถามนี้อย่างไม่ถูกต้อง

รายการตัวสร้างจะไม่ได้รับการจัดการและแยกวิเคราะห์ในลักษณะเดียวกับสตริงและด้วยเหตุนี้จึงมีมาตรการเพิ่มเติมที่เราต้องดำเนินการเพื่อแยกรายการตัวสร้างเป็นหลายบรรทัด


อาจเป็นการดีที่จะโพสต์คำตอบนี้ในเวอร์ชัน "ซ้ำ" ที่ลิงก์ไว้ นี่คือคำตอบที่ฉันกำลังมองหาเมื่อฉันเจอมัน
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.