CMake มีหลายวิธีในการระบุไฟล์ต้นฉบับสำหรับเป้าหมาย หนึ่งคือการใช้ globbing ( เอกสาร ) ตัวอย่างเช่น:
FILE(GLOB MY_SRCS dir/*)
อีกวิธีคือระบุไฟล์ทีละไฟล์
วิธีไหนเป็นที่ต้องการ Globbing ดูง่าย แต่ฉันได้ยินมาว่ามีข้อเสียอยู่บ้าง
CMake มีหลายวิธีในการระบุไฟล์ต้นฉบับสำหรับเป้าหมาย หนึ่งคือการใช้ globbing ( เอกสาร ) ตัวอย่างเช่น:
FILE(GLOB MY_SRCS dir/*)
อีกวิธีคือระบุไฟล์ทีละไฟล์
วิธีไหนเป็นที่ต้องการ Globbing ดูง่าย แต่ฉันได้ยินมาว่ามีข้อเสียอยู่บ้าง
คำตอบ:
การเปิดเผยอย่างเต็มรูปแบบ: ฉันต้องการวิธีที่เรียบง่าย แต่ในช่วงหลายปีที่ผ่านมาฉันได้รับการยอมรับว่าการแสดงรายการไฟล์อย่างชัดเจนนั้นมีข้อผิดพลาดน้อยกว่าสำหรับโครงการขนาดใหญ่ที่มีผู้พัฒนาหลายคน
คำตอบเดิม:
ข้อดีของการกลมกลืนคือ:
ง่ายต่อการเพิ่มไฟล์ใหม่เนื่องจากมีการระบุไว้ในที่เดียวเท่านั้น: บนดิสก์ ไม่ทำให้เกิดการซ้ำซ้อน
ไฟล์ CMakeLists.txt ของคุณจะสั้นลง นี่เป็นข้อดีอย่างมากหากคุณมีไฟล์จำนวนมาก การไม่ทำให้เกิดความสับสนนั้นทำให้คุณสูญเสียตรรกะ CMake ในรายการไฟล์ขนาดใหญ่
ข้อดีของการใช้รายการไฟล์ฮาร์ดโค้ดคือ:
CMake จะติดตามการขึ้นต่อกันของไฟล์ใหม่บนดิสก์อย่างถูกต้อง - หากเราใช้ glob แล้วไฟล์จะไม่ถูกทำให้กลมเป็นครั้งแรกเมื่อคุณวิ่ง CMake จะไม่ถูกหยิบขึ้นมา
คุณมั่นใจได้ว่าจะเพิ่มเฉพาะไฟล์ที่คุณต้องการ Globbing อาจรับไฟล์เร่ร่อนที่คุณไม่ต้องการ
เพื่อแก้ไขปัญหาแรกคุณสามารถ "แตะ" CMakeLists.txt ที่ทำงานแบบกลมโดยใช้คำสั่ง touch หรือเขียนไฟล์โดยไม่มีการเปลี่ยนแปลง สิ่งนี้จะบังคับให้ CMake ทำการรันใหม่และรับไฟล์ใหม่
เพื่อแก้ไขปัญหาที่สองคุณสามารถจัดระเบียบรหัสของคุณอย่างระมัดระวังลงในไดเรกทอรีซึ่งเป็นสิ่งที่คุณอาจทำอยู่แล้ว ในกรณีที่เลวร้ายที่สุดคุณสามารถใช้list(REMOVE_ITEM)
คำสั่งเพื่อล้างรายการไฟล์แบบโกลเด้น:
file(GLOB to_remove file_to_remove.cpp)
list(REMOVE_ITEM list ${to_remove})
สถานการณ์จริงเพียงข้อเดียวที่สิ่งนี้สามารถกัดคุณได้คือถ้าคุณใช้บางอย่างเช่นgit-bisectเพื่อลองใช้โค้ดรุ่นเก่าในไดเรกทอรีสร้างเดียวกัน ในกรณีนี้คุณอาจต้องทำความสะอาดและคอมไพล์มากกว่าที่จำเป็นเพื่อให้แน่ใจว่าคุณได้รับไฟล์ที่ถูกต้องในรายการ นี่เป็นกรณีมุมและเป็นสิ่งที่คุณมีอยู่แล้วที่นิ้วเท้าของคุณซึ่งไม่ใช่ปัญหาจริงๆ
Simply "touch" the CMakeLists.txt
ก็โอเคถ้าคุณเป็นนักพัฒนา แต่สำหรับคนอื่น ๆ การสร้างซอฟต์แวร์ของคุณจริงๆมันอาจจะเป็นอาการปวดจุดที่คุณสร้างล้มเหลวหลังจากการปรับปรุงและภาระที่อยู่บนพวกเขาในการตรวจสอบ ทำไม.
วิธีที่ดีที่สุดเพื่อระบุ sourcefiles ใน CMake โดยรายชื่อพวกเขาอย่างชัดเจน
ผู้สร้าง CMake แนะนำตัวเองว่าไม่ควรใช้การโค้ง
ดู: https://cmake.org/cmake/help/v3.15/command/file.html?highlight=glob#file
(เราไม่แนะนำให้ใช้ GLOB เพื่อรวบรวมรายการไฟล์ต้นฉบับจากซอร์สทรีของคุณหากไม่มีไฟล์ CMakeLists.txt เปลี่ยนแปลงเมื่อมีการเพิ่มหรือลบซอร์สระบบสร้างที่สร้างขึ้นจะไม่รู้ว่าจะให้ CMake สร้างใหม่เมื่อใด)
แน่นอนคุณอาจต้องการทราบว่าข้อเสียคืออะไร - อ่านต่อ!
ข้อเสียอย่างใหญ่หลวงที่ทำให้ globbing คือการสร้าง / ลบไฟล์จะไม่ปรับปรุงระบบการสร้างโดยอัตโนมัติ
หากคุณเป็นคนที่เพิ่มไฟล์สิ่งนี้อาจดูเหมือนเป็นการค้าที่ยอมรับได้ แต่สิ่งนี้ทำให้เกิดปัญหากับคนอื่น ๆ ที่สร้างรหัสของคุณพวกเขาอัพเดทโครงการจากการควบคุมเวอร์ชันรันบิวด์แล้วติดต่อคุณบ่นว่า
"บิลด์ เสีย"
ในการทำให้เรื่องแย่ลงความล้มเหลวมักจะมีข้อผิดพลาดในการเชื่อมโยงซึ่งไม่ได้ให้คำแนะนำเกี่ยวกับสาเหตุของปัญหาและเวลาจะหายไปในการแก้ไขปัญหา
ในโครงการที่ฉันทำงานเราเริ่มจากการวนรอบ แต่ได้รับการร้องเรียนจำนวนมากเมื่อมีการเพิ่มไฟล์ใหม่นั่นเป็นเหตุผลเพียงพอที่จะแสดงรายการไฟล์อย่างชัดเจนแทนการวนซ้ำ
สิ่งนี้ยังทำให้กระแสงานคอมไพล์ทั่วไป
( git bisect
และสลับไปมาระหว่างสาขาฟีเจอร์)
ดังนั้นฉันจึงไม่สามารถแนะนำสิ่งนี้ได้ปัญหาที่ทำให้เกินความสะดวกสบายเมื่อมีคนไม่สามารถสร้างซอฟต์แวร์ของคุณได้เนื่องจากสิ่งนี้พวกเขาอาจเสียเวลามากในการติดตามปัญหาหรือเพียงแค่ยอมแพ้
และอีกข้อสังเกตเพียงแค่ความทรงจำที่ต้องสัมผัสCMakeLists.txt
ไม่เพียงพอเสมอด้วยการสร้างอัตโนมัติที่ใช้การวนรอบฉันต้องเรียกใช้cmake
ก่อนการสร้างทุกครั้งเนื่องจากไฟล์อาจถูกเพิ่ม / ลบตั้งแต่การสร้างครั้งล่าสุด *
มีบางครั้งที่ globbing เป็นที่ต้องการ:
CMakeLists.txt
ไฟล์สำหรับโครงการที่มีอยู่ซึ่งไม่ได้ใช้ CMake cmake
เพื่อสร้างสร้างไฟล์ทุกครั้งที่จะได้รับความน่าเชื่อถือถูกต้อง / สร้าง(ซึ่งขัดกับความตั้งใจในการ CMake - การความสามารถในการกำหนดค่าแยกจากการสร้าง)* ใช่ฉันสามารถเขียนโค้ดเพื่อเปรียบเทียบทรีของไฟล์บนดิสก์ก่อนและหลังการอัปเดต แต่นี่ไม่ใช่วิธีการแก้ปัญหาที่ดีและสิ่งที่ดีกว่านั้นขึ้นอยู่กับระบบการสร้าง
ใน CMake 3.12 คำสั่งfile(GLOB ...)
และfile(GLOB_RECURSE ...)
คำสั่งได้รับCONFIGURE_DEPENDS
ตัวเลือกที่จะทำการฉายซ้ำ cmake หากค่าของ glob เปลี่ยนแปลง เนื่องจากเป็นข้อเสียเปรียบหลักของการวนซ้ำสำหรับไฟล์ต้นฉบับตอนนี้ก็โอเคที่จะทำเช่นนั้น:
# Whenever this glob's value changes, cmake will rerun and update the build with the
# new/removed files.
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.cpp")
add_executable(my_target ${sources})
อย่างไรก็ตามบางคนยังคงแนะนำให้หลีกเลี่ยงการทำให้กลมกลืนกับแหล่งที่มา อันที่จริงเอกสารระบุว่า:
เราไม่แนะนำให้ใช้ GLOB เพื่อรวบรวมรายการไฟล์ต้นฉบับจากแผนผังต้นกำเนิดของคุณ ...
CONFIGURE_DEPENDS
ธงอาจใช้งานไม่ได้กับเครื่องกำเนิดไฟฟ้าทั้งหมดหรือหากมีการเพิ่มเครื่องกำเนิดไฟฟ้าใหม่ในอนาคตที่ไม่สามารถรองรับได้โครงการที่ใช้งานจะติดอยู่ แม้ว่าCONFIGURE_DEPENDS
งานจะเชื่อถือได้ แต่ก็ยังมีค่าใช้จ่ายในการตรวจสอบทุกครั้งที่สร้างใหม่
โดยส่วนตัวแล้วฉันพิจารณาถึงประโยชน์ของการไม่ต้องจัดการรายชื่อไฟล์ต้นฉบับด้วยตนเองเพื่อเทียบกับข้อเสียที่อาจเกิดขึ้น หากคุณต้องเปลี่ยนกลับไปเป็นไฟล์ที่อยู่ในรายการด้วยตนเองสิ่งนี้สามารถทำได้โดยง่ายเพียงแค่พิมพ์รายการแหล่งข้อมูลแบบกลมและวางกลับเข้าไปใหม่
คุณสามารถ glob อย่างปลอดภัย (และอาจจะควร) ที่ค่าใช้จ่ายของไฟล์เพิ่มเติมเพื่อเก็บการอ้างอิง
เพิ่มฟังก์ชั่นแบบนี้:
# Compare the new contents with the existing file, if it exists and is the
# same we don't want to trigger a make by changing its timestamp.
function(update_file path content)
set(old_content "")
if(EXISTS "${path}")
file(READ "${path}" old_content)
endif()
if(NOT old_content STREQUAL content)
file(WRITE "${path}" "${content}")
endif()
endfunction(update_file)
# Creates a file called CMakeDeps.cmake next to your CMakeLists.txt with
# the list of dependencies in it - this file should be treated as part of
# CMakeLists.txt (source controlled, etc.).
function(update_deps_file deps)
set(deps_file "CMakeDeps.cmake")
# Normalize the list so it's the same on every machine
list(REMOVE_DUPLICATES deps)
foreach(dep IN LISTS deps)
file(RELATIVE_PATH rel_dep ${CMAKE_CURRENT_SOURCE_DIR} ${dep})
list(APPEND rel_deps ${rel_dep})
endforeach(dep)
list(SORT rel_deps)
# Update the deps file
set(content "# generated by make process\nset(sources ${rel_deps})\n")
update_file(${deps_file} "${content}")
# Include the file so it's tracked as a generation dependency we don't
# need the content.
include(${deps_file})
endfunction(update_deps_file)
และจากนั้นไปที่:
file(GLOB_RECURSE sources LIST_DIRECTORIES false *.h *.cpp)
update_deps_file("${sources}")
add_executable(test ${sources})
คุณยังคงใช้ระบบการอ้างอิงที่ชัดเจน (และเรียกใช้งานสร้างอัตโนมัติทั้งหมด!) เหมือนก่อนหน้านี้มีเพียงไฟล์สองไฟล์แทนที่จะเป็นไฟล์เดียว
ขั้นตอนการเปลี่ยนแปลงเพียงอย่างเดียวคือหลังจากที่คุณสร้างไฟล์ใหม่ หากคุณไม่ได้ปรับแต่งเวิร์กโฟลว์คือการปรับเปลี่ยน CMakeLists.txt จากภายใน Visual Studio และสร้างใหม่หากคุณทำงานแบบกลมคุณเรียกใช้ cmake อย่างชัดเจนหรือเพียงแตะที่ CMakeLists.txt
make
ให้ข้อผิดพลาดลิงเกอร์แปลก
ระบุแต่ละไฟล์แยกกัน!
ฉันใช้ CMakeLists.txt แบบเดิมและสคริปต์ python เพื่ออัปเดต ฉันรันสคริปต์ python ด้วยตนเองหลังจากเพิ่มไฟล์
ดูคำตอบของฉันที่นี่: https://stackoverflow.com/a/48318388/3929196