ในฐานะผู้เริ่มต้นมีชื่อทั่วไปสำหรับไดเร็กทอรีที่คุณไม่สามารถเพิกเฉยได้ซึ่งเป็นไปตามประเพณีอันยาวนานของระบบไฟล์ Unix เหล่านี้คือ:
trunk
├── bin : for all executables (applications)
├── lib : for all other binaries (static and shared libraries (.so or .dll))
├── include : for all header files
├── src : for source files
└── doc : for documentation
อาจเป็นความคิดที่ดีที่จะยึดติดกับเค้าโครงพื้นฐานนี้อย่างน้อยก็ในระดับบนสุด
เกี่ยวกับการแยกไฟล์ส่วนหัวและไฟล์ต้นฉบับ (cpp) ทั้งสองรูปแบบเป็นเรื่องธรรมดา อย่างไรก็ตามฉันมักจะชอบเก็บไว้ด้วยกันมันเป็นประโยชน์มากกว่าสำหรับงานประจำวันที่จะมีไฟล์ร่วมกัน นอกจากนี้เมื่อโค้ดทั้งหมดอยู่ภายใต้โฟลเดอร์ระดับบนสุดเช่นtrunk/src/
โฟลเดอร์คุณจะสังเกตได้ว่าโฟลเดอร์อื่น ๆ ทั้งหมด (bin, lib, include, doc และอาจจะเป็นโฟลเดอร์ทดสอบบางโฟลเดอร์) ที่ระดับบนสุดนอกเหนือจาก ไดเร็กทอรี "build" สำหรับบิลด์นอกซอร์สคือโฟลเดอร์ทั้งหมดที่ไม่มีอะไรมากไปกว่าไฟล์ที่สร้างขึ้นในกระบวนการสร้าง ดังนั้นจำเป็นต้องสำรองเฉพาะโฟลเดอร์ src หรือดีกว่ามากโดยเก็บไว้ภายใต้ระบบ / เซิร์ฟเวอร์ควบคุมเวอร์ชัน (เช่น Git หรือ SVN)
และเมื่อพูดถึงการติดตั้งไฟล์ส่วนหัวของคุณบนระบบปลายทาง (หากคุณต้องการกระจายไลบรารีของคุณในที่สุด) CMake มีคำสั่งสำหรับการติดตั้งไฟล์ (โดยปริยายจะสร้างเป้าหมาย "ติดตั้ง" เพื่อทำการ "ทำการติดตั้ง") ซึ่ง คุณสามารถใช้เพื่อใส่ส่วนหัวทั้งหมดลงใน/usr/include/
ไดเร็กทอรี ฉันใช้มาโคร cmake ต่อไปนี้เพื่อจุดประสงค์นี้:
# custom macro to register some headers as target for installation:
# setup_headers("/path/to/header/something.h" "/relative/install/path")
macro(setup_headers HEADER_FILES HEADER_PATH)
foreach(CURRENT_HEADER_FILE ${HEADER_FILES})
install(FILES "${SRCROOT}${CURRENT_HEADER_FILE}" DESTINATION "${INCLUDEROOT}${HEADER_PATH}")
endforeach(CURRENT_HEADER_FILE)
endmacro(setup_headers)
ในกรณีที่SRCROOT
เป็นตัวแปร CMake ที่ผมตั้งไปยังโฟลเดอร์ src และINCLUDEROOT
เป็นตัวแปร CMake ที่ผมกำหนดค่าไปยังปลายทางที่ส่วนหัวจะต้องไป แน่นอนว่ามีวิธีอื่นอีกมากมายในการทำเช่นนี้และฉันแน่ใจว่าวิธีของฉันไม่ใช่วิธีที่ดีที่สุด ประเด็นคือไม่มีเหตุผลที่จะแยกส่วนหัวและแหล่งที่มาเพียงเพราะจำเป็นต้องติดตั้งเฉพาะส่วนหัวในระบบเป้าหมายเนื่องจากเป็นเรื่องง่ายมากโดยเฉพาะอย่างยิ่งกับ CMake (หรือ CPack) เพื่อเลือกและกำหนดค่าส่วนหัวเป็น ติดตั้งโดยไม่ต้องมีในไดเร็กทอรีแยกต่างหาก และนี่คือสิ่งที่ฉันเห็นในห้องสมุดส่วนใหญ่
ข้อความอ้างอิง: ประการที่สองฉันต้องการใช้ Google C ++ Testing Framework สำหรับการทดสอบหน่วยของฉันเนื่องจากดูเหมือนว่าใช้งานง่าย คุณแนะนำให้รวมสิ่งนี้เข้ากับรหัสของฉันเช่นในโฟลเดอร์ "inc / gtest" หรือ "Contrib / gtest" หรือไม่ หากรวมกลุ่มคุณแนะนำให้ใช้สคริปต์ fuse_gtest_files.py เพื่อลดจำนวนหรือไฟล์หรือปล่อยทิ้งไว้ตามที่เป็นอยู่ หากไม่ได้รวมการพึ่งพานี้จะจัดการอย่างไร
อย่ารวมการอ้างอิงกับไลบรารีของคุณ โดยทั่วไปแล้วนี่เป็นความคิดที่ค่อนข้างน่ากลัวและฉันมักจะเกลียดมันเมื่อฉันพยายามสร้างห้องสมุดที่ทำแบบนั้น ควรเป็นทางเลือกสุดท้ายของคุณและระวังข้อผิดพลาด บ่อยครั้งที่ผู้คนรวมกลุ่มการอ้างอิงกับไลบรารีของพวกเขาไม่ว่าจะเป็นเพราะพวกเขากำหนดเป้าหมายสภาพแวดล้อมการพัฒนาที่แย่มาก (เช่น Windows) หรือเพราะพวกเขาสนับสนุนเฉพาะไลบรารีเวอร์ชันเก่า (เลิกใช้แล้ว) (การพึ่งพา) ที่เป็นปัญหา ข้อผิดพลาดหลักคือการพึ่งพาบันเดิลของคุณอาจปะทะกับไลบรารี / แอปพลิเคชันเดียวกันที่ติดตั้งไว้แล้ว (เช่นคุณรวม gtest แต่ผู้ที่พยายามสร้างไลบรารีของคุณมี gtest เวอร์ชันใหม่กว่า (หรือเก่ากว่า) ที่ติดตั้งไว้แล้ว ทั้งสองอาจปะทะกันและทำให้คนนั้นปวดหัวมาก) อย่างที่บอกไปว่าคุณต้องยอมรับความเสี่ยงเอง และฉันจะพูดเป็นทางเลือกสุดท้ายเท่านั้น การขอให้ผู้คนติดตั้งการอ้างอิงบางส่วนก่อนที่จะสามารถคอมไพล์ไลบรารีของคุณเป็นสิ่งที่ชั่วร้ายน้อยกว่าการพยายามแก้ไขข้อขัดแย้งระหว่างการอ้างอิงที่รวมและการติดตั้งที่มีอยู่
ข้อความอ้างอิง: เมื่อพูดถึงการทดสอบการเขียนโดยทั่วไปมีการจัดระเบียบอย่างไร? ฉันคิดว่าจะมีไฟล์ cpp หนึ่งไฟล์สำหรับแต่ละคลาส (เช่น test_vector3.cpp) แต่คอมไพล์ทั้งหมดเป็นไบนารีเดียวเพื่อให้สามารถรันร่วมกันได้อย่างง่ายดาย?
ไฟล์ cpp หนึ่งไฟล์ต่อคลาส (หรือกลุ่มคลาสและฟังก์ชั่นที่เหนียวแน่นขนาดเล็ก) เป็นเรื่องปกติและใช้งานได้จริงในความคิดของฉัน อย่างไรก็ตามอย่ารวบรวมทั้งหมดเป็นไบนารีเดียวเพียงเพื่อให้ "ทุกคนสามารถทำงานร่วมกันได้" นั่นเป็นความคิดที่แย่จริงๆ โดยทั่วไปแล้วเมื่อพูดถึงการเข้ารหัสคุณต้องแยกสิ่งต่างๆออกมากที่สุดเท่าที่จะทำได้ ในกรณีของการทดสอบหน่วยคุณไม่ต้องการให้ไบนารีหนึ่งรันการทดสอบทั้งหมดเพราะนั่นหมายความว่าการเปลี่ยนแปลงเพียงเล็กน้อยที่คุณทำกับสิ่งใด ๆ ในไลบรารีของคุณมีแนวโน้มที่จะทำให้เกิดการคอมไพล์ใหม่ทั้งหมดของโปรแกรมทดสอบหน่วยนั้น และนั่นเป็นเพียงนาที / ชั่วโมงที่เสียไปกับการรอการคอมไพล์ใหม่ เพียงแค่ยึดติดกับรูปแบบง่ายๆ: 1 หน่วย = 1 โปรแกรมทดสอบหน่วย จากนั้น
ข้อความอ้างอิง: เนื่องจากไลบรารี gtest มักสร้างโดยใช้ cmake และ make ฉันจึงคิดว่ามันจะสมเหตุสมผลไหมที่โครงการของฉันจะสร้างแบบนี้ หากฉันตัดสินใจใช้เค้าโครงโครงการต่อไปนี้:
ฉันอยากจะแนะนำเค้าโครงนี้:
trunk
├── bin
├── lib
│ └── project
│ └── libvector3.so
│ └── libvector3.a products of installation / building
├── docs
│ └── Doxyfile
├── include
│ └── project
│ └── vector3.hpp
│_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
│
├── src
│ └── CMakeLists.txt
│ └── Doxyfile.in
│ └── project part of version-control / source-distribution
│ └── CMakeLists.txt
│ └── vector3.hpp
│ └── vector3.cpp
│ └── test
│ └── test_vector3.cpp
│_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
│
├── build
└── test working directories for building / testing
└── test_vector3
ข้อสังเกตบางประการที่นี่ ขั้นแรกไดเร็กทอรีย่อยของไดเร็กทอรี src ของคุณควรสะท้อนไดเร็กทอรีย่อยของไดเร็กทอรี include ของคุณซึ่งเป็นเพียงเพื่อให้สิ่งต่าง ๆ ใช้งานง่าย (นอกจากนี้พยายามทำให้โครงสร้างไดเร็กทอรีย่อยของคุณแบนอย่างสมเหตุสมผล (ตื้น) เนื่องจากการซ้อนโฟลเดอร์อยู่ลึก ๆ มักจะยุ่งยากมากกว่าสิ่งอื่นใด) ประการที่สองไดเร็กทอรี "include" เป็นเพียงไดเร็กทอรีการติดตั้งเนื้อหาเป็นเพียงส่วนหัวที่เลือกออกจากไดเร็กทอรี src
ประการที่สามระบบ CMake มีวัตถุประสงค์เพื่อเผยแพร่ผ่านไดเรกทอรีย่อยต้นทางไม่ใช่ไฟล์ CMakeLists.txt ไฟล์เดียวที่ระดับบนสุด สิ่งนี้ช่วยรักษาสิ่งต่างๆในท้องถิ่นและนั่นเป็นสิ่งที่ดี (ในจิตวิญญาณของการแยกสิ่งต่างๆออกเป็นชิ้นส่วนที่เป็นอิสระ) หากคุณเพิ่มซอร์สใหม่ส่วนหัวใหม่หรือโปรแกรมทดสอบใหม่สิ่งที่คุณต้องมีคือแก้ไขไฟล์ CMakeLists.txt ขนาดเล็กและเรียบง่ายหนึ่งไฟล์ในไดเร็กทอรีย่อยที่เป็นปัญหาโดยไม่ส่งผลกระทบต่อสิ่งอื่นใด นอกจากนี้ยังช่วยให้คุณปรับโครงสร้างไดเรกทอรีได้อย่างง่ายดาย (CMakeLists เป็นแบบโลคัลและมีอยู่ในไดเรกทอรีย่อยที่ถูกย้าย) CMakeLists ระดับบนสุดควรมีการกำหนดค่าระดับบนสุดส่วนใหญ่เช่นการตั้งค่าไดเรกทอรีปลายทางคำสั่งที่กำหนดเอง (หรือมาโคร) และการค้นหาแพ็คเกจที่ติดตั้งบนระบบ CMakeLists ระดับล่างควรมีเฉพาะรายการส่วนหัวแหล่งที่มา
ข้อความอ้างอิง: CMakeLists.txt จะต้องมีลักษณะอย่างไรจึงจะสามารถสร้างเฉพาะไลบรารีหรือไลบรารีและการทดสอบได้
คำตอบพื้นฐานคือ CMake ช่วยให้คุณสามารถยกเว้นเป้าหมายบางเป้าหมายจาก "ทั้งหมด" โดยเฉพาะ (ซึ่งเป็นสิ่งที่สร้างขึ้นเมื่อคุณพิมพ์ "make") และคุณยังสามารถสร้างกลุ่มเป้าหมายเฉพาะได้อีกด้วย ฉันไม่สามารถทำแบบฝึกหัด CMake ได้ที่นี่ แต่มันค่อนข้างตรงไปตรงมาที่จะค้นหาด้วยตัวเอง อย่างไรก็ตามในกรณีนี้วิธีแก้ปัญหาที่แนะนำคือการใช้ CTest ซึ่งเป็นเพียงชุดคำสั่งเพิ่มเติมที่คุณสามารถใช้ในไฟล์ CMakeLists เพื่อลงทะเบียนเป้าหมาย (โปรแกรม) จำนวนหนึ่งที่ทำเครื่องหมายเป็นหน่วย การทดสอบ ดังนั้น CMake จะทำการทดสอบทั้งหมดในหมวดหมู่พิเศษของงานสร้างและนั่นคือสิ่งที่คุณขอเพื่อแก้ไขปัญหา
ข้อความอ้างอิง: นอกจากนี้ฉันยังเห็นโครงการไม่กี่โครงการที่มีไดเร็กทอรี bin โฆษณา build บิวด์เกิดขึ้นในไดเร็กทอรีบิลด์จากนั้นไบนารีย้ายออกไปยังไดเร็กทอรี bin หรือไม่ ไบนารีสำหรับการทดสอบและห้องสมุดจะอยู่ในที่เดียวกันหรือไม่ หรือจะเหมาะสมกว่าที่จะจัดโครงสร้างดังต่อไปนี้:
การมีบิวด์ไดเร็กทอรีนอกซอร์ส (บิวด์ "นอกแหล่ง") เป็นสิ่งเดียวที่ต้องทำจริงๆมันเป็นมาตรฐานโดยพฤตินัยในทุกวันนี้ ดังนั้นแน่นอนว่ามีไดเร็กทอรี "build" แยกต่างหากนอกไดเร็กทอรีซอร์สเช่นเดียวกับที่คน CMake แนะนำและอย่างที่โปรแกรมเมอร์ทุกคนที่ฉันเคยพบเจอ สำหรับไดเร็กทอรี bin นั่นเป็นข้อตกลงและมันเป็นความคิดที่ดีที่จะยึดตามที่ฉันได้กล่าวไว้ในตอนต้นของโพสต์นี้
ข้อความอ้างอิง: ฉันต้องการใช้ doxygen เพื่อบันทึกรหัสของฉันด้วย เป็นไปได้หรือไม่ที่จะทำให้สิ่งนี้ทำงานโดยอัตโนมัติด้วย cmake และ make?
ใช่. เป็นไปได้มากกว่านั้นมันยอดเยี่ยมมาก ขึ้นอยู่กับว่าคุณต้องการจินตนาการอย่างไรมีความเป็นไปได้หลายประการ CMake มีโมดูลสำหรับ Doxygen (เช่นfind_package(Doxygen)
) ซึ่งช่วยให้คุณสามารถลงทะเบียนเป้าหมายที่จะเรียกใช้ Doxygen ในไฟล์บางไฟล์ หากคุณต้องการทำสิ่งที่น่าสนใจมากขึ้นเช่นการอัปเดตหมายเลขเวอร์ชันใน Doxyfile หรือการป้อนวันที่ / ตราประทับของผู้แต่งสำหรับไฟล์ต้นฉบับและอื่น ๆ ทั้งหมดนี้เป็นไปได้ด้วย CMake kung-fu โดยทั่วไปการทำเช่นนี้จะเกี่ยวข้องกับการที่คุณเก็บ Doxyfile ต้นทาง (เช่น "Doxyfile.in" ที่ฉันใส่ไว้ในเค้าโครงโฟลเดอร์ด้านบน) ซึ่งมีโทเค็นที่จะพบและแทนที่ด้วยคำสั่งแยกวิเคราะห์ของ CMake ในไฟล์ CMakeLists ระดับบนสุดของฉันคุณจะพบ CMake kung-fu ชิ้นหนึ่งที่ทำสิ่งแปลก ๆ บางอย่างกับ cmake-doxygen ร่วมกัน