เป็นการดีกว่าที่จะระบุไฟล์ต้นฉบับด้วย GLOB หรือแต่ละไฟล์แยกกันใน CMake?


157

CMake มีหลายวิธีในการระบุไฟล์ต้นฉบับสำหรับเป้าหมาย หนึ่งคือการใช้ globbing ( เอกสาร ) ตัวอย่างเช่น:

FILE(GLOB MY_SRCS dir/*)

อีกวิธีคือระบุไฟล์ทีละไฟล์

วิธีไหนเป็นที่ต้องการ Globbing ดูง่าย แต่ฉันได้ยินมาว่ามีข้อเสียอยู่บ้าง

คำตอบ:


185

การเปิดเผยอย่างเต็มรูปแบบ: ฉันต้องการวิธีที่เรียบง่าย แต่ในช่วงหลายปีที่ผ่านมาฉันได้รับการยอมรับว่าการแสดงรายการไฟล์อย่างชัดเจนนั้นมีข้อผิดพลาดน้อยกว่าสำหรับโครงการขนาดใหญ่ที่มีผู้พัฒนาหลายคน

คำตอบเดิม:


ข้อดีของการกลมกลืนคือ:

  • ง่ายต่อการเพิ่มไฟล์ใหม่เนื่องจากมีการระบุไว้ในที่เดียวเท่านั้น: บนดิสก์ ไม่ทำให้เกิดการซ้ำซ้อน

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


1
ยังไม่ดีกับ globbing: ไฟล์ difftool ของคอมไพล์จะถูกเก็บไว้เป็น $ basename $ ext. $ type. $ pid. $ ext ซึ่งอาจทำให้เกิดข้อผิดพลาดที่สนุกสนานเมื่อพยายามรวบรวมหลังจากความละเอียดผสานเดียว
mathstuf

9
ผมคิดว่าคำตอบนี้คัดสรรมากกว่าข้อเสียของ CMake หายไปไฟล์ใหม่Simply "touch" the CMakeLists.txtก็โอเคถ้าคุณเป็นนักพัฒนา แต่สำหรับคนอื่น ๆ การสร้างซอฟต์แวร์ของคุณจริงๆมันอาจจะเป็นอาการปวดจุดที่คุณสร้างล้มเหลวหลังจากการปรับปรุงและภาระที่อยู่บนพวกเขาในการตรวจสอบ ทำไม.
ideasman42

36
คุณรู้อะไรไหม? ตั้งแต่เขียนคำตอบนี้เมื่อ6 ปีที่แล้วฉันเปลี่ยนใจเล็กน้อยและตอนนี้ต้องการแสดงไฟล์อย่างชัดเจน ข้อเสียที่แท้จริงเพียงอย่างเดียวคือ "เพิ่มงานได้อีกเล็กน้อย" แต่มันช่วยให้คุณปวดหัวได้ทุกประเภท และในหลาย ๆ วิธีที่ชัดเจนดีกว่าโดยนัย
richq

1
@richq ตะขอคอมไพล์นี้จะทำให้คุณพิจารณาตำแหน่งปัจจุบันของคุณอีกครั้งหรือไม่ :)
Antonio

8
เช่นเดียวกับอันโตนิโอพูดว่าคะแนนที่ได้รับสำหรับการสนับสนุนแนวทาง "กลมกลืน" การเปลี่ยนลักษณะของคำตอบนั้นเป็นสิ่งที่ต้องทำเพื่อผู้มีสิทธิเลือกตั้งเหล่านั้น ฉันได้เพิ่มการแก้ไขเพื่อแสดงความคิดเห็นที่เปลี่ยนแปลง ฉันขอโทษกับอินเทอร์เน็ตที่ทำให้เกิดพายุเช่นนี้ในถ้วยน้ำชา :-P
richq

113

วิธีที่ดีที่สุดเพื่อระบุ sourcefiles ใน CMake โดยรายชื่อพวกเขาอย่างชัดเจน

ผู้สร้าง CMake แนะนำตัวเองว่าไม่ควรใช้การโค้ง

ดู: https://cmake.org/cmake/help/v3.15/command/file.html?highlight=glob#file

(เราไม่แนะนำให้ใช้ GLOB เพื่อรวบรวมรายการไฟล์ต้นฉบับจากซอร์สทรีของคุณหากไม่มีไฟล์ CMakeLists.txt เปลี่ยนแปลงเมื่อมีการเพิ่มหรือลบซอร์สระบบสร้างที่สร้างขึ้นจะไม่รู้ว่าจะให้ CMake สร้างใหม่เมื่อใด)

แน่นอนคุณอาจต้องการทราบว่าข้อเสียคืออะไร - อ่านต่อ!


เมื่อ Globbing ล้มเหลว:

ข้อเสียอย่างใหญ่หลวงที่ทำให้ globbing คือการสร้าง / ลบไฟล์จะไม่ปรับปรุงระบบการสร้างโดยอัตโนมัติ

หากคุณเป็นคนที่เพิ่มไฟล์สิ่งนี้อาจดูเหมือนเป็นการค้าที่ยอมรับได้ แต่สิ่งนี้ทำให้เกิดปัญหากับคนอื่น ๆ ที่สร้างรหัสของคุณพวกเขาอัพเดทโครงการจากการควบคุมเวอร์ชันรันบิวด์แล้วติดต่อคุณบ่นว่า
"บิลด์ เสีย"

ในการทำให้เรื่องแย่ลงความล้มเหลวมักจะมีข้อผิดพลาดในการเชื่อมโยงซึ่งไม่ได้ให้คำแนะนำเกี่ยวกับสาเหตุของปัญหาและเวลาจะหายไปในการแก้ไขปัญหา

ในโครงการที่ฉันทำงานเราเริ่มจากการวนรอบ แต่ได้รับการร้องเรียนจำนวนมากเมื่อมีการเพิ่มไฟล์ใหม่นั่นเป็นเหตุผลเพียงพอที่จะแสดงรายการไฟล์อย่างชัดเจนแทนการวนซ้ำ

สิ่งนี้ยังทำให้กระแสงานคอมไพล์ทั่วไป
( git bisectและสลับไปมาระหว่างสาขาฟีเจอร์)

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

และอีกข้อสังเกตเพียงแค่ความทรงจำที่ต้องสัมผัสCMakeLists.txtไม่เพียงพอเสมอด้วยการสร้างอัตโนมัติที่ใช้การวนรอบฉันต้องเรียกใช้cmakeก่อนการสร้างทุกครั้งเนื่องจากไฟล์อาจถูกเพิ่ม / ลบตั้งแต่การสร้างครั้งล่าสุด *

ข้อยกเว้นกฎ:

มีบางครั้งที่ globbing เป็นที่ต้องการ:

  • สำหรับการตั้งค่าCMakeLists.txtไฟล์สำหรับโครงการที่มีอยู่ซึ่งไม่ได้ใช้ CMake
    เป็นวิธีที่รวดเร็วในการรับแหล่งอ้างอิงทั้งหมด (เมื่อระบบทำงานของบิลด์ - แทนที่ globbing ด้วยรายการไฟล์ที่ชัดเจน)
  • เมื่อ CMake ไม่ได้ใช้เป็นระบบสร้างหลักถ้าเช่นคุณกำลังใช้โครงการที่ไม่ได้ใช้ CMake และคุณต้องการบำรุงรักษาระบบการสร้างของคุณเอง
  • สำหรับสถานการณ์ใด ๆ ที่รายการไฟล์เปลี่ยนแปลงบ่อยครั้งจนไม่สามารถรักษาได้ ในกรณีนี้มันอาจจะมีประโยชน์ แต่แล้วคุณต้องยอมรับการทำงานcmakeเพื่อสร้างสร้างไฟล์ทุกครั้งที่จะได้รับความน่าเชื่อถือถูกต้อง / สร้าง(ซึ่งขัดกับความตั้งใจในการ CMake - การความสามารถในการกำหนดค่าแยกจากการสร้าง)

* ใช่ฉันสามารถเขียนโค้ดเพื่อเปรียบเทียบทรีของไฟล์บนดิสก์ก่อนและหลังการอัปเดต แต่นี่ไม่ใช่วิธีการแก้ปัญหาที่ดีและสิ่งที่ดีกว่านั้นขึ้นอยู่กับระบบการสร้าง


9
"ข้อเสียเปรียบครั้งใหญ่สำหรับการวนซ้ำคือการสร้างไฟล์ใหม่จะไม่อัปเดตระบบการสร้างโดยอัตโนมัติ" แต่ไม่เป็นความจริงที่ว่าถ้าคุณไม่ได้กลมคุณก็ยังต้องอัพเดต CMakeLists.txt ด้วยตนเองซึ่งหมายความว่า cmake ยังไม่ได้อัปเดตระบบสร้างโดยอัตโนมัติ ดูเหมือนว่าไม่ว่าคุณจะต้องทำอะไรด้วยตนเองเพื่อที่จะสร้างไฟล์ใหม่ การสัมผัส CMakeLists.txt ดูเหมือนง่ายกว่าการเปิดขึ้นและแก้ไขเพื่อเพิ่มไฟล์ใหม่
Dan

17
@Dan สำหรับระบบของคุณ - แน่นอนถ้าคุณพัฒนาเพียงอย่างเดียวสิ่งนี้ก็ใช้ได้ แต่คนอื่น ๆ ที่สร้างโครงการของคุณล่ะ คุณจะส่งอีเมลพวกเขาเพื่อไปและสัมผัสไฟล์ CMake ด้วยตนเอง? ทุกครั้งที่มีการเพิ่มหรือลบไฟล์ - การจัดเก็บรายการไฟล์ใน CMake ทำให้มั่นใจได้ว่าการสร้างจะใช้ไฟล์เดียวกันกับที่ vcs รู้ เชื่อฉัน - นี่ไม่ใช่แค่รายละเอียดเล็ก ๆ น้อย ๆ - เมื่องานสร้างของคุณล้มเหลวสำหรับ devs จำนวนมาก - พวกเขาจะส่งรายชื่ออีเมลและถาม IRC ว่ารหัสนั้นเสีย หมายเหตุ: (แม้ในระบบของคุณเองคุณอาจย้อนกลับไปในประวัติศาสตร์คอมไพล์เช่นและไม่คิดว่าจะเข้าไปและสัมผัสไฟล์ CMake)
คิด man42 42

2
ฉันไม่ได้คิดถึงกรณีนี้ นั่นคือเหตุผลที่ดีที่สุดที่ฉันเคยได้ยินจากการโค้งงอ ฉันหวังว่า cmake docs จะขยายออกไปทำไมพวกเขาจึงแนะนำให้คนหลีกเลี่ยง
Dan

1
ฉันได้คิดถึงวิธีแก้ปัญหาในการเขียนการประทับเวลาของการดำเนินการ cmake ครั้งล่าสุดลงในไฟล์ ปัญหาเดียวคือ: 1) มันอาจจะต้องทำโดย cmake ที่จะเป็นแพลตฟอร์มข้ามดังนั้นเราจึงต้องหลีกเลี่ยง cmake ทำงานตัวเองเป็นครั้งที่สองอย่างใด 2) อาจเป็นไปได้ที่จะรวมความขัดแย้งมากขึ้น (ซึ่งยังคงเกิดขึ้นกับรายชื่อไฟล์ btw) พวกเขาอาจได้รับการแก้ไขเล็กน้อยในกรณีนี้โดยการประทับเวลาในภายหลัง
Predelnik

2
@ tim-mb "แต่มันจะดีถ้า CMake สร้างไฟล์ filetree_updated ที่คุณสามารถเช็คอินได้ซึ่งมันจะเปลี่ยนโดยอัตโนมัติทุกครั้งที่อัพเดตไฟล์ต่างๆ" - คุณได้อธิบายอย่างชัดเจนว่าคำตอบของฉันทำอะไร
Glen Knowles

21

ใน 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งานจะเชื่อถือได้ แต่ก็ยังมีค่าใช้จ่ายในการตรวจสอบทุกครั้งที่สร้างใหม่

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


หากระบบการสร้างของคุณดำเนินการ cmake และวงจรการสร้างที่สมบูรณ์ (ลบไดเรกทอรีการสร้างให้เรียกใช้ cmake จากตรงนั้นแล้วเรียกใช้ makefile) หากไม่ได้ดึงไฟล์ที่ไม่ต้องการออกมา จากประสบการณ์ของฉันส่วน cmake วิ่งได้เร็วกว่างานสร้างดังนั้นมันจึงไม่ใช่ค่าใช้จ่ายเท่าไรหรอก
Den-Jason

9

คุณสามารถ 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


ตอนแรกฉันคิดว่านี่เป็นเครื่องมือที่จะอัปเดต Makefiles โดยอัตโนมัติเมื่อมีการเพิ่มไฟล์ต้นฉบับ แต่ตอนนี้ฉันเห็นคุณค่าของมันแล้ว ดี! นี้ความกังวลของคนที่แก้ปรับปรุงจากพื้นที่เก็บข้อมูลและมีการmakeให้ข้อผิดพลาดลิงเกอร์แปลก
Cris Luengo

1
ฉันเชื่อว่านี่อาจเป็นวิธีที่ดี แน่นอนว่ายังต้องจำไว้ว่าให้เรียกใช้ cmake หลังจากเพิ่มหรือลบไฟล์และมันก็จำเป็นที่จะต้องทำไฟล์อ้างอิงนี้ด้วยดังนั้นการศึกษาด้านผู้ใช้จึงจำเป็น ข้อเสียเปรียบที่สำคัญอาจเป็นได้ว่าไฟล์อ้างอิงนี้อาจก่อให้เกิดความขัดแย้งผสานที่น่ารังเกียจซึ่งอาจแก้ไขได้ยากโดยไม่ต้องให้นักพัฒนาซอฟต์แวร์มีความเข้าใจเกี่ยวกับกลไกอีกครั้ง
อันโตนิโอ

1
สิ่งนี้จะไม่ทำงานหากโครงการของคุณมีไฟล์ที่มีเงื่อนไข (เช่นบางไฟล์ที่ใช้เฉพาะเมื่อเปิดใช้งานคุณสมบัติหรือใช้สำหรับระบบปฏิบัติการบางระบบเท่านั้น) เป็นเรื่องปกติมากพอสำหรับซอฟต์แวร์พกพาที่บางไฟล์ใช้สำหรับแพลตฟอร์ม spesific เท่านั้น
ideasman42

0

ระบุแต่ละไฟล์แยกกัน!

ฉันใช้ CMakeLists.txt แบบเดิมและสคริปต์ python เพื่ออัปเดต ฉันรันสคริปต์ python ด้วยตนเองหลังจากเพิ่มไฟล์

ดูคำตอบของฉันที่นี่: https://stackoverflow.com/a/48318388/3929196

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