คำตอบสั้น ๆ
ไม่สามารถกำหนดค่าสุดท้ายของบรรทัดคำสั่งคอมไพเลอร์ให้กับตัวแปรในสคริปต์ CMake ได้ ซึ่งทำงานได้ในทุกกรณีการใช้งาน
ตอบยาว
น่าเสียดายที่แม้แต่โซลูชันที่ได้รับการยอมรับว่าเป็นคำตอบก็ยังไม่ได้รับแฟล็กคอมไพเลอร์ทั้งหมด ตามที่ระบุไว้ในความคิดเห็น มี ข้อกำหนดการใช้งานแบบสกรรมกริยา เป็นวิธีการเขียนไฟล์ CMake ที่ทันสมัยและเหมาะสม และได้รับความนิยมมากขึ้นเรื่อยๆ นอกจากนี้ คุณอาจมีตัวเลือกการคอมไพล์บางตัวที่กำหนดโดยใช้นิพจน์ตัวสร้าง (ดูเหมือนเป็นการอ้างอิงตัวแปร แต่จะไม่ขยายเมื่อจำเป็น)
ลองพิจารณาตัวอย่างต่อไปนี้:
add_executable(myexe ...);
target_compile_definitions(myexe PRIVATE "PLATFORM_$<PLATFORM_ID>");
add_library(mylib ...);
target_compile_definitions(mylib INTERFACE USING_MY_LIB);
target_link_libraries(myexe PUBLIC mylib);
หากคุณพยายามเรียกแมโคร GET_COMPILER_FLAGS ที่เสนอด้วยเป้าหมาย myexe
คุณจะได้รับผลลัพธ์ผลลัพธ์ -DPLATFORM_$<PLATFORM_ID>
แทนที่จะเป็น -DPLATFORM_Linux -DUSING_MY_LIB
ที่คาดไว้
เนื่องจากมีสองขั้นตอนระหว่างการเรียกใช้ CMake และการสร้างระบบ build:
- กำลังประมวลผล. ในขั้นตอนนี้ CMake อ่านและดำเนินการคำสั่งจากสคริปต์ cmake โดยเฉพาะค่าตัวแปรที่ได้รับการประเมินและกำหนด ในขณะนี้ CMake เพิ่งรวบรวมข้อมูลที่จำเป็นทั้งหมดและเตรียมสร้างระบบบิลด์ (makefiles)
- กำลังสร้าง CMake ใช้ค่าของ ตัวแปรพิเศษ และ คุณสมบัติ ถูกทิ้งไว้ที่ส่วนท้ายของสคริปต์ที่ประมวลผลเพื่อตัดสินใจและสุดท้าย แบบฟอร์มที่สร้างเอาต์พุต นี่คือที่ที่มันสร้างบรรทัดคำสั่งสุดท้ายสำหรับคอมไพเลอร์ตามอัลกอริธึมภายใน ซึ่งไม่สามารถใช้กับการเขียนสคริปต์ได้
คุณสมบัติเป้าหมายซึ่งอาจดึงข้อมูลได้ในขั้นตอนการประมวลผลด้วย get_target_property(...)
หรือ get_property(... TARGET ...)
ไม่สมบูรณ์ (แม้ว่าจะเรียกใช้ที่ส่วนท้ายของสคริปต์ก็ตาม) ในขั้นตอนการสร้าง CMake จะเดินผ่านแผนผังการพึ่งพาเป้าหมายแต่ละอัน (แบบเรียกซ้ำ) และผนวกค่าคุณสมบัติตามความต้องการการใช้งานแบบสกรรมกริยา (ค่าที่ติดแท็ก PUBLIC
และ INTERFACE
ได้รับการเผยแพร่)
แม้ว่าจะมีวิธีแก้ปัญหา ขึ้นอยู่กับผลลัพธ์สุดท้ายที่คุณตั้งเป้าไว้ ซึ่งสามารถทำได้โดยการใช้ นิพจน์ตัวสร้าง ซึ่งอนุญาตให้ใช้ค่า สุดท้าย ของคุณสมบัติของเป้าหมายใด ๆ (กำหนดไว้ในขั้นตอนการประมวลผล)... แต่ในภายหลัง!
ความเป็นไปได้ทั่วไปสองประการสามารถใช้ได้:
- สร้างไฟล์เอาต์พุตใดๆ ตามเทมเพลต ซึ่งเนื้อหามีการอ้างอิงตัวแปรและ/หรือนิพจน์ตัวสร้าง และกำหนดเป็นค่าตัวแปรสตริงหรือไฟล์อินพุต ไม่ยืดหยุ่นเนื่องจากการรองรับตรรกะแบบมีเงื่อนไขที่จำกัดมาก (เช่น คุณไม่สามารถใช้การต่อข้อมูลที่ซับซ้อนที่มีเฉพาะกับลูป
foreach()
ที่ซ้อนกันได้) แต่มีข้อดีคือ ไม่ต้องดำเนินการใดๆ เพิ่มเติม และเนื้อหาที่อธิบายด้วยวิธีที่ไม่ขึ้นกับแพลตฟอร์ม ใช้รูปแบบคำสั่ง file(GENERATE ...) โปรดทราบว่ามันทำงานแตกต่างจากตัวแปร file (WRITE ...)
- เพิ่มเป้าหมายที่กำหนดเอง (และ/หรือคำสั่งที่กำหนดเอง) ซึ่งใช้การใช้งานเพิ่มเติมของค่าที่ขยายเพิ่มเติม ขึ้นอยู่กับแพลตฟอร์มและต้องการให้ผู้ใช้เรียกใช้
make
เพิ่มเติม (ไม่ว่าจะมีเป้าหมายพิเศษหรือรวมเข้ากับเป้าหมาย all
) แต่มีข้อดีตรงที่มีความยืดหยุ่นเพียงพอเนื่องจากคุณสามารถใช้เชลล์สคริปต์ได้ (แต่ไม่มีบิตที่ปฏิบัติการได้)
ตัวอย่างที่สาธิตวิธีแก้ปัญหาด้วยการรวมตัวเลือกเหล่านี้:
set(target_name "myexe")
file(GENERATE OUTPUT script.sh CONTENT "#!/bin/sh\n echo \"${target_name} compile definitions: $<TARGET_PROPERTY:${target_name},COMPILE_DEFINITIONS>\"")
add_custom_target(mycustomtarget
COMMAND echo "\"Platform: $<PLATFORM_ID>\""
COMMAND /bin/sh -s < script.sh
)
หลังจากการเรียกไดเร็กทอรีบิลด์ CMake จะมีไฟล์ script.sh
และการเรียกใช้ make mycustomtarget
จะพิมพ์ไปที่คอนโซล:
Platform: Linux
myexe compile definitions: PLATFORM_Linux USING_MY_LIB
person
Artem Pisarenko
schedule
05.11.2019