ไวยากรณ์ของ CMake สำหรับการตั้งค่าและใช้ตัวแปรคืออะไร


168

ฉันขอสิ่งนี้เป็นเครื่องเตือนใจตัวเองในครั้งต่อไปที่ฉันใช้ CMake มันไม่ติดและผลลัพธ์ของ Google ก็ไม่ได้ยอดเยี่ยม

ไวยากรณ์ในการตั้งค่าและใช้ตัวแปรใน CMake คืออะไร

คำตอบ:


281

เมื่อเขียนสคริปต์ของ CMake มีจำนวนมากที่คุณต้องรู้เกี่ยวกับไวยากรณ์และวิธีการใช้ตัวแปรใน CMake

ไวยากรณ์

เงื่อนไขการใช้set():

  • set(MyString "Some Text")
  • set(MyStringWithVar "Some other Text: ${MyString}")
  • set(MyStringWithQuot "Some quote: \"${MyStringWithVar}\"")

หรือด้วยstring():

  • string(APPEND MyStringWithContent " ${MyString}")

รายการที่ใช้set():

  • set(MyList "a" "b" "c")
  • set(MyList ${MyList} "d")

หรือดีกว่าด้วยlist():

  • list(APPEND MyList "a" "b" "c")
  • list(APPEND MyList "d")

รายการชื่อไฟล์:

  • set(MySourcesList "File.name" "File with Space.name")
  • list(APPEND MySourcesList "File.name" "File with Space.name")
  • add_excutable(MyExeTarget ${MySourcesList})

เอกสารประกอบ

ขอบเขตหรือ "ตัวแปรของฉันมีค่าเท่าใด"

อันดับแรกมี "ตัวแปรปกติ" และสิ่งที่คุณต้องรู้เกี่ยวกับขอบเขต:

  • ตัวแปรปกติมองเห็นCMakeLists.txtพวกเขาจะตั้งอยู่ในและทุกอย่างที่เรียกว่าจากที่นั่น ( add_subdirectory(), include(), macro()และfunction())
  • add_subdirectory()และfunction()คำสั่งเป็นพิเศษเพราะพวกเขาเปิดขึ้นขอบเขตของตัวเอง
    • ตัวแปรความหมายset(...)มีให้เห็นได้เฉพาะที่นั่นและทำสำเนาของตัวแปรปกติทั้งหมดของระดับขอบเขตที่ถูกเรียกจาก (เรียกว่าขอบเขตหลัก)
    • ดังนั้นหากคุณอยู่ในไดเรกทอรีย่อยหรือฟังก์ชั่นคุณสามารถแก้ไขตัวแปรที่มีอยู่แล้วในขอบเขตพาเรนต์ด้วย set(... PARENT_SCOPE)
    • คุณสามารถใช้สิ่งนี้เช่นในฟังก์ชั่นโดยผ่านชื่อตัวแปรเป็นพารามิเตอร์ฟังก์ชั่น ตัวอย่างจะfunction(xyz _resultVar)เป็นการตั้งค่าset(${_resultVar} 1 PARENT_SCOPE)
  • ในทางกลับกันทุกสิ่งที่คุณตั้งค่าinclude()หรือmacro()สคริปต์จะปรับเปลี่ยนตัวแปรโดยตรงในขอบเขตที่พวกเขาจะถูกเรียกจาก

ประการที่สองคือ "แคชตัวแปรส่วนกลาง" สิ่งที่คุณต้องรู้เกี่ยวกับแคช:

  • หากไม่มีตัวแปรปกติที่มีชื่อที่กำหนดไว้ในขอบเขตปัจจุบัน CMake จะค้นหารายการแคชที่ตรงกัน
  • ค่าแคชจะถูกเก็บไว้ในCMakeCache.txtไฟล์ในไดเรกทอรีเอาต์พุตไบนารีของคุณ
  • ค่าใน Cache สามารถแก้ไขได้ในแอปพลิเคชันGUI ของ CMakeก่อนที่จะถูกสร้างขึ้น ดังนั้นพวกเขาจึง - เมื่อเทียบกับตัวแปรปกติ - มีและtype docstringปกติแล้วฉันจะไม่ใช้ GUI ดังนั้นฉันจึงใช้set(... CACHE INTERNAL "")เพื่อตั้งค่าโกลบอลและค่าที่ทนได้ของฉัน

    โปรดทราบว่าINTERNALประเภทตัวแปรแคชนั้นแปลว่าFORCE

  • ในสคริปต์ CMake คุณสามารถเปลี่ยนรายการแคชที่มีอยู่หากคุณใช้set(... CACHE ... FORCE)ไวยากรณ์ พฤติกรรมนี้มีการใช้งานเช่นโดย CMake เองเพราะปกติแล้วจะไม่บังคับให้รายการแคชเองดังนั้นคุณจึงสามารถกำหนดค่าล่วงหน้าด้วยค่าอื่นได้

  • คุณสามารถใช้บรรทัดคำสั่งเพื่อตั้งค่ารายการในแคชกับไวยากรณ์cmake -D var:type=valueเพียงหรือcmake -D var=valuecmake -C CMakeInitialCache.cmake
  • คุณสามารถล้างค่าunset(... CACHE)รายการในแคชด้วย

Cache นั้นเป็นสากลและคุณสามารถตั้งค่าได้แทบทุกที่ในสคริปต์ CMake ของคุณ แต่ฉันขอแนะนำให้คุณคิดสองครั้งเกี่ยวกับตำแหน่งที่จะใช้ตัวแปร Cache (เป็นแบบโกลบอลและแบบถาวร) ปกติแล้วฉันชอบset_property(GLOBAL PROPERTY ...)และset_property(GLOBAL APPEND PROPERTY ...)ไวยากรณ์เพื่อกำหนดตัวแปรทั่วโลกของฉันเองไม่ทน

ข้อผิดพลาดที่ผันแปรได้และ "วิธีการดีบักการเปลี่ยนแปลงตัวแปร"

เพื่อหลีกเลี่ยงข้อผิดพลาดคุณควรทราบข้อมูลเกี่ยวกับตัวแปรดังต่อไปนี้:

  • ตัวแปรโลคัลซ่อนตัวแปรที่แคชไว้หากทั้งคู่มีชื่อเหมือนกัน
  • find_...คำสั่ง - ถ้าประสบความสำเร็จ - อย่าเขียนผลของพวกเขาเป็นตัวแปรที่เก็บไว้ชั่วคราว "เพื่อให้ไม่มีการโทรจะค้นหาอีกครั้ง"
  • รายการใน CMake เป็นเพียงสตริงที่มีเครื่องหมายอัฒภาคคั่นดังนั้นเครื่องหมายคำพูดจึงมีความสำคัญ
    • set(MyVar a b c)เป็น"a;b;c"และset(MyVar "a b c")เป็น"a b c"
    • คำแนะนำคือคุณใช้เครื่องหมายคำพูดเสมอพร้อมกับข้อยกเว้นหนึ่งข้อเมื่อคุณต้องการให้รายการเป็นรายการ
    • โดยทั่วไปต้องการlist()คำสั่งสำหรับการจัดการรายการ
  • ปัญหาขอบเขตทั้งหมดที่อธิบายไว้ข้างต้น โดยเฉพาะอย่างยิ่งขอแนะนำให้ใช้functions()แทนmacros()เพราะคุณไม่ต้องการให้ตัวแปรในเครื่องของคุณปรากฏในขอบเขตพาเรนต์
  • ตัวแปรจำนวนมากที่ใช้โดย CMake ถูกตั้งค่าด้วยproject()และการenable_language()เรียก ดังนั้นการตั้งค่าตัวแปรให้สำคัญก่อนที่จะใช้คำสั่งเหล่านั้น
  • ตัวแปรสภาพแวดล้อมอาจแตกต่างจากที่ CMake สร้างสภาพแวดล้อมการผลิตและเมื่อไฟล์ make ถูกนำไปใช้
    • การเปลี่ยนแปลงในตัวแปรสภาพแวดล้อมไม่ทำให้กระบวนการสร้างเกิดขึ้นอีกครั้ง
    • โดยเฉพาะอย่างยิ่งสภาพแวดล้อม IDE ที่สร้างขึ้นอาจแตกต่างจากบรรทัดคำสั่งของคุณดังนั้นจึงขอแนะนำให้โอนตัวแปรสภาพแวดล้อมของคุณไปเป็นสิ่งที่แคช

บางครั้งการดีบักตัวแปรช่วยเท่านั้น ต่อไปนี้อาจช่วยคุณได้:

  • เพียงใช้printfรูปแบบการดีบักแบบเก่าโดยใช้message()คำสั่ง นอกจากนี้ยังมีบางส่วนที่พร้อมใช้งานโมดูลที่มาพร้อมกับ CMake เอง: CMakePrintHelpers.cmake , CMakePrintSystemInformation.cmake
  • ค้นหาCMakeCache.txtไฟล์ในไดเรกทอรีเอาต์พุตไบนารีของคุณ ไฟล์นี้จะถูกสร้างขึ้นหากการสร้างสภาพแวดล้อมการสร้างของคุณล้มเหลว
  • ใช้variable_watch ()เพื่อดูว่าตัวแปรของคุณอ่าน / เขียน / ลบ
  • ค้นหาในคุณสมบัติไดเร็กทอรีCACHE_VARIABLESและVARIABLES
  • โทรcmake --trace ...เพื่อดูกระบวนการแยกวิเคราะห์ที่สมบูรณ์ของ CMake นั่นคือการสำรองครั้งสุดท้ายเพราะมันสร้างผลผลิตจำนวนมาก

ไวยากรณ์พิเศษ

  • ตัวแปรสภาพแวดล้อม
    • คุณสามารถอ่าน$ENV{...}และเขียนset(ENV{...} ...)ตัวแปรสภาพแวดล้อมได้
  • นิพจน์ตัวสร้าง
    • นิพจน์ตัวสร้าง$<...>จะได้รับการประเมินก็ต่อเมื่อตัวสร้างของ CMake เขียนสภาพแวดล้อมที่สร้าง (เปรียบเทียบกับตัวแปรปกติที่ถูกแทนที่ "in-place" โดยตัวแยกวิเคราะห์)
    • มีประโยชน์มากเช่นในบรรทัดคำสั่งคอมไพเลอร์ / ลิงเกอร์และในสภาพแวดล้อมที่มีหลายการกำหนดค่า
  • อ้างอิง
    • ด้วย${${...}}คุณสามารถให้ชื่อตัวแปรในตัวแปรและอ้างอิงเนื้อหา
    • มักใช้เมื่อให้ชื่อตัวแปรเป็นฟังก์ชัน / พารามิเตอร์มาโคร
  • ค่าคงที่ (ดูif()คำสั่ง)
    • กับif(MyVariable)คุณโดยตรงสามารถตรวจสอบตัวแปรสำหรับ / เท็จจริง (ไม่จำเป็นต้องที่นี่สำหรับการปิดล้อม${...})
    • จริงถ้าอย่างต่อเนื่องคือ1, ON, YES, TRUE, Yหรือจำนวนไม่เป็นศูนย์
    • เท็จถ้าอย่างต่อเนื่องคือ0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, -NOTFOUNDสตริงที่ว่างเปล่าหรือปลายในคำต่อท้าย
    • ไวยากรณ์นี้มักจะใช้สำหรับสิ่งที่ชอบif(MSVC)แต่อาจทำให้สับสนสำหรับคนที่ไม่รู้จักทางลัดไวยากรณ์นี้
  • การทดแทนแบบเรียกซ้ำ
    • คุณสามารถสร้างชื่อตัวแปรโดยใช้ตัวแปร หลังจาก CMake ได้แทนที่ตัวแปรแล้วมันจะตรวจสอบอีกครั้งว่าผลลัพธ์นั้นเป็นตัวแปรหรือไม่ นี่เป็นคุณสมบัติที่ทรงพลังมากที่ใช้ใน CMake เองเช่นเป็นเทมเพลตset(CMAKE_${lang}_COMPILER ...)
    • แต่ระวังสิ่งนี้สามารถทำให้คุณปวดหัวในif()คำสั่ง นี่คือตัวอย่างในกรณีที่CMAKE_CXX_COMPILER_IDเป็น"MSVC"และMSVCเป็น"1":
      • if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") เป็นจริงเพราะมันประเมิน if("1" STREQUAL "1")
      • if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") เป็นเท็จเพราะมันประเมิน if("MSVC" STREQUAL "1")
      • ดังนั้นทางออกที่ดีที่สุดคือ - ดูด้านบน - เพื่อตรวจสอบโดยตรง if(MSVC)
    • ข่าวดีก็คือว่าเรื่องนี้ได้รับการแก้ไขใน CMake 3.1 ด้วยการแนะนำของCMP0054 นโยบาย ฉันขอแนะนำให้ตั้งค่าcmake_policy(SET CMP0054 NEW)เป็น "ตีความif()อาร์กิวเมนต์เท่านั้นเป็นตัวแปรหรือคำหลักเมื่อไม่ได้อ้างอิง"
  • option()คำสั่ง
    • ส่วนใหญ่เป็นเพียงแค่แคชสตริงที่สามารถONหรือหรือOFFพวกเขาให้การจัดการพิเศษบางอย่างเช่นการอ้างอิง
    • แต่ระวังอย่าทำผิดoptionกับsetคำสั่ง คุ้มค่าให้กับoptionมันเพียง "ค่าเริ่มต้น" (โอนครั้งเดียวไปยังแคชในระหว่างขั้นตอนการตั้งค่าครั้งแรก) และมีความหมายหลังจากนั้นก็จะมีการเปลี่ยนแปลงโดยผู้ใช้ผ่านCMake ของ GUI

อ้างอิง


เมื่อฉันใช้if ("${MyString}" ...)ฉันเห็นคำเตือน: Policy CMP0054 is not set: Only interpret if() arguments as variables or keywords when unquoted. ดูตัวอย่างเช่นสร้าง 367 ความคิดใด ๆ
jww

และ unquoting ${MyString}นำไปสู่พวงของข้อผิดพลาดสำหรับ"ถ้าข้อโต้แย้งให้ ..."เหมือนCMake ข้อผิดพลาดถ้าอยู่ใกล้กับ:“ถ้าข้อโต้แย้งให้” ตามด้วยวงเล็บ“ไม่”,“เท่ากับ” และที่คล้ายกัน
jww

@jww คำเตือนหมายความว่าMyStringมีชื่อตัวแปรที่จะถูกยกเลิกอีกครั้ง ฉันเชื่อว่าไม่มีใครต้องการOLDพฤติกรรมนั้นจริงๆ ดังนั้นจากมุมมองของฉันมันปลอดภัยและเข้ากันได้อย่างสมบูรณ์แบบย้อนหลังเพียงกำหนดนโยบายCMP0054เป็นNEW(ดูการสนทนาที่นี่ )
Florian

@jww และวิธีที่ปลอดภัยที่สุดในการหลีกเลี่ยงปัญหา / คำเตือนเหล่านั้นก็คือทำif (MyString ...)(ถ้ารหัสของคุณเป็นคำเตือน)
Florian

ขอบคุณ เราลบเหตุการณ์ทั้งหมด${MyString}และแทนที่ด้วยMyString(หรือฉันเชื่อว่าเราลบรายการทั้งหมด) ยังคงไม่มีความสุข: รูปร่าง 372 อึไม่ได้มาจากรหัสของเรา ดูเหมือนว่าจะมาจาก CMake สาย 283 if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")เป็น
jww

18

ต่อไปนี้เป็นตัวอย่างพื้นฐานในการเริ่มต้นอย่างรวดเร็วและสกปรก

ตัวแปรหนึ่งรายการ

ตั้งค่าตัวแปร:

SET(INSTALL_ETC_DIR "etc")

ใช้ตัวแปร:

SET(INSTALL_ETC_CROND_DIR "${INSTALL_ETC_DIR}/cron.d")

ตัวแปรหลายรายการ (เช่นรายการ)

ตั้งค่าตัวแปร:

SET(PROGRAM_SRCS
        program.c
        program_utils.c
        a_lib.c
        b_lib.c
        config.c
        )

ใช้ตัวแปร:

add_executable(program "${PROGRAM_SRCS}")

CMake เอกสารเกี่ยวกับตัวแปร


1

$ENV{FOO}สำหรับการใช้งานที่FOOมีการรับจากตัวแปรสภาพแวดล้อม มิฉะนั้นจะใช้เป็น${FOO}, ซึ่งFOOเป็นตัวแปรอื่น ๆ สำหรับการตั้งค่าSET(FOO "foo")จะใช้ใน CMake

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.